<commit_before>// Extend the application with navigation functionality

let showNavigation = false
let cacheItemCount = 0
const cache = {}

function processNewJournalItems(callback, startIndex) {
    if (!startIndex) startIndex = 0
    const journal = Twirlip7.getCurrentJournal()
    for (let i = startIndex; i < journal.itemCount(); i++) {
        const item = journal.getItemForLocation(i)
        if (item) {
            const key = journal.keyForLocation(i)
            callback({i, key, item: JSON.parse(item)})
        }
    }
}

function addToMap(itemContext) {
    const entity = itemContext.item.entity
    const attribute = itemContext.item.attribute
    let entityInfo = cache["e:" + entity]
    if (!entityInfo) {
        entityInfo = { entity, attributes: {} }
        cache["e:" + entity] = entityInfo
    }
    let attributeInfo = entityInfo.attributes["a:" + attribute]
    if (!attributeInfo) {
        attributeInfo = { attribute, itemsByKey: {}, itemsByOrder: [], open: false }
        entityInfo.attributes["a:" + attribute] = attributeInfo
    }
    if (!attributeInfo.itemsByKey[itemContext.key]) {
        const item = itemContext.item
        // Don't save entire item in case it is big and maybe store is not keeping it in memory
        const itemInfo = { open: false, i: itemContext.i, key: itemContext.key, timestamp: item.timestamp, contributor: item.contributor }
        attributeInfo.itemsByKey[itemContext.key] = itemInfo
        attributeInfo.itemsByOrder.push(itemInfo)
        cacheItemCount++
    }
}

function updateCacheIfNeeded() {
    const currentItemCount = Twirlip7.getCurrentJournal().itemCount()
    if (cacheItemCount < currentItemCount) {
        processNewJournalItems(addToMap, cacheItemCount)
    }
}

function load(key) {
    console.log("load", key)
    if (!Twirlip7.WorkspaceView.confirmClear()) return
    Twirlip7.WorkspaceView.goToKey(key)
}

Twirlip7.WorkspaceView.extensionsInstall({
    id: "navigation",
    tags: "footer",
    code: (context) => {
        // TODO: Updating the cache should be done outside of view function?
        updateCacheIfNeeded()
        return m("div",
            m("span.i.b", { title: "click to open or close navigation", onclick: () => showNavigation = !showNavigation }, "Item Navigation"),
            showNavigation ? 
                Object.keys(cache).sort().map((entityInfoKey) => {
                    const entityInfo = cache[entityInfoKey]
                    return m("div", 
                        m("span.ml2", { onclick: () => entityInfo.open = !entityInfo.open }, entityInfo.entity || "<no entity name>"),
                        entityInfo.open ?
                            Object.keys(entityInfo.attributes).sort().map((attributeInfoKey) => {
                                const attributeInfo = entityInfo.attributes[attributeInfoKey]
                                const lastItemInfo = attributeInfo.itemsByOrder[attributeInfo.itemsByOrder.length - 1]
                                const title = "Go to last added\n" + lastItemInfo.timestamp + "\n" + lastItemInfo.contributor
                                return m("div.ml3",
                                    m("span", { title: title, onclick: () => load(lastItemInfo.key) },
                                        attributeInfo.attribute  || "<no attribute name>"
                                    )
                                )
                            }) :
                            []
                    )
                }) :
                []
        )
    }
})

// Twirlip7.WorkspaceView.extensionsUninstall({id: "navigation"})<commit_msg>Improve navigation extension so can see versions of entity-attribute values<commit_after>// Extend the application with navigation functionality

let showNavigation = false
let cacheItemCount = 0
const cache = {}

function processNewJournalItems(callback, startIndex) {
    if (!startIndex) startIndex = 0
    const journal = Twirlip7.getCurrentJournal()
    for (let i = startIndex; i < journal.itemCount(); i++) {
        const item = journal.getItemForLocation(i)
        if (item) {
            const key = journal.keyForLocation(i)
            callback({i, key, item: JSON.parse(item)})
        }
    }
}

