diff --git a/main.js b/main.js index 107b8eb..4142008 100644 --- a/main.js +++ b/main.js @@ -43,7 +43,7 @@ Click to switch / create folder.`,l.onclick=u=>{u.preventDefault(),this.plugin.o `):e.slice(0,n).map(r=>`\u2022 ${r}`).join(` `)+` \u2026+ ${e.length-n} more`;return`${t.verb} ${e.length} notes${i}${s}: -${o}`}titleList(t,e=3){if(!t.length)return"";let i=t.map(n=>this.titleForNode(n).trim()||"(untitled)");return i.length<=e?i.map(n=>`"${n}"`).join(", "):`${i.slice(0,e).map(n=>`"${n}"`).join(", ")}, +${i.length-e} more`}titleForNode(t){if(!t.file)return"Untitled";let i=this.app.metadataCache.getFileCache(t.file)?.headings?.[0]?.heading;return i||t.file.basename.replace(/-[a-z0-9]{4,12}$/,"").replace(/-/g," ")||"Untitled"}async forceManualMode(t){let e=this.noteFolder;this.sortStore.getMode(e,t)!=="manual"&&(this.sortStore.setMode(e,t,"manual"),await this.sortStore.save(e))}renderNote(t,e,i){if(!e.file||this.currentEncryptionFilter()==="locked")return;let s=e.file,n=this.tree.getChildren(e.id).length,o=this.selection.has(e.id),r=i===this.cursorIdx,A=this.inListPicker?.activeIdx===i,l=t.createDiv({cls:"stashpad-note"});o&&l.addClass("is-selected"),r&&l.addClass("is-cursor"),r&&this.plugin.settings.autoExpandCursorRow&&l.addClass("is-cursor-expanded"),A&&l.addClass("is-pick-target"),this.isCompleted(e)&&l.addClass("is-completed"),this.isCutPending(e.id)?l.addClass("is-cut-pending"):this.isCopyPending(e.id)&&l.addClass("is-copy-pending"),l.dataset.idx=String(i),l.dataset.id=e.id;let d=this.currentViewMode()==="nested";l.draggable=d,d&&this.dnd.attachRowDnD(l,e,i),l.addEventListener("click",Q=>this.handleRowClick(Q,i,e)),l.addEventListener("dblclick",Q=>{!this.plugin.settings.doubleClickToFocus||Q.target?.closest?.(".internal-link, .tag, a, .stashpad-note-task-checkbox")||(Q.preventDefault(),window.getSelection()?.removeAllRanges(),this.navigateTo(e.id))});let g=this.isTask(e)||this.compactMode;g&&(l.addClass("is-task"),S.Platform.isMobile||this.addTaskCheckbox(l,e));let p=l.createDiv({cls:"stashpad-note-meta"}),h=p.createDiv({cls:"stashpad-note-meta-top"});h.createSpan({cls:"stashpad-note-time",text:this.formatTime(e.created)});let I=this.colorForNode(e),u=h.createDiv({cls:"stashpad-note-grip"});I&&u.addClass("has-color"),(0,S.setIcon)(u,"grip-vertical"),u.title=I?"Drag to reorder \xB7 right-click to change color":"Drag to reorder",u.draggable=d,d||(u.title=I?"Right-click to change color \xB7 drag disabled in this view mode":"Drag disabled in this view mode"),I&&u.style.setProperty("--stashpad-note-color",I);let f=g&&S.Platform.isMobile;if(n>0||f){let Q=p.createDiv({cls:"stashpad-note-meta-bottom"});if(f&&this.addTaskCheckbox(Q,e),n>0){let w=Q.createSpan({cls:"stashpad-note-enter"});I&&(w.style.color=I),(0,S.setIcon)(w.createSpan({cls:"stashpad-btn-icon"}),"corner-down-right"),w.createSpan({text:` ${n}`}),w.onclick=v=>{v.stopPropagation(),this.navigateTo(e.id)}}}if(I)l.addClass("has-color"),l.style.setProperty("--stashpad-note-color",I);else{let Q=this.inheritedColorForNode(e);Q&&Q.depth>0&&(l.addClass("has-inherited-color"),l.style.setProperty("--stashpad-inherited-color",Q.hex),l.style.setProperty("--stashpad-inherited-depth",String(Q.depth)))}let C=l.createDiv({cls:"stashpad-note-body"});this.currentViewMode()!=="nested"&&this.renderRowBreadcrumb(C,e);let m=C.createDiv({cls:"stashpad-note-body-content"}),B=l.createDiv({cls:"stashpad-note-actions"}),E;if(S.Platform.isMobile){let Q=B.createEl("button",{cls:"stashpad-pencil stashpad-note-more"});(0,S.setIcon)(Q,"ellipsis-vertical"),Q.title="Actions",Q.onclick=w=>{w.stopPropagation(),this.openNoteMenu(w,e)},E=Q}else{let Q=B.createEl("button",{cls:"stashpad-pencil"});(0,S.setIcon)(Q,"pencil"),Q.title="Edit in new tab",Q.onclick=v=>{v.stopPropagation(),this.openFileAtEnd(s)};let w=B.createEl("button",{cls:"stashpad-pencil stashpad-enter-btn"});(0,S.setIcon)(w,"arrow-right"),w.title="Open in Stashpad view",w.onclick=v=>{v.stopPropagation(),this.navigateTo(e.id)},E=Q}this.renderNoteBody(m,e,{clamp:!0,toggleHost:B,toggleAnchor:E}),l.oncontextmenu=Q=>{Q.preventDefault(),this.openNoteMenu(Q,e)}}addTaskCheckbox(t,e){let i=t.createSpan({cls:"stashpad-note-task-checkbox"}),s=this.isCompleted(e);(0,S.setIcon)(i,s?"check-square":"square"),i.title=s?"Mark not done":"Mark done",i.addEventListener("mousedown",n=>n.stopPropagation()),i.addEventListener("dblclick",n=>{n.preventDefault(),n.stopPropagation()}),i.onclick=n=>{n.preventDefault(),n.stopPropagation(),this.toggleCompletedForNode(e)}}renderNoteBody(t,e,i={clamp:!0}){if(!e.file)return;if(this.bodyRenderer.hasFreshRenderCache(e.file)||!this.bodyRenderer.isArmed()){this.renderNoteBodyNow(t,e,i);return}t.empty();let s=t.createDiv({cls:"stashpad-note-text is-plain is-lazy-placeholder"});s.textContent=this.titleForNode(e),this.bodyRenderer.defer(t,()=>this.renderNoteBodyNow(t,e,i))}renderNoteBodyNow(t,e,i={clamp:!0}){if(!e.file)return;let s=e.file,n=(t.__stashpadRenderToken??0)+1;t.__stashpadRenderToken=n,this.bodyRenderer.getOrComputeRender(s).then(o=>{if(t.__stashpadRenderToken!==n)return;let{text:r,attachments:A,html:l}=o;t.empty();let d=t.createDiv({cls:"stashpad-note-text"}),g=this.expandedNotes.has(e.id);if(i.clamp&&!g&&d.addClass("is-clamped"),this.compactMode||this.tinyMode?(d.addClass("is-plain"),d.textContent=r):d.append(document.createRange().createContextualFragment(l)),A.length>0&&this.renderAttachmentRail(t,A),this.renderAuthorshipFooter(t,e),!i.clamp)return;let p=this.lastListWidth;if(o.ovW===p&&o.ovV!==void 0&&!g){o.ovV?this.attachExpandToggle(i,t,e,g):d.removeClass("is-clamped");return}requestAnimationFrame(()=>{if(t.closest?.(".stashpad-note.is-cursor-expanded"))return;let h=d.scrollHeight>d.clientHeight+4;if(o.ovW=p,o.ovV=h,!h&&!g){d.removeClass("is-clamped");return}this.attachExpandToggle(i,t,e,g)})})}attachExpandToggle(t,e,i,s){let n=!!t.toggleHost,o=t.toggleHost??e;o.querySelector(".stashpad-expand-toggle")?.remove();let r=o.createEl("button",{cls:"stashpad-expand-toggle"});r.title=s?"Show less":"Show more",n||S.Platform.isMobile?((0,S.setIcon)(r,s?"chevron-up":"chevron-down"),r.addClass("is-icon"),n&&r.addClass("is-inline")):r.setText(s?"Show less":"Show more"),t.toggleAnchor&&t.toggleAnchor.parentElement===o&&o.insertBefore(r,t.toggleAnchor),r.onclick=A=>{A.stopPropagation(),this.expandedNotes.has(i.id)?this.expandedNotes.delete(i.id):this.expandedNotes.add(i.id),e.empty(),this.renderNoteBody(e,i,t)}}renderAttachmentRail(t,e){let i=t.createDiv({cls:"stashpad-rail"});for(let s of e){let n=this.app.metadataCache.getFirstLinkpathDest(s,""),o=(s.split(".").pop()??"").toLowerCase(),r=i.createDiv({cls:"stashpad-att"});if(r.title=s,n&&GA.has(o)){let A=r.createEl("img",{cls:"stashpad-att-img"});A.src=this.app.vault.getResourcePath(n),A.alt=s}else{r.createDiv({cls:"stashpad-att-ext",text:o.toUpperCase()||"?"});let A=(s.split("/").pop()??s).replace(/\.[^.]+$/,"");r.createDiv({cls:"stashpad-att-name",text:A})}r.onclick=A=>{A.stopPropagation(),n&&this.app.workspace.getLeaf("tab").openFile(n)}}}renderComposer(t){let e=_(),i=this.modeEnterSubmits,s=this.modeSplit??e.splitOnLines,n=t.createDiv({cls:"stashpad-composer"}),r=n.createDiv({cls:"stashpad-composer-input-wrap"}).createEl("textarea",{cls:"stashpad-composer-input",attr:{rows:"2",placeholder:this.composerPlaceholder(i,s)}});r.value=this.composerDraft,this.debouncedSaveDraft||(this.debouncedSaveDraft=(0,S.debounce)(k=>{this.saveDraft(k)},250)),r.addEventListener("input",()=>{this.composerDraft=r.value,this.debouncedSaveDraft(r.value)}),r.addEventListener("blur",()=>{this.saveDraft(r.value)});let A=null,l=()=>{A||(A=new S.Scope(this.app.scope),A.register([],"Escape",()=>(this.composerExitAt=Date.now(),r.blur(),this.viewRoot?.focus({preventScroll:!0}),!1)),A.register(["Mod"],"z",()=>!0),A.register(["Mod","Shift"],"z",()=>!0),this.app.keymap?.pushScope(A))},d=()=>{if(A){try{this.app.keymap?.popScope(A)}catch{}A=null}};if(r.addEventListener("focus",l),r.addEventListener("blur",d),document.activeElement===r&&l(),S.Platform.isMobile){let k=()=>{this.keyboardTransitionUntil=Date.now()+600};r.addEventListener("focus",()=>{document.body.classList.add("stashpad-keyboard-open"),k()}),r.addEventListener("blur",()=>{document.body.classList.remove("stashpad-keyboard-open"),k(),S.Platform.isMobile&&this.cursorIdx>=0&&setTimeout(()=>this.revealCursorRow(),350)})}this.composerInputEl=r,this.composerAutocomplete&&this.composerAutocomplete.detach(),this.composerAutocomplete=new Je(this.app,r),this.composerAutocomplete.attach();let g=async k=>{let H="";for(let D of k){let T=await this.importAttachment(D);if(!T)continue;let U=r.value+H,q=U&&!U.endsWith(` +${o}`}titleList(t,e=3){if(!t.length)return"";let i=t.map(n=>this.titleForNode(n).trim()||"(untitled)");return i.length<=e?i.map(n=>`"${n}"`).join(", "):`${i.slice(0,e).map(n=>`"${n}"`).join(", ")}, +${i.length-e} more`}titleForNode(t){if(!t.file)return"Untitled";let i=this.app.metadataCache.getFileCache(t.file)?.headings?.[0]?.heading;return i||t.file.basename.replace(/-[a-z0-9]{4,12}$/,"").replace(/-/g," ")||"Untitled"}async forceManualMode(t){let e=this.noteFolder;this.sortStore.getMode(e,t)!=="manual"&&(this.sortStore.setMode(e,t,"manual"),await this.sortStore.save(e))}renderNote(t,e,i){if(!e.file||this.currentEncryptionFilter()==="locked")return;let s=e.file,n=this.tree.getChildren(e.id).length,o=this.selection.has(e.id),r=i===this.cursorIdx,A=this.inListPicker?.activeIdx===i,l=t.createDiv({cls:"stashpad-note"});o&&l.addClass("is-selected"),r&&l.addClass("is-cursor"),r&&this.plugin.settings.autoExpandCursorRow&&l.addClass("is-cursor-expanded"),A&&l.addClass("is-pick-target"),this.isCompleted(e)&&l.addClass("is-completed"),this.isCutPending(e.id)?l.addClass("is-cut-pending"):this.isCopyPending(e.id)&&l.addClass("is-copy-pending"),l.dataset.idx=String(i),l.dataset.id=e.id;let d=this.currentViewMode()==="nested";l.draggable=d,d&&this.dnd.attachRowDnD(l,e,i),l.addEventListener("click",Q=>this.handleRowClick(Q,i,e)),l.addEventListener("dblclick",Q=>{!this.plugin.settings.doubleClickToFocus||Q.target?.closest?.(".internal-link, .tag, a, .stashpad-note-task-checkbox")||(Q.preventDefault(),window.getSelection()?.removeAllRanges(),this.navigateTo(e.id))});let g=this.isTask(e)||this.compactMode;g&&(l.addClass("is-task"),S.Platform.isMobile||this.addTaskCheckbox(l,e));let p=l.createDiv({cls:"stashpad-note-meta"}),h=p.createDiv({cls:"stashpad-note-meta-top"});h.createSpan({cls:"stashpad-note-time",text:this.formatTime(e.created)});let I=this.colorForNode(e),u=h.createDiv({cls:"stashpad-note-grip"});I&&u.addClass("has-color"),(0,S.setIcon)(u,"grip-vertical"),u.title=I?"Drag to reorder \xB7 right-click to change color":"Drag to reorder",u.draggable=d,d||(u.title=I?"Right-click to change color \xB7 drag disabled in this view mode":"Drag disabled in this view mode"),I&&u.style.setProperty("--stashpad-note-color",I);let f=g&&S.Platform.isMobile;if(n>0||f){let Q=p.createDiv({cls:"stashpad-note-meta-bottom"});if(f&&this.addTaskCheckbox(Q,e),n>0){let w=Q.createSpan({cls:"stashpad-note-enter"});I&&(w.style.color=I),(0,S.setIcon)(w.createSpan({cls:"stashpad-btn-icon"}),"corner-down-right"),w.createSpan({text:` ${n}`}),w.onclick=v=>{v.stopPropagation(),this.navigateTo(e.id)}}}if(I)l.addClass("has-color"),l.style.setProperty("--stashpad-note-color",I);else{let Q=this.inheritedColorForNode(e);Q&&Q.depth>0&&(l.addClass("has-inherited-color"),l.style.setProperty("--stashpad-inherited-color",Q.hex),l.style.setProperty("--stashpad-inherited-depth",String(Q.depth)))}let C=l.createDiv({cls:"stashpad-note-body"});this.currentViewMode()!=="nested"&&this.renderRowBreadcrumb(C,e);let m=C.createDiv({cls:"stashpad-note-body-content"}),B=l.createDiv({cls:"stashpad-note-actions"}),E;if(S.Platform.isMobile){let Q=B.createEl("button",{cls:"stashpad-pencil stashpad-note-more"});(0,S.setIcon)(Q,"ellipsis-vertical"),Q.title="Actions",Q.onclick=w=>{w.stopPropagation(),this.openNoteMenu(w,e)},E=Q}else{let Q=B.createEl("button",{cls:"stashpad-pencil"});(0,S.setIcon)(Q,"pencil"),Q.title="Edit in new tab",Q.onclick=v=>{v.stopPropagation(),this.openFileAtEnd(s)};let w=B.createEl("button",{cls:"stashpad-pencil stashpad-enter-btn"});(0,S.setIcon)(w,"arrow-right"),w.title="Open in Stashpad view",w.onclick=v=>{v.stopPropagation(),this.navigateTo(e.id)},E=Q}this.renderNoteBody(m,e,{clamp:!0,toggleHost:B,toggleAnchor:E}),l.oncontextmenu=Q=>{Q.preventDefault(),this.openNoteMenu(Q,e)}}addTaskCheckbox(t,e){let i=t.createSpan({cls:"stashpad-note-task-checkbox"}),s=this.isCompleted(e);(0,S.setIcon)(i,s?"check-square":"square"),i.title=s?"Mark not done":"Mark done",i.addEventListener("mousedown",n=>n.stopPropagation()),i.addEventListener("dblclick",n=>{n.preventDefault(),n.stopPropagation()}),i.onclick=n=>{n.preventDefault(),n.stopPropagation(),this.toggleCompletedForNode(e)}}renderNoteBody(t,e,i={clamp:!0}){if(!e.file)return;if(this.bodyRenderer.hasFreshRenderCache(e.file)||!this.bodyRenderer.isArmed()){this.renderNoteBodyNow(t,e,i);return}t.empty();let s=t.createDiv({cls:"stashpad-note-text is-plain is-lazy-placeholder"});s.textContent=this.titleForNode(e),this.bodyRenderer.defer(t,()=>this.renderNoteBodyNow(t,e,i))}renderNoteBodyNow(t,e,i={clamp:!0}){if(!e.file)return;let s=e.file,n=(t.__stashpadRenderToken??0)+1;t.__stashpadRenderToken=n,this.bodyRenderer.getOrComputeRender(s).then(o=>{if(t.__stashpadRenderToken!==n)return;let{text:r,attachments:A,html:l}=o;t.empty();let d=t.createDiv({cls:"stashpad-note-text"}),g=this.expandedNotes.has(e.id);if(i.clamp&&!g&&d.addClass("is-clamped"),this.compactMode||this.tinyMode?(d.addClass("is-plain"),d.textContent=r):d.append((0,S.sanitizeHTMLToDom)(l)),A.length>0&&this.renderAttachmentRail(t,A),this.renderAuthorshipFooter(t,e),!i.clamp)return;let p=this.lastListWidth;if(o.ovW===p&&o.ovV!==void 0&&!g){o.ovV?this.attachExpandToggle(i,t,e,g):d.removeClass("is-clamped");return}requestAnimationFrame(()=>{if(t.closest?.(".stashpad-note.is-cursor-expanded"))return;let h=d.scrollHeight>d.clientHeight+4;if(o.ovW=p,o.ovV=h,!h&&!g){d.removeClass("is-clamped");return}this.attachExpandToggle(i,t,e,g)})})}attachExpandToggle(t,e,i,s){let n=!!t.toggleHost,o=t.toggleHost??e;o.querySelector(".stashpad-expand-toggle")?.remove();let r=o.createEl("button",{cls:"stashpad-expand-toggle"});r.title=s?"Show less":"Show more",n||S.Platform.isMobile?((0,S.setIcon)(r,s?"chevron-up":"chevron-down"),r.addClass("is-icon"),n&&r.addClass("is-inline")):r.setText(s?"Show less":"Show more"),t.toggleAnchor&&t.toggleAnchor.parentElement===o&&o.insertBefore(r,t.toggleAnchor),r.onclick=A=>{A.stopPropagation(),this.expandedNotes.has(i.id)?this.expandedNotes.delete(i.id):this.expandedNotes.add(i.id),e.empty(),this.renderNoteBody(e,i,t)}}renderAttachmentRail(t,e){let i=t.createDiv({cls:"stashpad-rail"});for(let s of e){let n=this.app.metadataCache.getFirstLinkpathDest(s,""),o=(s.split(".").pop()??"").toLowerCase(),r=i.createDiv({cls:"stashpad-att"});if(r.title=s,n&&GA.has(o)){let A=r.createEl("img",{cls:"stashpad-att-img"});A.src=this.app.vault.getResourcePath(n),A.alt=s}else{r.createDiv({cls:"stashpad-att-ext",text:o.toUpperCase()||"?"});let A=(s.split("/").pop()??s).replace(/\.[^.]+$/,"");r.createDiv({cls:"stashpad-att-name",text:A})}r.onclick=A=>{A.stopPropagation(),n&&this.app.workspace.getLeaf("tab").openFile(n)}}}renderComposer(t){let e=_(),i=this.modeEnterSubmits,s=this.modeSplit??e.splitOnLines,n=t.createDiv({cls:"stashpad-composer"}),r=n.createDiv({cls:"stashpad-composer-input-wrap"}).createEl("textarea",{cls:"stashpad-composer-input",attr:{rows:"2",placeholder:this.composerPlaceholder(i,s)}});r.value=this.composerDraft,this.debouncedSaveDraft||(this.debouncedSaveDraft=(0,S.debounce)(k=>{this.saveDraft(k)},250)),r.addEventListener("input",()=>{this.composerDraft=r.value,this.debouncedSaveDraft(r.value)}),r.addEventListener("blur",()=>{this.saveDraft(r.value)});let A=null,l=()=>{A||(A=new S.Scope(this.app.scope),A.register([],"Escape",()=>(this.composerExitAt=Date.now(),r.blur(),this.viewRoot?.focus({preventScroll:!0}),!1)),A.register(["Mod"],"z",()=>!0),A.register(["Mod","Shift"],"z",()=>!0),this.app.keymap?.pushScope(A))},d=()=>{if(A){try{this.app.keymap?.popScope(A)}catch{}A=null}};if(r.addEventListener("focus",l),r.addEventListener("blur",d),document.activeElement===r&&l(),S.Platform.isMobile){let k=()=>{this.keyboardTransitionUntil=Date.now()+600};r.addEventListener("focus",()=>{document.body.classList.add("stashpad-keyboard-open"),k()}),r.addEventListener("blur",()=>{document.body.classList.remove("stashpad-keyboard-open"),k(),S.Platform.isMobile&&this.cursorIdx>=0&&setTimeout(()=>this.revealCursorRow(),350)})}this.composerInputEl=r,this.composerAutocomplete&&this.composerAutocomplete.detach(),this.composerAutocomplete=new Je(this.app,r),this.composerAutocomplete.attach();let g=async k=>{let H="";for(let D of k){let T=await this.importAttachment(D);if(!T)continue;let U=r.value+H,q=U&&!U.endsWith(` `)?` `:"";H+=`${q}${T} `}H&&(r.value=r.value+H,this.composerDraft=r.value,this.saveDraft(r.value),r.focus(),r.setSelectionRange(r.value.length,r.value.length))};r.addEventListener("dragover",k=>{if(!(!k.dataTransfer||!Array.from(k.dataTransfer.types).includes("Files"))){k.preventDefault(),k.stopPropagation();try{k.dataTransfer.dropEffect="copy"}catch{}}}),r.addEventListener("drop",k=>{let H=Array.from(k.dataTransfer?.files??[]);H.length!==0&&(k.preventDefault(),k.stopPropagation(),g(H))}),r.addEventListener("paste",k=>{let H=this.plugin.noteClipboard;if(H?.mode==="cut"&&H.text&&k.clipboardData?.getData("text/plain")===H.text){if(this.focusedInsideCut(H.ids)){k.preventDefault(),new S.Notice("Can't paste a cut note into the note you're cutting.");return}k.preventDefault(),this.completeCutIntoComposer();return}let D=[],T=k.clipboardData;if(T){for(let U of Array.from(T.files??[]))D.push(U);if(D.length===0){for(let U of Array.from(T.items??[]))if(U.kind==="file"){let q=U.getAsFile();q&&D.push(q)}}D.length!==0&&(k.preventDefault(),k.stopPropagation(),g(D))}});let p=n.createEl("input",{cls:"stashpad-composer-file-input",type:"file",attr:{multiple:"true"}});p.setCssStyles({display:"none"});let h=n.createDiv({cls:"stashpad-composer-btn-rail"}),I=h.createDiv({cls:"stashpad-composer-btn-group"}),u=I.createEl("button",{cls:"stashpad-composer-btn"});(0,S.setIcon)(u,"list-end"),u.title=s?"Split on newlines: ON (Mod+/)":"Split on newlines (Mod+/)",s&&u.addClass("is-active"),u.onmousedown=k=>k.preventDefault(),u.onclick=k=>{k.preventDefault(),this.toggleSplit()};let f=I.createEl("button",{cls:"stashpad-composer-btn"});(0,S.setIcon)(f,"map-pin"),this.nextDestination&&f.createSpan({text:` ${this.destinationLabel()}`,cls:"stashpad-btn-text"}),f.title="Set destination (Mod+D)",this.nextDestination&&f.addClass("is-active"),f.onmousedown=k=>k.preventDefault(),f.onclick=k=>{k.preventDefault();let H=document.activeElement===r;this.openDestinationPicker(H)};let C=I.createEl("button",{cls:"stashpad-composer-btn"});(0,S.setIcon)(C,i?"corner-down-left":"arrow-big-down-dash"),C.title=i?"Enter sends (click to switch to Shift+Enter)":"Shift+Enter sends (click to switch to Enter)",C.onmousedown=k=>k.preventDefault(),C.onclick=k=>{k.preventDefault(),this.modeEnterSubmits=!i,this.render(),this.composerInputEl?.focus()};let m=k=>{let H=r.value&&!r.value.endsWith(` @@ -73,7 +73,7 @@ Error: ${e.message}`,kind:"error",category:"system",duration:0,affectedPaths:[t] ${B}`):t=B}catch(m){console.warn("[Stashpad] template read failed",m)}}}let A=Et(t,this.activeStopwords()),l=wt(A,o),d=`${s}/${l}`,g=e??this.focusId,p=i.createdOverride??new Date().toISOString(),h=this.extractAttachments(t),I=this.authorship.currentAuthorLink();I&&this.authorship.ensureAuthorFile(I);let u=["---",`id: ${o}`,`parent: ${g}`,`created: ${p}`,`modified: ${p}`];if(I&&u.push(`author: "${I.link.replace(/"/g,'\\"')}"`),h.length>0){u.push("attachments:");for(let f of h)u.push(` - "${f.replace(/"/g,'\\"')}"`)}else u.push("attachments: []");u.push("---",t);try{let f=u.join(` `);this.plugin.importService.suppress(d,6e4),await Qt.timeAsync("write.createNote.file",()=>this.app.vault.create(d,f));try{let C=this.app.vault.getAbstractFileByPath(d);if(C&&C.extension==="md"){if(!n)this.tree.insertSynthetic({id:o,parent:g,children:[],file:C,created:p}),this.render(),this.fmSync.scheduleParentChange(o,null,g);else{this.render();let m=s.split("/").pop()||s,B=(t.split(` -`).find(E=>E.trim())??"note").trim().slice(0,60);this.plugin.notifications.show({message:`"${B}" landed in \`${m}\``,kind:"success",category:"create",duration:0,folder:s,affectedIds:[o],actions:[{label:"Jump to note",onClick:()=>{this.switchToFolderAndFocus(s,o)}}]})}if(r)try{await this.app.fileManager.processFrontMatter(C,m=>{for(let[B,E]of Object.entries(r))se.includes(B)||m[B]===void 0&&(m[B]=E)})}catch(m){console.warn("[Stashpad] template fm overlay failed",m)}}}catch{}if(await this.log.append({type:"create",id:o,payload:{path:d,parent:g}}),i.record!==!1){let C=t;this.plugin.getUndoStack(s).push({label:n?"Send note":"Create note",undo:async()=>{let m=this.app.vault.getAbstractFileByPath(d);if(m)try{await this.app.fileManager.trashFile(m)}catch{}if(this.composerDraft=C,this.saveDraft(C),this.recordLastSubmitted(""),this.composerInputEl){this.composerInputEl.value=C;let B=C.length;this.composerInputEl.setSelectionRange(B,B),this.composerInputEl.focus()}n||this.tree.rebuild(this.noteFolder),this.render()},redo:async()=>{await this.app.vault.adapter.exists(d)||await this.app.vault.create(d,f),this.composerDraft="",this.saveDraft(""),this.recordLastSubmitted(C),this.composerInputEl&&(this.composerInputEl.value=""),n||this.tree.rebuild(this.noteFolder),this.render()}})}return o}catch(f){return new S.Notice(`Stashpad: failed to create note (${f.message})`),null}}extractAttachments(t){let e=[],i=/!\[\[([^\]\|]+)(?:\|[^\]]+)?\]\]/g,s;for(;(s=i.exec(t))!==null;)e.push(s[1]);return e}async ensureFolder(t){if(await this.app.vault.adapter.exists(t)){let e=this.app.vault.getAbstractFileByPath(t);if(e&&!(e instanceof S.TFolder))throw new Error(`${t} exists and is not a folder`);return}try{await this.app.vault.createFolder(t)}catch(e){let i=e?.message??"";if(!/already exists/i.test(i))throw e}}async importAttachment(t){try{let e=await t.arrayBuffer(),i=`${this.noteFolder}/_attachments`;await this.ensureFolder(i);let s=t.name.replace(/[^\w.\-]+/g,"_"),n=Date.now().toString(36),o=`${i}/${n}-${s}`;return await this.app.vault.createBinary(o,e),await this.log.append({type:"attachment_add",id:F,payload:{path:o,name:t.name,size:t.size}}),this.plugin.notifications.show({message:`Attached ${t.name}`,kind:"success",category:"attachment",affectedPaths:[o],folder:this.noteFolder}),`![[${o}]]`}catch(e){return new S.Notice(`Stashpad: attachment failed (${e.message})`),null}}claimSelectedAsAuthor(){this.authorship.claimSelectedAsAuthor()}claimFolderAsAuthor(){this.authorship.claimFolderAsAuthor()}claimSelectedWithContributor(){this.authorship.claimSelectedWithContributor()}claimFolderWithContributor(){this.authorship.claimFolderWithContributor()}renderAuthorshipFooter(t,e){if(!e.file)return;let i=this.plugin.settings;if(!i.showAuthor&&!i.showContributors&&!i.showLastEdit)return;let s=this.app.metadataCache.getFileCache(e.file)?.frontmatter??{},n=typeof s.author=="string"?s.author:"",o=Array.isArray(s.contributors)?s.contributors.filter(I=>typeof I=="string"&&I.trim()!==""):[],r=typeof s.modified=="string"?s.modified:typeof s.created=="string"?s.created:"",A=i.showAuthor&&!!n,l=i.showContributors&&o.length>0,d=i.showLastEdit&&!!r;if(!A&&!l&&!d)return;let g=t.createDiv({cls:"stashpad-note-authorship"}),p=(I,u)=>{let f=u.replace(/^\[\[/,"").replace(/\]\]$/,""),C=f.indexOf("|"),m=C>=0?f.slice(0,C):f,B=C>=0?f.slice(C+1):f.split("/").pop()??f,E=I.createEl("a",{cls:"internal-link",text:B});E.setAttribute("data-href",m),E.setAttribute("href",m)},h=[];A&&h.push(I=>{I.createSpan({cls:"stashpad-authorship-label",text:"by "}),p(I,n)}),l&&h.push(I=>{I.createSpan({cls:"stashpad-authorship-label",text:"with "}),o.forEach((u,f)=>{f>0&&I.createSpan({text:", "}),p(I,u)})}),d&&h.push(I=>{I.createSpan({cls:"stashpad-authorship-label",text:"edited "}),I.createSpan({text:this.formatTimeInline(r)})}),h.forEach((I,u)=>{u>0&&g.createSpan({cls:"stashpad-authorship-sep",text:"\xB7"});let f=g.createSpan({cls:"stashpad-authorship-piece"});I(f)}),g.addEventListener("click",I=>this.handleRenderedClick(I,e))}activeStopwords(){return this.plugin.settings.slugStopWords??Rt}scheduleSlugRename(t){let e=this.slugDebouncers.get(t.path);e&&e.cancel(),e=(0,S.debounce)(()=>void this.maybeRenameForSlug(t),3e4),this.slugDebouncers.set(t.path,e),e()}async maybeRenameForSlug(t){let e=ve(t.basename);if(!e||e===F)return;let i=await this.app.vault.cachedRead(t),s=this.stripFrontmatter(i),n=Et(s,this.activeStopwords()),o=wt(n,e);if(t.name===o)return;let r=t.parent?`${t.parent.path}/${o}`:o;if(this.app.vault.getAbstractFileByPath(r))return;let A=t.path;try{await this.app.fileManager.renameFile(t,r),await this.log.append({type:"rename",id:e,payload:{from:A,to:r}})}catch{}}scheduleAttachmentSync(t){let e=this.attachmentDebouncers.get(t.path);e&&e.cancel(),e=(0,S.debounce)(()=>void this.syncAttachmentsFrontmatter(t),1500),this.attachmentDebouncers.set(t.path,e),e()}async syncAttachmentsFrontmatter(t){let e=await this.app.vault.cachedRead(t),i=this.stripFrontmatter(e),s=this.extractAttachments(i),n=this.app.metadataCache.getFileCache(t)?.frontmatter,r=(Array.isArray(n?.attachments)?n.attachments:[]).filter(d=>typeof d=="string").map(d=>Be(d));if(r.length===s.length&&r.every((d,g)=>d===s[g]))return;let l=s.map(d=>Gt(d));await this.app.fileManager.processFrontMatter(t,d=>{d.attachments=l})}stripFrontmatter(t){let e=t.replace(/^/,""),i=e.match(/^\s*---\s*\r?\n[\s\S]*?\r?\n---\s*(?:\r?\n|$)/);return i?e.slice(i[0].length):e}formatTime(t){if(!t)return"";let e=(0,S.moment)(t);if(!e.isValid())return"";if(_().useTemplatesFormat){let s=ps(this.app);if(s)return`${e.format(s.dateFormat)} +`).find(E=>E.trim())??"note").trim().slice(0,60);this.plugin.notifications.show({message:`"${B}" landed in \`${m}\``,kind:"success",category:"create",duration:0,folder:s,affectedIds:[o],actions:[{label:"Jump to note",onClick:()=>{this.switchToFolderAndFocus(s,o)}}]})}if(r)try{await this.app.fileManager.processFrontMatter(C,m=>{for(let[B,E]of Object.entries(r))se.includes(B)||m[B]===void 0&&(m[B]=E)})}catch(m){console.warn("[Stashpad] template fm overlay failed",m)}}}catch{}if(await this.log.append({type:"create",id:o,payload:{path:d,parent:g}}),i.record!==!1){let C=t;this.plugin.getUndoStack(s).push({label:n?"Send note":"Create note",undo:async()=>{let m=this.app.vault.getAbstractFileByPath(d);if(m)try{await this.app.fileManager.trashFile(m)}catch{}if(this.composerDraft=C,this.saveDraft(C),this.recordLastSubmitted(""),this.composerInputEl){this.composerInputEl.value=C;let B=C.length;this.composerInputEl.setSelectionRange(B,B),this.composerInputEl.focus()}n||this.tree.rebuild(this.noteFolder),this.render()},redo:async()=>{await this.app.vault.adapter.exists(d)||await this.app.vault.create(d,f),this.composerDraft="",this.saveDraft(""),this.recordLastSubmitted(C),this.composerInputEl&&(this.composerInputEl.value=""),n||this.tree.rebuild(this.noteFolder),this.render()}})}return o}catch(f){return new S.Notice(`Stashpad: failed to create note (${f.message})`),null}}extractAttachments(t){let e=[],i=/!\[\[([^\]\|]+)(?:\|[^\]]+)?\]\]/g,s;for(;(s=i.exec(t))!==null;)e.push(s[1]);return e}async ensureFolder(t){if(await this.app.vault.adapter.exists(t)){let e=this.app.vault.getAbstractFileByPath(t);if(e&&!(e instanceof S.TFolder))throw new Error(`${t} exists and is not a folder`);return}try{await this.app.vault.createFolder(t)}catch(e){let i=e?.message??"";if(!/already exists/i.test(i))throw e}}async importAttachment(t){try{let e=await t.arrayBuffer(),i=`${this.noteFolder}/_attachments`;await this.ensureFolder(i);let s=t.name.replace(/[^\w.\-]+/g,"_"),n=Date.now().toString(36),o=`${i}/${n}-${s}`;return await this.app.vault.createBinary(o,e),await this.log.append({type:"attachment_add",id:F,payload:{path:o,name:t.name,size:t.size}}),this.plugin.notifications.show({message:`Attached ${t.name}`,kind:"success",category:"attachment",affectedPaths:[o],folder:this.noteFolder}),`![[${o}]]`}catch(e){return new S.Notice(`Stashpad: attachment failed (${e.message})`),null}}claimSelectedAsAuthor(){this.authorship.claimSelectedAsAuthor()}claimFolderAsAuthor(){this.authorship.claimFolderAsAuthor()}claimSelectedWithContributor(){this.authorship.claimSelectedWithContributor()}claimFolderWithContributor(){this.authorship.claimFolderWithContributor()}renderAuthorshipFooter(t,e){if(!e.file)return;let i=this.plugin.settings;if(!i.showAuthor&&!i.showContributors&&!i.showLastEdit)return;let s=this.app.metadataCache.getFileCache(e.file)?.frontmatter??{},n=typeof s.author=="string"?s.author:"",o=Array.isArray(s.contributors)?s.contributors.filter(I=>typeof I=="string"&&I.trim()!==""):[],r=typeof s.modified=="string"?s.modified:typeof s.created=="string"?s.created:"",A=i.showAuthor&&!!n,l=i.showContributors&&o.length>0,d=i.showLastEdit&&!!r;if(!A&&!l&&!d)return;let g=t.createDiv({cls:"stashpad-note-authorship"}),p=(I,u)=>{let f=u.replace(/^\[\[/,"").replace(/\]\]$/,""),C=f.indexOf("|"),m=C>=0?f.slice(0,C):f,B=C>=0?f.slice(C+1):f.split("/").pop()??f,E=I.createEl("a",{cls:"internal-link",text:B});E.setAttribute("data-href",m),E.setAttribute("href",m)},h=[];A&&h.push(I=>{I.createSpan({cls:"stashpad-authorship-label",text:"by "}),p(I,n)}),l&&h.push(I=>{I.createSpan({cls:"stashpad-authorship-label",text:"with "}),o.forEach((u,f)=>{f>0&&I.createSpan({text:", "}),p(I,u)})}),d&&h.push(I=>{I.createSpan({cls:"stashpad-authorship-label",text:"edited "}),I.createSpan({text:this.formatTimeInline(r)})}),h.forEach((I,u)=>{u>0&&g.createSpan({cls:"stashpad-authorship-sep",text:"\xB7"});let f=g.createSpan({cls:"stashpad-authorship-piece"});I(f)}),g.addEventListener("click",I=>this.handleRenderedClick(I,e))}activeStopwords(){return this.plugin.settings.slugStopWords??Rt}scheduleSlugRename(t){let e=this.slugDebouncers.get(t.path);e&&e.cancel(),e=(0,S.debounce)(()=>void this.maybeRenameForSlug(t),3e4),this.slugDebouncers.set(t.path,e),e()}async maybeRenameForSlug(t){let e=ve(t.basename);if(!e||e===F)return;let i=await this.app.vault.cachedRead(t),s=this.stripFrontmatter(i),n=Et(s,this.activeStopwords()),o=wt(n,e);if(t.name===o)return;let r=t.parent?`${t.parent.path}/${o}`:o;if(this.app.vault.getAbstractFileByPath(r))return;let A=t.path;try{await this.app.fileManager.renameFile(t,r),await this.log.append({type:"rename",id:e,payload:{from:A,to:r}})}catch{}}scheduleAttachmentSync(t){let e=this.attachmentDebouncers.get(t.path);e&&e.cancel(),e=(0,S.debounce)(()=>void this.syncAttachmentsFrontmatter(t),1500),this.attachmentDebouncers.set(t.path,e),e()}async syncAttachmentsFrontmatter(t){let e=await this.app.vault.cachedRead(t),i=this.stripFrontmatter(e),s=this.extractAttachments(i),n=this.app.metadataCache.getFileCache(t)?.frontmatter,r=(Array.isArray(n?.attachments)?n.attachments:[]).filter(d=>typeof d=="string").map(d=>Be(d));if(r.length===s.length&&r.every((d,g)=>d===s[g]))return;let l=s.map(d=>Gt(d));await this.app.fileManager.processFrontMatter(t,d=>{d.attachments=l})}stripFrontmatter(t){let e=t.replace(/^\uFEFF/,""),i=e.match(/^\s*---\s*\r?\n[\s\S]*?\r?\n---\s*(?:\r?\n|$)/);return i?e.slice(i[0].length):e}formatTime(t){if(!t)return"";let e=(0,S.moment)(t);if(!e.isValid())return"";if(_().useTemplatesFormat){let s=ps(this.app);if(s)return`${e.format(s.dateFormat)} ${e.format(s.timeFormat)}`}return`${e.format("YYYY.MM.DD")} ${e.format("HH:mm A")}`}formatTimeInline(t){if(!t)return"";let e=(0,S.moment)(t);if(!e.isValid())return"";if(_().useTemplatesFormat){let s=ps(this.app);if(s){let n=/:ss/.test(s.timeFormat)?s.timeFormat:s.timeFormat.replace(/(:mm)/,"$1:ss");return`${e.format(s.dateFormat)} ${e.format(n)}`}}return`${e.format("YYYY.MM.DD")} ${e.format("HH:mm:ss A")}`}scrollListToBottom(){let t=this.listEl;if(!t)return;if(this.stickToListBottom=!0,t.scrollTop=t.scrollHeight,S.Platform.isMobile){let r=0,A=()=>{!this.stickToListBottom||r>=8||(r++,Date.now()>=this.keyboardTransitionUntil&&(t.scrollTop=t.scrollHeight),window.setTimeout(A,120))};window.setTimeout(A,60);return}this.stickyRowObserver?.disconnect();let e=()=>{if(!this.stickToListBottom){this.stickyRowObserver?.disconnect(),this.stickyRowObserver=null;return}t.scrollTop=t.scrollHeight},i=new ResizeObserver(e);for(let r of Array.from(t.children))r instanceof HTMLElement&&i.observe(r);this.stickyRowObserver=i;let s=performance.now(),n=t.scrollHeight,o=()=>{if(!this.stickToListBottom)return;let r=t.scrollHeight;r!==n&&(t.scrollTop=r,n=r),performance.now()-s<3e4?requestAnimationFrame(o):(this.stickToListBottom=!1,this.stickyRowObserver?.disconnect(),this.stickyRowObserver=null)};requestAnimationFrame(o)}openNoteMenu(t,e){if(!e.file)return;let i=e.file,s=new S.Menu;s.addItem(A=>A.setTitle("Open in new Stashpad tab").setIcon("layout-grid").onClick(()=>{this.openInNewStashpadTab(e.id)})),s.addItem(A=>A.setTitle("Open in editor").setIcon("file-text").onClick(()=>{this.openFileAtEnd(i)})),s.addItem(A=>A.setTitle("Focus in Stashpad").setIcon("arrow-right").onClick(()=>this.navigateTo(e.id))),s.addSeparator(),s.addItem(A=>A.setTitle("Split note\u2026").setIcon("split").onClick(()=>void this.cmdSplit(e))),s.addItem(A=>A.setTitle("Clone (duplicate / copy)").setIcon("files").onClick(()=>{this.selection.has(e.id)||(this.selection.clear(),this.selection.add(e.id),this.lastSelected=e.id),this.cmdClone()})),s.addItem(A=>A.setTitle("Insert template\u2026").setIcon("file-plus-2").onClick(()=>this.cmdInsertTemplate())),s.addItem(A=>A.setTitle("Export to .stash").setIcon("package").onClick(()=>{this.selection.has(e.id)||(this.selection.clear(),this.selection.add(e.id),this.lastSelected=e.id),this.cmdExportStash()})),this.plugin.settings.okfEnabled&&s.addItem(A=>A.setTitle("Export as OKF\u2026").setIcon("book-marked").onClick(()=>{this.selection.has(e.id)||(this.selection.clear(),this.selection.add(e.id),this.lastSelected=e.id),this.cmdExportOkf()})),this.plugin.encryption?.isConfigured?.()&&s.addItem(A=>A.setTitle("Encrypt (lock) note + children").setIcon("lock").onClick(async()=>{let l=this.order.getOrder(this.noteFolder,e.parent??F),d=l.indexOf(e.id),g=d>0?l[d-1]:null;await this.plugin.lockNoteSubtree(this.noteFolder,e.id,g)&&this.render()})),s.addSeparator(),s.addItem(A=>A.setTitle("Move to\u2026").setIcon("move").onClick(()=>this.cmdMovePicker())),s.addItem(A=>A.setTitle("Move to Home").setIcon("home").onClick(async()=>{await this.changeParent(e,F),this.plugin.settings.autoNavOnMoveOut&&this.focusId!==F&&this.navigateTo(F)}));let n={folder:this.noteFolder,id:e.id},o=this.plugin.isPinned(n);s.addItem(A=>A.setTitle(o?"Unpin from sidebar":"Pin to sidebar").setIcon(o?"pin-off":"pin").onClick(async()=>{o?await this.plugin.unpinNote(n):await this.plugin.pinNote(n)})),s.addItem(A=>A.setTitle("Set color\u2026").setIcon("palette").onClick(()=>{this.selection.has(e.id)||(this.selection.clear(),this.selection.add(e.id),this.lastSelected=e.id),this.cmdSetColor()}));let r=this.isCompleted(e);s.addItem(A=>A.setTitle(r?"Mark incomplete":"Mark complete").setIcon(r?"circle":"check-circle").onClick(()=>{this.selection.has(e.id)||(this.selection.clear(),this.selection.add(e.id),this.lastSelected=e.id),this.cmdToggleComplete()})),s.addSeparator(),s.addItem(A=>A.setTitle("Delete").setIcon("trash").onClick(async()=>{this.selection.has(e.id)||(this.selection.clear(),this.selection.add(e.id),this.lastSelected=e.id),await this.cmdDelete()})),s.addSeparator(),s.addItem(A=>A.setTitle("More commands\u2026").setIcon("terminal").onClick(()=>this.openCommandPalette())),s.showAtMouseEvent(t)}async deleteNote(t){if(!t.file)return;let e=[],i=I=>{for(let u of this.tree.getChildren(I.id))i(u),e.push(u)};i(t);let s=[...e,t],n=s.filter(I=>!!I.file),o=await Promise.all(n.map(I=>this.app.vault.cachedRead(I.file))),r=[];for(let I=0;I{let u=await this.snapshotNotes(s,I),f=0;if(I)for(let N of A){let P=this.app.metadataCache.getFirstLinkpathDest(N,"");if(P)try{await this.app.fileManager.trashFile(P),await this.log.append({type:"attachment_remove",id:F,payload:{path:P.path}}),this.plugin.notifications.show({message:`Deleted attachment "${P.name}"`,kind:"warning",category:"attachment",affectedPaths:[P.path],folder:this.noteFolder}),f+=1}catch{}}let C=new Set;for(let N of s)N.parent&&C.add(N.parent);let m=this.currentChildren.findIndex(N=>N.id===t.id),B=null;if(m>=0){for(let N=m+1;N=0;N--)if(this.currentChildren[N].id!==t.id){B=this.currentChildren[N].id;break}}}for(let N of s)if(N.file){try{await this.app.fileManager.trashFile(N.file)}catch{}await this.log.append({type:"delete",id:N.id,payload:{path:N.file.path,attachmentsRemoved:I?A:[]}})}this.selection.clear(),this.cursorIdx=-1,B&&(this.pendingFocusIds=[B]),this.tree.rebuild(this.noteFolder),this.render({kind:"follow-cursor"});for(let N of C)s.some(P=>P.id===N)||this.fmSync.scheduleParentOfDeleted(N);let E=this.noteFolder,Q=`Delete "${this.titleForNode(t)}"`,w=t.id,v=f>0?` with ${f} attachment${f===1?"":"s"}`:"",y=!1,b=async()=>{y||(y=!0,this.selection.clear(),this.cursorIdx=-1,await this.restoreSnapshots(u,[w]))};this.plugin.notifications.show({message:`Deleted "${this.titleForNode(t)}"${v}`,kind:"warning",category:"delete",duration:0,affectedIds:[t.id],affectedAuthorIds:l,folder:this.noteFolder,actions:[{label:"Undo delete",onClick:()=>void b()}]}),this.plugin.getUndoStack(E).push({label:Q,undo:async()=>{await b()},redo:async()=>{this.selection.clear(),this.cursorIdx=-1,y=!1,await this.trashNotesAndAttachments(u)}})},g=_(),p=e.length>0&&g.confirmBulkDelete,h=A.length>0&&g.confirmAttachmentDelete;if(!p&&!h){await d(!1),this.focusView();return}new Me(this.app,this.titleForNode(t),e.length,A.length,h,async I=>{await d(I),this.focusView()}).open()}async cmdSwapWithParent(){let t=this.cursorIdx>=0?this.currentChildren[this.cursorIdx]:null;if(!t?.file){new S.Notice("Pick a note first (move the cursor onto it).");return}if(!t.parent||t.parent===F){new S.Notice("Already at Home \u2014 no parent to swap with.");return}let e=this.tree.get(t.parent);if(!e?.file){new S.Notice("Couldn't find the parent note.");return}let i=e.parent??F,s=this.titleForNode(t),n=this.titleForNode(e),o=this.tree.getChildren(e.id).filter(A=>A.id!==t.id).length,r=[`"${n}" becomes a child of "${s}".`];o>0&&r.push(`${o} sibling${o===1?"":"s"} move with it.`),new gt(this.app,"Swap notes?",r.join(` `),"Swap",async A=>{A&&await this.swapParentChild(e,t,i)}).open()}offerSwapForDescendantMove(t,e){let i=this.tree.get(e);if(!i?.file){this.plugin.notifications.show({message:`Can't nest "${this.titleForNode(t)}" under one of its own descendants \u2014 that would create a cycle.`,kind:"warning",category:"move",affectedIds:[t.id],folder:this.noteFolder});return}if(i.parent!==t.id){this.plugin.notifications.show({message:`Can't nest "${this.titleForNode(t)}" under "${this.titleForNode(i)}" \u2014 it's a deeper descendant. Only direct parent \u2194 child swaps are supported (try moving "${this.titleForNode(i)}" up first, then swap).`,kind:"warning",category:"move",affectedIds:[t.id,i.id],folder:this.noteFolder});return}let s=this.titleForNode(t),n=this.titleForNode(i),o=this.tree.getChildren(t.id).filter(A=>A.id!==i.id).length,r=[`"${s}" becomes a child of "${n}".`];o>0&&r.push(`${o} sibling${o===1?"":"s"} move with it.`),new gt(this.app,"Confirm Note Swap",r.join(` diff --git a/manifest.json b/manifest.json index 006cd83..6944827 100644 --- a/manifest.json +++ b/manifest.json @@ -1,8 +1,8 @@ { "id": "stashpad", "name": "Stashpad", - "version": "0.102.16", - "minAppVersion": "1.7.0", + "version": "0.102.19", + "minAppVersion": "1.13.0", "description": "A chat-style, nested-notes workspace: rapid capture, outliner navigation, in-place editing, fast search, tasks, and per-folder templates, with one-click Open Knowledge Format (OKF) export so your notes are readable by LLMs and agents.", "author": "Human", "isDesktopOnly": false diff --git a/package.json b/package.json index 45ecb2a..4021030 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "stashpad-obsidian", - "version": "0.102.16", + "version": "0.102.19", "private": true, "scripts": { "dev": "node esbuild.config.mjs", diff --git a/release-notes/0.102.19.md b/release-notes/0.102.19.md new file mode 100644 index 0000000..02cf2c8 --- /dev/null +++ b/release-notes/0.102.19.md @@ -0,0 +1,19 @@ +# 0.102.19 — Plugin-store review compliance (round 2) + +Housekeeping release continuing the community-plugin-store review pass. No feature +changes. Still alpha; keep backups. + +## Compatibility + +- **Minimum Obsidian version is now 1.13.0.** Stashpad leans on APIs (the declarative + settings/search, workspace and encryption APIs) that the store review requires the + manifest to declare. The plugin already ran against 1.13+; this just makes the floor + explicit. If you're on an older Obsidian, update to 1.13 or later to install. + +## Under the hood + +- Cached note bodies are now re-hydrated with Obsidian's own `sanitizeHTMLToDom` + (the sanctioned, safe HTML-to-DOM API) instead of a lower-level call the review + flagged. Rendering is unchanged. +- Fixed an internal lint nit: a regex that strips a leading byte-order mark now uses an + escape sequence instead of a literal invisible character. No behavior change. diff --git a/src/view.ts b/src/view.ts index 46c3507..1a0ae68 100644 --- a/src/view.ts +++ b/src/view.ts @@ -1,7 +1,7 @@ import { App, ItemView, MarkdownRenderer, Menu, Notice, Platform, Scope, SuggestModal, TFile, TFolder, WorkspaceLeaf, debounce, - moment, setIcon, + moment, sanitizeHTMLToDom, setIcon, } from "obsidian"; import { ROOT_ID, STASHPAD_VIEW_TYPE, RESERVED_FRONTMATTER, fmHasTag, fmAddTag, fmRemoveTag, parseAssignees, parseAuthorRef, attachmentLinkPath, toAttachmentLink, @@ -4419,16 +4419,16 @@ export class StashpadView extends ItemView { } else { // Re-hydrate the cached markdown HTML. The string was produced by // Obsidian's own MarkdownRenderer from the user's note and persisted in - // the render cache; we parse it back into nodes and append them to the - // (freshly emptied) text element rather than assigning innerHTML - // (Obsidian lint: no-unsafe-innerHTML). createContextualFragment yields - // identical DOM — and, like innerHTML, never executes