diff --git a/web/app/app.js b/web/app/app.js index 2548988..33c2551 100644 --- a/web/app/app.js +++ b/web/app/app.js @@ -7,16 +7,16 @@ var t = ` - +
- - +
@@ -56,8 +56,8 @@ export default { data() { return { notes: [], - activeFilename: '', - activeFilenamePrevious: '', + tabs: [], + tabHistory: [], finderUri: '', finderQuery: '', showGraph: false, @@ -81,12 +81,12 @@ export default { this.showFinder = false; this.finderQuery = ''; if (value === null) { - this.resetActiveFilename(); + this.refocusActiveTab(); } else { const note = this.notes.find(note => note.Filename === value.Filename); if (note) { note.Linenum = value.Linenum; - this.activateNote(value.Filename); + this.activateTab(value.Filename); } else { this.fetchNote(value.Filename, value.Linenum); } @@ -97,9 +97,9 @@ export default { .then(r => r.ok ? r.json() : r.json().then(e => Promise.reject(e))) .then(note => { note.Linenum = linenum; - const index = insertAfterActive ? this.notes.findIndex(note => note.Filename === this.activeFilename) : -1; - (index === -1) ? this.notes.push(note) : this.notes.splice(index + 1, 0, note); - this.activateNote(note.Filename); + this.notes.push(note); + this.addTab('note', note.Filename, insertAfterActive); + this.activateTab(note.Filename); }) .catch(e => { this.addAlert({type: 'error', title: 'Error fetching note', body: e.Error, sticky: true}) @@ -137,7 +137,7 @@ export default { } this.notes[index] = note; - this.activateNote(note.Filename); + this.activateTab(note.Filename); // track lastSave to force sidepanel refresh this.lastSave = note.Mtime; @@ -215,7 +215,7 @@ export default { if (noteInfo.exists === "true") { this.openNote(noteInfo.filename, 1); return; } const index = this.notes.findIndex(note => note.Filename === noteInfo.filename); - if (index !== -1) { this.activateNote(noteInfo.filename); return; } + if (index !== -1) { this.activateTab(noteInfo.filename); return; } const ghost = { Filename: noteInfo.filename, @@ -227,7 +227,8 @@ export default { ghost: true, }; this.notes.push(ghost); - this.activateNote(ghost.Filename); + this.addTab('note', ghost.Filename); + this.activateTab(ghost.Filename); }) .catch(e => { this.addAlert({type: 'error', title: 'Error retrieving new note metadata', body: e.Error, sticky: true}); @@ -262,23 +263,41 @@ export default { const index = this.notes.findIndex(note => note.Filename === filename); if (index !== -1) { this.notes[index].Linenum = linenum; - this.activateNote(filename); + this.activateTab(filename); } else { this.fetchNote(filename, linenum, true); } }, - activateNote(filename) { - if (filename !== this.activeFilename) { - this.activeFilenamePrevious = this.activeFilename; - this.activeFilename = filename; + addTab(tabType, tabId, insertAfterActive = false) { + const tab = {type: tabType, id: tabId} + const index = this.tabs.findIndex(t => t.id === this.activeTabId); + if (insertAfterActive && index !== -1) { + this.tabs.splice(index + 1, 0, tab); + } else { + this.tabs.push(tab); } }, - resetActiveFilename() { - if (this.activeFilename) { - const af = this.activeFilename; - this.activeFilename = ''; - this.$nextTick(() => { this.activeFilename = af; }); - } + activateTab(tabId) { + if (tabId == this.activeTabId) return; + this.tabHistory = this.tabHistory.filter(id => id !== tabId); + this.tabHistory.push(tabId); + }, + refocusActiveTab() { + // required for cancelled keybind and finder + if (!this.activeTabId) return; + const tabId = this.activeTabId; + this.tabHistory = this.tabHistory.filter(id => id !== tabId); + this.$nextTick(() => { this.activateTab(tabId) }); + }, + moveTab(tabId, newIndex) { + const index = this.tabs.findIndex(t => t.id === tabId); + if (index === -1) return; + this.tabs.splice(newIndex, 0, this.tabs.splice(index, 1)[0]); + }, + closeTab(tabId) { + const index = this.tabs.findIndex(t => t.id === tabId); + if (index !== -1) this.tabs.splice(index, 1); + this.tabHistory = this.tabHistory.filter(id => id !== tabId); }, async closeNote(filename, confirmIfModified = true) { const index = this.notes.findIndex(note => note.Filename === filename); @@ -293,30 +312,7 @@ export default { } this.notes.splice(index, 1); - const notesLength = this.notes.length; - switch(notesLength) { - case 0: - this.activeFilename = ''; - this.activeFilenamePrevious = ''; - break; - case 1: - this.activeFilename = this.notes[0].Filename; - this.activeFilenamePrevious = ''; - break; - default: - const lastFilename = this.notes.at(-1).Filename; - if (filename == this.activeFilename) { - const previousExists = this.notes.some(note => note.Filename === this.activeFilenamePrevious); - this.activeFilename = previousExists ? this.activeFilenamePrevious : lastFilename; - } - this.activeFilenamePrevious = (this.activeFilename !== lastFilename) ? lastFilename : this.notes.at(-2).Filename; - break; - } - }, - moveNote(filename, newIndex) { - const index = this.notes.findIndex(note => note.Filename === filename); - if (index === -1) return; - this.notes.splice(newIndex, 0, this.notes.splice(index, 1)[0]); + this.closeTab(filename); }, addAlert({type, title, body, sticky = false}) { this.alerts.push({type, title, body, sticky, id: Date.now().toString(36)}); @@ -372,7 +368,7 @@ export default { setTimeout(() => { if (this.keySequence.length > 0) { this.keySequence = []; - this.resetActiveFilename(); + this.refocusActiveTab(); } }, 2000); return; @@ -397,9 +393,9 @@ export default { break; case `${leaderKey} KeyN KeyK`: this.keySequence = []; - this.activeFilename - ? this.openFinder('/api/raw/links?color=true&filename=' + this.activeFilename) - : this.openFinder('/api/raw/links?color=true'); + const tab = this.tabs.find(t => t.id === this.activeTabId); + const extraParams = tab?.type === 'note' ? `&filename=${tab.id}` : ''; + this.openFinder('/api/raw/links?color=true' + extraParams); break; case `${leaderKey} KeyN KeyS`: this.keySequence = []; @@ -460,6 +456,14 @@ export default { .catch(e => { console.error(e); }); }, }, + computed: { + activeTabId() { + return this.tabHistory[this.tabHistory.length - 1] || ''; + }, + previousTabId() { + return this.tabHistory[this.tabHistory.length - 2] || ''; + }, + }, mounted() { document.addEventListener('keydown', this.handleKeyPress); window.addEventListener('beforeunload', this.handleBeforeUnload); diff --git a/web/app/graph-panel.js b/web/app/graph-panel.js index fc3055d..2068162 100644 --- a/web/app/graph-panel.js +++ b/web/app/graph-panel.js @@ -91,7 +91,7 @@ import Icon from './icon.js' import GraphD3 from './graph-d3.js' export default { components: { Pane, Icon, GraphD3 }, - props: ['activeFilename', 'lastSave'], + props: ['activeTabId', 'lastSave'], emits: ['note-open'], data() { return { @@ -141,7 +141,7 @@ export default { const queryWords = this.query.toLowerCase().split(' '); return this.graphData.nodes.filter(node => queryWords.every(queryWord => node.title.toLowerCase().includes(queryWord))).map(node => node.id); } - return (this.display.emphasizeActive.value && this.activeFilename) ? [this.activeFilename] : null; + return (this.display.emphasizeActive.value && this.activeTabId) ? [this.activeTabId] : null; }, }, created() { diff --git a/web/app/nav-tabs.js b/web/app/nav-tabs.js index 4fd41da..32ea2bc 100644 --- a/web/app/nav-tabs.js +++ b/web/app/nav-tabs.js @@ -1,41 +1,40 @@ var t = `
-