├── BackToHistory.js ├── README.md └── images └── button.png /BackToHistory.js: -------------------------------------------------------------------------------- 1 | /* 2 | Back-to-historytory 3 | https://github.com/SiriusXT/trilium-back-to-history 4 | version: 0.5 for TriliumNext > 0.90.8 5 | */ 6 | 7 | 8 | const supportType = ['text', 'code'] 9 | const autoJump = true; 10 | const showJumpButton = true; 11 | const omitNoteIds=['root'] 12 | 13 | // The following code does not need to be modified 14 | 15 | if (!autoJump && !showJumpButton) { 16 | console.log("BackUpHistory: do nothing"); 17 | return; 18 | } 19 | 20 | function creatHistory() { 21 | return new Promise((resolve, reject) => { 22 | api.runOnBackend((noteId) => { 23 | try { 24 | const { note, branch } = api.createNewNote({ 25 | parentNoteId: noteId, 26 | title: 'history', 27 | content: '{}', 28 | type: 'code', 29 | mime: 'application/json' 30 | }); 31 | resolve(note); 32 | } catch (error) { 33 | // reject(error); 34 | } 35 | }, [api.startNote.noteId]); 36 | }); 37 | } 38 | 39 | let historyNoteId; 40 | async function getHistory() { 41 | let history; 42 | if (historyNoteId !== undefined) { 43 | const childNote = await api.getNote(historyNoteId); 44 | // return JSON.parse((await (await api.getNote(historyNoteId)).getNoteComplement()).content); 45 | history = JSON.parse((await childNote.getNoteComplement()).content); 46 | return history; 47 | } 48 | const children = api.startNote.children; 49 | let haveHistory = false; 50 | for (let i = 0; i < children.length; i++) { 51 | const childNote = await api.getNote(children[i]); 52 | if (childNote.type == 'code' && childNote.mime == 'application/json' && childNote.title == 'history') { 53 | historyNoteId = childNote.noteId; 54 | try { 55 | history = JSON.parse((await childNote.getNoteComplement()).content); 56 | } catch (error) { 57 | history = {} 58 | } 59 | haveHistory = true; 60 | break; 61 | } 62 | } 63 | if (!haveHistory) { 64 | const note = await creatHistory(); 65 | historyNoteId = note.noteId; 66 | history = JSON.parse((await note.getNoteComplement()).content); 67 | } 68 | return history; 69 | } 70 | 71 | const jumpButton = `
72 | 73 |
` 74 | var jumpHistory = class extends api.NoteContextAwareWidget { 75 | get position() { 76 | return 50; 77 | } 78 | static get parentWidget() { 79 | return 'note-detail-pane'; 80 | } 81 | constructor() { 82 | super(); 83 | } 84 | isEnabled() { 85 | return super.isEnabled() && supportType.includes(this.note.type) && !omitNoteIds.includes(this.noteId); 86 | } 87 | doRender() { 88 | this.$widget = $(''); 89 | return this.$widget; 90 | } 91 | scrollHandler = () => { 92 | clearTimeout(this.updateTimer); 93 | this.updateTimer = setTimeout(async () => { 94 | try { 95 | const sc = this.$scrollingContainer[0]; 96 | const nl = this.$scrollingContainer.find('.note-list-widget')[0]; 97 | const radio = (sc.scrollTop / (sc.scrollHeight - nl.offsetHeight)).toFixed(5); 98 | if (!isNaN(radio)) { 99 | let history = await getHistory(); 100 | history[this.note.noteId] = radio; 101 | saveHistory(history); 102 | } 103 | } 104 | catch (error) { 105 | const $noteSplit = $(`.note-split[data-ntx-id="${this.noteContext.ntxId}"]`); 106 | this.$scrollingContainer = $noteSplit.children('.scrolling-container'); 107 | console.warn('jumpHistory: ', error); 108 | } 109 | }, 3000); 110 | } 111 | scrollTo = () => { 112 | const sc = this.$scrollingContainer[0]; 113 | const nl = sc.querySelector('.note-list-widget'); 114 | const scrollHeight = sc.scrollHeight - nl.offsetHeight; 115 | $(sc).animate({ 116 | scrollTop: this.scrollToRadio * scrollHeight, 117 | }, 300); 118 | } 119 | addJumpButton() { 120 | const $ribbonTab = $(`.note-split[data-ntx-id="${this.noteContext.ntxId}"]`).find('.ribbon-tab-container'); 121 | $ribbonTab.find('.jump-history').remove(); 122 | const $button = $(jumpButton); 123 | $ribbonTab.append($button); 124 | $button.on('click', this.scrollTo); 125 | $button.attr('title', "Scorll To " + this.scrollToRadio * 100 + "%"); 126 | } 127 | 128 | autoJumpFunc() { 129 | this.scrollTo(); 130 | clearInterval(this.jumpInterval); 131 | let timesRun = 0; 132 | let preHeight = 0; 133 | this.jumpInterval = setInterval(() => { 134 | timesRun += 1; 135 | if (timesRun == 5) { 136 | clearInterval(this.jumpInterval); 137 | } 138 | if (this.$scrollingContainer[0].scrollHeight != preHeight) { 139 | preHeight = this.$scrollingContainer[0].scrollHeight; 140 | this.scrollTo(); 141 | } 142 | }, 200); 143 | } 144 | async initEvent() { 145 | this.updateTimeout = 0; 146 | const $noteSplit = $(`.note-split[data-ntx-id="${this.noteContext.ntxId}"]`); 147 | this.$scrollingContainer = $noteSplit.children('.scrolling-container'); 148 | this.$scrollingContainer.off('scroll', this.scrollHandler); 149 | if (showJumpButton && this.scrollToRadio != undefined) { 150 | this.addJumpButton(); 151 | } 152 | setTimeout(() => { 153 | // Automatic jump without monitoring 154 | this.$scrollingContainer.off('scroll', this.scrollHandler); 155 | this.$scrollingContainer.on('scroll', this.scrollHandler); 156 | }, 1000); 157 | } 158 | async refreshWithNote() { 159 | const history = await getHistory(); 160 | this.scrollToRadio = history[this.note.noteId]; 161 | await this.initEvent(); 162 | if (this.scrollToRadio != undefined && autoJump) { 163 | this.autoJumpFunc(); 164 | } 165 | } 166 | async entitiesReloadedEvent({ loadResults }) { 167 | if (loadResults.isNoteContentReloaded(this.noteId)) { 168 | const $noteSplit = $(`.note-split[data-ntx-id="${this.noteContext.ntxId}"]`); 169 | this.$scrollingContainer = $noteSplit.children('.scrolling-container'); 170 | this.scrollHandler(); 171 | } 172 | if (loadResults.getAttributeRows().find(attr => attr.noteId === this.noteId)) { 173 | this.initEvent(); 174 | } 175 | } 176 | } 177 | 178 | module.exports = jumpHistory; 179 | 180 | function saveHistory(history) { 181 | const keys = Object.keys(history); 182 | while (keys.length > 100) { 183 | const oldestKey = keys.shift(); // Get the oldest key 184 | delete history[oldestKey]; // Delete the element corresponding to the key 185 | } 186 | api.runAsyncOnBackendWithManualTransactionHandling(async (historyNoteId, history) => { 187 | const historyNote = await api.getNote(historyNoteId); 188 | historyNote.setContent(JSON.stringify(history)); 189 | }, [historyNoteId, history]); 190 | } 191 | 192 | 193 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiriusXT/trilium-back-to-history/48f9b414ee8e0f85abf27f3365efcfbc312615dd/README.md -------------------------------------------------------------------------------- /images/button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiriusXT/trilium-back-to-history/48f9b414ee8e0f85abf27f3365efcfbc312615dd/images/button.png --------------------------------------------------------------------------------