2023-06-29 11:55:02 +08:00
/ *
THIS IS A GENERATED / BUNDLED FILE BY ROLLUP
if you want to view the source visit the plugins github repository
* /
'use strict' ;
var obsidian = require ( 'obsidian' ) ;
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Copyright ( c ) Microsoft Corporation .
Permission to use , copy , modify , and / or distribute this software for any
purpose with or without fee is hereby granted .
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL , DIRECT ,
INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE , DATA OR PROFITS , WHETHER IN AN ACTION OF CONTRACT , NEGLIGENCE OR
OTHER TORTIOUS ACTION , ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
function _ _awaiter ( thisArg , _arguments , P , generator ) {
function adopt ( value ) { return value instanceof P ? value : new P ( function ( resolve ) { resolve ( value ) ; } ) ; }
return new ( P || ( P = Promise ) ) ( function ( resolve , reject ) {
function fulfilled ( value ) { try { step ( generator . next ( value ) ) ; } catch ( e ) { reject ( e ) ; } }
function rejected ( value ) { try { step ( generator [ "throw" ] ( value ) ) ; } catch ( e ) { reject ( e ) ; } }
function step ( result ) { result . done ? resolve ( result . value ) : adopt ( result . value ) . then ( fulfilled , rejected ) ; }
step ( ( generator = generator . apply ( thisArg , _arguments || [ ] ) ) . next ( ) ) ;
} ) ;
}
2024-04-15 11:54:03 +08:00
typeof SuppressedError === "function" ? SuppressedError : function ( error , suppressed , message ) {
var e = new Error ( message ) ;
return e . name = "SuppressedError" , e . error = error , e . suppressed = suppressed , e ;
} ;
const SAFE _DB _FLUSH _INTERVAL = 5000 ;
2023-06-29 11:55:02 +08:00
const DEFAULT _SETTINGS = {
dbFileName : '.obsidian/plugins/remember-cursor-position/cursor-positions.json' ,
2024-04-15 11:54:03 +08:00
delayAfterFileOpening : 100 ,
saveTimer : SAFE _DB _FLUSH _INTERVAL ,
2023-06-29 11:55:02 +08:00
} ;
class RememberCursorPosition extends obsidian . Plugin {
constructor ( ) {
super ( ... arguments ) ;
this . loadingFile = false ;
}
onload ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield this . loadSettings ( ) ;
try {
this . db = yield this . readDb ( ) ;
2024-04-15 11:54:03 +08:00
this . lastSavedDb = yield this . readDb ( ) ;
2023-06-29 11:55:02 +08:00
}
catch ( e ) {
2024-04-15 11:54:03 +08:00
console . error ( "Remember Cursor Position plugin can\'t read database: " + e ) ;
2023-06-29 11:55:02 +08:00
this . db = { } ;
2024-04-15 11:54:03 +08:00
this . lastSavedDb = { } ;
2023-06-29 11:55:02 +08:00
}
this . addSettingTab ( new SettingTab ( this . app , this ) ) ;
this . registerEvent ( this . app . workspace . on ( 'file-open' , ( file ) => this . restoreEphemeralState ( ) ) ) ;
this . registerEvent ( this . app . workspace . on ( 'quit' , ( ) => { this . writeDb ( this . db ) ; } ) ) ;
this . registerEvent ( this . app . vault . on ( 'rename' , ( file , oldPath ) => this . renameFile ( file , oldPath ) ) ) ;
this . registerEvent ( this . app . vault . on ( 'delete' , ( file ) => this . deleteFile ( file ) ) ) ;
//todo: replace by scroll and mouse cursor move events
this . registerInterval ( window . setInterval ( ( ) => this . checkEphemeralStateChanged ( ) , 100 ) ) ;
2024-04-15 11:54:03 +08:00
this . registerInterval ( window . setInterval ( ( ) => this . writeDb ( this . db ) , this . settings . saveTimer ) ) ;
2023-06-29 11:55:02 +08:00
this . restoreEphemeralState ( ) ;
} ) ;
}
renameFile ( file , oldPath ) {
let newName = file . path ;
let oldName = oldPath ;
this . db [ newName ] = this . db [ oldName ] ;
delete this . db [ oldName ] ;
}
deleteFile ( file ) {
let fileName = file . path ;
delete this . db [ fileName ] ;
}
checkEphemeralStateChanged ( ) {
var _a ;
let fileName = ( _a = this . app . workspace . getActiveFile ( ) ) === null || _a === void 0 ? void 0 : _a . path ;
//waiting for load new file
if ( ! fileName || ! this . lastLoadedFileName || fileName != this . lastLoadedFileName || this . loadingFile )
return ;
let st = this . getEphemeralState ( ) ;
if ( ! this . lastEphemeralState )
this . lastEphemeralState = st ;
if ( ! isNaN ( st . scroll ) && ! this . isEphemeralStatesEquals ( st , this . lastEphemeralState ) ) {
this . saveEphemeralState ( st ) ;
this . lastEphemeralState = st ;
}
}
isEphemeralStatesEquals ( state1 , state2 ) {
if ( state1 . cursor && ! state2 . cursor )
return false ;
if ( ! state1 . cursor && state2 . cursor )
return false ;
if ( state1 . cursor ) {
if ( state1 . cursor . from . ch != state2 . cursor . from . ch )
return false ;
if ( state1 . cursor . from . line != state2 . cursor . from . line )
return false ;
if ( state1 . cursor . to . ch != state2 . cursor . to . ch )
return false ;
if ( state1 . cursor . to . line != state2 . cursor . to . line )
return false ;
}
if ( state1 . scroll && ! state2 . scroll )
return false ;
if ( ! state1 . scroll && state2 . scroll )
return false ;
2024-04-15 11:54:03 +08:00
if ( state1 . scroll && state1 . scroll != state2 . scroll )
return false ;
2023-06-29 11:55:02 +08:00
return true ;
}
saveEphemeralState ( st ) {
var _a ;
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
let fileName = ( _a = this . app . workspace . getActiveFile ( ) ) === null || _a === void 0 ? void 0 : _a . path ;
if ( fileName && fileName == this . lastLoadedFileName ) { //do not save if file changed or was not loaded
this . db [ fileName ] = st ;
}
} ) ;
}
restoreEphemeralState ( ) {
var _a , _b , _c ;
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
let fileName = ( _a = this . app . workspace . getActiveFile ( ) ) === null || _a === void 0 ? void 0 : _a . path ;
if ( fileName && this . loadingFile && this . lastLoadedFileName == fileName ) //if already started loading
return ;
this . loadingFile = true ;
if ( this . lastLoadedFileName != fileName ) {
this . lastEphemeralState = { } ;
this . lastLoadedFileName = fileName ;
if ( fileName ) {
let st = this . db [ fileName ] ;
if ( st ) {
2024-04-15 11:54:03 +08:00
//waiting for load note
2023-06-29 11:55:02 +08:00
yield this . delay ( this . settings . delayAfterFileOpening ) ;
let scroll ;
for ( let i = 0 ; i < 20 ; i ++ ) {
scroll = ( _c = ( _b = this . app . workspace . getActiveViewOfType ( obsidian . MarkdownView ) ) === null || _b === void 0 ? void 0 : _b . currentMode ) === null || _c === void 0 ? void 0 : _c . getScroll ( ) ;
if ( scroll !== null )
break ;
yield this . delay ( 10 ) ;
}
//if note opened by link like [link](note.md#header), do not scroll it
2024-04-15 11:54:03 +08:00
// CHANGE this expression for compatability with the new PROPERTIES view after Obsidian Version 1.4
if ( scroll === 0 || true ) {
2023-06-29 11:55:02 +08:00
//force update scroll while note is loading
//todo: find better solution to wait for file loaded
for ( let i = 0 ; i < 20 ; i ++ ) {
this . setEphemeralState ( st ) ;
yield this . delay ( 10 ) ;
}
}
}
this . lastEphemeralState = st ;
}
this . loadingFile = false ;
}
} ) ;
}
readDb ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
let db = { } ;
if ( yield this . app . vault . adapter . exists ( this . settings . dbFileName ) ) {
let data = yield this . app . vault . adapter . read ( this . settings . dbFileName ) ;
db = JSON . parse ( data ) ;
}
return db ;
} ) ;
}
writeDb ( db ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
//create folder for db file if not exist
let newParentFolder = this . settings . dbFileName . substring ( 0 , this . settings . dbFileName . lastIndexOf ( "/" ) ) ;
if ( ! ( yield this . app . vault . adapter . exists ( newParentFolder ) ) )
this . app . vault . adapter . mkdir ( newParentFolder ) ;
2024-04-15 11:54:03 +08:00
if ( JSON . stringify ( this . db ) !== JSON . stringify ( this . lastSavedDb ) ) {
this . app . vault . adapter . write ( this . settings . dbFileName , JSON . stringify ( db ) ) ;
this . lastSavedDb = JSON . parse ( JSON . stringify ( db ) ) ;
}
2023-06-29 11:55:02 +08:00
} ) ;
}
getEphemeralState ( ) {
// let state: EphemeralState = this.app.workspace.getActiveViewOfType(MarkdownView)?.getEphemeralState(); //doesn't work properly
var _a , _b , _c ;
let state = { } ;
state . scroll = Number ( ( _c = ( _b = ( _a = this . app . workspace . getActiveViewOfType ( obsidian . MarkdownView ) ) === null || _a === void 0 ? void 0 : _a . currentMode ) === null || _b === void 0 ? void 0 : _b . getScroll ( ) ) === null || _c === void 0 ? void 0 : _c . toFixed ( 4 ) ) ;
let editor = this . getEditor ( ) ;
if ( editor ) {
let from = editor . getCursor ( "anchor" ) ;
let to = editor . getCursor ( "head" ) ;
if ( from && to ) {
state . cursor = {
from : {
ch : from . ch ,
line : from . line
} ,
to : {
ch : to . ch ,
line : to . line
}
} ;
}
}
return state ;
}
setEphemeralState ( state ) {
const view = this . app . workspace . getActiveViewOfType ( obsidian . MarkdownView ) ;
if ( state . cursor ) {
let editor = this . getEditor ( ) ;
if ( editor ) {
2024-04-15 11:54:03 +08:00
editor . setSelection ( state . cursor . from , state . cursor . to ) ;
2023-06-29 11:55:02 +08:00
}
}
if ( view && state . scroll ) {
view . setEphemeralState ( state ) ;
// view.previewMode.applyScroll(state.scroll);
// view.sourceMode.applyScroll(state.scroll);
}
}
getEditor ( ) {
var _a ;
2024-04-15 11:54:03 +08:00
return ( _a = this . app . workspace . getActiveViewOfType ( obsidian . MarkdownView ) ) === null || _a === void 0 ? void 0 : _a . editor ;
2023-06-29 11:55:02 +08:00
}
loadSettings ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
2024-04-15 11:54:03 +08:00
let settings = Object . assign ( { } , DEFAULT _SETTINGS , yield this . loadData ( ) ) ;
if ( ( settings === null || settings === void 0 ? void 0 : settings . saveTimer ) < SAFE _DB _FLUSH _INTERVAL ) {
settings . saveTimer = SAFE _DB _FLUSH _INTERVAL ;
}
this . settings = settings ;
2023-06-29 11:55:02 +08:00
} ) ;
}
saveSettings ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield this . saveData ( this . settings ) ;
} ) ;
}
delay ( ms ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
return new Promise ( resolve => setTimeout ( resolve , ms ) ) ;
} ) ;
}
}
class SettingTab extends obsidian . PluginSettingTab {
constructor ( app , plugin ) {
super ( app , plugin ) ;
this . plugin = plugin ;
}
display ( ) {
let { containerEl } = this ;
containerEl . empty ( ) ;
containerEl . createEl ( 'h2' , { text : 'Remember cursor position - Settings' } ) ;
new obsidian . Setting ( containerEl )
. setName ( 'Data file name' )
. setDesc ( 'Save positions to this file' )
2024-04-15 11:54:03 +08:00
. addText ( ( text ) => text
2023-06-29 11:55:02 +08:00
. setPlaceholder ( 'Example: cursor-positions.json' )
. setValue ( this . plugin . settings . dbFileName )
. onChange ( ( value ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . plugin . settings . dbFileName = value ;
yield this . plugin . saveSettings ( ) ;
} ) ) ) ;
new obsidian . Setting ( containerEl )
. setName ( 'Delay after opening a new note' )
2024-04-15 11:54:03 +08:00
. setDesc ( "This plugin shouldn't scroll if you used a link to the note header like [link](note.md#header). If it did, then increase the delay until everything works. If you are not using links to page sections, set the delay to zero (slider to the left). Slider values: 0-300 ms (default value: 100 ms)." )
. addSlider ( ( text ) => text
2023-06-29 11:55:02 +08:00
. setLimits ( 0 , 300 , 10 )
. setValue ( this . plugin . settings . delayAfterFileOpening )
. onChange ( ( value ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . plugin . settings . delayAfterFileOpening = value ;
yield this . plugin . saveSettings ( ) ;
} ) ) ) ;
2024-04-15 11:54:03 +08:00
new obsidian . Setting ( containerEl )
. setName ( 'Delay between saving the cursor position to file' )
. setDesc ( "Useful for multi-device users. If you don't want to wait until closing Obsidian to the cursor position been saved." )
. addSlider ( ( text ) => text
. setLimits ( SAFE _DB _FLUSH _INTERVAL , SAFE _DB _FLUSH _INTERVAL * 10 , 10 )
. setValue ( this . plugin . settings . saveTimer )
. onChange ( ( value ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . plugin . settings . saveTimer = value ;
yield this . plugin . saveSettings ( ) ;
} ) ) ) ;
2023-06-29 11:55:02 +08:00
}
}
module . exports = RememberCursorPosition ;
2024-04-15 11:54:03 +08:00
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXMiOlsibm9kZV9tb2R1bGVzL3RzbGliL3RzbGliLmVzNi5qcyIsIm1haW4udHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5Db3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi5cclxuXHJcblBlcm1pc3Npb24gdG8gdXNlLCBjb3B5LCBtb2RpZnksIGFuZC9vciBkaXN0cmlidXRlIHRoaXMgc29mdHdhcmUgZm9yIGFueVxyXG5wdXJwb3NlIHdpdGggb3Igd2l0aG91dCBmZWUgaXMgaGVyZWJ5IGdyYW50ZWQuXHJcblxyXG5USEUgU09GVFdBUkUgSVMgUFJPVklERUQgXCJBUyBJU1wiIEFORCBUSEUgQVVUSE9SIERJU0NMQUlNUyBBTEwgV0FSUkFOVElFUyBXSVRIXHJcblJFR0FSRCBUTyBUSElTIFNPRlRXQVJFIElOQ0xVRElORyBBTEwgSU1QTElFRCBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWVxyXG5BTkQgRklUTkVTUy4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFIEFVVEhPUiBCRSBMSUFCTEUgRk9SIEFOWSBTUEVDSUFMLCBESVJFQ1QsXHJcbklORElSRUNULCBPUiBDT05TRVFVRU5USUFMIERBTUFHRVMgT1IgQU5ZIERBTUFHRVMgV0hBVFNPRVZFUiBSRVNVTFRJTkcgRlJPTVxyXG5MT1NTIE9GIFVTRSwgREFUQSBPUiBQUk9GSVRTLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgTkVHTElHRU5DRSBPUlxyXG5PVEhFUiBUT1JUSU9VUyBBQ1RJT04sIEFSSVNJTkcgT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgVVNFIE9SXHJcblBFUkZPUk1BTkNFIE9GIFRISVMgU09GVFdBUkUuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqICovXHJcbi8qIGdsb2JhbCBSZWZsZWN0LCBQcm9taXNlLCBTdXBwcmVzc2VkRXJyb3IsIFN5bWJvbCAqL1xyXG5cclxudmFyIGV4dGVuZFN0YXRpY3MgPSBmdW5jdGlvbihkLCBiKSB7XHJcbiAgICBleHRlbmRTdGF0aWNzID0gT2JqZWN0LnNldFByb3RvdHlwZU9mIHx8XHJcbiAgICAgICAgKHsgX19wcm90b19fOiBbXSB9IGluc3RhbmNlb2YgQXJyYXkgJiYgZnVuY3Rpb24gKGQsIGIpIHsgZC5fX3Byb3RvX18gPSBiOyB9KSB8fFxyXG4gICAgICAgIGZ1bmN0aW9uIChkLCBiKSB7IGZvciAodmFyIHAgaW4gYikgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChiLCBwKSkgZFtwXSA9IGJbcF07IH07XHJcbiAgICByZXR1cm4gZXh0ZW5kU3RhdGljcyhkLCBiKTtcclxufTtcclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2V4dGVuZHMoZCwgYikge1xyXG4gICAgaWYgKHR5cGVvZiBiICE9PSBcImZ1bmN0aW9uXCIgJiYgYiAhPT0gbnVsbClcclxuICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKFwiQ2xhc3MgZXh0ZW5kcyB2YWx1ZSBcIiArIFN0cmluZyhiKSArIFwiIGlzIG5vdCBhIGNvbnN0cnVjdG9yIG9yIG51bGxcIik7XHJcbiAgICBleHRlbmRTdGF0aWNzKGQsIGIpO1xyXG4gICAgZnVuY3Rpb24gX18oKSB7IHRoaXMuY29uc3RydWN0b3IgPSBkOyB9XHJcbiAgICBkLnByb3RvdHlwZSA9IGIgPT09IG51bGwgPyBPYmplY3QuY3JlYXRlKGIpIDogKF9fLnByb3RvdHlwZSA9IGIucHJvdG90eXBlLCBuZXcgX18oKSk7XHJcbn1cclxuXHJcbmV4cG9ydCB2YXIgX19hc3NpZ24gPSBmdW5jdGlvbigpIHtcclxuICAgIF9fYXNzaWduID0gT2JqZWN0LmFzc2lnbiB8fCBmdW5jdGlvbiBfX2Fzc2lnbih0KSB7XHJcbiAgICAgICAgZm9yICh2YXIgcywgaSA9IDEsIG4gPSBhcmd1bWVudHMubGVuZ3RoOyBpIDwgbjsgaSsrKSB7XHJcbiAgICAgICAgICAgIHMgPSBhcmd1bWVudHNbaV07XHJcbiAgICAgICAgICAgIGZvciAodmFyIHAgaW4gcykgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChzLCBwKSkgdFtwXSA9IHNbcF07XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiB0O1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIF9fYXNzaWduLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX3Jlc3QocywgZSkge1xyXG4gICAgdmFyIHQgPSB7fTtcclxuICAgIGZvciAodmFyIHAgaW4gcykgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChzLCBwKSAmJiBlLmluZGV4T2YocCkgPCAwKVxyXG4gICAgICAgIHRbcF0gPSBzW3BdO1xyXG4gICAgaWYgKHMgIT0gbnVsbCAmJiB0eXBlb2YgT2JqZWN0LmdldE93blByb3BlcnR5U3ltYm9scyA9PT0gXCJmdW5jdGlvblwiKVxyXG4gICAgICAgIGZvciAodmFyIGkgPSAwLCBwID0gT2JqZWN0LmdldE93blByb3BlcnR5U3ltYm9scyhzKTsgaSA8IHAubGVuZ3RoOyBpKyspIHtcclxuICAgICAgICAgICAgaWYgKGUuaW5kZXhPZihwW2ldKSA8IDAgJiYgT2JqZWN0LnByb3RvdHlwZS5wcm9wZXJ0eUlzRW51bWVyYWJsZS5jYWxsKHMsIHBbaV0pKVxyXG4gICAgICAgICAgICAgICAgdFtwW2ldXSA9IHNbcFtpXV07XHJcbiAgICAgICAgfVxyXG4gICAgcmV0dXJuIHQ7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2RlY29yYXRlKGRlY29yYXRvcnMsIHRhcmdldCwga2V5LCBkZXNjKSB7XHJcbiAgICB2YXIgYyA9IGFyZ3VtZW50cy5sZW5ndGgsIHIgPSBjIDwgMyA/IHRhcmdldCA6IGRlc2MgPT09IG51bGwgPyBkZXNjID0gT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcih0YXJnZXQsIGtleSkgOiBkZXNjLCBkO1xyXG4gICAgaWYgKHR5cGVvZiBSZWZsZWN0ID09PSBcIm9iamVjdFwiICYmIHR5cGVvZiBSZWZsZWN0LmRlY29yYXRlID09PSBcImZ1bmN0aW9uXCIpIHIgPSBSZWZsZWN0LmRlY29yYXRlKGRlY29yYXRvcnMsIHRhcmdldCwga2V5LCBkZXNjKTtcclxuICAgIGVsc2UgZm9yICh2YXIgaSA9IGRlY29yYXRvcnMubGVuZ3RoIC0gMTsgaSA+PSAwOyBpLS0pIGlmIChkID0gZGVjb3JhdG9