function addToMap(itemContext) {
    const entity = itemContext.item.entity
    const attribute = itemContext.item.attribute
    let entityInfo = cache["e:" + entity]
    if (!entityInfo) {
        entityInfo = { open: false, entity, attributes: {}, lastItem: null }
        cache["e:" + entity] = entityInfo
    }
    let attributeInfo = entityInfo.attributes["a:" + attribute]
    if (!attributeInfo) {
        attributeInfo = { open: false, attribute, itemsByKey: {}, itemsByOrder: [] }
        entityInfo.attributes["a:" + attribute] = attributeInfo
    }
    if (!attributeInfo.itemsByKey[itemContext.key]) {
        const item = itemContext.item
        // Don't save entire item in case it is big and maybe store is not keeping it in memory
        const itemInfo = { open: false, i: itemContext.i, key: itemContext.key, timestamp: item.timestamp, contributor: item.contributor }
        attributeInfo.itemsByKey[itemContext.key] = itemInfo
        attributeInfo.itemsByOrder.push(itemInfo)
        entityInfo.lastItemInfo = itemInfo
        cacheItemCount++
    }
}

function updateCacheIfNeeded() {
    const currentItemCount = Twirlip7.getCurrentJournal().itemCount()
    if (cacheItemCount < currentItemCount) {
        processNewJournalItems(addToMap, cacheItemCount)
    }
}

function load(key) {
    console.log("load", key)
    if (!Twirlip7.WorkspaceView.confirmClear()) return
    Twirlip7.WorkspaceView.goToKey(key)
}

Twirlip7.WorkspaceView.extensionsInstall({
    id: "navigation",
    tags: "footer",
    code: (context) => {
        // TODO: Updating the cache should be done outside of view function?
        updateCacheIfNeeded()
        return m("div",
            m("span.i.b", { title: "click to open or close navigation", onclick: () => showNavigation = !showNavigation }, "Item Navigation"),
            showNavigation ? 
                Object.keys(cache).sort().map((entityInfoKey) => {
                    const entityInfo = cache[entityInfoKey]
                    const lastItemInfo = entityInfo.lastItemInfo
                    const title = "Go to last added\n" + lastItemInfo.timestamp + "\n" + lastItemInfo.contributor
                    return m("div", 
                        m("span", { onclick: () => entityInfo.open = !entityInfo.open }, entityInfo.open ? "▼" : "►"),
                        m("span.ml3", { title: title , onclick: () => load(lastItemInfo.key) }, entityInfo.entity || m("span.i", "<no entity name>")),
                        entityInfo.open ?
                            Object.keys(entityInfo.attributes).sort().map((attributeInfoKey) => {
                                const attributeInfo = entityInfo.attributes[attributeInfoKey]
                                const lastItemInfo = attributeInfo.itemsByOrder[attributeInfo.itemsByOrder.length - 1]
                                const title = "Go to last added\n" + lastItemInfo.timestamp + "\n" + lastItemInfo.contributor
                                return m("div.ml4",
                                    m("span", { onclick: () => attributeInfo.open = !attributeInfo.open }, attributeInfo.open ? "▼" : "►"),
                                    m("span", { title: title, onclick: () => load(lastItemInfo.key) },
                                        attributeInfo.attribute  || m("span.i", "<no attribute name>")
                                    ),
                                    attributeInfo.open ? 
                                        attributeInfo.itemsByOrder.map((itemInfo) => {
                                            return m("div.ml5", { title: "Go", onclick: () => load(itemInfo.key) },
                                                itemInfo.timestamp,
                                                m("span.ma2"),
                                                itemInfo.contributor
                                            )
                                        }) :
                                        []
                                )
                            }) :
                            []
                    )
                }) :
                []
        )
    }
})

// Twirlip7.WorkspaceView.extensionsUninstall({id: "navigation"})
{'fraction_non_alphanumeric': 0.1112817483756645, 'fraction_numerical': 0.002835203780271707, 'mean_word_length': 1.8257676902536715, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 21, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5700955', 'n_tokens_mistral': 2143, 'n_tokens_neox': 2101, 'n_words': 486}