From 3a0fd4a35a2ce83d1d7dc23b18a004e95f5501b7 Mon Sep 17 00:00:00 2001 From: Chenyan Wang Date: Sun, 3 May 2026 18:44:44 +0800 Subject: [PATCH] build: finalize TypeScript migration and lock dependencies --- .gitignore | 1 + main.js | 2192 ++++++++++++++++++++------------------------- package-lock.json | 633 +++++++++++++ src/main.ts | 1 - 4 files changed, 1605 insertions(+), 1222 deletions(-) create mode 100644 package-lock.json diff --git a/.gitignore b/.gitignore index 2752eb9..68e8abd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules/ +.tools/ .DS_Store diff --git a/main.js b/main.js index 830d3d3..844ee00 100644 --- a/main.js +++ b/main.js @@ -1,395 +1,394 @@ -'use strict'; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); -var obsidian = require('obsidian'); - -const VIEW_TYPE = 'smart-folder-view'; -const FILTER_STORAGE_PREFIX = 'smart-folder-view-filter:'; -const FILTER_PRESET_STORAGE_PREFIX = 'smart-folder-view-presets:'; -const MANUAL_ORDER_STORAGE_PREFIX = 'smart-folder-view-manual-order:'; -const EMPTY_OPTION_VALUE = '__SFV_EMPTY__'; - -const DEFAULT_COLOR_CONFIG = { +// src/main.ts +var obsidian = __toESM(require("obsidian")); +var VIEW_TYPE = "smart-folder-view"; +var FILTER_STORAGE_PREFIX = "smart-folder-view-filter:"; +var FILTER_PRESET_STORAGE_PREFIX = "smart-folder-view-presets:"; +var MANUAL_ORDER_STORAGE_PREFIX = "smart-folder-view-manual-order:"; +var EMPTY_OPTION_VALUE = "__SFV_EMPTY__"; +var DEFAULT_COLOR_CONFIG = { saturationBase: 55, saturationRange: 20, lightnessBase: 44, lightnessRange: 14, - fallbackColor: 'var(--interactive-accent)', + fallbackColor: "var(--interactive-accent)" }; - -const I18N = { +var I18N = { zh: { - title: 'Smart Folder View', - openBuilder: '新建 Smart Folder 页面', - openManager: '管理已保存页面', - openLast: '打开上次页面', - noProfile: '还没有保存任何页面,请先新建。', - setupTitle: '配置页面', - setupDesc: '页面设置用于锚定数据来源:文件夹、模板文件、可筛选属性。设置后先预览,再保存。', - profileName: '页面名称', - sourceFolder: '来源文件夹', - sourceFolderDesc: '从该文件夹读取卡片数据', - templateFile: '模板文件', - templateFileDesc: '从全库选择模板文件读取元数据字段(并融合该文件夹共用字段)', - colorField: '颜色字段', - sortField: '排序字段', - displayFields: '展示字段', - sortOrder: '排序方向', - viewMode: '视图模式', - boardField: '看板分组字段', - preview: '预览', - saveAndOpen: '保存并打开', - choose: '(请选择)', - rootFolder: '(根文件夹)', - timeline: '时间轴', - board: '看板', - asc: '升序(旧→新)', - desc: '倒序(新→旧)', - savePage: '保存页面', - updatePage: '更新页面', - openBuilderBtn: '新建页面', - openManagerBtn: '页面管理', - startActionTitle: 'Smart Folder', - startActionDesc: '请选择操作:', - startOpenPage: '打开页面', - startNewPage: '新建页面', - pageSaved: '页面已保存', - pageUpdated: '页面已更新', - saveFailed: '页面保存失败', - pageNamePrompt: '请输入页面名称', - managerTitle: '已保存页面', - open: '打开', - edit: '编辑', - delete: '删除', - deleteConfirm: '确认删除页面:{{name}}?', - noFiles: '文件夹 "{{folder}}" 里暂无笔记。', - matched: '匹配到 {{count}} 条记录', - noMatch: '没有匹配筛选条件的笔记。', - preset: '预设', - presetScopeDesc: '预设只保存当前页面下的可变视图状态:筛选、排序、显示模式(时间轴/看板)。', - noPreset: '(不使用预设)', - savePreset: '保存预设', - delPreset: '删除预设', - presetPrompt: '预设名称', - presetNameRequired: '请输入预设名称', - presetSaveHint: '说明:名称不变=覆盖更新当前预设;名称改为新名字=另存为新预设,旧预设保持不变。', - clear: '清空', - noneSelected: '(全不选)', - allText: '(全部)', - selectAll: '全选', - selectNone: '全不选', - selected: '已选 {{count}} 项', - allWhenEmpty: '未选择,等同于全部', - search: '搜索选项...', - saveOrder: '保存当前排序', - saveOrderDone: '已保存当前顺序', - resetOrder: '恢复默认顺序', - resetOrderDone: '已恢复默认顺序', - undoLastAction: '撤销上一步', - undoDone: '已撤销上一步操作', - undoNone: '没有可撤销的操作', - boardUnset: '(未设置)', - emptyValue: '空值', - moveAsk: '跨列拖拽:是否将 {{field}} 更新为 "{{value}}"?\n确定=自动推荐,取消=手动输入', - moveManual: '请输入 {{field}} 新值(留空清除):', - moveDone: '属性已更新:{{field}} = {{value}}', - moveFail: '属性更新失败', - timelineMoveTitle: '时间轴拖动', - timelineMoveChoice: '请选择拖动后的处理方式(字段:{{field}})', - timelineMoveCancel: '取消(不保存修改)', - timelineMoveOrderOnly: '仅更新时间轴顺序', - timelineMoveOrderAndProp: '更新时间轴和属性', - timelineUpdatePrompt: '请输入 {{field}} 新值(留空清除):', - timelineMoveDone: '已更新时间轴顺序', - timelineMoveNoChange: '已取消,未保存修改', - boardMoveTitle: '看板拖动', - boardMoveChoice: '请选择拖动后的处理方式(字段:{{field}})', - boardMoveCancel: '取消(不保存修改)', - boardMoveOrderOnly: '仅更新看板顺序', - boardMoveOrderAndProp: '更新看板顺序和属性', - boardMoveDone: '已更新看板顺序', - boardMoveNoChange: '已取消,未保存修改', - savePreviewOnClose: '当前为预览配置,关闭前是否保存为页面?', - refreshPreview: '刷新预览', - refreshPreviewDone: '卡片预览已刷新', - exportPage: '导出页面', - exportFolderTitle: '选择导出文件夹', - exportFolderDesc: '请选择导出到哪个文件夹(可选根文件夹)', - exportFolderConfirm: '导出到此文件夹', - exportPageDone: '页面配置已导出:{{path}}', - exportPageFailed: '页面导出失败', - exportBlockTitle: 'Smart Folder 页面导出', - exportBlockDesc: '点击下方按钮可打开该页面配置。', - exportBlockOpen: '打开此页面', - exportBlockImport: '导入并保存到页面管理', - exportBlockInvalid: '导出块解析失败', - colorSettings: '颜色设置', - saturationBase: '饱和度基线', - saturationRange: '饱和度波动', - lightnessBase: '亮度基线', - lightnessRange: '亮度波动', - fallbackColor: '默认颜色', - resetDefault: '恢复默认', - languageMode: '界面语言', - languageAuto: '自动(跟随 Obsidian)', - languageZh: '中文', - languageEn: 'English', - languageModeDesc: '默认自动匹配;你也可以手动固定语言。', + title: "Smart Folder View", + openBuilder: "\u65B0\u5EFA Smart Folder \u9875\u9762", + openManager: "\u7BA1\u7406\u5DF2\u4FDD\u5B58\u9875\u9762", + openLast: "\u6253\u5F00\u4E0A\u6B21\u9875\u9762", + noProfile: "\u8FD8\u6CA1\u6709\u4FDD\u5B58\u4EFB\u4F55\u9875\u9762\uFF0C\u8BF7\u5148\u65B0\u5EFA\u3002", + setupTitle: "\u914D\u7F6E\u9875\u9762", + setupDesc: "\u9875\u9762\u8BBE\u7F6E\u7528\u4E8E\u951A\u5B9A\u6570\u636E\u6765\u6E90\uFF1A\u6587\u4EF6\u5939\u3001\u6A21\u677F\u6587\u4EF6\u3001\u53EF\u7B5B\u9009\u5C5E\u6027\u3002\u8BBE\u7F6E\u540E\u5148\u9884\u89C8\uFF0C\u518D\u4FDD\u5B58\u3002", + profileName: "\u9875\u9762\u540D\u79F0", + sourceFolder: "\u6765\u6E90\u6587\u4EF6\u5939", + sourceFolderDesc: "\u4ECE\u8BE5\u6587\u4EF6\u5939\u8BFB\u53D6\u5361\u7247\u6570\u636E", + templateFile: "\u6A21\u677F\u6587\u4EF6", + templateFileDesc: "\u4ECE\u5168\u5E93\u9009\u62E9\u6A21\u677F\u6587\u4EF6\u8BFB\u53D6\u5143\u6570\u636E\u5B57\u6BB5\uFF08\u5E76\u878D\u5408\u8BE5\u6587\u4EF6\u5939\u5171\u7528\u5B57\u6BB5\uFF09", + colorField: "\u989C\u8272\u5B57\u6BB5", + sortField: "\u6392\u5E8F\u5B57\u6BB5", + displayFields: "\u5C55\u793A\u5B57\u6BB5", + sortOrder: "\u6392\u5E8F\u65B9\u5411", + viewMode: "\u89C6\u56FE\u6A21\u5F0F", + boardField: "\u770B\u677F\u5206\u7EC4\u5B57\u6BB5", + preview: "\u9884\u89C8", + saveAndOpen: "\u4FDD\u5B58\u5E76\u6253\u5F00", + choose: "(\u8BF7\u9009\u62E9)", + rootFolder: "(\u6839\u6587\u4EF6\u5939)", + timeline: "\u65F6\u95F4\u8F74", + board: "\u770B\u677F", + asc: "\u5347\u5E8F\uFF08\u65E7\u2192\u65B0\uFF09", + desc: "\u5012\u5E8F\uFF08\u65B0\u2192\u65E7\uFF09", + savePage: "\u4FDD\u5B58\u9875\u9762", + updatePage: "\u66F4\u65B0\u9875\u9762", + openBuilderBtn: "\u65B0\u5EFA\u9875\u9762", + openManagerBtn: "\u9875\u9762\u7BA1\u7406", + startActionTitle: "Smart Folder", + startActionDesc: "\u8BF7\u9009\u62E9\u64CD\u4F5C\uFF1A", + startOpenPage: "\u6253\u5F00\u9875\u9762", + startNewPage: "\u65B0\u5EFA\u9875\u9762", + pageSaved: "\u9875\u9762\u5DF2\u4FDD\u5B58", + pageUpdated: "\u9875\u9762\u5DF2\u66F4\u65B0", + saveFailed: "\u9875\u9762\u4FDD\u5B58\u5931\u8D25", + pageNamePrompt: "\u8BF7\u8F93\u5165\u9875\u9762\u540D\u79F0", + managerTitle: "\u5DF2\u4FDD\u5B58\u9875\u9762", + open: "\u6253\u5F00", + edit: "\u7F16\u8F91", + delete: "\u5220\u9664", + deleteConfirm: "\u786E\u8BA4\u5220\u9664\u9875\u9762\uFF1A{{name}}\uFF1F", + noFiles: '\u6587\u4EF6\u5939 "{{folder}}" \u91CC\u6682\u65E0\u7B14\u8BB0\u3002', + matched: "\u5339\u914D\u5230 {{count}} \u6761\u8BB0\u5F55", + noMatch: "\u6CA1\u6709\u5339\u914D\u7B5B\u9009\u6761\u4EF6\u7684\u7B14\u8BB0\u3002", + preset: "\u9884\u8BBE", + presetScopeDesc: "\u9884\u8BBE\u53EA\u4FDD\u5B58\u5F53\u524D\u9875\u9762\u4E0B\u7684\u53EF\u53D8\u89C6\u56FE\u72B6\u6001\uFF1A\u7B5B\u9009\u3001\u6392\u5E8F\u3001\u663E\u793A\u6A21\u5F0F\uFF08\u65F6\u95F4\u8F74/\u770B\u677F\uFF09\u3002", + noPreset: "(\u4E0D\u4F7F\u7528\u9884\u8BBE)", + savePreset: "\u4FDD\u5B58\u9884\u8BBE", + delPreset: "\u5220\u9664\u9884\u8BBE", + presetPrompt: "\u9884\u8BBE\u540D\u79F0", + presetNameRequired: "\u8BF7\u8F93\u5165\u9884\u8BBE\u540D\u79F0", + presetSaveHint: "\u8BF4\u660E\uFF1A\u540D\u79F0\u4E0D\u53D8=\u8986\u76D6\u66F4\u65B0\u5F53\u524D\u9884\u8BBE\uFF1B\u540D\u79F0\u6539\u4E3A\u65B0\u540D\u5B57=\u53E6\u5B58\u4E3A\u65B0\u9884\u8BBE\uFF0C\u65E7\u9884\u8BBE\u4FDD\u6301\u4E0D\u53D8\u3002", + clear: "\u6E05\u7A7A", + noneSelected: "(\u5168\u4E0D\u9009)", + allText: "(\u5168\u90E8)", + selectAll: "\u5168\u9009", + selectNone: "\u5168\u4E0D\u9009", + selected: "\u5DF2\u9009 {{count}} \u9879", + allWhenEmpty: "\u672A\u9009\u62E9\uFF0C\u7B49\u540C\u4E8E\u5168\u90E8", + search: "\u641C\u7D22\u9009\u9879...", + saveOrder: "\u4FDD\u5B58\u5F53\u524D\u6392\u5E8F", + saveOrderDone: "\u5DF2\u4FDD\u5B58\u5F53\u524D\u987A\u5E8F", + resetOrder: "\u6062\u590D\u9ED8\u8BA4\u987A\u5E8F", + resetOrderDone: "\u5DF2\u6062\u590D\u9ED8\u8BA4\u987A\u5E8F", + undoLastAction: "\u64A4\u9500\u4E0A\u4E00\u6B65", + undoDone: "\u5DF2\u64A4\u9500\u4E0A\u4E00\u6B65\u64CD\u4F5C", + undoNone: "\u6CA1\u6709\u53EF\u64A4\u9500\u7684\u64CD\u4F5C", + boardUnset: "(\u672A\u8BBE\u7F6E)", + emptyValue: "\u7A7A\u503C", + moveAsk: '\u8DE8\u5217\u62D6\u62FD\uFF1A\u662F\u5426\u5C06 {{field}} \u66F4\u65B0\u4E3A "{{value}}"\uFF1F\n\u786E\u5B9A=\u81EA\u52A8\u63A8\u8350\uFF0C\u53D6\u6D88=\u624B\u52A8\u8F93\u5165', + moveManual: "\u8BF7\u8F93\u5165 {{field}} \u65B0\u503C\uFF08\u7559\u7A7A\u6E05\u9664\uFF09\uFF1A", + moveDone: "\u5C5E\u6027\u5DF2\u66F4\u65B0\uFF1A{{field}} = {{value}}", + moveFail: "\u5C5E\u6027\u66F4\u65B0\u5931\u8D25", + timelineMoveTitle: "\u65F6\u95F4\u8F74\u62D6\u52A8", + timelineMoveChoice: "\u8BF7\u9009\u62E9\u62D6\u52A8\u540E\u7684\u5904\u7406\u65B9\u5F0F\uFF08\u5B57\u6BB5\uFF1A{{field}}\uFF09", + timelineMoveCancel: "\u53D6\u6D88\uFF08\u4E0D\u4FDD\u5B58\u4FEE\u6539\uFF09", + timelineMoveOrderOnly: "\u4EC5\u66F4\u65B0\u65F6\u95F4\u8F74\u987A\u5E8F", + timelineMoveOrderAndProp: "\u66F4\u65B0\u65F6\u95F4\u8F74\u548C\u5C5E\u6027", + timelineUpdatePrompt: "\u8BF7\u8F93\u5165 {{field}} \u65B0\u503C\uFF08\u7559\u7A7A\u6E05\u9664\uFF09\uFF1A", + timelineMoveDone: "\u5DF2\u66F4\u65B0\u65F6\u95F4\u8F74\u987A\u5E8F", + timelineMoveNoChange: "\u5DF2\u53D6\u6D88\uFF0C\u672A\u4FDD\u5B58\u4FEE\u6539", + boardMoveTitle: "\u770B\u677F\u62D6\u52A8", + boardMoveChoice: "\u8BF7\u9009\u62E9\u62D6\u52A8\u540E\u7684\u5904\u7406\u65B9\u5F0F\uFF08\u5B57\u6BB5\uFF1A{{field}}\uFF09", + boardMoveCancel: "\u53D6\u6D88\uFF08\u4E0D\u4FDD\u5B58\u4FEE\u6539\uFF09", + boardMoveOrderOnly: "\u4EC5\u66F4\u65B0\u770B\u677F\u987A\u5E8F", + boardMoveOrderAndProp: "\u66F4\u65B0\u770B\u677F\u987A\u5E8F\u548C\u5C5E\u6027", + boardMoveDone: "\u5DF2\u66F4\u65B0\u770B\u677F\u987A\u5E8F", + boardMoveNoChange: "\u5DF2\u53D6\u6D88\uFF0C\u672A\u4FDD\u5B58\u4FEE\u6539", + savePreviewOnClose: "\u5F53\u524D\u4E3A\u9884\u89C8\u914D\u7F6E\uFF0C\u5173\u95ED\u524D\u662F\u5426\u4FDD\u5B58\u4E3A\u9875\u9762\uFF1F", + refreshPreview: "\u5237\u65B0\u9884\u89C8", + refreshPreviewDone: "\u5361\u7247\u9884\u89C8\u5DF2\u5237\u65B0", + exportPage: "\u5BFC\u51FA\u9875\u9762", + exportFolderTitle: "\u9009\u62E9\u5BFC\u51FA\u6587\u4EF6\u5939", + exportFolderDesc: "\u8BF7\u9009\u62E9\u5BFC\u51FA\u5230\u54EA\u4E2A\u6587\u4EF6\u5939\uFF08\u53EF\u9009\u6839\u6587\u4EF6\u5939\uFF09", + exportFolderConfirm: "\u5BFC\u51FA\u5230\u6B64\u6587\u4EF6\u5939", + exportPageDone: "\u9875\u9762\u914D\u7F6E\u5DF2\u5BFC\u51FA\uFF1A{{path}}", + exportPageFailed: "\u9875\u9762\u5BFC\u51FA\u5931\u8D25", + exportBlockTitle: "Smart Folder \u9875\u9762\u5BFC\u51FA", + exportBlockDesc: "\u70B9\u51FB\u4E0B\u65B9\u6309\u94AE\u53EF\u6253\u5F00\u8BE5\u9875\u9762\u914D\u7F6E\u3002", + exportBlockOpen: "\u6253\u5F00\u6B64\u9875\u9762", + exportBlockImport: "\u5BFC\u5165\u5E76\u4FDD\u5B58\u5230\u9875\u9762\u7BA1\u7406", + exportBlockInvalid: "\u5BFC\u51FA\u5757\u89E3\u6790\u5931\u8D25", + colorSettings: "\u989C\u8272\u8BBE\u7F6E", + saturationBase: "\u9971\u548C\u5EA6\u57FA\u7EBF", + saturationRange: "\u9971\u548C\u5EA6\u6CE2\u52A8", + lightnessBase: "\u4EAE\u5EA6\u57FA\u7EBF", + lightnessRange: "\u4EAE\u5EA6\u6CE2\u52A8", + fallbackColor: "\u9ED8\u8BA4\u989C\u8272", + resetDefault: "\u6062\u590D\u9ED8\u8BA4", + languageMode: "\u754C\u9762\u8BED\u8A00", + languageAuto: "\u81EA\u52A8\uFF08\u8DDF\u968F Obsidian\uFF09", + languageZh: "\u4E2D\u6587", + languageEn: "English", + languageModeDesc: "\u9ED8\u8BA4\u81EA\u52A8\u5339\u914D\uFF1B\u4F60\u4E5F\u53EF\u4EE5\u624B\u52A8\u56FA\u5B9A\u8BED\u8A00\u3002" }, en: { - title: 'Smart Folder View', - openBuilder: 'Create Smart Folder Page', - openManager: 'Manage Saved Pages', - openLast: 'Open Last Page', - noProfile: 'No saved pages yet. Please create one first.', - setupTitle: 'Configure Page', - setupDesc: 'Page settings anchor data source: folder, template file, and filterable fields. Preview first, then save.', - profileName: 'Page name', - sourceFolder: 'Source folder', - sourceFolderDesc: 'Load cards from this folder', - templateFile: 'Template file', - templateFileDesc: 'Choose a template file from the whole vault; fields come from it plus common folder fields', - colorField: 'Color field', - sortField: 'Sort field', - displayFields: 'Display fields', - sortOrder: 'Sort order', - viewMode: 'View mode', - boardField: 'Board group field', - preview: 'Preview', - saveAndOpen: 'Save and Open', - choose: '(Choose)', - rootFolder: '(Vault Root)', - timeline: 'Timeline', - board: 'Board', - asc: 'Ascending (old -> new)', - desc: 'Descending (new -> old)', - savePage: 'Save Page', - updatePage: 'Update Page', - openBuilderBtn: 'Create Page', - openManagerBtn: 'Page Manager', - startActionTitle: 'Smart Folder', - startActionDesc: 'Choose an action:', - startOpenPage: 'Open Page', - startNewPage: 'Create Page', - pageSaved: 'Page saved', - pageUpdated: 'Page updated', - saveFailed: 'Failed to save page', - pageNamePrompt: 'Enter page name', - managerTitle: 'Saved Pages', - open: 'Open', - edit: 'Edit', - delete: 'Delete', - deleteConfirm: 'Delete page: {{name}}?', + title: "Smart Folder View", + openBuilder: "Create Smart Folder Page", + openManager: "Manage Saved Pages", + openLast: "Open Last Page", + noProfile: "No saved pages yet. Please create one first.", + setupTitle: "Configure Page", + setupDesc: "Page settings anchor data source: folder, template file, and filterable fields. Preview first, then save.", + profileName: "Page name", + sourceFolder: "Source folder", + sourceFolderDesc: "Load cards from this folder", + templateFile: "Template file", + templateFileDesc: "Choose a template file from the whole vault; fields come from it plus common folder fields", + colorField: "Color field", + sortField: "Sort field", + displayFields: "Display fields", + sortOrder: "Sort order", + viewMode: "View mode", + boardField: "Board group field", + preview: "Preview", + saveAndOpen: "Save and Open", + choose: "(Choose)", + rootFolder: "(Vault Root)", + timeline: "Timeline", + board: "Board", + asc: "Ascending (old -> new)", + desc: "Descending (new -> old)", + savePage: "Save Page", + updatePage: "Update Page", + openBuilderBtn: "Create Page", + openManagerBtn: "Page Manager", + startActionTitle: "Smart Folder", + startActionDesc: "Choose an action:", + startOpenPage: "Open Page", + startNewPage: "Create Page", + pageSaved: "Page saved", + pageUpdated: "Page updated", + saveFailed: "Failed to save page", + pageNamePrompt: "Enter page name", + managerTitle: "Saved Pages", + open: "Open", + edit: "Edit", + delete: "Delete", + deleteConfirm: "Delete page: {{name}}?", noFiles: 'No notes found in "{{folder}}".', - matched: '{{count}} notes matched', - noMatch: 'No notes match current filters.', - preset: 'Preset', - presetScopeDesc: 'Presets only save mutable view state in this page: filters, sort, and view mode (timeline/board).', - noPreset: '(No preset)', - savePreset: 'Save preset', - delPreset: 'Delete preset', - presetPrompt: 'Preset name', - presetNameRequired: 'Please enter a preset name', - presetSaveHint: 'Rule: same name = update this preset; new name = save as a new preset and keep the old one unchanged.', - clear: 'Clear', - noneSelected: '(None selected)', - allText: '(All)', - selectAll: 'Select all', - selectNone: 'Select none', - selected: '{{count}} selected', - allWhenEmpty: 'Nothing selected, equivalent to all', - search: 'Search options...', - saveOrder: 'Save current order', - saveOrderDone: 'Current order saved', - resetOrder: 'Reset default order', - resetOrderDone: 'Default order restored', - undoLastAction: 'Undo last action', - undoDone: 'Last action undone', - undoNone: 'Nothing to undo', - boardUnset: '(Unset)', - emptyValue: 'empty', + matched: "{{count}} notes matched", + noMatch: "No notes match current filters.", + preset: "Preset", + presetScopeDesc: "Presets only save mutable view state in this page: filters, sort, and view mode (timeline/board).", + noPreset: "(No preset)", + savePreset: "Save preset", + delPreset: "Delete preset", + presetPrompt: "Preset name", + presetNameRequired: "Please enter a preset name", + presetSaveHint: "Rule: same name = update this preset; new name = save as a new preset and keep the old one unchanged.", + clear: "Clear", + noneSelected: "(None selected)", + allText: "(All)", + selectAll: "Select all", + selectNone: "Select none", + selected: "{{count}} selected", + allWhenEmpty: "Nothing selected, equivalent to all", + search: "Search options...", + saveOrder: "Save current order", + saveOrderDone: "Current order saved", + resetOrder: "Reset default order", + resetOrderDone: "Default order restored", + undoLastAction: "Undo last action", + undoDone: "Last action undone", + undoNone: "Nothing to undo", + boardUnset: "(Unset)", + emptyValue: "empty", moveAsk: 'Cross-column move: update {{field}} to "{{value}}"?\nOK=recommended, Cancel=manual input', - moveManual: 'Enter new value for {{field}} (empty to clear):', - moveDone: 'Updated: {{field}} = {{value}}', - moveFail: 'Failed to update property', - timelineMoveTitle: 'Timeline move', - timelineMoveChoice: 'Choose how to apply this move (field: {{field}})', - timelineMoveCancel: 'Cancel (do not save)', - timelineMoveOrderOnly: 'Update timeline order only', - timelineMoveOrderAndProp: 'Update timeline order and property', - timelineUpdatePrompt: 'Enter new value for {{field}} (empty to clear):', - timelineMoveDone: 'Timeline order updated', - timelineMoveNoChange: 'Canceled, no changes saved', - boardMoveTitle: 'Board move', - boardMoveChoice: 'Choose how to apply this move (field: {{field}})', - boardMoveCancel: 'Cancel (do not save)', - boardMoveOrderOnly: 'Update board order only', - boardMoveOrderAndProp: 'Update board order and property', - boardMoveDone: 'Board order updated', - boardMoveNoChange: 'Canceled, no changes saved', - savePreviewOnClose: 'This is a preview configuration. Save it before closing?', - refreshPreview: 'Refresh preview', - refreshPreviewDone: 'Card preview refreshed', - exportPage: 'Export page', - exportFolderTitle: 'Choose export folder', - exportFolderDesc: 'Select where to export (including vault root).', - exportFolderConfirm: 'Export here', - exportPageDone: 'Page config exported: {{path}}', - exportPageFailed: 'Failed to export page', - exportBlockTitle: 'Smart Folder page export', - exportBlockDesc: 'Click the button below to open this page configuration.', - exportBlockOpen: 'Open this page', - exportBlockImport: 'Import and save to page manager', - exportBlockInvalid: 'Failed to parse export block', - colorSettings: 'Color settings', - saturationBase: 'Saturation base', - saturationRange: 'Saturation range', - lightnessBase: 'Lightness base', - lightnessRange: 'Lightness range', - fallbackColor: 'Fallback color', - resetDefault: 'Reset default', - languageMode: 'UI language', - languageAuto: 'Auto (follow Obsidian)', - languageZh: 'Chinese', - languageEn: 'English', - languageModeDesc: 'Auto by default; you can also force a fixed language.', - }, + moveManual: "Enter new value for {{field}} (empty to clear):", + moveDone: "Updated: {{field}} = {{value}}", + moveFail: "Failed to update property", + timelineMoveTitle: "Timeline move", + timelineMoveChoice: "Choose how to apply this move (field: {{field}})", + timelineMoveCancel: "Cancel (do not save)", + timelineMoveOrderOnly: "Update timeline order only", + timelineMoveOrderAndProp: "Update timeline order and property", + timelineUpdatePrompt: "Enter new value for {{field}} (empty to clear):", + timelineMoveDone: "Timeline order updated", + timelineMoveNoChange: "Canceled, no changes saved", + boardMoveTitle: "Board move", + boardMoveChoice: "Choose how to apply this move (field: {{field}})", + boardMoveCancel: "Cancel (do not save)", + boardMoveOrderOnly: "Update board order only", + boardMoveOrderAndProp: "Update board order and property", + boardMoveDone: "Board order updated", + boardMoveNoChange: "Canceled, no changes saved", + savePreviewOnClose: "This is a preview configuration. Save it before closing?", + refreshPreview: "Refresh preview", + refreshPreviewDone: "Card preview refreshed", + exportPage: "Export page", + exportFolderTitle: "Choose export folder", + exportFolderDesc: "Select where to export (including vault root).", + exportFolderConfirm: "Export here", + exportPageDone: "Page config exported: {{path}}", + exportPageFailed: "Failed to export page", + exportBlockTitle: "Smart Folder page export", + exportBlockDesc: "Click the button below to open this page configuration.", + exportBlockOpen: "Open this page", + exportBlockImport: "Import and save to page manager", + exportBlockInvalid: "Failed to parse export block", + colorSettings: "Color settings", + saturationBase: "Saturation base", + saturationRange: "Saturation range", + lightnessBase: "Lightness base", + lightnessRange: "Lightness range", + fallbackColor: "Fallback color", + resetDefault: "Reset default", + languageMode: "UI language", + languageAuto: "Auto (follow Obsidian)", + languageZh: "Chinese", + languageEn: "English", + languageModeDesc: "Auto by default; you can also force a fixed language." + } }; - function tpl(text, vars) { if (!vars) return text; - return text.replace(/\{\{(\w+)\}\}/g, (_m, key) => String(vars[key] ?? '')); + return text.replace(/\{\{(\w+)\}\}/g, (_m, key) => String(vars[key] ?? "")); } - function detectLanguage() { try { - const locale = window.moment?.locale?.() || ''; - if (locale.toLowerCase().startsWith('zh')) return 'zh'; - } catch {} - return 'en'; + const locale = window.moment?.locale?.() || ""; + if (locale.toLowerCase().startsWith("zh")) return "zh"; + } catch { + } + return "en"; } - function newId() { return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; } - function hashCode(str) { let h = 0; for (let i = 0; i < str.length; i++) { - h = ((h << 5) - h) + str.charCodeAt(i); + h = (h << 5) - h + str.charCodeAt(i); h |= 0; } return Math.abs(h); } - function clamp(value, min, max) { return Math.max(min, Math.min(max, value)); } - function colorForValue(value, config) { if (!value) return null; const hash = hashCode(value); const h = hash % 360; - const s = clamp(config.saturationBase + (hash % Math.max(1, config.saturationRange)), 20, 90); - const l = clamp(config.lightnessBase + (hash % Math.max(1, config.lightnessRange)), 20, 80); + const s = clamp(config.saturationBase + hash % Math.max(1, config.saturationRange), 20, 90); + const l = clamp(config.lightnessBase + hash % Math.max(1, config.lightnessRange), 20, 80); return `hsl(${h} ${s}% ${l}%)`; } - function uniqueSorted(items) { - return [...new Set(items)].sort((a, b) => a.localeCompare(b, 'zh-Hans-CN')); + return [...new Set(items)].sort((a, b) => a.localeCompare(b, "zh-Hans-CN")); } - function toFlatArray(value) { if (value == null) return []; - if (Array.isArray(value)) return value.map(v => String(v).trim()).filter(Boolean); + if (Array.isArray(value)) return value.map((v) => String(v).trim()).filter(Boolean); return [String(value).trim()].filter(Boolean); } - function getFrontmatterValues(cache, key) { if (!cache?.frontmatter) return []; return toFlatArray(cache.frontmatter[key]); } - function getAllFrontmatterKeys(caches) { - const keys = new Set(); + const keys = /* @__PURE__ */ new Set(); for (const cache of caches) { if (cache?.frontmatter) { for (const key of Object.keys(cache.frontmatter)) { - if (key !== 'position') keys.add(key); + if (key !== "position") keys.add(key); } } } return uniqueSorted([...keys]); } - function stripFrontmatter(text) { - return text.replace(/^---\n[\s\S]*?\n---\n?/, '').trim(); + return text.replace(/^---\n[\s\S]*?\n---\n?/, "").trim(); } - function parseDateLike(value) { - const raw = String(value || '').trim(); + const raw = String(value || "").trim(); if (!raw) return null; const asTimestamp = Date.parse(raw); if (Number.isFinite(asTimestamp)) return asTimestamp; - const normalized = raw.replace(/[年./]/g, '-').replace(/月/g, '-').replace(/日/g, '').replace(/\s+/g, ' ').trim(); + const normalized = raw.replace(/[年./]/g, "-").replace(/月/g, "-").replace(/日/g, "").replace(/\s+/g, " ").trim(); const ymd = normalized.match(/(\d{4})-(\d{1,2})(?:-(\d{1,2}))?/); if (ymd) return Date.UTC(Number(ymd[1]), Math.max(1, Number(ymd[2])) - 1, Math.max(1, Number(ymd[3] || 1))); const year = normalized.match(/\b(\d{3,4})\b/); if (year) return Date.UTC(Number(year[1]), 0, 1); return null; } - function parseSortValue(value) { - const raw = String(value == null ? '' : value).trim(); + const raw = String(value == null ? "" : value).trim(); const ts = parseDateLike(raw); - if (ts != null) return { kind: 'number', value: ts, raw }; - const numericText = raw.replace(/,/g, ''); - if (/^-?\d+(\.\d+)?$/.test(numericText)) return { kind: 'number', value: Number(numericText), raw }; - return { kind: 'string', value: raw, raw }; + if (ts != null) return { kind: "number", value: ts, raw }; + const numericText = raw.replace(/,/g, ""); + if (/^-?\d+(\.\d+)?$/.test(numericText)) return { kind: "number", value: Number(numericText), raw }; + return { kind: "string", value: raw, raw }; } - function clonePreset(preset) { return { - name: String(preset?.name || ''), + name: String(preset?.name || ""), enabledFilters: Array.isArray(preset?.enabledFilters) ? [...preset.enabledFilters] : [], selectedByAttr: JSON.parse(JSON.stringify(preset?.selectedByAttr || {})), - sortOrder: preset?.sortOrder === 'asc' ? 'asc' : 'desc', - viewMode: preset?.viewMode === 'board' ? 'board' : 'timeline', + sortOrder: preset?.sortOrder === "asc" ? "asc" : "desc", + viewMode: preset?.viewMode === "board" ? "board" : "timeline" }; } - function clonePresetList(presets) { return (Array.isArray(presets) ? presets : []).map(clonePreset); } - function cloneSelectedByAttr(state) { return JSON.parse(JSON.stringify(state || {})); } - function makeSelectSearchable(selectEl, placeholder) { const host = selectEl?.parentElement; - if (!host || selectEl.dataset.searchable === '1') return; - selectEl.dataset.searchable = '1'; - - selectEl.style.display = 'none'; - const box = host.createEl('details'); - box.style.cssText = 'position:relative;display:block;z-index:1;'; - const summary = box.createEl('summary'); - summary.style.cssText = 'list-style:none;cursor:pointer;min-height:30px;padding:5px 10px;border-radius:8px;border:1px solid var(--background-modifier-border);'; - const summaryText = summary.createEl('span'); - - const panel = box.createEl('div'); - panel.style.cssText = 'position:absolute;top:calc(100% + 4px);left:0;right:0;z-index:60;max-height:220px;overflow:auto;padding:8px;border-radius:10px;border:1px solid var(--background-modifier-border);background:var(--background-primary);'; - - const input = panel.createEl('input'); - input.type = 'search'; + if (!host || selectEl.dataset.searchable === "1") return; + selectEl.dataset.searchable = "1"; + selectEl.style.display = "none"; + const box = host.createEl("details"); + box.style.cssText = "position:relative;display:block;z-index:1;"; + const summary = box.createEl("summary"); + summary.style.cssText = "list-style:none;cursor:pointer;min-height:30px;padding:5px 10px;border-radius:8px;border:1px solid var(--background-modifier-border);"; + const summaryText = summary.createEl("span"); + const panel = box.createEl("div"); + panel.style.cssText = "position:absolute;top:calc(100% + 4px);left:0;right:0;z-index:60;max-height:220px;overflow:auto;padding:8px;border-radius:10px;border:1px solid var(--background-modifier-border);background:var(--background-primary);"; + const input = panel.createEl("input"); + input.type = "search"; input.placeholder = placeholder; - input.style.cssText = 'width:100%;height:28px;padding:2px 8px;border-radius:8px;border:1px solid var(--background-modifier-border);margin-bottom:6px;'; - - const list = panel.createEl('div'); + input.style.cssText = "width:100%;height:28px;padding:2px 8px;border-radius:8px;border:1px solid var(--background-modifier-border);margin-bottom:6px;"; + const list = panel.createEl("div"); const rows = []; - const optionTexts = Array.from(selectEl.options).map(opt => opt.textContent || ''); - const meter = document.createElement('span'); - meter.style.cssText = 'position:fixed;visibility:hidden;white-space:nowrap;font:var(--font-ui-medium);'; + const optionTexts = Array.from(selectEl.options).map((opt) => opt.textContent || ""); + const meter = document.createElement("span"); + meter.style.cssText = "position:fixed;visibility:hidden;white-space:nowrap;font:var(--font-ui-medium);"; document.body.appendChild(meter); let maxTextWidth = 0; for (const text of optionTexts) { @@ -399,220 +398,188 @@ function makeSelectSearchable(selectEl, placeholder) { meter.remove(); const fixedWidth = Math.max(170, Math.ceil(maxTextWidth) + 44); const clampedWidth = Math.min(fixedWidth, 340); - box.style.width = '100%'; + box.style.width = "100%"; box.style.maxWidth = `${clampedWidth}px`; panel.style.minWidth = `${Math.min(clampedWidth, 260)}px`; panel.style.maxWidth = `${clampedWidth}px`; - for (const opt of Array.from(selectEl.options)) { - const row = list.createEl('button', { text: opt.textContent || '' }); - row.type = 'button'; - row.style.cssText = 'all:unset;display:block;width:100%;box-sizing:border-box;text-align:left;padding:5px 8px;cursor:pointer;border-radius:6px;'; - row.addEventListener('mouseenter', () => row.style.background = 'var(--background-modifier-hover)'); - row.addEventListener('mouseleave', () => row.style.background = 'transparent'); - row.addEventListener('click', () => { + const row = list.createEl("button", { text: opt.textContent || "" }); + row.type = "button"; + row.style.cssText = "all:unset;display:block;width:100%;box-sizing:border-box;text-align:left;padding:5px 8px;cursor:pointer;border-radius:6px;"; + row.addEventListener("mouseenter", () => row.style.background = "var(--background-modifier-hover)"); + row.addEventListener("mouseleave", () => row.style.background = "transparent"); + row.addEventListener("click", () => { selectEl.value = opt.value; - selectEl.dispatchEvent(new Event('change')); - box.removeAttribute('open'); + selectEl.dispatchEvent(new Event("change")); + box.removeAttribute("open"); }); - rows.push({ text: (opt.textContent || '').toLowerCase(), row }); + rows.push({ text: (opt.textContent || "").toLowerCase(), row }); } - const refreshSummary = () => { const selected = selectEl.selectedOptions[0]; - summaryText.setText(selected?.textContent || ''); - summaryText.style.cssText = 'display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;'; + summaryText.setText(selected?.textContent || ""); + summaryText.style.cssText = "display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;"; }; - - input.addEventListener('input', () => { + input.addEventListener("input", () => { const q = input.value.trim().toLowerCase(); for (const { text, row } of rows) { - row.style.display = !q || text.includes(q) ? 'block' : 'none'; + row.style.display = !q || text.includes(q) ? "block" : "none"; } }); - - box.addEventListener('toggle', () => { + box.addEventListener("toggle", () => { if (box.open) { - box.style.zIndex = '160'; - input.value = ''; - for (const { row } of rows) row.style.display = 'block'; + box.style.zIndex = "160"; + input.value = ""; + for (const { row } of rows) row.style.display = "block"; window.setTimeout(() => input.focus(), 0); } else { - box.style.zIndex = '1'; + box.style.zIndex = "1"; } }); - - selectEl.addEventListener('change', refreshSummary); + selectEl.addEventListener("change", refreshSummary); refreshSummary(); } - -class SetupModal extends obsidian.Modal { +var SetupModal = class extends obsidian.Modal { constructor(app, plugin, profile) { super(app); this.plugin = plugin; this.hasPreviewedDraft = false; this.skipCloseSavePrompt = false; const folders = this.plugin.getFolderOptions(); - const sourceFolder = profile?.sourceFolder || folders[0] || ''; - const template = profile?.templateFile || this.plugin.getTemplateFileOptions()[0] || ''; + const sourceFolder = profile?.sourceFolder || folders[0] || ""; + const template = profile?.templateFile || this.plugin.getTemplateFileOptions()[0] || ""; this.draft = profile ? { ...profile, displayFields: [...profile.displayFields] } : { id: newId(), - name: '', + name: "", sourceFolder, templateFile: template, - colorField: '', + colorField: "", displayFields: [], - sortField: '', - sortOrder: 'desc', - viewMode: 'timeline', - boardField: '状态', + sortField: "", + sortOrder: "desc", + viewMode: "timeline", + boardField: "\u72B6\u6001", createdAt: Date.now(), - updatedAt: Date.now(), + updatedAt: Date.now() }; } - render() { const { contentEl } = this; contentEl.empty(); - contentEl.createEl('h2', { text: this.plugin.t('setupTitle') }); - contentEl.createEl('p', { text: this.plugin.t('setupDesc') }); - + contentEl.createEl("h2", { text: this.plugin.t("setupTitle") }); + contentEl.createEl("p", { text: this.plugin.t("setupDesc") }); const folders = this.plugin.getFolderOptions(); - if (!folders.includes(this.draft.sourceFolder)) this.draft.sourceFolder = folders[0] || ''; - + if (!folders.includes(this.draft.sourceFolder)) this.draft.sourceFolder = folders[0] || ""; const templates = this.plugin.getTemplateFileOptions(); - if (!templates.includes(this.draft.templateFile)) this.draft.templateFile = templates[0] || ''; - + if (!templates.includes(this.draft.templateFile)) this.draft.templateFile = templates[0] || ""; const fieldOptions = this.plugin.getFieldCandidates(this.draft.sourceFolder, this.draft.templateFile); - if (!fieldOptions.includes(this.draft.colorField)) this.draft.colorField = fieldOptions[0] || ''; - if (!fieldOptions.includes(this.draft.sortField)) this.draft.sortField = fieldOptions[0] || ''; + if (!fieldOptions.includes(this.draft.colorField)) this.draft.colorField = fieldOptions[0] || ""; + if (!fieldOptions.includes(this.draft.sortField)) this.draft.sortField = fieldOptions[0] || ""; if (!fieldOptions.includes(this.draft.boardField)) { - this.draft.boardField = fieldOptions.includes('状态') ? '状态' : (fieldOptions[0] || ''); + this.draft.boardField = fieldOptions.includes("\u72B6\u6001") ? "\u72B6\u6001" : fieldOptions[0] || ""; } - this.draft.displayFields = this.draft.displayFields.filter(k => fieldOptions.includes(k)); + this.draft.displayFields = this.draft.displayFields.filter((k) => fieldOptions.includes(k)); if (!this.draft.displayFields.length) this.draft.displayFields = fieldOptions.slice(0, 4); - - new obsidian.Setting(contentEl) - .setName(this.plugin.t('profileName')) - .addText(t => t.setValue(this.draft.name).onChange(v => { this.draft.name = v.trim(); })); - - new obsidian.Setting(contentEl) - .setName(this.plugin.t('sourceFolder')) - .setDesc(this.plugin.t('sourceFolderDesc')) - .addDropdown(d => { - for (const f of folders) d.addOption(f, f || this.plugin.t('rootFolder')); - d.setValue(this.draft.sourceFolder || ''); - makeSelectSearchable(d.selectEl, this.plugin.t('search')); - d.onChange(v => { - this.draft.sourceFolder = v; - this.render(); - }); + new obsidian.Setting(contentEl).setName(this.plugin.t("profileName")).addText((t) => t.setValue(this.draft.name).onChange((v) => { + this.draft.name = v.trim(); + })); + new obsidian.Setting(contentEl).setName(this.plugin.t("sourceFolder")).setDesc(this.plugin.t("sourceFolderDesc")).addDropdown((d) => { + for (const f of folders) d.addOption(f, f || this.plugin.t("rootFolder")); + d.setValue(this.draft.sourceFolder || ""); + makeSelectSearchable(d.selectEl, this.plugin.t("search")); + d.onChange((v) => { + this.draft.sourceFolder = v; + this.render(); }); - - new obsidian.Setting(contentEl) - .setName(this.plugin.t('templateFile')) - .setDesc(this.plugin.t('templateFileDesc')) - .addDropdown(d => { - d.addOption('', this.plugin.t('choose')); - for (const f of templates) d.addOption(f, f); - d.setValue(this.draft.templateFile || ''); - makeSelectSearchable(d.selectEl, this.plugin.t('search')); - d.onChange(v => { - this.draft.templateFile = v; - this.render(); - }); + }); + new obsidian.Setting(contentEl).setName(this.plugin.t("templateFile")).setDesc(this.plugin.t("templateFileDesc")).addDropdown((d) => { + d.addOption("", this.plugin.t("choose")); + for (const f of templates) d.addOption(f, f); + d.setValue(this.draft.templateFile || ""); + makeSelectSearchable(d.selectEl, this.plugin.t("search")); + d.onChange((v) => { + this.draft.templateFile = v; + this.render(); }); - - new obsidian.Setting(contentEl) - .setName(this.plugin.t('colorField')) - .addDropdown(d => { - d.addOption('', this.plugin.t('choose')); - for (const k of fieldOptions) d.addOption(k, k); - d.setValue(this.draft.colorField || ''); - makeSelectSearchable(d.selectEl, this.plugin.t('search')); - d.onChange(v => { this.draft.colorField = v; }); + }); + new obsidian.Setting(contentEl).setName(this.plugin.t("colorField")).addDropdown((d) => { + d.addOption("", this.plugin.t("choose")); + for (const k of fieldOptions) d.addOption(k, k); + d.setValue(this.draft.colorField || ""); + makeSelectSearchable(d.selectEl, this.plugin.t("search")); + d.onChange((v) => { + this.draft.colorField = v; }); - - new obsidian.Setting(contentEl) - .setName(this.plugin.t('sortField')) - .addDropdown(d => { - d.addOption('', this.plugin.t('choose')); - for (const k of fieldOptions) d.addOption(k, k); - d.setValue(this.draft.sortField || ''); - makeSelectSearchable(d.selectEl, this.plugin.t('search')); - d.onChange(v => { this.draft.sortField = v; }); + }); + new obsidian.Setting(contentEl).setName(this.plugin.t("sortField")).addDropdown((d) => { + d.addOption("", this.plugin.t("choose")); + for (const k of fieldOptions) d.addOption(k, k); + d.setValue(this.draft.sortField || ""); + makeSelectSearchable(d.selectEl, this.plugin.t("search")); + d.onChange((v) => { + this.draft.sortField = v; }); - - new obsidian.Setting(contentEl) - .setName(this.plugin.t('displayFields')) - .addText(t => t.setValue(this.draft.displayFields.join(', ')).onChange(v => { - this.draft.displayFields = v.split(',').map(s => s.trim()).filter(Boolean); - })); - - new obsidian.Setting(contentEl) - .setName(this.plugin.t('sortOrder')) - .addDropdown(d => { - d.addOption('desc', this.plugin.t('desc')); - d.addOption('asc', this.plugin.t('asc')); - d.setValue(this.draft.sortOrder); - makeSelectSearchable(d.selectEl, this.plugin.t('search')); - d.onChange(v => { this.draft.sortOrder = v; }); + }); + new obsidian.Setting(contentEl).setName(this.plugin.t("displayFields")).addText((t) => t.setValue(this.draft.displayFields.join(", ")).onChange((v) => { + this.draft.displayFields = v.split(",").map((s) => s.trim()).filter(Boolean); + })); + new obsidian.Setting(contentEl).setName(this.plugin.t("sortOrder")).addDropdown((d) => { + d.addOption("desc", this.plugin.t("desc")); + d.addOption("asc", this.plugin.t("asc")); + d.setValue(this.draft.sortOrder); + makeSelectSearchable(d.selectEl, this.plugin.t("search")); + d.onChange((v) => { + this.draft.sortOrder = v; }); - - new obsidian.Setting(contentEl) - .setName(this.plugin.t('viewMode')) - .addDropdown(d => { - d.addOption('timeline', this.plugin.t('timeline')); - d.addOption('board', this.plugin.t('board')); - d.setValue(this.draft.viewMode); - makeSelectSearchable(d.selectEl, this.plugin.t('search')); - d.onChange(v => { - this.draft.viewMode = v === 'board' ? 'board' : 'timeline'; - this.render(); - }); + }); + new obsidian.Setting(contentEl).setName(this.plugin.t("viewMode")).addDropdown((d) => { + d.addOption("timeline", this.plugin.t("timeline")); + d.addOption("board", this.plugin.t("board")); + d.setValue(this.draft.viewMode); + makeSelectSearchable(d.selectEl, this.plugin.t("search")); + d.onChange((v) => { + this.draft.viewMode = v === "board" ? "board" : "timeline"; + this.render(); }); - - new obsidian.Setting(contentEl) - .setName(this.plugin.t('boardField')) - .addDropdown(d => { - d.addOption('', this.plugin.t('choose')); - for (const k of fieldOptions) d.addOption(k, k); - d.setValue(this.draft.boardField || ''); - makeSelectSearchable(d.selectEl, this.plugin.t('search')); - d.onChange(v => { this.draft.boardField = v; }); + }); + new obsidian.Setting(contentEl).setName(this.plugin.t("boardField")).addDropdown((d) => { + d.addOption("", this.plugin.t("choose")); + for (const k of fieldOptions) d.addOption(k, k); + d.setValue(this.draft.boardField || ""); + makeSelectSearchable(d.selectEl, this.plugin.t("search")); + d.onChange((v) => { + this.draft.boardField = v; }); - - const action = contentEl.createEl('div'); - action.style.cssText = 'display:flex;justify-content:flex-end;gap:8px;margin-top:10px;'; - - action.createEl('button', { text: this.plugin.t('preview') }).addEventListener('click', async () => { + }); + const action = contentEl.createEl("div"); + action.style.cssText = "display:flex;justify-content:flex-end;gap:8px;margin-top:10px;"; + action.createEl("button", { text: this.plugin.t("preview") }).addEventListener("click", async () => { this.plugin.setDraftProfile(this.draft); await this.plugin.activateView(); this.hasPreviewedDraft = true; }); - - const saveBtn = action.createEl('button', { text: this.plugin.t('saveAndOpen') }); - saveBtn.addClass('mod-cta'); - saveBtn.addEventListener('click', async () => { + const saveBtn = action.createEl("button", { text: this.plugin.t("saveAndOpen") }); + saveBtn.addClass("mod-cta"); + saveBtn.addEventListener("click", async () => { await this.plugin.saveProfile(this.draft, true, false); await this.plugin.activateView({ profile: this.draft }); this.skipCloseSavePrompt = true; this.close(); }); } - - onOpen() { this.render(); } + onOpen() { + this.render(); + } async onClose() { this.contentEl.empty(); if (this.skipCloseSavePrompt) return; if (!this.hasPreviewedDraft) return; - if (!window.confirm(this.plugin.t('savePreviewOnClose'))) return; + if (!window.confirm(this.plugin.t("savePreviewOnClose"))) return; await this.plugin.saveProfile(this.draft, true, false); await this.plugin.activateView({ profile: this.draft }); } -} - -class TextInputModal extends obsidian.Modal { +}; +var TextInputModal = class extends obsidian.Modal { constructor(app, title, placeholder, initial, onSubmit, hint) { super(app); this.titleText = title; @@ -621,55 +588,47 @@ class TextInputModal extends obsidian.Modal { this.initialValue = initial; this.onSubmitValue = onSubmit; } - onOpen() { const { contentEl } = this; contentEl.empty(); - contentEl.createEl('h3', { text: this.titleText }); - if (this.hintText) contentEl.createEl('p', { text: this.hintText }).style.cssText = 'margin:6px 0;color:var(--text-muted);font-size:0.9em;'; - - const input = contentEl.createEl('input'); - input.type = 'text'; + contentEl.createEl("h3", { text: this.titleText }); + if (this.hintText) contentEl.createEl("p", { text: this.hintText }).style.cssText = "margin:6px 0;color:var(--text-muted);font-size:0.9em;"; + const input = contentEl.createEl("input"); + input.type = "text"; input.placeholder = this.placeholderText; input.value = this.initialValue; - input.style.cssText = 'width:100%;margin-top:6px;'; - - const action = contentEl.createEl('div'); - action.style.cssText = 'display:flex;justify-content:flex-end;gap:8px;margin-top:12px;'; - action.createEl('button', { text: 'Cancel' }).addEventListener('click', () => { + input.style.cssText = "width:100%;margin-top:6px;"; + const action = contentEl.createEl("div"); + action.style.cssText = "display:flex;justify-content:flex-end;gap:8px;margin-top:12px;"; + action.createEl("button", { text: "Cancel" }).addEventListener("click", () => { this.onSubmitValue(null); this.close(); }); - const okBtn = action.createEl('button', { text: 'OK' }); - okBtn.addClass('mod-cta'); - okBtn.addEventListener('click', () => { + const okBtn = action.createEl("button", { text: "OK" }); + okBtn.addClass("mod-cta"); + okBtn.addEventListener("click", () => { this.onSubmitValue(input.value.trim()); this.close(); }); - - input.addEventListener('keydown', (e) => { - if (e.key === 'Enter') { + input.addEventListener("keydown", (e) => { + if (e.key === "Enter") { e.preventDefault(); this.onSubmitValue(input.value.trim()); this.close(); } }); - window.setTimeout(() => input.focus(), 0); } - onClose() { this.contentEl.empty(); } -} - +}; function askTextInput(app, title, placeholder, initial, hint) { - return new Promise(resolve => { + return new Promise((resolve) => { new TextInputModal(app, title, placeholder, initial, resolve, hint).open(); }); } - -class ThreeChoiceModal extends obsidian.Modal { +var ThreeChoiceModal = class extends obsidian.Modal { constructor(app, title, message, labels, onSubmit) { super(app); this.titleText = title; @@ -677,80 +636,69 @@ class ThreeChoiceModal extends obsidian.Modal { this.labels = labels; this.onSubmitValue = onSubmit; } - onOpen() { const { contentEl } = this; contentEl.empty(); - contentEl.createEl('h3', { text: this.titleText }); - contentEl.createEl('p', { text: this.messageText }).style.cssText = 'margin:6px 0 10px;color:var(--text-muted);font-size:0.9em;'; - - const action = contentEl.createEl('div'); - action.style.cssText = 'display:flex;justify-content:flex-end;gap:8px;flex-wrap:wrap;'; - + contentEl.createEl("h3", { text: this.titleText }); + contentEl.createEl("p", { text: this.messageText }).style.cssText = "margin:6px 0 10px;color:var(--text-muted);font-size:0.9em;"; + const action = contentEl.createEl("div"); + action.style.cssText = "display:flex;justify-content:flex-end;gap:8px;flex-wrap:wrap;"; const addChoice = (key, label, isCta = false) => { if (!label) return; - const btn = action.createEl('button', { text: label }); - if (isCta) btn.addClass('mod-cta'); - btn.addEventListener('click', () => { + const btn = action.createEl("button", { text: label }); + if (isCta) btn.addClass("mod-cta"); + btn.addEventListener("click", () => { this.onSubmitValue(key); this.close(); }); }; - - addChoice('cancel', this.labels.cancel); - addChoice('order', this.labels.order); - addChoice('order+prop', this.labels.orderAndProp, true); + addChoice("cancel", this.labels.cancel); + addChoice("order", this.labels.order); + addChoice("order+prop", this.labels.orderAndProp, true); } - onClose() { this.contentEl.empty(); } -} - +}; function askThreeChoices(app, title, message, labels) { - return new Promise(resolve => { + return new Promise((resolve) => { new ThreeChoiceModal(app, title, message, labels, resolve).open(); }); } - -class StartActionModal extends obsidian.Modal { +var StartActionModal = class extends obsidian.Modal { constructor(app, plugin, onSubmit) { super(app); this.plugin = plugin; this.onSubmitValue = onSubmit; } - onOpen() { const { contentEl } = this; contentEl.empty(); - contentEl.createEl('h3', { text: this.plugin.t('startActionTitle') }); - contentEl.createEl('p', { text: this.plugin.t('startActionDesc') }).style.cssText = 'margin:6px 0 10px;color:var(--text-muted);font-size:0.9em;'; - const action = contentEl.createEl('div'); - action.style.cssText = 'display:flex;justify-content:flex-end;gap:8px;'; - action.createEl('button', { text: this.plugin.t('startOpenPage') }).addEventListener('click', () => { - this.onSubmitValue('open'); + contentEl.createEl("h3", { text: this.plugin.t("startActionTitle") }); + contentEl.createEl("p", { text: this.plugin.t("startActionDesc") }).style.cssText = "margin:6px 0 10px;color:var(--text-muted);font-size:0.9em;"; + const action = contentEl.createEl("div"); + action.style.cssText = "display:flex;justify-content:flex-end;gap:8px;"; + action.createEl("button", { text: this.plugin.t("startOpenPage") }).addEventListener("click", () => { + this.onSubmitValue("open"); this.close(); }); - const createBtn = action.createEl('button', { text: this.plugin.t('startNewPage') }); - createBtn.addClass('mod-cta'); - createBtn.addEventListener('click', () => { - this.onSubmitValue('create'); + const createBtn = action.createEl("button", { text: this.plugin.t("startNewPage") }); + createBtn.addClass("mod-cta"); + createBtn.addEventListener("click", () => { + this.onSubmitValue("create"); this.close(); }); } - onClose() { this.contentEl.empty(); } -} - +}; function askStartAction(app, plugin) { - return new Promise(resolve => { + return new Promise((resolve) => { new StartActionModal(app, plugin, resolve).open(); }); } - -class FolderSelectModal extends obsidian.Modal { +var FolderSelectModal = class extends obsidian.Modal { constructor(app, plugin, folders, initialValue, onSubmit) { super(app); this.plugin = plugin; @@ -758,417 +706,374 @@ class FolderSelectModal extends obsidian.Modal { this.initialValue = initialValue; this.onSubmitValue = onSubmit; } - onOpen() { const { contentEl } = this; contentEl.empty(); - contentEl.createEl('h3', { text: this.plugin.t('exportFolderTitle') }); - contentEl.createEl('p', { text: this.plugin.t('exportFolderDesc') }).style.cssText = 'margin:6px 0 10px;color:var(--text-muted);font-size:0.9em;'; - - const select = contentEl.createEl('select'); - select.style.cssText = 'width:100%;'; + contentEl.createEl("h3", { text: this.plugin.t("exportFolderTitle") }); + contentEl.createEl("p", { text: this.plugin.t("exportFolderDesc") }).style.cssText = "margin:6px 0 10px;color:var(--text-muted);font-size:0.9em;"; + const select = contentEl.createEl("select"); + select.style.cssText = "width:100%;"; for (const f of this.folders) { - select.createEl('option', { value: f, text: f || this.plugin.t('rootFolder') }); + select.createEl("option", { value: f, text: f || this.plugin.t("rootFolder") }); } select.value = this.initialValue; - makeSelectSearchable(select, this.plugin.t('search')); - - const action = contentEl.createEl('div'); - action.style.cssText = 'display:flex;justify-content:flex-end;gap:8px;margin-top:12px;'; - action.createEl('button', { text: 'Cancel' }).addEventListener('click', () => { + makeSelectSearchable(select, this.plugin.t("search")); + const action = contentEl.createEl("div"); + action.style.cssText = "display:flex;justify-content:flex-end;gap:8px;margin-top:12px;"; + action.createEl("button", { text: "Cancel" }).addEventListener("click", () => { this.onSubmitValue(null); this.close(); }); - const okBtn = action.createEl('button', { text: this.plugin.t('exportFolderConfirm') }); - okBtn.addClass('mod-cta'); - okBtn.addEventListener('click', () => { + const okBtn = action.createEl("button", { text: this.plugin.t("exportFolderConfirm") }); + okBtn.addClass("mod-cta"); + okBtn.addEventListener("click", () => { this.onSubmitValue(select.value); this.close(); }); } - onClose() { this.contentEl.empty(); } -} - +}; function askFolderSelect(app, plugin, folders, initialValue) { - return new Promise(resolve => { + return new Promise((resolve) => { new FolderSelectModal(app, plugin, folders, initialValue, resolve).open(); }); } - -class ProfileManagerModal extends obsidian.Modal { +var ProfileManagerModal = class extends obsidian.Modal { constructor(app, plugin) { super(app); this.plugin = plugin; } - onOpen() { const { contentEl } = this; contentEl.empty(); - contentEl.createEl('h2', { text: this.plugin.t('managerTitle') }); - + contentEl.createEl("h2", { text: this.plugin.t("managerTitle") }); for (const profile of this.plugin.data.profiles) { const s = new obsidian.Setting(contentEl).setName(profile.name || profile.sourceFolder).setDesc(profile.sourceFolder); - s.addButton(b => b.setButtonText(this.plugin.t('open')).onClick(async () => { + s.addButton((b) => b.setButtonText(this.plugin.t("open")).onClick(async () => { this.plugin.clearDraftProfile(); this.plugin.data.lastActiveProfileId = profile.id; await this.plugin.persist(); await this.plugin.activateView({ profile, newLeaf: true }); this.close(); })); - s.addButton(b => b.setButtonText(this.plugin.t('edit')).onClick(() => { + s.addButton((b) => b.setButtonText(this.plugin.t("edit")).onClick(() => { new SetupModal(this.app, this.plugin, profile).open(); })); - s.addButton(b => b.setButtonText(this.plugin.t('delete')).onClick(async () => { - if (!window.confirm(this.plugin.t('deleteConfirm', { name: profile.name || profile.sourceFolder }))) return; - this.plugin.data.profiles = this.plugin.data.profiles.filter(p => p.id !== profile.id); - if (this.plugin.data.lastActiveProfileId === profile.id) this.plugin.data.lastActiveProfileId = ''; + s.addButton((b) => b.setButtonText(this.plugin.t("delete")).onClick(async () => { + if (!window.confirm(this.plugin.t("deleteConfirm", { name: profile.name || profile.sourceFolder }))) return; + this.plugin.data.profiles = this.plugin.data.profiles.filter((p) => p.id !== profile.id); + if (this.plugin.data.lastActiveProfileId === profile.id) this.plugin.data.lastActiveProfileId = ""; await this.plugin.persist(); this.onOpen(); })); } } - - onClose() { this.contentEl.empty(); } -} - -class SmartFolderView extends obsidian.ItemView { + onClose() { + this.contentEl.empty(); + } +}; +var SmartFolderView = class extends obsidian.ItemView { constructor(leaf, plugin) { super(leaf); this.plugin = plugin; this.pinnedProfile = null; - this.stateOwnerId = ''; - this.profileDefaultsKey = ''; + this.stateOwnerId = ""; + this.profileDefaultsKey = ""; this.timelineDropBusy = false; - this.timelineDragToken = ''; + this.timelineDragToken = ""; this.pendingBoardFieldValueByPath = {}; this.lastUndoSnapshot = null; } - - getViewType() { return VIEW_TYPE; } + getViewType() { + return VIEW_TYPE; + } getDisplayText() { const p = this.pinnedProfile || this.plugin.getCurrentProfile(); - return p?.name || p?.sourceFolder || this.plugin.t('title'); + return p?.name || p?.sourceFolder || this.plugin.t("title"); + } + getIcon() { + return "layout-grid"; } - getIcon() { return 'layout-grid'; } - updateLeafTitle() { const title = this.getDisplayText(); const leafAny = this.leaf; if (leafAny?.setTitle) leafAny.setTitle(title); if (leafAny?.tabHeaderInnerTitleEl?.setText) leafAny.tabHeaderInnerTitleEl.setText(title); } - setPinnedProfile(profile) { if (!profile) { this.pinnedProfile = null; } else { - this.pinnedProfile = { ...profile, displayFields: [...(profile.displayFields || [])] }; + this.pinnedProfile = { ...profile, displayFields: [...profile.displayFields || []] }; } - this.stateOwnerId = ''; - this.profileDefaultsKey = ''; + this.stateOwnerId = ""; + this.profileDefaultsKey = ""; this.lastUndoSnapshot = null; this.updateLeafTitle(); } - getRenderProfile() { if (!this.pinnedProfile) return this.plugin.getCurrentProfile(); - const latest = this.plugin.data.profiles.find(p => p.id === this.pinnedProfile.id) || this.pinnedProfile; - this.pinnedProfile = { ...latest, displayFields: [...(latest.displayFields || [])] }; + const latest = this.plugin.data.profiles.find((p) => p.id === this.pinnedProfile.id) || this.pinnedProfile; + this.pinnedProfile = { ...latest, displayFields: [...latest.displayFields || []] }; return this.pinnedProfile; } - ensureState(ownerId, defaultSort, defaultViewMode) { const nextDefaultsKey = `${defaultSort}|${defaultViewMode}`; if (this.stateOwnerId === ownerId && this.profileDefaultsKey === nextDefaultsKey) return; this.stateOwnerId = ownerId; this.profileDefaultsKey = nextDefaultsKey; try { - const saved = JSON.parse(localStorage.getItem(FILTER_STORAGE_PREFIX + ownerId) || '{}'); + const saved = JSON.parse(localStorage.getItem(FILTER_STORAGE_PREFIX + ownerId) || "{}"); this.isFreshFilterState = !saved || Object.keys(saved).length === 0; - const savedDefaultsKey = typeof saved.profileDefaultsKey === 'string' ? saved.profileDefaultsKey : ''; + const savedDefaultsKey = typeof saved.profileDefaultsKey === "string" ? saved.profileDefaultsKey : ""; const defaultsChanged = !!savedDefaultsKey && savedDefaultsKey !== this.profileDefaultsKey; this.filterState = { enabledFilters: Array.isArray(saved.enabledFilters) ? saved.enabledFilters : [], - selectedByAttr: (saved.selectedByAttr && typeof saved.selectedByAttr === 'object') ? saved.selectedByAttr : {}, - sortOrder: defaultsChanged ? defaultSort : (saved.sortOrder === 'asc' ? 'asc' : defaultSort), - viewMode: defaultsChanged ? defaultViewMode : (saved.viewMode === 'board' ? 'board' : defaultViewMode), - presetName: typeof saved.presetName === 'string' ? saved.presetName : '', + selectedByAttr: saved.selectedByAttr && typeof saved.selectedByAttr === "object" ? saved.selectedByAttr : {}, + sortOrder: defaultsChanged ? defaultSort : saved.sortOrder === "asc" ? "asc" : defaultSort, + viewMode: defaultsChanged ? defaultViewMode : saved.viewMode === "board" ? "board" : defaultViewMode, + presetName: typeof saved.presetName === "string" ? saved.presetName : "" }; } catch { this.isFreshFilterState = true; - this.filterState = { enabledFilters: [], selectedByAttr: {}, sortOrder: defaultSort, viewMode: defaultViewMode, presetName: '' }; + this.filterState = { enabledFilters: [], selectedByAttr: {}, sortOrder: defaultSort, viewMode: defaultViewMode, presetName: "" }; } } - saveState() { localStorage.setItem(FILTER_STORAGE_PREFIX + this.stateOwnerId, JSON.stringify({ ...this.filterState, - profileDefaultsKey: this.profileDefaultsKey, + profileDefaultsKey: this.profileDefaultsKey })); } - loadPresets() { try { - const parsed = JSON.parse(localStorage.getItem(FILTER_PRESET_STORAGE_PREFIX + this.stateOwnerId) || '[]'); + const parsed = JSON.parse(localStorage.getItem(FILTER_PRESET_STORAGE_PREFIX + this.stateOwnerId) || "[]"); if (!Array.isArray(parsed)) return []; return clonePresetList(parsed); - } catch { return []; } + } catch { + return []; + } } - savePresets(presets) { localStorage.setItem(FILTER_PRESET_STORAGE_PREFIX + this.stateOwnerId, JSON.stringify(clonePresetList(presets))); } - contextKey(profile) { return JSON.stringify({ profileId: profile.id, selectedByAttr: this.filterState.selectedByAttr, enabled: this.filterState.enabledFilters, sort: this.filterState.sortOrder, viewMode: this.filterState.viewMode, boardField: profile.boardField }); } - orderKey(profile, column) { - return `${this.contextKey(profile)}:${column || 'timeline'}`; + return `${this.contextKey(profile)}:${column || "timeline"}`; } - loadOrderStore(ownerId) { try { - const parsed = JSON.parse(localStorage.getItem(MANUAL_ORDER_STORAGE_PREFIX + ownerId) || '{}'); - return parsed && typeof parsed === 'object' ? parsed : {}; - } catch { return {}; } + const parsed = JSON.parse(localStorage.getItem(MANUAL_ORDER_STORAGE_PREFIX + ownerId) || "{}"); + return parsed && typeof parsed === "object" ? parsed : {}; + } catch { + return {}; + } } - saveOrderStore(ownerId, store) { localStorage.setItem(MANUAL_ORDER_STORAGE_PREFIX + ownerId, JSON.stringify(store)); } - applyOrder(ownerId, key, items) { const store = this.loadOrderStore(ownerId); const order = store[key] || []; if (!order.length) return items; - const rank = new Map(); + const rank = /* @__PURE__ */ new Map(); order.forEach((p, i) => rank.set(p, i)); return [...items].sort((a, b) => (rank.get(a.file.path) ?? Number.MAX_SAFE_INTEGER) - (rank.get(b.file.path) ?? Number.MAX_SAFE_INTEGER)); } - saveOrder(ownerId, key, files) { const store = this.loadOrderStore(ownerId); - store[key] = [...new Set(files.map(f => f.path))]; + store[key] = [...new Set(files.map((f) => f.path))]; this.saveOrderStore(ownerId, store); } - clearOrder(ownerId, key) { const store = this.loadOrderStore(ownerId); delete store[key]; this.saveOrderStore(ownerId, store); } - recordUndoSnapshot(profile) { this.lastUndoSnapshot = { ownerId: profile.id, orderStore: this.loadOrderStore(profile.id), - pendingBoardFieldValueByPath: { ...this.pendingBoardFieldValueByPath }, + pendingBoardFieldValueByPath: { ...this.pendingBoardFieldValueByPath } }; } - async undoLastAction(profile) { if (!this.lastUndoSnapshot || this.lastUndoSnapshot.ownerId !== profile.id) { - new obsidian.Notice(this.plugin.t('undoNone')); + new obsidian.Notice(this.plugin.t("undoNone")); return; } this.saveOrderStore(profile.id, this.lastUndoSnapshot.orderStore || {}); - this.pendingBoardFieldValueByPath = { ...(this.lastUndoSnapshot.pendingBoardFieldValueByPath || {}) }; + this.pendingBoardFieldValueByPath = { ...this.lastUndoSnapshot.pendingBoardFieldValueByPath || {} }; this.lastUndoSnapshot = null; await this.rerenderPreserveScroll(); - new obsidian.Notice(this.plugin.t('undoDone')); + new obsidian.Notice(this.plugin.t("undoDone")); } - async onOpen() { await this.renderView(); } - async rerenderPreserveScroll() { const container = this.containerEl.children[1]; const scrollTop = container?.scrollTop || 0; await this.renderView(); const nextContainer = this.containerEl.children[1]; - // Wait two animation frames to ensure browser has completed layout before restoring scroll - await new Promise(resolve => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve))); + await new Promise((resolve) => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve))); if (nextContainer) nextContainer.scrollTop = scrollTop; } - async askAndMaybeUpdateField(file, field, targetColumn, unsetLabel) { - const suggested = targetColumn === unsetLabel ? '' : targetColumn; - const useSuggested = window.confirm(this.plugin.t('moveAsk', { field, value: suggested || this.plugin.t('emptyValue') })); - const finalValue = useSuggested ? suggested : window.prompt(this.plugin.t('moveManual', { field }), suggested); - if (finalValue == null) return undefined; + const suggested = targetColumn === unsetLabel ? "" : targetColumn; + const useSuggested = window.confirm(this.plugin.t("moveAsk", { field, value: suggested || this.plugin.t("emptyValue") })); + const finalValue = useSuggested ? suggested : window.prompt(this.plugin.t("moveManual", { field }), suggested); + if (finalValue == null) return void 0; const value = finalValue.trim(); try { - await this.app.fileManager.processFrontMatter(file, fm => { + await this.app.fileManager.processFrontMatter(file, (fm) => { if (value) fm[field] = value; else delete fm[field]; }); - new obsidian.Notice(this.plugin.t('moveDone', { field, value: value || this.plugin.t('emptyValue') })); + new obsidian.Notice(this.plugin.t("moveDone", { field, value: value || this.plugin.t("emptyValue") })); return value; } catch { - new obsidian.Notice(this.plugin.t('moveFail')); - return undefined; + new obsidian.Notice(this.plugin.t("moveFail")); + return void 0; } } - getBoardFieldValue(file, cache, boardField) { if (Object.prototype.hasOwnProperty.call(this.pendingBoardFieldValueByPath, file.path)) { - return this.pendingBoardFieldValueByPath[file.path] || ''; + return this.pendingBoardFieldValueByPath[file.path] || ""; } - return getFrontmatterValues(cache, boardField)[0] || ''; + return getFrontmatterValues(cache, boardField)[0] || ""; } - getBoardFieldValues(file, cache, boardField) { if (Object.prototype.hasOwnProperty.call(this.pendingBoardFieldValueByPath, file.path)) { return toFlatArray(this.pendingBoardFieldValueByPath[file.path]); } return getFrontmatterValues(cache, boardField); } - reconcilePendingBoardValue(file, cache, boardField) { if (!Object.prototype.hasOwnProperty.call(this.pendingBoardFieldValueByPath, file.path)) return; - const pending = this.pendingBoardFieldValueByPath[file.path] || ''; - const actual = getFrontmatterValues(cache, boardField)[0] || ''; + const pending = this.pendingBoardFieldValueByPath[file.path] || ""; + const actual = getFrontmatterValues(cache, boardField)[0] || ""; if (pending === actual) delete this.pendingBoardFieldValueByPath[file.path]; } - renderCard(parent, file, cache, profile, color, showDot) { - const card = parent.createEl('section'); + const card = parent.createEl("section"); card.style.cssText = `position:relative;margin:0 0 12px;padding:12px 14px;border:1px solid var(--background-modifier-border);border-left:4px solid ${color};border-radius:12px;background:color-mix(in srgb, ${color} 9%, var(--background-primary) 91%);`; - if (showDot) { - card.createEl('span').style.cssText = `position:absolute;left:-26px;top:17px;width:12px;height:12px;border-radius:50%;background:${color};border:2px solid var(--background-primary);`; + card.createEl("span").style.cssText = `position:absolute;left:-26px;top:17px;width:12px;height:12px;border-radius:50%;background:${color};border:2px solid var(--background-primary);`; } - - const top = card.createEl('div'); - top.style.cssText = 'display:flex;align-items:center;gap:10px;overflow:hidden;margin-bottom:5px;'; - - const meta = top.createEl('span'); - meta.setText(profile.displayFields.map(k => `${k}: ${getFrontmatterValues(cache, k).join('、') || '-'}`).join(' | ')); - meta.style.cssText = 'font-size:0.85em;color:var(--text-muted);white-space:nowrap;overflow-x:auto;flex:1 1 auto;min-width:100px;'; - - const link = top.createEl('a'); + const top = card.createEl("div"); + top.style.cssText = "display:flex;align-items:center;gap:10px;overflow:hidden;margin-bottom:5px;"; + const meta = top.createEl("span"); + meta.setText(profile.displayFields.map((k) => `${k}: ${getFrontmatterValues(cache, k).join("\u3001") || "-"}`).join(" | ")); + meta.style.cssText = "font-size:0.85em;color:var(--text-muted);white-space:nowrap;overflow-x:auto;flex:1 1 auto;min-width:100px;"; + const link = top.createEl("a"); link.setText(file.basename); - link.className = 'internal-link'; - link.setAttribute('data-href', file.path); - link.setAttribute('href', file.path); - link.style.cssText = 'font-size:1em;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:0 1 220px;min-width:80px;'; + link.className = "internal-link"; + link.setAttribute("data-href", file.path); + link.setAttribute("href", file.path); + link.style.cssText = "font-size:1em;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:0 1 220px;min-width:80px;"; const triggerHoverPreview = (e) => { - this.app.workspace?.trigger?.('hover-link', { + this.app.workspace?.trigger?.("hover-link", { event: e, source: VIEW_TYPE, hoverParent: card, targetEl: link, - linktext: file.path, + linktext: file.path }); }; - link.addEventListener('mouseenter', triggerHoverPreview); - link.addEventListener('mousemove', triggerHoverPreview); - link.addEventListener('mouseover', triggerHoverPreview); - link.addEventListener('pointerenter', triggerHoverPreview); - link.addEventListener('pointermove', triggerHoverPreview); - link.addEventListener('click', (e) => { + link.addEventListener("mouseenter", triggerHoverPreview); + link.addEventListener("mousemove", triggerHoverPreview); + link.addEventListener("mouseover", triggerHoverPreview); + link.addEventListener("pointerenter", triggerHoverPreview); + link.addEventListener("pointermove", triggerHoverPreview); + link.addEventListener("click", (e) => { e.preventDefault(); this.app.workspace.openLinkText(file.path, file.path, e.ctrlKey || e.metaKey); }); - const triggerHoverPreviewForTarget = (e, targetEl) => { - const linktext = targetEl.getAttribute('data-href') || targetEl.getAttribute('href') || targetEl.textContent?.trim() || ''; + const linktext = targetEl.getAttribute("data-href") || targetEl.getAttribute("href") || targetEl.textContent?.trim() || ""; if (!linktext) return; - this.app.workspace?.trigger?.('hover-link', { + this.app.workspace?.trigger?.("hover-link", { event: e, source: VIEW_TYPE, hoverParent: card, targetEl, - linktext, + linktext }); }; - const getInternalLinkElement = (target) => { - if (target instanceof Element) return target.closest('.internal-link'); - if (target instanceof Node && target.parentElement) return target.parentElement.closest('.internal-link'); + if (target instanceof Element) return target.closest(".internal-link"); + if (target instanceof Node && target.parentElement) return target.parentElement.closest(".internal-link"); return null; }; - - const bodyEl = card.createEl('div'); - bodyEl.addClass('sfv-card-body'); - bodyEl.style.cssText = 'max-height:calc(var(--line-height-normal) * 1em * 3);overflow-y:auto;overflow-x:hidden;padding-right:4px;'; - bodyEl.addEventListener('mouseover', (e) => { + const bodyEl = card.createEl("div"); + bodyEl.addClass("sfv-card-body"); + bodyEl.style.cssText = "max-height:calc(var(--line-height-normal) * 1em * 3);overflow-y:auto;overflow-x:hidden;padding-right:4px;"; + bodyEl.addEventListener("mouseover", (e) => { const el = getInternalLinkElement(e.target); if (!el) return; triggerHoverPreviewForTarget(e, el); }, true); - bodyEl.addEventListener('pointerover', (e) => { + bodyEl.addEventListener("pointerover", (e) => { const el = getInternalLinkElement(e.target); if (!el) return; triggerHoverPreviewForTarget(e, el); }, true); - bodyEl.addEventListener('mousemove', (e) => { + bodyEl.addEventListener("mousemove", (e) => { const el = getInternalLinkElement(e.target); if (!el) return; triggerHoverPreviewForTarget(e, el); }, true); - bodyEl.addEventListener('pointermove', (e) => { + bodyEl.addEventListener("pointermove", (e) => { const el = getInternalLinkElement(e.target); if (!el) return; triggerHoverPreviewForTarget(e, el); }, true); - bodyEl.addEventListener('click', (e) => { + bodyEl.addEventListener("click", (e) => { const el = getInternalLinkElement(e.target); if (!el) return; - const href = el.getAttribute('data-href') || el.getAttribute('href'); + const href = el.getAttribute("data-href") || el.getAttribute("href"); if (!href) return; e.preventDefault(); this.app.workspace.openLinkText(href, file.path, e.ctrlKey || e.metaKey); }, true); - this.app.vault.read(file).then(raw => { - const body = stripFrontmatter(raw) || '(无正文)'; + this.app.vault.read(file).then((raw) => { + const body = stripFrontmatter(raw) || "\uFF08\u65E0\u6B63\u6587\uFF09"; obsidian.MarkdownRenderer.render(this.app, body, bodyEl, file.path, this); - }).catch(() => bodyEl.setText('(正文读取失败)')); + }).catch(() => bodyEl.setText("\uFF08\u6B63\u6587\u8BFB\u53D6\u5931\u8D25\uFF09")); } - buildDropdown(container, label, options, selectedValues, onChange, onCommit) { - const wrap = container.createEl('div'); - wrap.style.cssText = 'display:flex;align-items:flex-start;gap:8px;flex:1 1 420px;min-width:320px;max-width:420px;position:relative;z-index:1;'; - wrap.createEl('span', { text: label }).style.cssText = 'font-size:1em;color:var(--text-normal);min-width:72px;padding-top:8px;white-space:nowrap;'; - - const box = wrap.createEl('details'); - box.style.cssText = 'flex:1;position:relative;z-index:1;min-width:0;'; - const summary = box.createEl('summary'); - summary.style.cssText = 'list-style:none;cursor:pointer;height:36px;padding:7px 34px 7px 12px;border-radius:10px;border:1px solid var(--background-modifier-border);font-size:1em;box-sizing:border-box;overflow:hidden;'; + const wrap = container.createEl("div"); + wrap.style.cssText = "display:flex;align-items:flex-start;gap:8px;flex:1 1 420px;min-width:320px;max-width:420px;position:relative;z-index:1;"; + wrap.createEl("span", { text: label }).style.cssText = "font-size:1em;color:var(--text-normal);min-width:72px;padding-top:8px;white-space:nowrap;"; + const box = wrap.createEl("details"); + box.style.cssText = "flex:1;position:relative;z-index:1;min-width:0;"; + const summary = box.createEl("summary"); + summary.style.cssText = "list-style:none;cursor:pointer;height:36px;padding:7px 34px 7px 12px;border-radius:10px;border:1px solid var(--background-modifier-border);font-size:1em;box-sizing:border-box;overflow:hidden;"; const initialValues = [...selectedValues]; let currentValues = [...initialValues]; - const summarySpan = summary.createEl('span'); - summarySpan.style.cssText = 'color:var(--text-normal);display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;'; - const toDisplayText = (v) => v === EMPTY_OPTION_VALUE ? this.plugin.t('emptyValue') : v; + const summarySpan = summary.createEl("span"); + summarySpan.style.cssText = "color:var(--text-normal);display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;"; + const toDisplayText = (v) => v === EMPTY_OPTION_VALUE ? this.plugin.t("emptyValue") : v; const refreshSummary = () => { const isAll = currentValues.length === options.length && options.length > 0; - const full = !currentValues.length - ? this.plugin.t('noneSelected') - : currentValues.map(toDisplayText).join('、'); - const short = !currentValues.length - ? this.plugin.t('noneSelected') - : isAll - ? this.plugin.t('selectAll') - : (currentValues.length > 3 ? `${this.plugin.t('selected', { count: currentValues.length })}` : full); + const full = !currentValues.length ? this.plugin.t("noneSelected") : currentValues.map(toDisplayText).join("\u3001"); + const short = !currentValues.length ? this.plugin.t("noneSelected") : isAll ? this.plugin.t("selectAll") : currentValues.length > 3 ? `${this.plugin.t("selected", { count: currentValues.length })}` : full; summarySpan.setText(short); - summarySpan.title = isAll ? this.plugin.t('selectAll') : full; + summarySpan.title = isAll ? this.plugin.t("selectAll") : full; }; refreshSummary(); - - const panel = box.createEl('div'); - panel.style.cssText = 'position:absolute;top:calc(100% + 4px);left:0;right:0;z-index:30;max-height:260px;overflow:auto;padding:8px;border-radius:10px;border:1px solid var(--background-modifier-border);background:var(--background-primary);'; - - const actionBar = panel.createEl('div'); - actionBar.style.cssText = 'display:flex;gap:10px;align-items:center;margin-bottom:4px;'; - - const allBtn = actionBar.createEl('button', { text: this.plugin.t('selectAll') }); - allBtn.style.cssText = 'border:none;background:transparent;color:var(--interactive-accent);cursor:pointer;'; - allBtn.addEventListener('click', (e) => { + const panel = box.createEl("div"); + panel.style.cssText = "position:absolute;top:calc(100% + 4px);left:0;right:0;z-index:30;max-height:260px;overflow:auto;padding:8px;border-radius:10px;border:1px solid var(--background-modifier-border);background:var(--background-primary);"; + const actionBar = panel.createEl("div"); + actionBar.style.cssText = "display:flex;gap:10px;align-items:center;margin-bottom:4px;"; + const allBtn = actionBar.createEl("button", { text: this.plugin.t("selectAll") }); + allBtn.style.cssText = "border:none;background:transparent;color:var(--interactive-accent);cursor:pointer;"; + allBtn.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); currentValues = [...options]; @@ -1179,10 +1084,9 @@ class SmartFolderView extends obsidian.ItemView { onChange(currentValues); refreshSummary(); }); - - const clearBtn = actionBar.createEl('button', { text: this.plugin.t('selectNone') }); - clearBtn.style.cssText = 'border:none;background:transparent;color:var(--interactive-accent);cursor:pointer;'; - clearBtn.addEventListener('click', (e) => { + const clearBtn = actionBar.createEl("button", { text: this.plugin.t("selectNone") }); + clearBtn.style.cssText = "border:none;background:transparent;color:var(--interactive-accent);cursor:pointer;"; + clearBtn.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); currentValues = []; @@ -1193,154 +1097,138 @@ class SmartFolderView extends obsidian.ItemView { onChange([]); refreshSummary(); }); - - const search = panel.createEl('input'); - search.type = 'search'; - search.placeholder = this.plugin.t('search'); - search.style.cssText = 'width:100%;height:32px;padding:4px 8px;border-radius:8px;border:1px solid var(--background-modifier-border);margin:6px 0;'; - - const list = panel.createEl('div'); + const search = panel.createEl("input"); + search.type = "search"; + search.placeholder = this.plugin.t("search"); + search.style.cssText = "width:100%;height:32px;padding:4px 8px;border-radius:8px;border:1px solid var(--background-modifier-border);margin:6px 0;"; + const list = panel.createEl("div"); const rows = []; for (const option of options) { - const row = list.createEl('label'); - row.style.cssText = 'display:flex;align-items:center;gap:6px;padding:4px;font-size:1em;'; - const cb = row.createEl('input'); - cb.type = 'checkbox'; + const row = list.createEl("label"); + row.style.cssText = "display:flex;align-items:center;gap:6px;padding:4px;font-size:1em;"; + const cb = row.createEl("input"); + cb.type = "checkbox"; cb.checked = currentValues.includes(option); - row.createEl('span', { text: toDisplayText(option) }); - cb.addEventListener('change', () => { - const next = cb.checked ? [...currentValues, option] : currentValues.filter(v => v !== option); + row.createEl("span", { text: toDisplayText(option) }); + cb.addEventListener("change", () => { + const next = cb.checked ? [...currentValues, option] : currentValues.filter((v) => v !== option); currentValues = [...new Set(next)]; onChange(currentValues); refreshSummary(); }); rows.push({ option, row }); } - - search.addEventListener('input', () => { + search.addEventListener("input", () => { const q = search.value.toLowerCase(); - for (const { option, row } of rows) row.style.display = (!q || option.toLowerCase().includes(q)) ? 'flex' : 'none'; + for (const { option, row } of rows) row.style.display = !q || option.toLowerCase().includes(q) ? "flex" : "none"; }); - - box.addEventListener('toggle', () => { + box.addEventListener("toggle", () => { if (box.open) { - wrap.style.zIndex = '170'; - box.style.zIndex = '170'; + wrap.style.zIndex = "170"; + box.style.zIndex = "170"; } else { - wrap.style.zIndex = '1'; - box.style.zIndex = '1'; + wrap.style.zIndex = "1"; + box.style.zIndex = "1"; if (onCommit) onCommit(); } }); } - async renderView() { const profile = this.getRenderProfile(); const container = this.containerEl.children[1]; container.empty(); - container.classList.add('smart-folder-view-root'); + container.classList.add("smart-folder-view-root"); this.updateLeafTitle(); - container.style.padding = '16px 18px'; - container.style.overflowY = 'auto'; - + container.style.padding = "16px 18px"; + container.style.overflowY = "auto"; if (!profile) { - container.createEl('p', { text: this.plugin.t('noProfile') }); + container.createEl("p", { text: this.plugin.t("noProfile") }); return; } - this.ensureState(profile.id, profile.sortOrder, profile.viewMode); - - const topBar = container.createEl('div'); - topBar.style.cssText = 'display:flex;justify-content:flex-start;gap:8px;margin-bottom:10px;'; - const saveBtn = topBar.createEl('button', { text: this.plugin.isDraft(profile.id) ? this.plugin.t('savePage') : this.plugin.t('updatePage') }); - saveBtn.addClass('mod-cta'); - saveBtn.addEventListener('click', async () => { + const topBar = container.createEl("div"); + topBar.style.cssText = "display:flex;justify-content:flex-start;gap:8px;margin-bottom:10px;"; + const saveBtn = topBar.createEl("button", { text: this.plugin.isDraft(profile.id) ? this.plugin.t("savePage") : this.plugin.t("updatePage") }); + saveBtn.addClass("mod-cta"); + saveBtn.addEventListener("click", async () => { try { - // Save current page config directly into saved pages/history list. await this.plugin.saveProfile(profile, true, false); await this.rerenderPreserveScroll(); } catch { - new obsidian.Notice(this.plugin.t('saveFailed')); + new obsidian.Notice(this.plugin.t("saveFailed")); } }); - topBar.createEl('button', { text: this.plugin.t('openBuilderBtn') }).addEventListener('click', () => new SetupModal(this.app, this.plugin).open()); - topBar.createEl('button', { text: this.plugin.t('openManagerBtn') }).addEventListener('click', () => new ProfileManagerModal(this.app, this.plugin).open()); - topBar.createEl('button', { text: this.plugin.t('exportPage') }).addEventListener('click', async () => { + topBar.createEl("button", { text: this.plugin.t("openBuilderBtn") }).addEventListener("click", () => new SetupModal(this.app, this.plugin).open()); + topBar.createEl("button", { text: this.plugin.t("openManagerBtn") }).addEventListener("click", () => new ProfileManagerModal(this.app, this.plugin).open()); + topBar.createEl("button", { text: this.plugin.t("exportPage") }).addEventListener("click", async () => { await this.plugin.exportProfileToMarkdown(profile); }); - topBar.createEl('button', { text: this.plugin.t('refreshPreview') }).addEventListener('click', async () => { + topBar.createEl("button", { text: this.plugin.t("refreshPreview") }).addEventListener("click", async () => { await this.renderView(); - new obsidian.Notice(this.plugin.t('refreshPreviewDone')); + new obsidian.Notice(this.plugin.t("refreshPreviewDone")); }); - const files = this.plugin.getFilesByFolder(profile.sourceFolder); if (!files.length) { - container.createEl('p', { text: this.plugin.t('noFiles', { folder: profile.sourceFolder }) }); + container.createEl("p", { text: this.plugin.t("noFiles", { folder: profile.sourceFolder }) }); return; } - - const entries = files.map(file => ({ file, cache: this.app.metadataCache.getFileCache(file) })); + const entries = files.map((file) => ({ file, cache: this.app.metadataCache.getFileCache(file) })); for (const entry of entries) { this.reconcilePendingBoardValue(entry.file, entry.cache, profile.boardField); } - const attrKeys = getAllFrontmatterKeys(entries.map(e => e.cache)); + const attrKeys = getAllFrontmatterKeys(entries.map((e) => e.cache)); const optionsByAttr = {}; for (const key of attrKeys) { - const values = uniqueSorted(entries.flatMap(e => getFrontmatterValues(e.cache, key))); - const hasEmpty = entries.some(e => getFrontmatterValues(e.cache, key).length === 0); + const values = uniqueSorted(entries.flatMap((e) => getFrontmatterValues(e.cache, key))); + const hasEmpty = entries.some((e) => getFrontmatterValues(e.cache, key).length === 0); optionsByAttr[key] = hasEmpty ? [...values, EMPTY_OPTION_VALUE] : values; } - if (!this.filterState.enabledFilters.length) this.filterState.enabledFilters = attrKeys.slice(0, 4); - - // Initialize any filter key that has no saved selection to all-selected by default let defaultSelectionInitialized = false; for (const key of attrKeys) { if (!Array.isArray(this.filterState.selectedByAttr[key])) { - this.filterState.selectedByAttr[key] = [...(optionsByAttr[key] || [])]; + this.filterState.selectedByAttr[key] = [...optionsByAttr[key] || []]; defaultSelectionInitialized = true; } } if (defaultSelectionInitialized) this.saveState(); - - const switchesEl = container.createEl('div'); - switchesEl.style.cssText = 'display:flex;flex-wrap:wrap;gap:8px;margin-bottom:10px;'; + const switchesEl = container.createEl("div"); + switchesEl.style.cssText = "display:flex;flex-wrap:wrap;gap:8px;margin-bottom:10px;"; for (const key of attrKeys) { const enabled = this.filterState.enabledFilters.includes(key); - const chip = switchesEl.createEl('label'); - chip.style.cssText = 'display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;border:1px solid var(--background-modifier-border);font-size:1em;color:var(--text-normal);'; - const cb = chip.createEl('input'); - cb.type = 'checkbox'; + const chip = switchesEl.createEl("label"); + chip.style.cssText = "display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;border:1px solid var(--background-modifier-border);font-size:1em;color:var(--text-normal);"; + const cb = chip.createEl("input"); + cb.type = "checkbox"; cb.checked = enabled; - chip.createEl('span', { text: key }); - cb.addEventListener('change', () => { + chip.createEl("span", { text: key }); + cb.addEventListener("change", () => { const s = new Set(this.filterState.enabledFilters); - if (cb.checked) s.add(key); else s.delete(key); - this.filterState.enabledFilters = attrKeys.filter(k => s.has(k)); + if (cb.checked) s.add(key); + else s.delete(key); + this.filterState.enabledFilters = attrKeys.filter((k) => s.has(k)); this.saveState(); this.renderView(); }); } - - const controls = container.createEl('div'); - controls.style.cssText = 'display:flex;flex-direction:column;gap:8px;margin-bottom:12px;position:relative;z-index:120;overflow:visible;'; - controls.createEl('p', { text: this.plugin.t('presetScopeDesc') }).style.cssText = 'margin:0;color:var(--text-muted);font-size:0.85em;'; - + const controls = container.createEl("div"); + controls.style.cssText = "display:flex;flex-direction:column;gap:8px;margin-bottom:12px;position:relative;z-index:120;overflow:visible;"; + controls.createEl("p", { text: this.plugin.t("presetScopeDesc") }).style.cssText = "margin:0;color:var(--text-muted);font-size:0.85em;"; const presets = this.loadPresets(); - const presetWrap = controls.createEl('div'); - presetWrap.style.cssText = 'display:flex;gap:8px;align-items:center;'; - presetWrap.createEl('span', { text: this.plugin.t('preset') }).style.cssText = 'min-width:72px;color:var(--text-normal);'; - const presetSel = presetWrap.createEl('select'); - presetSel.createEl('option', { text: this.plugin.t('noPreset'), value: '' }); + const presetWrap = controls.createEl("div"); + presetWrap.style.cssText = "display:flex;gap:8px;align-items:center;"; + presetWrap.createEl("span", { text: this.plugin.t("preset") }).style.cssText = "min-width:72px;color:var(--text-normal);"; + const presetSel = presetWrap.createEl("select"); + presetSel.createEl("option", { text: this.plugin.t("noPreset"), value: "" }); for (const p of presets) { - const opt = presetSel.createEl('option', { text: p.name, value: p.name }); + const opt = presetSel.createEl("option", { text: p.name, value: p.name }); if (p.name === this.filterState.presetName) opt.selected = true; } - presetSel.addEventListener('change', () => { + presetSel.addEventListener("change", () => { const currentPresets = this.loadPresets(); - const p = currentPresets.find(x => x.name === presetSel.value); + const p = currentPresets.find((x) => x.name === presetSel.value); if (!p) { - this.filterState.presetName = ''; + this.filterState.presetName = ""; } else { this.filterState.enabledFilters = [...p.enabledFilters]; this.filterState.selectedByAttr = cloneSelectedByAttr(p.selectedByAttr); @@ -1351,34 +1239,31 @@ class SmartFolderView extends obsidian.ItemView { this.saveState(); this.renderView(); }); - makeSelectSearchable(presetSel, this.plugin.t('search')); - - presetWrap.createEl('button', { text: this.plugin.t('savePreset') }).addEventListener('click', async () => { - const name = (await askTextInput(this.app, this.plugin.t('savePreset'), this.plugin.t('presetPrompt'), this.filterState.presetName || '', this.plugin.t('presetSaveHint')) || '').trim(); + makeSelectSearchable(presetSel, this.plugin.t("search")); + presetWrap.createEl("button", { text: this.plugin.t("savePreset") }).addEventListener("click", async () => { + const name = (await askTextInput(this.app, this.plugin.t("savePreset"), this.plugin.t("presetPrompt"), this.filterState.presetName || "", this.plugin.t("presetSaveHint")) || "").trim(); if (!name) { - new obsidian.Notice(this.plugin.t('presetNameRequired')); + new obsidian.Notice(this.plugin.t("presetNameRequired")); return; } const currentPresets = this.loadPresets(); - const next = currentPresets.filter(p => p.name !== name); + const next = currentPresets.filter((p) => p.name !== name); next.push(clonePreset({ name, enabledFilters: [...this.filterState.enabledFilters], selectedByAttr: cloneSelectedByAttr(this.filterState.selectedByAttr), sortOrder: this.filterState.sortOrder, viewMode: this.filterState.viewMode })); this.savePresets(next); this.filterState.presetName = name; this.saveState(); await this.renderView(); }); - - presetWrap.createEl('button', { text: this.plugin.t('delPreset') }).addEventListener('click', () => { + presetWrap.createEl("button", { text: this.plugin.t("delPreset") }).addEventListener("click", () => { if (!this.filterState.presetName) return; const currentPresets = this.loadPresets(); - this.savePresets(currentPresets.filter(p => p.name !== this.filterState.presetName)); - this.filterState.presetName = ''; + this.savePresets(currentPresets.filter((p) => p.name !== this.filterState.presetName)); + this.filterState.presetName = ""; this.saveState(); this.renderView(); }); - - const filterGrid = controls.createEl('div'); - filterGrid.style.cssText = 'display:flex;flex-wrap:wrap;gap:8px 10px;align-items:flex-start;'; + const filterGrid = controls.createEl("div"); + filterGrid.style.cssText = "display:flex;flex-wrap:wrap;gap:8px 10px;align-items:flex-start;"; for (const key of this.filterState.enabledFilters) { this.buildDropdown(filterGrid, key, optionsByAttr[key] || [], this.filterState.selectedByAttr[key] || [], (vals) => { this.filterState.selectedByAttr[key] = [...vals]; @@ -1388,125 +1273,111 @@ class SmartFolderView extends obsidian.ItemView { }); } this.isFreshFilterState = false; - - const sortWrap = controls.createEl('label'); - sortWrap.style.cssText = 'display:flex;gap:8px;align-items:center;'; - sortWrap.createEl('span', { text: this.plugin.t('sortOrder') }).style.cssText = 'min-width:72px;color:var(--text-normal);'; - const sortSel = sortWrap.createEl('select'); - sortSel.createEl('option', { value: 'desc', text: this.plugin.t('desc') }); - sortSel.createEl('option', { value: 'asc', text: this.plugin.t('asc') }); + const sortWrap = controls.createEl("label"); + sortWrap.style.cssText = "display:flex;gap:8px;align-items:center;"; + sortWrap.createEl("span", { text: this.plugin.t("sortOrder") }).style.cssText = "min-width:72px;color:var(--text-normal);"; + const sortSel = sortWrap.createEl("select"); + sortSel.createEl("option", { value: "desc", text: this.plugin.t("desc") }); + sortSel.createEl("option", { value: "asc", text: this.plugin.t("asc") }); sortSel.value = this.filterState.sortOrder; - makeSelectSearchable(sortSel, this.plugin.t('search')); - sortSel.dispatchEvent(new Event('change')); - sortSel.addEventListener('change', () => { + makeSelectSearchable(sortSel, this.plugin.t("search")); + sortSel.dispatchEvent(new Event("change")); + sortSel.addEventListener("change", () => { this.filterState.sortOrder = sortSel.value; this.saveState(); this.renderView(); }); - - const modeWrap = controls.createEl('label'); - modeWrap.style.cssText = 'display:flex;gap:8px;align-items:center;'; - modeWrap.createEl('span', { text: this.plugin.t('viewMode') }).style.cssText = 'min-width:72px;color:var(--text-normal);'; - const modeSel = modeWrap.createEl('select'); - modeSel.createEl('option', { value: 'timeline', text: this.plugin.t('timeline') }); - modeSel.createEl('option', { value: 'board', text: this.plugin.t('board') }); + const modeWrap = controls.createEl("label"); + modeWrap.style.cssText = "display:flex;gap:8px;align-items:center;"; + modeWrap.createEl("span", { text: this.plugin.t("viewMode") }).style.cssText = "min-width:72px;color:var(--text-normal);"; + const modeSel = modeWrap.createEl("select"); + modeSel.createEl("option", { value: "timeline", text: this.plugin.t("timeline") }); + modeSel.createEl("option", { value: "board", text: this.plugin.t("board") }); modeSel.value = this.filterState.viewMode; - makeSelectSearchable(modeSel, this.plugin.t('search')); - modeSel.dispatchEvent(new Event('change')); - modeSel.addEventListener('change', async () => { - this.filterState.viewMode = modeSel.value === 'board' ? 'board' : 'timeline'; + makeSelectSearchable(modeSel, this.plugin.t("search")); + modeSel.dispatchEvent(new Event("change")); + modeSel.addEventListener("change", async () => { + this.filterState.viewMode = modeSel.value === "board" ? "board" : "timeline"; this.saveState(); this.renderView(); }); - - if (this.filterState.viewMode === 'board') { - const boardFieldWrap = controls.createEl('label'); - boardFieldWrap.style.cssText = 'display:flex;gap:8px;align-items:center;'; - boardFieldWrap.createEl('span', { text: this.plugin.t('boardField') }).style.cssText = 'min-width:72px;color:var(--text-normal);'; - const boardFieldSel = boardFieldWrap.createEl('select'); - boardFieldSel.createEl('option', { value: '', text: this.plugin.t('choose') }); - for (const k of attrKeys) boardFieldSel.createEl('option', { value: k, text: k }); - boardFieldSel.value = profile.boardField || ''; - makeSelectSearchable(boardFieldSel, this.plugin.t('search')); - boardFieldSel.addEventListener('change', async () => { + if (this.filterState.viewMode === "board") { + const boardFieldWrap = controls.createEl("label"); + boardFieldWrap.style.cssText = "display:flex;gap:8px;align-items:center;"; + boardFieldWrap.createEl("span", { text: this.plugin.t("boardField") }).style.cssText = "min-width:72px;color:var(--text-normal);"; + const boardFieldSel = boardFieldWrap.createEl("select"); + boardFieldSel.createEl("option", { value: "", text: this.plugin.t("choose") }); + for (const k of attrKeys) boardFieldSel.createEl("option", { value: k, text: k }); + boardFieldSel.value = profile.boardField || ""; + makeSelectSearchable(boardFieldSel, this.plugin.t("search")); + boardFieldSel.addEventListener("change", async () => { const next = boardFieldSel.value; profile.boardField = next; - const idx = this.plugin.data.profiles.findIndex(p => p.id === profile.id); + const idx = this.plugin.data.profiles.findIndex((p) => p.id === profile.id); if (idx >= 0) this.plugin.data.profiles[idx].boardField = next; if (this.plugin.draftProfile?.id === profile.id) this.plugin.draftProfile.boardField = next; await this.plugin.persist(); await this.renderView(); }); } - const filtered = entries.filter(({ file, cache }) => { if (!this.filterState.enabledFilters.length) return true; for (const key of this.filterState.enabledFilters) { - const selected = Array.isArray(this.filterState.selectedByAttr[key]) - ? this.filterState.selectedByAttr[key] - : [...(optionsByAttr[key] || [])]; + const selected = Array.isArray(this.filterState.selectedByAttr[key]) ? this.filterState.selectedByAttr[key] : [...optionsByAttr[key] || []]; if (!selected.length) return false; - const vals = key === profile.boardField - ? this.getBoardFieldValues(file, cache, profile.boardField) - : getFrontmatterValues(cache, key); + const vals = key === profile.boardField ? this.getBoardFieldValues(file, cache, profile.boardField) : getFrontmatterValues(cache, key); const matchEmpty = selected.includes(EMPTY_OPTION_VALUE) && vals.length === 0; - const matchNormal = selected.some(v => v !== EMPTY_OPTION_VALUE && vals.includes(v)); + const matchNormal = selected.some((v) => v !== EMPTY_OPTION_VALUE && vals.includes(v)); if (!(matchEmpty || matchNormal)) return false; } return true; }); - filtered.sort((a, b) => { const va = getFrontmatterValues(a.cache, profile.sortField)[0] || a.file.basename; const vb = getFrontmatterValues(b.cache, profile.sortField)[0] || b.file.basename; const pa = parseSortValue(va); const pb = parseSortValue(vb); - const cmp = (pa.kind === 'number' && pb.kind === 'number') - ? Number(pa.value) - Number(pb.value) - : String(pa.raw).localeCompare(String(pb.raw), 'zh-Hans-CN', { numeric: true, sensitivity: 'base' }); - return this.filterState.sortOrder === 'asc' ? cmp : -cmp; + const cmp = pa.kind === "number" && pb.kind === "number" ? Number(pa.value) - Number(pb.value) : String(pa.raw).localeCompare(String(pb.raw), "zh-Hans-CN", { numeric: true, sensitivity: "base" }); + return this.filterState.sortOrder === "asc" ? cmp : -cmp; }); - - container.createEl('p', { text: this.plugin.t('matched', { count: filtered.length }) }).style.cssText = 'color:var(--text-muted);margin:0 0 10px;'; + container.createEl("p", { text: this.plugin.t("matched", { count: filtered.length }) }).style.cssText = "color:var(--text-muted);margin:0 0 10px;"; if (!filtered.length) { - container.createEl('p', { text: this.plugin.t('noMatch') }); + container.createEl("p", { text: this.plugin.t("noMatch") }); return; } - - if (this.filterState.viewMode === 'timeline') { + if (this.filterState.viewMode === "timeline") { const ordered = this.applyOrder(profile.id, this.orderKey(profile), filtered); - const orderActions = container.createEl('div'); - orderActions.style.cssText = 'display:flex;gap:8px;align-items:center;margin:0 0 10px;'; - orderActions.createEl('button', { text: this.plugin.t('saveOrder') }).addEventListener('click', () => { - this.saveOrder(profile.id, this.orderKey(profile), ordered.map(e => e.file)); - new obsidian.Notice(this.plugin.t('saveOrderDone')); + const orderActions2 = container.createEl("div"); + orderActions2.style.cssText = "display:flex;gap:8px;align-items:center;margin:0 0 10px;"; + orderActions2.createEl("button", { text: this.plugin.t("saveOrder") }).addEventListener("click", () => { + this.saveOrder(profile.id, this.orderKey(profile), ordered.map((e) => e.file)); + new obsidian.Notice(this.plugin.t("saveOrderDone")); }); - orderActions.createEl('button', { text: this.plugin.t('resetOrder') }).addEventListener('click', async () => { + orderActions2.createEl("button", { text: this.plugin.t("resetOrder") }).addEventListener("click", async () => { this.recordUndoSnapshot(profile); this.clearOrder(profile.id, this.orderKey(profile)); - new obsidian.Notice(this.plugin.t('resetOrderDone')); + new obsidian.Notice(this.plugin.t("resetOrderDone")); await this.renderView(); }); - orderActions.createEl('button', { text: this.plugin.t('undoLastAction') }).addEventListener('click', async () => { + orderActions2.createEl("button", { text: this.plugin.t("undoLastAction") }).addEventListener("click", async () => { await this.undoLastAction(profile); }); - - const timelineEl = container.createEl('div'); - timelineEl.style.cssText = 'position:relative;padding-left:34px;margin-top:8px;'; - const lineEl = timelineEl.createEl('div'); - lineEl.style.cssText = 'position:absolute;left:16px;top:0;bottom:0;width:2px;background:var(--background-modifier-border);'; - const indicator = timelineEl.createEl('div'); - indicator.style.cssText = 'position:absolute;left:22px;right:0;height:4px;border-radius:6px;background:var(--interactive-accent);opacity:0;pointer-events:none;z-index:40;transition:opacity 80ms ease;'; + const timelineEl = container.createEl("div"); + timelineEl.style.cssText = "position:relative;padding-left:34px;margin-top:8px;"; + const lineEl = timelineEl.createEl("div"); + lineEl.style.cssText = "position:absolute;left:16px;top:0;bottom:0;width:2px;background:var(--background-modifier-border);"; + const indicator = timelineEl.createEl("div"); + indicator.style.cssText = "position:absolute;left:22px;right:0;height:4px;border-radius:6px;background:var(--interactive-accent);opacity:0;pointer-events:none;z-index:40;transition:opacity 80ms ease;"; let dropBeforePath = null; const hideIndicator = () => { - indicator.style.opacity = '0'; + indicator.style.opacity = "0"; }; const updateDropIndicator = (clientY) => { - const cards = Array.from(timelineEl.querySelectorAll('section[data-sfv-path]')).filter(el => !el.classList.contains('sfv-dragging')); + const cards = Array.from(timelineEl.querySelectorAll("section[data-sfv-path]")).filter((el) => !el.classList.contains("sfv-dragging")); if (!cards.length) { dropBeforePath = null; - indicator.style.top = '0px'; - indicator.style.opacity = '1'; + indicator.style.top = "0px"; + indicator.style.opacity = "1"; return; } let beforeCard = null; @@ -1517,7 +1388,7 @@ class SmartFolderView extends obsidian.ItemView { break; } } - dropBeforePath = beforeCard?.getAttribute('data-sfv-path') || null; + dropBeforePath = beforeCard?.getAttribute("data-sfv-path") || null; let top = 0; if (beforeCard) { top = beforeCard.offsetTop - 2; @@ -1526,118 +1397,108 @@ class SmartFolderView extends obsidian.ItemView { top = last.offsetTop + last.offsetHeight - 2; } indicator.style.top = `${Math.max(0, top)}px`; - indicator.style.opacity = '1'; + indicator.style.opacity = "1"; }; - - timelineEl.addEventListener('dragover', (e) => { + timelineEl.addEventListener("dragover", (e) => { e.preventDefault(); - if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'; + if (e.dataTransfer) e.dataTransfer.dropEffect = "move"; updateDropIndicator(e.clientY); }); - timelineEl.addEventListener('dragleave', (e) => { + timelineEl.addEventListener("dragleave", (e) => { const related = e.relatedTarget; if (related instanceof Node && timelineEl.contains(related)) return; hideIndicator(); }); - const handleTimelineDrop = async (path, beforePath) => { if (this.timelineDropBusy) return; if (beforePath && path === beforePath) return; - const from = ordered.findIndex(e => e.file.path === path); + const from = ordered.findIndex((e) => e.file.path === path); if (from < 0) return; const moved = ordered[from]; - const nextOrdered = ordered.filter(e => e.file.path !== path); - const to = beforePath ? nextOrdered.findIndex(e => e.file.path === beforePath) : -1; + const nextOrdered = ordered.filter((e) => e.file.path !== path); + const to = beforePath ? nextOrdered.findIndex((e) => e.file.path === beforePath) : -1; const insertAt = to >= 0 ? to : nextOrdered.length; nextOrdered.splice(insertAt, 0, moved); - - const targetField = profile.sortField || '时间'; - const action = await askThreeChoices(this.app, this.plugin.t('timelineMoveTitle'), this.plugin.t('timelineMoveChoice', { field: targetField }), { - cancel: this.plugin.t('timelineMoveCancel'), - order: this.plugin.t('timelineMoveOrderOnly'), - orderAndProp: this.plugin.t('timelineMoveOrderAndProp'), + const targetField = profile.sortField || "\u65F6\u95F4"; + const action = await askThreeChoices(this.app, this.plugin.t("timelineMoveTitle"), this.plugin.t("timelineMoveChoice", { field: targetField }), { + cancel: this.plugin.t("timelineMoveCancel"), + order: this.plugin.t("timelineMoveOrderOnly"), + orderAndProp: this.plugin.t("timelineMoveOrderAndProp") }); - - if (action === 'cancel') { - new obsidian.Notice(this.plugin.t('timelineMoveNoChange')); + if (action === "cancel") { + new obsidian.Notice(this.plugin.t("timelineMoveNoChange")); return; } - this.timelineDropBusy = true; try { this.recordUndoSnapshot(profile); ordered.splice(0, ordered.length, ...nextOrdered); - this.saveOrder(profile.id, this.orderKey(profile), ordered.map(e => e.file)); - - if (action === 'order+prop') { - const field = profile.sortField || '时间'; - const current = getFrontmatterValues(this.app.metadataCache.getFileCache(moved.file), field)[0] || ''; - const next = await askTextInput(this.app, this.plugin.t('timelineMoveOrderAndProp'), this.plugin.t('timelineUpdatePrompt', { field }), current, this.plugin.t('timelineUpdatePrompt', { field })); + this.saveOrder(profile.id, this.orderKey(profile), ordered.map((e) => e.file)); + if (action === "order+prop") { + const field = profile.sortField || "\u65F6\u95F4"; + const current = getFrontmatterValues(this.app.metadataCache.getFileCache(moved.file), field)[0] || ""; + const next = await askTextInput(this.app, this.plugin.t("timelineMoveOrderAndProp"), this.plugin.t("timelineUpdatePrompt", { field }), current, this.plugin.t("timelineUpdatePrompt", { field })); if (next != null) { const value = next.trim(); - await this.app.fileManager.processFrontMatter(moved.file, fm => { + await this.app.fileManager.processFrontMatter(moved.file, (fm) => { if (value) fm[field] = value; else delete fm[field]; }); } - new obsidian.Notice(this.plugin.t('timelineMoveDone')); + new obsidian.Notice(this.plugin.t("timelineMoveDone")); await this.rerenderPreserveScroll(); } else { - // "order only": re-order existing card DOM nodes directly — no full re-render, no scroll jump - const cardEls = new Map(); - for (const el of timelineEl.querySelectorAll('section[data-sfv-path]')) { - cardEls.set(el.getAttribute('data-sfv-path'), el); + const cardEls = /* @__PURE__ */ new Map(); + for (const el of timelineEl.querySelectorAll("section[data-sfv-path]")) { + cardEls.set(el.getAttribute("data-sfv-path"), el); } for (const entry of ordered) { const el = cardEls.get(entry.file.path); - if (el) timelineEl.appendChild(el); // moves to end, re-ordering in place + if (el) timelineEl.appendChild(el); } - new obsidian.Notice(this.plugin.t('timelineMoveDone')); + new obsidian.Notice(this.plugin.t("timelineMoveDone")); } } finally { this.timelineDropBusy = false; } }; - - timelineEl.addEventListener('drop', async (e) => { + timelineEl.addEventListener("drop", async (e) => { e.preventDefault(); hideIndicator(); - const payload = e.dataTransfer?.getData('text/plain') || ''; + const payload = e.dataTransfer?.getData("text/plain") || ""; if (!payload) return; const obj = JSON.parse(payload); if (obj.token && this.timelineDragToken && obj.token !== this.timelineDragToken) return; - if (!obj.path || obj.from !== 'timeline') return; + if (!obj.path || obj.from !== "timeline") return; const beforePath = dropBeforePath; await handleTimelineDrop(obj.path, beforePath); }); - for (const { file, cache } of ordered) { const color = colorForValue(getFrontmatterValues(cache, profile.colorField)[0] || null, this.plugin.data.colorConfig) || this.plugin.data.colorConfig.fallbackColor; this.renderCard(timelineEl, file, cache, profile, color, true); const card = timelineEl.lastElementChild; - card.setAttribute('data-sfv-path', file.path); + card.setAttribute("data-sfv-path", file.path); card.draggable = true; - card.addEventListener('dragstart', (e) => { + card.addEventListener("dragstart", (e) => { this.timelineDragToken = newId(); - card.classList.add('sfv-dragging'); + card.classList.add("sfv-dragging"); if (e.dataTransfer) { - e.dataTransfer.effectAllowed = 'move'; + e.dataTransfer.effectAllowed = "move"; e.dataTransfer.setDragImage(card, 28, 20); } - e.dataTransfer?.setData('text/plain', JSON.stringify({ path: file.path, from: 'timeline', token: this.timelineDragToken })); + e.dataTransfer?.setData("text/plain", JSON.stringify({ path: file.path, from: "timeline", token: this.timelineDragToken })); }); - card.addEventListener('dragend', () => { - card.classList.remove('sfv-dragging'); - this.timelineDragToken = ''; + card.addEventListener("dragend", () => { + card.classList.remove("sfv-dragging"); + this.timelineDragToken = ""; hideIndicator(); }); - card.addEventListener('dragover', e => e.preventDefault()); + card.addEventListener("dragover", (e) => e.preventDefault()); } return; } - - const grouped = new Map(); - const unsetLabel = this.plugin.t('boardUnset'); + const grouped = /* @__PURE__ */ new Map(); + const unsetLabel = this.plugin.t("boardUnset"); for (const entry of filtered) { const cols = this.getBoardFieldValues(entry.file, entry.cache, profile.boardField); const targets = cols.length ? cols : [unsetLabel]; @@ -1646,114 +1507,103 @@ class SmartFolderView extends obsidian.ItemView { grouped.get(col).push(entry); } } - const columns = uniqueSorted([...grouped.keys()]); - - const orderActions = container.createEl('div'); - orderActions.style.cssText = 'display:flex;gap:8px;align-items:center;margin:0 0 10px;'; - - orderActions.createEl('button', { text: this.plugin.t('saveOrder') }).addEventListener('click', () => { + const orderActions = container.createEl("div"); + orderActions.style.cssText = "display:flex;gap:8px;align-items:center;margin:0 0 10px;"; + orderActions.createEl("button", { text: this.plugin.t("saveOrder") }).addEventListener("click", () => { for (const col of columns) { const arr = grouped.get(col) || []; - this.saveOrder(profile.id, this.orderKey(profile, col), arr.map(e => e.file)); + this.saveOrder(profile.id, this.orderKey(profile, col), arr.map((e) => e.file)); } - new obsidian.Notice(this.plugin.t('saveOrderDone')); + new obsidian.Notice(this.plugin.t("saveOrderDone")); }); - orderActions.createEl('button', { text: this.plugin.t('resetOrder') }).addEventListener('click', async () => { + orderActions.createEl("button", { text: this.plugin.t("resetOrder") }).addEventListener("click", async () => { this.recordUndoSnapshot(profile); for (const col of columns) this.clearOrder(profile.id, this.orderKey(profile, col)); - new obsidian.Notice(this.plugin.t('resetOrderDone')); + new obsidian.Notice(this.plugin.t("resetOrderDone")); await this.renderView(); }); - orderActions.createEl('button', { text: this.plugin.t('undoLastAction') }).addEventListener('click', async () => { + orderActions.createEl("button", { text: this.plugin.t("undoLastAction") }).addEventListener("click", async () => { await this.undoLastAction(profile); }); - - const boardEl = container.createEl('div'); - boardEl.style.cssText = 'display:flex;gap:12px;align-items:flex-start;overflow-x:auto;margin-top:2px;padding-top:2px;'; - + const boardEl = container.createEl("div"); + boardEl.style.cssText = "display:flex;gap:12px;align-items:flex-start;overflow-x:auto;margin-top:2px;padding-top:2px;"; const handleDrop = async (payload, to, beforePath) => { const fromArr = grouped.get(payload.from) || []; const toArr = grouped.get(to) || []; - const idx = fromArr.findIndex(e => e.file.path === payload.path); + const idx = fromArr.findIndex((e) => e.file.path === payload.path); if (idx < 0) return; const moved = fromArr[idx]; - - let action = 'order'; - let nextBoardValue = undefined; + let action = "order"; + let nextBoardValue = void 0; if (payload.from !== to && profile.boardField) { - action = await askThreeChoices(this.app, this.plugin.t('boardMoveTitle'), this.plugin.t('boardMoveChoice', { field: profile.boardField }), { - cancel: this.plugin.t('boardMoveCancel'), - orderAndProp: this.plugin.t('boardMoveOrderAndProp'), + action = await askThreeChoices(this.app, this.plugin.t("boardMoveTitle"), this.plugin.t("boardMoveChoice", { field: profile.boardField }), { + cancel: this.plugin.t("boardMoveCancel"), + orderAndProp: this.plugin.t("boardMoveOrderAndProp") }); - if (action === 'cancel') { - new obsidian.Notice(this.plugin.t('boardMoveNoChange')); + if (action === "cancel") { + new obsidian.Notice(this.plugin.t("boardMoveNoChange")); return; } - nextBoardValue = to === unsetLabel ? '' : to; + nextBoardValue = to === unsetLabel ? "" : to; } - this.recordUndoSnapshot(profile); const removed = fromArr.splice(idx, 1)[0]; - const pos = beforePath ? toArr.findIndex(e => e.file.path === beforePath) : -1; + const pos = beforePath ? toArr.findIndex((e) => e.file.path === beforePath) : -1; const insertAt = pos >= 0 ? pos : toArr.length; toArr.splice(insertAt, 0, removed); grouped.set(payload.from, fromArr); grouped.set(to, toArr); - this.saveOrder(profile.id, this.orderKey(profile, payload.from), fromArr.map(e => e.file)); - this.saveOrder(profile.id, this.orderKey(profile, to), toArr.map(e => e.file)); - - if (payload.from !== to && profile.boardField && action === 'order+prop') { + this.saveOrder(profile.id, this.orderKey(profile, payload.from), fromArr.map((e) => e.file)); + this.saveOrder(profile.id, this.orderKey(profile, to), toArr.map((e) => e.file)); + if (payload.from !== to && profile.boardField && action === "order+prop") { this.pendingBoardFieldValueByPath[removed.file.path] = nextBoardValue; if (removed.cache?.frontmatter) { if (nextBoardValue) removed.cache.frontmatter[profile.boardField] = nextBoardValue; else delete removed.cache.frontmatter[profile.boardField]; } - await this.app.fileManager.processFrontMatter(removed.file, fm => { + await this.app.fileManager.processFrontMatter(removed.file, (fm) => { if (nextBoardValue) fm[profile.boardField] = nextBoardValue; else delete fm[profile.boardField]; }); } - - new obsidian.Notice(this.plugin.t('boardMoveDone')); + new obsidian.Notice(this.plugin.t("boardMoveDone")); await this.rerenderPreserveScroll(); }; - for (const col of columns) { const ordered = this.applyOrder(profile.id, this.orderKey(profile, col), grouped.get(col) || []); grouped.set(col, ordered); - - const colWrap = boardEl.createEl('section'); - colWrap.style.cssText = 'min-width:280px;max-width:320px;flex:0 0 300px;border:1px solid var(--background-modifier-border);border-radius:12px;background:var(--background-primary-alt);padding:8px;'; - colWrap.createEl('strong', { text: `${col} (${ordered.length})` }); - const cardsEl = colWrap.createEl('div'); - cardsEl.style.cssText = 'margin-top:8px;min-height:40px;position:relative;'; - const dropIndicator = cardsEl.createEl('div'); - dropIndicator.style.cssText = 'height:10px;border:2px dashed var(--interactive-accent);border-radius:8px;background:color-mix(in srgb, var(--interactive-accent) 12%, transparent);margin:4px 0;opacity:0;pointer-events:none;transition:opacity 80ms ease;'; + const colWrap = boardEl.createEl("section"); + colWrap.style.cssText = "min-width:280px;max-width:320px;flex:0 0 300px;border:1px solid var(--background-modifier-border);border-radius:12px;background:var(--background-primary-alt);padding:8px;"; + colWrap.createEl("strong", { text: `${col} (${ordered.length})` }); + const cardsEl = colWrap.createEl("div"); + cardsEl.style.cssText = "margin-top:8px;min-height:40px;position:relative;"; + const dropIndicator = cardsEl.createEl("div"); + dropIndicator.style.cssText = "height:10px;border:2px dashed var(--interactive-accent);border-radius:8px;background:color-mix(in srgb, var(--interactive-accent) 12%, transparent);margin:4px 0;opacity:0;pointer-events:none;transition:opacity 80ms ease;"; let dropBeforePath = null; const hideDropIndicator = () => { - dropIndicator.style.opacity = '0'; + dropIndicator.style.opacity = "0"; dropBeforePath = null; }; const getNextCardPath = (cardEl) => { let next = cardEl.nextElementSibling; while (next) { - if (next.matches?.('section[data-sfv-path]')) return next.getAttribute('data-sfv-path') || null; + if (next.matches?.("section[data-sfv-path]")) return next.getAttribute("data-sfv-path") || null; next = next.nextElementSibling; } return null; }; const getCardDropBeforePath = (cardEl, clientY) => { const rect = cardEl.getBoundingClientRect(); - if (clientY < rect.top + rect.height / 2) return cardEl.getAttribute('data-sfv-path') || null; + if (clientY < rect.top + rect.height / 2) return cardEl.getAttribute("data-sfv-path") || null; return getNextCardPath(cardEl); }; const updateDropIndicator = (clientY) => { - const cards = Array.from(cardsEl.querySelectorAll('section[data-sfv-path]')).filter(el => !el.classList.contains('sfv-dragging')); + const cards = Array.from(cardsEl.querySelectorAll("section[data-sfv-path]")).filter((el) => !el.classList.contains("sfv-dragging")); if (!cards.length) { dropBeforePath = null; cardsEl.appendChild(dropIndicator); - dropIndicator.style.opacity = '1'; + dropIndicator.style.opacity = "1"; return; } let beforeCard = null; @@ -1764,276 +1614,201 @@ class SmartFolderView extends obsidian.ItemView { break; } } - dropBeforePath = beforeCard?.getAttribute('data-sfv-path') || null; + dropBeforePath = beforeCard?.getAttribute("data-sfv-path") || null; if (beforeCard) cardsEl.insertBefore(dropIndicator, beforeCard); else cardsEl.appendChild(dropIndicator); - dropIndicator.style.opacity = '1'; + dropIndicator.style.opacity = "1"; }; - cardsEl.addEventListener('dragover', e => { + cardsEl.addEventListener("dragover", (e) => { e.preventDefault(); - if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'; + if (e.dataTransfer) e.dataTransfer.dropEffect = "move"; updateDropIndicator(e.clientY); }); - cardsEl.addEventListener('dragleave', (e) => { + cardsEl.addEventListener("dragleave", (e) => { const related = e.relatedTarget; if (related instanceof Node && cardsEl.contains(related)) return; hideDropIndicator(); }); - cardsEl.addEventListener('drop', async (e) => { + cardsEl.addEventListener("drop", async (e) => { e.preventDefault(); const beforePath = dropBeforePath; hideDropIndicator(); - const payload = e.dataTransfer?.getData('text/plain') || ''; + const payload = e.dataTransfer?.getData("text/plain") || ""; if (!payload) return; await handleDrop(JSON.parse(payload), col, beforePath); }); - for (const { file, cache } of ordered) { const color = colorForValue(getFrontmatterValues(cache, profile.colorField)[0] || null, this.plugin.data.colorConfig) || this.plugin.data.colorConfig.fallbackColor; this.renderCard(cardsEl, file, cache, profile, color, false); const card = cardsEl.lastElementChild; - card.setAttribute('data-sfv-path', file.path); + card.setAttribute("data-sfv-path", file.path); card.draggable = true; - card.addEventListener('dragstart', (e) => { - card.classList.add('sfv-dragging'); - e.dataTransfer?.setData('text/plain', JSON.stringify({ path: file.path, from: col })); + card.addEventListener("dragstart", (e) => { + card.classList.add("sfv-dragging"); + e.dataTransfer?.setData("text/plain", JSON.stringify({ path: file.path, from: col })); }); - card.addEventListener('dragend', () => { - card.classList.remove('sfv-dragging'); + card.addEventListener("dragend", () => { + card.classList.remove("sfv-dragging"); hideDropIndicator(); }); - card.addEventListener('dragover', e => { + card.addEventListener("dragover", (e) => { e.preventDefault(); updateDropIndicator(e.clientY); }); - card.addEventListener('drop', async (e) => { + card.addEventListener("drop", async (e) => { e.preventDefault(); e.stopPropagation(); const beforePath = getCardDropBeforePath(card, e.clientY); hideDropIndicator(); - const payload = e.dataTransfer?.getData('text/plain') || ''; + const payload = e.dataTransfer?.getData("text/plain") || ""; if (!payload) return; await handleDrop(JSON.parse(payload), col, beforePath); }); } } } - - async onClose() {} -} - -class SmartFolderSettingTab extends obsidian.PluginSettingTab { + async onClose() { + } +}; +var SmartFolderSettingTab = class extends obsidian.PluginSettingTab { constructor(app, plugin) { super(app, plugin); this.plugin = plugin; } - display() { const { containerEl } = this; containerEl.empty(); - containerEl.createEl('h2', { text: this.plugin.t('title') }); - - new obsidian.Setting(containerEl) - .setName(this.plugin.t('openBuilder')) - .addButton(b => b.setButtonText(this.plugin.t('openBuilder')).onClick(() => new SetupModal(this.app, this.plugin).open())); - - new obsidian.Setting(containerEl) - .setName(this.plugin.t('openManager')) - .addButton(b => b.setButtonText(this.plugin.t('openManager')).onClick(() => new ProfileManagerModal(this.app, this.plugin).open())); - - new obsidian.Setting(containerEl) - .setName(this.plugin.t('languageMode')) - .setDesc(this.plugin.t('languageModeDesc')) - .addDropdown(d => { - d.addOption('auto', this.plugin.t('languageAuto')); - d.addOption('zh', this.plugin.t('languageZh')); - d.addOption('en', this.plugin.t('languageEn')); - d.setValue(this.plugin.data.languageMode); - makeSelectSearchable(d.selectEl, this.plugin.t('search')); - d.onChange(async (v) => { - this.plugin.data.languageMode = v; - await this.plugin.persist(); - this.display(); - }); + containerEl.createEl("h2", { text: this.plugin.t("title") }); + new obsidian.Setting(containerEl).setName(this.plugin.t("openBuilder")).addButton((b) => b.setButtonText(this.plugin.t("openBuilder")).onClick(() => new SetupModal(this.app, this.plugin).open())); + new obsidian.Setting(containerEl).setName(this.plugin.t("openManager")).addButton((b) => b.setButtonText(this.plugin.t("openManager")).onClick(() => new ProfileManagerModal(this.app, this.plugin).open())); + new obsidian.Setting(containerEl).setName(this.plugin.t("languageMode")).setDesc(this.plugin.t("languageModeDesc")).addDropdown((d) => { + d.addOption("auto", this.plugin.t("languageAuto")); + d.addOption("zh", this.plugin.t("languageZh")); + d.addOption("en", this.plugin.t("languageEn")); + d.setValue(this.plugin.data.languageMode); + makeSelectSearchable(d.selectEl, this.plugin.t("search")); + d.onChange(async (v) => { + this.plugin.data.languageMode = v; + await this.plugin.persist(); + this.display(); }); - - containerEl.createEl('h3', { text: this.plugin.t('colorSettings') }); - - new obsidian.Setting(containerEl) - .setName(this.plugin.t('saturationBase')) - .setDesc('20-80') - .addSlider(s => s - .setLimits(20, 80, 1) - .setValue(this.plugin.data.colorConfig.saturationBase) - .setDynamicTooltip() - .onChange(async (v) => { - this.plugin.data.colorConfig.saturationBase = v; - await this.plugin.persist(); - })); - - new obsidian.Setting(containerEl) - .setName(this.plugin.t('saturationRange')) - .setDesc('1-40') - .addSlider(s => s - .setLimits(1, 40, 1) - .setValue(this.plugin.data.colorConfig.saturationRange) - .setDynamicTooltip() - .onChange(async (v) => { - this.plugin.data.colorConfig.saturationRange = v; - await this.plugin.persist(); - })); - - new obsidian.Setting(containerEl) - .setName(this.plugin.t('lightnessBase')) - .setDesc('20-70') - .addSlider(s => s - .setLimits(20, 70, 1) - .setValue(this.plugin.data.colorConfig.lightnessBase) - .setDynamicTooltip() - .onChange(async (v) => { - this.plugin.data.colorConfig.lightnessBase = v; - await this.plugin.persist(); - })); - - new obsidian.Setting(containerEl) - .setName(this.plugin.t('lightnessRange')) - .setDesc('1-30') - .addSlider(s => s - .setLimits(1, 30, 1) - .setValue(this.plugin.data.colorConfig.lightnessRange) - .setDynamicTooltip() - .onChange(async (v) => { - this.plugin.data.colorConfig.lightnessRange = v; - await this.plugin.persist(); - })); - - new obsidian.Setting(containerEl) - .setName(this.plugin.t('fallbackColor')) - .setDesc('CSS color value, e.g. #3b82f6 or var(--interactive-accent)') - .addText(t => t - .setPlaceholder('var(--interactive-accent)') - .setValue(this.plugin.data.colorConfig.fallbackColor) - .onChange(async (v) => { - this.plugin.data.colorConfig.fallbackColor = v.trim() || DEFAULT_COLOR_CONFIG.fallbackColor; - await this.plugin.persist(); - })) - .addButton(b => b - .setButtonText(this.plugin.t('resetDefault')) - .onClick(async () => { - this.plugin.data.colorConfig = { ...DEFAULT_COLOR_CONFIG }; - await this.plugin.persist(); - this.display(); - })); + }); + containerEl.createEl("h3", { text: this.plugin.t("colorSettings") }); + new obsidian.Setting(containerEl).setName(this.plugin.t("saturationBase")).setDesc("20-80").addSlider((s) => s.setLimits(20, 80, 1).setValue(this.plugin.data.colorConfig.saturationBase).setDynamicTooltip().onChange(async (v) => { + this.plugin.data.colorConfig.saturationBase = v; + await this.plugin.persist(); + })); + new obsidian.Setting(containerEl).setName(this.plugin.t("saturationRange")).setDesc("1-40").addSlider((s) => s.setLimits(1, 40, 1).setValue(this.plugin.data.colorConfig.saturationRange).setDynamicTooltip().onChange(async (v) => { + this.plugin.data.colorConfig.saturationRange = v; + await this.plugin.persist(); + })); + new obsidian.Setting(containerEl).setName(this.plugin.t("lightnessBase")).setDesc("20-70").addSlider((s) => s.setLimits(20, 70, 1).setValue(this.plugin.data.colorConfig.lightnessBase).setDynamicTooltip().onChange(async (v) => { + this.plugin.data.colorConfig.lightnessBase = v; + await this.plugin.persist(); + })); + new obsidian.Setting(containerEl).setName(this.plugin.t("lightnessRange")).setDesc("1-30").addSlider((s) => s.setLimits(1, 30, 1).setValue(this.plugin.data.colorConfig.lightnessRange).setDynamicTooltip().onChange(async (v) => { + this.plugin.data.colorConfig.lightnessRange = v; + await this.plugin.persist(); + })); + new obsidian.Setting(containerEl).setName(this.plugin.t("fallbackColor")).setDesc("CSS color value, e.g. #3b82f6 or var(--interactive-accent)").addText((t) => t.setPlaceholder("var(--interactive-accent)").setValue(this.plugin.data.colorConfig.fallbackColor).onChange(async (v) => { + this.plugin.data.colorConfig.fallbackColor = v.trim() || DEFAULT_COLOR_CONFIG.fallbackColor; + await this.plugin.persist(); + })).addButton((b) => b.setButtonText(this.plugin.t("resetDefault")).onClick(async () => { + this.plugin.data.colorConfig = { ...DEFAULT_COLOR_CONFIG }; + await this.plugin.persist(); + this.display(); + })); } -} - -class SmartFolderPlugin extends obsidian.Plugin { +}; +var SmartFolderPlugin = class extends obsidian.Plugin { t(key, vars) { - const mode = this.data?.languageMode || 'auto'; - const lang = mode === 'auto' ? detectLanguage() : mode; + const mode = this.data?.languageMode || "auto"; + const lang = mode === "auto" ? detectLanguage() : mode; return tpl(I18N[lang]?.[key] || key, vars); } - getFolderOptions() { - const set = new Set(); + const set = /* @__PURE__ */ new Set(); for (const f of this.app.vault.getMarkdownFiles()) { if (f.parent?.path) set.add(f.parent.path); } - return ['', ...uniqueSorted([...set])]; + return ["", ...uniqueSorted([...set])]; } - getFilesByFolder(sourceFolder) { if (!sourceFolder) return this.app.vault.getMarkdownFiles(); - const prefix = sourceFolder.replace(/\/$/, '') + '/'; - return this.app.vault.getMarkdownFiles().filter(f => f.path.startsWith(prefix) || f.parent?.path === sourceFolder); + const prefix = sourceFolder.replace(/\/$/, "") + "/"; + return this.app.vault.getMarkdownFiles().filter((f) => f.path.startsWith(prefix) || f.parent?.path === sourceFolder); } - getTemplateFileOptions() { - return this.app.vault.getMarkdownFiles().map(f => f.path); + return this.app.vault.getMarkdownFiles().map((f) => f.path); } - getFieldCandidates(sourceFolder, templateFile) { const template = templateFile ? this.app.vault.getAbstractFileByPath(templateFile) : null; - const templateKeys = (template instanceof obsidian.TFile) - ? getAllFrontmatterKeys([this.app.metadataCache.getFileCache(template)]) - : []; - + const templateKeys = template instanceof obsidian.TFile ? getAllFrontmatterKeys([this.app.metadataCache.getFileCache(template)]) : []; const files = this.getFilesByFolder(sourceFolder); - const caches = files.map(f => this.app.metadataCache.getFileCache(f)); + const caches = files.map((f) => this.app.metadataCache.getFileCache(f)); const allKeys = getAllFrontmatterKeys(caches); - const common = allKeys.filter(k => caches.every(c => !c?.frontmatter || k in c.frontmatter)); + const common = allKeys.filter((k) => caches.every((c) => !c?.frontmatter || k in c.frontmatter)); return uniqueSorted([...templateKeys, ...common]); } - getCurrentProfile() { if (this.draftProfile) return this.draftProfile; const id = this.data.lastActiveProfileId; - return this.data.profiles.find(p => p.id === id) || null; + return this.data.profiles.find((p) => p.id === id) || null; } - isDraft(id) { return !!this.draftProfile && this.draftProfile.id === id; } - setDraftProfile(profile) { - this.draftProfile = { ...profile, displayFields: [...(profile.displayFields || [])], updatedAt: Date.now() }; + this.draftProfile = { ...profile, displayFields: [...profile.displayFields || []], updatedAt: Date.now() }; } - clearDraftProfile() { this.draftProfile = null; } - async saveProfile(profile, setActive, askNameIfEmpty) { - const next = { ...profile, displayFields: [...(profile.displayFields || [])], updatedAt: Date.now() }; - if (!next.name && askNameIfEmpty) next.name = (window.prompt(this.t('pageNamePrompt'), next.sourceFolder || '') || '').trim(); - if (!next.name) next.name = next.sourceFolder || `Page-${new Date().toISOString()}`; - - const idx = this.data.profiles.findIndex(p => p.id === next.id); + const next = { ...profile, displayFields: [...profile.displayFields || []], updatedAt: Date.now() }; + if (!next.name && askNameIfEmpty) next.name = (window.prompt(this.t("pageNamePrompt"), next.sourceFolder || "") || "").trim(); + if (!next.name) next.name = next.sourceFolder || `Page-${(/* @__PURE__ */ new Date()).toISOString()}`; + const idx = this.data.profiles.findIndex((p) => p.id === next.id); if (idx >= 0) { this.data.profiles[idx] = { ...this.data.profiles[idx], ...next }; - new obsidian.Notice(this.t('pageUpdated')); + new obsidian.Notice(this.t("pageUpdated")); } else { next.createdAt = next.createdAt || Date.now(); this.data.profiles.push(next); - new obsidian.Notice(this.t('pageSaved')); + new obsidian.Notice(this.t("pageSaved")); } - if (setActive) this.data.lastActiveProfileId = next.id; if (this.draftProfile?.id === next.id) this.draftProfile = null; await this.persist(); } - normalizeExportProfile(raw) { - if (!raw || typeof raw !== 'object') return null; + if (!raw || typeof raw !== "object") return null; const profile = { id: String(raw.id || newId()), - name: String(raw.name || ''), - sourceFolder: String(raw.sourceFolder || ''), - templateFile: String(raw.templateFile || ''), - colorField: String(raw.colorField || ''), - displayFields: Array.isArray(raw.displayFields) ? raw.displayFields.map(v => String(v)).filter(Boolean) : [], - sortField: String(raw.sortField || ''), - sortOrder: raw.sortOrder === 'asc' ? 'asc' : 'desc', - viewMode: raw.viewMode === 'board' ? 'board' : 'timeline', - boardField: String(raw.boardField || ''), - createdAt: typeof raw.createdAt === 'number' ? raw.createdAt : Date.now(), - updatedAt: Date.now(), + name: String(raw.name || ""), + sourceFolder: String(raw.sourceFolder || ""), + templateFile: String(raw.templateFile || ""), + colorField: String(raw.colorField || ""), + displayFields: Array.isArray(raw.displayFields) ? raw.displayFields.map((v) => String(v)).filter(Boolean) : [], + sortField: String(raw.sortField || ""), + sortOrder: raw.sortOrder === "asc" ? "asc" : "desc", + viewMode: raw.viewMode === "board" ? "board" : "timeline", + boardField: String(raw.boardField || ""), + createdAt: typeof raw.createdAt === "number" ? raw.createdAt : Date.now(), + updatedAt: Date.now() }; return profile; } - async openProfileFromExport(raw) { const profile = this.normalizeExportProfile(raw); if (!profile) return false; await this.activateView({ profile, newLeaf: true, persistLastActive: false }); return true; } - async importProfileFromExport(raw) { const profile = this.normalizeExportProfile(raw); if (!profile) return false; - const existing = this.data.profiles.find(p => p.id === profile.id) - || this.data.profiles.find(p => p.name === profile.name && p.sourceFolder === profile.sourceFolder); + const existing = this.data.profiles.find((p) => p.id === profile.id) || this.data.profiles.find((p) => p.name === profile.name && p.sourceFolder === profile.sourceFolder); if (existing) { profile.id = existing.id; profile.createdAt = existing.createdAt || profile.createdAt; @@ -2042,65 +1817,57 @@ class SmartFolderPlugin extends obsidian.Plugin { await this.activateView({ profile, newLeaf: true }); return true; } - async exportProfileToMarkdown(profile) { if (!profile) { - new obsidian.Notice(this.t('noProfile')); + new obsidian.Notice(this.t("noProfile")); return; } try { const folders = this.getFolderOptions(); - const defaultFolder = this.app.vault.getAbstractFileByPath('00 Homepage') instanceof obsidian.TFolder ? '00 Homepage' : ''; + const defaultFolder = this.app.vault.getAbstractFileByPath("00 Homepage") instanceof obsidian.TFolder ? "00 Homepage" : ""; const chosenFolder = await askFolderSelect(this.app, this, folders, defaultFolder); if (chosenFolder == null) return; - - const safeBase = (profile.name || profile.sourceFolder || 'smart-folder-page') - .replace(/[\\/:*?"<>|]/g, '-') - .trim() || 'smart-folder-page'; - const folderPrefix = chosenFolder ? `${chosenFolder}/` : ''; + const safeBase = (profile.name || profile.sourceFolder || "smart-folder-page").replace(/[\\/:*?"<>|]/g, "-").trim() || "smart-folder-page"; + const folderPrefix = chosenFolder ? `${chosenFolder}/` : ""; let path = `${folderPrefix}${safeBase}.smart-folder-page.md`; let i = 1; while (this.app.vault.getAbstractFileByPath(path)) { path = `${folderPrefix}${safeBase}.smart-folder-page-${i}.md`; i += 1; } - const payload = { ...profile, - exportedAt: new Date().toISOString(), + exportedAt: (/* @__PURE__ */ new Date()).toISOString() }; const content = [ - '---', - 'type: smart-folder-view-page-export', - `name: ${String(profile.name || '').replace(/\n/g, ' ')}`, - `sourceFolder: ${String(profile.sourceFolder || '').replace(/\n/g, ' ')}`, - `templateFile: ${String(profile.templateFile || '').replace(/\n/g, ' ')}`, - `viewMode: ${profile.viewMode || 'timeline'}`, - `sortField: ${String(profile.sortField || '').replace(/\n/g, ' ')}`, - `sortOrder: ${profile.sortOrder || 'desc'}`, - `boardField: ${String(profile.boardField || '').replace(/\n/g, ' ')}`, - `exportedAt: ${new Date().toISOString()}`, - '---', - '', - '# Smart Folder 页面导出', - '', - '```smart-folder-page', + "---", + "type: smart-folder-view-page-export", + `name: ${String(profile.name || "").replace(/\n/g, " ")}`, + `sourceFolder: ${String(profile.sourceFolder || "").replace(/\n/g, " ")}`, + `templateFile: ${String(profile.templateFile || "").replace(/\n/g, " ")}`, + `viewMode: ${profile.viewMode || "timeline"}`, + `sortField: ${String(profile.sortField || "").replace(/\n/g, " ")}`, + `sortOrder: ${profile.sortOrder || "desc"}`, + `boardField: ${String(profile.boardField || "").replace(/\n/g, " ")}`, + `exportedAt: ${(/* @__PURE__ */ new Date()).toISOString()}`, + "---", + "", + "# Smart Folder \u9875\u9762\u5BFC\u51FA", + "", + "```smart-folder-page", JSON.stringify(payload, null, 2), - '```', - '', - ].join('\n'); - + "```", + "" + ].join("\n"); await this.app.vault.create(path, content); - new obsidian.Notice(this.t('exportPageDone', { path })); + new obsidian.Notice(this.t("exportPageDone", { path })); } catch { - new obsidian.Notice(this.t('exportPageFailed')); + new obsidian.Notice(this.t("exportPageFailed")); } } - async onload() { await this.loadPluginData(); - - this.styleEl = document.createElement('style'); + this.styleEl = document.createElement("style"); this.styleEl.textContent = ` .smart-folder-view-root { position: relative; @@ -2156,101 +1923,92 @@ class SmartFolderPlugin extends obsidian.Plugin { } `; document.head.appendChild(this.styleEl); - - this.registerView(VIEW_TYPE, leaf => new SmartFolderView(leaf, this)); - - this.registerMarkdownCodeBlockProcessor('smart-folder-page', async (source, el) => { + this.registerView(VIEW_TYPE, (leaf) => new SmartFolderView(leaf, this)); + this.registerMarkdownCodeBlockProcessor("smart-folder-page", async (source, el) => { el.empty(); let raw; try { - raw = JSON.parse(source || '{}'); + raw = JSON.parse(source || "{}"); } catch { - el.createEl('p', { text: this.t('exportBlockInvalid') }); + el.createEl("p", { text: this.t("exportBlockInvalid") }); return; } - const profile = this.normalizeExportProfile(raw); if (!profile) { - el.createEl('p', { text: this.t('exportBlockInvalid') }); + el.createEl("p", { text: this.t("exportBlockInvalid") }); return; } - if (!el.dataset.sfvAutoOpened) { - el.dataset.sfvAutoOpened = '1'; + el.dataset.sfvAutoOpened = "1"; window.setTimeout(async () => { await this.openProfileFromExport(raw); }, 0); } - - const wrap = el.createEl('div'); - wrap.style.cssText = 'border:1px solid var(--background-modifier-border);border-radius:10px;padding:10px;background:var(--background-primary-alt);'; - wrap.createEl('strong', { text: this.t('exportBlockTitle') }); - wrap.createEl('p', { text: `${this.t('exportBlockDesc')} ${profile.name || profile.sourceFolder}` }).style.cssText = 'margin:6px 0;color:var(--text-muted);'; - const row = wrap.createEl('div'); - row.style.cssText = 'display:flex;gap:8px;flex-wrap:wrap;'; - const openBtn = row.createEl('button', { text: this.t('exportBlockOpen') }); - openBtn.addClass('mod-cta'); - openBtn.addEventListener('click', async () => { + const wrap = el.createEl("div"); + wrap.style.cssText = "border:1px solid var(--background-modifier-border);border-radius:10px;padding:10px;background:var(--background-primary-alt);"; + wrap.createEl("strong", { text: this.t("exportBlockTitle") }); + wrap.createEl("p", { text: `${this.t("exportBlockDesc")} ${profile.name || profile.sourceFolder}` }).style.cssText = "margin:6px 0;color:var(--text-muted);"; + const row = wrap.createEl("div"); + row.style.cssText = "display:flex;gap:8px;flex-wrap:wrap;"; + const openBtn = row.createEl("button", { text: this.t("exportBlockOpen") }); + openBtn.addClass("mod-cta"); + openBtn.addEventListener("click", async () => { const ok = await this.openProfileFromExport(raw); - if (!ok) new obsidian.Notice(this.t('exportBlockInvalid')); + if (!ok) new obsidian.Notice(this.t("exportBlockInvalid")); }); - const importBtn = row.createEl('button', { text: this.t('exportBlockImport') }); - importBtn.addEventListener('click', async () => { + const importBtn = row.createEl("button", { text: this.t("exportBlockImport") }); + importBtn.addEventListener("click", async () => { const ok = await this.importProfileFromExport(raw); - if (!ok) new obsidian.Notice(this.t('exportBlockInvalid')); + if (!ok) new obsidian.Notice(this.t("exportBlockInvalid")); }); }); - - this.registerDomEvent(document, 'pointerdown', (ev) => { + this.registerDomEvent(document, "pointerdown", (ev) => { const target = ev.target; if (!(target instanceof Element)) return; - const openBoxes = document.querySelectorAll('.smart-folder-view-root details[open]'); + const openBoxes = document.querySelectorAll(".smart-folder-view-root details[open]"); for (const box of openBoxes) { if (box.contains(target)) continue; - box.removeAttribute('open'); + box.removeAttribute("open"); } }, true); - this.registerDomEvent(document, 'keydown', (ev) => { - if (ev.key !== 'Escape') return; - const openBoxes = document.querySelectorAll('.smart-folder-view-root details[open]'); - for (const box of openBoxes) box.removeAttribute('open'); + this.registerDomEvent(document, "keydown", (ev) => { + if (ev.key !== "Escape") return; + const openBoxes = document.querySelectorAll(".smart-folder-view-root details[open]"); + for (const box of openBoxes) box.removeAttribute("open"); }); - - this.addRibbonIcon('layout-grid', this.t('title'), async () => { + this.addRibbonIcon("layout-grid", this.t("title"), async () => { const action = await askStartAction(this.app, this); - if (action === 'open') new ProfileManagerModal(this.app, this).open(); - else if (action === 'create') new SetupModal(this.app, this).open(); + if (action === "open") new ProfileManagerModal(this.app, this).open(); + else if (action === "create") new SetupModal(this.app, this).open(); }); - this.addCommand({ id: 'open-smart-folder-view', name: this.t('openBuilder'), callback: () => new SetupModal(this.app, this).open() }); - this.addCommand({ id: 'open-smart-folder-view-builder', name: this.t('openBuilder'), callback: () => new SetupModal(this.app, this).open() }); - this.addCommand({ id: 'open-smart-folder-view-manager', name: this.t('openManager'), callback: () => new ProfileManagerModal(this.app, this).open() }); - this.addCommand({ id: 'open-smart-folder-view-last', name: this.t('openLast'), callback: async () => { + this.addCommand({ id: "open-smart-folder-view", name: this.t("openBuilder"), callback: () => new SetupModal(this.app, this).open() }); + this.addCommand({ id: "open-smart-folder-view-builder", name: this.t("openBuilder"), callback: () => new SetupModal(this.app, this).open() }); + this.addCommand({ id: "open-smart-folder-view-manager", name: this.t("openManager"), callback: () => new ProfileManagerModal(this.app, this).open() }); + this.addCommand({ id: "open-smart-folder-view-last", name: this.t("openLast"), callback: async () => { if (!this.getCurrentProfile()) { - new obsidian.Notice(this.t('noProfile')); + new obsidian.Notice(this.t("noProfile")); new SetupModal(this.app, this).open(); return; } await this.activateView({ profile: this.getCurrentProfile(), newLeaf: true }); } }); - this.addCommand({ id: 'export-smart-folder-page', name: this.t('exportPage'), callback: async () => { + this.addCommand({ id: "export-smart-folder-page", name: this.t("exportPage"), callback: async () => { const profile = this.getCurrentProfile(); if (!profile) { - new obsidian.Notice(this.t('noProfile')); + new obsidian.Notice(this.t("noProfile")); return; } await this.exportProfileToMarkdown(profile); } }); - this.addSettingTab(new SmartFolderSettingTab(this.app, this)); } - async activateView(opts = {}) { const { profile = null, newLeaf = false, persistLastActive = true } = opts; const { workspace } = this.app; let leaf = null; if (!newLeaf) leaf = workspace.getLeavesOfType(VIEW_TYPE)[0] || null; if (!leaf) { - leaf = workspace.getLeaf('tab'); + leaf = workspace.getLeaf("tab"); await leaf.setViewState({ type: VIEW_TYPE, active: true }); } workspace.revealLeaf(leaf); @@ -2264,37 +2022,29 @@ class SmartFolderPlugin extends obsidian.Plugin { await view.onOpen(); } } - onunload() { if (this.styleEl) this.styleEl.remove(); this.app.workspace.detachLeavesOfType(VIEW_TYPE); } - async loadPluginData() { const raw = await this.loadData(); this.data = { - language: raw?.language === 'zh' || raw?.language === 'en' ? raw.language : detectLanguage(), - languageMode: raw?.languageMode === 'auto' || raw?.languageMode === 'zh' || raw?.languageMode === 'en' - ? raw.languageMode - : 'auto', + language: raw?.language === "zh" || raw?.language === "en" ? raw.language : detectLanguage(), + languageMode: raw?.languageMode === "auto" || raw?.languageMode === "zh" || raw?.languageMode === "en" ? raw.languageMode : "auto", profiles: Array.isArray(raw?.profiles) ? raw.profiles : [], - lastActiveProfileId: typeof raw?.lastActiveProfileId === 'string' ? raw.lastActiveProfileId : '', + lastActiveProfileId: typeof raw?.lastActiveProfileId === "string" ? raw.lastActiveProfileId : "", colorConfig: { - saturationBase: typeof raw?.colorConfig?.saturationBase === 'number' ? clamp(raw.colorConfig.saturationBase, 20, 80) : DEFAULT_COLOR_CONFIG.saturationBase, - saturationRange: typeof raw?.colorConfig?.saturationRange === 'number' ? clamp(raw.colorConfig.saturationRange, 1, 40) : DEFAULT_COLOR_CONFIG.saturationRange, - lightnessBase: typeof raw?.colorConfig?.lightnessBase === 'number' ? clamp(raw.colorConfig.lightnessBase, 20, 70) : DEFAULT_COLOR_CONFIG.lightnessBase, - lightnessRange: typeof raw?.colorConfig?.lightnessRange === 'number' ? clamp(raw.colorConfig.lightnessRange, 1, 30) : DEFAULT_COLOR_CONFIG.lightnessRange, - fallbackColor: typeof raw?.colorConfig?.fallbackColor === 'string' && raw.colorConfig.fallbackColor.trim() - ? raw.colorConfig.fallbackColor - : DEFAULT_COLOR_CONFIG.fallbackColor, - }, + saturationBase: typeof raw?.colorConfig?.saturationBase === "number" ? clamp(raw.colorConfig.saturationBase, 20, 80) : DEFAULT_COLOR_CONFIG.saturationBase, + saturationRange: typeof raw?.colorConfig?.saturationRange === "number" ? clamp(raw.colorConfig.saturationRange, 1, 40) : DEFAULT_COLOR_CONFIG.saturationRange, + lightnessBase: typeof raw?.colorConfig?.lightnessBase === "number" ? clamp(raw.colorConfig.lightnessBase, 20, 70) : DEFAULT_COLOR_CONFIG.lightnessBase, + lightnessRange: typeof raw?.colorConfig?.lightnessRange === "number" ? clamp(raw.colorConfig.lightnessRange, 1, 30) : DEFAULT_COLOR_CONFIG.lightnessRange, + fallbackColor: typeof raw?.colorConfig?.fallbackColor === "string" && raw.colorConfig.fallbackColor.trim() ? raw.colorConfig.fallbackColor : DEFAULT_COLOR_CONFIG.fallbackColor + } }; this.draftProfile = null; } - async persist() { await this.saveData(this.data); } -} - +}; module.exports = SmartFolderPlugin; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d2952c3 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,633 @@ +{ + "name": "smart-folder-view", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "smart-folder-view", + "version": "0.1.0", + "license": "MIT", + "devDependencies": { + "esbuild": "^0.25.4", + "obsidian": "latest", + "tslib": "^2.8.1", + "typescript": "^5.8.3" + } + }, + "node_modules/@codemirror/state": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.0.tgz", + "integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.38.6", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.6.tgz", + "integrity": "sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@codemirror/state": "^6.5.0", + "crelt": "^1.0.6", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/codemirror": { + "version": "5.60.8", + "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.8.tgz", + "integrity": "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/tern": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tern": { + "version": "0.23.9", + "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz", + "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/obsidian": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.12.3.tgz", + "integrity": "sha512-HxWqe763dOqzXjnNiHmAJTRERN8KILBSqxDSEqbeSr7W8R8Jxezzbca+nz1LiiqXnMpM8lV2jzAezw3CZ4xNUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/codemirror": "5.60.8", + "moment": "2.29.4" + }, + "peerDependencies": { + "@codemirror/state": "6.5.0", + "@codemirror/view": "6.38.6" + } + }, + "node_modules/style-mod": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "dev": true, + "license": "MIT", + "peer": true + } + } +} diff --git a/src/main.ts b/src/main.ts index ca98769..e7754e3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import * as obsidian from 'obsidian'; const VIEW_TYPE = 'smart-folder-view';