joplin-plugin-explorer 1.6.9 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/package.json +25 -25
- package/publish/index.js +1 -1
- package/publish/manifest.json +46 -46
- package/publish/plugin.jpl +0 -0
- package/publish/webview/chrome.css +21 -0
- package/publish/webview/panel.css +30 -8
- package/publish/webview/panel.js +1 -1
- package/publish/webview/smart-dialog.css +105 -0
- package/publish/webview/smart-dialog.js +1 -0
package/publish/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{"use strict";var e={982(e){e.exports=require("crypto")},896(e){e.exports=require("fs")},928(e){e.exports=require("path")}},t={};function o(a){var s=t[a];if(void 0!==s)return s.exports;var n=t[a]={exports:{}};return e[a](n,n.exports,o),n.exports}const a={zh_CN:{newNotebook:"新建笔记本",newNote:"新建笔记",newTodo:"新建待办",sort:"排序",collapseAll:"全部折叠",expandAll:"全部展开",search:"搜索笔记内容...",searchResultCount:"找到 {count} 条结果",searchNoResult:"没有找到匹配的笔记",searching:"搜索中...",sync:"同步",syncing:"同步中...",syncDone:"✔ 同步完成",syncFailed:"⚠ 同步失败",loading:"加载中...",syncTip:"同步 Joplin。注意:后台的附件下载和解密不计入完成状态,插件收不到它们的事件。",syncCancel:"取消同步",syncCancelling:"取消中...",sortUpdatedDesc:"↓ 修改时间",sortUpdatedAsc:"↑ 修改时间",sortTitleAsc:"↑ 标题",sortTitleDesc:"↓ 标题",sortManual:"⇅ 手动排序",ctxNewNoteHere:"在此新建笔记",ctxNewTodoHere:"在此新建待办",ctxNewSubNotebook:"新建子笔记本",ctxRenameFolder:"编辑",ctxExportFolder:"导出笔记本",ctxDeleteFolder:"删除笔记本",exportFailed:"导出失败",ctxImportFiles:"导入文件",importFailed:"导入失败",impFmtMd:"Markdown / 文本",impFmtEnex:"Evernote (ENEX)",impFmtCsvTable:"CSV → Markdown 表格",impFmtCsvCode:"CSV → 代码块",ctxOpenNote:"打开笔记",ctxOpenInNewWindow:"在新窗口中打开",ctxEditExternally:"用外部编辑器打开",ctxCopyLink:"复制 Markdown 链接",ctxDuplicateNote:"复制副本",ctxSwitchNoteType:"笔记/待办 切换",ctxToggleTodo:"切换完成状态",ctxRenameNote:"重命名",ctxMoveNote:"移动到笔记本...",ctxNoteInfo:"笔记属性",ctxDeleteNote:"删除笔记",confirmDeleteFolder:"确定删除此笔记本及其所有内容吗?",confirmDeleteNote:"确定删除此笔记吗?",promptRename:"请输入新名称:",promptMoveNote:"请输入目标笔记本名称:",promptNewNotebookName:"请输入新笔记本名称:",dropCreateNotebook:"释放以创建新笔记本",searchSectionNotes:"笔记",searchSectionTags:"标签",searchSectionFolders:"笔记本",searchTagNoteCount:"{count} 条笔记",pinned:"收藏夹",notebooksSection:"笔记本",tags:"标签",ctxRenameTag:"重命名标签",ctxDeleteTag:"删除标签",ctxUntagNote:"从标签移除",confirmDeleteTag:"删除该标签?(只删除标签本身,不会删除笔记)",ctxPublishNote:"发布笔记",ctxExport:"导出",sharedBadge:"已发布 · 点击打开发布对话框",sShowTags:"显示标签分区",sShowTagsDesc:"在笔记本树下方将 Joplin 标签显示为文件夹。",tagCountBoth:"{own} 条笔记带此标签,子树共 {sub} 条",tagCountSub:"子树共 {sub} 条笔记",sTagNestSep:"标签嵌套分隔符",sTagNestSepDesc:'按此分隔符将标签名切分并显示为层级树(例如填 "." 时,"工作.项目.甲" 会嵌套三层)。仅影响显示,不会修改标签本身。留空则保持扁平列表。',sAutoRefresh:"外部变更自动刷新",sAutoRefreshDesc:"每 5 秒轮询 Joplin 的变更事件,其他插件或同步创建、移动、删除笔记时自动刷新面板。当前打开笔记的编辑不受影响(本就实时更新)。",sRestoreUi:"启动时恢复上次视图状态",sRestoreUiDesc:"跨会话记住文件夹/分区的折叠状态。关闭后启动时全部收缩。",sShowSmart:"显示智能文件夹",sShowSmartDesc:"将保存的搜索显示为文件夹:最近更新、未完成待办,以及自定义规则。",sSmartRules:"自定义智能文件夹",sSmartRulesDesc:'"名称:查询" 用 ";" 分隔,使用 Joplin 搜索语法。示例:工作:notebook:Work updated:day-30;星标:tag:star',sSmartLimit:"智能文件夹结果条数",sSmartLimitDesc:"每个智能文件夹最多显示的笔记数(最新优先)。默认 5。",sHoverPreview:"悬停预览笔记",sHoverPreviewDesc:"悬停笔记时显示标题、更新时间和正文摘要。",sHoverPreviewLen:"悬停预览长度(字符)",sArrowPos:"展开箭头位置",sArrowPosDesc:"同时作用于笔记本、分区标题和分区行,保持风格统一。",sArrowRight:"文字后面",sArrowLeft:"文字左侧(经典树形)",sExpandMode:"展开按钮行为",sExpandModeDesc:"点击展开按钮时的动作。恢复:还原上次全部收缩前的状态(跟随浏览)。仅文件夹/全部展开:固定动作,不跟随浏览。仅折叠:按钮只折叠、不提供展开。",sExpandRestore:"恢复收缩前的状态",sExpandSkeleton:"仅文件夹(叶子笔记本保持收缩)",sExpandAllOpt:"全部展开",sExpandCollapseOnly:"仅折叠(不展开)",sCollapseScope:"折叠范围",sCollapseScopeDesc:"全部折叠按钮的作用范围。仅笔记本:只折叠笔记本树。全部分区:同时折叠收藏夹、智能文件夹、标签、回收站。",sScopeNotebooks:"仅笔记本",sScopeAll:"全部分区",sSectionOrder:"分区顺序",sSectionOrderDesc:"逗号分隔的分区顺序。可用键:pinned(收藏夹)、smart(智能文件夹)、notebooks(笔记本,旧键 tree 仍可用)、tags(标签)、trash(回收站)。遗漏的分区按默认顺序排在末尾。",clearSearch:"清空搜索",sStackHeaders:"分区标题常驻堆叠",sStackHeadersDesc:"滚动时已经过的分区标题堆叠在顶部、未到达的堆叠在底部,任何时候每个分区都一键可达。关闭则仅当前分区标题吸顶。",sSectionGap:"分区间距(px)",sSectionGapDesc:"每个分区分割线上方的留白。默认 5。",sNoteIcon:"笔记图标",sNoteIconDesc:"普通笔记(非待办)使用的图标。可填 Emoji、图片 URL、data URI 或本地图片路径。默认 📄。",sShowToggles:"显示展开箭头",sShowTogglesDesc:"在文件夹和收藏夹分区前显示展开/收缩箭头。",sOpenFolderIcon:"展开文件夹图标",sOpenFolderIconDesc:"无自定义笔记本图标时,展开的文件夹使用的 Emoji、图片 URL、data URI 或本地图片路径。",sClosedFolderIcon:"收起文件夹图标",sClosedFolderIconDesc:"无自定义笔记本图标时,收起的文件夹使用的 Emoji、图片 URL、data URI 或本地图片路径。",sOpenPinnedIcon:"收藏夹展开图标",sOpenPinnedIconDesc:"收藏夹分区展开时使用的 Emoji、图片 URL、data URI 或本地图片路径。",sClosedPinnedIcon:"收藏夹收起图标",sClosedPinnedIconDesc:"收藏夹分区收起时使用的 Emoji、图片 URL、data URI 或本地图片路径。",refresh:"刷新",newItem:"新建",trash:"回收站",ctxRestoreNote:"恢复",ctxPermanentDelete:"彻底删除",ctxEmptyTrash:"清空回收站",confirmEmptyTrash:"清空回收站?所有已删除的笔记本和笔记将被永久删除,无法恢复。",confirmRestoreParent:"这条笔记原来所在的笔记本「{folder}」也在回收站里。要连同笔记本一起恢复吗?",restoreBoth:"恢复笔记本和笔记",restoreNoteOnly:"仅恢复笔记(到根目录)",smartFolders:"智能文件夹",smartRecent:"最近更新",smartTodos:"未完成待办",pvType:"类型",pvSize:"大小",pvCreated:"创建",pvUpdated:"更新",pvNote:"笔记",pvTodo:"待办",pvChars:"字符",ctxSetTags:"设置标签",ctxMoveToFolder:"移动到笔记本",ctxCopyExternalLink:"复制外部链接",ctxCopyFolderId:"复制笔记本 ID",revealNote:"定位当前笔记",ctxPin:"📌 收藏",ctxUnpin:"取消收藏",cancel:"取消"},zh_TW:{newNotebook:"新建筆記本",newNote:"新建筆記",newTodo:"新建待辦",sort:"排序",collapseAll:"全部摺疊",expandAll:"全部展開",search:"搜尋筆記內容...",searchResultCount:"找到 {count} 條結果",searchNoResult:"沒有找到匹配的筆記",searching:"搜尋中...",sync:"同步",syncing:"同步中...",syncDone:"✔ 同步完成",syncFailed:"⚠ 同步失敗",loading:"載入中...",syncTip:"同步 Joplin。注意:背景的附件下載與解密不計入完成狀態,外掛收不到它們的事件。",syncCancel:"取消同步",syncCancelling:"取消中...",sortUpdatedDesc:"↓ 修改時間",sortUpdatedAsc:"↑ 修改時間",sortTitleAsc:"↑ 標題",sortTitleDesc:"↓ 標題",sortManual:"⇅ 手動排序",ctxNewNoteHere:"在此新建筆記",ctxNewTodoHere:"在此新建待辦",ctxNewSubNotebook:"新建子筆記本",ctxRenameFolder:"編輯",ctxExportFolder:"匯出筆記本",ctxDeleteFolder:"刪除筆記本",exportFailed:"匯出失敗",ctxImportFiles:"匯入檔案",importFailed:"匯入失敗",impFmtMd:"Markdown / 文字",impFmtEnex:"Evernote (ENEX)",impFmtCsvTable:"CSV → Markdown 表格",impFmtCsvCode:"CSV → 程式碼區塊",ctxOpenNote:"開啟筆記",ctxOpenInNewWindow:"在新視窗中開啟",ctxEditExternally:"用外部編輯器開啟",ctxCopyLink:"複製 Markdown 連結",ctxDuplicateNote:"複製副本",ctxSwitchNoteType:"筆記/待辦 切換",ctxToggleTodo:"切換完成狀態",ctxRenameNote:"重新命名",ctxMoveNote:"移動到筆記本...",ctxNoteInfo:"筆記屬性",ctxDeleteNote:"刪除筆記",confirmDeleteFolder:"確定刪除此筆記本及其所有內容嗎?",confirmDeleteNote:"確定刪除此筆記嗎?",promptRename:"請輸入新名稱:",promptMoveNote:"請輸入目標筆記本名稱:",promptNewNotebookName:"請輸入新筆記本名稱:",dropCreateNotebook:"釋放以建立新筆記本",searchSectionNotes:"筆記",searchSectionTags:"標籤",searchSectionFolders:"筆記本",searchTagNoteCount:"{count} 條筆記",pinned:"收藏夾",notebooksSection:"筆記本",tags:"標籤",ctxRenameTag:"重新命名標籤",ctxDeleteTag:"刪除標籤",ctxUntagNote:"自標籤移除",confirmDeleteTag:"刪除該標籤?(僅刪除標籤本身,不會刪除筆記)",ctxPublishNote:"發佈筆記",ctxExport:"匯出",sharedBadge:"已發佈 · 點擊開啟發佈對話框",sShowTags:"顯示標籤分區",sShowTagsDesc:"在筆記本樹下方將 Joplin 標籤顯示為資料夾。",tagCountBoth:"{own} 則筆記帶此標籤,子樹共 {sub} 則",tagCountSub:"子樹共 {sub} 則筆記",sTagNestSep:"標籤巢狀分隔符",sTagNestSepDesc:'依此分隔符切分標籤名稱並顯示為階層樹(例如填 "." 時,"工作.專案.甲" 會巢狀三層)。僅影響顯示,不會修改標籤本身。留空則維持扁平清單。',sAutoRefresh:"外部變更自動重新整理",sAutoRefreshDesc:"每 5 秒輪詢 Joplin 的變更事件,其他外掛或同步建立、移動、刪除筆記時自動重新整理面板。目前開啟筆記的編輯不受影響(本就即時更新)。",sRestoreUi:"啟動時還原上次檢視狀態",sRestoreUiDesc:"跨工作階段記住資料夾/分區的摺疊狀態。關閉後啟動時全部收合。",sShowSmart:"顯示智慧資料夾",sShowSmartDesc:"將儲存的搜尋顯示為資料夾:最近更新、未完成待辦,以及自訂規則。",sSmartRules:"自訂智慧資料夾",sSmartRulesDesc:'「名稱:查詢」用 ";" 分隔,使用 Joplin 搜尋語法。範例:工作:notebook:Work updated:day-30;星標:tag:star',sSmartLimit:"智慧資料夾結果筆數",sSmartLimitDesc:"每個智慧資料夾最多顯示的筆記數(最新優先)。預設 5。",sHoverPreview:"懸停預覽筆記",sHoverPreviewDesc:"懸停筆記時顯示標題、更新時間和內文摘要。",sHoverPreviewLen:"懸停預覽長度(字元)",sArrowPos:"展開箭頭位置",sArrowPosDesc:"同時作用於筆記本、分區標題和分區列,保持風格統一。",sArrowRight:"文字後面",sArrowLeft:"文字左側(經典樹狀)",sExpandMode:"展開按鈕行為",sExpandModeDesc:"點擊展開按鈕時的動作。還原:回到上次全部收合前的狀態(跟隨瀏覽)。僅資料夾/全部展開:固定動作,不跟隨瀏覽。僅收合:按鈕只收合、不提供展開。",sExpandRestore:"還原收合前的狀態",sExpandSkeleton:"僅資料夾(葉子筆記本保持收合)",sExpandAllOpt:"全部展開",sExpandCollapseOnly:"僅收合(不展開)",sCollapseScope:"收合範圍",sCollapseScopeDesc:"全部收合按鈕的作用範圍。僅筆記本:只收合筆記本樹。全部分區:同時收合最愛、智慧資料夾、標籤、回收站。",sScopeNotebooks:"僅筆記本",sScopeAll:"全部分區",sSectionOrder:"分區順序",sSectionOrderDesc:"逗號分隔的分區順序。可用鍵:pinned(最愛)、smart(智慧資料夾)、notebooks(筆記本,舊鍵 tree 仍可用)、tags(標籤)、trash(回收站)。遺漏的分區按預設順序排在末尾。",clearSearch:"清空搜尋",sStackHeaders:"分區標題常駐堆疊",sStackHeadersDesc:"捲動時已經過的分區標題堆疊在頂部、未到達的堆疊在底部,任何時候每個分區都一鍵可達。關閉則僅目前分區標題吸頂。",sSectionGap:"分區間距(px)",sSectionGapDesc:"每個分區分隔線上方的留白。預設 5。",sNoteIcon:"筆記圖示",sNoteIconDesc:"一般筆記(非待辦)使用的圖示。可填 Emoji、圖片 URL、data URI 或本機圖片路徑。預設 📄。",sShowToggles:"顯示展開箭頭",sShowTogglesDesc:"在資料夾和最愛分區前顯示展開/收合箭頭。",sOpenFolderIcon:"展開資料夾圖示",sOpenFolderIconDesc:"無自訂筆記本圖示時,展開的資料夾使用的 Emoji、圖片 URL、data URI 或本機圖片路徑。",sClosedFolderIcon:"收合資料夾圖示",sClosedFolderIconDesc:"無自訂筆記本圖示時,收合的資料夾使用的 Emoji、圖片 URL、data URI 或本機圖片路徑。",sOpenPinnedIcon:"最愛展開圖示",sOpenPinnedIconDesc:"最愛分區展開時使用的 Emoji、圖片 URL、data URI 或本機圖片路徑。",sClosedPinnedIcon:"最愛收合圖示",sClosedPinnedIconDesc:"最愛分區收合時使用的 Emoji、圖片 URL、data URI 或本機圖片路徑。",refresh:"重新整理",newItem:"新增",trash:"垃圾桶",ctxRestoreNote:"還原",ctxPermanentDelete:"永久刪除",ctxEmptyTrash:"清空垃圾桶",confirmEmptyTrash:"清空垃圾桶?所有已刪除的筆記本和筆記將被永久刪除,無法還原。",confirmRestoreParent:"這條筆記原來所在的筆記本「{folder}」也在垃圾桶裡。要連同筆記本一起還原嗎?",restoreBoth:"還原筆記本和筆記",restoreNoteOnly:"僅還原筆記(到根目錄)",smartFolders:"智慧資料夾",smartRecent:"最近更新",smartTodos:"未完成待辦",pvType:"類型",pvSize:"大小",pvCreated:"建立",pvUpdated:"更新",pvNote:"筆記",pvTodo:"待辦",pvChars:"字元",ctxSetTags:"設定標籤",ctxMoveToFolder:"移動到筆記本",ctxCopyExternalLink:"複製外部連結",ctxCopyFolderId:"複製筆記本 ID",revealNote:"定位目前筆記",ctxPin:"📌 收藏",ctxUnpin:"取消收藏",cancel:"取消"},en_US:{newNotebook:"New Notebook",newNote:"New Note",newTodo:"New To-do",sort:"Sort",collapseAll:"Collapse All",expandAll:"Expand All",search:"Search note contents...",searchResultCount:"{count} results found",searchNoResult:"No matching notes found",searching:"Searching...",sync:"Synchronise",syncing:"Syncing...",syncDone:"✔ Sync Done",syncFailed:"⚠ Sync Failed",loading:"Loading...",syncTip:"Synchronise Joplin. Note: background attachment downloads and decryption are not covered - the plugin receives no events for them.",syncCancel:"Cancel sync",syncCancelling:"Cancelling...",sortUpdatedDesc:"↓ Updated",sortUpdatedAsc:"↑ Updated",sortTitleAsc:"↑ Title",sortTitleDesc:"↓ Title",sortManual:"⇅ Manual",ctxNewNoteHere:"New Note Here",ctxNewTodoHere:"New To-do Here",ctxNewSubNotebook:"New Sub-notebook",ctxRenameFolder:"Edit",ctxExportFolder:"Export Notebook",ctxDeleteFolder:"Delete Notebook",exportFailed:"Export failed",ctxImportFiles:"Import files",importFailed:"Import failed",impFmtMd:"Markdown / Text",impFmtEnex:"Evernote (ENEX)",impFmtCsvTable:"CSV → Markdown table",impFmtCsvCode:"CSV → code block",ctxOpenNote:"Open Note",ctxOpenInNewWindow:"Open in New Window",ctxEditExternally:"Edit in external editor",ctxCopyLink:"Copy Markdown Link",ctxDuplicateNote:"Duplicate",ctxSwitchNoteType:"Switch Note/To-do",ctxToggleTodo:"Toggle Completed",ctxRenameNote:"Rename",ctxMoveNote:"Move to Notebook...",ctxNoteInfo:"Note Properties",ctxDeleteNote:"Delete Note",confirmDeleteFolder:"Delete this notebook and all its contents?",confirmDeleteNote:"Delete this note?",promptRename:"Enter new name:",promptMoveNote:"Enter target notebook name:",promptNewNotebookName:"Enter new notebook name:",dropCreateNotebook:"Release to create a new notebook",searchSectionNotes:"Notes",searchSectionTags:"Tags",searchSectionFolders:"Notebooks",searchTagNoteCount:"{count} notes",pinned:"Pinned",notebooksSection:"Notebooks",tags:"Tags",ctxRenameTag:"Rename tag",ctxDeleteTag:"Delete tag",ctxUntagNote:"Remove from tag",confirmDeleteTag:"Delete this tag? (Only the tag is removed - notes are kept.)",ctxPublishNote:"Publish note",ctxExport:"Export",sharedBadge:"Published · click to open the publish dialog",sShowTags:"Show tags section",sShowTagsDesc:"Show Joplin tags as folders below the notebook tree.",tagCountBoth:"{own} notes with this tag, {sub} in the whole subtree",tagCountSub:"{sub} notes in this subtree",sTagNestSep:"Tag nesting separator",sTagNestSepDesc:'Split tag names on this character and show them as a tree (with ".", the tag "work.project.a" nests three levels deep). Display only — your tags are never modified. Leave empty for a flat list.',sAutoRefresh:"Auto refresh on external changes",sAutoRefreshDesc:"Poll Joplin's change feed every 5 seconds and refresh the panel when notes are created, moved or deleted by other plugins or sync. Edits to the currently open note are ignored (they already update live).",sRestoreUi:"Restore last view state on startup",sRestoreUiDesc:"Remember collapsed/expanded folders and sections between sessions. When off, the tree starts fully collapsed.",sShowSmart:"Show smart folders",sShowSmartDesc:"Saved searches rendered as folders: Recently updated, Open to-dos, and your own rules.",sSmartRules:"Custom smart folders",sSmartRulesDesc:'Name:query pairs separated by ";", using Joplin search syntax. Example: Work:notebook:Work updated:day-30;Starred:tag:star',sSmartLimit:"Smart folder result limit",sSmartLimitDesc:"Maximum notes shown per smart folder (newest first). Default: 5.",sHoverPreview:"Note preview on hover",sHoverPreviewDesc:"Show title, updated time and a body snippet when hovering a note.",sHoverPreviewLen:"Hover preview length (characters)",sArrowPos:"Expand arrow position",sArrowPosDesc:"Applies to notebooks, section headers and section rows alike, so everything matches.",sArrowRight:"After the text",sArrowLeft:"Left of the text (classic tree)",sExpandMode:"Expand button behavior",sExpandModeDesc:"What the button does when expanding. Restore: return to the state before the last Collapse All (follows browsing). Folders only / Everything: fixed action, ignores browsing. Collapse only: the button just collapses, no expand.",sExpandRestore:"Restore the state before Collapse All",sExpandSkeleton:"Folders only (leaf notebooks stay collapsed)",sExpandAllOpt:"Everything",sExpandCollapseOnly:"Collapse only (no expand)",sCollapseScope:"Collapse scope",sCollapseScopeDesc:"What the Collapse All button affects. Notebooks only: just the notebook tree. All sections: also fold Pinned, Smart folders, Tags and Trash.",sScopeNotebooks:"Notebooks only",sScopeAll:"All sections",sSectionOrder:"Section order",sSectionOrderDesc:'Comma-separated section order. Keys: pinned, smart (smart folders), notebooks (legacy key "tree" still accepted), tags, trash. Omitted sections keep the default order at the end.',clearSearch:"Clear search",sStackHeaders:"Stack section headers",sStackHeadersDesc:"While scrolling, headers of sections you passed stack at the top and upcoming ones at the bottom, so every section is one click away at all times. Off: only the current section header sticks.",sSectionGap:"Section spacing (px)",sSectionGapDesc:"Blank space above each section divider. Default: 5.",sNoteIcon:"Note icon",sNoteIconDesc:"Icon for plain notes (non-to-do). Emoji, image URL, data URI, or local image path. Default: 📄.",sShowToggles:"Show toggle arrows",sShowTogglesDesc:"Show expand/collapse arrows before folders and the pinned section.",sOpenFolderIcon:"Open folder icon",sOpenFolderIconDesc:"Emoji, image URL, data URI, or local image path for expanded folders without a custom notebook icon.",sClosedFolderIcon:"Closed folder icon",sClosedFolderIconDesc:"Emoji, image URL, data URI, or local image path for collapsed folders without a custom notebook icon.",sOpenPinnedIcon:"Open pinned icon",sOpenPinnedIconDesc:"Emoji, image URL, data URI, or local image path for the expanded pinned section.",sClosedPinnedIcon:"Closed pinned icon",sClosedPinnedIconDesc:"Emoji, image URL, data URI, or local image path for the collapsed pinned section.",refresh:"Refresh",newItem:"New",trash:"Trash",ctxRestoreNote:"Restore",ctxPermanentDelete:"Delete permanently",ctxEmptyTrash:"Empty trash",confirmEmptyTrash:"Empty the trash? All deleted notebooks and notes will be permanently removed. This cannot be undone.",confirmRestoreParent:'The notebook "{folder}" that held this note is also in the trash. Restore the notebook together with the note?',restoreBoth:"Restore notebook + note",restoreNoteOnly:"Note only (to root)",smartFolders:"Smart folders",smartRecent:"Recently updated",smartTodos:"Open to-dos",pvType:"Type",pvSize:"Size",pvCreated:"Created",pvUpdated:"Updated",pvNote:"Note",pvTodo:"To-do",pvChars:"chars",ctxSetTags:"Set tags",ctxMoveToFolder:"Move to notebook",ctxCopyExternalLink:"Copy external link",ctxCopyFolderId:"Copy notebook ID",revealNote:"Reveal current note",ctxPin:"📌 Pin",ctxUnpin:"Unpin",cancel:"Cancel"},ru:{newNotebook:"Новый блокнот",newNote:"Новая заметка",newTodo:"Новая задача",sort:"Сортировка",collapseAll:"Свернуть все",expandAll:"Развернуть все",search:"Искать в содержимом заметок...",searchResultCount:"Найдено результатов: {count}",searchNoResult:"Заметки не найдены",searching:"Поиск...",sync:"Синхронизировать",syncing:"Синхронизация...",syncDone:"✔ Синхронизация завершена",syncFailed:"⚠ Ошибка синхронизации",loading:"Загрузка...",syncTip:"Синхронизировать Joplin. Фоновая загрузка вложений и расшифровка не учитываются - плагин не получает о них событий.",syncCancel:"Отменить",syncCancelling:"Отмена...",sortUpdatedDesc:"↓ Обновлено",sortUpdatedAsc:"↑ Обновлено",sortTitleAsc:"↑ Название",sortTitleDesc:"↓ Название",sortManual:"⇅ Вручную",ctxNewNoteHere:"Создать заметку здесь",ctxNewTodoHere:"Создать задачу здесь",ctxNewSubNotebook:"Новый вложенный блокнот",ctxRenameFolder:"Редактировать",ctxExportFolder:"Экспортировать блокнот",ctxDeleteFolder:"Удалить блокнот",exportFailed:"Ошибка экспорта",ctxImportFiles:"Импортировать файлы",importFailed:"Ошибка импорта",impFmtMd:"Markdown / текст",impFmtEnex:"Evernote (ENEX)",impFmtCsvTable:"CSV → таблица Markdown",impFmtCsvCode:"CSV → блок кода",ctxOpenNote:"Открыть заметку",ctxOpenInNewWindow:"Открыть в новом окне",ctxEditExternally:"Открыть во внешнем редакторе",ctxCopyLink:"Скопировать Markdown-ссылку",ctxDuplicateNote:"Создать копию",ctxSwitchNoteType:"Переключить между заметкой и задачей",ctxToggleTodo:"Переключить статус выполнения",ctxRenameNote:"Переименовать",ctxMoveNote:"Переместить в блокнот...",ctxNoteInfo:"Свойства заметки",ctxDeleteNote:"Удалить заметку",confirmDeleteFolder:"Удалить блокнот и все его содержимое?",confirmDeleteNote:"Удалить заметку?",promptRename:"Введите новое название:",promptMoveNote:"Переместить в блокнот:",promptNewNotebookName:"Введите название нового блокнота:",dropCreateNotebook:"Отпустите, чтобы создать новый блокнот",searchSectionNotes:"Заметки",searchSectionTags:"Метки",searchSectionFolders:"Блокноты",searchTagNoteCount:"Заметок: {count}",pinned:"Закрепленные",notebooksSection:"Блокноты",tags:"Теги",ctxRenameTag:"Переименовать тег",ctxDeleteTag:"Удалить тег",ctxUntagNote:"Убрать из тега",confirmDeleteTag:"Удалить этот тег? (Удаляется только тег, заметки сохраняются.)",ctxPublishNote:"Опубликовать заметку",ctxExport:"Экспорт",sharedBadge:"Опубликовано · нажмите, чтобы открыть диалог публикации",sShowTags:"Показывать раздел тегов",sShowTagsDesc:"Показывать теги Joplin как папки под деревом блокнотов.",tagCountBoth:"{own} заметок с этим тегом, {sub} во всём поддереве",tagCountSub:"{sub} заметок в этом поддереве",sTagNestSep:"Разделитель вложенности тегов",sTagNestSepDesc:'Разделять имена тегов по этому символу и показывать их деревом (с "." тег "работа.проект.а" вложится на три уровня). Только отображение — сами теги не изменяются. Пусто — плоский список.',sAutoRefresh:"Автообновление при внешних изменениях",sAutoRefreshDesc:"Опрашивать ленту изменений Joplin каждые 5 секунд и обновлять панель, когда заметки создаются, перемещаются или удаляются другими плагинами или синхронизацией. Правки открытой заметки игнорируются (они и так обновляются сразу).",sRestoreUi:"Восстанавливать состояние вида при запуске",sRestoreUiDesc:"Запоминать свёрнутые/развёрнутые папки и разделы между сеансами. Если выключено, дерево запускается полностью свёрнутым.",sShowSmart:"Показывать умные папки",sShowSmartDesc:"Сохранённые поиски в виде папок: Недавно обновлённые, Открытые задачи и ваши собственные правила.",sSmartRules:"Свои умные папки",sSmartRulesDesc:"Пары «Имя:запрос» через «;», синтаксис поиска Joplin. Пример: Работа:notebook:Work updated:day-30;Избранное:tag:star",sSmartLimit:"Лимит результатов умной папки",sSmartLimitDesc:"Максимум заметок в умной папке (сначала новые). По умолчанию: 5.",sHoverPreview:"Предпросмотр заметки при наведении",sHoverPreviewDesc:"Показывать заголовок, время обновления и фрагмент текста при наведении на заметку.",sHoverPreviewLen:"Длина предпросмотра (символов)",sArrowPos:"Положение стрелки раскрытия",sArrowPosDesc:"Действует одинаково на блокноты, заголовки и строки разделов.",sArrowRight:"После текста",sArrowLeft:"Слева от текста (классическое дерево)",sExpandMode:"Поведение кнопки разворачивания",sExpandModeDesc:"Что делает кнопка при разворачивании. Восстановить: вернуть состояние до последнего «Свернуть всё» (следует за просмотром). Только папки / Всё: фиксированное действие, не зависит от просмотра. Только сворачивать: кнопка только сворачивает.",sExpandRestore:"Восстановить состояние до сворачивания",sExpandSkeleton:"Только папки (листовые блокноты остаются свёрнутыми)",sExpandAllOpt:"Всё",sExpandCollapseOnly:"Только сворачивать (без разворота)",sCollapseScope:"Область сворачивания",sCollapseScopeDesc:"На что влияет кнопка «Свернуть всё». Только блокноты: только дерево блокнотов. Все разделы: также закладки, умные папки, теги и корзину.",sScopeNotebooks:"Только блокноты",sScopeAll:"Все разделы",sSectionOrder:"Порядок разделов",sSectionOrderDesc:'Порядок разделов через запятую. Ключи: pinned (закладки), smart (умные папки), notebooks (блокноты; старый ключ "tree" тоже работает), tags (теги), trash (корзина). Пропущенные разделы идут в конце в порядке по умолчанию.',clearSearch:"Очистить поиск",sStackHeaders:"Складывать заголовки разделов",sStackHeadersDesc:"При прокрутке заголовки пройденных разделов закрепляются сверху, а предстоящих — снизу, так что каждый раздел всегда в одном клике. Выкл.: закрепляется только заголовок текущего раздела.",sSectionGap:"Отступ между разделами (px)",sSectionGapDesc:"Пустое место над разделителем каждого раздела. По умолчанию: 5.",sNoteIcon:"Значок заметки",sNoteIconDesc:"Значок для обычных заметок (не задач). Emoji, URL изображения, data URI или локальный путь. По умолчанию: 📄.",sShowToggles:"Показывать стрелки раскрытия",sShowTogglesDesc:"Показывать стрелки перед папками и разделом закладок.",sOpenFolderIcon:"Значок открытой папки",sOpenFolderIconDesc:"Emoji, URL изображения, data URI или локальный путь для развёрнутых папок без своего значка блокнота.",sClosedFolderIcon:"Значок закрытой папки",sClosedFolderIconDesc:"Emoji, URL изображения, data URI или локальный путь для свёрнутых папок без своего значка блокнота.",sOpenPinnedIcon:"Значок раскрытых закладок",sOpenPinnedIconDesc:"Emoji, URL изображения, data URI или локальный путь для развёрнутого раздела закладок.",sClosedPinnedIcon:"Значок свёрнутых закладок",sClosedPinnedIconDesc:"Emoji, URL изображения, data URI или локальный путь для свёрнутого раздела закладок.",refresh:"Обновить",newItem:"Создать",trash:"Корзина",ctxRestoreNote:"Восстановить",ctxPermanentDelete:"Удалить навсегда",ctxEmptyTrash:"Очистить корзину",confirmEmptyTrash:"Очистить корзину? Все удалённые блокноты и заметки будут удалены безвозвратно.",confirmRestoreParent:"Блокнот «{folder}», в котором была эта заметка, тоже в корзине. Восстановить блокнот вместе с заметкой?",restoreBoth:"Восстановить блокнот и заметку",restoreNoteOnly:"Только заметку (в корень)",smartFolders:"Умные папки",smartRecent:"Недавно обновлённые",smartTodos:"Незавершённые задачи",pvType:"Тип",pvSize:"Размер",pvCreated:"Создано",pvUpdated:"Обновлено",pvNote:"Заметка",pvTodo:"Задача",pvChars:"симв.",ctxSetTags:"Назначить теги",ctxMoveToFolder:"Переместить в блокнот",ctxCopyExternalLink:"Копировать внешнюю ссылку",ctxCopyFolderId:"Копировать ID блокнота",revealNote:"Показать текущую заметку",ctxPin:"📌 Закрепить",ctxUnpin:"Открепить",cancel:"Отмена"},ja_JP:{newNotebook:"新規ノートブック",newNote:"新規ノート",newTodo:"新規タスク",sort:"並べ替え",collapseAll:"すべて折りたたむ",expandAll:"すべて展開",search:"ノート内容を検索...",searchResultCount:"{count} 件の結果",searchNoResult:"一致するノートが見つかりません",searching:"検索中...",sync:"同期",syncing:"同期中...",syncDone:"✔ 同期完了",syncFailed:"⚠ 同期に失敗しました",loading:"読み込み中...",syncTip:"Joplin を同期します。注意:バックグラウンドの添付ファイル取得と復号は完了状態に含まれません(プラグインにイベントが届かないため)。",syncCancel:"同期をキャンセル",syncCancelling:"キャンセル中...",sortUpdatedDesc:"↓ 更新日時",sortUpdatedAsc:"↑ 更新日時",sortTitleAsc:"↑ タイトル",sortTitleDesc:"↓ タイトル",sortManual:"⇅ 手動",ctxNewNoteHere:"ここに新規ノート",ctxNewTodoHere:"ここに新規タスク",ctxNewSubNotebook:"新規サブノートブック",ctxRenameFolder:"編集",ctxExportFolder:"ノートブックをエクスポート",ctxDeleteFolder:"ノートブックを削除",exportFailed:"エクスポート失敗",ctxImportFiles:"ファイルをインポート",importFailed:"インポート失敗",impFmtMd:"Markdown / テキスト",impFmtEnex:"Evernote (ENEX)",impFmtCsvTable:"CSV → Markdown テーブル",impFmtCsvCode:"CSV → コードブロック",ctxOpenNote:"ノートを開く",ctxOpenInNewWindow:"新しいウィンドウで開く",ctxEditExternally:"外部エディタで開く",ctxCopyLink:"Markdownリンクをコピー",ctxDuplicateNote:"複製",ctxSwitchNoteType:"ノート/タスク切り替え",ctxToggleTodo:"完了状態を切り替え",ctxRenameNote:"名前を変更",ctxMoveNote:"ノートブックに移動...",ctxNoteInfo:"ノートプロパティ",ctxDeleteNote:"ノートを削除",confirmDeleteFolder:"このノートブックとその内容をすべて削除しますか?",confirmDeleteNote:"このノートを削除しますか?",promptRename:"新しい名前を入力:",promptMoveNote:"移動先のノートブック名:",promptNewNotebookName:"新しいノートブック名を入力:",dropCreateNotebook:"ドロップして新規ノートブックを作成",searchSectionNotes:"ノート",searchSectionTags:"タグ",searchSectionFolders:"ノートブック",searchTagNoteCount:"{count} 件のノート",pinned:"ピン留め",notebooksSection:"ノートブック",tags:"タグ",ctxRenameTag:"タグ名を変更",ctxDeleteTag:"タグを削除",ctxUntagNote:"タグから外す",confirmDeleteTag:"このタグを削除しますか?(タグのみ削除され、ノートは残ります)",ctxPublishNote:"ノートを公開",ctxExport:"エクスポート",sharedBadge:"公開済み · クリックで公開ダイアログを開く",sShowTags:"タグセクションを表示",sShowTagsDesc:"ノートブックツリーの下に Joplin のタグをフォルダーとして表示します。",tagCountBoth:"このタグのノート {own} 件、サブツリー全体で {sub} 件",tagCountSub:"サブツリーに {sub} 件のノート",sTagNestSep:"タグ階層の区切り文字",sTagNestSepDesc:'この文字でタグ名を分割し、ツリー表示します("." の場合、"仕事.案件.甲" は3階層になります)。表示のみで、タグ自体は変更されません。空欄でフラット表示。',sAutoRefresh:"外部変更時に自動更新",sAutoRefreshDesc:"Joplin の変更フィードを 5 秒ごとに確認し、他のプラグインや同期によるノートの作成・移動・削除時にパネルを更新します。開いているノートの編集は対象外(もともと即時反映)。",sRestoreUi:"起動時に前回の表示状態を復元",sRestoreUiDesc:"フォルダー/セクションの折りたたみ状態をセッションをまたいで記憶します。オフの場合は全て折りたたんだ状態で起動します。",sShowSmart:"スマートフォルダーを表示",sShowSmartDesc:"保存済み検索をフォルダーとして表示:最近の更新、未完了タスク、独自ルール。",sSmartRules:"カスタムスマートフォルダー",sSmartRulesDesc:'「名前:クエリ」を ";" 区切りで。Joplin 検索構文を使用。例:仕事:notebook:Work updated:day-30;スター:tag:star',sSmartLimit:"スマートフォルダー表示件数",sSmartLimitDesc:"各スマートフォルダーに表示する最大ノート数(新しい順)。既定値:5。",sHoverPreview:"ホバーでノートをプレビュー",sHoverPreviewDesc:"ノートにカーソルを合わせるとタイトル・更新日時・本文の抜粋を表示します。",sHoverPreviewLen:"ホバープレビューの長さ(文字数)",sArrowPos:"展開矢印の位置",sArrowPosDesc:"ノートブック・セクション見出し・セクション行すべてに適用され、統一されます。",sArrowRight:"テキストの後ろ",sArrowLeft:"テキストの左(クラシックツリー)",sExpandMode:"展開ボタンの動作",sExpandModeDesc:"展開時のボタンの動作。復元:前回「すべて折りたたむ」前の状態に戻す(閲覧に追従)。フォルダーのみ/すべて:固定動作、閲覧に追従しない。折りたたみのみ:ボタンは折りたたむだけ。",sExpandRestore:"折りたたみ前の状態を復元",sExpandSkeleton:"フォルダーのみ(末端ノートブックは折りたたんだまま)",sExpandAllOpt:"すべて",sExpandCollapseOnly:"折りたたみのみ(展開なし)",sCollapseScope:"折りたたむ範囲",sCollapseScopeDesc:"「すべて折りたたむ」ボタンの対象。ノートブックのみ:ノートブックツリーだけ。すべてのセクション:お気に入り・スマートフォルダー・タグ・ゴミ箱も折りたたみます。",sScopeNotebooks:"ノートブックのみ",sScopeAll:"すべてのセクション",sSectionOrder:"セクションの順序",sSectionOrderDesc:"カンマ区切りのセクション順。キー:pinned(お気に入り)、smart(スマートフォルダー)、notebooks(ノートブック。旧キー tree も使用可)、tags(タグ)、trash(ゴミ箱)。省略したセクションは既定順で末尾に並びます。",clearSearch:"検索をクリア",sStackHeaders:"セクション見出しを積み重ね表示",sStackHeadersDesc:"スクロール時、通過したセクションの見出しを上部に、未到達のものを下部に固定し、常に全セクションへワンクリックで移動できます。オフ:現在のセクション見出しのみ固定。",sSectionGap:"セクション間隔(px)",sSectionGapDesc:"各セクション区切り線の上の余白。既定値:5。",sNoteIcon:"ノートアイコン",sNoteIconDesc:"通常ノート(タスク以外)のアイコン。Emoji、画像 URL、data URI、ローカル画像パス。既定値:📄。",sShowToggles:"展開矢印を表示",sShowTogglesDesc:"フォルダーとお気に入りセクションの前に展開/折りたたみ矢印を表示します。",sOpenFolderIcon:"展開フォルダーのアイコン",sOpenFolderIconDesc:"カスタムアイコンのないノートブックが展開時に使う Emoji、画像 URL、data URI、ローカル画像パス。",sClosedFolderIcon:"折りたたみフォルダーのアイコン",sClosedFolderIconDesc:"カスタムアイコンのないノートブックが折りたたみ時に使う Emoji、画像 URL、data URI、ローカル画像パス。",sOpenPinnedIcon:"お気に入り展開アイコン",sOpenPinnedIconDesc:"お気に入りセクション展開時に使う Emoji、画像 URL、data URI、ローカル画像パス。",sClosedPinnedIcon:"お気に入り折りたたみアイコン",sClosedPinnedIconDesc:"お気に入りセクション折りたたみ時に使う Emoji、画像 URL、data URI、ローカル画像パス。",refresh:"更新",newItem:"新規",trash:"ゴミ箱",ctxRestoreNote:"復元",ctxPermanentDelete:"完全に削除",ctxEmptyTrash:"ゴミ箱を空にする",confirmEmptyTrash:"ゴミ箱を空にしますか?削除済みのノートブックとノートは完全に削除され、元に戻せません。",confirmRestoreParent:"このノートが入っていたノートブック「{folder}」もゴミ箱にあります。ノートブックごと復元しますか?",restoreBoth:"ノートブックとノートを復元",restoreNoteOnly:"ノートのみ(ルートへ)",smartFolders:"スマートフォルダ",smartRecent:"最近更新",smartTodos:"未完了の ToDo",pvType:"種類",pvSize:"サイズ",pvCreated:"作成",pvUpdated:"更新",pvNote:"ノート",pvTodo:"ToDo",pvChars:"文字",ctxSetTags:"タグを設定",ctxMoveToFolder:"ノートブックへ移動",ctxCopyExternalLink:"外部リンクをコピー",ctxCopyFolderId:"ノートブック ID をコピー",revealNote:"現在のノートを表示",ctxPin:"📌 ピン留め",ctxUnpin:"ピン解除",cancel:"キャンセル"}},s=o(896),n=o(928),i=o(982);function l(e){let t=0,o=0;const a=/^[ \t]*(?:[-*+]|\d+[.)])\s+\[([ xX])\]/gm;let s;for(;s=a.exec(e);)t++," "!==s[1]&&o++;return{total:t,done:o}}let r="Published";function d(e){let t="";return e.is_shared&&(t+='<span class="shared-badge" title="'+r+'"><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg></span>'),e.cb_total&&(t+=function(e,t){if(!t)return"";const o=Math.max(0,Math.min(1,e/t));let a="";if(o>=1)a='<circle cx="8" cy="8" r="7" class="pie-fill"/><path d="M4.8 8.3 L7.1 10.6 L11.3 5.9" fill="none" stroke="var(--joplin-background-color, #fff)" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>';else if(o>0){const e=2*o*Math.PI-Math.PI/2,t=8+7*Math.cos(e),s=8+7*Math.sin(e);a='<path class="pie-fill" d="M8 8 L8 1 A7 7 0 '+(o>.5?1:0)+" 1 "+t.toFixed(2)+" "+s.toFixed(2)+' Z"/>'}return'<span class="todo-pie" title="'+e+"/"+t+'"><svg width="12" height="12" viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" class="pie-ring"/>'+a+"</svg></span>"}(e.cb_done||0,e.cb_total)),t}function c(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}const p={".gif":"image/gif",".ico":"image/x-icon",".jpeg":"image/jpeg",".jpg":"image/jpeg",".png":"image/png",".svg":"image/svg+xml",".webp":"image/webp"};function m(e,t){const o="icon"+(t?" "+t:"");return"image"===e.type?'<span class="'+o+'"><img class="custom-icon" src="'+c(e.value)+'" /></span>':'<span class="'+o+'">'+c(e.value)+"</span>"}const u={};async function f(e,t,o,a){const l=o+":"+String(e);if(u[l])return u[l];const r=await async function(e,t,o,a){const l="string"==typeof e?e.trim():"";if(!l)return{type:"text",value:t};if(/^data:image\//i.test(l)||/^https?:\/\//i.test(l))return{type:"image",value:l};const r=function(e){const t=e.trim();return/^file:\/\//i.test(t)?decodeURIComponent(t.replace(/^file:\/\//i,"")):0===t.indexOf("~/")&&process&&process.env&&process.env.HOME?n.join(process.env.HOME,t.substring(2)):n.isAbsolute(t)?t:null}(l);if(!r)return{type:"text",value:l};const d=n.extname(r).toLowerCase(),c=p[d];if(!c)return console.warn("Joplin Explorer: unsupported icon file type",r),{type:"text",value:t};const m=n.join(a,"custom-icons"),u=i.createHash("sha1").update(r).digest("hex"),f=n.join(m,o+"-"+u+d);try{await s.promises.mkdir(m,{recursive:!0}),(await s.promises.stat(r)).isFile()&&await s.promises.copyFile(r,f)}catch(e){console.warn("Joplin Explorer: failed to copy icon file",r,e)}try{return{type:"image",value:"data:"+c+";base64,"+(await s.promises.readFile(f)).toString("base64")}}catch(e){return console.warn("Joplin Explorer: failed to load icon file",f,e),{type:"text",value:t}}}(e,t,o,a);return u[l]=r,r}async function g(){let e=[],t=1,o=!0;for(;o;){const a=await joplin.data.get(["folders"],{fields:["id","title","parent_id","icon"],page:t,limit:100});e=e.concat(a.items),o=a.has_more,t++}return e}function h(e,t){const o=e.order||0,a=t.order||0;return o!==a?a-o:(e.title||"").localeCompare(t.title||"")}function w(e,t=!0,o={type:"text",value:"📂"},a={type:"text",value:"📁"}){let s=e.icon;if(s&&"string"==typeof s)try{s=JSON.parse(s)}catch(e){s=null}return s&&s.emoji?{type:"text",value:String(s.emoji)}:t?a:o}let y="📄";function x(e){return e.is_todo?e.todo_completed?"☑":"☐":y}function v(e){const t=new Set(e.noteIds);let o=e.bounded;for(const a of e.children){const e=v(a);e.ids.forEach(e=>t.add(e)),e.bounded&&(o=!0)}return{text:o?"100+":t.size?String(t.size):"",ids:t,bounded:o}}function b(e,t,o,a=0){let s="";for(const n of e){const e=26+18*a,i=n.children.length>0,l=!n.tagId;s+='<div class="tree-item folder tag-folder collapsed'+(l?" tag-virtual":"")+'" style="padding-left:'+e+'px"'+(n.tagId?' data-tag-id="'+n.tagId+'"':"")+' data-tag-path="'+c(n.path)+'" data-type="tag" title="'+c(n.path)+'">',t&&(s+='<span class="toggle">▶</span>'),s+='<span class="icon">'+(l?"🔖":"🏷️")+"</span>",s+='<span class="label">'+c(n.label)+"</span>";const r=v(n).text,d=n.count;let p="",m="";!l&&i&&d&&r&&d!==r?(p=c(d)+' <span class="count-sub">('+c(r)+")</span>",m=o.both.replace("{own}",d).replace("{sub}",r)):!l&&d?p=c(d):r&&(p=r,m=o.sub.replace("{sub}",r)),p&&(s+='<span class="count"'+(m?' title="'+c(m)+'"':"")+">"+p+"</span>"),s+="</div>",s+='<div class="tag-children collapsed" data-tag-path="'+c(n.path)+'"'+(n.tagId?' data-tag-id="'+n.tagId+'"':"")+' data-note-indent="'+(e+18)+'">',i&&(s+=b(n.children,t,o,a+1)),s+="</div>"}return s}function S(e,t,o,a=0,s=!0,n={type:"text",value:"📂"},i={type:"text",value:"📁"}){let l="";for(const r of e){const e=26+18*a;if("folder"===r.type){const d=r.total_count||r.note_count||0,p=!0===o[r.id],u=p?"▶":"▼",f=p?"toggle":"toggle expanded";l+='<div class="tree-item folder'+(p?" collapsed":"")+'" style="padding-left:'+e+'px" data-id="'+r.id+'" data-type="folder">',s&&(l+='<span class="'+f+'">'+u+"</span>"),l+=m(w(r,!1,n,i),"folder-icon icon-when-open"),l+=m(w(r,!0,n,i),"folder-icon icon-when-collapsed"),l+='<span class="label">'+c(r.title)+"</span>",l+='<span class="count">'+d+"</span>",l+="</div>",l+='<div class="children'+(p?" collapsed":"")+'" data-folder-id="'+r.id+'">',r.children&&(l+=S(r.children,t,o,a+1,s,n,i)),l+="</div>"}else{const o=r.id===t?" selected":"",a=x(r);l+='<div class="tree-item note'+o+'" style="padding-left:'+e+'px" data-id="'+r.id+'" data-type="note" data-todo="'+(r.is_todo?1:0)+'">',l+='<span class="icon note-icon">'+a+"</span>",l+='<span class="label">'+c(r.title)+"</span>",l+=d(r),l+="</div>"}}return l}function _(e,t){const o=e.slice();switch(t){case"title_asc":o.sort((e,t)=>(e.title||"").localeCompare(t.title||""));break;case"title_desc":o.sort((e,t)=>(t.title||"").localeCompare(e.title||""));break;case"updated_asc":o.sort((e,t)=>(e.user_updated_time||0)-(t.user_updated_time||0));break;case"manual":o.sort((e,t)=>{const o=e.order||0,a=t.order||0;return o!==a?a-o:(t.user_updated_time||0)-(e.user_updated_time||0)});break;default:o.sort((e,t)=>(t.user_updated_time||0)-(e.user_updated_time||0))}return o}joplin.plugins.register({onStart:async function(){const e=(t=await joplin.settings.globalValue("locale")||"en_US",a[t]||a[t.split("_")[0]]||a.en_US);var t;r=e.sharedBadge||r;const o=await joplin.views.panels.create("notesInListPanel");await joplin.views.panels.addScript(o,"webview/panel.css"),await joplin.views.panels.addScript(o,"webview/panel.js"),await joplin.views.panels.setHtml(o,'<div id="notes-in-list-root"><p style="padding:12px;">'+e.loading+"</p></div>"),await joplin.views.panels.show(o,!0);try{await joplin.settings.registerSection("joplinExplorer",{label:"Joplin Explorer",iconName:"fas fa-columns"}),await joplin.settings.registerSettings({pinnedItems:{section:"joplinExplorer",type:2,value:'{"notes":[],"folders":[]}',public:!1,label:"Pinned Items (JSON)"},folderOrder:{section:"joplinExplorer",type:2,value:"{}",public:!1,label:"Folder Manual Order (JSON)"},noteSortMode:{section:"joplinExplorer",type:2,value:"updated_desc",public:!1,label:"Note Sort Mode"},uiState:{section:"joplinExplorer",type:2,value:"{}",public:!1,label:"UI State (JSON)"},showTagsSection:{section:"joplinExplorer",type:3,value:!0,public:!0,label:e.sShowTags,description:e.sShowTagsDesc},stackSectionHeaders:{section:"joplinExplorer",type:3,value:!0,public:!0,label:e.sStackHeaders,description:e.sStackHeadersDesc},tagNestSeparator:{section:"joplinExplorer",type:2,value:"",public:!0,label:e.sTagNestSep,description:e.sTagNestSepDesc},autoRefresh:{section:"joplinExplorer",type:3,value:!0,public:!0,label:e.sAutoRefresh,description:e.sAutoRefreshDesc},restoreUiState:{section:"joplinExplorer",type:3,value:!0,public:!0,label:e.sRestoreUi,description:e.sRestoreUiDesc},showSmartFolders:{section:"joplinExplorer",type:3,value:!0,public:!0,label:e.sShowSmart,description:e.sShowSmartDesc},smartFolderRules:{section:"joplinExplorer",type:2,value:"",public:!0,label:e.sSmartRules,description:e.sSmartRulesDesc},smartFolderLimit:{section:"joplinExplorer",type:1,value:5,minimum:1,maximum:50,public:!0,label:e.sSmartLimit,description:e.sSmartLimitDesc},hoverPreview:{section:"joplinExplorer",type:3,value:!0,public:!0,label:e.sHoverPreview,description:e.sHoverPreviewDesc},hoverPreviewLength:{section:"joplinExplorer",type:1,value:200,minimum:50,maximum:1e3,public:!0,label:e.sHoverPreviewLen},toggleArrowPosition:{section:"joplinExplorer",type:2,value:"right",isEnum:!0,options:{right:e.sArrowRight,left:e.sArrowLeft},public:!0,label:e.sArrowPos,description:e.sArrowPosDesc},expandAllMode:{section:"joplinExplorer",type:2,value:"restore",isEnum:!0,options:{restore:e.sExpandRestore,skeleton:e.sExpandSkeleton,all:e.sExpandAllOpt,collapseOnly:e.sExpandCollapseOnly},public:!0,label:e.sExpandMode,description:e.sExpandModeDesc},collapseAllScope:{section:"joplinExplorer",type:2,value:"notebooksOnly",isEnum:!0,options:{notebooksOnly:e.sScopeNotebooks,allSections:e.sScopeAll},public:!0,label:e.sCollapseScope,description:e.sCollapseScopeDesc},sectionOrder:{section:"joplinExplorer",type:2,value:"pinned,smart,tree,tags,trash",public:!0,label:e.sSectionOrder,description:e.sSectionOrderDesc},sectionSpacing:{section:"joplinExplorer",type:1,value:5,minimum:0,maximum:30,public:!0,label:e.sSectionGap,description:e.sSectionGapDesc},noteIcon:{section:"joplinExplorer",type:2,value:"📄",public:!0,label:e.sNoteIcon,description:e.sNoteIconDesc},showFolderToggles:{section:"joplinExplorer",type:3,value:!0,public:!0,label:e.sShowToggles,description:e.sShowTogglesDesc},openFolderIcon:{section:"joplinExplorer",type:2,value:"📂",public:!0,label:e.sOpenFolderIcon,description:e.sOpenFolderIconDesc},closedFolderIcon:{section:"joplinExplorer",type:2,value:"📁",public:!0,label:e.sClosedFolderIcon,description:e.sClosedFolderIconDesc},openPinnedIcon:{section:"joplinExplorer",type:2,value:"📌",public:!0,label:e.sOpenPinnedIcon,description:e.sOpenPinnedIconDesc},closedPinnedIcon:{section:"joplinExplorer",type:2,value:"📌",public:!0,label:e.sClosedPinnedIcon,description:e.sClosedPinnedIconDesc}})}catch(e){console.error("Joplin Explorer: failed to register settings",e)}async function i(){try{const e=String(await joplin.settings.value("noteIcon")||"").trim();y=e||"📄"}catch(e){}}await i();const p=await joplin.views.dialogs.create("explorerInputDialog"),v=await joplin.views.dialogs.create("explorerConfirmDialog"),N=await joplin.views.dialogs.create("explorerInfoDialog");async function j(t){return await joplin.views.dialogs.setHtml(v,'<div style="padding:10px;box-sizing:border-box;width:340px;max-width:100%;"><div style="font-size:13px;line-height:1.6;white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere;">'+c(t)+"</div></div>"),await joplin.views.dialogs.setButtons(v,[{id:"ok",title:"OK"},{id:"cancel",title:e.cancel||"Cancel"}]),"ok"===(await joplin.views.dialogs.open(v)).id}async function k(e,t){await joplin.views.dialogs.setHtml(N,'<div style="padding:10px;min-width:320px;"><div style="font-size:14px;font-weight:bold;margin-bottom:10px;">'+c(e)+'</div><div style="font-size:12px;line-height:1.8;white-space:pre-wrap;">'+c(t)+"</div></div>"),await joplin.views.dialogs.setButtons(N,[{id:"ok",title:"OK"}]),await joplin.views.dialogs.open(N)}async function I(t,o){await joplin.views.dialogs.setHtml(p,'<div style="padding:10px;box-sizing:border-box;width:360px;max-width:100%;"><div style="margin-bottom:8px;font-size:13px;word-break:break-word;overflow-wrap:anywhere;">'+c(t)+'</div><form name="inputForm"><input name="value" type="text" autofocus value="'+c(o||"")+'" style="width:100%;box-sizing:border-box;padding:6px 8px;font-size:13px;" /></form><script>setTimeout(function(){var i=document.querySelector(\'input[name=value]\');if(i){i.focus();i.select();}},30);<\/script></div>'),await joplin.views.dialogs.setButtons(p,[{id:"ok",title:"OK"},{id:"cancel",title:e.cancel||"Cancel"}]);const a=await joplin.views.dialogs.open(p);return"ok"===a.id&&a.formData&&a.formData.inputForm&&a.formData.inputForm.value||null}let D="",T={},E="updated_desc";try{const e=String(await joplin.settings.value("noteSortMode")||"");["updated_desc","updated_asc","title_asc","title_desc","manual"].indexOf(e)>=0&&(E=e)}catch(e){}let C=[],F={},R=[],O=[],A=!1,P=!1,M=!0,U=!1,L=!1,J={},H=!0,z=null,B=[],q={},V=!0,W=!1;if(P=!0,!1!==await joplin.settings.value("restoreUiState"))try{const e=String(await joplin.settings.value("uiState")||"{}"),t=JSON.parse(e);t&&"object"==typeof t&&(t.collapsedFolders&&"object"==typeof t.collapsedFolders&&!Array.isArray(t.collapsedFolders)&&(T=t.collapsedFolders,W=!0),"boolean"==typeof t.tagsCollapsed&&(P=t.tagsCollapsed),"boolean"==typeof t.pinnedCollapsed&&(A=t.pinnedCollapsed),"boolean"==typeof t.trashCollapsed&&(M=t.trashCollapsed),"boolean"==typeof t.smartCollapsed&&(U=t.smartCollapsed),"boolean"==typeof t.treeCollapsed&&(L=t.treeCollapsed),t.seenFolders&&"object"==typeof t.seenFolders&&!Array.isArray(t.seenFolders)&&(J=t.seenFolders,H=!1),Array.isArray(t.collapseSnapshot)&&(z=t.collapseSnapshot.map(String)))}catch(e){}let G=null;function X(){G&&clearTimeout(G),G=setTimeout(async()=>{G=null;try{await joplin.settings.setValue("uiState",JSON.stringify({collapsedFolders:T,tagsCollapsed:P,pinnedCollapsed:A,trashCollapsed:M,smartCollapsed:U,treeCollapsed:L,seenFolders:J,collapseSnapshot:z}))}catch(e){console.error("Joplin Explorer: failed to save UI state",e)}},500)}const K=await joplin.plugins.dataDir();let $=null;function Z(e=600){$&&clearTimeout($),$=setTimeout(async()=>{$=null,await se()},e)}let Q=null,Y={};async function ee(){try{await joplin.settings.setValue("pinnedItems",JSON.stringify(O))}catch(e){console.error("Joplin Explorer: failed to save pinned items",e)}}async function te(){try{await joplin.settings.setValue("folderOrder",JSON.stringify(q))}catch(e){console.error("Joplin Explorer: failed to save folder order",e)}}function oe(e){let t=e;for(;t;){delete T[t];const e=F[t];t=e?e.parent_id:null}}async function ae(){let e=-1;try{e=(await g()).length}catch(e){return}(async()=>{for(let t=0;t<240;t++){await new Promise(e=>setTimeout(e,500));try{const t=await g();if(t.length>e){const e=new Set(Object.keys(F));for(const o of t)e.has(o.id)||(J[o.id]=!0,o.parent_id&&delete T[o.parent_id]);return X(),void Z(100)}}catch(e){return}}})()}async function se(){try{const t=await g();await async function(){try{const e=await joplin.settings.value("folderOrder"),t=e?JSON.parse(e):{};q=t&&"object"==typeof t&&!Array.isArray(t)?t:{}}catch(e){q={}}}();let a=!1;const s=new Set(t.map(e=>e.id));for(const e of Object.keys(q))s.has(e)||(delete q[e],a=!0);a&&await te();for(const e of t)e.order=q[e.id]||0;C=t,F={};for(const e of t)F[e.id]=e;let n=!1;for(const e of t)J[e.id]||(J[e.id]=!0,H||(T[e.id]=!0),n=!0);if(H&&(H=!1,n=!0),n&&X(),V){if(!W)for(const e of t)T[e.id]=!0;const e=await joplin.workspace.selectedNote();e&&(D=e.id,oe(e.parent_id)),V=!1}const i=await async function(){let e=[],t=1,o=!0;for(;o;){const a=await joplin.data.get(["notes"],{fields:["id","title","parent_id","is_todo","todo_completed","updated_time","user_updated_time","order","is_shared","body"],page:t,limit:100});for(const e of a.items){const t=l(e.body||"");e.cb_total=t.total,e.cb_done=t.done,delete e.body}e=e.concat(a.items),o=a.has_more,t++}return e}(),r={};for(const e of t)r[e.id]=[];for(const e of i)r[e.parent_id]&&r[e.parent_id].push(e);for(const e of Object.keys(r))r[e]=_(r[e],E);const p=[];for(const e of Object.keys(r))for(const t of r[e])p.push(t);R=p;const u=await joplin.settings.value("showFolderToggles"),y=await joplin.settings.value("openFolderIcon"),v=await joplin.settings.value("closedFolderIcon"),N=await joplin.settings.value("openPinnedIcon"),j=await joplin.settings.value("closedPinnedIcon"),k=await f(y,"📂","openFolderIcon",K),I=await f(v,"📁","closedFolderIcon",K),G=await f(N,"📌","openPinnedIcon",K),$=await f(j,"📌","closedPinnedIcon",K),Z=!1!==u,Q=function(e,t){const o={},a={},s={};for(const t of e)o[t.id]={type:"folder",id:t.id,title:t.title,parent_id:t.parent_id,icon:t.icon,order:t.order||0,note_count:0,children:[]},a[t.id]=[],s[t.id]=[];for(const e of Object.keys(t))if(s[e])for(const o of t[e])s[e].push({type:"note",id:o.id,title:o.title||"(untitled)",is_todo:o.is_todo,todo_completed:o.todo_completed,is_shared:o.is_shared,cb_total:o.cb_total,cb_done:o.cb_done});const n=[];for(const t of e){const e=o[t.id];t.parent_id&&o[t.parent_id]?a[t.parent_id].push(e):n.push(e)}function i(e){const t=a[e.id].sort(h);for(const e of t)i(e);e.note_count=s[e.id].length,e.children=t.concat(s[e.id])}n.sort(h);for(const e of n)i(e);const l=n;function r(e){let t=0;if(!e.children)return 0;for(const o of e.children)"note"===o.type?t++:"folder"===o.type&&(r(o),t+=o.total_count||0);return e.total_count=t,t}for(const e of l)r(e);return l}(t,r),Y=S(Q,D,T,0,Z,k,I),ae=!1!==await joplin.settings.value("showTagsSection");if(ae)try{let e=[],t=1,o=!0;for(;o;){const a=await joplin.data.get(["tags"],{fields:["id","title"],page:t,limit:100});e=e.concat(a.items),o=a.has_more,t++}e.sort((e,t)=>(e.title||"").localeCompare(t.title||""));let a=e.map(()=>""),s=e.map(()=>[]);if(e.length<=200){const t=await Promise.all(e.map(e=>joplin.data.get(["tags",e.id,"notes"],{fields:["id","deleted_time"],limit:100}).then(e=>{const t=(e.items||[]).filter(e=>!e.deleted_time);return{count:0!==t.length||e.has_more?e.has_more?"100+":String(t.length):"0",ids:t.map(e=>e.id)}}).catch(()=>({count:"",ids:[]}))));a=t.map(e=>e.count),s=t.map(e=>e.ids);const o=a.map(e=>"0"!==e);e=e.filter((e,t)=>o[t]),s=s.filter((e,t)=>o[t]),a=a.filter(e=>"0"!==e)}B=e.map((e,t)=>({id:e.id,title:e.title||"",count:a[t]||"",noteIds:s[t]||[]}))}catch(e){B=[]}else B=[];await async function(){try{const e=await joplin.settings.value("pinnedItems");if(!e)return;const t=JSON.parse(e);if(t&&!Array.isArray(t)&&t.notes&&t.folders){O=[];for(const e of t.folders)O.push({id:e,type:"folder"});for(const e of t.notes)O.push({id:e,type:"note"});await ee()}else Array.isArray(t)&&(O=t)}catch(e){O=[]}}();const se=new Set(t.map(e=>e.id)),ne=new Set(p.map(e=>e.id)),ie=O.length;O=O.filter(e=>"folder"===e.type?se.has(e.id):ne.has(e.id)),O.length!==ie&&await ee();let re="";const pe=O.length;re+='<div class="pinned-section-header'+(A?" collapsed":"")+'" id="pinned-header">',Z&&(re+='<span class="toggle">'+(A?"▶":"▼")+"</span>"),re+=m(G,"icon-when-open")+m($,"icon-when-collapsed")+'<span class="label">'+e.pinned+"</span>"+(pe?'<span class="count">'+pe+"</span>":"")+"</div>",re+='<div class="pinned-section-body'+(A?" collapsed":"")+'" id="pinned-body">';for(const e of O)if("folder"===e.type){const t=F[e.id]||null;if(t){const e=w(t,!0,k,I);re+='<div class="tree-item folder pinned-item" data-id="'+t.id+'" data-type="folder">',re+=m(e,"folder-icon"),re+='<span class="label">'+c(t.title)+"</span>",re+="</div>"}}else{let t=null;for(const o of p)if(o.id===e.id){t=o;break}if(t){const e=t.id===D?" selected":"",o=x(t);re+='<div class="tree-item note pinned-item'+e+'" data-id="'+t.id+'" data-type="note" data-todo="'+(t.is_todo?1:0)+'">',re+='<span class="icon note-icon">'+o+"</span>",re+='<span class="label">'+c(t.title)+"</span>",re+=d(t),re+="</div>"}}re+="</div>";let me="";if(ae){me+='<div class="tags-section-header'+(P?" collapsed":"")+'" id="tags-header">',Z&&(me+='<span class="toggle">'+(P?"▶":"▼")+"</span>"),me+='<span class="icon">🏷️</span><span class="label">'+e.tags+"</span>"+(B.length?'<span class="count">'+B.length+"</span>":"")+"</div>",me+='<div class="tags-section-body'+(P?" collapsed":"")+'" id="tags-body">';const t=String(await joplin.settings.value("tagNestSeparator")||"");if(t)me+=b(function(e,t){const o=[],a={},s=(e,t)=>({path:e,label:t,tagId:"",count:"",noteIds:[],bounded:!1,children:[]});for(const n of e){const e=n.title.split(t).filter(e=>""!==e);if(!e.length)continue;let i="",l=o;for(let o=0;o<e.length;o++){i=0===o?e[0]:i+t+e[o];let r=a[i];r||(r=s(i,e[o]),a[i]=r,l.push(r)),o===e.length-1&&(r.tagId=n.id,r.count=n.count,r.noteIds=n.noteIds||[],r.bounded="100+"===n.count),l=r.children}}const n=e=>{e.sort((e,t)=>e.label.localeCompare(t.label)),e.forEach(e=>n(e.children))};return n(o),o}(B,t),Z,{both:e.tagCountBoth,sub:e.tagCountSub});else for(const e of B)me+='<div class="tree-item folder tag-folder collapsed" data-tag-id="'+e.id+'" data-type="tag">',Z&&(me+='<span class="toggle">▶</span>'),me+='<span class="icon">🏷️</span>',me+='<span class="label">'+c(e.title)+"</span>",e.count&&(me+='<span class="count">'+e.count+"</span>"),me+="</div>",me+='<div class="tag-children collapsed" data-tag-id="'+e.id+'"></div>';me+="</div>"}let ue="";try{const t={};let o=0,a=!1,s=1,n=!0;for(;n;){const e=await joplin.data.get(["folders"],{fields:["id","deleted_time"],include_deleted:"1",page:s,limit:100});for(const a of e.items||[])a.deleted_time>0&&(t[a.id]=!0,o++);n=e.has_more,s++}const i=(await joplin.data.get(["notes"],{fields:["id","parent_id","deleted_time"],include_deleted:"1",order_by:"deleted_time",order_dir:"DESC",limit:100})).items||[];for(const e of i){if(!e.deleted_time)break;t[e.parent_id]||o++}100===i.length&&i[99].deleted_time>0&&(a=!0),ue+='<div class="trash-section-header'+(M?" collapsed":"")+'" id="trash-header">',Z&&(ue+='<span class="toggle">'+(M?"▶":"▼")+"</span>"),ue+='<span class="icon">🗑️</span><span class="label">'+e.trash+"</span>"+(o>0||a?'<span class="count">'+o+(a?"+":"")+"</span>":"")+"</div>",ue+='<div class="trash-children'+(M?" collapsed":"")+'" id="trash-children"></div>'}catch(e){}let fe="";if(!1!==await joplin.settings.value("showSmartFolders")){const t=[{id:"recent",title:e.smartRecent,query:"updated:day-7"},{id:"todos",title:e.smartTodos,query:"type:todo iscompleted:0"}],o=String(await joplin.settings.value("smartFolderRules")||"").trim();if(o){const e=o.split(";");for(let o=0;o<e.length;o++){const a=e[o].trim();if(!a)continue;const s=a.indexOf(":");s<=0||t.push({id:"rule"+o,title:a.slice(0,s).trim(),query:a.slice(s+1).trim()})}}fe+='<div class="smart-section-header'+(U?" collapsed":"")+'" id="smart-header">',Z&&(fe+='<span class="toggle">'+(U?"▶":"▼")+"</span>"),fe+='<span class="icon">🔍</span><span class="label">'+e.smartFolders+"</span></div>",fe+='<div class="smart-section-body'+(U?" collapsed":"")+'" id="smart-body">';for(const e of t){if(!e.title||!e.query)continue;let t=0,o=!1;try{if("recent"===e.id)t=(await de(1)).length;else if("todos"===e.id){const e=await ce(101);t=Math.min(e.length,100),o=e.length>100}else{let a=1,s=!0;for(;s&&t<=100;){const o=await joplin.data.get(["search"],{query:e.query,fields:["id"],page:a,limit:100});t+=(o.items||[]).length,s=o.has_more,a++}t>100&&(t=100,o=!0)}}catch(e){}const a=t>0;fe+='<div class="tree-item folder smart-folder collapsed'+(a?"":" smart-empty")+'" style="padding-left:26px" data-smart-id="'+e.id+'" data-query="'+c(e.query)+'" data-type="smart"><span class="toggle">▶</span><span class="icon">🔍</span><span class="label">'+c(e.title)+"</span>"+(a&&"recent"!==e.id?'<span class="count">'+t+(o?"+":"")+"</span>":"")+"</div>",a&&(fe+='<div class="smart-children collapsed" data-smart-id="'+e.id+'"></div>')}fe+="</div>"}const ge=c(JSON.stringify(O)),he=t.length>0&&t.every(e=>!0===T[e.id]),we={updated_desc:e.sortUpdatedDesc,updated_asc:e.sortUpdatedAsc,title_asc:e.sortTitleAsc,title_desc:e.sortTitleDesc,manual:e.sortManual},ye=c(JSON.stringify(e)),xe=String(await joplin.settings.value("expandAllMode")||"restore"),ve=String(await joplin.settings.value("collapseAllScope")||"notebooksOnly"),be=!1!==await joplin.settings.value("hoverPreview")?"1":"0",Se="left"===String(await joplin.settings.value("toggleArrowPosition")||"right")?"left":"right",_e={pinned:re,smart:fe,tree:'<div class="tree-section-header'+(L?" collapsed":"")+'" id="tree-header">'+(Z?'<span class="toggle">'+(L?"▶":"▼")+"</span>":"")+'<span class="icon">📚</span><span class="label">'+e.notebooksSection+'</span><span class="count">'+Q.reduce((e,t)=>e+("folder"===t.type?t.total_count||t.note_count||0:1),0)+'</span></div><div id="main-tree"'+(L?' class="collapsed"':"")+">"+Y+"</div>",tags:me,trash:ue},Ne=[];for(let e of String(await joplin.settings.value("sectionOrder")||"").split(",").map(e=>e.trim().toLowerCase()).filter(e=>!!e))"notebooks"===e&&(e="tree"),void 0!==_e[e]&&Ne.indexOf(e)<0&&Ne.push(e);for(const e of["pinned","smart","tree","tags","trash"])Ne.indexOf(e)<0&&Ne.push(e);const je=Ne.map(e=>_e[e]).join("");let ke=Number(await joplin.settings.value("sectionSpacing"));(!isFinite(ke)||ke<0)&&(ke=5),ke>30&&(ke=30);const Ie=!1!==await joplin.settings.value("stackSectionHeaders")?"1":"0",De='<div id="notes-in-list-root" style="--sec-gap:'+ke+'px" data-i18n="'+ye+'" data-pinned="'+ge+'" data-sort="'+c(E)+'" data-expand-mode="'+c(xe)+'" data-collapse-scope="'+c(ve)+'" data-hover-preview="'+be+'" data-arrow-pos="'+Se+'" data-stack-headers="'+Ie+'" data-collapse-snapshot="'+c(JSON.stringify(z))+'"> <div class="toolbar"> <button id="btn-new" title="'+e.newItem+'">+</button> <button id="btn-sort" title="'+e.sort+'">'+we[E]+'</button> <button id="btn-collapse-all" data-mode="'+(he&&"collapseOnly"!==xe?"expand":"collapse")+'" title="'+(he&&"collapseOnly"!==xe?e.expandAll:e.collapseAll)+'">'+(he&&"collapseOnly"!==xe?"▼":"▲")+'</button> <button id="btn-reveal" title="'+(e.revealNote||"Reveal current note")+'">◎</button> <button id="btn-refresh" title="'+e.refresh+'">↻</button> </div> <div class="search-bar"> <input id="search-input" type="text" placeholder="🔍 '+e.search+'" /> <button id="search-clear" title="'+(e.clearSearch||"Clear")+'" aria-label="'+(e.clearSearch||"Clear")+'">✕</button> </div> <div id="tree-container">'+je+' <div id="drop-zone-empty" class="drop-zone-empty">+ '+e.dropCreateNotebook+'</div> </div> <div id="search-results" style="display:none;"></div> <div class="bottom-bar"> <button id="btn-sync" title="'+c(e.syncTip||e.sync)+'">🔄 '+e.sync+"</button> </div></div>";if(await joplin.views.panels.setHtml(o,De),ue&&!M)try{const e=await le();await joplin.views.panels.postMessage(o,{name:"trashNotes",folders:e.folders,notes:e.notes})}catch(e){}}catch(e){console.error("Notes In List: refresh error",e),await joplin.views.panels.setHtml(o,'<div style="padding:12px;color:red;">Error: '+c(String(e))+"</div>")}}async function ne(e){try{const t=R.findIndex(t=>t.id===e),a=await joplin.data.get(["notes",e],{fields:["id","title","parent_id","is_todo","todo_completed","updated_time","user_updated_time","order","is_shared","body"]}),s=l(a.body||"");if(a.cb_total=s.total,a.cb_done=s.done,delete a.body,t<0||R[t].parent_id!==a.parent_id)return void await se();const n=R[t],i=0===E.indexOf("title_")&&(n.title||"")!==(a.title||""),r=0===E.indexOf("updated_")&&n.user_updated_time!==a.user_updated_time;if(R[t]=a,i||r)return void await se();await joplin.views.panels.postMessage(o,{name:"updateNote",id:a.id,title:a.title||"(untitled)",icon:x(a),badges:d(a)})}catch(e){await se()}}async function ie(e){let t=[],o=1,a=!0;for(;a&&t.length<200;){const s=await joplin.data.get(["tags",e,"notes"],{fields:["id","title","is_todo","todo_completed","deleted_time"],page:o,limit:50});t=t.concat((s.items||[]).filter(e=>!e.deleted_time)),a=s.has_more,o++}return t.map(e=>({id:e.id,title:e.title||"(untitled)",is_todo:e.is_todo,todo_completed:e.todo_completed}))}async function le(){const e=[],t={},o={};try{let a=1,s=!0;for(;s;){const n=await joplin.data.get(["folders"],{fields:["id","title","parent_id","deleted_time"],include_deleted:"1",page:a,limit:100});for(const a of n.items||[])o[a.id]=a.parent_id||"",a.deleted_time>0&&(e.push({id:a.id,title:a.title||"(untitled)",parent_id:a.parent_id}),t[a.id]=!0);s=n.has_more,a++}}catch(e){}const a=[],s={},n=[];for(const o of e)o.parent_id&&t[o.parent_id]?(s[o.parent_id]=s[o.parent_id]||[]).push(o):n.push(o);const i=(e,t)=>{a.push({id:e.id,title:e.title,depth:t,hasSub:!(!s[e.id]||!s[e.id].length)});for(const o of s[e.id]||[])i(o,t+1)};for(const e of n)i(e,0);await Promise.all(a.map(async e=>{try{const t=await joplin.data.get(["folders",e.id,"notes"],{fields:["id"],include_deleted:"1",limit:1});e.hasNotes=!(!t.items||!t.items.length)}catch(t){e.hasNotes=!0}}));const l=[];let r=1,d=!0;for(;d&&l.length<200;){const e=await joplin.data.get(["notes"],{fields:["id","title","parent_id","is_todo","todo_completed","deleted_time"],include_deleted:"1",order_by:"deleted_time",order_dir:"DESC",page:r,limit:50});let a=!1;for(const s of e.items||[]){if(!s.deleted_time){a=!0;break}let e=!1,n=s.parent_id,i=0;for(;n&&i++<50;){if(t[n]){e=!0;break}n=o[n]||""}e||l.push({id:s.id,title:s.title||"(untitled)",is_todo:s.is_todo,todo_completed:s.todo_completed})}if(a)break;d=e.has_more,r++}return{folders:a,notes:l}}async function re(e){const t={};try{let e=1,o=!0;for(;o;){const a=await joplin.data.get(["folders"],{fields:["id","parent_id"],include_deleted:"1",page:e,limit:100});for(const e of a.items||[])e.parent_id&&(t[e.parent_id]=t[e.parent_id]||[]).push(e.id);o=a.has_more,e++}}catch(e){}const o=async e=>{for(const a of t[e]||[])await o(a);try{let t=1,o=!0;for(;o;){const a=await joplin.data.get(["folders",e,"notes"],{fields:["id"],include_deleted:"1",page:t,limit:100});for(const e of a.items||[])try{await joplin.data.delete(["notes",e.id],{permanent:"1"})}catch(e){}o=(a.items||[]).length>0&&a.has_more}}catch(e){}try{await joplin.data.delete(["folders",e],{permanent:"1"})}catch(e){}};await o(e)}async function de(e){return((await joplin.data.get(["notes"],{fields:["id","title","is_todo","todo_completed","user_updated_time"],order_by:"user_updated_time",order_dir:"DESC",limit:Math.min(e,100)})).items||[]).map(e=>({id:e.id,title:e.title||"(untitled)",is_todo:e.is_todo,todo_completed:e.todo_completed}))}async function ce(e){const t=[];let o=1,a=!0;for(;a&&t.length<e&&o<=10;){const e=await joplin.data.get(["notes"],{fields:["id","title","is_todo","todo_completed","user_updated_time"],order_by:"is_todo",order_dir:"DESC",page:o,limit:100});let s=!1;for(const o of e.items||[]){if(!o.is_todo){s=!0;break}o.todo_completed||t.push({id:o.id,title:o.title||"(untitled)",is_todo:o.is_todo,todo_completed:o.todo_completed,user_updated_time:o.user_updated_time||0})}if(s)break;a=e.has_more,o++}return t.sort((e,t)=>(t.user_updated_time||0)-(e.user_updated_time||0)),t.slice(0,e)}async function pe(e){return await Promise.all(e.map(async e=>{try{const t=await joplin.data.get(["notes",e.id],{fields:["is_shared","body"]}),o=l(t.body||"");e.is_shared=t.is_shared,e.cb_total=o.total,e.cb_done=o.done,e.badges=d(e)}catch(t){e.badges=""}})),e}await joplin.settings.onChange(async e=>{e.keys&&e.keys.indexOf("autoRefresh")>=0&&await be(),e.keys&&(e.keys.indexOf("toggleArrowPosition")>=0||e.keys.indexOf("showSmartFolders")>=0||e.keys.indexOf("smartFolderRules")>=0||e.keys.indexOf("hoverPreview")>=0||e.keys.indexOf("expandAllMode")>=0||e.keys.indexOf("collapseAllScope")>=0||e.keys.indexOf("showTagsSection")>=0||e.keys.indexOf("tagNestSeparator")>=0||e.keys.indexOf("stackSectionHeaders")>=0||e.keys.indexOf("sectionOrder")>=0||e.keys.indexOf("sectionSpacing")>=0||e.keys.indexOf("showFolderToggles")>=0||e.keys.indexOf("openFolderIcon")>=0||e.keys.indexOf("closedFolderIcon")>=0||e.keys.indexOf("openPinnedIcon")>=0||e.keys.indexOf("closedPinnedIcon")>=0||e.keys.indexOf("noteIcon")>=0)&&(e.keys.indexOf("noteIcon")>=0&&await i(),function(){for(const e of Object.keys(u))delete u[e]}(),await se())}),await joplin.views.panels.onMessage(o,async t=>{if("openNote"===t.name)D=t.id,await joplin.commands.execute("openNote",t.id),await joplin.views.panels.postMessage(o,{name:"selectNote",id:t.id});else if("refresh"===t.name)await se();else if("toggleFolder"===t.name)T[t.id]?delete T[t.id]:T[t.id]=!0,X();else if("collapseAll"===t.name){for(const e of C)T[e.id]=!0;Array.isArray(t.prevCollapsed)&&(z=t.prevCollapsed.map(String)),t.sections&&(A=!0,U=!0,P=!0,M=!0,L=!0),X()}else if("expandAll"===t.name){if(T={},Array.isArray(t.collapsedIds))for(const e of t.collapsedIds)T[String(e)]=!0;t.sections&&(A=!1,U=!1,P=!1,M=!1,L=!1),X()}else if("panelReady"===t.name)await se();else if("notePreview"===t.name)try{if(!1===await joplin.settings.value("hoverPreview"))return;const e=Number(await joplin.settings.value("hoverPreviewLength"))||200,a=await joplin.data.get(["notes",t.id],{fields:["id","title","body","is_todo","user_created_time","user_updated_time"],include_deleted:"1"});if(!a)return;const s=String(a.body||"").replace(/```[\s\S]*?```/g," ").replace(/!\[[^\]]*\]\([^)]*\)/g," ").replace(/\[([^\]]*)\]\([^)]*\)/g,"$1").replace(/^#{1,6}\s+/gm,"").replace(/[`*_>~]/g,"").replace(/\s+/g," ").trim().slice(0,e);await joplin.views.panels.postMessage(o,{name:"notePreview",id:t.id,title:a.title||"(untitled)",created:a.user_created_time||0,updated:a.user_updated_time||0,isTodo:!!a.is_todo,size:String(a.body||"").length,snippet:s})}catch(e){}else if("refreshView"===t.name)await se();else if("newNotebook"===t.name)try{await joplin.commands.execute("openFolderDialog",{isNew:!0,parentId:""}),ae()}catch(e){console.error("Joplin Explorer: newNotebook error",e)}else if("newNote"===t.name){await joplin.commands.execute("newNote");const e=await joplin.workspace.selectedNote();e&&(D=e.id,oe(e.parent_id)),await se()}else if("newTodo"===t.name){await joplin.commands.execute("newTodo");const e=await joplin.workspace.selectedNote();e&&(D=e.id,oe(e.parent_id)),await se()}else if("search"===t.name)await async function(e){const t=e.query;if(t&&t.trim())try{const a=t.toLowerCase(),s={};for(const e of C)s[e.id]=e.title;let n=[],i=1,l=!0;for(;l&&n.length<100;){const e=await joplin.data.get(["search"],{query:t,fields:["id","title","body","parent_id","is_todo","todo_completed"],page:i,limit:20});n=n.concat(e.items),l=e.has_more,i++}const r=new Set(n.map(e=>e.id)),d=[];for(const e of R)!r.has(e.id)&&(e.title||"").toLowerCase().indexOf(a)>=0&&d.push(e);for(let e=0;e<d.length&&e<50;e++)try{const t=await joplin.data.get(["notes",d[e].id],{fields:["id","title","body","parent_id","is_todo","todo_completed"]});n.push(t)}catch(e){}const c=[];for(const e of n){let t="";const o=e.body||"",n=o.toLowerCase().indexOf(a);if(n>=0){const e=Math.max(0,n-40),s=Math.min(o.length,n+a.length+80);t=(e>0?"...":"")+o.substring(e,s).replace(/\n/g," ")+(s<o.length?"...":"")}else t=o.substring(0,120).replace(/\n/g," ")+(o.length>120?"...":"");c.push({id:e.id,title:e.title||"(untitled)",is_todo:e.is_todo,todo_completed:e.todo_completed,snippet:t,folderName:s[e.parent_id]||""})}let p=[],m=1,u=!0;for(;u;){const e=await joplin.data.get(["tags"],{fields:["id","title"],page:m,limit:100});p=p.concat(e.items),u=e.has_more,m++}const f=[];for(const e of p)if((e.title||"").toLowerCase().indexOf(a)>=0){let t=0;try{const o=await joplin.data.get(["tags",e.id,"notes"],{fields:["id"],limit:100});t=o.has_more?o.items.length+"+":o.items.length}catch(e){}f.push({id:e.id,title:e.title,noteCount:t})}const g=[];for(const e of C)if((e.title||"").toLowerCase().indexOf(a)>=0){let t="";if(e.icon&&"string"==typeof e.icon)try{const o=JSON.parse(e.icon);o&&o.emoji&&(t=String(o.emoji))}catch(e){}g.push({id:e.id,title:e.title,parent_id:e.parent_id,iconEmoji:t})}await joplin.views.panels.postMessage(o,{name:"searchResults",notes:c,tags:f,folders:g,query:t,searchId:e.searchId})}catch(e){console.error("Joplin Explorer: search error",e)}else await joplin.views.panels.postMessage(o,{name:"searchResults",results:null,query:"",searchId:e.searchId})}(t);else if("loadTagNotes"===t.name)try{const e=t.tagId;let a=[],s=1,n=!0;for(;n&&a.length<100;){const t=await joplin.data.get(["tags",e,"notes"],{fields:["id","title","parent_id","is_todo","todo_completed"],page:s,limit:50});a=a.concat(t.items),n=t.has_more,s++}const i={};for(const e of C)i[e.id]=e.title;const l=a.map(e=>({id:e.id,title:e.title||"(untitled)",is_todo:e.is_todo,todo_completed:e.todo_completed,snippet:"",folderName:i[e.parent_id]||""}));await joplin.views.panels.postMessage(o,{name:"tagNotes",tagId:e,notes:l})}catch(e){console.error("Joplin Explorer: loadTagNotes error",e)}else if("toggleTagsSection"===t.name)P=!P,X();else if("tagFolderNotes"===t.name)try{const e=await ie(t.tagId);await joplin.views.panels.postMessage(o,{name:"tagFolderNotes",tagId:t.tagId,notes:e})}catch(e){console.error("Joplin Explorer: tagFolderNotes error",e)}else if("tagNoteAdd"===t.name)try{await joplin.data.post(["tags",t.tagId,"notes"],null,{id:t.noteId});const e=await ie(t.tagId);await joplin.views.panels.postMessage(o,{name:"tagFolderNotes",tagId:t.tagId,notes:e})}catch(e){console.error("Joplin Explorer: tagNoteAdd error",e)}else if("toggleSmartSection"===t.name)U=!U,X();else if("toggleTreeSection"===t.name)L=!L,X();else if("smartFolderNotes"===t.name)try{const e=Math.max(1,Number(await joplin.settings.value("smartFolderLimit"))||5);if("recent"===t.smartId||"todos"===t.smartId){const a="recent"===t.smartId?await de(e):await ce(e);return await pe(a),void await joplin.views.panels.postMessage(o,{name:"smartFolderNotes",smartId:t.smartId,notes:a})}let a=[],s=1,n=!0;for(;n&&a.length<100;){const e=await joplin.data.get(["search"],{query:String(t.query||""),fields:["id","title","is_todo","todo_completed","user_updated_time"],page:s,limit:50});a=a.concat((e.items||[]).map(e=>({id:e.id,title:e.title||"(untitled)",is_todo:e.is_todo,todo_completed:e.todo_completed,user_updated_time:e.user_updated_time||0}))),n=e.has_more,s++}a.sort((e,t)=>(t.user_updated_time||0)-(e.user_updated_time||0));const i=a.slice(0,e);await pe(i),await joplin.views.panels.postMessage(o,{name:"smartFolderNotes",smartId:t.smartId,notes:i})}catch(e){console.error("Joplin Explorer: smartFolderNotes error",e)}else if("toggleTrashSection"===t.name)M=!M,X();else if("trashFolderNotes"===t.name)try{let e=[],a=1,s=!0;for(;s&&e.length<200;){const o=await joplin.data.get(["folders",t.folderId,"notes"],{fields:["id","title","is_todo","todo_completed"],include_deleted:"1",page:a,limit:50});e=e.concat((o.items||[]).map(e=>({id:e.id,title:e.title||"(untitled)",is_todo:e.is_todo,todo_completed:e.todo_completed}))),s=o.has_more,a++}await joplin.views.panels.postMessage(o,{name:"trashFolderNotes",folderId:t.folderId,notes:e})}catch(e){console.error("Joplin Explorer: trashFolderNotes error",e)}else if("trashNotes"===t.name)try{const e=await le();await joplin.views.panels.postMessage(o,{name:"trashNotes",folders:e.folders,notes:e.notes})}catch(e){console.error("Joplin Explorer: trashNotes error",e)}else if("locateFolder"===t.name)try{oe(t.folderId),await se(),await joplin.views.panels.postMessage(o,{name:"exitSearchAndLocate",folderId:t.folderId})}catch(e){console.error("Joplin Explorer: locateFolder error",e)}else if("revealNote"===t.name){if(Array.isArray(t.folderIds)){for(const e of t.folderIds)delete T[String(e)];X()}}else if("locatePinnedFolder"===t.name)try{oe(t.folderId),await se(),await joplin.views.panels.postMessage(o,{name:"scrollToFolder",folderId:t.folderId})}catch(e){console.error("Joplin Explorer: locatePinnedFolder error",e)}else if("pinItemAt"===t.name)try{const e=String(t.dragId||"");if(e){const o="folder"===t.dragType?"folder":"note";O=O.filter(t=>t.id!==e);let a=O.length;if(t.targetId){const e=O.findIndex(e=>e.id===t.targetId);e>=0&&(a=e+("after"===t.position?1:0))}O.splice(a,0,{id:e,type:o}),await ee(),await se()}}catch(e){console.error("Joplin Explorer: pinItemAt error",e)}else if("reorderPinned"===t.name)try{const e=t.dragId,o=t.targetId,a=t.position,s=O.findIndex(t=>t.id===e);if(s<0)return;const n=O[s];O.splice(s,1);let i=O.findIndex(e=>e.id===o);i<0?O.push(n):("after"===a&&i++,O.splice(i,0,n)),await ee(),await se()}catch(e){console.error("Joplin Explorer: reorderPinned error",e)}else if("togglePinnedCollapse"===t.name)A=!A,X();else if("cycleSort"===t.name){const e=["updated_desc","updated_asc","title_asc","title_desc","manual"],t=e.indexOf(E);E=e[(t+1)%e.length];try{await joplin.settings.setValue("noteSortMode",E)}catch(e){}await se()}else if("syncCancel"===t.name)try{await joplin.commands.execute("synchronize",!0)}catch(e){console.error("Joplin Explorer: sync cancel failed",e)}else if("sync"===t.name)try{await joplin.commands.execute("synchronize",!1)}catch(t){console.error("Joplin Explorer: sync command failed",t),await joplin.views.panels.postMessage(o,{name:"syncState",state:"error"});const a=t&&t.message?String(t.message):String(t);await k(e.syncFailed,a)}else if("contextMenu"===t.name)await async function(t){const a=t.action,s=t.id,n=t.itemType;try{if("folder"===n)switch(a){case"newNote":{const t=await joplin.data.post(["notes"],null,{title:e.newNote,parent_id:s});await joplin.commands.execute("openNote",t.id),D=t.id,oe(s);break}case"newTodo":{const t=await joplin.data.post(["notes"],null,{title:e.newTodo,parent_id:s,is_todo:1});await joplin.commands.execute("openNote",t.id),D=t.id,oe(s);break}case"newSubNotebook":try{delete T[s],await joplin.commands.execute("openFolderDialog",{isNew:!0,parentId:s}),ae()}catch(e){console.error("Joplin Explorer: newSubNotebook error",e)}break;case"deleteFolder":{const t=await joplin.data.get(["folders",s],{fields:["title"]});await j(e.confirmDeleteFolder+"\n\n"+t.title)&&await joplin.data.delete(["folders",s]);break}case"renameFolder":try{const e=(await joplin.data.get(["folders",s],{fields:["updated_time"]})).updated_time,t=D;await joplin.commands.execute("openFolderDialog",{folderId:s}),(async()=>{for(let o=0;o<240;o++){await new Promise(e=>setTimeout(e,500));try{if((await joplin.data.get(["folders",s],{fields:["updated_time"]})).updated_time!==e){if(Z(100),t){await new Promise(e=>setTimeout(e,400));try{const e=await joplin.workspace.selectedNote();e&&e.id===t||await joplin.commands.execute("openNote",t)}catch(e){}}return}}catch(e){return void Z(100)}}})()}catch(t){const o=await joplin.data.get(["folders",s],{fields:["title"]}),a=await I(e.promptRename,o.title);a&&a.trim()&&await joplin.data.put(["folders",s],null,{title:a.trim()})}break;case"copyFolderId":try{await joplin.clipboard.writeText(s)}catch(e){await joplin.views.panels.postMessage(o,{name:"copyText",text:s})}break;case"exportFolder":try{const e=await joplin.views.dialogs.showOpenDialog({properties:["openDirectory"]}),o=Array.isArray(e)?e[0]:e;o&&await joplin.commands.execute("exportFolders",[s],t.format||"jex",o)}catch(t){console.error("Joplin Explorer: exportFolder error",t),await k(e.exportFailed||"Export failed",String(t&&t.message?t.message:t))}break;case"pinFolder":O.some(e=>e.id===s)||(O.push({id:s,type:"folder"}),await ee());break;case"unpinFolder":O=O.filter(e=>e.id!==s),await ee()}else if("note"===n)switch(a){case"openNote":await joplin.commands.execute("openNote",s),D=s;break;case"openInNewWindow":try{await joplin.commands.execute("openNoteInNewWindow",s)}catch(e){await joplin.commands.execute("openNote",s)}break;case"editExternally":try{await joplin.commands.execute("openNote",s),D=s,await joplin.commands.execute("startExternalEditing",s)}catch(e){await k("Joplin",String(e&&e.message||e))}break;case"copyLink":{const e=await joplin.data.get(["notes",s],{fields:["id","title"]}),t="["+e.title+"](:/"+e.id+")";try{await joplin.clipboard.writeText(t)}catch(e){await joplin.views.panels.postMessage(o,{name:"copyText",text:t})}break}case"duplicateNote":{const e=await joplin.data.get(["notes",s],{fields:["title","body","parent_id","is_todo"]}),t=await joplin.data.post(["notes"],null,{title:e.title+" (copy)",body:e.body,parent_id:e.parent_id,is_todo:e.is_todo});await joplin.commands.execute("openNote",t.id),D=t.id;break}case"switchNoteType":{const e=await joplin.data.get(["notes",s],{fields:["is_todo"]});await joplin.data.put(["notes",s],null,{is_todo:e.is_todo?0:1});break}case"toggleTodo":{const e=await joplin.data.get(["notes",s],{fields:["is_todo","todo_completed"]});e.is_todo&&await joplin.data.put(["notes",s],null,{todo_completed:e.todo_completed?0:Date.now()});break}case"publishNote":try{await joplin.commands.execute("showShareNoteDialog",[s])}catch(e){}break;case"exportPdf":try{await joplin.commands.execute("exportPdf",[s])}catch(e){console.error("Joplin Explorer: exportPdf error",e)}break;case"exportNote":try{const e=await joplin.views.dialogs.showOpenDialog({properties:["openDirectory"]}),o=Array.isArray(e)?e[0]:e;o&&await joplin.commands.execute("exportNotes",[s],t.format||"md",o)}catch(e){console.error("Joplin Explorer: exportNote error",e)}break;case"setTags":try{await joplin.commands.execute("setTags",[s])}catch(e){}break;case"moveToFolderDialog":try{await joplin.commands.execute("moveToFolder",[s])}catch(e){}break;case"copyExternalLink":{const e="joplin://x-callback-url/openNote?id="+s;try{await joplin.clipboard.writeText(e)}catch(t){await joplin.views.panels.postMessage(o,{name:"copyText",text:e})}break}case"renameNote":{const t=await joplin.data.get(["notes",s],{fields:["title"]}),o=await I(e.promptRename,t.title);o&&o.trim()&&await joplin.data.put(["notes",s],null,{title:o.trim()});break}case"moveNote":if(t.targetFolderName){let e=null;for(const o of C)if(o.title===t.targetFolderName){e=o;break}e&&await joplin.data.put(["notes",s],null,{parent_id:e.id})}break;case"noteInfo":{const t=await joplin.data.get(["notes",s],{fields:["id","title","created_time","updated_time","is_todo","parent_id","body"]}),o=F[t.parent_id]?F[t.parent_id].title:"",a=(t.body||"").length,n="ID: "+t.id+"\n"+(e.ctxRenameNote||"Title")+": "+t.title+"\n"+(e.newNotebook||"Notebook")+": "+o+"\n"+(e.sortUpdatedDesc?e.sortUpdatedDesc.replace(/[↓↑\u2193\u2191]\s*/,""):"Created")+": "+new Date(t.created_time).toLocaleString()+"\n"+(e.sortUpdatedAsc?e.sortUpdatedAsc.replace(/[↓↑\u2193\u2191]\s*/,""):"Updated")+": "+new Date(t.updated_time).toLocaleString()+"\nType: "+(t.is_todo?"To-do":"Note")+"\nSize: "+a+" chars";await k(t.title,n);break}case"deleteNote":{const t=await joplin.data.get(["notes",s],{fields:["title"]});await j(e.confirmDeleteNote+"\n\n"+t.title)&&await joplin.data.delete(["notes",s]);break}case"untagNote":if(t.tagId){await joplin.data.delete(["tags",t.tagId,"notes",s]);try{const e=await ie(t.tagId);await joplin.views.panels.postMessage(o,{name:"tagFolderNotes",tagId:t.tagId,notes:e})}catch(e){}}break;case"pinNote":O.some(e=>e.id===s)||(O.push({id:s,type:"note"}),await ee());break;case"unpinNote":O=O.filter(e=>e.id!==s),await ee()}else if("tag"===n)switch(a){case"renameTag":{const t=await joplin.data.get(["tags",s],{fields:["title"]}),o=await I(e.ctxRenameTag,t.title);o&&o.trim()&&await joplin.data.put(["tags",s],null,{title:o.trim()});break}case"deleteTag":{const t=await joplin.data.get(["tags",s],{fields:["title"]});await j(e.confirmDeleteTag+"\n\n"+t.title)&&await joplin.data.delete(["tags",s]);break}}else if("trashSection"===n){if("emptyTrash"===a&&await j(e.confirmEmptyTrash)){try{await joplin.commands.execute("emptyTrash")}catch(e){const t=await le();for(const e of t.folders)0===e.depth&&await re(e.id);for(const e of t.notes)try{await joplin.data.delete(["notes",e.id],{permanent:"1"})}catch(e){}}try{const e=await le();await joplin.views.panels.postMessage(o,{name:"trashNotes",folders:e.folders,notes:e.notes})}catch(e){}}}else if("trashNote"===n||"trashFolder"===n){const t="trashFolder"===n;switch(a){case"restoreItem":try{if(!t){let t=null;try{const e=await joplin.data.get(["notes",s],{fields:["parent_id"],include_deleted:"1"});if(e&&e.parent_id){const o=await joplin.data.get(["folders",e.parent_id],{fields:["id","title","deleted_time"],include_deleted:"1"});o&&o.deleted_time>0&&(t=o)}}catch(e){}if(t){const o=(e.confirmRestoreParent||'The notebook "{folder}" that held this note is also in the trash. Restore the notebook together with the note?').replace("{folder}",t.title||""),a=await async function(t,o,a){return await joplin.views.dialogs.setHtml(v,'<div style="padding:10px;box-sizing:border-box;width:360px;max-width:100%;"><div style="font-size:13px;line-height:1.6;white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere;">'+c(t)+"</div></div>"),await joplin.views.dialogs.setButtons(v,[{id:"both",title:o},{id:"noteOnly",title:a},{id:"cancel",title:e.cancel||"Cancel"}]),(await joplin.views.dialogs.open(v)).id}(o,e.restoreBoth||"Restore notebook + note",e.restoreNoteOnly||"Note only (to root)");if("cancel"===a)break;if("both"===a){await joplin.commands.execute("restoreFolder",[t.id]);break}}}await joplin.commands.execute(t?"restoreFolder":"restoreNote",[s])}catch(e){console.error("Joplin Explorer: restore error",e)}break;case"permanentDeleteItem":{const o=t?"folders":"notes";let a="";try{const e=await joplin.data.get([o,s],{fields:["title"],include_deleted:"1"});a=e?e.title:""}catch(e){}if(await j(e.confirmDeleteNote+"\n\n"+a))if(t)await re(s);else try{await joplin.data.delete(["notes",s],{permanent:"1"})}catch(e){try{await joplin.commands.execute("permanentlyDeleteNote",[s])}catch(e){}}break}}try{const e=await le();await joplin.views.panels.postMessage(o,{name:"trashNotes",folders:e.folders,notes:e.notes})}catch(e){}}await se()}catch(e){console.error("Notes In List: context menu error",e)}}(t);else if("dragDrop"===t.name)await async function(e){try{const t=e.dragId,o=e.dragType,a=e.targetId,s=e.position;if("note"===o)if(F[a])await joplin.data.put(["notes",t],null,{parent_id:a});else{const e=await joplin.data.get(["notes",a],{fields:["parent_id"]});"manual"!==E||"above"!==s&&"below"!==s?await joplin.data.put(["notes",t],null,{parent_id:e.parent_id}):await async function(e,t,o){const a=(await joplin.data.get(["notes",t],{fields:["parent_id"]})).parent_id,s=R.filter(t=>t.parent_id===a&&t.id!==e).sort((e,t)=>{const o=e.order||0,a=t.order||0;return o!==a?a-o:(t.user_updated_time||0)-(e.user_updated_time||0)}),n=s.findIndex(e=>e.id===t),i=n<0?s.length:"above"===o?n:n+1,l=i>0?s[i-1]:null,r=i<s.length?s[i]:null,d=l?l.order||0:null,c=r?r.order||0:null,p=1e6;if(s.every(e=>0===(e.order||0))||null!==d&&null!==c&&d-c<2){const t=Date.now(),o=s.slice();o.splice(i,0,null);let n=t;const l=[];for(let e=0;e<o.length;e++){const a=t-e*p,s=o[e];null===s?n=a:l.push(joplin.data.put(["notes",s.id],null,{order:a}))}return await Promise.all(l),void await joplin.data.put(["notes",e],null,{parent_id:a,order:n})}let m;m=null!==d&&null!==c?(d+c)/2:null!==d?d-p:null!==c?c+p:Date.now(),await joplin.data.put(["notes",e],null,{parent_id:a,order:m})}(t,a,s)}else if("folder"===o){const e=!!F[a];"into"===s?e&&t!==a&&await joplin.data.put(["folders",t],null,{parent_id:a}):!e||"above"!==s&&"below"!==s||await async function(e,t,o){const a=F[t],s=F[e];if(!a||!s||e===t)return;const n=a.parent_id||"";let i=n;for(;i;){if(i===e)return;i=F[i]&&F[i].parent_id||""}const l=C.filter(t=>(t.parent_id||"")===n&&t.id!==e).sort((e,t)=>{const o=e.order||0,a=t.order||0;return o!==a?a-o:(e.title||"").localeCompare(t.title||"")}),r=l.findIndex(e=>e.id===t),d=r<0?l.length:"above"===o?r:r+1,c=d>0?l[d-1]:null,p=d<l.length?l[d]:null,m=c?c.order||0:null,u=p?p.order||0:null,f=l.every(e=>0===(e.order||0)),g=null!==m&&null!==u&&m-u<2,h=1e6;if((s.parent_id||"")!==n&&await joplin.data.put(["folders",e],null,{parent_id:n}),f||g){const t=Date.now(),o=l.slice();o.splice(d,0,null);for(let a=0;a<o.length;a++){const s=t-a*h,n=o[a];null===n?q[e]=s:q[n.id]=s}return void await te()}let w;w=null!==m&&null!==u?(m+u)/2:null!==m?m-h:null!==u?u+h:Date.now(),q[e]=w,await te()}(t,a,s)}await se()}catch(e){console.error("Notes In List: drag drop error",e)}}(t);else if("dragToEmpty"===t.name)try{const e=t.dragId,o=t.dragType,a=new Set((await g()).map(e=>e.id));await joplin.commands.execute("openFolderDialog",{isNew:!0,parentId:""}),(async()=>{for(let t=0;t<240;t++){await new Promise(e=>setTimeout(e,500));try{const t=(await g()).find(e=>!a.has(e.id));if(t)return J[t.id]=!0,delete T[t.id],"note"===o?await joplin.data.put(["notes",e],null,{parent_id:t.id}):"folder"===o&&e!==t.id&&await joplin.data.put(["folders",e],null,{parent_id:t.id}),X(),void await se()}catch(e){return}}})()}catch(e){console.error("Joplin Explorer: drag to empty error",e)}});let me="",ue=null,fe="";async function ge(){const e=[];let t=1,o=!0;for(;o;){const a=await joplin.data.get(["folders"],{fields:["id","title","parent_id","icon"],page:t,limit:100});for(const t of a.items||[])e.push(t.id+""+(t.title||"")+""+(t.parent_id||"")+""+(t.icon||""));o=a.has_more,t++}const a=e.sort().join("\n"),s=""!==fe&&a!==fe;return fe=a,s}let he="",we=null,ye=null;async function xe(){let e=!1;try{if(D){const t=((await joplin.data.get(["notes",D,"tags"],{fields:["id"],limit:100})).items||[]).map(e=>e.id).sort().join(",");D===he&&null!==we&&we!==t&&(e=!0),he=D,we=t}}catch(e){}try{let t="",o=1,a=!0;for(;a&&o<=5;){const e=await joplin.data.get(["tags"],{fields:["id","title","updated_time"],page:o,limit:100});for(const o of e.items||[])t+=o.id+(o.title||"")+o.updated_time+";";a=e.has_more,o++}null!==ye&&ye!==t&&(e=!0),ye=t}catch(e){}return e}async function ve(){try{if(!me){const e=await joplin.data.get(["events"]);return me=String(e.cursor||""),await ge(),void await xe()}let e=!1,t=0;for(;t++<20;){const t=await joplin.data.get(["events"],{cursor:me});for(const o of t.items||[])2===Number(o.type)&&o.item_id===D||(e=!0);if(t.cursor&&(me=String(t.cursor)),!t.has_more)break}!e&&await ge()&&(e=!0),!e&&await xe()&&(e=!0),e&&Z(300)}catch(e){ue&&(clearInterval(ue),ue=null),console.warn("Joplin Explorer: change-events polling disabled",e)}}async function be(){const e=!1!==await joplin.settings.value("autoRefresh");ue&&(clearInterval(ue),ue=null),e&&(me="",await ve(),ue=setInterval(ve,5e3))}await be();try{{const t=e=>async t=>{const o=String(t.sourcePath||""),a=function(e){let t=e.toString("utf8");if(t.indexOf("�")>=0)try{t=new globalThis.TextDecoder("gbk").decode(e)}catch(e){}return 65279===t.charCodeAt(0)&&(t=t.slice(1)),t}(s.readFileSync(o)),i=n.basename(o).replace(/\.(csv|tsv)$/i,""),l=e?function(e,t){const o=function(e,t){const o=[];let a=[],s="",n=!1;for(let i=0;i<e.length;i++){const l=e[i];n?'"'===l?'"'===e[i+1]?(s+='"',i++):n=!1:s+=l:'"'===l?n=!0:l===t?(a.push(s),s=""):"\n"===l||"\r"===l?("\r"===l&&"\n"===e[i+1]&&i++,a.push(s),s="",o.push(a),a=[]):s+=l}for((""!==s||a.length)&&(a.push(s),o.push(a));o.length&&o[o.length-1].every(e=>""===e);)o.pop();return o}(e,t);if(!o.length)return"";const a=Math.max(...o.map(e=>e.length)),s=e=>"| "+Array.from({length:a},(t,o)=>(e[o]||"").replace(/\|/g,"\\|").replace(/\r?\n/g,"<br>").trim()).join(" | ")+" |",n=[s(o[0]),"|"+Array.from({length:a},()=>" --- |").join("")];for(let e=1;e<o.length;e++)n.push(s(o[e]));return n.join("\n")+"\n"}(a,/\.tsv$/i.test(o)?"\t":","):"```csv\n"+a.replace(/```/g,"```")+(a.endsWith("\n")?"":"\n")+"```\n",r=t.options&&t.options.destinationFolderId||"",d={title:i,body:l};r&&(d.parent_id=r),await joplin.data.post(["notes"],null,d)},o=(e,t)=>(e||t).replace(/^\s*CSV\s*→\s*/i,"");await joplin.interop.registerImportModule({format:"csv",description:o(e.impFmtCsvTable,"Markdown table"),fileExtensions:["csv","tsv"],sources:["file"],isNoteArchive:!1,onExec:t(!0)}),await joplin.interop.registerImportModule({format:"csv_code",description:o(e.impFmtCsvCode,"Code block"),fileExtensions:["csv","tsv"],sources:["file"],isNoteArchive:!1,onExec:t(!1)}),console.info("Joplin Explorer: CSV import modules registered")}}catch(e){console.warn("Joplin Explorer: CSV import module registration failed (older Joplin without joplin.interop? context-menu CSV import still works)",e)}await joplin.workspace.onNoteSelectionChange(async()=>{const e=await joplin.workspace.selectedNote();e&&e.id!==D&&(D=e.id,await joplin.views.panels.postMessage(o,{name:"selectNote",id:e.id}))}),await joplin.workspace.onNoteChange(async e=>{var t;e&&e.id?(t=e.id,Y[t]=!0,Q&&clearTimeout(Q),Q=setTimeout(async()=>{Q=null;const e=Object.keys(Y);Y={};for(const t of e)await ne(t)},600)):Z()});let Se=null;try{await joplin.workspace.onSyncStart(async()=>{Se&&(clearTimeout(Se),Se=null),await joplin.views.panels.postMessage(o,{name:"syncState",state:"syncing"})})}catch(e){console.warn("Joplin Explorer: onSyncStart not available",e)}try{await joplin.workspace.onSyncComplete(async t=>{const a=!(!t||!t.withErrors);await se(),Se&&clearTimeout(Se),Se=setTimeout(()=>{Se=null,joplin.views.panels.postMessage(o,{name:"syncState",state:a?"error":"done"})},800),a&&await k(e.syncFailed,"Joplin reported errors during synchronisation. Open the Synchronisation Status screen (Tools → Synchronisation Status) to see the details.")})}catch(e){console.warn("Joplin Explorer: onSyncComplete not available",e)}await se()}})})();
|
|
1
|
+
(()=>{"use strict";var e={982(e){e.exports=require("crypto")},896(e){e.exports=require("fs")},928(e){e.exports=require("path")}},t={};function o(a){var s=t[a];if(void 0!==s)return s.exports;var n=t[a]={exports:{}};return e[a](n,n.exports,o),n.exports}const a={zh_CN:{newNotebook:"新建笔记本",newNote:"新建笔记",newTodo:"新建待办",sort:"排序",collapseAll:"全部折叠",expandAll:"全部展开",search:"搜索笔记内容...",searchResultCount:"找到 {count} 条结果",searchNoResult:"没有找到匹配的笔记",searching:"搜索中...",sync:"同步",syncing:"同步中...",syncDone:"✔ 同步完成",syncFailed:"⚠ 同步失败",loading:"加载中...",syncTip:"同步 Joplin。注意:后台的附件下载和解密不计入完成状态,插件收不到它们的事件。",syncCancel:"取消同步",syncCancelling:"取消中...",sortUpdatedDesc:"↓ 修改时间",sortUpdatedAsc:"↑ 修改时间",sortTitleAsc:"↑ 标题",sortTitleDesc:"↓ 标题",sortManual:"⇅ 手动排序",ctxNewNoteHere:"在此新建笔记",ctxNewTodoHere:"在此新建待办",ctxNewSubNotebook:"新建子笔记本",ctxRenameFolder:"编辑",ctxExportFolder:"导出笔记本",ctxDeleteFolder:"删除笔记本",exportFailed:"导出失败",ctxImportFiles:"导入文件",importFailed:"导入失败",impFmtMd:"Markdown / 文本",impFmtEnex:"Evernote (ENEX)",impFmtCsvTable:"CSV → Markdown 表格",impFmtCsvCode:"CSV → 代码块",ctxOpenNote:"打开笔记",ctxOpenInNewWindow:"在新窗口中打开",ctxEditExternally:"用外部编辑器打开",ctxCopyLink:"复制 Markdown 链接",ctxDuplicateNote:"复制副本",ctxSwitchNoteType:"笔记/待办 切换",ctxToggleTodo:"切换完成状态",ctxRenameNote:"重命名",ctxMoveNote:"移动到笔记本...",ctxNoteInfo:"笔记属性",ctxDeleteNote:"删除笔记",confirmDeleteFolder:"确定删除此笔记本及其所有内容吗?",confirmDeleteNote:"确定删除此笔记吗?",promptRename:"请输入新名称:",promptMoveNote:"请输入目标笔记本名称:",promptNewNotebookName:"请输入新笔记本名称:",dropCreateNotebook:"释放以创建新笔记本",searchSectionNotes:"笔记",searchSectionTags:"标签",searchSectionFolders:"笔记本",searchTagNoteCount:"{count} 条笔记",pinned:"收藏夹",notebooksSection:"笔记本",tags:"标签",ctxRenameTag:"重命名标签",ctxDeleteTag:"删除标签",ctxUntagNote:"从标签移除",confirmDeleteTag:"删除该标签?(只删除标签本身,不会删除笔记)",ctxPublishNote:"发布笔记",ctxExport:"导出",sharedBadge:"已发布 · 点击打开发布对话框",sShowTags:"显示标签分区",sShowTagsDesc:"在笔记本树下方将 Joplin 标签显示为文件夹。",tagCountBoth:"{own} 条笔记带此标签,子树共 {sub} 条",tagCountSub:"子树共 {sub} 条笔记",sTagNestSep:"标签嵌套分隔符",sTagNestSepDesc:'按此分隔符将标签名切分并显示为层级树(例如填 "." 时,"工作.项目.甲" 会嵌套三层)。仅影响显示,不会修改标签本身。留空则保持扁平列表。',sAutoRefresh:"外部变更自动刷新",sAutoRefreshDesc:"每 5 秒轮询 Joplin 的变更事件,其他插件或同步创建、移动、删除笔记时自动刷新面板。当前打开笔记的编辑不受影响(本就实时更新)。",sRestoreUi:"启动时恢复上次视图状态",sRestoreUiDesc:"跨会话记住文件夹/分区的折叠状态。关闭后启动时全部收缩。",sShowSmart:"显示智能文件夹",sShowSmartDesc:"将保存的搜索显示为文件夹:最近更新、未完成待办,以及自定义规则。",sSmartRules:"自定义智能文件夹",sSmartRulesDesc:'"名称:查询" 用 ";" 分隔;第一个冒号之前是名称,之后全是查询(Joplin 搜索语法)。示例:工作:notebook:Work updated:day-30;星标:tag:star 会生成「工作」和「星标」两个文件夹。更方便的做法:工具 > 管理智能文件夹,或在搜索栏试好查询后点「存为智能文件夹」;右键智能文件夹可编辑查询、重命名或删除。',sSmartLimit:"智能文件夹结果条数",sSmartLimitDesc:"每个智能文件夹最多显示的笔记数(最新优先)。默认 5。",sHoverPreview:"悬停预览笔记",sHoverPreviewDesc:"悬停笔记时显示标题、更新时间和正文摘要。",sHoverPreviewLen:"悬停预览长度(字符)",sArrowPos:"展开箭头位置",sArrowPosDesc:"同时作用于笔记本、分区标题和分区行,保持风格统一。",sArrowRight:"文字后面",sArrowLeft:"文字左侧(经典树形)",sExpandMode:"展开按钮行为",sExpandModeDesc:"点击展开按钮时的动作。恢复:还原上次全部收缩前的状态(跟随浏览)。仅文件夹/全部展开:固定动作,不跟随浏览。仅折叠:按钮只折叠、不提供展开。",sExpandRestore:"恢复收缩前的状态",sExpandSkeleton:"仅文件夹(叶子笔记本保持收缩)",sExpandAllOpt:"全部展开",sExpandCollapseOnly:"仅折叠(不展开)",sCollapseScope:"折叠范围",sCollapseScopeDesc:"全部折叠按钮的作用范围。仅笔记本:只折叠笔记本树。全部分区:同时折叠收藏夹、智能文件夹、标签、回收站。",sScopeNotebooks:"仅笔记本",sScopeAll:"全部分区",sSectionOrder:"分区顺序",sSectionOrderDesc:"逗号分隔的分区顺序。可用键:pinned(收藏夹)、smart(智能文件夹)、notebooks(笔记本,旧键 tree 仍可用)、tags(标签)、trash(回收站)。遗漏的分区按默认顺序排在末尾。",clearSearch:"清空搜索",sStackHeaders:"分区标题常驻堆叠",sStackHeadersDesc:"滚动时已经过的分区标题堆叠在顶部、未到达的堆叠在底部,任何时候每个分区都一键可达。关闭则仅当前分区标题吸顶。",sSectionGap:"分区间距(px)",sSectionGapDesc:"每个分区分割线上方的留白。默认 5。",sRowFont:"行字号(px)",sRowFontDesc:"树形列表里的文字大小,图标跟着缩放。默认 13。",sRowHeight:"行高(px)",sRowHeightDesc:"每行的最小高度。实际高度不会低于内容本身,所以想真正压紧要连行字号一起调小。默认 24。",cToggle:"显示/隐藏 Explorer 侧栏",sNoteIcon:"笔记图标",sNoteIconDesc:"普通笔记(非待办)使用的图标。可填 Emoji、图片 URL、data URI 或本地图片路径。默认 📄。",sShowToggles:"显示展开箭头",sShowTogglesDesc:"在文件夹和收藏夹分区前显示展开/收缩箭头。",sOpenFolderIcon:"展开文件夹图标",sOpenFolderIconDesc:"无自定义笔记本图标时,展开的文件夹使用的 Emoji、图片 URL、data URI 或本地图片路径。",sClosedFolderIcon:"收起文件夹图标",sClosedFolderIconDesc:"无自定义笔记本图标时,收起的文件夹使用的 Emoji、图片 URL、data URI 或本地图片路径。",sOpenPinnedIcon:"收藏夹展开图标",sOpenPinnedIconDesc:"收藏夹分区展开时使用的 Emoji、图片 URL、data URI 或本地图片路径。",sClosedPinnedIcon:"收藏夹收起图标",sClosedPinnedIconDesc:"收藏夹分区收起时使用的 Emoji、图片 URL、data URI 或本地图片路径。",refresh:"刷新",newItem:"新建",trash:"回收站",ctxRestoreNote:"恢复",ctxPermanentDelete:"彻底删除",ctxEmptyTrash:"清空回收站",confirmEmptyTrash:"清空回收站?所有已删除的笔记本和笔记将被永久删除,无法恢复。",confirmRestoreParent:"这条笔记原来所在的笔记本「{folder}」也在回收站里。要连同笔记本一起恢复吗?",restoreBoth:"恢复笔记本和笔记",restoreNoteOnly:"仅恢复笔记(到根目录)",smartFolders:"智能文件夹",smartRecent:"最近更新",smartTodos:"未完成待办",smartSaveSearch:"存为智能文件夹",smartSaveName:"智能文件夹名称:",smartEditQueryLabel:"编辑「{name}」的搜索查询:",smartManage:"管理智能文件夹...",smartManageHint:"每行一个智能文件夹:名称和 Joplin 搜索查询。右侧数字是当前匹配的笔记数,为 0 通常说明查询写错了。「最近更新」和「未完成待办」是内置的,不在这里。",smartColName:"名称",smartColQuery:"查询",smartAdd:"添加",smartMoveUp:"上移",smartMoveDown:"下移",smartRemove:"删除",smartEmptyList:"还没有自定义智能文件夹。",smartCountTip:"当前匹配的笔记数(最多显示 100)",smartRowError:"第 {name} 行:{err}",smartDupName:"有两个智能文件夹都叫「{name}」",ctxSmartOpenSearch:"在搜索栏中打开",ctxSmartEditQuery:"编辑查询...",ctxSmartRename:"重命名...",ctxSmartDelete:"删除智能文件夹",smartConfirmDelete:"删除智能文件夹「{name}」?\n笔记本身不受影响。",smartConfirmReplace:"已有名为「{name}」的智能文件夹,替换它的查询?",smartInvalidName:"名称不能为空,也不能包含 : 或 ;",smartInvalidQuery:"查询不能为空,也不能包含 ;",smartInvalidTitle:"无法保存智能文件夹",pvType:"类型",pvSize:"大小",pvCreated:"创建",pvUpdated:"更新",pvNote:"笔记",pvTodo:"待办",pvChars:"字符",ctxSetTags:"设置标签",ctxMoveToFolder:"移动到笔记本",ctxCopyExternalLink:"复制外部链接",ctxCopyFolderId:"复制笔记本 ID",revealNote:"定位当前笔记",ctxPin:"📌 收藏",ctxUnpin:"取消收藏",cancel:"取消"},zh_TW:{newNotebook:"新建筆記本",newNote:"新建筆記",newTodo:"新建待辦",sort:"排序",collapseAll:"全部摺疊",expandAll:"全部展開",search:"搜尋筆記內容...",searchResultCount:"找到 {count} 條結果",searchNoResult:"沒有找到匹配的筆記",searching:"搜尋中...",sync:"同步",syncing:"同步中...",syncDone:"✔ 同步完成",syncFailed:"⚠ 同步失敗",loading:"載入中...",syncTip:"同步 Joplin。注意:背景的附件下載與解密不計入完成狀態,外掛收不到它們的事件。",syncCancel:"取消同步",syncCancelling:"取消中...",sortUpdatedDesc:"↓ 修改時間",sortUpdatedAsc:"↑ 修改時間",sortTitleAsc:"↑ 標題",sortTitleDesc:"↓ 標題",sortManual:"⇅ 手動排序",ctxNewNoteHere:"在此新建筆記",ctxNewTodoHere:"在此新建待辦",ctxNewSubNotebook:"新建子筆記本",ctxRenameFolder:"編輯",ctxExportFolder:"匯出筆記本",ctxDeleteFolder:"刪除筆記本",exportFailed:"匯出失敗",ctxImportFiles:"匯入檔案",importFailed:"匯入失敗",impFmtMd:"Markdown / 文字",impFmtEnex:"Evernote (ENEX)",impFmtCsvTable:"CSV → Markdown 表格",impFmtCsvCode:"CSV → 程式碼區塊",ctxOpenNote:"開啟筆記",ctxOpenInNewWindow:"在新視窗中開啟",ctxEditExternally:"用外部編輯器開啟",ctxCopyLink:"複製 Markdown 連結",ctxDuplicateNote:"複製副本",ctxSwitchNoteType:"筆記/待辦 切換",ctxToggleTodo:"切換完成狀態",ctxRenameNote:"重新命名",ctxMoveNote:"移動到筆記本...",ctxNoteInfo:"筆記屬性",ctxDeleteNote:"刪除筆記",confirmDeleteFolder:"確定刪除此筆記本及其所有內容嗎?",confirmDeleteNote:"確定刪除此筆記嗎?",promptRename:"請輸入新名稱:",promptMoveNote:"請輸入目標筆記本名稱:",promptNewNotebookName:"請輸入新筆記本名稱:",dropCreateNotebook:"釋放以建立新筆記本",searchSectionNotes:"筆記",searchSectionTags:"標籤",searchSectionFolders:"筆記本",searchTagNoteCount:"{count} 條筆記",pinned:"收藏夾",notebooksSection:"筆記本",tags:"標籤",ctxRenameTag:"重新命名標籤",ctxDeleteTag:"刪除標籤",ctxUntagNote:"自標籤移除",confirmDeleteTag:"刪除該標籤?(僅刪除標籤本身,不會刪除筆記)",ctxPublishNote:"發佈筆記",ctxExport:"匯出",sharedBadge:"已發佈 · 點擊開啟發佈對話框",sShowTags:"顯示標籤分區",sShowTagsDesc:"在筆記本樹下方將 Joplin 標籤顯示為資料夾。",tagCountBoth:"{own} 則筆記帶此標籤,子樹共 {sub} 則",tagCountSub:"子樹共 {sub} 則筆記",sTagNestSep:"標籤巢狀分隔符",sTagNestSepDesc:'依此分隔符切分標籤名稱並顯示為階層樹(例如填 "." 時,"工作.專案.甲" 會巢狀三層)。僅影響顯示,不會修改標籤本身。留空則維持扁平清單。',sAutoRefresh:"外部變更自動重新整理",sAutoRefreshDesc:"每 5 秒輪詢 Joplin 的變更事件,其他外掛或同步建立、移動、刪除筆記時自動重新整理面板。目前開啟筆記的編輯不受影響(本就即時更新)。",sRestoreUi:"啟動時還原上次檢視狀態",sRestoreUiDesc:"跨工作階段記住資料夾/分區的摺疊狀態。關閉後啟動時全部收合。",sShowSmart:"顯示智慧資料夾",sShowSmartDesc:"將儲存的搜尋顯示為資料夾:最近更新、未完成待辦,以及自訂規則。",sSmartRules:"自訂智慧資料夾",sSmartRulesDesc:'「名稱:查詢」用 ";" 分隔;第一個冒號之前是名稱,之後全是查詢(Joplin 搜尋語法)。範例:工作:notebook:Work updated:day-30;星標:tag:star 會產生「工作」和「星標」兩個資料夾。更方便的做法:工具 > 管理智慧資料夾,或在搜尋列試好查詢後點「存為智慧資料夾」;右鍵智慧資料夾可編輯查詢、重新命名或刪除。',sSmartLimit:"智慧資料夾結果筆數",sSmartLimitDesc:"每個智慧資料夾最多顯示的筆記數(最新優先)。預設 5。",sHoverPreview:"懸停預覽筆記",sHoverPreviewDesc:"懸停筆記時顯示標題、更新時間和內文摘要。",sHoverPreviewLen:"懸停預覽長度(字元)",sArrowPos:"展開箭頭位置",sArrowPosDesc:"同時作用於筆記本、分區標題和分區列,保持風格統一。",sArrowRight:"文字後面",sArrowLeft:"文字左側(經典樹狀)",sExpandMode:"展開按鈕行為",sExpandModeDesc:"點擊展開按鈕時的動作。還原:回到上次全部收合前的狀態(跟隨瀏覽)。僅資料夾/全部展開:固定動作,不跟隨瀏覽。僅收合:按鈕只收合、不提供展開。",sExpandRestore:"還原收合前的狀態",sExpandSkeleton:"僅資料夾(葉子筆記本保持收合)",sExpandAllOpt:"全部展開",sExpandCollapseOnly:"僅收合(不展開)",sCollapseScope:"收合範圍",sCollapseScopeDesc:"全部收合按鈕的作用範圍。僅筆記本:只收合筆記本樹。全部分區:同時收合最愛、智慧資料夾、標籤、回收站。",sScopeNotebooks:"僅筆記本",sScopeAll:"全部分區",sSectionOrder:"分區順序",sSectionOrderDesc:"逗號分隔的分區順序。可用鍵:pinned(最愛)、smart(智慧資料夾)、notebooks(筆記本,舊鍵 tree 仍可用)、tags(標籤)、trash(回收站)。遺漏的分區按預設順序排在末尾。",clearSearch:"清空搜尋",sStackHeaders:"分區標題常駐堆疊",sStackHeadersDesc:"捲動時已經過的分區標題堆疊在頂部、未到達的堆疊在底部,任何時候每個分區都一鍵可達。關閉則僅目前分區標題吸頂。",sSectionGap:"分區間距(px)",sSectionGapDesc:"每個分區分隔線上方的留白。預設 5。",sRowFont:"列字型大小(px)",sRowFontDesc:"樹狀清單裡的文字大小,圖示跟著縮放。預設 13。",sRowHeight:"列高(px)",sRowHeightDesc:"每列的最小高度。實際高度不會低於內容本身,所以想真正壓緊要連字型大小一起調小。預設 24。",cToggle:"顯示/隱藏 Explorer 側欄",sNoteIcon:"筆記圖示",sNoteIconDesc:"一般筆記(非待辦)使用的圖示。可填 Emoji、圖片 URL、data URI 或本機圖片路徑。預設 📄。",sShowToggles:"顯示展開箭頭",sShowTogglesDesc:"在資料夾和最愛分區前顯示展開/收合箭頭。",sOpenFolderIcon:"展開資料夾圖示",sOpenFolderIconDesc:"無自訂筆記本圖示時,展開的資料夾使用的 Emoji、圖片 URL、data URI 或本機圖片路徑。",sClosedFolderIcon:"收合資料夾圖示",sClosedFolderIconDesc:"無自訂筆記本圖示時,收合的資料夾使用的 Emoji、圖片 URL、data URI 或本機圖片路徑。",sOpenPinnedIcon:"最愛展開圖示",sOpenPinnedIconDesc:"最愛分區展開時使用的 Emoji、圖片 URL、data URI 或本機圖片路徑。",sClosedPinnedIcon:"最愛收合圖示",sClosedPinnedIconDesc:"最愛分區收合時使用的 Emoji、圖片 URL、data URI 或本機圖片路徑。",refresh:"重新整理",newItem:"新增",trash:"垃圾桶",ctxRestoreNote:"還原",ctxPermanentDelete:"永久刪除",ctxEmptyTrash:"清空垃圾桶",confirmEmptyTrash:"清空垃圾桶?所有已刪除的筆記本和筆記將被永久刪除,無法還原。",confirmRestoreParent:"這條筆記原來所在的筆記本「{folder}」也在垃圾桶裡。要連同筆記本一起還原嗎?",restoreBoth:"還原筆記本和筆記",restoreNoteOnly:"僅還原筆記(到根目錄)",smartFolders:"智慧資料夾",smartRecent:"最近更新",smartTodos:"未完成待辦",smartSaveSearch:"存為智慧資料夾",smartSaveName:"智慧資料夾名稱:",smartEditQueryLabel:"編輯「{name}」的搜尋查詢:",smartManage:"管理智慧資料夾...",smartManageHint:"每列一個智慧資料夾:名稱和 Joplin 搜尋查詢。右側數字是目前符合的筆記數,為 0 通常表示查詢寫錯了。「最近更新」和「未完成待辦」是內建的,不在這裡。",smartColName:"名稱",smartColQuery:"查詢",smartAdd:"新增",smartMoveUp:"上移",smartMoveDown:"下移",smartRemove:"刪除",smartEmptyList:"還沒有自訂智慧資料夾。",smartCountTip:"目前符合的筆記數(最多顯示 100)",smartRowError:"第 {name} 列:{err}",smartDupName:"有兩個智慧資料夾都叫「{name}」",ctxSmartOpenSearch:"在搜尋列中開啟",ctxSmartEditQuery:"編輯查詢...",ctxSmartRename:"重新命名...",ctxSmartDelete:"刪除智慧資料夾",smartConfirmDelete:"刪除智慧資料夾「{name}」?\n筆記本身不受影響。",smartConfirmReplace:"已有名為「{name}」的智慧資料夾,取代它的查詢?",smartInvalidName:"名稱不能為空,也不能包含 : 或 ;",smartInvalidQuery:"查詢不能為空,也不能包含 ;",smartInvalidTitle:"無法儲存智慧資料夾",pvType:"類型",pvSize:"大小",pvCreated:"建立",pvUpdated:"更新",pvNote:"筆記",pvTodo:"待辦",pvChars:"字元",ctxSetTags:"設定標籤",ctxMoveToFolder:"移動到筆記本",ctxCopyExternalLink:"複製外部連結",ctxCopyFolderId:"複製筆記本 ID",revealNote:"定位目前筆記",ctxPin:"📌 收藏",ctxUnpin:"取消收藏",cancel:"取消"},en_US:{newNotebook:"New Notebook",newNote:"New Note",newTodo:"New To-do",sort:"Sort",collapseAll:"Collapse All",expandAll:"Expand All",search:"Search note contents...",searchResultCount:"{count} results found",searchNoResult:"No matching notes found",searching:"Searching...",sync:"Synchronise",syncing:"Syncing...",syncDone:"✔ Sync Done",syncFailed:"⚠ Sync Failed",loading:"Loading...",syncTip:"Synchronise Joplin. Note: background attachment downloads and decryption are not covered - the plugin receives no events for them.",syncCancel:"Cancel sync",syncCancelling:"Cancelling...",sortUpdatedDesc:"↓ Updated",sortUpdatedAsc:"↑ Updated",sortTitleAsc:"↑ Title",sortTitleDesc:"↓ Title",sortManual:"⇅ Manual",ctxNewNoteHere:"New Note Here",ctxNewTodoHere:"New To-do Here",ctxNewSubNotebook:"New Sub-notebook",ctxRenameFolder:"Edit",ctxExportFolder:"Export Notebook",ctxDeleteFolder:"Delete Notebook",exportFailed:"Export failed",ctxImportFiles:"Import files",importFailed:"Import failed",impFmtMd:"Markdown / Text",impFmtEnex:"Evernote (ENEX)",impFmtCsvTable:"CSV → Markdown table",impFmtCsvCode:"CSV → code block",ctxOpenNote:"Open Note",ctxOpenInNewWindow:"Open in New Window",ctxEditExternally:"Edit in external editor",ctxCopyLink:"Copy Markdown Link",ctxDuplicateNote:"Duplicate",ctxSwitchNoteType:"Switch Note/To-do",ctxToggleTodo:"Toggle Completed",ctxRenameNote:"Rename",ctxMoveNote:"Move to Notebook...",ctxNoteInfo:"Note Properties",ctxDeleteNote:"Delete Note",confirmDeleteFolder:"Delete this notebook and all its contents?",confirmDeleteNote:"Delete this note?",promptRename:"Enter new name:",promptMoveNote:"Enter target notebook name:",promptNewNotebookName:"Enter new notebook name:",dropCreateNotebook:"Release to create a new notebook",searchSectionNotes:"Notes",searchSectionTags:"Tags",searchSectionFolders:"Notebooks",searchTagNoteCount:"{count} notes",pinned:"Pinned",notebooksSection:"Notebooks",tags:"Tags",ctxRenameTag:"Rename tag",ctxDeleteTag:"Delete tag",ctxUntagNote:"Remove from tag",confirmDeleteTag:"Delete this tag? (Only the tag is removed - notes are kept.)",ctxPublishNote:"Publish note",ctxExport:"Export",sharedBadge:"Published · click to open the publish dialog",sShowTags:"Show tags section",sShowTagsDesc:"Show Joplin tags as folders below the notebook tree.",tagCountBoth:"{own} notes with this tag, {sub} in the whole subtree",tagCountSub:"{sub} notes in this subtree",sTagNestSep:"Tag nesting separator",sTagNestSepDesc:'Split tag names on this character and show them as a tree (with ".", the tag "work.project.a" nests three levels deep). Display only — your tags are never modified. Leave empty for a flat list.',sAutoRefresh:"Auto refresh on external changes",sAutoRefreshDesc:"Poll Joplin's change feed every 5 seconds and refresh the panel when notes are created, moved or deleted by other plugins or sync. Edits to the currently open note are ignored (they already update live).",sRestoreUi:"Restore last view state on startup",sRestoreUiDesc:"Remember collapsed/expanded folders and sections between sessions. When off, the tree starts fully collapsed.",sShowSmart:"Show smart folders",sShowSmartDesc:"Saved searches rendered as folders: Recently updated, Open to-dos, and your own rules.",sSmartRules:"Custom smart folders",sSmartRulesDesc:'Name:query pairs separated by ";". The name is everything before the FIRST colon; the rest is the query, in Joplin search syntax. Example: Work:notebook:Work updated:day-30;Starred:tag:star gives two folders, "Work" and "Starred". Easier: Tools > Manage smart folders, or try a query in the search bar and click "Save as smart folder"; right-click a smart folder to edit its query, rename or delete it.',sSmartLimit:"Smart folder result limit",sSmartLimitDesc:"Maximum notes shown per smart folder (newest first). Default: 5.",sHoverPreview:"Note preview on hover",sHoverPreviewDesc:"Show title, updated time and a body snippet when hovering a note.",sHoverPreviewLen:"Hover preview length (characters)",sArrowPos:"Expand arrow position",sArrowPosDesc:"Applies to notebooks, section headers and section rows alike, so everything matches.",sArrowRight:"After the text",sArrowLeft:"Left of the text (classic tree)",sExpandMode:"Expand button behavior",sExpandModeDesc:"What the button does when expanding. Restore: return to the state before the last Collapse All (follows browsing). Folders only / Everything: fixed action, ignores browsing. Collapse only: the button just collapses, no expand.",sExpandRestore:"Restore the state before Collapse All",sExpandSkeleton:"Folders only (leaf notebooks stay collapsed)",sExpandAllOpt:"Everything",sExpandCollapseOnly:"Collapse only (no expand)",sCollapseScope:"Collapse scope",sCollapseScopeDesc:"What the Collapse All button affects. Notebooks only: just the notebook tree. All sections: also fold Pinned, Smart folders, Tags and Trash.",sScopeNotebooks:"Notebooks only",sScopeAll:"All sections",sSectionOrder:"Section order",sSectionOrderDesc:'Comma-separated section order. Keys: pinned, smart (smart folders), notebooks (legacy key "tree" still accepted), tags, trash. Omitted sections keep the default order at the end.',clearSearch:"Clear search",sStackHeaders:"Stack section headers",sStackHeadersDesc:"While scrolling, headers of sections you passed stack at the top and upcoming ones at the bottom, so every section is one click away at all times. Off: only the current section header sticks.",sSectionGap:"Section spacing (px)",sSectionGapDesc:"Blank space above each section divider. Default: 5.",sRowFont:"Row font size (px)",sRowFontDesc:"Text size in the tree. Icons scale with it. Default: 13.",sRowHeight:"Row height (px)",sRowHeightDesc:"Minimum height of each row. A row is never shorter than its own contents, so to actually compact the tree lower the row font size as well. Default: 24.",cToggle:"Toggle Explorer sidebar",sNoteIcon:"Note icon",sNoteIconDesc:"Icon for plain notes (non-to-do). Emoji, image URL, data URI, or local image path. Default: 📄.",sShowToggles:"Show toggle arrows",sShowTogglesDesc:"Show expand/collapse arrows before folders and the pinned section.",sOpenFolderIcon:"Open folder icon",sOpenFolderIconDesc:"Emoji, image URL, data URI, or local image path for expanded folders without a custom notebook icon.",sClosedFolderIcon:"Closed folder icon",sClosedFolderIconDesc:"Emoji, image URL, data URI, or local image path for collapsed folders without a custom notebook icon.",sOpenPinnedIcon:"Open pinned icon",sOpenPinnedIconDesc:"Emoji, image URL, data URI, or local image path for the expanded pinned section.",sClosedPinnedIcon:"Closed pinned icon",sClosedPinnedIconDesc:"Emoji, image URL, data URI, or local image path for the collapsed pinned section.",refresh:"Refresh",newItem:"New",trash:"Trash",ctxRestoreNote:"Restore",ctxPermanentDelete:"Delete permanently",ctxEmptyTrash:"Empty trash",confirmEmptyTrash:"Empty the trash? All deleted notebooks and notes will be permanently removed. This cannot be undone.",confirmRestoreParent:'The notebook "{folder}" that held this note is also in the trash. Restore the notebook together with the note?',restoreBoth:"Restore notebook + note",restoreNoteOnly:"Note only (to root)",smartFolders:"Smart folders",smartRecent:"Recently updated",smartTodos:"Open to-dos",smartSaveSearch:"Save as smart folder",smartSaveName:"Smart folder name:",smartEditQueryLabel:'Search query for "{name}":',smartManage:"Manage smart folders...",smartManageHint:"One smart folder per row: a name and a Joplin search query. The number on the right is how many notes match right now; 0 usually means a typo in the query. Recently updated and Open to-dos are built in and not listed here.",smartColName:"Name",smartColQuery:"Query",smartAdd:"Add",smartMoveUp:"Move up",smartMoveDown:"Move down",smartRemove:"Remove",smartEmptyList:"No custom smart folders yet.",smartCountTip:"Notes matching now (shown up to 100)",smartRowError:"Row {name}: {err}",smartDupName:'Two smart folders are named "{name}"',ctxSmartOpenSearch:"Open in search bar",ctxSmartEditQuery:"Edit query...",ctxSmartRename:"Rename...",ctxSmartDelete:"Delete smart folder",smartConfirmDelete:'Delete the smart folder "{name}"?\nThe notes themselves are not affected.',smartConfirmReplace:'A smart folder named "{name}" already exists. Replace its query?',smartInvalidName:"The name must not be empty or contain : or ;",smartInvalidQuery:"The query must not be empty or contain ;",smartInvalidTitle:"Cannot save the smart folder",pvType:"Type",pvSize:"Size",pvCreated:"Created",pvUpdated:"Updated",pvNote:"Note",pvTodo:"To-do",pvChars:"chars",ctxSetTags:"Set tags",ctxMoveToFolder:"Move to notebook",ctxCopyExternalLink:"Copy external link",ctxCopyFolderId:"Copy notebook ID",revealNote:"Reveal current note",ctxPin:"📌 Pin",ctxUnpin:"Unpin",cancel:"Cancel"},ru:{newNotebook:"Новый блокнот",newNote:"Новая заметка",newTodo:"Новая задача",sort:"Сортировка",collapseAll:"Свернуть все",expandAll:"Развернуть все",search:"Искать в содержимом заметок...",searchResultCount:"Найдено результатов: {count}",searchNoResult:"Заметки не найдены",searching:"Поиск...",sync:"Синхронизировать",syncing:"Синхронизация...",syncDone:"✔ Синхронизация завершена",syncFailed:"⚠ Ошибка синхронизации",loading:"Загрузка...",syncTip:"Синхронизировать Joplin. Фоновая загрузка вложений и расшифровка не учитываются - плагин не получает о них событий.",syncCancel:"Отменить",syncCancelling:"Отмена...",sortUpdatedDesc:"↓ Обновлено",sortUpdatedAsc:"↑ Обновлено",sortTitleAsc:"↑ Название",sortTitleDesc:"↓ Название",sortManual:"⇅ Вручную",ctxNewNoteHere:"Создать заметку здесь",ctxNewTodoHere:"Создать задачу здесь",ctxNewSubNotebook:"Новый вложенный блокнот",ctxRenameFolder:"Редактировать",ctxExportFolder:"Экспортировать блокнот",ctxDeleteFolder:"Удалить блокнот",exportFailed:"Ошибка экспорта",ctxImportFiles:"Импортировать файлы",importFailed:"Ошибка импорта",impFmtMd:"Markdown / текст",impFmtEnex:"Evernote (ENEX)",impFmtCsvTable:"CSV → таблица Markdown",impFmtCsvCode:"CSV → блок кода",ctxOpenNote:"Открыть заметку",ctxOpenInNewWindow:"Открыть в новом окне",ctxEditExternally:"Открыть во внешнем редакторе",ctxCopyLink:"Скопировать Markdown-ссылку",ctxDuplicateNote:"Создать копию",ctxSwitchNoteType:"Переключить между заметкой и задачей",ctxToggleTodo:"Переключить статус выполнения",ctxRenameNote:"Переименовать",ctxMoveNote:"Переместить в блокнот...",ctxNoteInfo:"Свойства заметки",ctxDeleteNote:"Удалить заметку",confirmDeleteFolder:"Удалить блокнот и все его содержимое?",confirmDeleteNote:"Удалить заметку?",promptRename:"Введите новое название:",promptMoveNote:"Переместить в блокнот:",promptNewNotebookName:"Введите название нового блокнота:",dropCreateNotebook:"Отпустите, чтобы создать новый блокнот",searchSectionNotes:"Заметки",searchSectionTags:"Метки",searchSectionFolders:"Блокноты",searchTagNoteCount:"Заметок: {count}",pinned:"Закрепленные",notebooksSection:"Блокноты",tags:"Теги",ctxRenameTag:"Переименовать тег",ctxDeleteTag:"Удалить тег",ctxUntagNote:"Убрать из тега",confirmDeleteTag:"Удалить этот тег? (Удаляется только тег, заметки сохраняются.)",ctxPublishNote:"Опубликовать заметку",ctxExport:"Экспорт",sharedBadge:"Опубликовано · нажмите, чтобы открыть диалог публикации",sShowTags:"Показывать раздел тегов",sShowTagsDesc:"Показывать теги Joplin как папки под деревом блокнотов.",tagCountBoth:"{own} заметок с этим тегом, {sub} во всём поддереве",tagCountSub:"{sub} заметок в этом поддереве",sTagNestSep:"Разделитель вложенности тегов",sTagNestSepDesc:'Разделять имена тегов по этому символу и показывать их деревом (с "." тег "работа.проект.а" вложится на три уровня). Только отображение — сами теги не изменяются. Пусто — плоский список.',sAutoRefresh:"Автообновление при внешних изменениях",sAutoRefreshDesc:"Опрашивать ленту изменений Joplin каждые 5 секунд и обновлять панель, когда заметки создаются, перемещаются или удаляются другими плагинами или синхронизацией. Правки открытой заметки игнорируются (они и так обновляются сразу).",sRestoreUi:"Восстанавливать состояние вида при запуске",sRestoreUiDesc:"Запоминать свёрнутые/развёрнутые папки и разделы между сеансами. Если выключено, дерево запускается полностью свёрнутым.",sShowSmart:"Показывать умные папки",sShowSmartDesc:"Сохранённые поиски в виде папок: Недавно обновлённые, Открытые задачи и ваши собственные правила.",sSmartRules:"Свои умные папки",sSmartRulesDesc:"Пары «Имя:запрос» через «;». Имя - всё до ПЕРВОГО двоеточия, остальное - запрос в синтаксисе поиска Joplin. Пример: Работа:notebook:Work updated:day-30;Избранное:tag:star даёт две папки, «Работа» и «Избранное». Проще: Инструменты > Управление умными папками, или подберите запрос в строке поиска и нажмите «Сохранить как умную папку»; правый клик по умной папке - изменить запрос, переименовать или удалить.",sSmartLimit:"Лимит результатов умной папки",sSmartLimitDesc:"Максимум заметок в умной папке (сначала новые). По умолчанию: 5.",sHoverPreview:"Предпросмотр заметки при наведении",sHoverPreviewDesc:"Показывать заголовок, время обновления и фрагмент текста при наведении на заметку.",sHoverPreviewLen:"Длина предпросмотра (символов)",sArrowPos:"Положение стрелки раскрытия",sArrowPosDesc:"Действует одинаково на блокноты, заголовки и строки разделов.",sArrowRight:"После текста",sArrowLeft:"Слева от текста (классическое дерево)",sExpandMode:"Поведение кнопки разворачивания",sExpandModeDesc:"Что делает кнопка при разворачивании. Восстановить: вернуть состояние до последнего «Свернуть всё» (следует за просмотром). Только папки / Всё: фиксированное действие, не зависит от просмотра. Только сворачивать: кнопка только сворачивает.",sExpandRestore:"Восстановить состояние до сворачивания",sExpandSkeleton:"Только папки (листовые блокноты остаются свёрнутыми)",sExpandAllOpt:"Всё",sExpandCollapseOnly:"Только сворачивать (без разворота)",sCollapseScope:"Область сворачивания",sCollapseScopeDesc:"На что влияет кнопка «Свернуть всё». Только блокноты: только дерево блокнотов. Все разделы: также закладки, умные папки, теги и корзину.",sScopeNotebooks:"Только блокноты",sScopeAll:"Все разделы",sSectionOrder:"Порядок разделов",sSectionOrderDesc:'Порядок разделов через запятую. Ключи: pinned (закладки), smart (умные папки), notebooks (блокноты; старый ключ "tree" тоже работает), tags (теги), trash (корзина). Пропущенные разделы идут в конце в порядке по умолчанию.',clearSearch:"Очистить поиск",sStackHeaders:"Складывать заголовки разделов",sStackHeadersDesc:"При прокрутке заголовки пройденных разделов закрепляются сверху, а предстоящих — снизу, так что каждый раздел всегда в одном клике. Выкл.: закрепляется только заголовок текущего раздела.",sSectionGap:"Отступ между разделами (px)",sSectionGapDesc:"Пустое место над разделителем каждого раздела. По умолчанию: 5.",sRowFont:"Размер шрифта строки (px)",sRowFontDesc:"Размер текста в дереве. Значки масштабируются вместе с ним. По умолчанию: 13.",sRowHeight:"Высота строки (px)",sRowHeightDesc:"Минимальная высота строки. Строка не бывает ниже своего содержимого - чтобы действительно уплотнить дерево, уменьшите и шрифт. По умолчанию: 24.",cToggle:"Показать/скрыть панель Explorer",sNoteIcon:"Значок заметки",sNoteIconDesc:"Значок для обычных заметок (не задач). Emoji, URL изображения, data URI или локальный путь. По умолчанию: 📄.",sShowToggles:"Показывать стрелки раскрытия",sShowTogglesDesc:"Показывать стрелки перед папками и разделом закладок.",sOpenFolderIcon:"Значок открытой папки",sOpenFolderIconDesc:"Emoji, URL изображения, data URI или локальный путь для развёрнутых папок без своего значка блокнота.",sClosedFolderIcon:"Значок закрытой папки",sClosedFolderIconDesc:"Emoji, URL изображения, data URI или локальный путь для свёрнутых папок без своего значка блокнота.",sOpenPinnedIcon:"Значок раскрытых закладок",sOpenPinnedIconDesc:"Emoji, URL изображения, data URI или локальный путь для развёрнутого раздела закладок.",sClosedPinnedIcon:"Значок свёрнутых закладок",sClosedPinnedIconDesc:"Emoji, URL изображения, data URI или локальный путь для свёрнутого раздела закладок.",refresh:"Обновить",newItem:"Создать",trash:"Корзина",ctxRestoreNote:"Восстановить",ctxPermanentDelete:"Удалить навсегда",ctxEmptyTrash:"Очистить корзину",confirmEmptyTrash:"Очистить корзину? Все удалённые блокноты и заметки будут удалены безвозвратно.",confirmRestoreParent:"Блокнот «{folder}», в котором была эта заметка, тоже в корзине. Восстановить блокнот вместе с заметкой?",restoreBoth:"Восстановить блокнот и заметку",restoreNoteOnly:"Только заметку (в корень)",smartFolders:"Умные папки",smartRecent:"Недавно обновлённые",smartTodos:"Незавершённые задачи",smartSaveSearch:"Сохранить как умную папку",smartSaveName:"Имя умной папки:",smartEditQueryLabel:"Поисковый запрос для «{name}»:",smartManage:"Управление умными папками...",smartManageHint:"Одна умная папка на строку: имя и поисковый запрос Joplin. Число справа - сколько заметок совпадает сейчас; 0 обычно означает опечатку в запросе. «Недавно обновлённые» и «Незавершённые задачи» встроены и здесь не показаны.",smartColName:"Имя",smartColQuery:"Запрос",smartAdd:"Добавить",smartMoveUp:"Выше",smartMoveDown:"Ниже",smartRemove:"Удалить",smartEmptyList:"Своих умных папок пока нет.",smartCountTip:"Совпадающих заметок сейчас (до 100)",smartRowError:"Строка {name}: {err}",smartDupName:"Две умные папки называются «{name}»",ctxSmartOpenSearch:"Открыть в строке поиска",ctxSmartEditQuery:"Изменить запрос...",ctxSmartRename:"Переименовать...",ctxSmartDelete:"Удалить умную папку",smartConfirmDelete:"Удалить умную папку «{name}»?\nСами заметки не затрагиваются.",smartConfirmReplace:"Умная папка «{name}» уже есть. Заменить её запрос?",smartInvalidName:"Имя не может быть пустым или содержать : и ;",smartInvalidQuery:"Запрос не может быть пустым или содержать ;",smartInvalidTitle:"Не удалось сохранить умную папку",pvType:"Тип",pvSize:"Размер",pvCreated:"Создано",pvUpdated:"Обновлено",pvNote:"Заметка",pvTodo:"Задача",pvChars:"симв.",ctxSetTags:"Назначить теги",ctxMoveToFolder:"Переместить в блокнот",ctxCopyExternalLink:"Копировать внешнюю ссылку",ctxCopyFolderId:"Копировать ID блокнота",revealNote:"Показать текущую заметку",ctxPin:"📌 Закрепить",ctxUnpin:"Открепить",cancel:"Отмена"},ja_JP:{newNotebook:"新規ノートブック",newNote:"新規ノート",newTodo:"新規タスク",sort:"並べ替え",collapseAll:"すべて折りたたむ",expandAll:"すべて展開",search:"ノート内容を検索...",searchResultCount:"{count} 件の結果",searchNoResult:"一致するノートが見つかりません",searching:"検索中...",sync:"同期",syncing:"同期中...",syncDone:"✔ 同期完了",syncFailed:"⚠ 同期に失敗しました",loading:"読み込み中...",syncTip:"Joplin を同期します。注意:バックグラウンドの添付ファイル取得と復号は完了状態に含まれません(プラグインにイベントが届かないため)。",syncCancel:"同期をキャンセル",syncCancelling:"キャンセル中...",sortUpdatedDesc:"↓ 更新日時",sortUpdatedAsc:"↑ 更新日時",sortTitleAsc:"↑ タイトル",sortTitleDesc:"↓ タイトル",sortManual:"⇅ 手動",ctxNewNoteHere:"ここに新規ノート",ctxNewTodoHere:"ここに新規タスク",ctxNewSubNotebook:"新規サブノートブック",ctxRenameFolder:"編集",ctxExportFolder:"ノートブックをエクスポート",ctxDeleteFolder:"ノートブックを削除",exportFailed:"エクスポート失敗",ctxImportFiles:"ファイルをインポート",importFailed:"インポート失敗",impFmtMd:"Markdown / テキスト",impFmtEnex:"Evernote (ENEX)",impFmtCsvTable:"CSV → Markdown テーブル",impFmtCsvCode:"CSV → コードブロック",ctxOpenNote:"ノートを開く",ctxOpenInNewWindow:"新しいウィンドウで開く",ctxEditExternally:"外部エディタで開く",ctxCopyLink:"Markdownリンクをコピー",ctxDuplicateNote:"複製",ctxSwitchNoteType:"ノート/タスク切り替え",ctxToggleTodo:"完了状態を切り替え",ctxRenameNote:"名前を変更",ctxMoveNote:"ノートブックに移動...",ctxNoteInfo:"ノートプロパティ",ctxDeleteNote:"ノートを削除",confirmDeleteFolder:"このノートブックとその内容をすべて削除しますか?",confirmDeleteNote:"このノートを削除しますか?",promptRename:"新しい名前を入力:",promptMoveNote:"移動先のノートブック名:",promptNewNotebookName:"新しいノートブック名を入力:",dropCreateNotebook:"ドロップして新規ノートブックを作成",searchSectionNotes:"ノート",searchSectionTags:"タグ",searchSectionFolders:"ノートブック",searchTagNoteCount:"{count} 件のノート",pinned:"ピン留め",notebooksSection:"ノートブック",tags:"タグ",ctxRenameTag:"タグ名を変更",ctxDeleteTag:"タグを削除",ctxUntagNote:"タグから外す",confirmDeleteTag:"このタグを削除しますか?(タグのみ削除され、ノートは残ります)",ctxPublishNote:"ノートを公開",ctxExport:"エクスポート",sharedBadge:"公開済み · クリックで公開ダイアログを開く",sShowTags:"タグセクションを表示",sShowTagsDesc:"ノートブックツリーの下に Joplin のタグをフォルダーとして表示します。",tagCountBoth:"このタグのノート {own} 件、サブツリー全体で {sub} 件",tagCountSub:"サブツリーに {sub} 件のノート",sTagNestSep:"タグ階層の区切り文字",sTagNestSepDesc:'この文字でタグ名を分割し、ツリー表示します("." の場合、"仕事.案件.甲" は3階層になります)。表示のみで、タグ自体は変更されません。空欄でフラット表示。',sAutoRefresh:"外部変更時に自動更新",sAutoRefreshDesc:"Joplin の変更フィードを 5 秒ごとに確認し、他のプラグインや同期によるノートの作成・移動・削除時にパネルを更新します。開いているノートの編集は対象外(もともと即時反映)。",sRestoreUi:"起動時に前回の表示状態を復元",sRestoreUiDesc:"フォルダー/セクションの折りたたみ状態をセッションをまたいで記憶します。オフの場合は全て折りたたんだ状態で起動します。",sShowSmart:"スマートフォルダーを表示",sShowSmartDesc:"保存済み検索をフォルダーとして表示:最近の更新、未完了タスク、独自ルール。",sSmartRules:"カスタムスマートフォルダー",sSmartRulesDesc:'「名前:クエリ」を ";" 区切りで。最初のコロンより前が名前、残りがすべてクエリ(Joplin 検索構文)です。例:仕事:notebook:Work updated:day-30;スター:tag:star で「仕事」と「スター」の 2 つのフォルダができます。もっと簡単に:ツール > スマートフォルダを管理、または検索バーでクエリを試してから「スマートフォルダとして保存」を押してください。スマートフォルダを右クリックするとクエリの編集・名前の変更・削除ができます。',sSmartLimit:"スマートフォルダー表示件数",sSmartLimitDesc:"各スマートフォルダーに表示する最大ノート数(新しい順)。既定値:5。",sHoverPreview:"ホバーでノートをプレビュー",sHoverPreviewDesc:"ノートにカーソルを合わせるとタイトル・更新日時・本文の抜粋を表示します。",sHoverPreviewLen:"ホバープレビューの長さ(文字数)",sArrowPos:"展開矢印の位置",sArrowPosDesc:"ノートブック・セクション見出し・セクション行すべてに適用され、統一されます。",sArrowRight:"テキストの後ろ",sArrowLeft:"テキストの左(クラシックツリー)",sExpandMode:"展開ボタンの動作",sExpandModeDesc:"展開時のボタンの動作。復元:前回「すべて折りたたむ」前の状態に戻す(閲覧に追従)。フォルダーのみ/すべて:固定動作、閲覧に追従しない。折りたたみのみ:ボタンは折りたたむだけ。",sExpandRestore:"折りたたみ前の状態を復元",sExpandSkeleton:"フォルダーのみ(末端ノートブックは折りたたんだまま)",sExpandAllOpt:"すべて",sExpandCollapseOnly:"折りたたみのみ(展開なし)",sCollapseScope:"折りたたむ範囲",sCollapseScopeDesc:"「すべて折りたたむ」ボタンの対象。ノートブックのみ:ノートブックツリーだけ。すべてのセクション:お気に入り・スマートフォルダー・タグ・ゴミ箱も折りたたみます。",sScopeNotebooks:"ノートブックのみ",sScopeAll:"すべてのセクション",sSectionOrder:"セクションの順序",sSectionOrderDesc:"カンマ区切りのセクション順。キー:pinned(お気に入り)、smart(スマートフォルダー)、notebooks(ノートブック。旧キー tree も使用可)、tags(タグ)、trash(ゴミ箱)。省略したセクションは既定順で末尾に並びます。",clearSearch:"検索をクリア",sStackHeaders:"セクション見出しを積み重ね表示",sStackHeadersDesc:"スクロール時、通過したセクションの見出しを上部に、未到達のものを下部に固定し、常に全セクションへワンクリックで移動できます。オフ:現在のセクション見出しのみ固定。",sSectionGap:"セクション間隔(px)",sSectionGapDesc:"各セクション区切り線の上の余白。既定値:5。",sRowFont:"行の文字サイズ(px)",sRowFontDesc:"ツリー内の文字サイズ。アイコンも連動します。既定値:13。",sRowHeight:"行の高さ(px)",sRowHeightDesc:"各行の最小高さ。行は内容より低くならないため、本当に詰めるには文字サイズも下げてください。既定値:24。",cToggle:"Explorer サイドバーの表示切替",sNoteIcon:"ノートアイコン",sNoteIconDesc:"通常ノート(タスク以外)のアイコン。Emoji、画像 URL、data URI、ローカル画像パス。既定値:📄。",sShowToggles:"展開矢印を表示",sShowTogglesDesc:"フォルダーとお気に入りセクションの前に展開/折りたたみ矢印を表示します。",sOpenFolderIcon:"展開フォルダーのアイコン",sOpenFolderIconDesc:"カスタムアイコンのないノートブックが展開時に使う Emoji、画像 URL、data URI、ローカル画像パス。",sClosedFolderIcon:"折りたたみフォルダーのアイコン",sClosedFolderIconDesc:"カスタムアイコンのないノートブックが折りたたみ時に使う Emoji、画像 URL、data URI、ローカル画像パス。",sOpenPinnedIcon:"お気に入り展開アイコン",sOpenPinnedIconDesc:"お気に入りセクション展開時に使う Emoji、画像 URL、data URI、ローカル画像パス。",sClosedPinnedIcon:"お気に入り折りたたみアイコン",sClosedPinnedIconDesc:"お気に入りセクション折りたたみ時に使う Emoji、画像 URL、data URI、ローカル画像パス。",refresh:"更新",newItem:"新規",trash:"ゴミ箱",ctxRestoreNote:"復元",ctxPermanentDelete:"完全に削除",ctxEmptyTrash:"ゴミ箱を空にする",confirmEmptyTrash:"ゴミ箱を空にしますか?削除済みのノートブックとノートは完全に削除され、元に戻せません。",confirmRestoreParent:"このノートが入っていたノートブック「{folder}」もゴミ箱にあります。ノートブックごと復元しますか?",restoreBoth:"ノートブックとノートを復元",restoreNoteOnly:"ノートのみ(ルートへ)",smartFolders:"スマートフォルダ",smartRecent:"最近更新",smartTodos:"未完了の ToDo",smartSaveSearch:"スマートフォルダとして保存",smartSaveName:"スマートフォルダの名前:",smartEditQueryLabel:"「{name}」の検索クエリ:",smartManage:"スマートフォルダを管理...",smartManageHint:"1 行に 1 つのスマートフォルダ:名前と Joplin の検索クエリ。右の数字は現在一致するノート数で、0 の場合はたいていクエリの書き間違いです。「最近更新」と「未完了の ToDo」は組み込みのため、ここには表示されません。",smartColName:"名前",smartColQuery:"クエリ",smartAdd:"追加",smartMoveUp:"上へ",smartMoveDown:"下へ",smartRemove:"削除",smartEmptyList:"カスタムのスマートフォルダはまだありません。",smartCountTip:"現在一致するノート数(最大 100 まで表示)",smartRowError:"{name} 行目:{err}",smartDupName:"「{name}」という名前のスマートフォルダが 2 つあります",ctxSmartOpenSearch:"検索バーで開く",ctxSmartEditQuery:"クエリを編集...",ctxSmartRename:"名前を変更...",ctxSmartDelete:"スマートフォルダを削除",smartConfirmDelete:"スマートフォルダ「{name}」を削除しますか?\nノート自体には影響しません。",smartConfirmReplace:"「{name}」という名前のスマートフォルダが既にあります。クエリを置き換えますか?",smartInvalidName:"名前は空にできず、: と ; は使えません",smartInvalidQuery:"クエリは空にできず、; は使えません",smartInvalidTitle:"スマートフォルダを保存できません",pvType:"種類",pvSize:"サイズ",pvCreated:"作成",pvUpdated:"更新",pvNote:"ノート",pvTodo:"ToDo",pvChars:"文字",ctxSetTags:"タグを設定",ctxMoveToFolder:"ノートブックへ移動",ctxCopyExternalLink:"外部リンクをコピー",ctxCopyFolderId:"ノートブック ID をコピー",revealNote:"現在のノートを表示",ctxPin:"📌 ピン留め",ctxUnpin:"ピン解除",cancel:"キャンセル"}},s=o(896),n=o(928),i=o(982);function r(e){let t=0,o=0;const a=/^[ \t]*(?:[-*+]|\d+[.)])\s+\[([ xX])\]/gm;let s;for(;s=a.exec(e);)t++," "!==s[1]&&o++;return{total:t,done:o}}let l="Published";function d(e){let t="";return e.is_shared&&(t+='<span class="shared-badge" title="'+l+'"><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg></span>'),e.cb_total&&(t+=function(e,t){if(!t)return"";const o=Math.max(0,Math.min(1,e/t));let a="";if(o>=1)a='<circle cx="8" cy="8" r="7" class="pie-fill"/><path d="M4.8 8.3 L7.1 10.6 L11.3 5.9" fill="none" stroke="var(--joplin-background-color, #fff)" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>';else if(o>0){const e=2*o*Math.PI-Math.PI/2,t=8+7*Math.cos(e),s=8+7*Math.sin(e);a='<path class="pie-fill" d="M8 8 L8 1 A7 7 0 '+(o>.5?1:0)+" 1 "+t.toFixed(2)+" "+s.toFixed(2)+' Z"/>'}return'<span class="todo-pie" title="'+e+"/"+t+'"><svg width="12" height="12" viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" class="pie-ring"/>'+a+"</svg></span>"}(e.cb_done||0,e.cb_total)),t}function c(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}const p={".gif":"image/gif",".ico":"image/x-icon",".jpeg":"image/jpeg",".jpg":"image/jpeg",".png":"image/png",".svg":"image/svg+xml",".webp":"image/webp"};function m(e,t){const o="icon"+(t?" "+t:"");return"image"===e.type?'<span class="'+o+'"><img class="custom-icon" src="'+c(e.value)+'" /></span>':'<span class="'+o+'">'+c(e.value)+"</span>"}const u={};async function w(e,t,o,a){const r=o+":"+String(e);if(u[r])return u[r];const l=await async function(e,t,o,a){const r="string"==typeof e?e.trim():"";if(!r)return{type:"text",value:t};if(/^data:image\//i.test(r)||/^https?:\/\//i.test(r))return{type:"image",value:r};const l=function(e){const t=e.trim();return/^file:\/\//i.test(t)?decodeURIComponent(t.replace(/^file:\/\//i,"")):0===t.indexOf("~/")&&process&&process.env&&process.env.HOME?n.join(process.env.HOME,t.substring(2)):n.isAbsolute(t)?t:null}(r);if(!l)return{type:"text",value:r};const d=n.extname(l).toLowerCase(),c=p[d];if(!c)return console.warn("Joplin Explorer: unsupported icon file type",l),{type:"text",value:t};const m=n.join(a,"custom-icons"),u=i.createHash("sha1").update(l).digest("hex"),w=n.join(m,o+"-"+u+d);try{await s.promises.mkdir(m,{recursive:!0}),(await s.promises.stat(l)).isFile()&&await s.promises.copyFile(l,w)}catch(e){console.warn("Joplin Explorer: failed to copy icon file",l,e)}try{return{type:"image",value:"data:"+c+";base64,"+(await s.promises.readFile(w)).toString("base64")}}catch(e){return console.warn("Joplin Explorer: failed to load icon file",w,e),{type:"text",value:t}}}(e,t,o,a);return u[r]=l,l}async function g(){let e=[],t=1,o=!0;for(;o;){const a=await joplin.data.get(["folders"],{fields:["id","title","parent_id","icon"],page:t,limit:100});e=e.concat(a.items),o=a.has_more,t++}return e}function f(e,t){const o=e.order||0,a=t.order||0;return o!==a?a-o:(e.title||"").localeCompare(t.title||"")}function h(e,t=!0,o={type:"text",value:"📂"},a={type:"text",value:"📁"}){let s=e.icon;if(s&&"string"==typeof s)try{s=JSON.parse(s)}catch(e){s=null}return s&&s.emoji?{type:"text",value:String(s.emoji)}:t?a:o}let y="📄";function x(e){return e.is_todo?e.todo_completed?"☑":"☐":y}function v(e){const t=new Set(e.noteIds);let o=e.bounded;for(const a of e.children){const e=v(a);e.ids.forEach(e=>t.add(e)),e.bounded&&(o=!0)}return{text:o?"100+":t.size?String(t.size):"",ids:t,bounded:o}}function b(e,t,o,a=0){let s="";for(const n of e){const e=26+18*a,i=n.children.length>0,r=!n.tagId;s+='<div class="tree-item folder tag-folder collapsed'+(r?" tag-virtual":"")+'" style="padding-left:'+e+'px"'+(n.tagId?' data-tag-id="'+n.tagId+'"':"")+' data-tag-path="'+c(n.path)+'" data-type="tag" title="'+c(n.path)+'">',t&&(s+='<span class="toggle">▶</span>'),s+='<span class="icon">'+(r?"🔖":"🏷️")+"</span>",s+='<span class="label">'+c(n.label)+"</span>";const l=v(n).text,d=n.count;let p="",m="";!r&&i&&d&&l&&d!==l?(p=c(d)+' <span class="count-sub">('+c(l)+")</span>",m=o.both.replace("{own}",d).replace("{sub}",l)):!r&&d?p=c(d):l&&(p=l,m=o.sub.replace("{sub}",l)),p&&(s+='<span class="count"'+(m?' title="'+c(m)+'"':"")+">"+p+"</span>"),s+="</div>",s+='<div class="tag-children collapsed" data-tag-path="'+c(n.path)+'"'+(n.tagId?' data-tag-id="'+n.tagId+'"':"")+' data-note-indent="'+(e+18)+'">',i&&(s+=b(n.children,t,o,a+1)),s+="</div>"}return s}function S(e,t,o,a=0,s=!0,n={type:"text",value:"📂"},i={type:"text",value:"📁"}){let r="";for(const l of e){const e=26+18*a;if("folder"===l.type){const d=l.total_count||l.note_count||0,p=!0===o[l.id],u=p?"▶":"▼",w=p?"toggle":"toggle expanded";r+='<div class="tree-item folder'+(p?" collapsed":"")+'" style="padding-left:'+e+'px" data-id="'+l.id+'" data-type="folder">',s&&(r+='<span class="'+w+'">'+u+"</span>"),r+=m(h(l,!1,n,i),"folder-icon icon-when-open"),r+=m(h(l,!0,n,i),"folder-icon icon-when-collapsed"),r+='<span class="label">'+c(l.title)+"</span>",r+='<span class="count">'+d+"</span>",r+="</div>",r+='<div class="children'+(p?" collapsed":"")+'" data-folder-id="'+l.id+'">',l.children&&(r+=S(l.children,t,o,a+1,s,n,i)),r+="</div>"}else{const o=l.id===t?" selected":"",a=x(l);r+='<div class="tree-item note'+o+'" style="padding-left:'+e+'px" data-id="'+l.id+'" data-type="note" data-todo="'+(l.is_todo?1:0)+'">',r+='<span class="icon note-icon">'+a+"</span>",r+='<span class="label">'+c(l.title)+"</span>",r+=d(l),r+="</div>"}}return r}function N(e,t){const o=e.slice();switch(t){case"title_asc":o.sort((e,t)=>(e.title||"").localeCompare(t.title||""));break;case"title_desc":o.sort((e,t)=>(t.title||"").localeCompare(e.title||""));break;case"updated_asc":o.sort((e,t)=>(e.user_updated_time||0)-(t.user_updated_time||0));break;case"manual":o.sort((e,t)=>{const o=e.order||0,a=t.order||0;return o!==a?a-o:(t.user_updated_time||0)-(e.user_updated_time||0)});break;default:o.sort((e,t)=>(t.user_updated_time||0)-(e.user_updated_time||0))}return o}joplin.plugins.register({onStart:async function(){const e=(t=await joplin.settings.globalValue("locale")||"en_US",a[t]||a[t.split("_")[0]]||a.en_US);var t;l=e.sharedBadge||l;const o=await joplin.views.panels.create("notesInListPanel");await joplin.views.panels.addScript(o,"webview/panel.css"),await joplin.views.panels.addScript(o,"webview/panel.js"),await joplin.views.panels.setHtml(o,'<div id="notes-in-list-root"><p style="padding:12px;">'+e.loading+"</p></div>"),await joplin.views.panels.show(o,!0),await joplin.commands.register({name:"toggleExplorerPanel",label:e.cToggle,iconName:"fas fa-sitemap jop-explorer-icon",execute:async()=>{const e=await joplin.views.panels.visible(o);await joplin.views.panels.show(o,!e)}}),await joplin.views.toolbarButtons.create("explorerPanelButton","toggleExplorerPanel","noteToolbar"),await joplin.views.menuItems.create("explorerPanelMenuItem","toggleExplorerPanel","view"),await joplin.commands.register({name:"explorerManageSmartFolders",label:e.smartManage,execute:async()=>{await O()}}),await joplin.views.menuItems.create("explorerManageSmartMenuItem","explorerManageSmartFolders","tools");try{const e=await joplin.plugins.installationDir();await joplin.window.loadChromeCssFile(e+"/webview/chrome.css")}catch(e){console.info("Joplin Explorer: custom toolbar icon unavailable, falling back to the Font Awesome glyph",e)}try{await joplin.settings.registerSection("joplinExplorer",{label:"Joplin Explorer",iconName:"fas fa-columns"}),await joplin.settings.registerSettings({pinnedItems:{section:"joplinExplorer",type:2,value:'{"notes":[],"folders":[]}',public:!1,label:"Pinned Items (JSON)"},folderOrder:{section:"joplinExplorer",type:2,value:"{}",public:!1,label:"Folder Manual Order (JSON)"},noteSortMode:{section:"joplinExplorer",type:2,value:"updated_desc",public:!1,label:"Note Sort Mode"},uiState:{section:"joplinExplorer",type:2,value:"{}",public:!1,label:"UI State (JSON)"},showTagsSection:{section:"joplinExplorer",type:3,value:!0,public:!0,label:e.sShowTags,description:e.sShowTagsDesc},stackSectionHeaders:{section:"joplinExplorer",type:3,value:!0,public:!0,label:e.sStackHeaders,description:e.sStackHeadersDesc},tagNestSeparator:{section:"joplinExplorer",type:2,value:"",public:!0,label:e.sTagNestSep,description:e.sTagNestSepDesc},autoRefresh:{section:"joplinExplorer",type:3,value:!0,public:!0,label:e.sAutoRefresh,description:e.sAutoRefreshDesc},restoreUiState:{section:"joplinExplorer",type:3,value:!0,public:!0,label:e.sRestoreUi,description:e.sRestoreUiDesc},showSmartFolders:{section:"joplinExplorer",type:3,value:!0,public:!0,label:e.sShowSmart,description:e.sShowSmartDesc},smartFolderRules:{section:"joplinExplorer",type:2,value:"",public:!0,advanced:!0,label:e.sSmartRules,description:e.sSmartRulesDesc},smartFolderLimit:{section:"joplinExplorer",type:1,value:5,minimum:1,maximum:50,public:!0,label:e.sSmartLimit,description:e.sSmartLimitDesc},hoverPreview:{section:"joplinExplorer",type:3,value:!0,public:!0,label:e.sHoverPreview,description:e.sHoverPreviewDesc},hoverPreviewLength:{section:"joplinExplorer",type:1,value:200,minimum:50,maximum:1e3,public:!0,label:e.sHoverPreviewLen},toggleArrowPosition:{section:"joplinExplorer",type:2,value:"right",isEnum:!0,options:{right:e.sArrowRight,left:e.sArrowLeft},public:!0,label:e.sArrowPos,description:e.sArrowPosDesc},expandAllMode:{section:"joplinExplorer",type:2,value:"restore",isEnum:!0,options:{restore:e.sExpandRestore,skeleton:e.sExpandSkeleton,all:e.sExpandAllOpt,collapseOnly:e.sExpandCollapseOnly},public:!0,label:e.sExpandMode,description:e.sExpandModeDesc},collapseAllScope:{section:"joplinExplorer",type:2,value:"notebooksOnly",isEnum:!0,options:{notebooksOnly:e.sScopeNotebooks,allSections:e.sScopeAll},public:!0,label:e.sCollapseScope,description:e.sCollapseScopeDesc},sectionOrder:{section:"joplinExplorer",type:2,value:"pinned,smart,tree,tags,trash",public:!0,label:e.sSectionOrder,description:e.sSectionOrderDesc},sectionSpacing:{section:"joplinExplorer",type:1,value:5,minimum:0,maximum:30,public:!0,label:e.sSectionGap,description:e.sSectionGapDesc},rowFontSize:{section:"joplinExplorer",type:1,value:13,minimum:9,maximum:18,public:!0,advanced:!0,label:e.sRowFont,description:e.sRowFontDesc},rowHeight:{section:"joplinExplorer",type:1,value:24,minimum:16,maximum:40,public:!0,advanced:!0,label:e.sRowHeight,description:e.sRowHeightDesc},noteIcon:{section:"joplinExplorer",type:2,value:"📄",public:!0,label:e.sNoteIcon,description:e.sNoteIconDesc},showFolderToggles:{section:"joplinExplorer",type:3,value:!0,public:!0,label:e.sShowToggles,description:e.sShowTogglesDesc},openFolderIcon:{section:"joplinExplorer",type:2,value:"📂",public:!0,label:e.sOpenFolderIcon,description:e.sOpenFolderIconDesc},closedFolderIcon:{section:"joplinExplorer",type:2,value:"📁",public:!0,label:e.sClosedFolderIcon,description:e.sClosedFolderIconDesc},openPinnedIcon:{section:"joplinExplorer",type:2,value:"📌",public:!0,label:e.sOpenPinnedIcon,description:e.sOpenPinnedIconDesc},closedPinnedIcon:{section:"joplinExplorer",type:2,value:"📌",public:!0,label:e.sClosedPinnedIcon,description:e.sClosedPinnedIconDesc}})}catch(e){console.error("Joplin Explorer: failed to register settings",e)}async function i(){try{const e=String(await joplin.settings.value("noteIcon")||"").trim();y=e||"📄"}catch(e){}}await i();const p=await joplin.views.dialogs.create("explorerInputDialog"),v=await joplin.views.dialogs.create("explorerConfirmDialog"),_=await joplin.views.dialogs.create("explorerInfoDialog"),j=await joplin.views.dialogs.create("explorerSmartDialog");async function D(t){return await joplin.views.dialogs.setHtml(v,'<div style="padding:10px;box-sizing:border-box;width:340px;max-width:100%;"><div style="font-size:13px;line-height:1.6;white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere;">'+c(t)+"</div></div>"),await joplin.views.dialogs.setButtons(v,[{id:"ok",title:"OK"},{id:"cancel",title:e.cancel||"Cancel"}]),"ok"===(await joplin.views.dialogs.open(v)).id}async function k(e,t){await joplin.views.dialogs.setHtml(_,'<div style="padding:10px;min-width:320px;"><div style="font-size:14px;font-weight:bold;margin-bottom:10px;">'+c(e)+'</div><div style="font-size:12px;line-height:1.8;white-space:pre-wrap;">'+c(t)+"</div></div>"),await joplin.views.dialogs.setButtons(_,[{id:"ok",title:"OK"}]),await joplin.views.dialogs.open(_)}async function I(t,o){await joplin.views.dialogs.setHtml(p,'<div style="padding:10px;box-sizing:border-box;width:360px;max-width:100%;"><div style="margin-bottom:8px;font-size:13px;word-break:break-word;overflow-wrap:anywhere;">'+c(t)+'</div><form name="inputForm"><input name="value" type="text" autofocus value="'+c(o||"")+'" style="width:100%;box-sizing:border-box;padding:6px 8px;font-size:13px;" /></form><script>setTimeout(function(){var i=document.querySelector(\'input[name=value]\');if(i){i.focus();i.select();}},30);<\/script></div>'),await joplin.views.dialogs.setButtons(p,[{id:"ok",title:"OK"},{id:"cancel",title:e.cancel||"Cancel"}]);const a=await joplin.views.dialogs.open(p);return"ok"===a.id&&a.formData&&a.formData.inputForm&&a.formData.inputForm.value||null}function T(e,t){return String(e||"").split("{name}").join(t)}async function E(){const e=String(await joplin.settings.value("smartFolderRules")||"");return e.trim()?e.split(";"):[]}async function C(e){const t=e.map(e=>e.trim()).filter(e=>!!e);await joplin.settings.setValue("smartFolderRules",t.join(";"))}function R(e){const t=String(e||"").trim(),o=t.indexOf(":");return o<=0?null:{name:t.slice(0,o).trim(),query:t.slice(o+1).trim()}}function F(t,o){return!t||t.indexOf(":")>=0||t.indexOf(";")>=0?e.smartInvalidName:!o||o.indexOf(";")>=0?e.smartInvalidQuery:""}async function O(){let t=(await E()).filter(e=>e.trim()).map(e=>R(e)||{name:"",query:e.trim()}),o="";const a={};for(const t of["smartColName","smartColQuery","smartMoveUp","smartMoveDown","smartRemove","smartEmptyList","smartCountTip","smartInvalidName","smartInvalidQuery"])a[t]=e[t];for(;;){await joplin.views.dialogs.setHtml(j,'<form name="smartForm"><div id="smart-dialog" data-rules="'+c(JSON.stringify(t))+'" data-i18n="'+c(JSON.stringify(a))+'"><div class="sd-title">'+c(String(e.smartManage).replace(/(\.\.\.|…)$/,""))+'</div><div class="sd-hint">'+c(e.smartManageHint)+"</div>"+(o?'<div class="sd-error">'+c(o)+"</div>":"")+'<div class="sd-head"><span>'+c(e.smartColName)+"</span><span>"+c(e.smartColQuery)+'</span><span></span><span></span><span></span><span></span></div><div class="sd-list"></div><button type="button" class="sd-add">+ '+c(e.smartAdd)+'</button><input type="hidden" id="smart-rules-json" name="rules" value="'+c(JSON.stringify(t))+'" /></div></form>'),await joplin.views.dialogs.setButtons(j,[{id:"ok",title:"OK"},{id:"cancel",title:e.cancel||"Cancel"}]);const s=await joplin.views.dialogs.open(j);if("ok"!==s.id)return;let n=[];try{n=JSON.parse(s.formData&&s.formData.smartForm&&s.formData.smartForm.rules||"[]")}catch(e){return}t=(Array.isArray(n)?n:[]).map(e=>({name:String(e&&e.name||"").trim(),query:String(e&&e.query||"").trim()})).filter(e=>e.name||e.query);const i=[],r={};if(t.forEach((t,o)=>{const a=F(t.name,t.query);a?i.push(T(e.smartRowError,String(o+1)).split("{err}").join(a)):r[t.name]&&i.push(T(e.smartDupName,t.name)),r[t.name]=!0}),!i.length)return await C(t.map(e=>e.name+":"+e.query)),t.length&&(await joplin.settings.setValue("showSmartFolders",!0),Q=!1),void await pe();o=i.join("\n")}}await joplin.views.dialogs.addScript(j,"webview/smart-dialog.css"),await joplin.views.dialogs.addScript(j,"webview/smart-dialog.js"),await joplin.views.panels.onMessage(j,async e=>{if(!e||"smartCount"!==e.name)return null;try{return await async function(e){let t=0,o=1,a=!0;for(;a&&t<=100;){const s=await joplin.data.get(["search"],{query:e,fields:["id"],page:o,limit:100});t+=(s.items||[]).length,a=s.has_more,o++}return{count:Math.min(t,100),more:t>100}}(String(e.query||""))}catch(e){return{error:String(e&&e.message||e)}}});let M="",A={},P="updated_desc";try{const e=String(await joplin.settings.value("noteSortMode")||"");["updated_desc","updated_asc","title_asc","title_desc","manual"].indexOf(e)>=0&&(P=e)}catch(e){}let U=[],L={},J=[],H=[],q=!1,z=!1,B=!0,Q=!1,V=!1,W={},G=!0,X=null,K=[],$={},Z=!0,Y=!1;if(z=!0,!1!==await joplin.settings.value("restoreUiState"))try{const e=String(await joplin.settings.value("uiState")||"{}"),t=JSON.parse(e);t&&"object"==typeof t&&(t.collapsedFolders&&"object"==typeof t.collapsedFolders&&!Array.isArray(t.collapsedFolders)&&(A=t.collapsedFolders,Y=!0),"boolean"==typeof t.tagsCollapsed&&(z=t.tagsCollapsed),"boolean"==typeof t.pinnedCollapsed&&(q=t.pinnedCollapsed),"boolean"==typeof t.trashCollapsed&&(B=t.trashCollapsed),"boolean"==typeof t.smartCollapsed&&(Q=t.smartCollapsed),"boolean"==typeof t.treeCollapsed&&(V=t.treeCollapsed),t.seenFolders&&"object"==typeof t.seenFolders&&!Array.isArray(t.seenFolders)&&(W=t.seenFolders,G=!1),Array.isArray(t.collapseSnapshot)&&(X=t.collapseSnapshot.map(String)))}catch(e){}let ee=null;function te(){ee&&clearTimeout(ee),ee=setTimeout(async()=>{ee=null;try{await joplin.settings.setValue("uiState",JSON.stringify({collapsedFolders:A,tagsCollapsed:z,pinnedCollapsed:q,trashCollapsed:B,smartCollapsed:Q,treeCollapsed:V,seenFolders:W,collapseSnapshot:X}))}catch(e){console.error("Joplin Explorer: failed to save UI state",e)}},500)}const oe=await joplin.plugins.dataDir();let ae=null;function se(e=600){ae&&clearTimeout(ae),ae=setTimeout(async()=>{ae=null,await pe()},e)}let ne=null,ie={};async function re(){try{await joplin.settings.setValue("pinnedItems",JSON.stringify(H))}catch(e){console.error("Joplin Explorer: failed to save pinned items",e)}}async function le(){try{await joplin.settings.setValue("folderOrder",JSON.stringify($))}catch(e){console.error("Joplin Explorer: failed to save folder order",e)}}function de(e){let t=e;for(;t;){delete A[t];const e=L[t];t=e?e.parent_id:null}}async function ce(){let e=-1;try{e=(await g()).length}catch(e){return}(async()=>{for(let t=0;t<240;t++){await new Promise(e=>setTimeout(e,500));try{const t=await g();if(t.length>e){const e=new Set(Object.keys(L));for(const o of t)e.has(o.id)||(W[o.id]=!0,o.parent_id&&delete A[o.parent_id]);return te(),void se(100)}}catch(e){return}}})()}async function pe(){try{const t=await g();await async function(){try{const e=await joplin.settings.value("folderOrder"),t=e?JSON.parse(e):{};$=t&&"object"==typeof t&&!Array.isArray(t)?t:{}}catch(e){$={}}}();let a=!1;const s=new Set(t.map(e=>e.id));for(const e of Object.keys($))s.has(e)||(delete $[e],a=!0);a&&await le();for(const e of t)e.order=$[e.id]||0;U=t,L={};for(const e of t)L[e.id]=e;let n=!1;for(const e of t)W[e.id]||(W[e.id]=!0,G||(A[e.id]=!0),n=!0);if(G&&(G=!1,n=!0),n&&te(),Z){if(!Y)for(const e of t)A[e.id]=!0;const e=await joplin.workspace.selectedNote();e&&(M=e.id,de(e.parent_id)),Z=!1}const i=await async function(){let e=[],t=1,o=!0;for(;o;){const a=await joplin.data.get(["notes"],{fields:["id","title","parent_id","is_todo","todo_completed","updated_time","user_updated_time","order","is_shared","body"],page:t,limit:100});for(const e of a.items){const t=r(e.body||"");e.cb_total=t.total,e.cb_done=t.done,delete e.body}e=e.concat(a.items),o=a.has_more,t++}return e}(),l={};for(const e of t)l[e.id]=[];for(const e of i)l[e.parent_id]&&l[e.parent_id].push(e);for(const e of Object.keys(l))l[e]=N(l[e],P);const p=[];for(const e of Object.keys(l))for(const t of l[e])p.push(t);J=p;const u=await joplin.settings.value("showFolderToggles"),y=await joplin.settings.value("openFolderIcon"),v=await joplin.settings.value("closedFolderIcon"),_=await joplin.settings.value("openPinnedIcon"),j=await joplin.settings.value("closedPinnedIcon"),D=await w(y,"📂","openFolderIcon",oe),k=await w(v,"📁","closedFolderIcon",oe),I=await w(_,"📌","openPinnedIcon",oe),T=await w(j,"📌","closedPinnedIcon",oe),E=!1!==u,C=function(e,t){const o={},a={},s={};for(const t of e)o[t.id]={type:"folder",id:t.id,title:t.title,parent_id:t.parent_id,icon:t.icon,order:t.order||0,note_count:0,children:[]},a[t.id]=[],s[t.id]=[];for(const e of Object.keys(t))if(s[e])for(const o of t[e])s[e].push({type:"note",id:o.id,title:o.title||"(untitled)",is_todo:o.is_todo,todo_completed:o.todo_completed,is_shared:o.is_shared,cb_total:o.cb_total,cb_done:o.cb_done});const n=[];for(const t of e){const e=o[t.id];t.parent_id&&o[t.parent_id]?a[t.parent_id].push(e):n.push(e)}function i(e){const t=a[e.id].sort(f);for(const e of t)i(e);e.note_count=s[e.id].length,e.children=t.concat(s[e.id])}n.sort(f);for(const e of n)i(e);const r=n;function l(e){let t=0;if(!e.children)return 0;for(const o of e.children)"note"===o.type?t++:"folder"===o.type&&(l(o),t+=o.total_count||0);return e.total_count=t,t}for(const e of r)l(e);return r}(t,l),R=S(C,M,A,0,E,D,k),F=!1!==await joplin.settings.value("showTagsSection");if(F)try{let e=[],t=1,o=!0;for(;o;){const a=await joplin.data.get(["tags"],{fields:["id","title"],page:t,limit:100});e=e.concat(a.items),o=a.has_more,t++}e.sort((e,t)=>(e.title||"").localeCompare(t.title||""));let a=e.map(()=>""),s=e.map(()=>[]);if(e.length<=200){const t=await Promise.all(e.map(e=>joplin.data.get(["tags",e.id,"notes"],{fields:["id","deleted_time"],limit:100}).then(e=>{const t=(e.items||[]).filter(e=>!e.deleted_time);return{count:0!==t.length||e.has_more?e.has_more?"100+":String(t.length):"0",ids:t.map(e=>e.id)}}).catch(()=>({count:"",ids:[]}))));a=t.map(e=>e.count),s=t.map(e=>e.ids);const o=a.map(e=>"0"!==e);e=e.filter((e,t)=>o[t]),s=s.filter((e,t)=>o[t]),a=a.filter(e=>"0"!==e)}K=e.map((e,t)=>({id:e.id,title:e.title||"",count:a[t]||"",noteIds:s[t]||[]}))}catch(e){K=[]}else K=[];await async function(){try{const e=await joplin.settings.value("pinnedItems");if(!e)return;const t=JSON.parse(e);if(t&&!Array.isArray(t)&&t.notes&&t.folders){H=[];for(const e of t.folders)H.push({id:e,type:"folder"});for(const e of t.notes)H.push({id:e,type:"note"});await re()}else Array.isArray(t)&&(H=t)}catch(e){H=[]}}();const O=new Set(t.map(e=>e.id)),ee=new Set(p.map(e=>e.id)),ae=H.length;H=H.filter(e=>"folder"===e.type?O.has(e.id):ee.has(e.id)),H.length!==ae&&await re();let se="";const ne=H.length;se+='<div class="pinned-section-header'+(q?" collapsed":"")+'" id="pinned-header">',E&&(se+='<span class="toggle">'+(q?"▶":"▼")+"</span>"),se+=m(I,"icon-when-open")+m(T,"icon-when-collapsed")+'<span class="label">'+e.pinned+"</span>"+(ne?'<span class="count">'+ne+"</span>":"")+"</div>",se+='<div class="pinned-section-body'+(q?" collapsed":"")+'" id="pinned-body">';for(const e of H)if("folder"===e.type){const t=L[e.id]||null;if(t){const e=h(t,!0,D,k);se+='<div class="tree-item folder pinned-item" data-id="'+t.id+'" data-type="folder">',se+=m(e,"folder-icon"),se+='<span class="label">'+c(t.title)+"</span>",se+="</div>"}}else{let t=null;for(const o of p)if(o.id===e.id){t=o;break}if(t){const e=t.id===M?" selected":"",o=x(t);se+='<div class="tree-item note pinned-item'+e+'" data-id="'+t.id+'" data-type="note" data-todo="'+(t.is_todo?1:0)+'">',se+='<span class="icon note-icon">'+o+"</span>",se+='<span class="label">'+c(t.title)+"</span>",se+=d(t),se+="</div>"}}se+="</div>";let ie="";if(F){ie+='<div class="tags-section-header'+(z?" collapsed":"")+'" id="tags-header">',E&&(ie+='<span class="toggle">'+(z?"▶":"▼")+"</span>"),ie+='<span class="icon">🏷️</span><span class="label">'+e.tags+"</span>"+(K.length?'<span class="count">'+K.length+"</span>":"")+"</div>",ie+='<div class="tags-section-body'+(z?" collapsed":"")+'" id="tags-body">';const t=String(await joplin.settings.value("tagNestSeparator")||"");if(t)ie+=b(function(e,t){const o=[],a={},s=(e,t)=>({path:e,label:t,tagId:"",count:"",noteIds:[],bounded:!1,children:[]});for(const n of e){const e=n.title.split(t).filter(e=>""!==e);if(!e.length)continue;let i="",r=o;for(let o=0;o<e.length;o++){i=0===o?e[0]:i+t+e[o];let l=a[i];l||(l=s(i,e[o]),a[i]=l,r.push(l)),o===e.length-1&&(l.tagId=n.id,l.count=n.count,l.noteIds=n.noteIds||[],l.bounded="100+"===n.count),r=l.children}}const n=e=>{e.sort((e,t)=>e.label.localeCompare(t.label)),e.forEach(e=>n(e.children))};return n(o),o}(K,t),E,{both:e.tagCountBoth,sub:e.tagCountSub});else for(const e of K)ie+='<div class="tree-item folder tag-folder collapsed" data-tag-id="'+e.id+'" data-type="tag">',E&&(ie+='<span class="toggle">▶</span>'),ie+='<span class="icon">🏷️</span>',ie+='<span class="label">'+c(e.title)+"</span>",e.count&&(ie+='<span class="count">'+e.count+"</span>"),ie+="</div>",ie+='<div class="tag-children collapsed" data-tag-id="'+e.id+'"></div>';ie+="</div>"}let ce="";try{const t={};let o=0,a=!1,s=1,n=!0;for(;n;){const e=await joplin.data.get(["folders"],{fields:["id","deleted_time"],include_deleted:"1",page:s,limit:100});for(const a of e.items||[])a.deleted_time>0&&(t[a.id]=!0,o++);n=e.has_more,s++}const i=(await joplin.data.get(["notes"],{fields:["id","parent_id","deleted_time"],include_deleted:"1",order_by:"deleted_time",order_dir:"DESC",limit:100})).items||[];for(const e of i){if(!e.deleted_time)break;t[e.parent_id]||o++}100===i.length&&i[99].deleted_time>0&&(a=!0),ce+='<div class="trash-section-header'+(B?" collapsed":"")+'" id="trash-header">',E&&(ce+='<span class="toggle">'+(B?"▶":"▼")+"</span>"),ce+='<span class="icon">🗑️</span><span class="label">'+e.trash+"</span>"+(o>0||a?'<span class="count">'+o+(a?"+":"")+"</span>":"")+"</div>",ce+='<div class="trash-children'+(B?" collapsed":"")+'" id="trash-children"></div>'}catch(e){}let pe="";if(!1!==await joplin.settings.value("showSmartFolders")){const t=[{id:"recent",title:e.smartRecent,query:"updated:day-7"},{id:"todos",title:e.smartTodos,query:"type:todo iscompleted:0"}],o=String(await joplin.settings.value("smartFolderRules")||"").trim();if(o){const e=o.split(";");for(let o=0;o<e.length;o++){const a=e[o].trim();if(!a)continue;const s=a.indexOf(":");s<=0||t.push({id:"rule"+o,title:a.slice(0,s).trim(),query:a.slice(s+1).trim()})}}pe+='<div class="smart-section-header'+(Q?" collapsed":"")+'" id="smart-header">',E&&(pe+='<span class="toggle">'+(Q?"▶":"▼")+"</span>"),pe+='<span class="icon">🔍</span><span class="label">'+e.smartFolders+"</span></div>",pe+='<div class="smart-section-body'+(Q?" collapsed":"")+'" id="smart-body">';for(const e of t){if(!e.title||!e.query)continue;let t=0,o=!1;try{if("recent"===e.id)t=(await fe(1)).length;else if("todos"===e.id){const e=await he(101);t=Math.min(e.length,100),o=e.length>100}else{let a=1,s=!0;for(;s&&t<=100;){const o=await joplin.data.get(["search"],{query:e.query,fields:["id"],page:a,limit:100});t+=(o.items||[]).length,s=o.has_more,a++}t>100&&(t=100,o=!0)}}catch(e){}const a=t>0;pe+='<div class="tree-item folder smart-folder collapsed'+(a?"":" smart-empty")+'" style="padding-left:26px" data-smart-id="'+e.id+'" data-query="'+c(e.query)+'" data-title="'+c(e.title)+'" data-custom="'+(0===e.id.indexOf("rule")?"1":"0")+'" data-type="smart"><span class="toggle">▶</span><span class="icon">🔍</span><span class="label">'+c(e.title)+"</span>"+(a&&"recent"!==e.id?'<span class="count">'+t+(o?"+":"")+"</span>":"")+"</div>",a&&(pe+='<div class="smart-children collapsed" data-smart-id="'+e.id+'"></div>')}pe+="</div>"}const me=c(JSON.stringify(H)),ue=t.length>0&&t.every(e=>!0===A[e.id]),ge={updated_desc:e.sortUpdatedDesc,updated_asc:e.sortUpdatedAsc,title_asc:e.sortTitleAsc,title_desc:e.sortTitleDesc,manual:e.sortManual},ye=c(JSON.stringify(e)),xe=String(await joplin.settings.value("expandAllMode")||"restore"),ve=String(await joplin.settings.value("collapseAllScope")||"notebooksOnly"),be=!1!==await joplin.settings.value("hoverPreview")?"1":"0",Se="left"===String(await joplin.settings.value("toggleArrowPosition")||"right")?"left":"right",Ne={pinned:se,smart:pe,tree:'<div class="tree-section-header'+(V?" collapsed":"")+'" id="tree-header">'+(E?'<span class="toggle">'+(V?"▶":"▼")+"</span>":"")+'<span class="icon">📚</span><span class="label">'+e.notebooksSection+'</span><span class="count">'+C.reduce((e,t)=>e+("folder"===t.type?t.total_count||t.note_count||0:1),0)+'</span></div><div id="main-tree"'+(V?' class="collapsed"':"")+">"+R+"</div>",tags:ie,trash:ce},_e=[];for(let e of String(await joplin.settings.value("sectionOrder")||"").split(",").map(e=>e.trim().toLowerCase()).filter(e=>!!e))"notebooks"===e&&(e="tree"),void 0!==Ne[e]&&_e.indexOf(e)<0&&_e.push(e);for(const e of["pinned","smart","tree","tags","trash"])_e.indexOf(e)<0&&_e.push(e);const je=_e.map(e=>Ne[e]).join("");let De=Number(await joplin.settings.value("sectionSpacing"));(!isFinite(De)||De<0)&&(De=5),De>30&&(De=30);const ke=!1!==await joplin.settings.value("stackSectionHeaders")?"1":"0";let Ie=Number(await joplin.settings.value("rowFontSize"));(!isFinite(Ie)||Ie<9)&&(Ie=13),Ie>18&&(Ie=18);let Te=Number(await joplin.settings.value("rowHeight"));(!isFinite(Te)||Te<16)&&(Te=24),Te>40&&(Te=40);const Ee='<div id="notes-in-list-root" style="--sec-gap:'+De+"px;--row-font:"+Ie+"px;--row-h:"+Te+'px" data-i18n="'+ye+'" data-pinned="'+me+'" data-sort="'+c(P)+'" data-expand-mode="'+c(xe)+'" data-collapse-scope="'+c(ve)+'" data-hover-preview="'+be+'" data-arrow-pos="'+Se+'" data-stack-headers="'+ke+'" data-collapse-snapshot="'+c(JSON.stringify(X))+'"> <div class="toolbar"> <button id="btn-new" title="'+e.newItem+'">+</button> <button id="btn-sort" title="'+e.sort+'">'+ge[P]+'</button> <button id="btn-collapse-all" data-mode="'+(ue&&"collapseOnly"!==xe?"expand":"collapse")+'" title="'+(ue&&"collapseOnly"!==xe?e.expandAll:e.collapseAll)+'">'+(ue&&"collapseOnly"!==xe?"▼":"▲")+'</button> <button id="btn-reveal" title="'+(e.revealNote||"Reveal current note")+'">◎</button> <button id="btn-refresh" title="'+e.refresh+'">↻</button> </div> <div class="search-bar"> <input id="search-input" type="text" placeholder="🔍 '+e.search+'" /> <button id="search-clear" title="'+(e.clearSearch||"Clear")+'" aria-label="'+(e.clearSearch||"Clear")+'">✕</button> </div> <div id="tree-container">'+je+' <div id="drop-zone-empty" class="drop-zone-empty">+ '+e.dropCreateNotebook+'</div> </div> <div id="search-results" style="display:none;"></div> <div class="bottom-bar"> <button id="btn-sync" title="'+c(e.syncTip||e.sync)+'">🔄 '+e.sync+"</button> </div></div>";if(await joplin.views.panels.setHtml(o,Ee),ce&&!B)try{const e=await we();await joplin.views.panels.postMessage(o,{name:"trashNotes",folders:e.folders,notes:e.notes})}catch(e){}}catch(e){console.error("Notes In List: refresh error",e),await joplin.views.panels.setHtml(o,'<div style="padding:12px;color:red;">Error: '+c(String(e))+"</div>")}}async function me(e){try{const t=J.findIndex(t=>t.id===e),a=await joplin.data.get(["notes",e],{fields:["id","title","parent_id","is_todo","todo_completed","updated_time","user_updated_time","order","is_shared","body"]}),s=r(a.body||"");if(a.cb_total=s.total,a.cb_done=s.done,delete a.body,t<0||J[t].parent_id!==a.parent_id)return void await pe();const n=J[t],i=0===P.indexOf("title_")&&(n.title||"")!==(a.title||""),l=0===P.indexOf("updated_")&&n.user_updated_time!==a.user_updated_time;if(J[t]=a,i||l)return void await pe();await joplin.views.panels.postMessage(o,{name:"updateNote",id:a.id,title:a.title||"(untitled)",icon:x(a),badges:d(a)})}catch(e){await pe()}}async function ue(e){let t=[],o=1,a=!0;for(;a&&t.length<200;){const s=await joplin.data.get(["tags",e,"notes"],{fields:["id","title","is_todo","todo_completed","deleted_time"],page:o,limit:50});t=t.concat((s.items||[]).filter(e=>!e.deleted_time)),a=s.has_more,o++}return t.map(e=>({id:e.id,title:e.title||"(untitled)",is_todo:e.is_todo,todo_completed:e.todo_completed}))}async function we(){const e=[],t={},o={};try{let a=1,s=!0;for(;s;){const n=await joplin.data.get(["folders"],{fields:["id","title","parent_id","deleted_time"],include_deleted:"1",page:a,limit:100});for(const a of n.items||[])o[a.id]=a.parent_id||"",a.deleted_time>0&&(e.push({id:a.id,title:a.title||"(untitled)",parent_id:a.parent_id}),t[a.id]=!0);s=n.has_more,a++}}catch(e){}const a=[],s={},n=[];for(const o of e)o.parent_id&&t[o.parent_id]?(s[o.parent_id]=s[o.parent_id]||[]).push(o):n.push(o);const i=(e,t)=>{a.push({id:e.id,title:e.title,depth:t,hasSub:!(!s[e.id]||!s[e.id].length)});for(const o of s[e.id]||[])i(o,t+1)};for(const e of n)i(e,0);await Promise.all(a.map(async e=>{try{const t=await joplin.data.get(["folders",e.id,"notes"],{fields:["id"],include_deleted:"1",limit:1});e.hasNotes=!(!t.items||!t.items.length)}catch(t){e.hasNotes=!0}}));const r=[];let l=1,d=!0;for(;d&&r.length<200;){const e=await joplin.data.get(["notes"],{fields:["id","title","parent_id","is_todo","todo_completed","deleted_time"],include_deleted:"1",order_by:"deleted_time",order_dir:"DESC",page:l,limit:50});let a=!1;for(const s of e.items||[]){if(!s.deleted_time){a=!0;break}let e=!1,n=s.parent_id,i=0;for(;n&&i++<50;){if(t[n]){e=!0;break}n=o[n]||""}e||r.push({id:s.id,title:s.title||"(untitled)",is_todo:s.is_todo,todo_completed:s.todo_completed})}if(a)break;d=e.has_more,l++}return{folders:a,notes:r}}async function ge(e){const t={};try{let e=1,o=!0;for(;o;){const a=await joplin.data.get(["folders"],{fields:["id","parent_id"],include_deleted:"1",page:e,limit:100});for(const e of a.items||[])e.parent_id&&(t[e.parent_id]=t[e.parent_id]||[]).push(e.id);o=a.has_more,e++}}catch(e){}const o=async e=>{for(const a of t[e]||[])await o(a);try{let t=1,o=!0;for(;o;){const a=await joplin.data.get(["folders",e,"notes"],{fields:["id"],include_deleted:"1",page:t,limit:100});for(const e of a.items||[])try{await joplin.data.delete(["notes",e.id],{permanent:"1"})}catch(e){}o=(a.items||[]).length>0&&a.has_more}}catch(e){}try{await joplin.data.delete(["folders",e],{permanent:"1"})}catch(e){}};await o(e)}async function fe(e){return((await joplin.data.get(["notes"],{fields:["id","title","is_todo","todo_completed","user_updated_time"],order_by:"user_updated_time",order_dir:"DESC",limit:Math.min(e,100)})).items||[]).map(e=>({id:e.id,title:e.title||"(untitled)",is_todo:e.is_todo,todo_completed:e.todo_completed}))}async function he(e){const t=[];let o=1,a=!0;for(;a&&t.length<e&&o<=10;){const e=await joplin.data.get(["notes"],{fields:["id","title","is_todo","todo_completed","user_updated_time"],order_by:"is_todo",order_dir:"DESC",page:o,limit:100});let s=!1;for(const o of e.items||[]){if(!o.is_todo){s=!0;break}o.todo_completed||t.push({id:o.id,title:o.title||"(untitled)",is_todo:o.is_todo,todo_completed:o.todo_completed,user_updated_time:o.user_updated_time||0})}if(s)break;a=e.has_more,o++}return t.sort((e,t)=>(t.user_updated_time||0)-(e.user_updated_time||0)),t.slice(0,e)}async function ye(e){return await Promise.all(e.map(async e=>{try{const t=await joplin.data.get(["notes",e.id],{fields:["is_shared","body"]}),o=r(t.body||"");e.is_shared=t.is_shared,e.cb_total=o.total,e.cb_done=o.done,e.badges=d(e)}catch(t){e.badges=""}})),e}await joplin.settings.onChange(async e=>{e.keys&&e.keys.indexOf("autoRefresh")>=0&&await Ie(),e.keys&&(e.keys.indexOf("toggleArrowPosition")>=0||e.keys.indexOf("showSmartFolders")>=0||e.keys.indexOf("smartFolderRules")>=0||e.keys.indexOf("hoverPreview")>=0||e.keys.indexOf("expandAllMode")>=0||e.keys.indexOf("collapseAllScope")>=0||e.keys.indexOf("showTagsSection")>=0||e.keys.indexOf("tagNestSeparator")>=0||e.keys.indexOf("stackSectionHeaders")>=0||e.keys.indexOf("sectionOrder")>=0||e.keys.indexOf("sectionSpacing")>=0||e.keys.indexOf("rowFontSize")>=0||e.keys.indexOf("rowHeight")>=0||e.keys.indexOf("showFolderToggles")>=0||e.keys.indexOf("openFolderIcon")>=0||e.keys.indexOf("closedFolderIcon")>=0||e.keys.indexOf("openPinnedIcon")>=0||e.keys.indexOf("closedPinnedIcon")>=0||e.keys.indexOf("noteIcon")>=0)&&(e.keys.indexOf("noteIcon")>=0&&await i(),function(){for(const e of Object.keys(u))delete u[e]}(),await pe())}),await joplin.views.panels.onMessage(o,async t=>{if("openNote"===t.name)M=t.id,await joplin.commands.execute("openNote",t.id),await joplin.views.panels.postMessage(o,{name:"selectNote",id:t.id});else if("refresh"===t.name)await pe();else if("toggleFolder"===t.name)A[t.id]?delete A[t.id]:A[t.id]=!0,te();else if("collapseAll"===t.name){for(const e of U)A[e.id]=!0;Array.isArray(t.prevCollapsed)&&(X=t.prevCollapsed.map(String)),t.sections&&(q=!0,Q=!0,z=!0,B=!0,V=!0),te()}else if("expandAll"===t.name){if(A={},Array.isArray(t.collapsedIds))for(const e of t.collapsedIds)A[String(e)]=!0;t.sections&&(q=!1,Q=!1,z=!1,B=!1,V=!1),te()}else if("panelReady"===t.name)await pe();else if("notePreview"===t.name)try{if(!1===await joplin.settings.value("hoverPreview"))return;const e=Number(await joplin.settings.value("hoverPreviewLength"))||200,a=await joplin.data.get(["notes",t.id],{fields:["id","title","body","is_todo","user_created_time","user_updated_time"],include_deleted:"1"});if(!a)return;const s=String(a.body||"").replace(/```[\s\S]*?```/g," ").replace(/!\[[^\]]*\]\([^)]*\)/g," ").replace(/\[([^\]]*)\]\([^)]*\)/g,"$1").replace(/^#{1,6}\s+/gm,"").replace(/[`*_>~]/g,"").replace(/\s+/g," ").trim().slice(0,e);await joplin.views.panels.postMessage(o,{name:"notePreview",id:t.id,title:a.title||"(untitled)",created:a.user_created_time||0,updated:a.user_updated_time||0,isTodo:!!a.is_todo,size:String(a.body||"").length,snippet:s})}catch(e){}else if("refreshView"===t.name)await pe();else if("newNotebook"===t.name)try{await joplin.commands.execute("openFolderDialog",{isNew:!0,parentId:""}),ce()}catch(e){console.error("Joplin Explorer: newNotebook error",e)}else if("newNote"===t.name){await joplin.commands.execute("newNote");const e=await joplin.workspace.selectedNote();e&&(M=e.id,de(e.parent_id)),await pe()}else if("newTodo"===t.name){await joplin.commands.execute("newTodo");const e=await joplin.workspace.selectedNote();e&&(M=e.id,de(e.parent_id)),await pe()}else if("manageSmartFolders"===t.name)await O();else if("saveSmartFolder"===t.name)await async function(t,o){t=String(t||"").trim();const a=(await I(e.smartSaveName,o||"")||"").trim();if(!a)return;const s=F(a,t);if(s)return void await k(e.smartInvalidTitle,s);const n=await E(),i=n.findIndex(e=>{const t=R(e);return!!t&&t.name===a});if(i>=0){if(!await D(T(e.smartConfirmReplace,a)))return;n[i]=a+":"+t}else n.push(a+":"+t);await joplin.settings.setValue("showSmartFolders",!0),Q=!1,await C(n),await pe()}(t.query,t.defaultName);else if("search"===t.name)await async function(e){const t=e.query;if(t&&t.trim())try{const a=t.toLowerCase(),s={};for(const e of U)s[e.id]=e.title;let n=[],i=1,r=!0;for(;r&&n.length<100;){const e=await joplin.data.get(["search"],{query:t,fields:["id","title","body","parent_id","is_todo","todo_completed"],page:i,limit:20});n=n.concat(e.items),r=e.has_more,i++}const l=new Set(n.map(e=>e.id)),d=[];for(const e of J)!l.has(e.id)&&(e.title||"").toLowerCase().indexOf(a)>=0&&d.push(e);for(let e=0;e<d.length&&e<50;e++)try{const t=await joplin.data.get(["notes",d[e].id],{fields:["id","title","body","parent_id","is_todo","todo_completed"]});n.push(t)}catch(e){}const c=[];for(const e of n){let t="";const o=e.body||"",n=o.toLowerCase().indexOf(a);if(n>=0){const e=Math.max(0,n-40),s=Math.min(o.length,n+a.length+80);t=(e>0?"...":"")+o.substring(e,s).replace(/\n/g," ")+(s<o.length?"...":"")}else t=o.substring(0,120).replace(/\n/g," ")+(o.length>120?"...":"");c.push({id:e.id,title:e.title||"(untitled)",is_todo:e.is_todo,todo_completed:e.todo_completed,snippet:t,folderName:s[e.parent_id]||""})}let p=[],m=1,u=!0;for(;u;){const e=await joplin.data.get(["tags"],{fields:["id","title"],page:m,limit:100});p=p.concat(e.items),u=e.has_more,m++}const w=[];for(const e of p)if((e.title||"").toLowerCase().indexOf(a)>=0){let t=0;try{const o=await joplin.data.get(["tags",e.id,"notes"],{fields:["id"],limit:100});t=o.has_more?o.items.length+"+":o.items.length}catch(e){}w.push({id:e.id,title:e.title,noteCount:t})}const g=[];for(const e of U)if((e.title||"").toLowerCase().indexOf(a)>=0){let t="";if(e.icon&&"string"==typeof e.icon)try{const o=JSON.parse(e.icon);o&&o.emoji&&(t=String(o.emoji))}catch(e){}g.push({id:e.id,title:e.title,parent_id:e.parent_id,iconEmoji:t})}await joplin.views.panels.postMessage(o,{name:"searchResults",notes:c,tags:w,folders:g,query:t,searchId:e.searchId})}catch(e){console.error("Joplin Explorer: search error",e)}else await joplin.views.panels.postMessage(o,{name:"searchResults",results:null,query:"",searchId:e.searchId})}(t);else if("loadTagNotes"===t.name)try{const e=t.tagId;let a=[],s=1,n=!0;for(;n&&a.length<100;){const t=await joplin.data.get(["tags",e,"notes"],{fields:["id","title","parent_id","is_todo","todo_completed"],page:s,limit:50});a=a.concat(t.items),n=t.has_more,s++}const i={};for(const e of U)i[e.id]=e.title;const r=a.map(e=>({id:e.id,title:e.title||"(untitled)",is_todo:e.is_todo,todo_completed:e.todo_completed,snippet:"",folderName:i[e.parent_id]||""}));await joplin.views.panels.postMessage(o,{name:"tagNotes",tagId:e,notes:r})}catch(e){console.error("Joplin Explorer: loadTagNotes error",e)}else if("toggleTagsSection"===t.name)z=!z,te();else if("tagFolderNotes"===t.name)try{const e=await ue(t.tagId);await joplin.views.panels.postMessage(o,{name:"tagFolderNotes",tagId:t.tagId,notes:e})}catch(e){console.error("Joplin Explorer: tagFolderNotes error",e)}else if("tagNoteAdd"===t.name)try{await joplin.data.post(["tags",t.tagId,"notes"],null,{id:t.noteId});const e=await ue(t.tagId);await joplin.views.panels.postMessage(o,{name:"tagFolderNotes",tagId:t.tagId,notes:e})}catch(e){console.error("Joplin Explorer: tagNoteAdd error",e)}else if("toggleSmartSection"===t.name)Q=!Q,te();else if("toggleTreeSection"===t.name)V=!V,te();else if("smartFolderNotes"===t.name)try{const e=Math.max(1,Number(await joplin.settings.value("smartFolderLimit"))||5);if("recent"===t.smartId||"todos"===t.smartId){const a="recent"===t.smartId?await fe(e):await he(e);return await ye(a),void await joplin.views.panels.postMessage(o,{name:"smartFolderNotes",smartId:t.smartId,notes:a})}let a=[],s=1,n=!0;for(;n&&a.length<100;){const e=await joplin.data.get(["search"],{query:String(t.query||""),fields:["id","title","is_todo","todo_completed","user_updated_time"],page:s,limit:50});a=a.concat((e.items||[]).map(e=>({id:e.id,title:e.title||"(untitled)",is_todo:e.is_todo,todo_completed:e.todo_completed,user_updated_time:e.user_updated_time||0}))),n=e.has_more,s++}a.sort((e,t)=>(t.user_updated_time||0)-(e.user_updated_time||0));const i=a.slice(0,e);await ye(i),await joplin.views.panels.postMessage(o,{name:"smartFolderNotes",smartId:t.smartId,notes:i})}catch(e){console.error("Joplin Explorer: smartFolderNotes error",e)}else if("toggleTrashSection"===t.name)B=!B,te();else if("trashFolderNotes"===t.name)try{let e=[],a=1,s=!0;for(;s&&e.length<200;){const o=await joplin.data.get(["folders",t.folderId,"notes"],{fields:["id","title","is_todo","todo_completed"],include_deleted:"1",page:a,limit:50});e=e.concat((o.items||[]).map(e=>({id:e.id,title:e.title||"(untitled)",is_todo:e.is_todo,todo_completed:e.todo_completed}))),s=o.has_more,a++}await joplin.views.panels.postMessage(o,{name:"trashFolderNotes",folderId:t.folderId,notes:e})}catch(e){console.error("Joplin Explorer: trashFolderNotes error",e)}else if("trashNotes"===t.name)try{const e=await we();await joplin.views.panels.postMessage(o,{name:"trashNotes",folders:e.folders,notes:e.notes})}catch(e){console.error("Joplin Explorer: trashNotes error",e)}else if("locateFolder"===t.name)try{de(t.folderId),await pe(),await joplin.views.panels.postMessage(o,{name:"exitSearchAndLocate",folderId:t.folderId})}catch(e){console.error("Joplin Explorer: locateFolder error",e)}else if("revealNote"===t.name){if(Array.isArray(t.folderIds)){for(const e of t.folderIds)delete A[String(e)];te()}}else if("locatePinnedFolder"===t.name)try{de(t.folderId),await pe(),await joplin.views.panels.postMessage(o,{name:"scrollToFolder",folderId:t.folderId})}catch(e){console.error("Joplin Explorer: locatePinnedFolder error",e)}else if("pinItemAt"===t.name)try{const e=String(t.dragId||"");if(e){const o="folder"===t.dragType?"folder":"note";H=H.filter(t=>t.id!==e);let a=H.length;if(t.targetId){const e=H.findIndex(e=>e.id===t.targetId);e>=0&&(a=e+("after"===t.position?1:0))}H.splice(a,0,{id:e,type:o}),await re(),await pe()}}catch(e){console.error("Joplin Explorer: pinItemAt error",e)}else if("reorderPinned"===t.name)try{const e=t.dragId,o=t.targetId,a=t.position,s=H.findIndex(t=>t.id===e);if(s<0)return;const n=H[s];H.splice(s,1);let i=H.findIndex(e=>e.id===o);i<0?H.push(n):("after"===a&&i++,H.splice(i,0,n)),await re(),await pe()}catch(e){console.error("Joplin Explorer: reorderPinned error",e)}else if("togglePinnedCollapse"===t.name)q=!q,te();else if("cycleSort"===t.name){const e=["updated_desc","updated_asc","title_asc","title_desc","manual"],t=e.indexOf(P);P=e[(t+1)%e.length];try{await joplin.settings.setValue("noteSortMode",P)}catch(e){}await pe()}else if("syncCancel"===t.name)try{await joplin.commands.execute("synchronize",!0)}catch(e){console.error("Joplin Explorer: sync cancel failed",e)}else if("sync"===t.name)try{await joplin.commands.execute("synchronize",!1)}catch(t){console.error("Joplin Explorer: sync command failed",t),await joplin.views.panels.postMessage(o,{name:"syncState",state:"error"});const a=t&&t.message?String(t.message):String(t);await k(e.syncFailed,a)}else if("contextMenu"===t.name)await async function(t){const a=t.action,s=t.id,n=t.itemType;try{if("folder"===n)switch(a){case"newNote":{const t=await joplin.data.post(["notes"],null,{title:e.newNote,parent_id:s});await joplin.commands.execute("openNote",t.id),M=t.id,de(s);break}case"newTodo":{const t=await joplin.data.post(["notes"],null,{title:e.newTodo,parent_id:s,is_todo:1});await joplin.commands.execute("openNote",t.id),M=t.id,de(s);break}case"newSubNotebook":try{delete A[s],await joplin.commands.execute("openFolderDialog",{isNew:!0,parentId:s}),ce()}catch(e){console.error("Joplin Explorer: newSubNotebook error",e)}break;case"deleteFolder":{const t=await joplin.data.get(["folders",s],{fields:["title"]});await D(e.confirmDeleteFolder+"\n\n"+t.title)&&await joplin.data.delete(["folders",s]);break}case"renameFolder":try{const e=(await joplin.data.get(["folders",s],{fields:["updated_time"]})).updated_time,t=M;await joplin.commands.execute("openFolderDialog",{folderId:s}),(async()=>{for(let o=0;o<240;o++){await new Promise(e=>setTimeout(e,500));try{if((await joplin.data.get(["folders",s],{fields:["updated_time"]})).updated_time!==e){if(se(100),t){await new Promise(e=>setTimeout(e,400));try{const e=await joplin.workspace.selectedNote();e&&e.id===t||await joplin.commands.execute("openNote",t)}catch(e){}}return}}catch(e){return void se(100)}}})()}catch(t){const o=await joplin.data.get(["folders",s],{fields:["title"]}),a=await I(e.promptRename,o.title);a&&a.trim()&&await joplin.data.put(["folders",s],null,{title:a.trim()})}break;case"copyFolderId":try{await joplin.clipboard.writeText(s)}catch(e){await joplin.views.panels.postMessage(o,{name:"copyText",text:s})}break;case"exportFolder":try{const e=await joplin.views.dialogs.showOpenDialog({properties:["openDirectory"]}),o=Array.isArray(e)?e[0]:e;o&&await joplin.commands.execute("exportFolders",[s],t.format||"jex",o)}catch(t){console.error("Joplin Explorer: exportFolder error",t),await k(e.exportFailed||"Export failed",String(t&&t.message?t.message:t))}break;case"pinFolder":H.some(e=>e.id===s)||(H.push({id:s,type:"folder"}),await re());break;case"unpinFolder":H=H.filter(e=>e.id!==s),await re()}else if("note"===n)switch(a){case"openNote":await joplin.commands.execute("openNote",s),M=s;break;case"openInNewWindow":try{await joplin.commands.execute("openNoteInNewWindow",s)}catch(e){await joplin.commands.execute("openNote",s)}break;case"editExternally":try{await joplin.commands.execute("openNote",s),M=s,await joplin.commands.execute("startExternalEditing",s)}catch(e){await k("Joplin",String(e&&e.message||e))}break;case"copyLink":{const e=await joplin.data.get(["notes",s],{fields:["id","title"]}),t="["+e.title+"](:/"+e.id+")";try{await joplin.clipboard.writeText(t)}catch(e){await joplin.views.panels.postMessage(o,{name:"copyText",text:t})}break}case"duplicateNote":{const e=await joplin.data.get(["notes",s],{fields:["title","body","parent_id","is_todo"]}),t=await joplin.data.post(["notes"],null,{title:e.title+" (copy)",body:e.body,parent_id:e.parent_id,is_todo:e.is_todo});await joplin.commands.execute("openNote",t.id),M=t.id;break}case"switchNoteType":{const e=await joplin.data.get(["notes",s],{fields:["is_todo"]});await joplin.data.put(["notes",s],null,{is_todo:e.is_todo?0:1});break}case"toggleTodo":{const e=await joplin.data.get(["notes",s],{fields:["is_todo","todo_completed"]});e.is_todo&&await joplin.data.put(["notes",s],null,{todo_completed:e.todo_completed?0:Date.now()});break}case"publishNote":try{await joplin.commands.execute("showShareNoteDialog",[s])}catch(e){}break;case"exportPdf":try{await joplin.commands.execute("exportPdf",[s])}catch(e){console.error("Joplin Explorer: exportPdf error",e)}break;case"exportNote":try{const e=await joplin.views.dialogs.showOpenDialog({properties:["openDirectory"]}),o=Array.isArray(e)?e[0]:e;o&&await joplin.commands.execute("exportNotes",[s],t.format||"md",o)}catch(e){console.error("Joplin Explorer: exportNote error",e)}break;case"setTags":try{await joplin.commands.execute("setTags",[s])}catch(e){}break;case"moveToFolderDialog":try{await joplin.commands.execute("moveToFolder",[s])}catch(e){}break;case"copyExternalLink":{const e="joplin://x-callback-url/openNote?id="+s;try{await joplin.clipboard.writeText(e)}catch(t){await joplin.views.panels.postMessage(o,{name:"copyText",text:e})}break}case"renameNote":{const t=await joplin.data.get(["notes",s],{fields:["title"]}),o=await I(e.promptRename,t.title);o&&o.trim()&&await joplin.data.put(["notes",s],null,{title:o.trim()});break}case"moveNote":if(t.targetFolderName){let e=null;for(const o of U)if(o.title===t.targetFolderName){e=o;break}e&&await joplin.data.put(["notes",s],null,{parent_id:e.id})}break;case"noteInfo":{const t=await joplin.data.get(["notes",s],{fields:["id","title","created_time","updated_time","is_todo","parent_id","body"]}),o=L[t.parent_id]?L[t.parent_id].title:"",a=(t.body||"").length,n="ID: "+t.id+"\n"+(e.ctxRenameNote||"Title")+": "+t.title+"\n"+(e.newNotebook||"Notebook")+": "+o+"\n"+(e.sortUpdatedDesc?e.sortUpdatedDesc.replace(/[↓↑\u2193\u2191]\s*/,""):"Created")+": "+new Date(t.created_time).toLocaleString()+"\n"+(e.sortUpdatedAsc?e.sortUpdatedAsc.replace(/[↓↑\u2193\u2191]\s*/,""):"Updated")+": "+new Date(t.updated_time).toLocaleString()+"\nType: "+(t.is_todo?"To-do":"Note")+"\nSize: "+a+" chars";await k(t.title,n);break}case"deleteNote":{const t=await joplin.data.get(["notes",s],{fields:["title"]});await D(e.confirmDeleteNote+"\n\n"+t.title)&&await joplin.data.delete(["notes",s]);break}case"untagNote":if(t.tagId){await joplin.data.delete(["tags",t.tagId,"notes",s]);try{const e=await ue(t.tagId);await joplin.views.panels.postMessage(o,{name:"tagFolderNotes",tagId:t.tagId,notes:e})}catch(e){}}break;case"pinNote":H.some(e=>e.id===s)||(H.push({id:s,type:"note"}),await re());break;case"unpinNote":H=H.filter(e=>e.id!==s),await re()}else if("smart"===n){const t=/^rule(\d+)$/.exec(String(s||"")),o=await E(),n=t?Number(t[1]):-1,i=n>=0&&n<o.length?R(o[n]):null;if(i)if("smartEditQuery"===a){const t=await I(T(e.smartEditQueryLabel,i.name),i.query);if(null!==t&&t.trim()!==i.query){const a=F(i.name,t.trim());a?await k(e.smartInvalidTitle,a):(o[n]=i.name+":"+t.trim(),await C(o))}}else if("smartRename"===a){const t=await I(e.smartSaveName,i.name);if(null!==t&&t.trim()!==i.name){const a=F(t.trim(),i.query);a?await k(e.smartInvalidTitle,a):(o[n]=t.trim()+":"+i.query,await C(o))}}else"smartDelete"===a&&await D(T(e.smartConfirmDelete,i.name))&&(o.splice(n,1),await C(o))}else if("tag"===n)switch(a){case"renameTag":{const t=await joplin.data.get(["tags",s],{fields:["title"]}),o=await I(e.ctxRenameTag,t.title);o&&o.trim()&&await joplin.data.put(["tags",s],null,{title:o.trim()});break}case"deleteTag":{const t=await joplin.data.get(["tags",s],{fields:["title"]});await D(e.confirmDeleteTag+"\n\n"+t.title)&&await joplin.data.delete(["tags",s]);break}}else if("trashSection"===n){if("emptyTrash"===a&&await D(e.confirmEmptyTrash)){try{await joplin.commands.execute("emptyTrash")}catch(e){const t=await we();for(const e of t.folders)0===e.depth&&await ge(e.id);for(const e of t.notes)try{await joplin.data.delete(["notes",e.id],{permanent:"1"})}catch(e){}}try{const e=await we();await joplin.views.panels.postMessage(o,{name:"trashNotes",folders:e.folders,notes:e.notes})}catch(e){}}}else if("trashNote"===n||"trashFolder"===n){const t="trashFolder"===n;switch(a){case"restoreItem":try{if(!t){let t=null;try{const e=await joplin.data.get(["notes",s],{fields:["parent_id"],include_deleted:"1"});if(e&&e.parent_id){const o=await joplin.data.get(["folders",e.parent_id],{fields:["id","title","deleted_time"],include_deleted:"1"});o&&o.deleted_time>0&&(t=o)}}catch(e){}if(t){const o=(e.confirmRestoreParent||'The notebook "{folder}" that held this note is also in the trash. Restore the notebook together with the note?').replace("{folder}",t.title||""),a=await async function(t,o,a){return await joplin.views.dialogs.setHtml(v,'<div style="padding:10px;box-sizing:border-box;width:360px;max-width:100%;"><div style="font-size:13px;line-height:1.6;white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere;">'+c(t)+"</div></div>"),await joplin.views.dialogs.setButtons(v,[{id:"both",title:o},{id:"noteOnly",title:a},{id:"cancel",title:e.cancel||"Cancel"}]),(await joplin.views.dialogs.open(v)).id}(o,e.restoreBoth||"Restore notebook + note",e.restoreNoteOnly||"Note only (to root)");if("cancel"===a)break;if("both"===a){await joplin.commands.execute("restoreFolder",[t.id]);break}}}await joplin.commands.execute(t?"restoreFolder":"restoreNote",[s])}catch(e){console.error("Joplin Explorer: restore error",e)}break;case"permanentDeleteItem":{const o=t?"folders":"notes";let a="";try{const e=await joplin.data.get([o,s],{fields:["title"],include_deleted:"1"});a=e?e.title:""}catch(e){}if(await D(e.confirmDeleteNote+"\n\n"+a))if(t)await ge(s);else try{await joplin.data.delete(["notes",s],{permanent:"1"})}catch(e){try{await joplin.commands.execute("permanentlyDeleteNote",[s])}catch(e){}}break}}try{const e=await we();await joplin.views.panels.postMessage(o,{name:"trashNotes",folders:e.folders,notes:e.notes})}catch(e){}}await pe()}catch(e){console.error("Notes In List: context menu error",e)}}(t);else if("dragDrop"===t.name)await async function(e){try{const t=e.dragId,o=e.dragType,a=e.targetId,s=e.position;if("note"===o)if(L[a])await joplin.data.put(["notes",t],null,{parent_id:a});else{const e=await joplin.data.get(["notes",a],{fields:["parent_id"]});"manual"!==P||"above"!==s&&"below"!==s?await joplin.data.put(["notes",t],null,{parent_id:e.parent_id}):await async function(e,t,o){const a=(await joplin.data.get(["notes",t],{fields:["parent_id"]})).parent_id,s=J.filter(t=>t.parent_id===a&&t.id!==e).sort((e,t)=>{const o=e.order||0,a=t.order||0;return o!==a?a-o:(t.user_updated_time||0)-(e.user_updated_time||0)}),n=s.findIndex(e=>e.id===t),i=n<0?s.length:"above"===o?n:n+1,r=i>0?s[i-1]:null,l=i<s.length?s[i]:null,d=r?r.order||0:null,c=l?l.order||0:null,p=1e6;if(s.every(e=>0===(e.order||0))||null!==d&&null!==c&&d-c<2){const t=Date.now(),o=s.slice();o.splice(i,0,null);let n=t;const r=[];for(let e=0;e<o.length;e++){const a=t-e*p,s=o[e];null===s?n=a:r.push(joplin.data.put(["notes",s.id],null,{order:a}))}return await Promise.all(r),void await joplin.data.put(["notes",e],null,{parent_id:a,order:n})}let m;m=null!==d&&null!==c?(d+c)/2:null!==d?d-p:null!==c?c+p:Date.now(),await joplin.data.put(["notes",e],null,{parent_id:a,order:m})}(t,a,s)}else if("folder"===o){const e=!!L[a];"into"===s?e&&t!==a&&await joplin.data.put(["folders",t],null,{parent_id:a}):!e||"above"!==s&&"below"!==s||await async function(e,t,o){const a=L[t],s=L[e];if(!a||!s||e===t)return;const n=a.parent_id||"";let i=n;for(;i;){if(i===e)return;i=L[i]&&L[i].parent_id||""}const r=U.filter(t=>(t.parent_id||"")===n&&t.id!==e).sort((e,t)=>{const o=e.order||0,a=t.order||0;return o!==a?a-o:(e.title||"").localeCompare(t.title||"")}),l=r.findIndex(e=>e.id===t),d=l<0?r.length:"above"===o?l:l+1,c=d>0?r[d-1]:null,p=d<r.length?r[d]:null,m=c?c.order||0:null,u=p?p.order||0:null,w=r.every(e=>0===(e.order||0)),g=null!==m&&null!==u&&m-u<2,f=1e6;if((s.parent_id||"")!==n&&await joplin.data.put(["folders",e],null,{parent_id:n}),w||g){const t=Date.now(),o=r.slice();o.splice(d,0,null);for(let a=0;a<o.length;a++){const s=t-a*f,n=o[a];null===n?$[e]=s:$[n.id]=s}return void await le()}let h;h=null!==m&&null!==u?(m+u)/2:null!==m?m-f:null!==u?u+f:Date.now(),$[e]=h,await le()}(t,a,s)}await pe()}catch(e){console.error("Notes In List: drag drop error",e)}}(t);else if("dragToEmpty"===t.name)try{const e=t.dragId,o=t.dragType,a=new Set((await g()).map(e=>e.id));await joplin.commands.execute("openFolderDialog",{isNew:!0,parentId:""}),(async()=>{for(let t=0;t<240;t++){await new Promise(e=>setTimeout(e,500));try{const t=(await g()).find(e=>!a.has(e.id));if(t)return W[t.id]=!0,delete A[t.id],"note"===o?await joplin.data.put(["notes",e],null,{parent_id:t.id}):"folder"===o&&e!==t.id&&await joplin.data.put(["folders",e],null,{parent_id:t.id}),te(),void await pe()}catch(e){return}}})()}catch(e){console.error("Joplin Explorer: drag to empty error",e)}});let xe="",ve=null,be="";async function Se(){const e=[];let t=1,o=!0;for(;o;){const a=await joplin.data.get(["folders"],{fields:["id","title","parent_id","icon"],page:t,limit:100});for(const t of a.items||[])e.push(t.id+""+(t.title||"")+""+(t.parent_id||"")+""+(t.icon||""));o=a.has_more,t++}const a=e.sort().join("\n"),s=""!==be&&a!==be;return be=a,s}let Ne="",_e=null,je=null;async function De(){let e=!1;try{if(M){const t=((await joplin.data.get(["notes",M,"tags"],{fields:["id"],limit:100})).items||[]).map(e=>e.id).sort().join(",");M===Ne&&null!==_e&&_e!==t&&(e=!0),Ne=M,_e=t}}catch(e){}try{let t="",o=1,a=!0;for(;a&&o<=5;){const e=await joplin.data.get(["tags"],{fields:["id","title","updated_time"],page:o,limit:100});for(const o of e.items||[])t+=o.id+(o.title||"")+o.updated_time+";";a=e.has_more,o++}null!==je&&je!==t&&(e=!0),je=t}catch(e){}return e}async function ke(){try{if(!xe){const e=await joplin.data.get(["events"]);return xe=String(e.cursor||""),await Se(),void await De()}let e=!1,t=0;for(;t++<20;){const t=await joplin.data.get(["events"],{cursor:xe});for(const o of t.items||[])2===Number(o.type)&&o.item_id===M||(e=!0);if(t.cursor&&(xe=String(t.cursor)),!t.has_more)break}!e&&await Se()&&(e=!0),!e&&await De()&&(e=!0),e&&se(300)}catch(e){ve&&(clearInterval(ve),ve=null),console.warn("Joplin Explorer: change-events polling disabled",e)}}async function Ie(){const e=!1!==await joplin.settings.value("autoRefresh");ve&&(clearInterval(ve),ve=null),e&&(xe="",await ke(),ve=setInterval(ke,5e3))}await Ie();try{{const t=e=>async t=>{const o=String(t.sourcePath||""),a=function(e){let t=e.toString("utf8");if(t.indexOf("�")>=0)try{t=new globalThis.TextDecoder("gbk").decode(e)}catch(e){}return 65279===t.charCodeAt(0)&&(t=t.slice(1)),t}(s.readFileSync(o)),i=n.basename(o).replace(/\.(csv|tsv)$/i,""),r=e?function(e,t){const o=function(e,t){const o=[];let a=[],s="",n=!1;for(let i=0;i<e.length;i++){const r=e[i];n?'"'===r?'"'===e[i+1]?(s+='"',i++):n=!1:s+=r:'"'===r?n=!0:r===t?(a.push(s),s=""):"\n"===r||"\r"===r?("\r"===r&&"\n"===e[i+1]&&i++,a.push(s),s="",o.push(a),a=[]):s+=r}for((""!==s||a.length)&&(a.push(s),o.push(a));o.length&&o[o.length-1].every(e=>""===e);)o.pop();return o}(e,t);if(!o.length)return"";const a=Math.max(...o.map(e=>e.length)),s=e=>"| "+Array.from({length:a},(t,o)=>(e[o]||"").replace(/\|/g,"\\|").replace(/\r?\n/g,"<br>").trim()).join(" | ")+" |",n=[s(o[0]),"|"+Array.from({length:a},()=>" --- |").join("")];for(let e=1;e<o.length;e++)n.push(s(o[e]));return n.join("\n")+"\n"}(a,/\.tsv$/i.test(o)?"\t":","):"```csv\n"+a.replace(/```/g,"```")+(a.endsWith("\n")?"":"\n")+"```\n",l=t.options&&t.options.destinationFolderId||"",d={title:i,body:r};l&&(d.parent_id=l),await joplin.data.post(["notes"],null,d)},o=(e,t)=>(e||t).replace(/^\s*CSV\s*→\s*/i,"");await joplin.interop.registerImportModule({format:"csv",description:o(e.impFmtCsvTable,"Markdown table"),fileExtensions:["csv","tsv"],sources:["file"],isNoteArchive:!1,onExec:t(!0)}),await joplin.interop.registerImportModule({format:"csv_code",description:o(e.impFmtCsvCode,"Code block"),fileExtensions:["csv","tsv"],sources:["file"],isNoteArchive:!1,onExec:t(!1)}),console.info("Joplin Explorer: CSV import modules registered")}}catch(e){console.warn("Joplin Explorer: CSV import module registration failed (older Joplin without joplin.interop? context-menu CSV import still works)",e)}await joplin.workspace.onNoteSelectionChange(async()=>{const e=await joplin.workspace.selectedNote();e&&e.id!==M&&(M=e.id,await joplin.views.panels.postMessage(o,{name:"selectNote",id:e.id}))}),await joplin.workspace.onNoteChange(async e=>{var t;e&&e.id?(t=e.id,ie[t]=!0,ne&&clearTimeout(ne),ne=setTimeout(async()=>{ne=null;const e=Object.keys(ie);ie={};for(const t of e)await me(t)},600)):se()});let Te=null;try{await joplin.workspace.onSyncStart(async()=>{Te&&(clearTimeout(Te),Te=null),await joplin.views.panels.postMessage(o,{name:"syncState",state:"syncing"})})}catch(e){console.warn("Joplin Explorer: onSyncStart not available",e)}try{await joplin.workspace.onSyncComplete(async t=>{const a=!(!t||!t.withErrors);await pe(),Te&&clearTimeout(Te),Te=setTimeout(()=>{Te=null,joplin.views.panels.postMessage(o,{name:"syncState",state:a?"error":"done"})},800),a&&await k(e.syncFailed,"Joplin reported errors during synchronisation. Open the Synchronisation Status screen (Tools → Synchronisation Status) to see the details.")})}catch(e){console.warn("Joplin Explorer: onSyncComplete not available",e)}await pe()}})})();
|