diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..af8e136 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# node modules +node_modules/ + +# build artifacts +main.js +main.js.map +data.json + +# editor settings +.vscode/ +.idea/ + +# operating system files +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/esbuild.config.mjs b/esbuild.config.mjs new file mode 100644 index 0000000..7df115d --- /dev/null +++ b/esbuild.config.mjs @@ -0,0 +1,48 @@ +import esbuild from "esbuild"; +import process from "process"; +import builtins from "builtin-modules"; + +const banner = +`/* +THIS IS A GENERATED/BUNDLED FILE BY ESBUILD +if you want to view the source, please visit the github repository of this plugin +*/ +`; + +const prod = process.argv[2] === "production"; + +const context = await esbuild.context({ + banner: { + js: banner, + }, + entryPoints: ["src/main.ts"], + bundle: true, + external: [ + "obsidian", + "electron", + "@codemirror/autocomplete", + "@codemirror/collab", + "@codemirror/commands", + "@codemirror/language", + "@codemirror/lint", + "@codemirror/search", + "@codemirror/state", + "@codemirror/view", + "@lezer/common", + "@lezer/highlight", + "@lezer/lr", + ...builtins], + format: "cjs", + target: "es2018", + logLevel: "info", + sourcemap: prod ? false : "inline", + treeShaking: true, + outfile: "main.js", +}); + +if (prod) { + await context.rebuild(); + process.exit(0); +} else { + await context.watch(); +} \ No newline at end of file diff --git a/main.js b/main.js deleted file mode 100644 index d99a96d..0000000 --- a/main.js +++ /dev/null @@ -1,1466 +0,0 @@ -/* -THIS IS A GENERATED/BUNDLED FILE BY ESBUILD -if you want to view the source, please visit the github repository of this plugin -*/ - -var __defProp = Object.defineProperty; -var __getOwnPropDesc = Object.getOwnPropertyDescriptor; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __hasOwnProp = Object.prototype.hasOwnProperty; -var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { get: all[name], enumerable: true }); -}; -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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); - -// src/main.ts -var main_exports = {}; -__export(main_exports, { - default: () => DailyTaskPlugin -}); -module.exports = __toCommonJS(main_exports); -var import_obsidian4 = require("obsidian"); - -// src/models/settings.ts -var DEFAULT_TEMPLATE_ZH = `## {{dateWithIcon}}\uFF08{{weekday}}\uFF09 - -### \u{1F9D8} \u4ECA\u65E5\u8BA1\u5212 ---- - -- [ ] \u51A5\u60F3 10 \u5206\u949F -- [ ] \u590D\u76D8\u524D\u4E00\u65E5\u8BA1\u5212 -- [ ] \u9605\u8BFB 20 \u9875\u4E66 - -### \u{1F4DD} \u5DE5\u4F5C\u4EFB\u52A1 ---- - -- [ ] \u6574\u7406\u4ECA\u65E5\u5DE5\u4F5C\u8BA1\u5212 -- [ ] \u5B8C\u6210\u91CD\u8981\u9879\u76EE\u8FDB\u5EA6 -`; -var DEFAULT_TEMPLATE_EN = `## {{dateWithIcon}} ({{weekday}}) - -### \u{1F9D8} Today's Plan ---- - -- [ ] Meditate for 10 minutes -- [ ] Review yesterday's plan -- [ ] Read 20 pages - -### \u{1F4DD} Work Tasks ---- - -- [ ] Organize today's work schedule -- [ ] Progress on important projects -`; -var DEFAULT_SETTINGS = { - rootDir: "DailyTasks", - autoGenerateMode: "workday" /* WORKDAY */, - language: "auto" /* AUTO */, - templateZh: DEFAULT_TEMPLATE_ZH, - templateEn: DEFAULT_TEMPLATE_EN, - customTemplate: "", - // 默认为空,表示使用语言相关的默认模板 - hasCustomTemplate: false, - // 默认不使用自定义模板 - taskStatistics: false, - // 默认关闭任务统计功能 - successNotificationDuration: 3e3 -}; - -// src/settings/settings.ts -var import_obsidian3 = require("obsidian"); - -// src/i18n/i18n.ts -var translationsZH = { - // 设置页面 - "settings.title": "\u6BCF\u65E5\u4EFB\u52A1\u81EA\u52A8\u751F\u6210\u5668\u8BBE\u7F6E", - "settings.rootDir": "\u{1F4C1} \u4EFB\u52A1\u6587\u4EF6\u5B58\u653E\u76EE\u5F55", - "settings.rootDir.desc": '\u6307\u5B9A\u4FDD\u5B58\u4EFB\u52A1\u6587\u4EF6\u7684\u6839\u76EE\u5F55\uFF0C\u4EFB\u52A1\u5C06\u6309"\u5E74\u4EFD/\u6708\u4EFD.md"\u683C\u5F0F\u5B58\u50A8', - "settings.rootDir.saved": "\u2713 \u76EE\u5F55\u5DF2\u4FDD\u5B58", - "settings.save": "\u{1F4BE} \u4FDD\u5B58", - "settings.autoGenerate": "\u{1F504} \u81EA\u52A8\u751F\u6210\u6A21\u5F0F", - "settings.autoGenerate.desc": "\u9009\u62E9\u4F55\u65F6\u81EA\u52A8\u751F\u6210\u6BCF\u65E5\u4EFB\u52A1", - "settings.mode.none": "\u274C \u5173\u95ED", - "settings.mode.daily": "\u{1F4C6} \u6BCF\u5929", - "settings.mode.workday": "\u{1F4BC} \u4EC5\u5DE5\u4F5C\u65E5", - "settings.language": "\u{1F524} \u754C\u9762\u8BED\u8A00", - "settings.language.desc": "\u9009\u62E9\u63D2\u4EF6\u754C\u9762\u663E\u793A\u7684\u8BED\u8A00", - "settings.language.auto": "\u{1F50D} \u81EA\u52A8\u68C0\u6D4B", - "settings.language.zh": "\u{1F1E8}\u{1F1F3} \u4E2D\u6587", - "settings.language.en": "\u{1F1EC}\u{1F1E7} \u82F1\u6587", - "settings.animations": "\u2728 \u52A8\u753B\u6548\u679C", - "settings.animations.desc": "\u542F\u7528\u754C\u9762\u52A8\u753B\u6548\u679C", - "settings.template": "\u{1F4DD} \u4EFB\u52A1\u6A21\u677F", - "settings.template.zh": "\u{1F1E8}\u{1F1F3} \u4E2D\u6587\u6A21\u677F", - "settings.template.en": "\u{1F1EC}\u{1F1E7} \u82F1\u6587\u6A21\u677F", - "settings.template.preview": "\u{1F441}\uFE0F \u663E\u793A\u9884\u89C8", - "settings.template.hide": "\u{1F441}\uFE0F\u200D\u{1F5E8}\uFE0F \u9690\u85CF\u9884\u89C8", - "settings.resetToDefault": "\u{1F504} \u6062\u590D\u9ED8\u8BA4\u8BBE\u7F6E", - "settings.addTaskButton": "\u2795 \u624B\u52A8\u6DFB\u52A0\u4ECA\u65E5\u4EFB\u52A1", - "settings.notificationDuration": "\u23F1\uFE0F \u901A\u77E5\u663E\u793A\u65F6\u95F4", - "settings.notificationDuration.desc": "\u6210\u529F/\u5931\u8D25\u63D0\u793A\u663E\u793A\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09", - "settings.preview": "\u9884\u89C8\u6A21\u677F\u6548\u679C", - "settings.resetDefault": "\u6062\u590D\u9ED8\u8BA4\u8BBE\u7F6E", - "template.dateWithIcon": "\u5E26\u56FE\u6807\u7684\u5F53\u524D\u65E5\u671F", - "settings.basicSettings": "\u57FA\u672C\u8BBE\u7F6E", - "settings.templateSettings": "\u6A21\u677F\u8BBE\u7F6E", - // 通知 - "notification.taskAdded": "\u4ECA\u65E5\u4EFB\u52A1\u5DF2\u6DFB\u52A0", - "notification.taskExists": "\u4ECA\u65E5\u4EFB\u52A1\u5DF2\u5B58\u5728", - "notification.error": "\u9519\u8BEF\uFF1A", - // 星期 - "weekday.mon": "\u661F\u671F\u4E00", - "weekday.tue": "\u661F\u671F\u4E8C", - "weekday.wed": "\u661F\u671F\u4E09", - "weekday.thu": "\u661F\u671F\u56DB", - "weekday.fri": "\u661F\u671F\u4E94", - "weekday.sat": "\u661F\u671F\u516D", - "weekday.sun": "\u661F\u671F\u65E5", - // 插件名称和描述 - "plugin.name": "\u6BCF\u65E5\u4EFB\u52A1\u81EA\u52A8\u751F\u6210\u5668", - "plugin.description": "\u4E00\u4E2A\u5F3A\u5927\u7684\u4EFB\u52A1\u81EA\u52A8\u751F\u6210\u5668\uFF0C\u5E2E\u52A9\u4F60\u9AD8\u6548\u5730\u7BA1\u7406\u65E5\u5E38\u4EFB\u52A1", - // 一般按钮和消息 - "button.addTask": "\u6DFB\u52A0\u4EFB\u52A1", - "button.save": "\u4FDD\u5B58", - "button.cancel": "\u53D6\u6D88", - "button.done": "\u5B8C\u6210", - // 添加任务统计相关翻译 - "settings.taskStatistics": "\u{1F4CA} \u4EFB\u52A1\u5B8C\u6210\u7EDF\u8BA1", - "settings.taskStatistics.desc": "\u5F00\u542F\u540E\uFF0C\u6BCF\u65E5\u751F\u6210\u4EFB\u52A1\u524D\u4F1A\u81EA\u52A8\u7EDF\u8BA1\u524D\u4E00\u5929\u7684\u4EFB\u52A1\u5B8C\u6210\u60C5\u51B5", - "statistics.title": "\u{1F4CA} \u6628\u65E5\u4EFB\u52A1\u7EDF\u8BA1", - "statistics.totalTasks": "\u4EFB\u52A1\u603B\u6570", - "statistics.completedTasks": "\u5DF2\u5B8C\u6210\u4EFB\u52A1", - "statistics.completionRate": "\u5B8C\u6210\u7387", - "statistics.unfinishedTasks": "\u672A\u5B8C\u6210\u7684\u4EFB\u52A1", - "statistics.suggestions": "\u5EFA\u8BAE\u4ECA\u65E5\u8003\u8651\u5B8C\u6210\u4EE5\u4E0B\u4EFB\u52A1", - "statistics.moreTasks.singular": "\u8FD8\u67091\u4E2A\u672A\u5B8C\u6210\u4EFB\u52A1", - "statistics.moreTasks.plural": "\u8FD8\u6709\u66F4\u591A\u672A\u5B8C\u6210\u4EFB\u52A1" -}; -var translationsEN = { - // 设置页面 - "settings.title": "Daily Task Auto Generator Settings", - "settings.rootDir": "\u{1F4C1} Task Directory", - "settings.rootDir.desc": 'Specify the root directory for storing task files, tasks will be stored in "Year/Month.md" format', - "settings.rootDir.saved": "\u2713 Directory saved", - "settings.save": "\u{1F4BE} Save", - "settings.autoGenerate": "\u{1F504} Auto Generate Mode", - "settings.autoGenerate.desc": "Choose when to automatically generate daily tasks", - "settings.mode.none": "\u274C Off", - "settings.mode.daily": "\u{1F4C6} Daily", - "settings.mode.workday": "\u{1F4BC} Workdays Only", - "settings.language": "\u{1F524} Interface Language", - "settings.language.desc": "Select the language for the plugin interface", - "settings.language.auto": "\u{1F50D} Auto Detect", - "settings.language.zh": "\u{1F1E8}\u{1F1F3} Chinese", - "settings.language.en": "\u{1F1EC}\u{1F1E7} English", - "settings.animations": "\u2728 Animation Effects", - "settings.animations.desc": "Enable interface animation effects", - "settings.template": "\u{1F4DD} Task Template", - "settings.template.zh": "\u{1F1E8}\u{1F1F3} Chinese Template", - "settings.template.en": "\u{1F1EC}\u{1F1E7} English Template", - "settings.template.preview": "\u{1F441}\uFE0F Show Preview", - "settings.template.hide": "\u{1F441}\uFE0F\u200D\u{1F5E8}\uFE0F Hide Preview", - "settings.resetToDefault": "\u{1F504} Reset to Default", - "settings.addTaskButton": "\u2795 Add Today's Task Manually", - "settings.notificationDuration": "\u23F1\uFE0F Notification Duration", - "settings.notificationDuration.desc": "Duration to show success/failure notifications (milliseconds)", - "settings.preview": "Preview Template", - "settings.resetDefault": "Reset to Default", - "template.dateWithIcon": "Current date with icon", - "settings.basicSettings": "Basic Settings", - "settings.templateSettings": "Template Settings", - // 通知 - "notification.taskAdded": "Today's task has been added", - "notification.taskExists": "Today's task already exists", - "notification.error": "Error: ", - // 星期 - "weekday.mon": "Monday", - "weekday.tue": "Tuesday", - "weekday.wed": "Wednesday", - "weekday.thu": "Thursday", - "weekday.fri": "Friday", - "weekday.sat": "Saturday", - "weekday.sun": "Sunday", - // 插件名称和描述 - "plugin.name": "Daily Task Auto Generator", - "plugin.description": "A powerful task auto generator to help you efficiently manage daily tasks", - // 一般按钮和消息 - "button.addTask": "Add Task", - "button.save": "Save", - "button.cancel": "Cancel", - "button.done": "Done", - // 添加任务统计相关翻译 - "settings.taskStatistics": "\u{1F4CA} Task Completion Statistics", - "settings.taskStatistics.desc": "When enabled, automatically analyze yesterday's task completion before generating today's tasks", - "statistics.title": "\u{1F4CA} Yesterday's Task Summary", - "statistics.totalTasks": "Total Tasks", - "statistics.completedTasks": "Completed Tasks", - "statistics.completionRate": "Completion Rate", - "statistics.unfinishedTasks": "Unfinished Tasks", - "statistics.suggestions": "Consider completing the following tasks today", - "statistics.moreTasks.singular": "more task", - "statistics.moreTasks.plural": "more tasks" -}; -var translations = { - "zh": translationsZH, - "en": translationsEN -}; -var currentLanguage = "en"; -function setCurrentLanguage(language) { - currentLanguage = language; -} -function getTranslation(key, fallbackLanguage) { - var _a; - const language = fallbackLanguage || currentLanguage; - const translation = (_a = translations[language]) == null ? void 0 : _a[key]; - if (!translation) { - return translations["en"][key] || key; - } - return translation; -} -function getLocalizedWeekday(dayOfWeek) { - const weekdayKeys = [ - "weekday.sun", - "weekday.mon", - "weekday.tue", - "weekday.wed", - "weekday.thu", - "weekday.fri", - "weekday.sat" - ]; - const normalizedDayOfWeek = (dayOfWeek % 7 + 7) % 7; - return getTranslation(weekdayKeys[normalizedDayOfWeek]); -} - -// src/utils/dateUtils.ts -function getCurrentYear() { - return new Date().getFullYear().toString(); -} -function getCurrentMonth() { - const month = (new Date().getMonth() + 1).toString(); - return month.padStart(2, "0"); -} -function getLocalizedMonthName(isEnglish = false) { - const monthIndex = new Date().getMonth(); - const chineseMonths = [ - "1\u6708", - "2\u6708", - "3\u6708", - "4\u6708", - "5\u6708", - "6\u6708", - "7\u6708", - "8\u6708", - "9\u6708", - "10\u6708", - "11\u6708", - "12\u6708" - ]; - const englishMonths = [ - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December" - ]; - return isEnglish ? englishMonths[monthIndex] : chineseMonths[monthIndex]; -} -function getDayIcon() { - const day = new Date().getDate(); - const dayIcons = [ - "\u{1F311}", - // 1日 - "\u{1F312}", - // 2日 - "\u{1F313}", - // 3日 - "\u{1F314}", - // 4日 - "\u{1F315}", - // 5日 - "\u{1F316}", - // 6日 - "\u{1F317}", - // 7日 - "\u{1F318}", - // 8日 - "\u{1F31F}", - // 9日 - "\u2B50", - // 10日 - "\u{1F308}", - // 11日 - "\u{1F31E}", - // 12日 - "\u{1F324}\uFE0F", - // 13日 - "\u26C5", - // 14日 - "\u{1F326}\uFE0F", - // 15日 - "\u{1F327}\uFE0F", - // 16日 - "\u26C8\uFE0F", - // 17日 - "\u{1F329}\uFE0F", - // 18日 - "\u{1F32A}\uFE0F", - // 19日 - "\u{1F32B}\uFE0F", - // 20日 - "\u{1F32C}\uFE0F", - // 21日 - "\u{1F340}", - // 22日 - "\u{1F331}", - // 23日 - "\u{1F332}", - // 24日 - "\u{1F333}", - // 25日 - "\u{1F334}", - // 26日 - "\u{1F335}", - // 27日 - "\u{1F33A}", - // 28日 - "\u{1F33B}", - // 29日 - "\u{1F33C}", - // 30日 - "\u{1F338}" - // 31日 - ]; - return dayIcons[day - 1] || "\u{1F4C5}"; -} -function isEnglishEnvironment() { - const mondayText = getTranslation("weekday.mon"); - return mondayText === "Monday"; -} -function getCurrentDay() { - const day = new Date().getDate().toString(); - return day.padStart(2, "0"); -} -function getCurrentDate() { - return `${getCurrentYear()}-${getCurrentMonth()}-${getCurrentDay()}`; -} -function getCurrentDateWithIcon() { - const icon = getDayIcon(); - return `${icon} ${getCurrentDate()}`; -} -function isWorkday() { - const day = new Date().getDay(); - return day >= 1 && day <= 5; -} -function getCurrentWeekdayName() { - const day = new Date().getDay(); - return getLocalizedWeekday(day); -} -function getYearProgress() { - const now = new Date(); - const start = new Date(now.getFullYear(), 0, 1); - const end = new Date(now.getFullYear() + 1, 0, 1); - const totalDays = (end.getTime() - start.getTime()) / (1e3 * 60 * 60 * 24); - const passedDays = (now.getTime() - start.getTime()) / (1e3 * 60 * 60 * 24); - return Math.round(passedDays / totalDays * 100); -} -function getMonthProgress() { - const now = new Date(); - const start = new Date(now.getFullYear(), now.getMonth(), 1); - const end = new Date(now.getFullYear(), now.getMonth() + 1, 1); - const totalDays = (end.getTime() - start.getTime()) / (1e3 * 60 * 60 * 24); - const passedDays = (now.getTime() - start.getTime()) / (1e3 * 60 * 60 * 24); - return Math.round(passedDays / totalDays * 100); -} - -// src/utils/templateEngine.ts -function getCurrentTime() { - const now = new Date(); - const hours = now.getHours().toString().padStart(2, "0"); - const minutes = now.getMinutes().toString().padStart(2, "0"); - return `${hours}:${minutes}`; -} -function renderTemplate(template) { - const variableMap = { - "date": getCurrentDate(), - "dateWithIcon": getCurrentDateWithIcon(), - "weekday": getCurrentWeekdayName(), - "yearProgress": getYearProgress(), - "monthProgress": getMonthProgress(), - "time": getCurrentTime() - }; - let renderedContent = template; - for (const [variable, value] of Object.entries(variableMap)) { - renderedContent = renderedContent.replace( - new RegExp(`{{${variable}}}`, "g"), - value.toString() - ); - } - return renderedContent; -} - -// src/taskGenerator.ts -var import_obsidian2 = require("obsidian"); - -// src/utils/fileUtils.ts -var import_obsidian = require("obsidian"); -async function ensureFolderExists(vault, path) { - try { - path = path.endsWith("/") ? path : path + "/"; - const folders = path.split("/").filter((p) => p.length > 0); - let currentPath = ""; - for (const folder of folders) { - if (currentPath) { - currentPath += "/" + folder; - } else { - currentPath = folder; - } - console.log(`\u68C0\u67E5\u76EE\u5F55: ${currentPath}`); - const existingItem = vault.getAbstractFileByPath(currentPath); - if (!existingItem) { - console.log(`\u76EE\u5F55\u4E0D\u5B58\u5728\uFF0C\u6B63\u5728\u521B\u5EFA: ${currentPath}`); - try { - await vault.createFolder(currentPath); - console.log(`\u76EE\u5F55\u521B\u5EFA\u6210\u529F: ${currentPath}`); - } catch (e) { - console.log(`\u521B\u5EFA\u76EE\u5F55\u65F6\u51FA\u73B0\u5F02\u5E38\uFF0C\u53EF\u80FD\u5DF2\u88AB\u5176\u4ED6\u8FDB\u7A0B\u521B\u5EFA: ${e}`); - const folderAfterError = vault.getAbstractFileByPath(currentPath); - if (!folderAfterError || !(folderAfterError instanceof import_obsidian.TFolder)) { - console.error(`\u76EE\u5F55\u521B\u5EFA\u5931\u8D25\uFF0C\u4E14\u65E0\u6CD5\u786E\u8BA4\u76EE\u5F55\u5DF2\u5B58\u5728: ${currentPath}`); - return false; - } - } - } else if (!(existingItem instanceof import_obsidian.TFolder)) { - console.error(`\u8DEF\u5F84 ${currentPath} \u5DF2\u5B58\u5728\u4F46\u4E0D\u662F\u6587\u4EF6\u5939\uFF0C\u800C\u662F: ${existingItem.constructor.name}`); - return false; - } else { - console.log(`\u76EE\u5F55\u5DF2\u5B58\u5728\uFF0C\u65E0\u9700\u521B\u5EFA: ${currentPath}`); - } - } - return true; - } catch (error) { - console.error(`\u521B\u5EFA\u76EE\u5F55\u65F6\u51FA\u73B0\u672A\u9884\u671F\u9519\u8BEF(${path}):`, error); - return false; - } -} -async function ensureFileExists(vault, path, content = "") { - try { - const existingItem = vault.getAbstractFileByPath(path); - if (existingItem) { - if (existingItem instanceof import_obsidian.TFile) { - console.log(`\u6587\u4EF6\u5DF2\u5B58\u5728\uFF0C\u65E0\u9700\u521B\u5EFA: ${path}`); - return true; - } else { - console.error(`\u8DEF\u5F84 ${path} \u5B58\u5728\u4F46\u4E0D\u662F\u6587\u4EF6\uFF0C\u65E0\u6CD5\u521B\u5EFA\u6587\u4EF6\uFF0C\u53EF\u80FD\u662F\u540C\u540D\u76EE\u5F55`); - return false; - } - } - const lastSlashIndex = path.lastIndexOf("/"); - if (lastSlashIndex > 0) { - const parentPath = path.substring(0, lastSlashIndex); - console.log(`\u68C0\u67E5\u6587\u4EF6\u7236\u76EE\u5F55: ${parentPath}`); - const parentExists = await ensureFolderExists(vault, parentPath); - if (!parentExists) { - console.error(`\u65E0\u6CD5\u786E\u4FDD\u7236\u76EE\u5F55\u5B58\u5728: ${parentPath}`); - return false; - } - } - try { - console.log(`\u5F00\u59CB\u521B\u5EFA\u6587\u4EF6: ${path}`); - await vault.create(path, content); - console.log(`\u6587\u4EF6\u521B\u5EFA\u6210\u529F: ${path}`); - return true; - } catch (e) { - console.log(`\u521B\u5EFA\u6587\u4EF6\u65F6\u51FA\u73B0\u5F02\u5E38: ${e}`); - const fileAfterError = vault.getAbstractFileByPath(path); - if (fileAfterError && fileAfterError instanceof import_obsidian.TFile) { - console.log(`\u5C3D\u7BA1\u51FA\u73B0\u5F02\u5E38\uFF0C\u4F46\u6587\u4EF6\u5DF2\u5B58\u5728\uFF0C\u53EF\u80FD\u88AB\u5176\u4ED6\u8FDB\u7A0B\u521B\u5EFA: ${path}`); - return true; - } - console.error(`\u6587\u4EF6\u521B\u5EFA\u6700\u7EC8\u5931\u8D25: ${path}`); - return false; - } - } catch (error) { - console.error(`\u521B\u5EFA\u6587\u4EF6\u65F6\u51FA\u73B0\u672A\u9884\u671F\u9519\u8BEF(${path}):`, error); - return false; - } -} -async function appendToFile(vault, path, content) { - try { - const file = vault.getAbstractFileByPath(path); - if (file && file instanceof import_obsidian.TFile) { - try { - const currentContent = await vault.read(file); - await vault.modify(file, currentContent + "\n\n" + content); - return true; - } catch (e) { - console.error(`\u8BFB\u53D6\u6216\u4FEE\u6539\u6587\u4EF6\u65F6\u51FA\u9519: ${e}`); - return false; - } - } else { - console.log(`\u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u5C1D\u8BD5\u521B\u5EFA: ${path}`); - return await ensureFileExists(vault, path, content); - } - } catch (error) { - console.error(`Error appending to file at ${path}:`, error); - return false; - } -} -function getTaskFilePath(rootDir) { - const year = getCurrentYear(); - const isEnglish = isEnglishEnvironment(); - const monthName = getLocalizedMonthName(isEnglish); - return (0, import_obsidian.normalizePath)(`${rootDir}/${year}/${monthName}.md`); -} -async function todayTaskExists(vault, rootDir) { - const taskFilePath = getTaskFilePath(rootDir); - const date = getCurrentDate(); - try { - console.log(`\u68C0\u67E5\u4ECA\u65E5\u4EFB\u52A1\u662F\u5426\u5B58\u5728\u4E8E: ${taskFilePath}`); - const file = vault.getAbstractFileByPath(taskFilePath); - if (file && file instanceof import_obsidian.TFile) { - const content = await vault.read(file); - const dateRegex = new RegExp(`## [^\\n]*${date}[^\\n]*\\n`); - return dateRegex.test(content); - } - return false; - } catch (error) { - console.error(`\u68C0\u67E5\u4ECA\u65E5\u4EFB\u52A1\u65F6\u51FA\u9519:`, error); - return false; - } -} -function getYesterdayDate() { - const today = new Date(); - const yesterday = new Date(today); - yesterday.setDate(today.getDate() - 1); - const year = yesterday.getFullYear(); - const month = (yesterday.getMonth() + 1).toString().padStart(2, "0"); - const day = yesterday.getDate().toString().padStart(2, "0"); - return `${year}-${month}-${day}`; -} -function getYesterdayTaskFilePath(rootDir) { - const yesterday = new Date(); - yesterday.setDate(yesterday.getDate() - 1); - const year = yesterday.getFullYear().toString(); - const isEnglish = isEnglishEnvironment(); - const month = new Date().getMonth(); - const yesterdayMonth = yesterday.getMonth(); - const tempDate = new Date(); - tempDate.setMonth(yesterdayMonth); - const monthName = isEnglish ? getMonthNameEN(yesterdayMonth) : getMonthNameZH(yesterdayMonth); - return (0, import_obsidian.normalizePath)(`${rootDir}/${year}/${monthName}.md`); -} -function getMonthNameZH(monthIndex) { - const months = [ - "1\u6708", - "2\u6708", - "3\u6708", - "4\u6708", - "5\u6708", - "6\u6708", - "7\u6708", - "8\u6708", - "9\u6708", - "10\u6708", - "11\u6708", - "12\u6708" - ]; - return months[monthIndex]; -} -function getMonthNameEN(monthIndex) { - const months = [ - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December" - ]; - return months[monthIndex]; -} -async function extractTasksForDate(vault, filePath, date) { - try { - const file = vault.getAbstractFileByPath(filePath); - if (!file || !(file instanceof import_obsidian.TFile)) { - console.log(`\u627E\u4E0D\u5230\u6587\u4EF6: ${filePath}`); - return null; - } - const content = await vault.read(file); - const dateHeaderRegex = new RegExp(`## [^\\n]*${date}[^\\n]*\\n(.*?)(?=\\n## |$)`, "s"); - const match = content.match(dateHeaderRegex); - if (match && match[1]) { - return match[1].trim(); - } - console.log(`\u627E\u4E0D\u5230\u65E5\u671F ${date} \u7684\u4EFB\u52A1\u5185\u5BB9`); - return null; - } catch (error) { - console.error(`\u63D0\u53D6\u4EFB\u52A1\u5185\u5BB9\u65F6\u51FA\u9519: ${error}`); - return null; - } -} -function analyzeTaskCompletion(taskContent) { - const result = { - totalTasks: 0, - completedTasks: 0, - unfinishedTasksList: [] - }; - if (!taskContent) { - return result; - } - const allTasksRegex = /- \[([ x])\] (.+)$/gm; - const completedTasksRegex = /- \[x\] (.+)$/gm; - const unfinishedTasksRegex = /- \[ \] (.+)$/gm; - const allTasksMatches = [...taskContent.matchAll(allTasksRegex)]; - result.totalTasks = allTasksMatches.length; - const completedTasksMatches = [...taskContent.matchAll(completedTasksRegex)]; - result.completedTasks = completedTasksMatches.length; - const unfinishedTasksMatches = [...taskContent.matchAll(unfinishedTasksRegex)]; - result.unfinishedTasksList = unfinishedTasksMatches.map((match) => match[1].trim()); - return result; -} -async function yesterdayStatisticsExists(vault, filePath, date) { - try { - const file = vault.getAbstractFileByPath(filePath); - if (!file || !(file instanceof import_obsidian.TFile)) { - console.log(`\u627E\u4E0D\u5230\u6587\u4EF6: ${filePath}`); - return false; - } - const content = await vault.read(file); - const statisticsTitleRegex = new RegExp(`## [^\\n]*${date}[^\\n]*\\n.*?${getTranslation("statistics.title")}`, "s"); - return statisticsTitleRegex.test(content); - } catch (error) { - console.error(`\u68C0\u67E5\u6628\u65E5\u7EDF\u8BA1\u4FE1\u606F\u65F6\u51FA\u9519: ${error}`); - return false; - } -} -function generateStatisticsContent(tasks, t) { - let totalTasks; - let completedTasks; - let unfinishedTasks = []; - if (Array.isArray(tasks)) { - totalTasks = tasks.length; - completedTasks = tasks.filter((task) => task.isCompleted).length; - unfinishedTasks = tasks.filter((task) => !task.isCompleted).map((task) => ({ task: task.task })); - } else { - totalTasks = tasks.totalTasks; - completedTasks = tasks.completedTasks; - unfinishedTasks = tasks.unfinishedTasksList.map((task) => ({ task })); - } - const completionRate = totalTasks > 0 ? Math.round(completedTasks / totalTasks * 100) : 0; - const displayTasks = unfinishedTasks.slice(0, 5); - let content = `## ${t("statistics.title")} ---- -`; - content += `- ${t("statistics.totalTasks")}: ${totalTasks} -`; - content += `- ${t("statistics.completedTasks")}: ${completedTasks} -`; - content += `- ${t("statistics.completionRate")}: ${completionRate}% - -`; - if (unfinishedTasks.length > 0) { - content += `### ${t("statistics.suggestions")} ---- -`; - displayTasks.forEach((item) => { - content += `- [ ] ${item.task} -`; - }); - if (unfinishedTasks.length > 5) { - const remaining = unfinishedTasks.length - 5; - const moreTasks = remaining === 1 ? t("statistics.moreTasks.singular") : `${remaining} ${t("statistics.moreTasks.plural")}`; - content += `- *${moreTasks}* -`; - } - } - return content; -} - -// src/taskGenerator.ts -var TaskGenerator = class { - constructor(app, settingsManager) { - this.app = app; - this.vault = app.vault; - this.settingsManager = settingsManager; - } - /** - * 生成每日任务 - * @param openFile 是否打开文件 - * @param quietMode 静默模式,减少日志输出 - * @returns 成功或失败 - */ - async generateDailyTask(openFile = true, quietMode = false) { - try { - const settings = this.settingsManager.getSettings(); - const rootDir = settings.rootDir.trim() || "DailyTasks"; - const filePath = getTaskFilePath(rootDir); - if (!quietMode) - console.log(`\u751F\u6210\u4EFB\u52A1\u6587\u4EF6\u8DEF\u5F84: ${filePath}`); - const pathParts = filePath.split("/"); - const year = pathParts.length > 1 ? pathParts[1] : ""; - const monthName = pathParts.length > 2 ? pathParts[2].replace(".md", "") : ""; - const yearFolder = `${rootDir}/${year}`; - if (!quietMode) - console.log(`\u6B63\u5728\u786E\u4FDD\u6839\u76EE\u5F55\u5B58\u5728: ${rootDir}`); - const rootCreated = await ensureFolderExists(this.vault, rootDir); - if (!rootCreated) { - console.error(`\u65E0\u6CD5\u8BBF\u95EE\u6216\u521B\u5EFA\u6839\u76EE\u5F55: ${rootDir}`); - throw new Error(`\u65E0\u6CD5\u8BBF\u95EE\u6839\u76EE\u5F55: ${rootDir}\uFF0C\u53EF\u80FD\u662F\u5B58\u5728\u540C\u540D\u6587\u4EF6\u6216\u6743\u9650\u95EE\u9898`); - } - if (!quietMode) - console.log(`\u6839\u76EE\u5F55\u786E\u8BA4: ${rootDir}`); - if (!quietMode) - console.log(`\u6B63\u5728\u786E\u4FDD\u5E74\u4EFD\u76EE\u5F55\u5B58\u5728: ${yearFolder}`); - const yearCreated = await ensureFolderExists(this.vault, yearFolder); - if (!yearCreated) { - console.error(`\u65E0\u6CD5\u8BBF\u95EE\u6216\u521B\u5EFA\u5E74\u4EFD\u76EE\u5F55: ${yearFolder}`); - throw new Error(`\u65E0\u6CD5\u8BBF\u95EE\u5E74\u4EFD\u76EE\u5F55: ${yearFolder}\uFF0C\u53EF\u80FD\u662F\u5B58\u5728\u540C\u540D\u6587\u4EF6\u6216\u6743\u9650\u95EE\u9898`); - } - if (!quietMode) - console.log(`\u5E74\u4EFD\u76EE\u5F55\u786E\u8BA4: ${yearFolder}`); - if (!quietMode) - console.log(`\u6B63\u5728\u786E\u4FDD\u6708\u4EFD\u6587\u4EF6\u5B58\u5728: ${filePath} (${monthName})`); - const fileCreated = await ensureFileExists(this.vault, filePath); - if (!fileCreated) { - console.error(`\u65E0\u6CD5\u8BBF\u95EE\u6216\u521B\u5EFA\u6708\u4EFD\u6587\u4EF6: ${filePath}`); - throw new Error(`\u65E0\u6CD5\u8BBF\u95EE\u6708\u4EFD\u6587\u4EF6: ${filePath}\uFF0C\u8BF7\u68C0\u67E5\u662F\u5426\u5B58\u5728\u540C\u540D\u76EE\u5F55\u6216\u6743\u9650\u95EE\u9898`); - } - if (!quietMode) - console.log(`\u6708\u4EFD\u6587\u4EF6\u786E\u8BA4: ${filePath}`); - const date = getCurrentDate(); - const dateRegex = new RegExp(`## [^\\n]*${date}[^\\n]*\\n`); - const fileContent = await this.vault.read(this.vault.getAbstractFileByPath(filePath)); - const existingTaskCheck = dateRegex.test(fileContent); - if (existingTaskCheck) { - console.log(`\u4ECA\u65E5(${date})\u4EFB\u52A1\u5DF2\u5B58\u5728\u4E8E\u6587\u4EF6\u4E2D\uFF0C\u8DF3\u8FC7\u521B\u5EFA`); - if (openFile) { - this.showWarningNotice(`\u{1F4CC} ${getTranslation("notification.taskExists")}`); - const file = this.vault.getAbstractFileByPath(filePath); - if (file && file instanceof import_obsidian2.TFile) { - const leaf = this.app.workspace.getLeaf(); - await leaf.openFile(file); - } - } - return true; - } - let statisticsContent = ""; - if (settings.taskStatistics) { - statisticsContent = await this.generateYesterdayStatistics(rootDir, date, quietMode); - } - let template = ""; - if (this.settingsManager.hasCustomTemplate()) { - template = this.settingsManager.getSettings().customTemplate; - } else { - template = this.settingsManager.getTemplateByLanguage(); - } - const renderedContent = renderTemplate(template); - const fullContent = statisticsContent ? `${renderedContent} - -${statisticsContent}` : renderedContent; - if (!quietMode) - console.log(`\u6B63\u5728\u5411\u6587\u4EF6\u8FFD\u52A0\u5185\u5BB9`); - const success = await appendToFile(this.vault, filePath, fullContent); - if (success) { - console.log(`\u2705 \u4EFB\u52A1\u5185\u5BB9\u8FFD\u52A0\u6210\u529F ${date}`); - if (openFile) { - const file = this.vault.getAbstractFileByPath(filePath); - if (file && file instanceof import_obsidian2.TFile) { - const leaf = this.app.workspace.getLeaf(); - await leaf.openFile(file); - setTimeout(() => { - this.showSuccessNotice(`\u2728 ${getTranslation("notification.taskAdded")}`); - }, 300); - } else { - throw new Error(`\u6587\u4EF6\u521B\u5EFA\u6210\u529F\u4F46\u65E0\u6CD5\u6253\u5F00: ${filePath}`); - } - } else { - console.log(`\u2728 \u4ECA\u65E5(${date})\u4EFB\u52A1\u5DF2\u9759\u9ED8\u6DFB\u52A0\uFF0C\u65E0\u9700\u6253\u5F00\u6587\u4EF6`); - } - } else { - throw new Error(`\u65E0\u6CD5\u5411\u6587\u4EF6\u8FFD\u52A0\u5185\u5BB9: ${filePath}`); - } - return success; - } catch (error) { - console.error("Error generating daily task:", error); - const errorMsg = error instanceof Error ? error.message : String(error); - this.showErrorNotice(`${getTranslation("notification.error")} ${errorMsg}`); - return false; - } - } - /** - * 生成昨日任务统计信息 - * @param rootDir 根目录 - * @param todayDate 今日日期(用于检查是否已存在统计) - * @param quietMode 静默模式 - * @returns 统计信息内容或空字符串 - */ - async generateYesterdayStatistics(rootDir, todayDate, quietMode = false) { - try { - const yesterdayDate = getYesterdayDate(); - const yesterdayFilePath = getYesterdayTaskFilePath(rootDir); - if (!quietMode) - console.log(`\u6B63\u5728\u83B7\u53D6\u6628\u65E5(${yesterdayDate})\u4EFB\u52A1\u7EDF\u8BA1\uFF0C\u6587\u4EF6\u8DEF\u5F84: ${yesterdayFilePath}`); - const todayFilePath = getTaskFilePath(rootDir); - const statsExists = await yesterdayStatisticsExists(this.vault, todayFilePath, todayDate); - if (statsExists) { - if (!quietMode) - console.log("\u6628\u65E5\u7EDF\u8BA1\u4FE1\u606F\u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u751F\u6210"); - return ""; - } - const yesterdayContent = await extractTasksForDate(this.vault, yesterdayFilePath, yesterdayDate); - if (!yesterdayContent) { - if (!quietMode) - console.log(`\u627E\u4E0D\u5230\u6628\u65E5(${yesterdayDate})\u4EFB\u52A1\u5185\u5BB9\uFF0C\u8DF3\u8FC7\u7EDF\u8BA1`); - return ""; - } - const taskStats = analyzeTaskCompletion(yesterdayContent); - if (!quietMode) { - console.log(`\u6628\u65E5\u4EFB\u52A1\u7EDF\u8BA1: \u603B\u6570=${taskStats.totalTasks}, \u5DF2\u5B8C\u6210=${taskStats.completedTasks}`); - console.log(`\u672A\u5B8C\u6210\u4EFB\u52A1: ${taskStats.unfinishedTasksList.length}\u4E2A`); - } - if (taskStats.totalTasks === 0) { - if (!quietMode) - console.log("\u6628\u65E5\u6CA1\u6709\u4EFB\u52A1\uFF0C\u8DF3\u8FC7\u7EDF\u8BA1"); - return ""; - } - return generateStatisticsContent(taskStats, getTranslation); - } catch (error) { - console.error(`\u751F\u6210\u6628\u65E5\u7EDF\u8BA1\u4FE1\u606F\u65F6\u51FA\u9519:`, error); - return ""; - } - } - /** - * 手动添加今日任务 - * @returns 成功或失败 - */ - async addTaskManually() { - try { - const settings = this.settingsManager.getSettings(); - const rootDir = settings.rootDir.trim() || "DailyTasks"; - const exists = await todayTaskExists(this.vault, rootDir); - if (exists) { - this.showWarningNotice(`\u{1F4CC} ${getTranslation("notification.taskExists")}`); - return false; - } - return await this.generateDailyTask(); - } catch (error) { - console.error("Error adding task manually:", error); - const errorMsg = error instanceof Error ? error.message : String(error); - this.showErrorNotice(`${getTranslation("notification.error")} ${errorMsg}`); - return false; - } - } - /** - * 显示成功通知 - * @param message 通知内容 - */ - showSuccessNotice(message) { - const notice = new import_obsidian2.Notice( - message, - this.settingsManager.getSettings().successNotificationDuration - ); - if (notice.noticeEl) { - notice.noticeEl.addClass("daily-task-success-notice"); - } - } - /** - * 显示警告通知 - * @param message 通知内容 - */ - showWarningNotice(message) { - const notice = new import_obsidian2.Notice( - message, - this.settingsManager.getSettings().successNotificationDuration - ); - if (notice.noticeEl) { - notice.noticeEl.addClass("daily-task-warning-notice"); - } - } - /** - * 显示错误通知 - * @param message 通知内容 - */ - showErrorNotice(message) { - const notice = new import_obsidian2.Notice( - message, - this.settingsManager.getSettings().successNotificationDuration - ); - if (notice.noticeEl) { - notice.noticeEl.addClass("daily-task-error-notice"); - } - } -}; - -// src/settings/settings.ts -var TextCenterCSS = "daily-task-text-center"; -var DailyTaskSettingTab = class extends import_obsidian3.PluginSettingTab { - constructor(app, plugin) { - super(app, plugin); - this.previewEl = null; - this.addTaskButton = null; - // 目录输入框 - this.rootDirInput = null; - // 标记设置是否已修改但未保存 - this.dirtySettings = false; - this.plugin = plugin; - this.settingsManager = plugin.settingsManager; - this.taskGenerator = new TaskGenerator(app, this.settingsManager); - } - display() { - const { containerEl } = this; - containerEl.empty(); - containerEl.classList.add("daily-task-setting-tab"); - const settings = this.settingsManager.getSettings(); - const topSpacing = containerEl.createEl("div"); - topSpacing.style.marginTop = "30px"; - const rootDirSetting = new import_obsidian3.Setting(containerEl).setName(getTranslation("settings.rootDir")).setDesc(getTranslation("settings.rootDir.desc")); - const inputContainer = document.createElement("div"); - inputContainer.style.display = "flex"; - inputContainer.style.width = "100%"; - inputContainer.style.position = "relative"; - rootDirSetting.controlEl.appendChild(inputContainer); - this.rootDirInput = new import_obsidian3.TextComponent(inputContainer).setValue(settings.rootDir).onChange(async (value) => { - if (value.trim() !== "") { - this.dirtySettings = true; - this.autoSaveRootDir(value); - } - }); - if (this.rootDirInput && this.rootDirInput.inputEl) { - this.rootDirInput.inputEl.placeholder = "DailyTasks"; - this.rootDirInput.inputEl.style.width = "100%"; - this.rootDirInput.inputEl.style.borderRadius = "4px"; - this.rootDirInput.inputEl.style.padding = "8px 35px 8px 10px"; - this.rootDirInput.inputEl.style.transition = "all 0.3s ease"; - } - const saveIndicator = document.createElement("div"); - saveIndicator.classList.add("save-indicator"); - saveIndicator.style.position = "absolute"; - saveIndicator.style.right = "10px"; - saveIndicator.style.top = "50%"; - saveIndicator.style.transform = "translateY(-50%)"; - saveIndicator.style.opacity = "0"; - saveIndicator.style.transition = "opacity 0.3s ease"; - inputContainer.appendChild(saveIndicator); - const saveSuccessIcon = createSpan({ cls: "svg-icon lucide-check" }); - saveSuccessIcon.style.color = "#4CAF50"; - saveSuccessIcon.style.width = "18px"; - saveSuccessIcon.style.height = "18px"; - saveIndicator.appendChild(saveSuccessIcon); - let autoSaveTimer = null; - this.autoSaveRootDir = async (value) => { - if (autoSaveTimer !== null) { - clearTimeout(autoSaveTimer); - } - autoSaveTimer = setTimeout(async () => { - let pathToSave = value.trim(); - if (pathToSave === "") { - pathToSave = "DailyTasks"; - } - await this.settingsManager.updateSettings({ rootDir: pathToSave }); - this.dirtySettings = false; - saveIndicator.style.opacity = "1"; - setTimeout(() => { - saveIndicator.style.opacity = "0"; - }, 2e3); - }, 800); - }; - const autoGenSetting = new import_obsidian3.Setting(containerEl).setName(getTranslation("settings.autoGenerate")).setDesc(getTranslation("settings.autoGenerate.desc")); - const toggleContainer = document.createElement("div"); - toggleContainer.classList.add("mode-toggle-container"); - toggleContainer.style.width = "20%"; - toggleContainer.style.marginLeft = "auto"; - const modes = [ - { value: "none" /* NONE */, label: getTranslation("settings.mode.none") }, - { value: "daily" /* DAILY */, label: getTranslation("settings.mode.daily") }, - { value: "workday" /* WORKDAY */, label: getTranslation("settings.mode.workday") } - ]; - const slider = document.createElement("div"); - slider.classList.add("mode-toggle-slider"); - toggleContainer.appendChild(slider); - const updateSlider = (index) => { - slider.style.left = `${index * 33.33}%`; - }; - let currentModeIndex = modes.findIndex((mode) => mode.value === settings.autoGenerateMode); - if (currentModeIndex === -1) { - currentModeIndex = modes.findIndex((mode) => mode.value === "workday" /* WORKDAY */); - this.settingsManager.updateSettings({ autoGenerateMode: "workday" /* WORKDAY */ }); - } - updateSlider(currentModeIndex); - modes.forEach((mode, index) => { - const option = document.createElement("div"); - option.classList.add("mode-toggle-option"); - option.textContent = mode.label; - if (mode.value === settings.autoGenerateMode) { - option.classList.add("active"); - } - option.addEventListener("click", async () => { - toggleContainer.querySelectorAll(".mode-toggle-option").forEach((el) => { - el.classList.remove("active"); - }); - option.classList.add("active"); - updateSlider(index); - await this.settingsManager.updateSettings({ autoGenerateMode: mode.value }); - }); - toggleContainer.appendChild(option); - }); - autoGenSetting.controlEl.appendChild(toggleContainer); - new import_obsidian3.Setting(containerEl).setName(getTranslation("settings.language")).setDesc(getTranslation("settings.language.desc")).addDropdown((dropdown) => { - dropdown.addOption("auto" /* AUTO */, getTranslation("settings.language.auto")).addOption("zh" /* ZH */, getTranslation("settings.language.zh")).addOption("en" /* EN */, getTranslation("settings.language.en")).setValue(settings.language).onChange(async (value) => { - await this.settingsManager.updateSettings({ language: value }); - this.display(); - }); - }); - new import_obsidian3.Setting(containerEl).setName(getTranslation("settings.notificationDuration")).setDesc(getTranslation("settings.notificationDuration.desc")).addText((text) => { - const component = text.setValue(settings.successNotificationDuration.toString()).onChange(async (value) => { - const duration = parseInt(value); - if (!isNaN(duration) && duration > 0) { - await this.settingsManager.updateSettings({ successNotificationDuration: duration }); - } - }); - component.inputEl.placeholder = "3000"; - return component; - }); - new import_obsidian3.Setting(containerEl).setName(getTranslation("settings.taskStatistics")).setDesc(getTranslation("settings.taskStatistics.desc")).addToggle((toggle) => { - const toggleEl = toggle.setValue(settings.taskStatistics).onChange(async (value) => { - await this.settingsManager.updateSettings({ taskStatistics: value }); - }); - const toggleControl = toggle.toggleEl || toggle.containerEl.querySelector(".checkbox-container"); - if (toggleControl) { - toggleControl.classList.add("task-statistics-toggle"); - const toggleContainer2 = toggleControl.parentElement; - if (toggleContainer2) { - const iconEl = createSpan({ cls: "svg-icon lucide-bar-chart-2" }); - iconEl.style.marginRight = "8px"; - iconEl.style.color = "var(--text-accent)"; - toggleContainer2.prepend(iconEl); - toggleContainer2.style.transition = "all 0.3s ease"; - toggleControl.style.transition = "all 0.3s ease"; - } - } - return toggleEl; - }); - containerEl.createEl("h3", { text: getTranslation("settings.template") }); - const templateDescription = containerEl.createEl("p", { - text: this.settingsManager.getCurrentLanguage() === "zh" ? "\u6CE8\u610F\uFF1A\u9ED8\u8BA4\u6A21\u677F\u4F1A\u6839\u636E\u5F53\u524D\u8BED\u8A00\u73AF\u5883\u81EA\u52A8\u9009\u62E9\u5BF9\u5E94\u8BED\u8A00\u7684\u5185\u5BB9\u3002\u5982\u679C\u60A8\u81EA\u5B9A\u4E49\u6A21\u677F\uFF0C\u5C06\u5728\u6240\u6709\u8BED\u8A00\u73AF\u5883\u4E2D\u4F7F\u7528\u60A8\u7684\u81EA\u5B9A\u4E49\u5185\u5BB9\u3002" : "Note: Default template automatically adapts to your language environment. If you customize the template, your content will be used in all language environments." - }); - templateDescription.style.fontSize = "0.85em"; - templateDescription.style.opacity = "0.8"; - templateDescription.style.marginBottom = "15px"; - const templateVariablesEl = containerEl.createEl("p"); - templateVariablesEl.innerHTML = this.settingsManager.getCurrentLanguage() === "zh" ? "\u53EF\u7528\u53D8\u91CF\uFF1A {{date}} - \u65E5\u671F, {{dateWithIcon}} - \u5E26\u56FE\u6807\u7684\u65E5\u671F, {{weekday}} - \u661F\u671F\u51E0, {{yearProgress}} - \u5E74\u8FDB\u5EA6, {{monthProgress}} - \u6708\u8FDB\u5EA6, {{time}} - \u5F53\u524D\u65F6\u95F4" : "Available variables: {{date}} - Date, {{dateWithIcon}} - Date with icon, {{weekday}} - Day of week, {{yearProgress}} - Year progress, {{monthProgress}} - Month progress, {{time}} - Current time"; - templateVariablesEl.style.fontSize = "0.85em"; - templateVariablesEl.style.marginBottom = "10px"; - const templateSetting = new import_obsidian3.Setting(containerEl).setName(getTranslation("settings.template")).setClass("template-setting"); - const templateContainer = document.createElement("div"); - templateContainer.style.width = "100%"; - const currentTemplate = this.settingsManager.hasCustomTemplate() ? this.settingsManager.getSettings().customTemplate : this.settingsManager.getTemplateByLanguage(); - const textarea = new import_obsidian3.TextAreaComponent(templateContainer).setValue(currentTemplate).setPlaceholder(this.settingsManager.getCurrentLanguage() === "zh" ? "\u5728\u6B64\u5904\u8F93\u5165\u4EFB\u52A1\u6A21\u677F..." : "Enter task template here...").onChange(async (value) => { - await this.settingsManager.updateSettings({ - customTemplate: value, - hasCustomTemplate: true - }); - this.updatePreview(this.previewEl, value); - }); - textarea.inputEl.classList.add("template-editor"); - textarea.inputEl.style.height = "200px"; - textarea.inputEl.style.border = "1px solid var(--background-modifier-border-hover)"; - textarea.inputEl.style.borderRadius = "4px"; - textarea.inputEl.style.padding = "12px"; - textarea.inputEl.style.lineHeight = "1.5"; - textarea.inputEl.style.fontSize = "14px"; - textarea.inputEl.style.fontFamily = "var(--font-monospace)"; - textarea.inputEl.style.transition = "all 0.2s ease"; - textarea.inputEl.style.boxShadow = "inset 0 1px 3px rgba(0, 0, 0, 0.1)"; - textarea.inputEl.style.backgroundColor = "var(--background-primary)"; - textarea.inputEl.style.color = "var(--text-normal)"; - textarea.inputEl.style.resize = "vertical"; - textarea.inputEl.addEventListener("focus", () => { - textarea.inputEl.style.border = "1px solid var(--interactive-accent)"; - textarea.inputEl.style.boxShadow = "0 0 0 2px rgba(var(--interactive-accent-rgb), 0.2), inset 0 1px 3px rgba(0, 0, 0, 0.1)"; - textarea.inputEl.style.outline = "none"; - }); - textarea.inputEl.addEventListener("blur", () => { - textarea.inputEl.style.border = "1px solid var(--background-modifier-border-hover)"; - textarea.inputEl.style.boxShadow = "inset 0 1px 3px rgba(0, 0, 0, 0.1)"; - }); - const previewHeader = document.createElement("div"); - previewHeader.classList.add("template-preview-header"); - previewHeader.style.display = "flex"; - previewHeader.style.justifyContent = "space-between"; - previewHeader.style.marginTop = "15px"; - previewHeader.style.marginBottom = "10px"; - previewHeader.style.width = "100%"; - const previewBtnContainer = document.createElement("div"); - previewBtnContainer.style.display = "flex"; - previewBtnContainer.style.alignItems = "center"; - const resetBtnContainer = document.createElement("div"); - resetBtnContainer.style.display = "flex"; - resetBtnContainer.style.alignItems = "center"; - const toggleButton = new import_obsidian3.ButtonComponent(previewBtnContainer).setButtonText(getTranslation("settings.template.preview")); - toggleButton.buttonEl.addClass(TextCenterCSS); - toggleButton.buttonEl.style.textAlign = "center"; - toggleButton.buttonEl.style.display = "flex"; - toggleButton.buttonEl.style.alignItems = "center"; - toggleButton.buttonEl.style.justifyContent = "center"; - toggleButton.buttonEl.style.width = "130px"; - toggleButton.buttonEl.style.borderRadius = "4px"; - toggleButton.buttonEl.style.boxShadow = "0 2px 5px rgba(0,0,0,0.1)"; - toggleButton.buttonEl.style.transition = "all 0.2s ease"; - const eyeIcon = createSpan({ cls: "svg-icon lucide-eye" }); - eyeIcon.style.marginRight = "6px"; - toggleButton.buttonEl.prepend(eyeIcon); - this.previewEl = document.createElement("div"); - this.previewEl.classList.add("template-preview"); - this.previewEl.style.marginTop = "15px"; - this.previewEl.style.padding = "15px"; - this.previewEl.style.border = "1px dashed var(--background-modifier-border)"; - this.previewEl.style.borderRadius = "8px"; - this.previewEl.style.backgroundColor = "var(--background-secondary)"; - this.previewEl.style.display = "none"; - this.previewEl.style.maxHeight = "200px"; - this.previewEl.style.overflow = "auto"; - this.previewEl.style.boxShadow = "inset 0 0 5px rgba(0,0,0,0.1)"; - this.updatePreview(this.previewEl, currentTemplate); - templateContainer.appendChild(this.previewEl); - toggleButton.onClick(() => { - this.togglePreview(this.previewEl); - if (this.previewEl && this.previewEl.classList.contains("visible")) { - eyeIcon.className = "svg-icon lucide-eye-off"; - toggleButton.setButtonText(getTranslation("settings.template.hide")); - toggleButton.buttonEl.style.backgroundColor = "var(--background-modifier-success)"; - } else { - eyeIcon.className = "svg-icon lucide-eye"; - toggleButton.setButtonText(getTranslation("settings.template.preview")); - toggleButton.buttonEl.style.backgroundColor = ""; - } - }); - const resetBtn = new import_obsidian3.ButtonComponent(resetBtnContainer).setButtonText(getTranslation("settings.resetDefault")); - resetBtn.buttonEl.addClass(TextCenterCSS); - resetBtn.buttonEl.style.textAlign = "center"; - resetBtn.buttonEl.style.display = "flex"; - resetBtn.buttonEl.style.alignItems = "center"; - resetBtn.buttonEl.style.justifyContent = "center"; - resetBtn.buttonEl.style.width = "150px"; - resetBtn.buttonEl.style.borderRadius = "4px"; - resetBtn.buttonEl.style.boxShadow = "0 2px 5px rgba(0,0,0,0.1)"; - resetBtn.buttonEl.style.transition = "all 0.2s ease"; - const resetIcon = createSpan({ cls: "svg-icon lucide-refresh-cw" }); - resetIcon.style.marginRight = "6px"; - resetBtn.buttonEl.prepend(resetIcon); - resetBtn.onClick(async () => { - await this.settingsManager.updateSettings({ - customTemplate: "", - hasCustomTemplate: false - }); - const defaultTemplate = this.settingsManager.getTemplateByLanguage(); - textarea.setValue(defaultTemplate); - this.updatePreview(this.previewEl, defaultTemplate); - resetBtn.buttonEl.style.backgroundColor = "var(--background-modifier-success)"; - setTimeout(() => { - resetBtn.buttonEl.style.backgroundColor = ""; - }, 1e3); - }); - previewHeader.appendChild(previewBtnContainer); - previewHeader.appendChild(resetBtnContainer); - templateContainer.appendChild(previewHeader); - templateSetting.controlEl.appendChild(templateContainer); - const resetContainer = document.createElement("div"); - resetContainer.style.display = "flex"; - resetContainer.style.justifyContent = "flex-end"; - resetContainer.style.marginTop = "20px"; - resetContainer.style.marginBottom = "10px"; - containerEl.appendChild(resetContainer); - const resetDefaultBtn = new import_obsidian3.ButtonComponent(resetContainer).setButtonText(getTranslation("settings.resetToDefault")); - resetDefaultBtn.buttonEl.addClass(TextCenterCSS); - resetDefaultBtn.buttonEl.addClass("danger-button"); - resetDefaultBtn.buttonEl.style.textAlign = "center"; - resetDefaultBtn.buttonEl.style.display = "flex"; - resetDefaultBtn.buttonEl.style.alignItems = "center"; - resetDefaultBtn.buttonEl.style.justifyContent = "center"; - resetDefaultBtn.buttonEl.style.width = "150px"; - resetDefaultBtn.buttonEl.prepend(createSpan({ cls: "svg-icon lucide-refresh-cw" })); - resetDefaultBtn.onClick(async () => { - await this.settingsManager.resetToDefaults(); - this.display(); - }); - const addTaskContainer = document.createElement("div"); - addTaskContainer.style.display = "flex"; - addTaskContainer.style.justifyContent = "flex-end"; - addTaskContainer.style.marginTop = "20px"; - containerEl.appendChild(addTaskContainer); - this.addTaskButton = new import_obsidian3.ButtonComponent(addTaskContainer).setButtonText(getTranslation("settings.addTaskButton")).setCta(); - if (this.addTaskButton && this.addTaskButton.buttonEl) { - this.addTaskButton.buttonEl.addClass(TextCenterCSS); - this.addTaskButton.buttonEl.style.textAlign = "center"; - this.addTaskButton.buttonEl.style.display = "flex"; - this.addTaskButton.buttonEl.style.alignItems = "center"; - this.addTaskButton.buttonEl.style.justifyContent = "center"; - } - this.addTaskButton.onClick(async () => { - var _a; - const rootDir = this.settingsManager.getSettings().rootDir; - if (this.addTaskButton && this.addTaskButton.buttonEl) { - this.addTaskButton.buttonEl.classList.add("loading"); - } - (_a = this.addTaskButton) == null ? void 0 : _a.setDisabled(true); - try { - await this.taskGenerator.addTaskManually(); - } catch (e) { - console.error("\u6DFB\u52A0\u4EFB\u52A1\u51FA\u9519:", e); - new import_obsidian3.Notice(`\u6DFB\u52A0\u4EFB\u52A1\u5931\u8D25: ${e.message || e}`); - } finally { - setTimeout(() => { - var _a2; - if (this.addTaskButton && this.addTaskButton.buttonEl) { - this.addTaskButton.buttonEl.classList.remove("loading"); - } - (_a2 = this.addTaskButton) == null ? void 0 : _a2.setDisabled(false); - }, 500); - } - }); - this.addTaskButton.buttonEl.prepend(createSpan({ cls: "svg-icon lucide-calendar-plus" })); - } - /** - * 更新模板预览 - */ - updatePreview(previewEl, template) { - if (!previewEl) - return; - const renderedContent = renderTemplate(template); - previewEl.innerHTML = renderedContent.replace(/\n/g, "
"); - } - /** - * 切换预览的显示/隐藏 - */ - togglePreview(previewEl) { - if (!previewEl) - return; - if (previewEl.style.display === "none") { - previewEl.style.display = "block"; - previewEl.classList.add("visible"); - } else { - previewEl.style.display = "none"; - previewEl.classList.remove("visible"); - } - } -}; -var SettingsManager = class { - constructor(plugin) { - this.plugin = plugin; - this.settings = Object.assign({}, DEFAULT_SETTINGS); - } - /** - * 获取当前设置 - */ - getSettings() { - return this.settings; - } - /** - * 更新设置并保存 - * @param settings 要更新的设置 - */ - async updateSettings(settings) { - this.settings = { - ...this.settings, - ...settings - }; - await this.saveSettings(); - this.updateCurrentLanguage(); - } - /** - * 保存设置到数据存储 - */ - async saveSettings() { - await this.plugin.saveData(this.settings); - } - /** - * 加载设置 - */ - async loadSettings() { - const loadedData = await this.plugin.loadData(); - if (loadedData) { - this.settings = { - ...DEFAULT_SETTINGS, - ...loadedData - }; - this.ensureSettingsCompleteness(); - } else { - this.settings = { - ...DEFAULT_SETTINGS, - autoGenerateMode: "workday" /* WORKDAY */ - }; - } - this.updateCurrentLanguage(); - } - /** - * 确保设置完整性,为新增的设置项提供默认值 - */ - ensureSettingsCompleteness() { - const defaultKeys = Object.keys(DEFAULT_SETTINGS); - defaultKeys.forEach((key) => { - if (!(key in this.settings)) { - this.settings[key] = DEFAULT_SETTINGS[key]; - } - }); - } - /** - * 恢复默认设置 - */ - async resetToDefaults() { - this.settings = Object.assign({}, DEFAULT_SETTINGS); - await this.saveSettings(); - this.updateCurrentLanguage(); - } - /** - * 根据语言获取当前使用的模板 - * 如果当前模板不是默认模板,则不再区分语言 - */ - getCurrentTemplate() { - const language = this.getCurrentLanguage(); - if (language === "zh") { - if (this.settings.templateZh !== DEFAULT_TEMPLATE_ZH) { - return this.settings.templateZh; - } - if (this.settings.templateEn !== DEFAULT_TEMPLATE_EN) { - return this.settings.templateEn; - } - return this.settings.templateZh; - } else { - if (this.settings.templateEn !== DEFAULT_TEMPLATE_EN) { - return this.settings.templateEn; - } - if (this.settings.templateZh !== DEFAULT_TEMPLATE_ZH) { - return this.settings.templateZh; - } - return this.settings.templateEn; - } - } - /** - * 获取当前语言设置 - */ - getCurrentLanguage() { - if (this.settings.language === "auto" /* AUTO */) { - const systemLanguage = window.navigator.language.toLowerCase(); - return systemLanguage.startsWith("zh") ? "zh" : "en"; - } - return this.settings.language; - } - /** - * 更新当前语言 - */ - updateCurrentLanguage() { - const language = this.getCurrentLanguage(); - setCurrentLanguage(language); - } - /** - * 获取当前语言的模板 - */ - getTemplateByLanguage() { - const language = this.getCurrentLanguage(); - if (language === "zh") { - if (this.settings.templateZh !== DEFAULT_TEMPLATE_ZH) { - return this.settings.templateZh; - } - if (this.settings.templateEn !== DEFAULT_TEMPLATE_EN) { - return this.settings.templateEn; - } - return this.settings.templateZh; - } else { - if (this.settings.templateEn !== DEFAULT_TEMPLATE_EN) { - return this.settings.templateEn; - } - if (this.settings.templateZh !== DEFAULT_TEMPLATE_ZH) { - return this.settings.templateZh; - } - return this.settings.templateEn; - } - } - /** - * 检查是否存在自定义模板 - */ - hasCustomTemplate() { - return !!this.settings.customTemplate; - } -}; - -// src/main.ts -var ICON = ` - -`; -var DailyTaskPlugin = class extends import_obsidian4.Plugin { - /** - * 插件加载时调用 - */ - async onload() { - console.log("Loading Daily Task Auto Generator plugin"); - (0, import_obsidian4.addIcon)("daily-task", ICON); - this.settingsManager = new SettingsManager(this); - await this.settingsManager.loadSettings(); - this.taskGenerator = new TaskGenerator(this.app, this.settingsManager); - setCurrentLanguage(this.settingsManager.getCurrentLanguage()); - this.addSettingTab(new DailyTaskSettingTab(this.app, this)); - this.addCommand({ - id: "add-daily-task", - name: "\u624B\u52A8\u6DFB\u52A0\u4ECA\u65E5\u4EFB\u52A1", - callback: async () => { - await this.taskGenerator.addTaskManually(); - } - }); - console.log("\u8BA1\u5212\u572810\u79D2\u540E\u68C0\u67E5\u662F\u5426\u9700\u8981\u81EA\u52A8\u751F\u6210\u4EFB\u52A1"); - setTimeout(async () => { - console.log("\u5F00\u59CB\u68C0\u67E5\u662F\u5426\u9700\u8981\u81EA\u52A8\u751F\u6210\u4EFB\u52A1"); - await this.checkAutoGenerate(); - }, 1e4); - } - /** - * 插件卸载时调用 - */ - onunload() { - console.log("Unloading Daily Task Auto Generator plugin"); - } - /** - * 检查是否需要自动生成任务 - */ - async checkAutoGenerate() { - const settings = this.settingsManager.getSettings(); - switch (settings.autoGenerateMode) { - case "daily" /* DAILY */: - console.log("\u6839\u636E\u8BBE\u7F6E\uFF0C\u6BCF\u5929\u81EA\u52A8\u751F\u6210\u4EFB\u52A1\uFF08\u9759\u9ED8\u6A21\u5F0F\uFF09"); - await this.taskGenerator.generateDailyTask(false, true); - break; - case "workday" /* WORKDAY */: - if (isWorkday()) { - console.log("\u6839\u636E\u8BBE\u7F6E\uFF0C\u5DE5\u4F5C\u65E5\u81EA\u52A8\u751F\u6210\u4EFB\u52A1\uFF08\u9759\u9ED8\u6A21\u5F0F\uFF09"); - await this.taskGenerator.generateDailyTask(false, true); - } else { - console.log("\u4ECA\u5929\u4E0D\u662F\u5DE5\u4F5C\u65E5\uFF0C\u8DF3\u8FC7\u81EA\u52A8\u751F\u6210\u4EFB\u52A1"); - } - break; - case "none" /* NONE */: - default: - console.log("\u5DF2\u7981\u7528\u81EA\u52A8\u751F\u6210\u4EFB\u52A1\u529F\u80FD"); - break; - } - } -}; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..c9193e7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2363 @@ +{ + "name": "daily-task-auto-generator", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "daily-task-auto-generator", + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "@types/node": "^16.11.6", + "@typescript-eslint/eslint-plugin": "^5.29.0", + "@typescript-eslint/parser": "^5.29.0", + "builtin-modules": "^3.3.0", + "esbuild": "0.17.3", + "obsidian": "latest", + "tslib": "2.4.0", + "typescript": "4.7.4" + } + }, + "node_modules/@codemirror/state": { + "version": "6.5.2", + "resolved": "https://registry.npmmirror.com/@codemirror/state/-/state-6.5.2.tgz", + "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.36.5", + "resolved": "https://registry.npmmirror.com/@codemirror/view/-/view-6.36.5.tgz", + "integrity": "sha512-cd+FZEUlu3GQCYnguYm3EkhJ8KJVisqqUsCOKedBoAt/d9c76JUUap6U0UrpElln5k6VyrEOYliMuDAKIeDQLg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@codemirror/state": "^6.5.0", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.17.3.tgz", + "integrity": "sha512-1Mlz934GvbgdDmt26rTLmf03cAgLg5HyOgJN+ZGCeP3Q9ynYTNMn2/LQxIl7Uy+o4K6Rfi2OuLsr12JQQR8gNg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.17.3.tgz", + "integrity": "sha512-XvJsYo3dO3Pi4kpalkyMvfQsjxPWHYjoX4MDiB/FUM4YMfWcXa5l4VCwFWVYI1+92yxqjuqrhNg0CZg3gSouyQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.17.3.tgz", + "integrity": "sha512-nuV2CmLS07Gqh5/GrZLuqkU9Bm6H6vcCspM+zjp9TdQlxJtIe+qqEXQChmfc7nWdyr/yz3h45Utk1tUn8Cz5+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.3.tgz", + "integrity": "sha512-01Hxaaat6m0Xp9AXGM8mjFtqqwDjzlMP0eQq9zll9U85ttVALGCGDuEvra5Feu/NbP5AEP1MaopPwzsTcUq1cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.17.3.tgz", + "integrity": "sha512-Eo2gq0Q/er2muf8Z83X21UFoB7EU6/m3GNKvrhACJkjVThd0uA+8RfKpfNhuMCl1bKRfBzKOk6xaYKQZ4lZqvA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.3.tgz", + "integrity": "sha512-CN62ESxaquP61n1ZjQP/jZte8CE09M6kNn3baos2SeUfdVBkWN5n6vGp2iKyb/bm/x4JQzEvJgRHLGd5F5b81w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.3.tgz", + "integrity": "sha512-feq+K8TxIznZE+zhdVurF3WNJ/Sa35dQNYbaqM/wsCbWdzXr5lyq+AaTUSER2cUR+SXPnd/EY75EPRjf4s1SLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.17.3.tgz", + "integrity": "sha512-CLP3EgyNuPcg2cshbwkqYy5bbAgK+VhyfMU7oIYyn+x4Y67xb5C5ylxsNUjRmr8BX+MW3YhVNm6Lq6FKtRTWHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.17.3.tgz", + "integrity": "sha512-JHeZXD4auLYBnrKn6JYJ0o5nWJI9PhChA/Nt0G4MvLaMrvXuWnY93R3a7PiXeJQphpL1nYsaMcoV2QtuvRnF/g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.17.3.tgz", + "integrity": "sha512-FyXlD2ZjZqTFh0sOQxFDiWG1uQUEOLbEh9gKN/7pFxck5Vw0qjWSDqbn6C10GAa1rXJpwsntHcmLqydY9ST9ZA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.17.3.tgz", + "integrity": "sha512-OrDGMvDBI2g7s04J8dh8/I7eSO+/E7nMDT2Z5IruBfUO/RiigF1OF6xoH33Dn4W/OwAWSUf1s2nXamb28ZklTA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.3.tgz", + "integrity": "sha512-DcnUpXnVCJvmv0TzuLwKBC2nsQHle8EIiAJiJ+PipEVC16wHXaPEKP0EqN8WnBe0TPvMITOUlP2aiL5YMld+CQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.3.tgz", + "integrity": "sha512-BDYf/l1WVhWE+FHAW3FzZPtVlk9QsrwsxGzABmN4g8bTjmhazsId3h127pliDRRu5674k1Y2RWejbpN46N9ZhQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.3.tgz", + "integrity": "sha512-WViAxWYMRIi+prTJTyV1wnqd2mS2cPqJlN85oscVhXdb/ZTFJdrpaqm/uDsZPGKHtbg5TuRX/ymKdOSk41YZow==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.17.3.tgz", + "integrity": "sha512-Iw8lkNHUC4oGP1O/KhumcVy77u2s6+KUjieUqzEU3XuWJqZ+AY7uVMrrCbAiwWTkpQHkr00BuXH5RpC6Sb/7Ug==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.17.3.tgz", + "integrity": "sha512-0AGkWQMzeoeAtXQRNB3s4J1/T2XbigM2/Mn2yU1tQSmQRmHIZdkGbVq2A3aDdNslPyhb9/lH0S5GMTZ4xsjBqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.3.tgz", + "integrity": "sha512-4+rR/WHOxIVh53UIQIICryjdoKdHsFZFD4zLSonJ9RRw7bhKzVyXbnRPsWSfwybYqw9sB7ots/SYyufL1mBpEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.3.tgz", + "integrity": "sha512-cVpWnkx9IYg99EjGxa5Gc0XmqumtAwK3aoz7O4Dii2vko+qXbkHoujWA68cqXjhh6TsLaQelfDO4MVnyr+ODeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.17.3.tgz", + "integrity": "sha512-RxmhKLbTCDAY2xOfrww6ieIZkZF+KBqG7S2Ako2SljKXRFi+0863PspK74QQ7JpmWwncChY25JTJSbVBYGQk2Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.17.3.tgz", + "integrity": "sha512-0r36VeEJ4efwmofxVJRXDjVRP2jTmv877zc+i+Pc7MNsIr38NfsjkQj23AfF7l0WbB+RQ7VUb+LDiqC/KY/M/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.17.3.tgz", + "integrity": "sha512-wgO6rc7uGStH22nur4aLFcq7Wh86bE9cOFmfTr/yxN3BXvDEdCSXyKkO+U5JIt53eTOgC47v9k/C1bITWL/Teg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.17.3.tgz", + "integrity": "sha512-FdVl64OIuiKjgXBjwZaJLKp0eaEckifbhn10dXWhysMJkWblg3OEEGKSIyhiD5RSgAya8WzP3DNkngtIg3Nt7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.6.0", + "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.6.0.tgz", + "integrity": "sha512-WhCn7Z7TauhBtmzhvKpoQs0Wwb/kBcy4CwpuI0/eEIr2Lx2auxmulAzLr91wVZJaz47iUZdkXOK7WlAfxGKCnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/codemirror": { + "version": "5.60.8", + "resolved": "https://registry.npmmirror.com/@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.7", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "16.18.126", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-16.18.126.tgz", + "integrity": "sha512-OTcgaiwfGFBKacvfwuHzzn1KLxH/er8mluiy8/uM3sGXHaRe73RrSIj01jow9t4kJEW633Ov+cOexXeiApTyAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/semver": { + "version": "7.7.0", + "resolved": "https://registry.npmmirror.com/@types/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tern": { + "version": "0.23.9", + "resolved": "https://registry.npmmirror.com/@types/tern/-/tern-0.23.9.tgz", + "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peer": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0", + "peer": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/esbuild": { + "version": "0.17.3", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.17.3.tgz", + "integrity": "sha512-9n3AsBRe6sIyOc6kmoXg2ypCLgf3eZSraWFRpnkto+svt8cZNuKTkb1bhQcitBcvIqjNiK7K0J3KPmwGSfkA8g==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.17.3", + "@esbuild/android-arm64": "0.17.3", + "@esbuild/android-x64": "0.17.3", + "@esbuild/darwin-arm64": "0.17.3", + "@esbuild/darwin-x64": "0.17.3", + "@esbuild/freebsd-arm64": "0.17.3", + "@esbuild/freebsd-x64": "0.17.3", + "@esbuild/linux-arm": "0.17.3", + "@esbuild/linux-arm64": "0.17.3", + "@esbuild/linux-ia32": "0.17.3", + "@esbuild/linux-loong64": "0.17.3", + "@esbuild/linux-mips64el": "0.17.3", + "@esbuild/linux-ppc64": "0.17.3", + "@esbuild/linux-riscv64": "0.17.3", + "@esbuild/linux-s390x": "0.17.3", + "@esbuild/linux-x64": "0.17.3", + "@esbuild/netbsd-x64": "0.17.3", + "@esbuild/openbsd-x64": "0.17.3", + "@esbuild/sunos-x64": "0.17.3", + "@esbuild/win32-arm64": "0.17.3", + "@esbuild/win32-ia32": "0.17.3", + "@esbuild/win32-x64": "0.17.3" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmmirror.com/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmmirror.com/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmmirror.com/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true, + "license": "MIT" + }, + "node_modules/obsidian": { + "version": "1.8.7", + "resolved": "https://registry.npmmirror.com/obsidian/-/obsidian-1.8.7.tgz", + "integrity": "sha512-h4bWwNFAGRXlMlMAzdEiIM2ppTGlrh7uGOJS6w4gClrsjc+ei/3YAtU2VdFUlCiPuTHpY4aBpFJJW75S1Tl/JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/codemirror": "5.60.8", + "moment": "2.29.4" + }, + "peerDependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-mod": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/style-mod/-/style-mod-4.1.2.tgz", + "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmmirror.com/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "4.7.4", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmmirror.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..7e6c2ce --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "daily-task-auto-generator", + "version": "1.0.0", + "description": "Obsidian plugin to automatically generate daily tasks", + "main": "main.js", + "scripts": { + "dev": "node esbuild.config.mjs", + "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", + "version": "node version-bump.mjs && git add manifest.json versions.json" + }, + "keywords": [ + "obsidian", + "tasks", + "automation" + ], + "author": "maigamo", + "license": "MIT", + "devDependencies": { + "@types/node": "^16.11.6", + "@typescript-eslint/eslint-plugin": "^5.29.0", + "@typescript-eslint/parser": "^5.29.0", + "builtin-modules": "^3.3.0", + "esbuild": "0.17.3", + "obsidian": "latest", + "tslib": "2.4.0", + "typescript": "4.7.4" + } +} \ No newline at end of file diff --git a/src/ambient.d.ts b/src/ambient.d.ts new file mode 100644 index 0000000..d36291c --- /dev/null +++ b/src/ambient.d.ts @@ -0,0 +1,10 @@ +/** + * 扩展HTMLElement接口,添加Obsidian特有的方法 + */ +interface HTMLElement { + addClass(cls: string): void; + removeClass(cls: string): void; + toggleClass(cls: string): void; + empty(): void; + createEl(tag: K, attrs?: object): HTMLElementTagNameMap[K]; +} \ No newline at end of file diff --git a/src/i18n/i18n.js b/src/i18n/i18n.js new file mode 100644 index 0000000..e89ad1d --- /dev/null +++ b/src/i18n/i18n.js @@ -0,0 +1,178 @@ +/** + * i18n.ts + * 国际化支持模块 + */ +// 中文翻译 +const translationsZH = { + // 设置页面 + 'settings.title': '每日任务自动生成器设置', + 'settings.rootDir': '📁 任务文件存放目录', + 'settings.rootDir.desc': '指定保存任务文件的根目录,任务将按"年份/月份.md"格式存储', + 'settings.rootDir.saved': '✓ 目录已保存', + 'settings.save': '💾 保存', + 'settings.autoGenerate': '🔄 自动生成模式', + 'settings.autoGenerate.desc': '选择何时自动生成每日任务', + 'settings.mode.none': '❌ 关闭', + 'settings.mode.daily': '📆 每天', + 'settings.mode.workday': '💼 仅工作日', + 'settings.language': '🔤 界面语言', + 'settings.language.desc': '选择插件界面显示的语言', + 'settings.language.auto': '🔍 自动检测', + 'settings.language.zh': '🇨🇳 中文', + 'settings.language.en': '🇬🇧 英文', + 'settings.animations': '✨ 动画效果', + 'settings.animations.desc': '启用界面动画效果', + 'settings.template': '📝 任务模板', + 'settings.template.zh': '🇨🇳 中文模板', + 'settings.template.en': '🇬🇧 英文模板', + 'settings.template.preview': '👁️ 显示预览', + 'settings.template.hide': '👁️‍🗨️ 隐藏预览', + 'settings.resetToDefault': '🔄 恢复默认设置', + 'settings.addTaskButton': '➕ 手动添加今日任务', + 'settings.notificationDuration': '⏱️ 通知显示时间', + 'settings.notificationDuration.desc': '成功/失败提示显示时间(毫秒)', + 'settings.preview': '预览模板效果', + 'settings.resetDefault': '恢复默认设置', + 'template.dateWithIcon': '带图标的当前日期', + 'settings.basicSettings': '基本设置', + 'settings.templateSettings': '模板设置', + // 通知 + 'notification.taskAdded': '今日任务已添加', + 'notification.taskExists': '今日任务已存在', + 'notification.error': '错误:', + // 星期 + 'weekday.mon': '星期一', + 'weekday.tue': '星期二', + 'weekday.wed': '星期三', + 'weekday.thu': '星期四', + 'weekday.fri': '星期五', + 'weekday.sat': '星期六', + 'weekday.sun': '星期日', + // 插件名称和描述 + 'plugin.name': '每日任务自动生成器', + 'plugin.description': '一个强大的任务自动生成器,帮助你高效地管理日常任务', + // 一般按钮和消息 + 'button.addTask': '添加任务', + 'button.save': '保存', + 'button.cancel': '取消', + 'button.done': '完成', + // 添加任务统计相关翻译 + 'settings.taskStatistics': '📊 任务完成统计', + 'settings.taskStatistics.desc': '开启后,每日生成任务前会自动统计前一天的任务完成情况', + 'statistics.title': '📊 昨日任务统计', + 'statistics.totalTasks': '任务总数', + 'statistics.completedTasks': '已完成任务', + 'statistics.completionRate': '完成率', + 'statistics.unfinishedTasks': '未完成的任务', + 'statistics.suggestions': '建议今日考虑完成以下任务', +}; +// 英文翻译 +const translationsEN = { + // 设置页面 + 'settings.title': 'Daily Task Auto Generator Settings', + 'settings.rootDir': '📁 Task Directory', + 'settings.rootDir.desc': 'Specify the root directory for storing task files, tasks will be stored in "Year/Month.md" format', + 'settings.rootDir.saved': '✓ Directory saved', + 'settings.save': '💾 Save', + 'settings.autoGenerate': '🔄 Auto Generate Mode', + 'settings.autoGenerate.desc': 'Choose when to automatically generate daily tasks', + 'settings.mode.none': '❌ Off', + 'settings.mode.daily': '📆 Daily', + 'settings.mode.workday': '💼 Workdays Only', + 'settings.language': '🔤 Interface Language', + 'settings.language.desc': 'Select the language for the plugin interface', + 'settings.language.auto': '🔍 Auto Detect', + 'settings.language.zh': '🇨🇳 Chinese', + 'settings.language.en': '🇬🇧 English', + 'settings.animations': '✨ Animation Effects', + 'settings.animations.desc': 'Enable interface animation effects', + 'settings.template': '📝 Task Template', + 'settings.template.zh': '🇨🇳 Chinese Template', + 'settings.template.en': '🇬🇧 English Template', + 'settings.template.preview': '👁️ Show Preview', + 'settings.template.hide': '👁️‍🗨️ Hide Preview', + 'settings.resetToDefault': '🔄 Reset to Default', + 'settings.addTaskButton': '➕ Add Today\'s Task Manually', + 'settings.notificationDuration': '⏱️ Notification Duration', + 'settings.notificationDuration.desc': 'Duration to show success/failure notifications (milliseconds)', + 'settings.preview': 'Preview Template', + 'settings.resetDefault': 'Reset to Default', + 'template.dateWithIcon': 'Current date with icon', + 'settings.basicSettings': 'Basic Settings', + 'settings.templateSettings': 'Template Settings', + // 通知 + 'notification.taskAdded': 'Today\'s task has been added', + 'notification.taskExists': 'Today\'s task already exists', + 'notification.error': 'Error: ', + // 星期 + 'weekday.mon': 'Monday', + 'weekday.tue': 'Tuesday', + 'weekday.wed': 'Wednesday', + 'weekday.thu': 'Thursday', + 'weekday.fri': 'Friday', + 'weekday.sat': 'Saturday', + 'weekday.sun': 'Sunday', + // 插件名称和描述 + 'plugin.name': 'Daily Task Auto Generator', + 'plugin.description': 'A powerful task auto generator to help you efficiently manage daily tasks', + // 一般按钮和消息 + 'button.addTask': 'Add Task', + 'button.save': 'Save', + 'button.cancel': 'Cancel', + 'button.done': 'Done', + // 添加任务统计相关翻译 + 'settings.taskStatistics': '📊 Task Completion Statistics', + 'settings.taskStatistics.desc': 'When enabled, automatically analyze yesterday\'s task completion before generating today\'s tasks', + 'statistics.title': '📊 Yesterday\'s Task Summary', + 'statistics.totalTasks': 'Total Tasks', + 'statistics.completedTasks': 'Completed Tasks', + 'statistics.completionRate': 'Completion Rate', + 'statistics.unfinishedTasks': 'Unfinished Tasks', + 'statistics.suggestions': 'Consider completing the following tasks today', +}; +// 翻译查找表 +const translations = { + 'zh': translationsZH, + 'en': translationsEN +}; +// 当前语言 +let currentLanguage = 'en'; +/** + * 设置当前语言 + * @param language 语言代码 + */ +export function setCurrentLanguage(language) { + currentLanguage = language; +} +/** + * 获取翻译文本 + * @param key 翻译键 + * @param fallbackLanguage 备用语言 + * @returns 翻译后的文本 + */ +export function getTranslation(key, fallbackLanguage) { + var _a; + const language = fallbackLanguage || currentLanguage; + // 获取对应语言的翻译 + const translation = (_a = translations[language]) === null || _a === void 0 ? void 0 : _a[key]; + // 如果没有找到对应翻译,尝试使用英文,再没有则返回键名 + if (!translation) { + return translations['en'][key] || key; + } + return translation; +} +/** + * 获取星期几的本地化名称 + * @param dayOfWeek 星期几的数字表示 (0-6, 0代表星期日) + * @returns 本地化的星期名称 + */ +export function getLocalizedWeekday(dayOfWeek) { + const weekdayKeys = [ + 'weekday.sun', 'weekday.mon', 'weekday.tue', + 'weekday.wed', 'weekday.thu', 'weekday.fri', 'weekday.sat' + ]; + // 确保dayOfWeek在有效范围内 + const normalizedDayOfWeek = ((dayOfWeek % 7) + 7) % 7; + return getTranslation(weekdayKeys[normalizedDayOfWeek]); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaTE4bi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImkxOG4udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBNkhILE9BQU87QUFDUCxNQUFNLGNBQWMsR0FBbUM7SUFDbkQsT0FBTztJQUNQLGdCQUFnQixFQUFFLGFBQWE7SUFDL0Isa0JBQWtCLEVBQUUsYUFBYTtJQUNqQyx1QkFBdUIsRUFBRSxpQ0FBaUM7SUFDMUQsd0JBQXdCLEVBQUUsU0FBUztJQUNuQyxlQUFlLEVBQUUsT0FBTztJQUN4Qix1QkFBdUIsRUFBRSxXQUFXO0lBQ3BDLDRCQUE0QixFQUFFLGNBQWM7SUFDNUMsb0JBQW9CLEVBQUUsTUFBTTtJQUM1QixxQkFBcUIsRUFBRSxPQUFPO0lBQzlCLHVCQUF1QixFQUFFLFNBQVM7SUFDbEMsbUJBQW1CLEVBQUUsU0FBUztJQUM5Qix3QkFBd0IsRUFBRSxhQUFhO0lBQ3ZDLHdCQUF3QixFQUFFLFNBQVM7SUFDbkMsc0JBQXNCLEVBQUUsU0FBUztJQUNqQyxzQkFBc0IsRUFBRSxTQUFTO0lBQ2pDLHFCQUFxQixFQUFFLFFBQVE7SUFDL0IsMEJBQTBCLEVBQUUsVUFBVTtJQUN0QyxtQkFBbUIsRUFBRSxTQUFTO0lBQzlCLHNCQUFzQixFQUFFLFdBQVc7SUFDbkMsc0JBQXNCLEVBQUUsV0FBVztJQUNuQywyQkFBMkIsRUFBRSxVQUFVO0lBQ3ZDLHdCQUF3QixFQUFFLGNBQWM7SUFDeEMseUJBQXlCLEVBQUUsV0FBVztJQUN0Qyx3QkFBd0IsRUFBRSxZQUFZO0lBQ3RDLCtCQUErQixFQUFFLFdBQVc7SUFDNUMsb0NBQW9DLEVBQUUsaUJBQWlCO0lBQ3ZELGtCQUFrQixFQUFFLFFBQVE7SUFDNUIsdUJBQXVCLEVBQUUsUUFBUTtJQUNqQyx1QkFBdUIsRUFBRSxVQUFVO0lBQ25DLHdCQUF3QixFQUFFLE1BQU07SUFDaEMsMkJBQTJCLEVBQUUsTUFBTTtJQUVuQyxLQUFLO0lBQ0wsd0JBQXdCLEVBQUUsU0FBUztJQUNuQyx5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QUFDLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDdEQsT0FBTyxjQUFjLENBQUMsV0FBVyxDQUFDLG1CQUFtQixDQUFDLENBQUMsQ0FBQztBQUM1RCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBpMThuLnRzXG4gKiDlm73pmYXljJbmlK/mjIHmqKHlnZdcbiAqL1xuXG4vLyDlrprkuYnnv7vor5HplK7nsbvlnotcbmV4cG9ydCB0eXBlIFRyYW5zbGF0aW9uS2V5ID0gXG4gICAgLy8g6K6+572u6aG16Z2iXG4gICAgfCAnc2V0dGluZ3MudGl0bGUnXG4gICAgfCAnc2V0dGluZ3Mucm9vdERpcidcbiAgICB8ICdzZXR0aW5ncy5yb290RGlyLmRlc2MnXG4gICAgfCAnc2V0dGluZ3Mucm9vdERpci5zYXZlZCdcbiAgICB8ICdzZXR0aW5ncy5zYXZlJ1xuICAgIHwgJ3NldHRpbmdzLmF1dG9HZW5lcmF0ZSdcbiAgICB8ICdzZXR0aW5ncy5hdXRvR2VuZXJhdGUuZGVzYydcbiAgICB8ICdzZXR0aW5ncy5tb2RlLm5vbmUnXG4gICAgfCAnc2V0dGluZ3MubW9kZS5kYWlseSdcbiAgICB8ICdzZXR0aW5ncy5tb2RlLndvcmtkYXknXG4gICAgfCAnc2V0dGluZ3MubGFuZ3VhZ2UnXG4gICAgfCAnc2V0dGluZ3MubGFuZ3VhZ2UuZGVzYydcbiAgICB8ICdzZXR0aW5ncy5sYW5ndWFnZS5hdXRvJ1xuICAgIHwgJ3NldHRpbmdzLmxhbmd1YWdlLnpoJ1xuICAgIHwgJ3NldHRpbmdzLmxhbmd1YWdlLmVuJ1xuICAgIHwgJ3NldHRpbmdzLmFuaW1hdGlvbnMnXG4gICAgfCAnc2V0dGluZ3MuYW5pbWF0aW9ucy5kZXNjJ1xuICAgIHwgJ3NldHRpbmdzLnRlbXBsYXRlJ1xuICAgIHwgJ3NldHRpbmdzLnRlbXBsYXRlLnpoJ1xuICAgIHwgJ3NldHRpbmdzLnRlbXBsYXRlLmVuJ1xuICAgIHwgJ3NldHRpbmdzLnRlbXBsYXRlLnByZXZpZXcnXG4gICAgfCAnc2V0dGluZ3MudGVtcGxhdGUuaGlkZSdcbiAgICB8ICdzZXR0aW5ncy5yZXNldERlZmF1bHQnXG4gICAgfCAnc2V0dGluZ3MuYWRkVGFza0J1dHRvbidcbiAgICB8ICdzZXR0aW5ncy5yZXNldFRvRGVmYXVsdCdcbiAgICB8ICdzZXR0aW5ncy5ub3RpZmljYXRpb25EdXJhdGlvbidcbiAgICB8ICdzZXR0aW5ncy5ub3RpZmljYXRpb25EdXJhdGlvbi5kZXNjJ1xuICAgIHwgJ3NldHRpbmdzLnByZXZpZXcnXG4gICAgfCAndGVtcGxhdGUuZGF0ZVdpdGhJY29uJ1xuICAgIFxuICAgIC8vIOmAmuefpVxuICAgIHwgJ25vdGlmaWNhdGlvbi50YXNrQWRkZWQnXG4gICAgfCAnbm90aWZpY2F0aW9uLnRhc2tFeGlzdHMnXG4gICAgfCAnbm90aWZpY2F0aW9uLmVycm9yJ1xuICAgIFxuICAgIC8vIOaYn+acn1xuICAgIHwgJ3dlZWtkYXkubW9uJ1xuICAgIHwgJ3dlZWtkYXkudHVlJ1xuICAgIHwgJ3dlZWtkYXkud2VkJ1xuICAgIHwgJ3dlZWtkYXkudGh1J1xuICAgIHwgJ3dlZWtkYXkuZnJpJ1xuICAgIHwgJ3dlZWtkYXkuc2F0J1xuICAgIHwgJ3dlZWtkYXkuc3VuJ1xuICAgIFxuICAgIC8vIOaPkuS7tuWQjeensOWSjOaPj+i/sFxuICAgIHwgJ3BsdWdpbi5uYW1lJ1xuICAgIHwgJ3BsdWdpbi5kZXNjcmlwdGlvbidcbiAgICBcbiAgICAvLyDkuIDoiKzmjInpkq7lkozmtojmga9cbiAgICB8ICdidXR0b24uYWRkVGFzaydcbiAgICB8ICdidXR0b24uc2F2ZSdcbiAgICB8ICdidXR0b24uY2FuY2VsJ1xuICAgIHwgJ2J1dHRvbi5kb25lJ1xuICAgIFxuICAgIC8vIOiuvue9rlxuICAgIHwgJ3NldHRpbmdzLmJhc2ljU2V0dGluZ3MnXG4gICAgfCAnc2V0dGluZ3MudGVtcGxhdGVTZXR0aW5ncydcbiAgICB8ICdzZXR0aW5ncy5yb290RGlyJ1xuICAgIHwgJ3NldHRpbmdzLnJvb3REaXIuZGVzYydcbiAgICB8ICdzZXR0aW5ncy5yb290RGlyLnNhdmVkJ1xuICAgIHwgJ3NldHRpbmdzLnNhdmUnXG4gICAgfCAnc2V0dGluZ3MuYXV0b0dlbmVyYXRlJ1xuICAgIHwgJ3NldHRpbmdzLmF1dG9HZW5lcmF0ZS5kZXNjJ1xuICAgIHwgJ3NldHRpbmdzLm1vZGUubm9uZSdcbiAgICB8ICdzZXR0aW5ncy5tb2RlLmRhaWx5J1xuICAgIHwgJ3NldHRpbmdzLm1vZGUud29ya2RheSdcbiAgICB8ICdzZXR0aW5ncy5sYW5ndWFnZSdcbiAgICB8ICdzZXR0aW5ncy5sYW5ndWFnZS5kZXNjJ1xuICAgIHwgJ3NldHRpbmdzLmxhbmd1YWdlLmF1dG8nXG4gICAgfCAnc2V0dGluZ3MubGFuZ3VhZ2UuemgnXG4gICAgfCAnc2V0dGluZ3MubGFuZ3VhZ2UuZW4nXG4gICAgfCAnc2V0dGluZ3MuYW5pbWF0aW9ucydcbiAgICB8ICdzZXR0aW5ncy5hbmltYXRpb25zLmRlc2MnXG4gICAgfCAnc2V0dGluZ3MudGVtcGxhdGUnXG4gICAgfCAnc2V0dGluZ3MudGVtcGxhdGUuemgnXG4gICAgfCAnc2V0dGluZ3MudGVtcGxhdGUuZW4nXG4gICAgfCAnc2V0dGluZ3MudGVtcGxhdGUucHJldmlldydcbiAgICB8ICdzZXR0aW5ncy50ZW1wbGF0ZS5oaWRlJ1xuICAgIHwgJ3NldHRpbmdzLnJlc2V0RGVmYXVsdCdcbiAgICB8ICdzZXR0aW5ncy5hZGRUYXNrQnV0dG9uJ1xuICAgIHwgJ3NldHRpbmdzLnJlc2V0VG9EZWZhdWx0J1xuICAgIHwgJ3NldHRpbmdzLm5vdGlmaWNhdGlvbkR1cmF0aW9uJ1xuICAgIHwgJ3NldHRpbmdzLm5vdGlmaWNhdGlvbkR1cmF0aW9uLmRlc2MnXG4gICAgfCAnc2V0dGluZ3MucHJldmlldydcbiAgICB8ICd0ZW1wbGF0ZS5kYXRlV2l0aEljb24nXG4gICAgXG4gICAgLy8g6YCa55+lXG4gICAgfCAnbm90aWZpY2F0aW9uLnRhc2tBZGRlZCdcbiAgICB8ICdub3RpZmljYXRpb24udGFza0V4aXN0cydcbiAgICB8ICdub3RpZmljYXRpb24uZXJyb3InXG4gICAgXG4gICAgLy8g5pif5pyfXG4gICAgfCAnd2Vla2RheS5tb24nXG4gICAgfCAnd2Vla2RheS50dWUnXG4gICAgfCAnd2Vla2RheS53ZWQnXG4gICAgfCAnd2Vla2RheS50aHUnXG4gICAgfCAnd2Vla2RheS5mcmknXG4gICAgfCAnd2Vla2RheS5zYXQnXG4gICAgfCAnd2Vla2RheS5zdW4nXG4gICAgXG4gICAgLy8g5o+S5Lu25ZCN56ew5ZKM5o+P6L+wXG4gICAgfCAncGx1Z2luLm5hbWUnXG4gICAgfCAncGx1Z2luLmRlc2NyaXB0aW9uJ1xuICAgIFxuICAgIC8vIOS4gOiIrOaMiemSruWSjOa2iOaBr1xuICAgIHwgJ2J1dHRvbi5hZGRUYXNrJ1xuICAgIHwgJ2J1dHRvbi5zYXZlJ1xuICAgIHwgJ2J1dHRvbi5jYW5jZWwnXG4gICAgfCAnYnV0dG9uLmRvbmUnXG4gICAgXG4gICAgLy8g5re75Yqg5Lu75Yqh57uf6K6h55u45YWz57+76K+RXG4gICAgfCAnc2V0dGluZ3MudGFza1N0YXRpc3RpY3MnXG4gICAgfCAnc2V0dGluZ3MudGFza1N0YXRpc3RpY3MuZGVzYydcbiAgICB8ICdzdGF0aXN0aWNzLnRpdGxlJ1xuICAgIHwgJ3N0YXRpc3RpY3MudG90YWxUYXNrcydcbiAgICB8ICdzdGF0aXN0aWNzLmNvbXBsZXRlZFRhc2tzJ1xuICAgIHwgJ3N0YXRpc3RpY3MuY29tcGxldGlvblJhdGUnXG4gICAgfCAnc3RhdGlzdGljcy51bmZpbmlzaGVkVGFza3MnXG4gICAgfCAnc3RhdGlzdGljcy5zdWdnZXN0aW9ucyc7XG5cbi8vIOS4reaWh+e/u+ivkVxuY29uc3QgdHJhbnNsYXRpb25zWkg6IFJlY29yZDxUcmFuc2xhdGlvbktleSwgc3RyaW5nPiA9IHtcbiAgICAvLyDorr7nva7pobXpnaJcbiAgICAnc2V0dGluZ3MudGl0bGUnOiAn5q+P5pel5Lu75Yqh6Ieq5Yqo55Sf5oiQ5Zmo6K6+572uJyxcbiAgICAnc2V0dGluZ3Mucm9vdERpcic6ICfwn5OBIOS7u+WKoeaWh+S7tuWtmOaUvuebruW9lScsXG4gICAgJ3NldHRpbmdzLnJvb3REaXIuZGVzYyc6ICfmjIflrprkv53lrZjku7vliqHmlofku7bnmoTmoLnnm67lvZXvvIzku7vliqHlsIbmjIlcIuW5tOS7vS/mnIjku70ubWRcIuagvOW8j+WtmOWCqCcsXG4gICAgJ3NldHRpbmdzLnJvb3REaXIuc2F2ZWQnOiAn4pyTIOebruW9leW3suS/neWtmCcsXG4gICAgJ3NldHRpbmdzLnNhdmUnOiAn8J+SviDkv53lrZgnLFxuICAgICdzZXR0aW5ncy5hdXRvR2VuZXJhdGUnOiAn8J+UhCDoh6rliqjnlJ/miJDmqKHlvI8nLFxuICAgICdzZXR0aW5ncy5hdXRvR2VuZXJhdGUuZGVzYyc6ICfpgInmi6nkvZXml7boh6rliqjnlJ/miJDmr4/ml6Xku7vliqEnLFxuICAgICdzZXR0aW5ncy5tb2RlLm5vbmUnOiAn4p2MIOWFs+mXrScsXG4gICAgJ3NldHRpbmdzLm1vZGUuZGFpbHknOiAn8J+ThiDmr4/lpKknLFxuICAgICdzZXR0aW5ncy5tb2RlLndvcmtkYXknOiAn8J+SvCDku4Xlt6XkvZzml6UnLFxuICAgICdzZXR0aW5ncy5sYW5ndWFnZSc6ICfwn5SkIOeVjOmdouivreiogCcsXG4gICAgJ3NldHRpbmdzLmxhbmd1YWdlLmRlc2MnOiAn6YCJ5oup5o+S5Lu255WM6Z2i5pi+56S655qE6K+t6KiAJyxcbiAgICAnc2V0dGluZ3MubGFuZ3VhZ2UuYXV0byc6ICfwn5SNIOiHquWKqOajgOa1iycsXG4gICAgJ3NldHRpbmdzLmxhbmd1YWdlLnpoJzogJ/Cfh6jwn4ezIOS4reaWhycsXG4gICAgJ3NldHRpbmdzLmxhbmd1YWdlLmVuJzogJ/Cfh6zwn4enIOiLseaWhycsXG4gICAgJ3NldHRpbmdzLmFuaW1hdGlvbnMnOiAn4pyoIOWKqOeUu+aViOaenCcsXG4gICAgJ3NldHRpbmdzLmFuaW1hdGlvbnMuZGVzYyc6ICflkK/nlKjnlYzpnaLliqjnlLvmlYjmnpwnLFxuICAgICdzZXR0aW5ncy50ZW1wbGF0ZSc6ICfwn5OdIOS7u+WKoeaooeadvycsXG4gICAgJ3NldHRpbmdzLnRlbXBsYXRlLnpoJzogJ/Cfh6jwn4ezIOS4reaWh+aooeadvycsXG4gICAgJ3NldHRpbmdzLnRlbXBsYXRlLmVuJzogJ/Cfh6zwn4enIOiLseaWh+aooeadvycsXG4gICAgJ3NldHRpbmdzLnRlbXBsYXRlLnByZXZpZXcnOiAn8J+Rge+4jyDmmL7npLrpooTop4gnLFxuICAgICdzZXR0aW5ncy50ZW1wbGF0ZS5oaWRlJzogJ/CfkYHvuI/igI3wn5eo77iPIOmakOiXj+mihOiniCcsXG4gICAgJ3NldHRpbmdzLnJlc2V0VG9EZWZhdWx0JzogJ/CflIQg5oGi5aSN6buY6K6k6K6+572uJyxcbiAgICAnc2V0dGluZ3MuYWRkVGFza0J1dHRvbic6ICfinpUg5omL5Yqo5re75Yqg5LuK5pel5Lu75YqhJyxcbiAgICAnc2V0dGluZ3Mubm90aWZpY2F0aW9uRHVyYXRpb24nOiAn4o+x77iPIOmAmuefpeaYvuekuuaXtumXtCcsXG4gICAgJ3NldHRpbmdzLm5vdGlmaWNhdGlvbkR1cmF0aW9uLmRlc2MnOiAn5oiQ5YqfL+Wksei0peaPkOekuuaYvuekuuaXtumXtO+8iOavq+enku+8iScsXG4gICAgJ3NldHRpbmdzLnByZXZpZXcnOiAn6aKE6KeI5qih5p2/5pWI5p6cJyxcbiAgICAnc2V0dGluZ3MucmVzZXREZWZhdWx0JzogJ+aBouWkjem7mOiupOiuvue9ricsXG4gICAgJ3RlbXBsYXRlLmRhdGVXaXRoSWNvbic6ICfluKblm77moIfnmoTlvZPliY3ml6XmnJ8nLFxuICAgICdzZXR0aW5ncy5iYXNpY1NldHRpbmdzJzogJ+WfuuacrOiuvue9ricsXG4gICAgJ3NldHRpbmdzLnRlbXBsYXRlU2V0dGluZ3MnOiAn5qih5p2/6K6+572uJyxcbiAgICBcbiAgICAvLyDpgJrnn6VcbiAgICAnbm90aWZpY2F0aW9uLnRhc2tBZGRlZCc6ICfku4rml6Xku7vliqHlt7Lmt7vliqAnLFxuICAgICdub3RpZmljYXRpb24udGFza0V4aXN0cyc6ICfku4rml6Xku7vliqHlt7LlrZjlnKgnLFxuICAgICdub3RpZmljYXRpb24uZXJyb3InOiAn6ZSZ6K+v77yaJyxcbiAgICBcbiAgICAvLyDmmJ/mnJ9cbiAgICAnd2Vla2RheS5tb24nOiAn5pif5pyf5LiAJyxcbiAgICAnd2Vla2RheS50dWUnOiAn5pif5pyf5LqMJyxcbiAgICAnd2Vla2RheS53ZWQnOiAn5pif5pyf5LiJJyxcbiAgICAnd2Vla2RheS50aHUnOiAn5pif5pyf5ZubJyxcbiAgICAnd2Vla2RheS5mcmknOiAn5pif5pyf5LqUJyxcbiAgICAnd2Vla2RheS5zYXQnOiAn5pif5pyf5YWtJyxcbiAgICAnd2Vla2RheS5zdW4nOiAn5pif5pyf5pelJyxcbiAgICBcbiAgICAvLyDmj5Lku7blkI3np7Dlkozmj4/ov7BcbiAgICAncGx1Z2luLm5hbWUnOiAn5q+P5pel5Lu75Yqh6Ieq5Yqo55Sf5oiQ5ZmoJyxcbiAgICAncGx1Z2luLmRlc2NyaXB0aW9uJzogJ+S4gOS4quW8uuWkp+eahOS7u+WKoeiHquWKqOeUn+aIkOWZqO+8jOW4ruWKqeS9oOmrmOaViOWcsOeuoeeQhuaXpeW4uOS7u+WKoScsXG4gICAgXG4gICAgLy8g5LiA6Iis5oyJ6ZKu5ZKM5raI5oGvXG4gICAgJ2J1dHRvbi5hZGRUYXNrJzogJ+a3u+WKoOS7u+WKoScsXG4gICAgJ2J1dHRvbi5zYXZlJzogJ+S/neWtmCcsXG4gICAgJ2J1dHRvbi5jYW5jZWwnOiAn5Y+W5raIJyxcbiAgICAnYnV0dG9uLmRvbmUnOiAn5a6M5oiQJyxcbiAgICBcbiAgICAvLyDmt7vliqDku7vliqHnu5/orqHnm7jlhbPnv7vor5FcbiAgICAnc2V0dGluZ3MudGFza1N0YXRpc3RpY3MnOiAn8J+TiiDku7vliqHlrozmiJDnu5/orqEnLFxuICAgICdzZXR0aW5ncy50YXNrU3RhdGlzdGljcy5kZXNjJzogJ+W8gOWQr+WQju+8jOavj+aXpeeUn+aIkOS7u+WKoeWJjeS8muiHquWKqOe7n+iuoeWJjeS4gOWkqeeahOS7u+WKoeWujOaIkOaDheWGtScsXG4gICAgJ3N0YXRpc3RpY3MudGl0bGUnOiAn8J+TiiDmmKjml6Xku7vliqHnu5/orqEnLFxuICAgICdzdGF0aXN0aWNzLnRvdGFsVGFza3MnOiAn5Lu75Yqh5oC75pWwJyxcbiAgICAnc3RhdGlzdGljcy5jb21wbGV0ZWRUYXNrcyc6ICflt7LlrozmiJDku7vliqEnLFxuICAgICdzdGF0aXN0aWNzLmNvbXBsZXRpb25SYXRlJzogJ+WujOaIkOeOhycsXG4gICAgJ3N0YXRpc3RpY3MudW5maW5pc2hlZFRhc2tzJzogJ+acquWujOaIkOeahOS7u+WKoScsXG4gICAgJ3N0YXRpc3RpY3Muc3VnZ2VzdGlvbnMnOiAn5bu66K6u5LuK5pel6ICD6JmR5a6M5oiQ5Lul5LiL5Lu75YqhJyxcbn07XG5cbi8vIOiLseaWh+e/u+ivkVxuY29uc3QgdHJhbnNsYXRpb25zRU46IFJlY29yZDxUcmFuc2xhdGlvbktleSwgc3RyaW5nPiA9IHtcbiAgICAvLyDorr7nva7pobXpnaJcbiAgICAnc2V0dGluZ3MudGl0bGUnOiAnRGFpbHkgVGFzayBBdXRvIEdlbmVyYXRvciBTZXR0aW5ncycsXG4gICAgJ3NldHRpbmdzLnJvb3REaXInOiAn8J+TgSBUYXNrIERpcmVjdG9yeScsXG4gICAgJ3NldHRpbmdzLnJvb3REaXIuZGVzYyc6ICdTcGVjaWZ5IHRoZSByb290IGRpcmVjdG9yeSBmb3Igc3RvcmluZyB0YXNrIGZpbGVzLCB0YXNrcyB3aWxsIGJlIHN0b3JlZCBpbiBcIlllYXIvTW9udGgubWRcIiBmb3JtYXQnLFxuICAgICdzZXR0aW5ncy5yb290RGlyLnNhdmVkJzogJ+KckyBEaXJlY3Rvcnkgc2F2ZWQnLFxuICAgICdzZXR0aW5ncy5zYXZlJzogJ/Cfkr4gU2F2ZScsXG4gICAgJ3NldHRpbmdzLmF1dG9HZW5lcmF0ZSc6ICfwn5SEIEF1dG8gR2VuZXJhdGUgTW9kZScsXG4gICAgJ3NldHRpbmdzLmF1dG9HZW5lcmF0ZS5kZXNjJzogJ0Nob29zZSB3aGVuIHRvIGF1dG9tYXRpY2FsbHkgZ2VuZXJhdGUgZGFpbHkgdGFza3MnLFxuICAgICdzZXR0aW5ncy5tb2RlLm5vbmUnOiAn4p2MIE9mZicsXG4gICAgJ3NldHRpbmdzLm1vZGUuZGFpbHknOiAn8J+ThiBEYWlseScsXG4gICAgJ3NldHRpbmdzLm1vZGUud29ya2RheSc6ICfwn5K8IFdvcmtkYXlzIE9ubHknLFxuICAgICdzZXR0aW5ncy5sYW5ndWFnZSc6ICfwn5SkIEludGVyZmFjZSBMYW5ndWFnZScsXG4gICAgJ3NldHRpbmdzLmxhbmd1YWdlLmRlc2MnOiAnU2VsZWN0IHRoZSBsYW5ndWFnZSBmb3IgdGhlIHBsdWdpbiBpbnRlcmZhY2UnLFxuICAgICdzZXR0aW5ncy5sYW5ndWFnZS5hdXRvJzogJ/CflI0gQXV0byBEZXRlY3QnLFxuICAgICdzZXR0aW5ncy5sYW5ndWFnZS56aCc6ICfwn4eo8J+HsyBDaGluZXNlJyxcbiAgICAnc2V0dGluZ3MubGFuZ3VhZ2UuZW4nOiAn8J+HrPCfh6cgRW5nbGlzaCcsXG4gICAgJ3NldHRpbmdzLmFuaW1hdGlvbnMnOiAn4pyoIEFuaW1hdGlvbiBFZmZlY3RzJyxcbiAgICAnc2V0dGluZ3MuYW5pbWF0aW9ucy5kZXNjJzogJ0VuYWJsZSBpbnRlcmZhY2UgYW5pbWF0aW9uIGVmZmVjdHMnLFxuICAgICdzZXR0aW5ncy50ZW1wbGF0ZSc6ICfwn5OdIFRhc2sgVGVtcGxhdGUnLFxuICAgICdzZXR0aW5ncy50ZW1wbGF0ZS56aCc6ICfwn4eo8J+HsyBDaGluZXNlIFRlbXBsYXRlJyxcbiAgICAnc2V0dGluZ3MudGVtcGxhdGUuZW4nOiAn8J+HrPCfh6cgRW5nbGlzaCBUZW1wbGF0ZScsXG4gICAgJ3NldHRpbmdzLnRlbXBsYXRlLnByZXZpZXcnOiAn8J+Rge+4jyBTaG93IFByZXZpZXcnLFxuICAgICdzZXR0aW5ncy50ZW1wbGF0ZS5oaWRlJzogJ/CfkYHvuI/igI3wn5eo77iPIEhpZGUgUHJldmlldycsXG4gICAgJ3NldHRpbmdzLnJlc2V0VG9EZWZhdWx0JzogJ/CflIQgUmVzZXQgdG8gRGVmYXVsdCcsXG4gICAgJ3NldHRpbmdzLmFkZFRhc2tCdXR0b24nOiAn4p6VIEFkZCBUb2RheVxcJ3MgVGFzayBNYW51YWxseScsXG4gICAgJ3NldHRpbmdzLm5vdGlmaWNhdGlvbkR1cmF0aW9uJzogJ+KPse+4jyBOb3RpZmljYXRpb24gRHVyYXRpb24nLFxuICAgICdzZXR0aW5ncy5ub3RpZmljYXRpb25EdXJhdGlvbi5kZXNjJzogJ0R1cmF0aW9uIHRvIHNob3cgc3VjY2Vzcy9mYWlsdXJlIG5vdGlmaWNhdGlvbnMgKG1pbGxpc2Vjb25kcyknLFxuICAgICdzZXR0aW5ncy5wcmV2aWV3JzogJ1ByZXZpZXcgVGVtcGxhdGUnLFxuICAgICdzZXR0aW5ncy5yZXNldERlZmF1bHQnOiAnUmVzZXQgdG8gRGVmYXVsdCcsXG4gICAgJ3RlbXBsYXRlLmRhdGVXaXRoSWNvbic6ICdDdXJyZW50IGRhdGUgd2l0aCBpY29uJyxcbiAgICAnc2V0dGluZ3MuYmFzaWNTZXR0aW5ncyc6ICdCYXNpYyBTZXR0aW5ncycsXG4gICAgJ3NldHRpbmdzLnRlbXBsYXRlU2V0dGluZ3MnOiAnVGVtcGxhdGUgU2V0dGluZ3MnLFxuICAgIFxuICAgIC8vIOmAmuefpVxuICAgICdub3RpZmljYXRpb24udGFza0FkZGVkJzogJ1RvZGF5XFwncyB0YXNrIGhhcyBiZWVuIGFkZGVkJyxcbiAgICAnbm90aWZpY2F0aW9uLnRhc2tFeGlzdHMnOiAnVG9kYXlcXCdzIHRhc2sgYWxyZWFkeSBleGlzdHMnLFxuICAgICdub3RpZmljYXRpb24uZXJyb3InOiAnRXJyb3I6ICcsXG4gICAgXG4gICAgLy8g5pif5pyfXG4gICAgJ3dlZWtkYXkubW9uJzogJ01vbmRheScsXG4gICAgJ3dlZWtkYXkudHVlJzogJ1R1ZXNkYXknLFxuICAgICd3ZWVrZGF5LndlZCc6ICdXZWRuZXNkYXknLFxuICAgICd3ZWVrZGF5LnRodSc6ICdUaHVyc2RheScsXG4gICAgJ3dlZWtkYXkuZnJpJzogJ0ZyaWRheScsXG4gICAgJ3dlZWtkYXkuc2F0JzogJ1NhdHVyZGF5JyxcbiAgICAnd2Vla2RheS5zdW4nOiAnU3VuZGF5JyxcbiAgICBcbiAgICAvLyDmj5Lku7blkI3np7Dlkozmj4/ov7BcbiAgICAncGx1Z2luLm5hbWUnOiAnRGFpbHkgVGFzayBBdXRvIEdlbmVyYXRvcicsXG4gICAgJ3BsdWdpbi5kZXNjcmlwdGlvbic6ICdBIHBvd2VyZnVsIHRhc2sgYXV0byBnZW5lcmF0b3IgdG8gaGVscCB5b3UgZWZmaWNpZW50bHkgbWFuYWdlIGRhaWx5IHRhc2tzJyxcbiAgICBcbiAgICAvLyDkuIDoiKzmjInpkq7lkozmtojmga9cbiAgICAnYnV0dG9uLmFkZFRhc2snOiAnQWRkIFRhc2snLFxuICAgICdidXR0b24uc2F2ZSc6ICdTYXZlJyxcbiAgICAnYnV0dG9uLmNhbmNlbCc6ICdDYW5jZWwnLFxuICAgICdidXR0b24uZG9uZSc6ICdEb25lJyxcbiAgICBcbiAgICAvLyDmt7vliqDku7vliqHnu5/orqHnm7jlhbPnv7vor5FcbiAgICAnc2V0dGluZ3MudGFza1N0YXRpc3RpY3MnOiAn8J+TiiBUYXNrIENvbXBsZXRpb24gU3RhdGlzdGljcycsXG4gICAgJ3NldHRpbmdzLnRhc2tTdGF0aXN0aWNzLmRlc2MnOiAnV2hlbiBlbmFibGVkLCBhdXRvbWF0aWNhbGx5IGFuYWx5emUgeWVzdGVyZGF5XFwncyB0YXNrIGNvbXBsZXRpb24gYmVmb3JlIGdlbmVyYXRpbmcgdG9kYXlcXCdzIHRhc2tzJyxcbiAgICAnc3RhdGlzdGljcy50aXRsZSc6ICfwn5OKIFllc3RlcmRheVxcJ3MgVGFzayBTdW1tYXJ5JyxcbiAgICAnc3RhdGlzdGljcy50b3RhbFRhc2tzJzogJ1RvdGFsIFRhc2tzJyxcbiAgICAnc3RhdGlzdGljcy5jb21wbGV0ZWRUYXNrcyc6ICdDb21wbGV0ZWQgVGFza3MnLFxuICAgICdzdGF0aXN0aWNzLmNvbXBsZXRpb25SYXRlJzogJ0NvbXBsZXRpb24gUmF0ZScsXG4gICAgJ3N0YXRpc3RpY3MudW5maW5pc2hlZFRhc2tzJzogJ1VuZmluaXNoZWQgVGFza3MnLFxuICAgICdzdGF0aXN0aWNzLnN1Z2dlc3Rpb25zJzogJ0NvbnNpZGVyIGNvbXBsZXRpbmcgdGhlIGZvbGxvd2luZyB0YXNrcyB0b2RheScsXG59O1xuXG4vLyDnv7vor5Hmn6Xmib7ooahcbmNvbnN0IHRyYW5zbGF0aW9uczogUmVjb3JkPHN0cmluZywgUmVjb3JkPFRyYW5zbGF0aW9uS2V5LCBzdHJpbmc+PiA9IHtcbiAgICAnemgnOiB0cmFuc2xhdGlvbnNaSCxcbiAgICAnZW4nOiB0cmFuc2xhdGlvbnNFTlxufTtcblxuLy8g5b2T5YmN6K+t6KiAXG5sZXQgY3VycmVudExhbmd1YWdlID0gJ2VuJztcblxuLyoqXG4gKiDorr7nva7lvZPliY3or63oqIBcbiAqIEBwYXJhbSBsYW5ndWFnZSDor63oqIDku6PnoIFcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNldEN1cnJlbnRMYW5ndWFnZShsYW5ndWFnZTogc3RyaW5nKTogdm9pZCB7XG4gICAgY3VycmVudExhbmd1YWdlID0gbGFuZ3VhZ2U7XG59XG5cbi8qKlxuICog6I635Y+W57+76K+R5paH5pysXG4gKiBAcGFyYW0ga2V5IOe/u+ivkemUrlxuICogQHBhcmFtIGZhbGxiYWNrTGFuZ3VhZ2Ug5aSH55So6K+t6KiAXG4gKiBAcmV0dXJucyDnv7vor5HlkI7nmoTmlofmnKxcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFRyYW5zbGF0aW9uKGtleTogVHJhbnNsYXRpb25LZXksIGZhbGxiYWNrTGFuZ3VhZ2U/OiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIGNvbnN0IGxhbmd1YWdlID0gZmFsbGJhY2tMYW5ndWFnZSB8fCBjdXJyZW50TGFuZ3VhZ2U7XG4gICAgXG4gICAgLy8g6I635Y+W5a+55bqU6K+t6KiA55qE57+76K+RXG4gICAgY29uc3QgdHJhbnNsYXRpb24gPSB0cmFuc2xhdGlvbnNbbGFuZ3VhZ2VdPy5ba2V5XTtcbiAgICBcbiAgICAvLyDlpoLmnpzmsqHmnInmib7liLDlr7nlupTnv7vor5HvvIzlsJ3or5Xkvb/nlKjoi7HmlofvvIzlho3msqHmnInliJnov5Tlm57plK7lkI1cbiAgICBpZiAoIXRyYW5zbGF0aW9uKSB7XG4gICAgICAgIHJldHVybiB0cmFuc2xhdGlvbnNbJ2VuJ11ba2V5XSB8fCBrZXk7XG4gICAgfVxuICAgIFxuICAgIHJldHVybiB0cmFuc2xhdGlvbjtcbn1cblxuLyoqXG4gKiDojrflj5bmmJ/mnJ/lh6DnmoTmnKzlnLDljJblkI3np7BcbiAqIEBwYXJhbSBkYXlPZldlZWsg5pif5pyf5Yeg55qE5pWw5a2X6KGo56S6ICgwLTYsIDDku6PooajmmJ/mnJ/ml6UpXG4gKiBAcmV0dXJucyDmnKzlnLDljJbnmoTmmJ/mnJ/lkI3np7BcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldExvY2FsaXplZFdlZWtkYXkoZGF5T2ZXZWVrOiBudW1iZXIpOiBzdHJpbmcge1xuICAgIGNvbnN0IHdlZWtkYXlLZXlzOiBUcmFuc2xhdGlvbktleVtdID0gW1xuICAgICAgICAnd2Vla2RheS5zdW4nLCAnd2Vla2RheS5tb24nLCAnd2Vla2RheS50dWUnLFxuICAgICAgICAnd2Vla2RheS53ZWQnLCAnd2Vla2RheS50aHUnLCAnd2Vla2RheS5mcmknLCAnd2Vla2RheS5zYXQnXG4gICAgXTtcbiAgICBcbiAgICAvLyDnoa7kv51kYXlPZldlZWvlnKjmnInmlYjojIPlm7TlhoVcbiAgICBjb25zdCBub3JtYWxpemVkRGF5T2ZXZWVrID0gKChkYXlPZldlZWsgJSA3KSArIDcpICUgNztcbiAgICByZXR1cm4gZ2V0VHJhbnNsYXRpb24od2Vla2RheUtleXNbbm9ybWFsaXplZERheU9mV2Vla10pO1xufSAiXX0= \ No newline at end of file diff --git a/src/i18n/i18n.ts b/src/i18n/i18n.ts new file mode 100644 index 0000000..71b5a84 --- /dev/null +++ b/src/i18n/i18n.ts @@ -0,0 +1,326 @@ +/** + * i18n.ts + * 国际化支持模块 + */ + +// 定义翻译键类型 +export type TranslationKey = + // 设置页面 + | 'settings.title' + | 'settings.rootDir' + | 'settings.rootDir.desc' + | 'settings.rootDir.saved' + | 'settings.save' + | 'settings.autoGenerate' + | 'settings.autoGenerate.desc' + | 'settings.mode.none' + | 'settings.mode.daily' + | 'settings.mode.workday' + | 'settings.language' + | 'settings.language.desc' + | 'settings.language.auto' + | 'settings.language.zh' + | 'settings.language.en' + | 'settings.animations' + | 'settings.animations.desc' + | 'settings.template' + | 'settings.template.zh' + | 'settings.template.en' + | 'settings.template.preview' + | 'settings.template.hide' + | 'settings.resetDefault' + | 'settings.addTaskButton' + | 'settings.resetToDefault' + | 'settings.notificationDuration' + | 'settings.notificationDuration.desc' + | 'settings.preview' + | 'template.dateWithIcon' + + // 通知 + | 'notification.taskAdded' + | 'notification.taskExists' + | 'notification.error' + + // 星期 + | 'weekday.mon' + | 'weekday.tue' + | 'weekday.wed' + | 'weekday.thu' + | 'weekday.fri' + | 'weekday.sat' + | 'weekday.sun' + + // 插件名称和描述 + | 'plugin.name' + | 'plugin.description' + + // 一般按钮和消息 + | 'button.addTask' + | 'button.save' + | 'button.cancel' + | 'button.done' + + // 设置 + | 'settings.basicSettings' + | 'settings.templateSettings' + | 'settings.rootDir' + | 'settings.rootDir.desc' + | 'settings.rootDir.saved' + | 'settings.save' + | 'settings.autoGenerate' + | 'settings.autoGenerate.desc' + | 'settings.mode.none' + | 'settings.mode.daily' + | 'settings.mode.workday' + | 'settings.language' + | 'settings.language.desc' + | 'settings.language.auto' + | 'settings.language.zh' + | 'settings.language.en' + | 'settings.animations' + | 'settings.animations.desc' + | 'settings.template' + | 'settings.template.zh' + | 'settings.template.en' + | 'settings.template.preview' + | 'settings.template.hide' + | 'settings.resetDefault' + | 'settings.addTaskButton' + | 'settings.resetToDefault' + | 'settings.notificationDuration' + | 'settings.notificationDuration.desc' + | 'settings.preview' + | 'template.dateWithIcon' + + // 通知 + | 'notification.taskAdded' + | 'notification.taskExists' + | 'notification.error' + + // 星期 + | 'weekday.mon' + | 'weekday.tue' + | 'weekday.wed' + | 'weekday.thu' + | 'weekday.fri' + | 'weekday.sat' + | 'weekday.sun' + + // 插件名称和描述 + | 'plugin.name' + | 'plugin.description' + + // 一般按钮和消息 + | 'button.addTask' + | 'button.save' + | 'button.cancel' + | 'button.done' + + // 添加任务统计相关翻译 + | 'settings.taskStatistics' + | 'settings.taskStatistics.desc' + | 'statistics.title' + | 'statistics.totalTasks' + | 'statistics.completedTasks' + | 'statistics.completionRate' + | 'statistics.unfinishedTasks' + | 'statistics.suggestions' + | 'statistics.moreTasks.singular' + | 'statistics.moreTasks.plural'; + +// 中文翻译 +const translationsZH: Record = { + // 设置页面 + 'settings.title': '每日任务自动生成器设置', + 'settings.rootDir': '📁 任务文件存放目录', + 'settings.rootDir.desc': '指定保存任务文件的根目录,任务将按"年份/月份.md"格式存储', + 'settings.rootDir.saved': '✓ 目录已保存', + 'settings.save': '💾 保存', + 'settings.autoGenerate': '🔄 自动生成模式', + 'settings.autoGenerate.desc': '选择何时自动生成每日任务', + 'settings.mode.none': '❌ 关闭', + 'settings.mode.daily': '📆 每天', + 'settings.mode.workday': '💼 仅工作日', + 'settings.language': '🔤 界面语言', + 'settings.language.desc': '选择插件界面显示的语言', + 'settings.language.auto': '🔍 自动检测', + 'settings.language.zh': '🇨🇳 中文', + 'settings.language.en': '🇬🇧 英文', + 'settings.animations': '✨ 动画效果', + 'settings.animations.desc': '启用界面动画效果', + 'settings.template': '📝 任务模板', + 'settings.template.zh': '🇨🇳 中文模板', + 'settings.template.en': '🇬🇧 英文模板', + 'settings.template.preview': '👁️ 显示预览', + 'settings.template.hide': '👁️‍🗨️ 隐藏预览', + 'settings.resetToDefault': '🔄 恢复默认设置', + 'settings.addTaskButton': '➕ 手动添加今日任务', + 'settings.notificationDuration': '⏱️ 通知显示时间', + 'settings.notificationDuration.desc': '成功/失败提示显示时间(毫秒)', + 'settings.preview': '预览模板效果', + 'settings.resetDefault': '恢复默认设置', + 'template.dateWithIcon': '带图标的当前日期', + 'settings.basicSettings': '基本设置', + 'settings.templateSettings': '模板设置', + + // 通知 + 'notification.taskAdded': '今日任务已添加', + 'notification.taskExists': '今日任务已存在', + 'notification.error': '错误:', + + // 星期 + 'weekday.mon': '星期一', + 'weekday.tue': '星期二', + 'weekday.wed': '星期三', + 'weekday.thu': '星期四', + 'weekday.fri': '星期五', + 'weekday.sat': '星期六', + 'weekday.sun': '星期日', + + // 插件名称和描述 + 'plugin.name': '每日任务自动生成器', + 'plugin.description': '一个强大的任务自动生成器,帮助你高效地管理日常任务', + + // 一般按钮和消息 + 'button.addTask': '添加任务', + 'button.save': '保存', + 'button.cancel': '取消', + 'button.done': '完成', + + // 添加任务统计相关翻译 + 'settings.taskStatistics': '📊 任务完成统计', + 'settings.taskStatistics.desc': '开启后,每日生成任务前会自动统计前一天的任务完成情况', + 'statistics.title': '📊 昨日任务统计', + 'statistics.totalTasks': '任务总数', + 'statistics.completedTasks': '已完成任务', + 'statistics.completionRate': '完成率', + 'statistics.unfinishedTasks': '未完成的任务', + 'statistics.suggestions': '建议今日考虑完成以下任务', + 'statistics.moreTasks.singular': '还有1个未完成任务', + 'statistics.moreTasks.plural': '还有更多未完成任务', +}; + +// 英文翻译 +const translationsEN: Record = { + // 设置页面 + 'settings.title': 'Daily Task Auto Generator Settings', + 'settings.rootDir': '📁 Task Directory', + 'settings.rootDir.desc': 'Specify the root directory for storing task files, tasks will be stored in "Year/Month.md" format', + 'settings.rootDir.saved': '✓ Directory saved', + 'settings.save': '💾 Save', + 'settings.autoGenerate': '🔄 Auto Generate Mode', + 'settings.autoGenerate.desc': 'Choose when to automatically generate daily tasks', + 'settings.mode.none': '❌ Off', + 'settings.mode.daily': '📆 Daily', + 'settings.mode.workday': '💼 Workdays Only', + 'settings.language': '🔤 Interface Language', + 'settings.language.desc': 'Select the language for the plugin interface', + 'settings.language.auto': '🔍 Auto Detect', + 'settings.language.zh': '🇨🇳 Chinese', + 'settings.language.en': '🇬🇧 English', + 'settings.animations': '✨ Animation Effects', + 'settings.animations.desc': 'Enable interface animation effects', + 'settings.template': '📝 Task Template', + 'settings.template.zh': '🇨🇳 Chinese Template', + 'settings.template.en': '🇬🇧 English Template', + 'settings.template.preview': '👁️ Show Preview', + 'settings.template.hide': '👁️‍🗨️ Hide Preview', + 'settings.resetToDefault': '🔄 Reset to Default', + 'settings.addTaskButton': '➕ Add Today\'s Task Manually', + 'settings.notificationDuration': '⏱️ Notification Duration', + 'settings.notificationDuration.desc': 'Duration to show success/failure notifications (milliseconds)', + 'settings.preview': 'Preview Template', + 'settings.resetDefault': 'Reset to Default', + 'template.dateWithIcon': 'Current date with icon', + 'settings.basicSettings': 'Basic Settings', + 'settings.templateSettings': 'Template Settings', + + // 通知 + 'notification.taskAdded': 'Today\'s task has been added', + 'notification.taskExists': 'Today\'s task already exists', + 'notification.error': 'Error: ', + + // 星期 + 'weekday.mon': 'Monday', + 'weekday.tue': 'Tuesday', + 'weekday.wed': 'Wednesday', + 'weekday.thu': 'Thursday', + 'weekday.fri': 'Friday', + 'weekday.sat': 'Saturday', + 'weekday.sun': 'Sunday', + + // 插件名称和描述 + 'plugin.name': 'Daily Task Auto Generator', + 'plugin.description': 'A powerful task auto generator to help you efficiently manage daily tasks', + + // 一般按钮和消息 + 'button.addTask': 'Add Task', + 'button.save': 'Save', + 'button.cancel': 'Cancel', + 'button.done': 'Done', + + // 添加任务统计相关翻译 + 'settings.taskStatistics': '📊 Task Completion Statistics', + 'settings.taskStatistics.desc': 'When enabled, automatically analyze yesterday\'s task completion before generating today\'s tasks', + 'statistics.title': '📊 Yesterday\'s Task Summary', + 'statistics.totalTasks': 'Total Tasks', + 'statistics.completedTasks': 'Completed Tasks', + 'statistics.completionRate': 'Completion Rate', + 'statistics.unfinishedTasks': 'Unfinished Tasks', + 'statistics.suggestions': 'Consider completing the following tasks today', + 'statistics.moreTasks.singular': 'more task', + 'statistics.moreTasks.plural': 'more tasks', +}; + +// 翻译查找表 +const translations: Record> = { + 'zh': translationsZH, + 'en': translationsEN +}; + +// 当前语言 +let currentLanguage = 'en'; + +/** + * 设置当前语言 + * @param language 语言代码 + */ +export function setCurrentLanguage(language: string): void { + currentLanguage = language; +} + +/** + * 获取翻译文本 + * @param key 翻译键 + * @param fallbackLanguage 备用语言 + * @returns 翻译后的文本 + */ +export function getTranslation(key: TranslationKey, fallbackLanguage?: string): string { + const language = fallbackLanguage || currentLanguage; + + // 获取对应语言的翻译 + const translation = translations[language]?.[key]; + + // 如果没有找到对应翻译,尝试使用英文,再没有则返回键名 + if (!translation) { + return translations['en'][key] || key; + } + + return translation; +} + +/** + * 获取星期几的本地化名称 + * @param dayOfWeek 星期几的数字表示 (0-6, 0代表星期日) + * @returns 本地化的星期名称 + */ +export function getLocalizedWeekday(dayOfWeek: number): string { + const weekdayKeys: TranslationKey[] = [ + 'weekday.sun', 'weekday.mon', 'weekday.tue', + 'weekday.wed', 'weekday.thu', 'weekday.fri', 'weekday.sat' + ]; + + // 确保dayOfWeek在有效范围内 + const normalizedDayOfWeek = ((dayOfWeek % 7) + 7) % 7; + return getTranslation(weekdayKeys[normalizedDayOfWeek]); +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..9a4101a --- /dev/null +++ b/src/main.ts @@ -0,0 +1,100 @@ +import { Plugin, addIcon } from 'obsidian'; +import { AutoGenerateMode } from './models/settings'; +import { DailyTaskSettingTab, SettingsManager } from './settings/settings'; +import { setCurrentLanguage } from './i18n/i18n'; +import { isWorkday } from './utils/dateUtils'; +import { TaskGenerator } from './taskGenerator'; + +// 定义插件图标 +const ICON = ` + +`; + +/** + * 每日任务自动生成器插件主类 + */ +export default class DailyTaskPlugin extends Plugin { + // 设置管理器 + settingsManager: SettingsManager; + + // 任务生成器 + taskGenerator: TaskGenerator; + + /** + * 插件加载时调用 + */ + async onload() { + console.log('Loading Daily Task Auto Generator plugin'); + + // 添加插件图标 + addIcon('daily-task', ICON); + + // 初始化设置管理器 + this.settingsManager = new SettingsManager(this); + await this.settingsManager.loadSettings(); + + // 初始化任务生成器 + this.taskGenerator = new TaskGenerator(this.app, this.settingsManager); + + // 设置语言 + setCurrentLanguage(this.settingsManager.getCurrentLanguage()); + + // 添加设置标签页 + this.addSettingTab(new DailyTaskSettingTab(this.app, this)); + + // 添加手动生成任务命令 + this.addCommand({ + id: 'add-daily-task', + name: '手动添加今日任务', + callback: async () => { + await this.taskGenerator.addTaskManually(); + } + }); + + // 延迟10秒后检查是否需要自动生成任务 + // 这样可以确保Obsidian完全加载,避免与启动过程冲突 + console.log('计划在10秒后检查是否需要自动生成任务'); + setTimeout(async () => { + console.log('开始检查是否需要自动生成任务'); + await this.checkAutoGenerate(); + }, 10000); + } + + /** + * 插件卸载时调用 + */ + onunload() { + console.log('Unloading Daily Task Auto Generator plugin'); + } + + /** + * 检查是否需要自动生成任务 + */ + private async checkAutoGenerate() { + const settings = this.settingsManager.getSettings(); + + switch (settings.autoGenerateMode) { + case AutoGenerateMode.DAILY: + // 每天自动生成(静默模式,不打开文件,减少日志) + console.log('根据设置,每天自动生成任务(静默模式)'); + await this.taskGenerator.generateDailyTask(false, true); + break; + + case AutoGenerateMode.WORKDAY: + // 工作日自动生成(静默模式,不打开文件,减少日志) + if (isWorkday()) { + console.log('根据设置,工作日自动生成任务(静默模式)'); + await this.taskGenerator.generateDailyTask(false, true); + } else { + console.log('今天不是工作日,跳过自动生成任务'); + } + break; + + case AutoGenerateMode.NONE: + default: + // 不自动生成 + console.log('已禁用自动生成任务功能'); + break; + } + } +} \ No newline at end of file diff --git a/src/models/settings.js b/src/models/settings.js new file mode 100644 index 0000000..ed0d11c --- /dev/null +++ b/src/models/settings.js @@ -0,0 +1,69 @@ +/** + * 自动生成模式枚举 + */ +export var AutoGenerateMode; +(function (AutoGenerateMode) { + AutoGenerateMode["NONE"] = "none"; + AutoGenerateMode["DAILY"] = "daily"; + AutoGenerateMode["WORKDAY"] = "workday"; // 仅工作日自动生成 +})(AutoGenerateMode || (AutoGenerateMode = {})); +/** + * 设置界面语言 + */ +export var Language; +(function (Language) { + Language["AUTO"] = "auto"; + Language["ZH"] = "zh"; + Language["EN"] = "en"; +})(Language || (Language = {})); +/** + * 默认中文模板 + */ +export const DEFAULT_TEMPLATE_ZH = `## {{dateWithIcon}}({{weekday}}) + +### 🧘 今日计划 +--- + +- [ ] 冥想 10 分钟 +- [ ] 复盘前一日计划 +- [ ] 阅读 20 页书 + +### 📝 工作任务 +--- + +- [ ] 整理今日工作计划 +- [ ] 完成重要项目进度 +`; +/** + * 默认英文模板 + */ +export const DEFAULT_TEMPLATE_EN = `## {{dateWithIcon}} ({{weekday}}) + +### 🧘 Today's Plan +--- + +- [ ] Meditate for 10 minutes +- [ ] Review yesterday's plan +- [ ] Read 20 pages + +### 📝 Work Tasks +--- + +- [ ] Organize today's work schedule +- [ ] Progress on important projects +`; +/** + * 默认设置 + */ +export const DEFAULT_SETTINGS = { + rootDir: 'DailyTasks', + autoGenerateMode: AutoGenerateMode.WORKDAY, + language: Language.AUTO, + templateZh: DEFAULT_TEMPLATE_ZH, + templateEn: DEFAULT_TEMPLATE_EN, + customTemplate: '', + hasCustomTemplate: false, + taskStatistics: false, + successNotificationDuration: 3000 +}; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2V0dGluZ3MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJzZXR0aW5ncy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUNILE1BQU0sQ0FBTixJQUFZLGdCQUlYO0FBSkQsV0FBWSxnQkFBZ0I7SUFDeEIsaUNBQWEsQ0FBQTtJQUNiLG1DQUFlLENBQUE7SUFDZix1Q0FBbUIsQ0FBQSxDQUFDLFdBQVc7QUFDbkMsQ0FBQyxFQUpXLGdCQUFnQixLQUFoQixnQkFBZ0IsUUFJM0I7QUFFRDs7R0FFRztBQUNILE1BQU0sQ0FBTixJQUFZLFFBSVg7QUFKRCxXQUFZLFFBQVE7SUFDaEIseUJBQWEsQ0FBQTtJQUNiLHFCQUFTLENBQUE7SUFDVCxxQkFBUyxDQUFBO0FBQ2IsQ0FBQyxFQUpXLFFBQVEsS0FBUixRQUFRLFFBSW5CO0FBNEJEOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sbUJBQW1CLEdBQUc7Ozs7Ozs7Ozs7Ozs7O0NBY2xDLENBQUM7QUFFRjs7R0FFRztBQUNILE1BQU0sQ0FBQyxNQUFNLG1CQUFtQixHQUFHOzs7Ozs7Ozs7Ozs7OztDQWNsQyxDQUFDO0FBRUY7O0dBRUc7QUFDSCxNQUFNLENBQUMsTUFBTSxnQkFBZ0IsR0FBc0I7SUFDL0MsT0FBTyxFQUFFLFlBQVk7SUFDckIsZ0JBQWdCLEVBQUUsZ0JBQWdCLENBQUMsT0FBTztJQUMxQyxRQUFRLEVBQUUsUUFBUSxDQUFDLElBQUk7SUFDdkIsVUFBVSxFQUFFLG1CQUFtQjtJQUMvQixVQUFVLEVBQUUsbUJBQW1CO0lBQy9CLGNBQWMsRUFBRSxFQUFFO0lBQ2xCLGlCQUFpQixFQUFFLEtBQUs7SUFDeEIsY0FBYyxFQUFFLEtBQUs7SUFDckIsMkJBQTJCLEVBQUUsSUFBSTtDQUNwQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiDoh6rliqjnlJ/miJDmqKHlvI/mnprkuL5cbiAqL1xuZXhwb3J0IGVudW0gQXV0b0dlbmVyYXRlTW9kZSB7XG4gICAgTk9ORSA9ICdub25lJywgICAgIC8vIOWFs+mXreiHquWKqOeUn+aIkFxuICAgIERBSUxZID0gJ2RhaWx5JywgICAvLyDmr4/ml6Xoh6rliqjnlJ/miJBcbiAgICBXT1JLREFZID0gJ3dvcmtkYXknIC8vIOS7heW3peS9nOaXpeiHquWKqOeUn+aIkFxufVxuXG4vKipcbiAqIOiuvue9rueVjOmdouivreiogFxuICovXG5leHBvcnQgZW51bSBMYW5ndWFnZSB7XG4gICAgQVVUTyA9ICdhdXRvJyxcbiAgICBaSCA9ICd6aCcsXG4gICAgRU4gPSAnZW4nXG59XG5cbi8qKlxuICog5o+S5Lu26K6+572u5o6l5Y+jXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRGFpbHlUYXNrU2V0dGluZ3Mge1xuICAgIC8vIOWfuuehgOmFjee9rlxuICAgIHJvb3REaXI6IHN0cmluZzsgICAgICAgICAgICAgICAvLyDku7vliqHmlofku7blrZjmlL7nmoTmoLnnm67lvZVcbiAgICBhdXRvR2VuZXJhdGVNb2RlOiBBdXRvR2VuZXJhdGVNb2RlOyAvLyDoh6rliqjnlJ/miJDmqKHlvI9cbiAgICBsYW5ndWFnZTogc3RyaW5nOyAgICAgICAgICAgIC8vIOeVjOmdouivreiogFxuXG4gICAgLy8g5qih5p2/6YWN572uXG4gICAgdGVtcGxhdGVaaDogc3RyaW5nOyAgICAgICAgICAgIC8vIOS4reaWh+S7u+WKoeaooeadv1xuICAgIHRlbXBsYXRlRW46IHN0cmluZzsgICAgICAgICAgICAvLyDoi7Hmlofku7vliqHmqKHmnb9cblxuICAgIC8vIOaWsOWinu+8mueUqOaIt+iHquWumuS5ieaooeadv1xuICAgIGN1c3RvbVRlbXBsYXRlOiBzdHJpbmc7XG5cbiAgICAvLyDmlrDlop7vvJrmoIforrDmmK/lkKbkvb/nlKjoh6rlrprkuYnmqKHmnb9cbiAgICBoYXNDdXN0b21UZW1wbGF0ZTogYm9vbGVhbjtcblxuICAgIC8vIOaWsOWinu+8muaYr+WQpuW8gOWQr+S7u+WKoee7n+iuoeWKn+iDvVxuICAgIHRhc2tTdGF0aXN0aWNzOiBib29sZWFuO1xuXG4gICAgLy8gVUnphY3nva5cbiAgICBzdWNjZXNzTm90aWZpY2F0aW9uRHVyYXRpb246IG51bWJlcjsgLy8g5oiQ5Yqf6YCa55+l5pi+56S65pe26Ze0KOavq+enkilcbn1cblxuLyoqXG4gKiDpu5jorqTkuK3mlofmqKHmnb9cbiAqL1xuZXhwb3J0IGNvbnN0IERFRkFVTFRfVEVNUExBVEVfWkggPSBgIyMge3tkYXRlV2l0aEljb259fe+8iHt7d2Vla2RheX1977yJXG5cbiMjIyDwn6eYIOS7iuaXpeiuoeWIklxuLS0tXG5cbi0gWyBdIOWGpeaDsyAxMCDliIbpkp8gIFxuLSBbIF0g5aSN55uY5YmN5LiA5pel6K6h5YiSICBcbi0gWyBdIOmYheivuyAyMCDpobXkuaZcblxuIyMjIPCfk50g5bel5L2c5Lu75YqhXG4tLS1cblxuLSBbIF0g5pW055CG5LuK5pel5bel5L2c6K6h5YiSXG4tIFsgXSDlrozmiJDph43opoHpobnnm67ov5vluqZcbmA7XG5cbi8qKlxuICog6buY6K6k6Iux5paH5qih5p2/XG4gKi9cbmV4cG9ydCBjb25zdCBERUZBVUxUX1RFTVBMQVRFX0VOID0gYCMjIHt7ZGF0ZVdpdGhJY29ufX0gKHt7d2Vla2RheX19KVxuXG4jIyMg8J+nmCBUb2RheSdzIFBsYW5cbi0tLVxuXG4tIFsgXSBNZWRpdGF0ZSBmb3IgMTAgbWludXRlcyAgXG4tIFsgXSBSZXZpZXcgeWVzdGVyZGF5J3MgcGxhbiAgXG4tIFsgXSBSZWFkIDIwIHBhZ2VzXG5cbiMjIyDwn5OdIFdvcmsgVGFza3Ncbi0tLVxuXG4tIFsgXSBPcmdhbml6ZSB0b2RheSdzIHdvcmsgc2NoZWR1bGVcbi0gWyBdIFByb2dyZXNzIG9uIGltcG9ydGFudCBwcm9qZWN0c1xuYDtcblxuLyoqXG4gKiDpu5jorqTorr7nva5cbiAqL1xuZXhwb3J0IGNvbnN0IERFRkFVTFRfU0VUVElOR1M6IERhaWx5VGFza1NldHRpbmdzID0ge1xuICAgIHJvb3REaXI6ICdEYWlseVRhc2tzJyxcbiAgICBhdXRvR2VuZXJhdGVNb2RlOiBBdXRvR2VuZXJhdGVNb2RlLldPUktEQVksXG4gICAgbGFuZ3VhZ2U6IExhbmd1YWdlLkFVVE8sXG4gICAgdGVtcGxhdGVaaDogREVGQVVMVF9URU1QTEFURV9aSCxcbiAgICB0ZW1wbGF0ZUVuOiBERUZBVUxUX1RFTVBMQVRFX0VOLFxuICAgIGN1c3RvbVRlbXBsYXRlOiAnJywgLy8g6buY6K6k5Li656m677yM6KGo56S65L2/55So6K+t6KiA55u45YWz55qE6buY6K6k5qih5p2/XG4gICAgaGFzQ3VzdG9tVGVtcGxhdGU6IGZhbHNlLCAvLyDpu5jorqTkuI3kvb/nlKjoh6rlrprkuYnmqKHmnb9cbiAgICB0YXNrU3RhdGlzdGljczogZmFsc2UsIC8vIOm7mOiupOWFs+mXreS7u+WKoee7n+iuoeWKn+iDvVxuICAgIHN1Y2Nlc3NOb3RpZmljYXRpb25EdXJhdGlvbjogMzAwMFxufTsgIl19 \ No newline at end of file diff --git a/src/models/settings.ts b/src/models/settings.ts new file mode 100644 index 0000000..5994d8b --- /dev/null +++ b/src/models/settings.ts @@ -0,0 +1,96 @@ +/** + * 自动生成模式枚举 + */ +export enum AutoGenerateMode { + NONE = 'none', // 关闭自动生成 + DAILY = 'daily', // 每日自动生成 + WORKDAY = 'workday' // 仅工作日自动生成 +} + +/** + * 设置界面语言 + */ +export enum Language { + AUTO = 'auto', + ZH = 'zh', + EN = 'en' +} + +/** + * 插件设置接口 + */ +export interface DailyTaskSettings { + // 基础配置 + rootDir: string; // 任务文件存放的根目录 + autoGenerateMode: AutoGenerateMode; // 自动生成模式 + language: string; // 界面语言 + + // 模板配置 + templateZh: string; // 中文任务模板 + templateEn: string; // 英文任务模板 + + // 新增:用户自定义模板 + customTemplate: string; + + // 新增:标记是否使用自定义模板 + hasCustomTemplate: boolean; + + // 新增:是否开启任务统计功能 + taskStatistics: boolean; + + // UI配置 + successNotificationDuration: number; // 成功通知显示时间(毫秒) +} + +/** + * 默认中文模板 + */ +export const DEFAULT_TEMPLATE_ZH = `## {{dateWithIcon}}({{weekday}}) + +### 🧘 今日计划 +--- + +- [ ] 冥想 10 分钟 +- [ ] 复盘前一日计划 +- [ ] 阅读 20 页书 + +### 📝 工作任务 +--- + +- [ ] 整理今日工作计划 +- [ ] 完成重要项目进度 +`; + +/** + * 默认英文模板 + */ +export const DEFAULT_TEMPLATE_EN = `## {{dateWithIcon}} ({{weekday}}) + +### 🧘 Today's Plan +--- + +- [ ] Meditate for 10 minutes +- [ ] Review yesterday's plan +- [ ] Read 20 pages + +### 📝 Work Tasks +--- + +- [ ] Organize today's work schedule +- [ ] Progress on important projects +`; + +/** + * 默认设置 + */ +export const DEFAULT_SETTINGS: DailyTaskSettings = { + rootDir: 'DailyTasks', + autoGenerateMode: AutoGenerateMode.WORKDAY, + language: Language.AUTO, + templateZh: DEFAULT_TEMPLATE_ZH, + templateEn: DEFAULT_TEMPLATE_EN, + customTemplate: '', // 默认为空,表示使用语言相关的默认模板 + hasCustomTemplate: false, // 默认不使用自定义模板 + taskStatistics: false, // 默认关闭任务统计功能 + successNotificationDuration: 3000 +}; \ No newline at end of file diff --git a/src/obsidian.d.ts b/src/obsidian.d.ts new file mode 100644 index 0000000..981999c --- /dev/null +++ b/src/obsidian.d.ts @@ -0,0 +1,136 @@ +/** + * Obsidian核心API类型声明 + */ + +declare module "obsidian" { + export class Plugin { + app: App; + manifest: PluginManifest; + loadData(): Promise; + saveData(data: any): Promise; + addSettingTab(settingTab: PluginSettingTab): void; + addCommand(command: Command): void; + } + + export interface App { + workspace: Workspace; + vault: Vault; + } + + export interface Workspace { + getLeaf(): WorkspaceLeaf; + } + + export interface WorkspaceLeaf { + openFile(file: TFile): Promise; + } + + export interface Vault { + getAbstractFileByPath(path: string): TAbstractFile | null; + createFolder(path: string): Promise; + create(path: string, data: string): Promise; + read(file: TFile): Promise; + modify(file: TFile, data: string): Promise; + } + + export class TAbstractFile { + path: string; + name: string; + vault: Vault; + } + + export class TFile extends TAbstractFile { + extension: string; + } + + export class TFolder extends TAbstractFile { + children: TAbstractFile[]; + } + + export interface PluginManifest { + id: string; + name: string; + version: string; + description: string; + } + + export interface Command { + id: string; + name: string; + callback: () => any; + checkCallback?: (checking: boolean) => boolean | void; + hotkeys?: Hotkey[]; + } + + export interface Hotkey { + modifiers: string[]; + key: string; + } + + export abstract class PluginSettingTab { + constructor(app: App, plugin: Plugin); + containerEl: HTMLElement; + abstract display(): void; + } + + export class Setting { + constructor(containerEl: HTMLElement); + setName(name: string): this; + setDesc(desc: string): this; + setClass(cls: string): this; + addText(cb: (text: TextComponent) => any): this; + addButton(cb: (button: ButtonComponent) => any): this; + addTextArea(cb: (text: TextAreaComponent) => any): this; + addToggle(cb: (toggle: ToggleComponent) => any): this; + addDropdown(cb: (dropdown: DropdownComponent) => any): this; + controlEl: HTMLElement; + } + + export class ButtonComponent { + constructor(containerEl: HTMLElement); + buttonEl: HTMLButtonElement; + setButtonText(name: string): this; + setDisabled(disabled: boolean): this; + setCta(): this; + onClick(callback: () => any): this; + } + + export class TextComponent { + constructor(containerEl: HTMLElement); + inputEl: HTMLInputElement; + setValue(value: string): this; + getValue(): string; + onChange(callback: (value: string) => any): this; + } + + export class TextAreaComponent { + constructor(containerEl: HTMLElement); + inputEl: HTMLTextAreaElement; + setValue(value: string): this; + getValue(): string; + setPlaceholder(placeholder: string): this; + onChange(callback: (value: string) => any): this; + } + + export class ToggleComponent { + constructor(containerEl: HTMLElement); + setValue(value: boolean): this; + onChange(callback: (value: boolean) => any): this; + } + + export class DropdownComponent { + constructor(containerEl: HTMLElement); + addOption(value: string, display: string): this; + setValue(value: string): this; + onChange(callback: (value: string) => any): this; + } + + export function addIcon(iconId: string, svgContent: string): void; + + export class Notice { + constructor(message: string, timeout?: number); + noticeEl: HTMLElement; + } + + export function normalizePath(path: string): string; +} \ No newline at end of file diff --git a/src/settings/index.ts b/src/settings/index.ts new file mode 100644 index 0000000..057194a --- /dev/null +++ b/src/settings/index.ts @@ -0,0 +1,9 @@ +/** + * 设置模块入口文件 + * 导出设置模块中的关键内容 + */ + +import { DEFAULT_SETTINGS } from '../models/settings'; + +export { DEFAULT_SETTINGS }; +export * from './settings'; \ No newline at end of file diff --git a/src/settings/settings.js b/src/settings/settings.js new file mode 100644 index 0000000..df73d5b --- /dev/null +++ b/src/settings/settings.js @@ -0,0 +1,692 @@ +import { ButtonComponent, Notice, PluginSettingTab, Setting, TextAreaComponent, TextComponent } from 'obsidian'; +import { AutoGenerateMode, DEFAULT_SETTINGS, DEFAULT_TEMPLATE_EN, DEFAULT_TEMPLATE_ZH, Language } from '../models/settings'; +import { getTranslation, setCurrentLanguage } from '../i18n/i18n'; +import { renderTemplate } from '../utils/templateEngine'; +import { TaskGenerator } from '../taskGenerator'; +// CSS 相关代码 +const SettingsSectionCSS = "daily-task-settings-section"; +const ButtonCSS = "daily-task-button"; +const PreviewButtonCSS = "daily-task-preview-button"; +const ResetButtonCSS = "daily-task-reset-button"; +const EditorCSS = "daily-task-editor"; +const VerticalStackCSS = "daily-task-vertical-stack"; +const TextRightCSS = "daily-task-text-right"; +const TextCenterCSS = "daily-task-text-center"; +const ScrollbarSlimCSS = "daily-task-slim-scrollbar"; +/** + * 添加插件自定义样式 + */ +function addCustomStyles() { + const cssText = ` + .${SettingsSectionCSS} { + margin-top: 24px; + margin-bottom: 24px; + padding: 12px 0; + border-top: 1px solid var(--background-modifier-border); + } + + .${ButtonCSS} { + margin-top: 6px; + margin-bottom: 6px; + } + + .${PreviewButtonCSS}, .${ResetButtonCSS} { + display: inline-block; + text-align: center !important; + width: 100%; + } + + .${EditorCSS} { + height: 200px; + margin-top: 12px; + margin-bottom: 12px; + } + + .${VerticalStackCSS} { + display: flex; + flex-direction: column; + } + + .${TextRightCSS} { + text-align: right; + } + + .${TextCenterCSS} { + text-align: center !important; + } + + .${ScrollbarSlimCSS} .CodeMirror-vscrollbar { + width: 20% !important; + } + + /* 自定义通知样式 */ + .daily-task-success-notice { + background-color: rgba(0, 255, 127, 0.2) !important; + } + + .daily-task-warning-notice { + background-color: rgba(255, 165, 0, 0.2) !important; + } + + .daily-task-error-notice { + background-color: rgba(255, 69, 0, 0.2) !important; + } + `; + // 添加自定义样式 + const styleElement = document.createElement('style'); + styleElement.textContent = cssText; + document.head.appendChild(styleElement); +} +/** + * 插件设置标签页 + */ +export class DailyTaskSettingTab extends PluginSettingTab { + constructor(app, plugin) { + super(app, plugin); + this.previewEl = null; + this.addTaskButton = null; + // 目录输入框 + this.rootDirInput = null; + // 标记设置是否已修改但未保存 + this.dirtySettings = false; + this.plugin = plugin; + // 获取父插件中的设置管理器引用 + this.settingsManager = plugin.settingsManager; + // 创建任务生成器 + this.taskGenerator = new TaskGenerator(app, this.settingsManager); + } + display() { + const { containerEl } = this; + containerEl.empty(); + containerEl.classList.add('daily-task-setting-tab'); + const settings = this.settingsManager.getSettings(); + // 添加顶部间距(增加间距以改善界面美观) + const topSpacing = containerEl.createEl('div'); + topSpacing.style.marginTop = '30px'; + // 根目录设置 + const rootDirSetting = new Setting(containerEl) + .setName(getTranslation('settings.rootDir')) + .setDesc(getTranslation('settings.rootDir.desc')); + // 创建输入框容器,使其可以包含额外元素 + const inputContainer = document.createElement('div'); + inputContainer.style.display = 'flex'; + inputContainer.style.width = '100%'; + inputContainer.style.position = 'relative'; + rootDirSetting.controlEl.appendChild(inputContainer); + this.rootDirInput = new TextComponent(inputContainer) + .setValue(settings.rootDir) + .onChange(async (value) => { + if (value.trim() !== '') { + // 设置已更改,准备自动保存 + this.dirtySettings = true; + // 启动自动保存定时器 + this.autoSaveRootDir(value); + } + }); + // 给input元素直接设置placeholder属性 + if (this.rootDirInput && this.rootDirInput.inputEl) { + this.rootDirInput.inputEl.placeholder = 'DailyTasks'; + // 增加宽度 + this.rootDirInput.inputEl.style.width = '100%'; + // 美化输入框样式 + this.rootDirInput.inputEl.style.borderRadius = '4px'; + this.rootDirInput.inputEl.style.padding = '8px 35px 8px 10px'; + this.rootDirInput.inputEl.style.transition = 'all 0.3s ease'; + } + // 添加自动保存指示器 + const saveIndicator = document.createElement('div'); + saveIndicator.classList.add('save-indicator'); + saveIndicator.style.position = 'absolute'; + saveIndicator.style.right = '10px'; + saveIndicator.style.top = '50%'; + saveIndicator.style.transform = 'translateY(-50%)'; + saveIndicator.style.opacity = '0'; + saveIndicator.style.transition = 'opacity 0.3s ease'; + inputContainer.appendChild(saveIndicator); + // 创建保存成功图标 + const saveSuccessIcon = createSpan({ cls: 'svg-icon lucide-check' }); + saveSuccessIcon.style.color = '#4CAF50'; + saveSuccessIcon.style.width = '18px'; + saveSuccessIcon.style.height = '18px'; + saveIndicator.appendChild(saveSuccessIcon); + // 记录自动保存定时器 + let autoSaveTimer = null; + // 自动保存方法 + this.autoSaveRootDir = async (value) => { + // 清除之前的定时器 + if (autoSaveTimer !== null) { + clearTimeout(autoSaveTimer); + } + // 设置新的定时器,延迟800ms保存(在用户停止输入后) + autoSaveTimer = setTimeout(async () => { + let pathToSave = value.trim(); + if (pathToSave === '') { + pathToSave = 'DailyTasks'; // 默认存放目录 + } + // 保存设置 + await this.settingsManager.updateSettings({ rootDir: pathToSave }); + this.dirtySettings = false; + // 显示保存成功指示器 + saveIndicator.style.opacity = '1'; + // 3秒后隐藏 + setTimeout(() => { + saveIndicator.style.opacity = '0'; + }, 2000); + }, 800); + }; + // 自动生成模式 + const autoGenSetting = new Setting(containerEl) + .setName(getTranslation('settings.autoGenerate')) + .setDesc(getTranslation('settings.autoGenerate.desc')); + // 自定义三选滑块 + const toggleContainer = document.createElement('div'); + toggleContainer.classList.add('mode-toggle-container'); + toggleContainer.style.width = '20%'; // 缩短滑动条长度为原来的20% + toggleContainer.style.marginLeft = 'auto'; // 靠右显示 + const modes = [ + { value: AutoGenerateMode.NONE, label: getTranslation('settings.mode.none') }, + { value: AutoGenerateMode.DAILY, label: getTranslation('settings.mode.daily') }, + { value: AutoGenerateMode.WORKDAY, label: getTranslation('settings.mode.workday') } + ]; + // 滑块指示器 + const slider = document.createElement('div'); + slider.classList.add('mode-toggle-slider'); + toggleContainer.appendChild(slider); + // 更新滑块位置 + const updateSlider = (index) => { + slider.style.left = `${index * 33.33}%`; + }; + // 设置默认模式为仅工作日生成(WorkDay) + let currentModeIndex = modes.findIndex(mode => mode.value === settings.autoGenerateMode); + if (currentModeIndex === -1) { + // 如果没有找到有效的模式,设置为工作日模式 + currentModeIndex = modes.findIndex(mode => mode.value === AutoGenerateMode.WORKDAY); + // 更新设置到工作日模式 + this.settingsManager.updateSettings({ autoGenerateMode: AutoGenerateMode.WORKDAY }); + } + updateSlider(currentModeIndex); + // 创建选项 + modes.forEach((mode, index) => { + const option = document.createElement('div'); + option.classList.add('mode-toggle-option'); + option.textContent = mode.label; + if (mode.value === settings.autoGenerateMode) { + option.classList.add('active'); + } + option.addEventListener('click', async () => { + // 更新视觉状态 + toggleContainer.querySelectorAll('.mode-toggle-option').forEach(el => { + el.classList.remove('active'); + }); + option.classList.add('active'); + // 更新滑块位置 + updateSlider(index); + // 保存设置 + await this.settingsManager.updateSettings({ autoGenerateMode: mode.value }); + }); + toggleContainer.appendChild(option); + }); + autoGenSetting.controlEl.appendChild(toggleContainer); + // 语言设置 + new Setting(containerEl) + .setName(getTranslation('settings.language')) + .setDesc(getTranslation('settings.language.desc')) + .addDropdown(dropdown => { + dropdown + .addOption(Language.AUTO, getTranslation('settings.language.auto')) + .addOption(Language.ZH, getTranslation('settings.language.zh')) + .addOption(Language.EN, getTranslation('settings.language.en')) + .setValue(settings.language) + .onChange(async (value) => { + await this.settingsManager.updateSettings({ language: value }); + // 需要重新加载设置页面以更新翻译 + this.display(); + }); + }); + // 通知显示时间 + new Setting(containerEl) + .setName(getTranslation('settings.notificationDuration')) + .setDesc(getTranslation('settings.notificationDuration.desc')) + .addText(text => { + const component = text + .setValue(settings.successNotificationDuration.toString()) + .onChange(async (value) => { + const duration = parseInt(value); + if (!isNaN(duration) && duration > 0) { + await this.settingsManager.updateSettings({ successNotificationDuration: duration }); + } + }); + // 直接设置placeholder属性 + component.inputEl.placeholder = '3000'; + return component; + }); + // 任务统计功能开关 + new Setting(containerEl) + .setName(getTranslation('settings.taskStatistics')) + .setDesc(getTranslation('settings.taskStatistics.desc')) + .addToggle((toggle) => { + // 设置开关样式 + const toggleEl = toggle + .setValue(settings.taskStatistics) + .onChange(async (value) => { + await this.settingsManager.updateSettings({ taskStatistics: value }); + }); + // 自定义开关样式 - 使用DOM元素访问 + // @ts-ignore - 添加这行来忽略TypeScript警告 + const toggleControl = toggle.toggleEl || toggle.containerEl.querySelector('.checkbox-container'); + if (toggleControl) { + toggleControl.classList.add('task-statistics-toggle'); + // 添加图标 - 使用DOM父元素访问 + const toggleContainer = toggleControl.parentElement; + if (toggleContainer) { + const iconEl = createSpan({ cls: 'svg-icon lucide-bar-chart-2' }); + iconEl.style.marginRight = '8px'; + iconEl.style.color = 'var(--text-accent)'; + toggleContainer.prepend(iconEl); + // 添加过渡效果 + toggleContainer.style.transition = 'all 0.3s ease'; + toggleControl.style.transition = 'all 0.3s ease'; + } + } + return toggleEl; + }); + // 模板设置 + containerEl.createEl('h3', { text: getTranslation('settings.template') }); + // 添加模板使用逻辑说明 + const templateDescription = containerEl.createEl('p', { + text: this.settingsManager.getCurrentLanguage() === 'zh' ? + '注意:默认模板会根据当前语言环境自动选择对应语言的内容。如果您自定义模板,将在所有语言环境中使用您的自定义内容。' : + 'Note: Default template automatically adapts to your language environment. If you customize the template, your content will be used in all language environments.' + }); + templateDescription.style.fontSize = '0.85em'; + templateDescription.style.opacity = '0.8'; + templateDescription.style.marginBottom = '15px'; + // 添加模板变量说明 + const templateVariablesEl = containerEl.createEl('p'); + templateVariablesEl.innerHTML = this.settingsManager.getCurrentLanguage() === 'zh' ? + '可用变量: {{date}} - 日期, {{dateWithIcon}} - 带图标的日期, {{weekday}} - 星期几, {{yearProgress}} - 年进度, {{monthProgress}} - 月进度, {{time}} - 当前时间' : + 'Available variables: {{date}} - Date, {{dateWithIcon}} - Date with icon, {{weekday}} - Day of week, {{yearProgress}} - Year progress, {{monthProgress}} - Month progress, {{time}} - Current time'; + templateVariablesEl.style.fontSize = '0.85em'; + templateVariablesEl.style.marginBottom = '10px'; + // 单一模板设置 + const templateSetting = new Setting(containerEl) + .setName(getTranslation('settings.template')) + .setClass('template-setting'); + const templateContainer = document.createElement('div'); + templateContainer.style.width = '100%'; + // 获取当前模板内容 + const currentTemplate = this.settingsManager.hasCustomTemplate() ? + this.settingsManager.getSettings().customTemplate : + this.settingsManager.getTemplateByLanguage(); + const textarea = new TextAreaComponent(templateContainer) + .setValue(currentTemplate) + .setPlaceholder(this.settingsManager.getCurrentLanguage() === 'zh' ? + '在此处输入任务模板...' : + 'Enter task template here...') + .onChange(async (value) => { + // 更新为自定义模板 + await this.settingsManager.updateSettings({ + customTemplate: value, + hasCustomTemplate: true + }); + this.updatePreview(this.previewEl, value); + }); + // 改进TextArea样式 + textarea.inputEl.classList.add('template-editor'); + textarea.inputEl.style.height = '200px'; + textarea.inputEl.style.border = '1px solid var(--background-modifier-border-hover)'; + textarea.inputEl.style.borderRadius = '4px'; + textarea.inputEl.style.padding = '12px'; + textarea.inputEl.style.lineHeight = '1.5'; + textarea.inputEl.style.fontSize = '14px'; + textarea.inputEl.style.fontFamily = 'var(--font-monospace)'; + textarea.inputEl.style.transition = 'all 0.2s ease'; + textarea.inputEl.style.boxShadow = 'inset 0 1px 3px rgba(0, 0, 0, 0.1)'; + textarea.inputEl.style.backgroundColor = 'var(--background-primary)'; + textarea.inputEl.style.color = 'var(--text-normal)'; + textarea.inputEl.style.resize = 'vertical'; + // 当获取焦点时改变边框样式 + textarea.inputEl.addEventListener('focus', () => { + textarea.inputEl.style.border = '1px solid var(--interactive-accent)'; + textarea.inputEl.style.boxShadow = '0 0 0 2px rgba(var(--interactive-accent-rgb), 0.2), inset 0 1px 3px rgba(0, 0, 0, 0.1)'; + textarea.inputEl.style.outline = 'none'; + }); + // 失去焦点时恢复原来的边框样式 + textarea.inputEl.addEventListener('blur', () => { + textarea.inputEl.style.border = '1px solid var(--background-modifier-border-hover)'; + textarea.inputEl.style.boxShadow = 'inset 0 1px 3px rgba(0, 0, 0, 0.1)'; + }); + // 预览标题,使用flex布局居中 + const previewHeader = document.createElement('div'); + previewHeader.classList.add('template-preview-header'); + previewHeader.style.display = 'flex'; + previewHeader.style.justifyContent = 'center'; + previewHeader.style.marginTop = '15px'; + previewHeader.style.gap = '10px'; + // 预览按钮 + const toggleButton = new ButtonComponent(previewHeader) + .setButtonText(getTranslation('settings.template.preview')); + // 添加样式类 + toggleButton.buttonEl.addClass(TextCenterCSS); + toggleButton.buttonEl.style.textAlign = 'center'; + toggleButton.buttonEl.style.display = 'flex'; + toggleButton.buttonEl.style.alignItems = 'center'; + toggleButton.buttonEl.style.justifyContent = 'center'; + toggleButton.buttonEl.style.width = '120px'; + // 手动添加眼睛图标 + const eyeIcon = createSpan({ cls: 'svg-icon lucide-eye' }); + toggleButton.buttonEl.prepend(eyeIcon); + // 预览区域 + this.previewEl = document.createElement('div'); + this.previewEl.classList.add('template-preview'); + this.previewEl.style.marginTop = '15px'; + this.previewEl.style.padding = '15px'; + this.previewEl.style.border = '1px dashed var(--background-modifier-border)'; + this.previewEl.style.borderRadius = '4px'; + this.previewEl.style.backgroundColor = 'var(--background-secondary)'; + this.previewEl.style.display = 'none'; + this.updatePreview(this.previewEl, currentTemplate); + templateContainer.appendChild(this.previewEl); + toggleButton.onClick(() => { + this.togglePreview(this.previewEl); + // 切换图标和按钮文本 + if (this.previewEl && this.previewEl.classList.contains('visible')) { + eyeIcon.className = 'svg-icon lucide-eye-off'; + toggleButton.setButtonText(getTranslation('settings.template.hide')); + } + else { + eyeIcon.className = 'svg-icon lucide-eye'; + toggleButton.setButtonText(getTranslation('settings.template.preview')); + } + }); + // 重置按钮 + const resetBtn = new ButtonComponent(previewHeader) + .setButtonText(getTranslation('settings.resetDefault')); + // 添加样式类 + resetBtn.buttonEl.addClass(TextCenterCSS); + resetBtn.buttonEl.style.textAlign = 'center'; + resetBtn.buttonEl.style.display = 'flex'; + resetBtn.buttonEl.style.alignItems = 'center'; + resetBtn.buttonEl.style.justifyContent = 'center'; + resetBtn.buttonEl.style.width = '120px'; + // 添加重置图标 + resetBtn.buttonEl.prepend(createSpan({ cls: 'svg-icon lucide-refresh-cw' })); + // 添加重置事件 + resetBtn.onClick(async () => { + // 将自定义模板设置为空,回到使用默认模板 + await this.settingsManager.updateSettings({ + customTemplate: '', + hasCustomTemplate: false + }); + // 获取当前语言的默认模板 + const defaultTemplate = this.settingsManager.getTemplateByLanguage(); + // 更新输入框和预览 + textarea.setValue(defaultTemplate); + this.updatePreview(this.previewEl, defaultTemplate); + }); + // 将按钮添加到标题栏 + previewHeader.appendChild(toggleButton.buttonEl); + previewHeader.appendChild(resetBtn.buttonEl); + templateContainer.appendChild(previewHeader); + templateSetting.controlEl.appendChild(templateContainer); + // 恢复默认设置 - 创建容器让按钮右对齐 + const resetContainer = document.createElement('div'); + resetContainer.style.display = 'flex'; + resetContainer.style.justifyContent = 'flex-end'; // 确保右对齐 + resetContainer.style.marginTop = '20px'; + resetContainer.style.marginBottom = '10px'; + containerEl.appendChild(resetContainer); + // 恢复默认设置按钮 + const resetDefaultBtn = new ButtonComponent(resetContainer) + .setButtonText(getTranslation('settings.resetToDefault')); + // 添加样式类 + resetDefaultBtn.buttonEl.addClass(TextCenterCSS); + resetDefaultBtn.buttonEl.addClass('danger-button'); + resetDefaultBtn.buttonEl.style.textAlign = 'center'; + resetDefaultBtn.buttonEl.style.display = 'flex'; + resetDefaultBtn.buttonEl.style.alignItems = 'center'; + resetDefaultBtn.buttonEl.style.justifyContent = 'center'; + resetDefaultBtn.buttonEl.style.width = '120px'; + // 添加重置图标和危险按钮样式 + resetDefaultBtn.buttonEl.prepend(createSpan({ cls: 'svg-icon lucide-refresh-cw' })); + // 为全局重置按钮添加事件处理 + resetDefaultBtn.onClick(async () => { + await this.settingsManager.resetToDefaults(); + this.display(); + }); + // 手动添加今日任务按钮 - 右对齐显示 + const addTaskContainer = document.createElement('div'); + addTaskContainer.style.display = 'flex'; + addTaskContainer.style.justifyContent = 'flex-end'; // 确保右对齐 + addTaskContainer.style.marginTop = '20px'; + containerEl.appendChild(addTaskContainer); + this.addTaskButton = new ButtonComponent(addTaskContainer) + .setButtonText(getTranslation('settings.addTaskButton')) + .setCta(); + // 添加样式类 - 确保按钮文字居中 + if (this.addTaskButton && this.addTaskButton.buttonEl) { + this.addTaskButton.buttonEl.addClass(TextCenterCSS); + this.addTaskButton.buttonEl.style.textAlign = 'center'; + this.addTaskButton.buttonEl.style.display = 'flex'; + this.addTaskButton.buttonEl.style.alignItems = 'center'; + this.addTaskButton.buttonEl.style.justifyContent = 'center'; + } + // 手动添加任务按钮事件处理 + this.addTaskButton.onClick(async () => { + var _a; + // 检查目录设置 + const rootDir = this.settingsManager.getSettings().rootDir; + // 添加loading状态 + if (this.addTaskButton && this.addTaskButton.buttonEl) { + this.addTaskButton.buttonEl.classList.add('loading'); + } + (_a = this.addTaskButton) === null || _a === void 0 ? void 0 : _a.setDisabled(true); + try { + // 添加任务 + await this.taskGenerator.addTaskManually(); + } + catch (e) { + console.error("添加任务出错:", e); + new Notice(`添加任务失败: ${e.message || e}`); + } + finally { + // 移除loading状态 + setTimeout(() => { + var _a; + if (this.addTaskButton && this.addTaskButton.buttonEl) { + this.addTaskButton.buttonEl.classList.remove('loading'); + } + (_a = this.addTaskButton) === null || _a === void 0 ? void 0 : _a.setDisabled(false); + }, 500); + } + }); + // 添加图标 + this.addTaskButton.buttonEl.prepend(createSpan({ cls: 'svg-icon lucide-calendar-plus' })); + } + /** + * 更新模板预览 + */ + updatePreview(previewEl, template) { + if (!previewEl) + return; + const renderedContent = renderTemplate(template); + // 使用MarkdownRenderer需要导入相关组件 + previewEl.innerHTML = renderedContent.replace(/\n/g, '
'); + } + /** + * 切换预览的显示/隐藏 + */ + togglePreview(previewEl) { + if (!previewEl) + return; + if (previewEl.style.display === 'none') { + previewEl.style.display = 'block'; + previewEl.classList.add('visible'); + } + else { + previewEl.style.display = 'none'; + previewEl.classList.remove('visible'); + } + } +} +/** + * 设置管理器 + * 负责加载、保存和提供设置访问接口 + */ +export class SettingsManager { + constructor(plugin) { + this.plugin = plugin; + this.settings = Object.assign({}, DEFAULT_SETTINGS); + } + /** + * 获取当前设置 + */ + getSettings() { + return this.settings; + } + /** + * 更新设置并保存 + * @param settings 要更新的设置 + */ + async updateSettings(settings) { + this.settings = Object.assign(Object.assign({}, this.settings), settings); + await this.saveSettings(); + // 更新当前语言 + this.updateCurrentLanguage(); + } + /** + * 保存设置到数据存储 + */ + async saveSettings() { + await this.plugin.saveData(this.settings); + } + /** + * 加载设置 + */ + async loadSettings() { + const loadedData = await this.plugin.loadData(); + if (loadedData) { + // 合并默认设置和已保存的设置 + this.settings = Object.assign(Object.assign({}, DEFAULT_SETTINGS), loadedData); + // 确保在升级插件后,新增的设置项也有默认值 + this.ensureSettingsCompleteness(); + } + else { + // 如果没有加载到数据,使用默认设置但将自动生成模式改为工作日 + this.settings = Object.assign(Object.assign({}, DEFAULT_SETTINGS), { autoGenerateMode: AutoGenerateMode.WORKDAY }); + } + // 更新当前语言 + this.updateCurrentLanguage(); + } + /** + * 确保设置完整性,为新增的设置项提供默认值 + */ + ensureSettingsCompleteness() { + const defaultKeys = Object.keys(DEFAULT_SETTINGS); + defaultKeys.forEach(key => { + // 如果当前设置中缺少某个默认设置项,添加默认值 + if (!(key in this.settings)) { + this.settings[key] = DEFAULT_SETTINGS[key]; + } + }); + } + /** + * 恢复默认设置 + */ + async resetToDefaults() { + this.settings = Object.assign({}, DEFAULT_SETTINGS); + await this.saveSettings(); + // 更新当前语言 + this.updateCurrentLanguage(); + } + /** + * 根据语言获取当前使用的模板 + * 如果当前模板不是默认模板,则不再区分语言 + */ + getCurrentTemplate() { + const language = this.getCurrentLanguage(); + // 中文环境 + if (language === 'zh') { + // 如果中文模板已被修改(不等于默认模板),使用中文模板 + if (this.settings.templateZh !== DEFAULT_TEMPLATE_ZH) { + return this.settings.templateZh; + } + // 如果英文模板已被修改,使用英文模板 + if (this.settings.templateEn !== DEFAULT_TEMPLATE_EN) { + return this.settings.templateEn; + } + // 如果都是默认模板,使用中文默认模板 + return this.settings.templateZh; + } + // 英文环境 + else { + // 如果英文模板已被修改(不等于默认模板),使用英文模板 + if (this.settings.templateEn !== DEFAULT_TEMPLATE_EN) { + return this.settings.templateEn; + } + // 如果中文模板已被修改,使用中文模板 + if (this.settings.templateZh !== DEFAULT_TEMPLATE_ZH) { + return this.settings.templateZh; + } + // 如果都是默认模板,使用英文默认模板 + return this.settings.templateEn; + } + } + /** + * 获取当前语言设置 + */ + getCurrentLanguage() { + if (this.settings.language === Language.AUTO) { + // 自动检测系统语言 + const systemLanguage = window.navigator.language.toLowerCase(); + return systemLanguage.startsWith('zh') ? 'zh' : 'en'; + } + return this.settings.language; + } + /** + * 更新当前语言 + */ + updateCurrentLanguage() { + const language = this.getCurrentLanguage(); + setCurrentLanguage(language); + } + /** + * 获取当前语言的模板 + */ + getTemplateByLanguage() { + const language = this.getCurrentLanguage(); + // 中文环境 + if (language === 'zh') { + // 如果中文模板已被修改(不等于默认模板),使用中文模板 + if (this.settings.templateZh !== DEFAULT_TEMPLATE_ZH) { + return this.settings.templateZh; + } + // 如果英文模板已被修改,使用英文模板 + if (this.settings.templateEn !== DEFAULT_TEMPLATE_EN) { + return this.settings.templateEn; + } + // 如果都是默认模板,使用中文默认模板 + return this.settings.templateZh; + } + // 英文环境 + else { + // 如果英文模板已被修改(不等于默认模板),使用英文模板 + if (this.settings.templateEn !== DEFAULT_TEMPLATE_EN) { + return this.settings.templateEn; + } + // 如果中文模板已被修改,使用中文模板 + if (this.settings.templateZh !== DEFAULT_TEMPLATE_ZH) { + return this.settings.templateZh; + } + // 如果都是默认模板,使用英文默认模板 + return this.settings.templateEn; + } + } + /** + * 检查是否存在自定义模板 + */ + hasCustomTemplate() { + return !!this.settings.customTemplate; + } +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2V0dGluZ3MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJzZXR0aW5ncy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQU8sZUFBZSxFQUFxQixNQUFNLEVBQVUsZ0JBQWdCLEVBQUUsT0FBTyxFQUFFLGlCQUFpQixFQUFFLGFBQWEsRUFBbUIsTUFBTSxVQUFVLENBQUM7QUFDakssT0FBTyxFQUFFLGdCQUFnQixFQUFFLGdCQUFnQixFQUFFLG1CQUFtQixFQUFFLG1CQUFtQixFQUFxQixRQUFRLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUMvSSxPQUFPLEVBQUUsY0FBYyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQ2xFLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN6RCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFTakQsV0FBVztBQUNYLE1BQU0sa0JBQWtCLEdBQUcsNkJBQTZCLENBQUM7QUFDekQsTUFBTSxTQUFTLEdBQUcsbUJBQW1CLENBQUM7QUFDdEMsTUFBTSxnQkFBZ0IsR0FBRywyQkFBMkIsQ0FBQztBQUNyRCxNQUFNLGNBQWMsR0FBRyx5QkFBeUIsQ0FBQztBQUNqRCxNQUFNLFNBQVMsR0FBRyxtQkFBbUIsQ0FBQztBQUN0QyxNQUFNLGdCQUFnQixHQUFHLDJCQUEyQixDQUFDO0FBQ3JELE1BQU0sWUFBWSxHQUFHLHVCQUF1QixDQUFDO0FBQzdDLE1BQU0sYUFBYSxHQUFHLHdCQUF3QixDQUFDO0FBQy9DLE1BQU0sZ0JBQWdCLEdBQUcsMkJBQTJCLENBQUM7QUFFckQ7O0dBRUc7QUFDSCxTQUFTLGVBQWU7SUFDcEIsTUFBTSxPQUFPLEdBQUc7V0FDVCxrQkFBa0I7Ozs7Ozs7V0FPbEIsU0FBUzs7Ozs7V0FLVCxnQkFBZ0IsTUFBTSxjQUFjOzs7Ozs7V0FNcEMsU0FBUzs7Ozs7O1dBTVQsZ0JBQWdCOzs7OztXQUtoQixZQUFZOzs7O1dBSVos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O1lBQ3BCLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxVQUFVLEtBQUssbUJBQW1CLEVBQUU7Z0JBQ2xELE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUM7YUFDbkM7WUFDRCxvQkFBb0I7WUFDcEIsT0FBTyxJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQztTQUNuQztRQUNELE9BQU87YUFDRjtZQUNELDZCQUE2QjtZQUM3QixJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsVUFBVSxLQUFLLG1CQUFtQixFQUFFO2dCQUNsRCxPQUFPLElBQUksQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDO2FBQ25DO1lBQ0Qsb0JBQW9CO1lBQ3BCLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxVQUFVLEtBQUssbUJBQW1CLEVBQUU7Z0JBQ2xELE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUM7YUFDbkM7WUFDRCxvQkFBb0I7WUFDcEIsT0FBTyxJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQztTQUNuQztJQUNMLENBQUM7SUFFRDs7T0FFRztJQUNILGlCQUFpQjtRQUNiLE9BQU8sQ0FBQyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsY0FBYyxDQUFDO0lBQzFDLENBQUM7Q0FDSiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEFwcCwgQnV0dG9uQ29tcG9uZW50LCBEcm9wZG93bkNvbXBvbmVudCwgTm90aWNlLCBQbHVnaW4sIFBsdWdpblNldHRpbmdUYWIsIFNldHRpbmcsIFRleHRBcmVhQ29tcG9uZW50LCBUZXh0Q29tcG9uZW50LCBUb2dnbGVDb21wb25lbnQgfSBmcm9tICdvYnNpZGlhbic7XG5pbXBvcnQgeyBBdXRvR2VuZXJhdGVNb2RlLCBERUZBVUxUX1NFVFRJTkdTLCBERUZBVUxUX1RFTVBMQVRFX0VOLCBERUZBVUxUX1RFTVBMQVRFX1pILCBEYWlseVRhc2tTZXR0aW5ncywgTGFuZ3VhZ2UgfSBmcm9tICcuLi9tb2RlbHMvc2V0dGluZ3MnO1xuaW1wb3J0IHsgZ2V0VHJhbnNsYXRpb24sIHNldEN1cnJlbnRMYW5ndWFnZSB9IGZyb20gJy4uL2kxOG4vaTE4bic7XG5pbXBvcnQgeyByZW5kZXJUZW1wbGF0ZSB9IGZyb20gJy4uL3V0aWxzL3RlbXBsYXRlRW5naW5lJztcbmltcG9ydCB7IFRhc2tHZW5lcmF0b3IgfSBmcm9tICcuLi90YXNrR2VuZXJhdG9yJztcblxuLy8g5YmN5ZCR5aOw5piO77yM6YG/5YWN5b6q546v5a+85YWlXG5kZWNsYXJlIGNsYXNzIERhaWx5VGFza1BsdWdpbiB7XG4gICAgc2F2ZURhdGEoZGF0YTogYW55KTogUHJvbWlzZTx2b2lkPjtcbiAgICBsb2FkRGF0YSgpOiBQcm9taXNlPGFueT47XG4gICAgYXBwOiBBcHA7XG59XG5cbi8vIENTUyDnm7jlhbPku6PnoIFcbmNvbnN0IFNldHRpbmdzU2VjdGlvbkNTUyA9IFwiZGFpbHktdGFzay1zZXR0aW5ncy1zZWN0aW9uXCI7XG5jb25zdCBCdXR0b25DU1MgPSBcImRhaWx5LXRhc2stYnV0dG9uXCI7XG5jb25zdCBQcmV2aWV3QnV0dG9uQ1NTID0gXCJkYWlseS10YXNrLXByZXZpZXctYnV0dG9uXCI7XG5jb25zdCBSZXNldEJ1dHRvbkNTUyA9IFwiZGFpbHktdGFzay1yZXNldC1idXR0b25cIjtcbmNvbnN0IEVkaXRvckNTUyA9IFwiZGFpbHktdGFzay1lZGl0b3JcIjtcbmNvbnN0IFZlcnRpY2FsU3RhY2tDU1MgPSBcImRhaWx5LXRhc2stdmVydGljYWwtc3RhY2tcIjtcbmNvbnN0IFRleHRSaWdodENTUyA9IFwiZGFpbHktdGFzay10ZXh0LXJpZ2h0XCI7XG5jb25zdCBUZXh0Q2VudGVyQ1NTID0gXCJkYWlseS10YXNrLXRleHQtY2VudGVyXCI7XG5jb25zdCBTY3JvbGxiYXJTbGltQ1NTID0gXCJkYWlseS10YXNrLXNsaW0tc2Nyb2xsYmFyXCI7XG5cbi8qKlxuICog5re75Yqg5o+S5Lu26Ieq5a6a5LmJ5qC35byPXG4gKi9cbmZ1bmN0aW9uIGFkZEN1c3RvbVN0eWxlcygpIHtcbiAgICBjb25zdCBjc3NUZXh0ID0gYFxuICAgICAgICAuJHtTZXR0aW5nc1NlY3Rpb25DU1N9IHtcbiAgICAgICAgICAgIG1hcmdpbi10b3A6IDI0cHg7XG4gICAgICAgICAgICBtYXJnaW4tYm90dG9tOiAyNHB4O1xuICAgICAgICAgICAgcGFkZGluZzogMTJweCAwO1xuICAgICAgICAgICAgYm9yZGVyLXRvcDogMXB4IHNvbGlkIHZhcigtLWJhY2tncm91bmQtbW9kaWZpZXItYm9yZGVyKTtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgLiR7QnV0dG9uQ1NTfSB7XG4gICAgICAgICAgICBtYXJnaW4tdG9wOiA2cHg7XG4gICAgICAgICAgICBtYXJnaW4tYm90dG9tOiA2cHg7XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIC4ke1ByZXZpZXdCdXR0b25DU1N9LCAuJHtSZXNldEJ1dHRvbkNTU30ge1xuICAgICAgICAgICAgZGlzcGxheTogaW5saW5lLWJsb2NrO1xuICAgICAgICAgICAgdGV4dC1hbGlnbjogY2VudGVyICFpbXBvcnRhbnQ7XG4gICAgICAgICAgICB3aWR0aDogMTAwJTtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgLiR7RWRpdG9yQ1NTfSB7XG4gICAgICAgICAgICBoZWlnaHQ6IDIwMHB4O1xuICAgICAgICAgICAgbWFyZ2luLXRvcDogMTJweDtcbiAgICAgICAgICAgIG1hcmdpbi1ib3R0b206IDEycHg7XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIC4ke1ZlcnRpY2FsU3RhY2tDU1N9IHtcbiAgICAgICAgICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgICAgICAgICBmbGV4LWRpcmVjdGlvbjogY29sdW1uO1xuICAgICAgICB9XG4gICAgICAgIFxuICAgICAgICAuJHtUZXh0UmlnaHRDU1N9IHtcbiAgICAgICAgICAgIHRleHQtYWxpZ246IHJpZ2h0O1xuICAgICAgICB9XG4gICAgICAgIFxuICAgICAgICAuJHtUZXh0Q2VudGVyQ1NTfSB7XG4gICAgICAgICAgICB0ZXh0LWFsaWduOiBjZW50ZXIgIWltcG9ydGFudDtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgLiR7U2Nyb2xsYmFyU2xpbUNTU30gLkNvZGVNaXJyb3ItdnNjcm9sbGJhciB7XG4gICAgICAgICAgICB3aWR0aDogMjAlICFpbXBvcnRhbnQ7XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIC8qIOiHquWumuS5iemAmuefpeagt+W8jyAqL1xuICAgICAgICAuZGFpbHktdGFzay1zdWNjZXNzLW5vdGljZSB7XG4gICAgICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiByZ2JhKDAsIDI1NSwgMTI3LCAwLjIpICFpbXBvcnRhbnQ7XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIC5kYWlseS10YXNrLXdhcm5pbmctbm90aWNlIHtcbiAgICAgICAgICAgIGJhY2tncm91bmQtY29sb3I6IHJnYmEoMjU1LCAxNjUsIDAsIDAuMikgIWltcG9ydGFudDtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgLmRhaWx5LXRhc2stZXJyb3Itbm90aWNlIHtcbiAgICAgICAgICAgIGJhY2tncm91bmQtY29sb3I6IHJnYmEoMjU1LCA2OSwgMCwgMC4yKSAhaW1wb3J0YW50O1xuICAgICAgICB9XG4gICAgYDtcbiAgICBcbiAgICAvLyDmt7vliqDoh6rlrprkuYnmoLflvI9cbiAgICBjb25zdCBzdHlsZUVsZW1lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzdHlsZScpO1xuICAgIHN0eWxlRWxlbWVudC50ZXh0Q29udGVudCA9IGNzc1RleHQ7XG4gICAgZG9jdW1lbnQuaGVhZC5hcHBlbmRDaGlsZChzdHlsZUVsZW1lbnQpO1xufVxuXG4vKipcbiAqIOaPkuS7tuiuvue9ruagh+etvumhtVxuICovXG5leHBvcnQgY2xhc3MgRGFpbHlUYXNrU2V0dGluZ1RhYiBleHRlbmRzIFBsdWdpblNldHRpbmdUYWIge1xuICAgIHBsdWdpbjogUGx1Z2luO1xuICAgIHNldHRpbmdzTWFuYWdlcjogU2V0dGluZ3NNYW5hZ2VyO1xuICAgIHRhc2tHZW5lcmF0b3I6IFRhc2tHZW5lcmF0b3I7XG4gICAgcHJldmlld0VsOiBIVE1MRWxlbWVudCB8IG51bGwgPSBudWxsO1xuICAgIGFkZFRhc2tCdXR0b246IEJ1dHRvbkNvbXBvbmVudCB8IG51bGwgPSBudWxsO1xuICAgIFxuICAgIC8vIOebruW9lei+k+WFpeahhlxuICAgIHJvb3REaXJJbnB1dDogVGV4dENvbXBvbmVudCB8IG51bGwgPSBudWxsO1xuICAgIFxuICAgIC8vIOagh+iusOiuvue9ruaYr+WQpuW3suS/ruaUueS9huacquS/neWtmFxuICAgIGRpcnR5U2V0dGluZ3M6IGJvb2xlYW4gPSBmYWxzZTtcbiAgICBcbiAgICAvLyDoh6rliqjkv53lrZjnm67lvZXnmoTmlrnms5VcbiAgICBhdXRvU2F2ZVJvb3REaXI6ICh2YWx1ZTogc3RyaW5nKSA9PiBQcm9taXNlPHZvaWQ+O1xuXG4gICAgY29uc3RydWN0b3IoYXBwOiBBcHAsIHBsdWdpbjogUGx1Z2luKSB7XG4gICAgICAgIHN1cGVyKGFwcCwgcGx1Z2luKTtcbiAgICAgICAgdGhpcy5wbHVnaW4gPSBwbHVnaW47XG4gICAgICAgIFxuICAgICAgICAvLyDojrflj5bniLbmj5Lku7bkuK3nmoTorr7nva7nrqHnkIblmajlvJXnlKhcbiAgICAgICAgdGhpcy5zZXR0aW5nc01hbmFnZXIgPSAocGx1Z2luIGFzIGFueSkuc2V0dGluZ3NNYW5hZ2VyO1xuICAgICAgICBcbiAgICAgICAgLy8g5Yib5bu65Lu75Yqh55Sf5oiQ5ZmoXG4gICAgICAgIHRoaXMudGFza0dlbmVyYXRvciA9IG5ldyBUYXNrR2VuZXJhdG9yKGFwcCwgdGhpcy5zZXR0aW5nc01hbmFnZXIpO1xuICAgIH1cblxuICAgIGRpc3BsYXkoKTogdm9pZCB7XG4gICAgICAgIGNvbnN0IHsgY29udGFpbmVyRWwgfSA9IHRoaXM7XG4gICAgICAgIGNvbnRhaW5lckVsLmVtcHR5KCk7XG4gICAgICAgIGNvbnRhaW5lckVsLmNsYXNzTGlzdC5hZGQoJ2RhaWx5LXRhc2stc2V0dGluZy10YWInKTtcbiAgICAgICAgXG4gICAgICAgIGNvbnN0IHNldHRpbmdzID0gdGhpcy5zZXR0aW5nc01hbmFnZXIuZ2V0U2V0dGluZ3MoKTtcbiAgICAgICAgXG4gICAgICAgIC8vIOa3u+WKoOmhtumDqOmXtOi3ne+8iOWinuWKoOmXtOi3neS7peaUueWWhOeVjOmdoue+juingu+8iVxuICAgICAgICBjb25zdCB0b3BTcGFjaW5nID0gY29udGFpbmVyRWwuY3JlYXRlRWwoJ2RpdicpO1xuICAgICAgICB0b3BTcGFjaW5nLnN0eWxlLm1hcmdpblRvcCA9ICczMHB4JztcbiAgICAgICAgXG4gICAgICAgIC8vIOagueebruW9leiuvue9rlxuICAgICAgICBjb25zdCByb290RGlyU2V0dGluZyA9IG5ldyBTZXR0aW5nKGNvbnRhaW5lckVsKVxuICAgICAgICAgICAgLnNldE5hbWUoZ2V0VHJhbnNsYXRpb24oJ3NldHRpbmdzLnJvb3REaXInKSlcbiAgICAgICAgICAgIC5zZXREZXNjKGdldFRyYW5zbGF0aW9uKCdzZXR0aW5ncy5yb290RGlyLmRlc2MnKSk7XG4gICAgICAgICAgICBcbiAgICAgICAgLy8g5Yib5bu66L6T5YWl5qGG5a655Zmo77yM5L2/5YW25Y+v5Lul5YyF5ZCr6aKd5aSW5YWD57SgXG4gICAgICAgIGNvbnN0IGlucHV0Q29udGFpbmVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICAgIGlucHV0Q29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPSAnZmxleCc7XG4gICAgICAgIGlucHV0Q29udGFpbmVyLnN0eWxlLndpZHRoID0gJzEwMCUnO1xuICAgICAgICBpbnB1dENvbnRhaW5lci5zdHlsZS5wb3NpdGlvbiA9ICdyZWxhdGl2ZSc7XG4gICAgICAgIHJvb3REaXJTZXR0aW5nLmNvbnRyb2xFbC5hcHBlbmRDaGlsZChpbnB1dENvbnRhaW5lcik7XG4gICAgICAgIFxuICAgICAgICB0aGlzLnJvb3REaXJJbnB1dCA9IG5ldyBUZXh0Q29tcG9uZW50KGlucHV0Q29udGFpbmVyKVxuICAgICAgICAgICAgLnNldFZhbHVlKHNldHRpbmdzLnJvb3REaXIpXG4gICAgICAgICAgICAub25DaGFuZ2UoYXN5bmMgKHZhbHVlKSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKHZhbHVlLnRyaW0oKSAhPT0gJycpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8g6K6+572u5bey5pu05pS577yM5YeG5aSH6Ieq5Yqo5L+d5a2YXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZGlydHlTZXR0aW5ncyA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgICAgICAvLyDlkK/liqjoh6rliqjkv53lrZjlrprml7blmahcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5hdXRvU2F2ZVJvb3REaXIodmFsdWUpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICBcbiAgICAgICAgLy8g57uZaW5wdXTlhYPntKDnm7TmjqXorr7nva5wbGFjZWhvbGRlcuWxnuaAp1xuICAgICAgICBpZiAodGhpcy5yb290RGlySW5wdXQgJiYgdGhpcy5yb290RGlySW5wdXQuaW5wdXRFbCkge1xuICAgICAgICAgICAgdGhpcy5yb290RGlySW5wdXQuaW5wdXRFbC5wbGFjZWhvbGRlciA9ICdEYWlseVRhc2tzJztcbiAgICAgICAgICAgIC8vIOWinuWKoOWuveW6plxuICAgICAgICAgICAgdGhpcy5yb290RGlySW5wdXQuaW5wdXRFbC5zdHlsZS53aWR0aCA9ICcxMDAlJztcbiAgICAgICAgICAgIC8vIOe+juWMlui+k+WFpeahhuagt+W8j1xuICAgICAgICAgICAgdGhpcy5yb290RGlySW5wdXQuaW5wdXRFbC5zdHlsZS5ib3JkZXJSYWRpdXMgPSAnNHB4JztcbiAgICAgICAgICAgIHRoaXMucm9vdERpcklucHV0LmlucHV0RWwuc3R5bGUucGFkZGluZyA9ICc4cHggMzVweCA4cHggMTBweCc7XG4gICAgICAgICAgICB0aGlzLnJvb3REaXJJbnB1dC5pbnB1dEVsLnN0eWxlLnRyYW5zaXRpb24gPSAnYWxsIDAuM3MgZWFzZSc7XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIC8vIOa3u+WKoOiHquWKqOS/neWtmOaMh+ekuuWZqFxuICAgICAgICBjb25zdCBzYXZlSW5kaWNhdG9yID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICAgIHNhdmVJbmRpY2F0b3IuY2xhc3NMaXN0LmFkZCgnc2F2ZS1pbmRpY2F0b3InKTtcbiAgICAgICAgc2F2ZUluZGljYXRvci5zdHlsZS5wb3NpdGlvbiA9ICdhYnNvbHV0ZSc7XG4gICAgICAgIHNhdmVJbmRpY2F0b3Iuc3R5bGUucmlnaHQgPSAnMTBweCc7XG4gICAgICAgIHNhdmVJbmRpY2F0b3Iuc3R5bGUudG9wID0gJzUwJSc7XG4gICAgICAgIHNhdmVJbmRpY2F0b3Iuc3R5bGUudHJhbnNmb3JtID0gJ3RyYW5zbGF0ZVkoLTUwJSknO1xuICAgICAgICBzYXZlSW5kaWNhdG9yLnN0eWxlLm9wYWNpdHkgPSAnMCc7XG4gICAgICAgIHNhdmVJbmRpY2F0b3Iuc3R5bGUudHJhbnNpdGlvbiA9ICdvcGFjaXR5IDAuM3MgZWFzZSc7XG4gICAgICAgIGlucHV0Q29udGFpbmVyLmFwcGVuZENoaWxkKHNhdmVJbmRpY2F0b3IpO1xuICAgICAgICBcbiAgICAgICAgLy8g5Yib5bu65L+d5a2Y5oiQ5Yqf5Zu+5qCHXG4gICAgICAgIGNvbnN0IHNhdmVTdWNjZXNzSWNvbiA9IGNyZWF0ZVNwYW4oe2NsczogJ3N2Zy1pY29uIGx1Y2lkZS1jaGVjayd9KTtcbiAgICAgICAgc2F2ZVN1Y2Nlc3NJY29uLnN0eWxlLmNvbG9yID0gJyM0Q0FGNTAnO1xuICAgICAgICBzYXZlU3VjY2Vzc0ljb24uc3R5bGUud2lkdGggPSAnMThweCc7XG4gICAgICAgIHNhdmVTdWNjZXNzSWNvbi5zdHlsZS5oZWlnaHQgPSAnMThweCc7XG4gICAgICAgIHNhdmVJbmRpY2F0b3IuYXBwZW5kQ2hpbGQoc2F2ZVN1Y2Nlc3NJY29uKTtcblxuICAgICAgICAvLyDorrDlvZXoh6rliqjkv53lrZjlrprml7blmahcbiAgICAgICAgbGV0IGF1dG9TYXZlVGltZXI6IG51bWJlciB8IG51bGwgPSBudWxsO1xuICAgICAgICBcbiAgICAgICAgLy8g6Ieq5Yqo5L+d5a2Y5pa55rOVXG4gICAgICAgIHRoaXMuYXV0b1NhdmVSb290RGlyID0gYXN5bmMgKHZhbHVlOiBzdHJpbmcpID0+IHtcbiAgICAgICAgICAgIC8vIOa4hemZpOS5i+WJjeeahOWumuaXtuWZqFxuICAgICAgICAgICAgaWYgKGF1dG9TYXZlVGltZXIgIT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICBjbGVhclRpbWVvdXQoYXV0b1NhdmVUaW1lcik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIOiuvue9ruaWsOeahOWumuaXtuWZqO+8jOW7tui/nzgwMG1z5L+d5a2Y77yI5Zyo55So5oi35YGc5q2i6L6T5YWl5ZCO77yJXG4gICAgICAgICAgICBhdXRvU2F2ZVRpbWVyID0gc2V0VGltZW91dChhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICAgICAgbGV0IHBhdGhUb1NhdmUgPSB2YWx1ZS50cmltKCk7XG4gICAgICAgICAgICAgICAgaWYgKHBhdGhUb1NhdmUgPT09ICcnKSB7XG4gICAgICAgICAgICAgICAgICAgIHBhdGhUb1NhdmUgPSAnRGFpbHlUYXNrcyc7IC8vIOm7mOiupOWtmOaUvuebruW9lVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAvLyDkv53lrZjorr7nva5cbiAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLnNldHRpbmdzTWFuYWdlci51cGRhdGVTZXR0aW5ncyh7IHJvb3REaXI6IHBhdGhUb1NhdmUgfSk7XG4gICAgICAgICAgICAgICAgdGhpcy5kaXJ0eVNldHRpbmdzID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgLy8g5pi+56S65L+d5a2Y5oiQ5Yqf5oyH56S65ZmoXG4gICAgICAgICAgICAgICAgc2F2ZUluZGljYXRvci5zdHlsZS5vcGFjaXR5ID0gJzEnO1xuICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgIC8vIDPnp5LlkI7pmpDol49cbiAgICAgICAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgc2F2ZUluZGljYXRvci5zdHlsZS5vcGFjaXR5ID0gJzAnO1xuICAgICAgICAgICAgICAgIH0sIDIwMDApO1xuICAgICAgICAgICAgfSwgODAwKSBhcyB1bmtub3duIGFzIG51bWJlcjtcbiAgICAgICAgfTtcbiAgICAgICAgXG4gICAgICAgIC8vIOiHquWKqOeUn+aIkOaooeW8j1xuICAgICAgICBjb25zdCBhdXRvR2VuU2V0dGluZyA9IG5ldyBTZXR0aW5nKGNvbnRhaW5lckVsKVxuICAgICAgICAgICAgLnNldE5hbWUoZ2V0VHJhbnNsYXRpb24oJ3NldHRpbmdzLmF1dG9HZW5lcmF0ZScpKVxuICAgICAgICAgICAgLnNldERlc2MoZ2V0VHJhbnNsYXRpb24oJ3NldHRpbmdzLmF1dG9HZW5lcmF0ZS5kZXNjJykpO1xuICAgICAgICAgICAgXG4gICAgICAgIC8vIOiHquWumuS5ieS4iemAiea7keWdl1xuICAgICAgICBjb25zdCB0b2dnbGVDb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICAgICAgdG9nZ2xlQ29udGFpbmVyLmNsYXNzTGlzdC5hZGQoJ21vZGUtdG9nZ2xlLWNvbnRhaW5lcicpO1xuICAgICAgICB0b2dnbGVDb250YWluZXIuc3R5bGUud2lkdGggPSAnMjAlJzsgLy8g57yp55+t5ruR5Yqo5p2h6ZW/5bqm5Li65Y6f5p2l55qEMjAlXG4gICAgICAgIHRvZ2dsZUNvbnRhaW5lci5zdHlsZS5tYXJnaW5MZWZ0ID0gJ2F1dG8nOyAvLyDpnaDlj7PmmL7npLpcbiAgICAgICAgXG4gICAgICAgIGNvbnN0IG1vZGVzID0gW1xuICAgICAgICAgICAgeyB2YWx1ZTogQXV0b0dlbmVyYXRlTW9kZS5OT05FLCBsYWJlbDogZ2V0VHJhbnNsYXRpb24oJ3NldHRpbmdzLm1vZGUubm9uZScpIH0sXG4gICAgICAgICAgICB7IHZhbHVlOiBBdXRvR2VuZXJhdGVNb2RlLkRBSUxZLCBsYWJlbDogZ2V0VHJhbnNsYXRpb24oJ3NldHRpbmdzLm1vZGUuZGFpbHknKSB9LFxuICAgICAgICAgICAgeyB2YWx1ZTogQXV0b0dlbmVyYXRlTW9kZS5XT1JLREFZLCBsYWJlbDogZ2V0VHJhbnNsYXRpb24oJ3NldHRpbmdzLm1vZGUud29ya2RheScpIH1cbiAgICAgICAgXTtcbiAgICAgICAgXG4gICAgICAgIC8vIOa7keWdl+aMh+ekuuWZqFxuICAgICAgICBjb25zdCBzbGlkZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICAgICAgc2xpZGVyLmNsYXNzTGlzdC5hZGQoJ21vZGUtdG9nZ2xlLXNsaWRlcicpO1xuICAgICAgICB0b2dnbGVDb250YWluZXIuYXBwZW5kQ2hpbGQoc2xpZGVyKTtcbiAgICAgICAgXG4gICAgICAgIC8vIOabtOaWsOa7keWdl+S9jee9rlxuICAgICAgICBjb25zdCB1cGRhdGVTbGlkZXIgPSAoaW5kZXg6IG51bWJlcikgPT4ge1xuICAgICAgICAgICAgc2xpZGVyLnN0eWxlLmxlZnQgPSBgJHtpbmRleCAqIDMzLjMzfSVgO1xuICAgICAgICB9O1xuICAgICAgICBcbiAgICAgICAgLy8g6K6+572u6buY6K6k5qih5byP5Li65LuF5bel5L2c5pel55Sf5oiQ77yIV29ya0Rhee+8iVxuICAgICAgICBsZXQgY3VycmVudE1vZGVJbmRleCA9IG1vZGVzLmZpbmRJbmRleChtb2RlID0+IG1vZGUudmFsdWUgPT09IHNldHRpbmdzLmF1dG9HZW5lcmF0ZU1vZGUpO1xuICAgICAgICBpZiAoY3VycmVudE1vZGVJbmRleCA9PT0gLTEpIHtcbiAgICAgICAgICAgIC8vIOWmguaenOayoeacieaJvuWIsOacieaViOeahOaooeW8j++8jOiuvue9ruS4uuW3peS9nOaXpeaooeW8j1xuICAgICAgICAgICAgY3VycmVudE1vZGVJbmRleCA9IG1vZGVzLmZpbmRJbmRleChtb2RlID0+IG1vZGUudmFsdWUgPT09IEF1dG9HZW5lcmF0ZU1vZGUuV09SS0RBWSk7XG4gICAgICAgICAgICAvLyDmm7TmlrDorr7nva7liLDlt6XkvZzml6XmqKHlvI9cbiAgICAgICAgICAgIHRoaXMuc2V0dGluZ3NNYW5hZ2VyLnVwZGF0ZVNldHRpbmdzKHsgYXV0b0dlbmVyYXRlTW9kZTogQXV0b0dlbmVyYXRlTW9kZS5XT1JLREFZIH0pO1xuICAgICAgICB9XG4gICAgICAgIHVwZGF0ZVNsaWRlcihjdXJyZW50TW9kZUluZGV4KTtcbiAgICAgICAgXG4gICAgICAgIC8vIOWIm+W7uumAiemhuVxuICAgICAgICBtb2Rlcy5mb3JFYWNoKChtb2RlLCBpbmRleCkgPT4ge1xuICAgICAgICAgICAgY29uc3Qgb3B0aW9uID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICAgICAgICBvcHRpb24uY2xhc3NMaXN0LmFkZCgnbW9kZS10b2dnbGUtb3B0aW9uJyk7XG4gICAgICAgICAgICBvcHRpb24udGV4dENvbnRlbnQgPSBtb2RlLmxhYmVsO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBpZiAobW9kZS52YWx1ZSA9PT0gc2V0dGluZ3MuYXV0b0dlbmVyYXRlTW9kZSkge1xuICAgICAgICAgICAgICAgIG9wdGlvbi5jbGFzc0xpc3QuYWRkKCdhY3RpdmUnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIFxuICAgICAgICAgICAgb3B0aW9uLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgYXN5bmMgKCkgPT4ge1xuICAgICAgICAgICAgICAgIC8vIOabtOaWsOinhuinieeKtuaAgVxuICAgICAgICAgICAgICAgIHRvZ2dsZUNvbnRhaW5lci5xdWVyeVNlbGVjdG9yQWxsKCcubW9kZS10b2dnbGUtb3B0aW9uJykuZm9yRWFjaChlbCA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGVsLmNsYXNzTGlzdC5yZW1vdmUoJ2FjdGl2ZScpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIG9wdGlvbi5jbGFzc0xpc3QuYWRkKCdhY3RpdmUnKTtcbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAvLyDmm7TmlrDmu5HlnZfkvY3nva5cbiAgICAgICAgICAgICAgICB1cGRhdGVTbGlkZXIoaW5kZXgpO1xuICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgIC8vIOS/neWtmOiuvue9rlxuICAgICAgICAgICAgICAgIGF3YWl0IHRoaXMuc2V0dGluZ3NNYW5hZ2VyLnVwZGF0ZVNldHRpbmdzKHsgYXV0b0dlbmVyYXRlTW9kZTogbW9kZS52YWx1ZSB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICB0b2dnbGVDb250YWluZXIuYXBwZW5kQ2hpbGQob3B0aW9uKTtcbiAgICAgICAgfSk7XG4gICAgICAgIFxuICAgICAgICBhdXRvR2VuU2V0dGluZy5jb250cm9sRWwuYXBwZW5kQ2hpbGQodG9nZ2xlQ29udGFpbmVyKTtcbiAgICAgICAgXG4gICAgICAgIC8vIOivreiogOiuvue9rlxuICAgICAgICBuZXcgU2V0dGluZyhjb250YWluZXJFbClcbiAgICAgICAgICAgIC5zZXROYW1lKGdldFRyYW5zbGF0aW9uKCdzZXR0aW5ncy5sYW5ndWFnZScpKVxuICAgICAgICAgICAgLnNldERlc2MoZ2V0VHJhbnNsYXRpb24oJ3NldHRpbmdzLmxhbmd1YWdlLmRlc2MnKSlcbiAgICAgICAgICAgIC5hZGREcm9wZG93bihkcm9wZG93biA9PiB7XG4gICAgICAgICAgICAgICAgZHJvcGRvd25cbiAgICAgICAgICAgICAgICAgICAgLmFkZE9wdGlvbihMYW5ndWFnZS5BVVRPLCBnZXRUcmFuc2xhdGlvbignc2V0dGluZ3MubGFuZ3VhZ2UuYXV0bycpKVxuICAgICAgICAgICAgICAgICAgICAuYWRkT3B0aW9uKExhbmd1YWdlLlpILCBnZXRUcmFuc2xhdGlvbignc2V0dGluZ3MubGFuZ3VhZ2UuemgnKSlcbiAgICAgICAgICAgICAgICAgICAgLmFkZE9wdGlvbihMYW5ndWFnZS5FTiwgZ2V0VHJhbnNsYXRpb24oJ3NldHRpbmdzLmxhbmd1YWdlLmVuJykpXG4gICAgICAgICAgICAgICAgICAgIC5zZXRWYWx1ZShzZXR0aW5ncy5sYW5ndWFnZSlcbiAgICAgICAgICAgICAgICAgICAgLm9uQ2hhbmdlKGFzeW5jICh2YWx1ZSkgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgYXdhaXQgdGhpcy5zZXR0aW5nc01hbmFnZXIudXBkYXRlU2V0dGluZ3MoeyBsYW5ndWFnZTogdmFsdWUgYXMgTGFuZ3VhZ2UgfSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyDpnIDopoHph43mlrDliqDovb3orr7nva7pobXpnaLku6Xmm7TmlrDnv7vor5FcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuZGlzcGxheSgpO1xuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICBcbiAgICAgICAgLy8g6YCa55+l5pi+56S65pe26Ze0XG4gICAgICAgIG5ldyBTZXR0aW5nKGNvbnRhaW5lckVsKVxuICAgICAgICAgICAgLnNldE5hbWUoZ2V0VHJhbnNsYXRpb24oJ3NldHRpbmdzLm5vdGlmaWNhdGlvbkR1cmF0aW9uJykpXG4gICAgICAgICAgICAuc2V0RGVzYyhnZXRUcmFuc2xhdGlvbignc2V0dGluZ3Mubm90aWZpY2F0aW9uRHVyYXRpb24uZGVzYycpKVxuICAgICAgICAgICAgLmFkZFRleHQodGV4dCA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgY29tcG9uZW50ID0gdGV4dFxuICAgICAgICAgICAgICAgICAgICAuc2V0VmFsdWUoc2V0dGluZ3Muc3VjY2Vzc05vdGlmaWNhdGlvbkR1cmF0aW9uLnRvU3RyaW5nKCkpXG4gICAgICAgICAgICAgICAgICAgIC5vbkNoYW5nZShhc3luYyAodmFsdWUpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGR1cmF0aW9uID0gcGFyc2VJbnQodmFsdWUpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFpc05hTihkdXJhdGlvbikgJiYgZHVyYXRpb24gPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYXdhaXQgdGhpcy5zZXR0aW5nc01hbmFnZXIudXBkYXRlU2V0dGluZ3MoeyBzdWNjZXNzTm90aWZpY2F0aW9uRHVyYXRpb246IGR1cmF0aW9uIH0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAvLyDnm7TmjqXorr7nva5wbGFjZWhvbGRlcuWxnuaAp1xuICAgICAgICAgICAgICAgIGNvbXBvbmVudC5pbnB1dEVsLnBsYWNlaG9sZGVyID0gJzMwMDAnO1xuICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgIHJldHVybiBjb21wb25lbnQ7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgXG4gICAgICAgIC8vIOS7u+WKoee7n+iuoeWKn+iDveW8gOWFs1xuICAgICAgICBuZXcgU2V0dGluZyhjb250YWluZXJFbClcbiAgICAgICAgICAgIC5zZXROYW1lKGdldFRyYW5zbGF0aW9uKCdzZXR0aW5ncy50YXNrU3RhdGlzdGljcycpKVxuICAgICAgICAgICAgLnNldERlc2MoZ2V0VHJhbnNsYXRpb24oJ3NldHRpbmdzLnRhc2tTdGF0aXN0aWNzLmRlc2MnKSlcbiAgICAgICAgICAgIC5hZGRUb2dnbGUoKHRvZ2dsZSkgPT4ge1xuICAgICAgICAgICAgICAgIC8vIOiuvue9ruW8gOWFs+agt+W8j1xuICAgICAgICAgICAgICAgIGNvbnN0IHRvZ2dsZUVsID0gdG9nZ2xlXG4gICAgICAgICAgICAgICAgICAgIC5zZXRWYWx1ZShzZXR0aW5ncy50YXNrU3RhdGlzdGljcylcbiAgICAgICAgICAgICAgICAgICAgLm9uQ2hhbmdlKGFzeW5jICh2YWx1ZSkgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgYXdhaXQgdGhpcy5zZXR0aW5nc01hbmFnZXIudXBkYXRlU2V0dGluZ3MoeyB0YXNrU3RhdGlzdGljczogdmFsdWUgfSk7XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAvLyDoh6rlrprkuYnlvIDlhbPmoLflvI8gLSDkvb/nlKhET03lhYPntKDorr/pl65cbiAgICAgICAgICAgICAgICAvLyBAdHMtaWdub3JlIC0g5re75Yqg6L+Z6KGM5p2l5b+955WlVHlwZVNjcmlwdOitpuWRilxuICAgICAgICAgICAgICAgIGNvbnN0IHRvZ2dsZUNvbnRyb2wgPSB0b2dnbGUudG9nZ2xlRWwgfHwgdG9nZ2xlLmNvbnRhaW5lckVsLnF1ZXJ5U2VsZWN0b3IoJy5jaGVja2JveC1jb250YWluZXInKTtcbiAgICAgICAgICAgICAgICBpZiAodG9nZ2xlQ29udHJvbCkge1xuICAgICAgICAgICAgICAgICAgICB0b2dnbGVDb250cm9sLmNsYXNzTGlzdC5hZGQoJ3Rhc2stc3RhdGlzdGljcy10b2dnbGUnKTtcbiAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgIC8vIOa3u+WKoOWbvuaghyAtIOS9v+eUqERPTeeItuWFg+e0oOiuv+mXrlxuICAgICAgICAgICAgICAgICAgICBjb25zdCB0b2dnbGVDb250YWluZXIgPSB0b2dnbGVDb250cm9sLnBhcmVudEVsZW1lbnQ7XG4gICAgICAgICAgICAgICAgICAgIGlmICh0b2dnbGVDb250YWluZXIpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGljb25FbCA9IGNyZWF0ZVNwYW4oe2NsczogJ3N2Zy1pY29uIGx1Y2lkZS1iYXItY2hhcnQtMid9KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGljb25FbC5zdHlsZS5tYXJnaW5SaWdodCA9ICc4cHgnO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWNvbkVsLnN0eWxlLmNvbG9yID0gJ3ZhcigtLXRleHQtYWNjZW50KSc7XG4gICAgICAgICAgICAgICAgICAgICAgICB0b2dnbGVDb250YWluZXIucHJlcGVuZChpY29uRWwpO1xuICAgICAgICAgICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyDmt7vliqDov4fmuKHmlYjmnpxcbiAgICAgICAgICAgICAgICAgICAgICAgIHRvZ2dsZUNvbnRhaW5lci5zdHlsZS50cmFuc2l0aW9uID0gJ2FsbCAwLjNzIGVhc2UnO1xuICAgICAgICAgICAgICAgICAgICAgICAgdG9nZ2xlQ29udHJvbC5zdHlsZS50cmFuc2l0aW9uID0gJ2FsbCAwLjNzIGVhc2UnO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgIHJldHVybiB0b2dnbGVFbDtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICBcbiAgICAgICAgLy8g5qih5p2/6K6+572uXG4gICAgICAgIGNvbnRhaW5lckVsLmNyZWF0ZUVsKCdoMycsIHsgdGV4dDogZ2V0VHJhbnNsYXRpb24oJ3NldHRpbmdzLnRlbXBsYXRlJykgfSk7XG4gICAgICAgIFxuICAgICAgICAvLyDmt7vliqDmqKHmnb/kvb/nlKjpgLvovpHor7TmmI5cbiAgICAgICAgY29uc3QgdGVtcGxhdGVEZXNjcmlwdGlvbiA9IGNvbnRhaW5lckVsLmNyZWF0ZUVsKCdwJywgeyBcbiAgICAgICAgICAgIHRleHQ6IHRoaXMuc2V0dGluZ3NNYW5hZ2VyLmdldEN1cnJlbnRMYW5ndWFnZSgpID09PSAnemgnID9cbiAgICAgICAgICAgICAgICAn5rOo5oSP77ya6buY6K6k5qih5p2/5Lya5qC55o2u5b2T5YmN6K+t6KiA546v5aKD6Ieq5Yqo6YCJ5oup5a+55bqU6K+t6KiA55qE5YaF5a6544CC5aaC5p6c5oKo6Ieq5a6a5LmJ5qih5p2/77yM5bCG5Zyo5omA5pyJ6K+t6KiA546v5aKD5Lit5L2/55So5oKo55qE6Ieq5a6a5LmJ5YaF5a6544CCJyA6XG4gICAgICAgICAgICAgICAgJ05vdGU6IERlZmF1bHQgdGVtcGxhdGUgYXV0b21hdGljYWxseSBhZGFwdHMgdG8geW91ciBsYW5ndWFnZSBlbnZpcm9ubWVudC4gSWYgeW91IGN1c3RvbWl6ZSB0aGUgdGVtcGxhdGUsIHlvdXIgY29udGVudCB3aWxsIGJlIHVzZWQgaW4gYWxsIGxhbmd1YWdlIGVudmlyb25tZW50cy4nXG4gICAgICAgIH0pO1xuICAgICAgICB0ZW1wbGF0ZURlc2NyaXB0aW9uLnN0eWxlLmZvbnRTaXplID0gJzAuODVlbSc7XG4gICAgICAgIHRlbXBsYXRlRGVzY3JpcHRpb24uc3R5bGUub3BhY2l0eSA9ICcwLjgnO1xuICAgICAgICB0ZW1wbGF0ZURlc2NyaXB0aW9uLnN0eWxlLm1hcmdpbkJvdHRvbSA9ICcxNXB4JztcbiAgICAgICAgXG4gICAgICAgIC8vIOa3u+WKoOaooeadv+WPmOmHj+ivtOaYjlxuICAgICAgICBjb25zdCB0ZW1wbGF0ZVZhcmlhYmxlc0VsID0gY29udGFpbmVyRWwuY3JlYXRlRWwoJ3AnKTtcbiAgICAgICAgdGVtcGxhdGVWYXJpYWJsZXNFbC5pbm5lckhUTUwgPSB0aGlzLnNldHRpbmdzTWFuYWdlci5nZXRDdXJyZW50TGFuZ3VhZ2UoKSA9PT0gJ3poJyA/XG4gICAgICAgICAgICAnPHN0cm9uZz7lj6/nlKjlj5jph4/vvJo8L3N0cm9uZz4ge3tkYXRlfX0gLSDml6XmnJ8sIHt7ZGF0ZVdpdGhJY29ufX0gLSDluKblm77moIfnmoTml6XmnJ8sIHt7d2Vla2RheX19IC0g5pif5pyf5YegLCB7e3llYXJQcm9ncmVzc319IC0g5bm06L+b5bqmLCB7e21vbnRoUHJvZ3Jlc3N9fSAtIOaciOi/m+W6piwge3t0aW1lfX0gLSDlvZPliY3ml7bpl7QnIDpcbiAgICAgICAgICAgICc8c3Ryb25nPkF2YWlsYWJsZSB2YXJpYWJsZXM6PC9zdHJvbmc+IHt7ZGF0ZX19IC0gRGF0ZSwge3tkYXRlV2l0aEljb259fSAtIERhdGUgd2l0aCBpY29uLCB7e3dlZWtkYXl9fSAtIERheSBvZiB3ZWVrLCB7e3llYXJQcm9ncmVzc319IC0gWWVhciBwcm9ncmVzcywge3ttb250aFByb2dyZXNzfX0gLSBNb250aCBwcm9ncmVzcywge3t0aW1lfX0gLSBDdXJyZW50IHRpbWUnO1xuICAgICAgICB0ZW1wbGF0ZVZhcmlhYmxlc0VsLnN0eWxlLmZvbnRTaXplID0gJzAuODVlbSc7XG4gICAgICAgIHRlbXBsYXRlVmFyaWFibGVzRWwuc3R5bGUubWFyZ2luQm90dG9tID0gJzEwcHgnO1xuICAgICAgICBcbiAgICAgICAgLy8g5Y2V5LiA5qih5p2/6K6+572uXG4gICAgICAgIGNvbnN0IHRlbXBsYXRlU2V0dGluZyA9IG5ldyBTZXR0aW5nKGNvbnRhaW5lckVsKVxuICAgICAgICAgICAgLnNldE5hbWUoZ2V0VHJhbnNsYXRpb24oJ3NldHRpbmdzLnRlbXBsYXRlJykpXG4gICAgICAgICAgICAuc2V0Q2xhc3MoJ3RlbXBsYXRlLXNldHRpbmcnKTtcbiAgICAgICAgXG4gICAgICAgIGNvbnN0IHRlbXBsYXRlQ29udGFpbmVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICAgIHRlbXBsYXRlQ29udGFpbmVyLnN0eWxlLndpZHRoID0gJzEwMCUnO1xuICAgICAgICBcbiAgICAgICAgLy8g6I635Y+W5b2T5YmN5qih5p2/5YaF5a65XG4gICAgICAgIGNvbnN0IGN1cnJlbnRUZW1wbGF0ZSA9IHRoaXMuc2V0dGluZ3NNYW5hZ2VyLmhhc0N1c3RvbVRlbXBsYXRlKCkgPyBcbiAgICAgICAgICAgIHRoaXMuc2V0dGluZ3NNYW5hZ2VyLmdldFNldHRpbmdzKCkuY3VzdG9tVGVtcGxhdGUgOiBcbiAgICAgICAgICAgIHRoaXMuc2V0dGluZ3NNYW5hZ2VyLmdldFRlbXBsYXRlQnlMYW5ndWFnZSgpO1xuICAgICAgICBcbiAgICAgICAgY29uc3QgdGV4dGFyZWEgPSBuZXcgVGV4dEFyZWFDb21wb25lbnQodGVtcGxhdGVDb250YWluZXIpXG4gICAgICAgICAgICAuc2V0VmFsdWUoY3VycmVudFRlbXBsYXRlKVxuICAgICAgICAgICAgLnNldFBsYWNlaG9sZGVyKHRoaXMuc2V0dGluZ3NNYW5hZ2VyLmdldEN1cnJlbnRMYW5ndWFnZSgpID09PSAnemgnID8gXG4gICAgICAgICAgICAgICAgJ+WcqOatpOWkhOi+k+WFpeS7u+WKoeaooeadvy4uLicgOiBcbiAgICAgICAgICAgICAgICAnRW50ZXIgdGFzayB0ZW1wbGF0ZSBoZXJlLi4uJylcbiAgICAgICAgICAgIC5vbkNoYW5nZShhc3luYyAodmFsdWUpID0+IHtcbiAgICAgICAgICAgICAgICAvLyDmm7TmlrDkuLroh6rlrprkuYnmqKHmnb9cbiAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLnNldHRpbmdzTWFuYWdlci51cGRhdGVTZXR0aW5ncyh7IFxuICAgICAgICAgICAgICAgICAgICBjdXN0b21UZW1wbGF0ZTogdmFsdWUsXG4gICAgICAgICAgICAgICAgICAgIGhhc0N1c3RvbVRlbXBsYXRlOiB0cnVlXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgdGhpcy51cGRhdGVQcmV2aWV3KHRoaXMucHJldmlld0VsLCB2YWx1ZSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgXG4gICAgICAgIC8vIOaUuei/m1RleHRBcmVh5qC35byPXG4gICAgICAgIHRleHRhcmVhLmlucHV0RWwuY2xhc3NMaXN0LmFkZCgndGVtcGxhdGUtZWRpdG9yJyk7XG4gICAgICAgIHRleHRhcmVhLmlucHV0RWwuc3R5bGUuaGVpZ2h0ID0gJzIwMHB4JztcbiAgICAgICAgdGV4dGFyZWEuaW5wdXRFbC5zdHlsZS5ib3JkZXIgPSAnMXB4IHNvbGlkIHZhcigtLWJhY2tncm91bmQtbW9kaWZpZXItYm9yZGVyLWhvdmVyKSc7XG4gICAgICAgIHRleHRhcmVhLmlucHV0RWwuc3R5bGUuYm9yZGVyUmFkaXVzID0gJzRweCc7XG4gICAgICAgIHRleHRhcmVhLmlucHV0RWwuc3R5bGUucGFkZGluZyA9ICcxMnB4JztcbiAgICAgICAgdGV4dGFyZWEuaW5wdXRFbC5zdHlsZS5saW5lSGVpZ2h0ID0gJzEuNSc7XG4gICAgICAgIHRleHRhcmVhLmlucHV0RWwuc3R5bGUuZm9udFNpemUgPSAnMTRweCc7XG4gICAgICAgIHRleHRhcmVhLmlucHV0RWwuc3R5bGUuZm9udEZhbWlseSA9ICd2YXIoLS1mb250LW1vbm9zcGFjZSknO1xuICAgICAgICB0ZXh0YXJlYS5pbnB1dEVsLnN0eWxlLnRyYW5zaXRpb24gPSAnYWxsIDAuMnMgZWFzZSc7XG4gICAgICAgIHRleHRhcmVhLmlucHV0RWwuc3R5bGUuYm94U2hhZG93ID0gJ2luc2V0IDAgMXB4IDNweCByZ2JhKDAsIDAsIDAsIDAuMSknO1xuICAgICAgICB0ZXh0YXJlYS5pbnB1dEVsLnN0eWxlLmJhY2tncm91bmRDb2xvciA9ICd2YXIoLS1iYWNrZ3JvdW5kLXByaW1hcnkpJztcbiAgICAgICAgdGV4dGFyZWEuaW5wdXRFbC5zdHlsZS5jb2xvciA9ICd2YXIoLS10ZXh0LW5vcm1hbCknO1xuICAgICAgICB0ZXh0YXJlYS5pbnB1dEVsLnN0eWxlLnJlc2l6ZSA9ICd2ZXJ0aWNhbCc7XG4gICAgICAgIFxuICAgICAgICAvLyDlvZPojrflj5bnhKbngrnml7bmlLnlj5jovrnmoYbmoLflvI9cbiAgICAgICAgdGV4dGFyZWEuaW5wdXRFbC5hZGRFdmVudExpc3RlbmVyKCdmb2N1cycsICgpID0+IHtcbiAgICAgICAgICAgIHRleHRhcmVhLmlucHV0RWwuc3R5bGUuYm9yZGVyID0gJzFweCBzb2xpZCB2YXIoLS1pbnRlcmFjdGl2ZS1hY2NlbnQpJztcbiAgICAgICAgICAgIHRleHRhcmVhLmlucHV0RWwuc3R5bGUuYm94U2hhZG93ID0gJzAgMCAwIDJweCByZ2JhKHZhcigtLWludGVyYWN0aXZlLWFjY2VudC1yZ2IpLCAwLjIpLCBpbnNldCAwIDFweCAzcHggcmdiYSgwLCAwLCAwLCAwLjEpJztcbiAgICAgICAgICAgIHRleHRhcmVhLmlucHV0RWwuc3R5bGUub3V0bGluZSA9ICdub25lJztcbiAgICAgICAgfSk7XG4gICAgICAgIFxuICAgICAgICAvLyDlpLHljrvnhKbngrnml7bmgaLlpI3ljp/mnaXnmoTovrnmoYbmoLflvI9cbiAgICAgICAgdGV4dGFyZWEuaW5wdXRFbC5hZGRFdmVudExpc3RlbmVyKCdibHVyJywgKCkgPT4ge1xuICAgICAgICAgICAgdGV4dGFyZWEuaW5wdXRFbC5zdHlsZS5ib3JkZXIgPSAnMXB4IHNvbGlkIHZhcigtLWJhY2tncm91bmQtbW9kaWZpZXItYm9yZGVyLWhvdmVyKSc7XG4gICAgICAgICAgICB0ZXh0YXJlYS5pbnB1dEVsLnN0eWxlLmJveFNoYWRvdyA9ICdpbnNldCAwIDFweCAzcHggcmdiYSgwLCAwLCAwLCAwLjEpJztcbiAgICAgICAgfSk7XG4gICAgICAgIFxuICAgICAgICAvLyDpooTop4jmoIfpopjvvIzkvb/nlKhmbGV45biD5bGA5bGF5LitXG4gICAgICAgIGNvbnN0IHByZXZpZXdIZWFkZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICAgICAgcHJldmlld0hlYWRlci5jbGFzc0xpc3QuYWRkKCd0ZW1wbGF0ZS1wcmV2aWV3LWhlYWRlcicpO1xuICAgICAgICBwcmV2aWV3SGVhZGVyLnN0eWxlLmRpc3BsYXkgPSAnZmxleCc7XG4gICAgICAgIHByZXZpZXdIZWFkZXIuc3R5bGUuanVzdGlmeUNvbnRlbnQgPSAnY2VudGVyJztcbiAgICAgICAgcHJldmlld0hlYWRlci5zdHlsZS5tYXJnaW5Ub3AgPSAnMTVweCc7XG4gICAgICAgIHByZXZpZXdIZWFkZXIuc3R5bGUuZ2FwID0gJzEwcHgnO1xuICAgICAgICBcbiAgICAgICAgLy8g6aKE6KeI5oyJ6ZKuXG4gICAgICAgIGNvbnN0IHRvZ2dsZUJ1dHRvbiA9IG5ldyBCdXR0b25Db21wb25lbnQocHJldmlld0hlYWRlcilcbiAgICAgICAgICAgIC5zZXRCdXR0b25UZXh0KGdldFRyYW5zbGF0aW9uKCdzZXR0aW5ncy50ZW1wbGF0ZS5wcmV2aWV3JykpO1xuICAgICAgICBcbiAgICAgICAgLy8g5re75Yqg5qC35byP57G7XG4gICAgICAgIHRvZ2dsZUJ1dHRvbi5idXR0b25FbC5hZGRDbGFzcyhUZXh0Q2VudGVyQ1NTKTtcbiAgICAgICAgdG9nZ2xlQnV0dG9uLmJ1dHRvbkVsLnN0eWxlLnRleHRBbGlnbiA9ICdjZW50ZXInO1xuICAgICAgICB0b2dnbGVCdXR0b24uYnV0dG9uRWwuc3R5bGUuZGlzcGxheSA9ICdmbGV4JztcbiAgICAgICAgdG9nZ2xlQnV0dG9uLmJ1dHRvbkVsLnN0eWxlLmFsaWduSXRlbXMgPSAnY2VudGVyJztcbiAgICAgICAgdG9nZ2xlQnV0dG9uLmJ1dHRvbkVsLnN0eWxlLmp1c3RpZnlDb250ZW50ID0gJ2NlbnRlcic7XG4gICAgICAgIHRvZ2dsZUJ1dHRvbi5idXR0b25FbC5zdHlsZS53aWR0aCA9ICcxMjBweCc7XG4gICAgICAgIFxuICAgICAgICAvLyDmiYvliqjmt7vliqDnnLznnZvlm77moIdcbiAgICAgICAgY29uc3QgZXllSWNvbiA9IGNyZWF0ZVNwYW4oe2NsczogJ3N2Zy1pY29uIGx1Y2lkZS1leWUnfSk7XG4gICAgICAgIHRvZ2dsZUJ1dHRvbi5idXR0b25FbC5wcmVwZW5kKGV5ZUljb24pO1xuICAgICAgICBcbiAgICAgICAgLy8g6aKE6KeI5Yy65Z+fXG4gICAgICAgIHRoaXMucHJldmlld0VsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICAgIHRoaXMucHJldmlld0VsLmNsYXNzTGlzdC5hZGQoJ3RlbXBsYXRlLXByZXZpZXcnKTtcbiAgICAgICAgdGhpcy5wcmV2aWV3RWwuc3R5bGUubWFyZ2luVG9wID0gJzE1cHgnO1xuICAgICAgICB0aGlzLnByZXZpZXdFbC5zdHlsZS5wYWRkaW5nID0gJzE1cHgnO1xuICAgICAgICB0aGlzLnByZXZpZXdFbC5zdHlsZS5ib3JkZXIgPSAnMXB4IGRhc2hlZCB2YXIoLS1iYWNrZ3JvdW5kLW1vZGlmaWVyLWJvcmRlciknO1xuICAgICAgICB0aGlzLnByZXZpZXdFbC5zdHlsZS5ib3JkZXJSYWRpdXMgPSAnNHB4JztcbiAgICAgICAgdGhpcy5wcmV2aWV3RWwuc3R5bGUuYmFja2dyb3VuZENvbG9yID0gJ3ZhcigtLWJhY2tncm91bmQtc2Vjb25kYXJ5KSc7XG4gICAgICAgIHRoaXMucHJldmlld0VsLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XG4gICAgICAgIHRoaXMudXBkYXRlUHJldmlldyh0aGlzLnByZXZpZXdFbCwgY3VycmVudFRlbXBsYXRlKTtcbiAgICAgICAgdGVtcGxhdGVDb250YWluZXIuYXBwZW5kQ2hpbGQodGhpcy5wcmV2aWV3RWwpO1xuICAgICAgICBcbiAgICAgICAgdG9nZ2xlQnV0dG9uLm9uQ2xpY2soKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy50b2dnbGVQcmV2aWV3KHRoaXMucHJldmlld0VsKTtcbiAgICAgICAgICAgIC8vIOWIh+aNouWbvuagh+WSjOaMiemSruaWh+acrFxuICAgICAgICAgICAgaWYgKHRoaXMucHJldmlld0VsICYmIHRoaXMucHJldmlld0VsLmNsYXNzTGlzdC5jb250YWlucygndmlzaWJsZScpKSB7XG4gICAgICAgICAgICAgICAgZXllSWNvbi5jbGFzc05hbWUgPSAnc3ZnLWljb24gbHVjaWRlLWV5ZS1vZmYnO1xuICAgICAgICAgICAgICAgIHRvZ2dsZUJ1dHRvbi5zZXRCdXR0b25UZXh0KGdldFRyYW5zbGF0aW9uKCdzZXR0aW5ncy50ZW1wbGF0ZS5oaWRlJykpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBleWVJY29uLmNsYXNzTmFtZSA9ICdzdmctaWNvbiBsdWNpZGUtZXllJztcbiAgICAgICAgICAgICAgICB0b2dnbGVCdXR0b24uc2V0QnV0dG9uVGV4dChnZXRUcmFuc2xhdGlvbignc2V0dGluZ3MudGVtcGxhdGUucHJldmlldycpKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIFxuICAgICAgICAvLyDph43nva7mjInpkq5cbiAgICAgICAgY29uc3QgcmVzZXRCdG4gPSBuZXcgQnV0dG9uQ29tcG9uZW50KHByZXZpZXdIZWFkZXIpXG4gICAgICAgICAgICAuc2V0QnV0dG9uVGV4dChnZXRUcmFuc2xhdGlvbignc2V0dGluZ3MucmVzZXREZWZhdWx0JykpO1xuICAgICAgICBcbiAgICAgICAgLy8g5re75Yqg5qC35byP57G7XG4gICAgICAgIHJlc2V0QnRuLmJ1dHRvbkVsLmFkZENsYXNzKFRleHRDZW50ZXJDU1MpO1xuICAgICAgICByZXNldEJ0bi5idXR0b25FbC5zdHlsZS50ZXh0QWxpZ24gPSAnY2VudGVyJztcbiAgICAgICAgcmVzZXRCdG4uYnV0dG9uRWwuc3R5bGUuZGlzcGxheSA9ICdmbGV4JztcbiAgICAgICAgcmVzZXRCdG4uYnV0dG9uRWwuc3R5bGUuYWxpZ25JdGVtcyA9ICdjZW50ZXInO1xuICAgICAgICByZXNldEJ0bi5idXR0b25FbC5zdHlsZS5qdXN0aWZ5Q29udGVudCA9ICdjZW50ZXInO1xuICAgICAgICByZXNldEJ0bi5idXR0b25FbC5zdHlsZS53aWR0aCA9ICcxMjBweCc7XG4gICAgICAgIFxuICAgICAgICAvLyDmt7vliqDph43nva7lm77moIdcbiAgICAgICAgcmVzZXRCdG4uYnV0dG9uRWwucHJlcGVuZChjcmVhdGVTcGFuKHtjbHM6ICdzdmctaWNvbiBsdWNpZGUtcmVmcmVzaC1jdyd9KSk7XG4gICAgICAgIFxuICAgICAgICAvLyDmt7vliqDph43nva7kuovku7ZcbiAgICAgICAgcmVzZXRCdG4ub25DbGljayhhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICAvLyDlsIboh6rlrprkuYnmqKHmnb/orr7nva7kuLrnqbrvvIzlm57liLDkvb/nlKjpu5jorqTmqKHmnb9cbiAgICAgICAgICAgIGF3YWl0IHRoaXMuc2V0dGluZ3NNYW5hZ2VyLnVwZGF0ZVNldHRpbmdzKHsgXG4gICAgICAgICAgICAgICAgY3VzdG9tVGVtcGxhdGU6ICcnLFxuICAgICAgICAgICAgICAgIGhhc0N1c3RvbVRlbXBsYXRlOiBmYWxzZVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIOiOt+WPluW9k+WJjeivreiogOeahOm7mOiupOaooeadv1xuICAgICAgICAgICAgY29uc3QgZGVmYXVsdFRlbXBsYXRlID0gdGhpcy5zZXR0aW5nc01hbmFnZXIuZ2V0VGVtcGxhdGVCeUxhbmd1YWdlKCk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIOabtOaWsOi+k+WFpeahhuWSjOmihOiniFxuICAgICAgICAgICAgdGV4dGFyZWEuc2V0VmFsdWUoZGVmYXVsdFRlbXBsYXRlKTtcbiAgICAgICAgICAgIHRoaXMudXBkYXRlUHJldmlldyh0aGlzLnByZXZpZXdFbCwgZGVmYXVsdFRlbXBsYXRlKTtcbiAgICAgICAgfSk7XG4gICAgICAgIFxuICAgICAgICAvLyDlsIbmjInpkq7mt7vliqDliLDmoIfpopjmoI9cbiAgICAgICAgcHJldmlld0hlYWRlci5hcHBlbmRDaGlsZCh0b2dnbGVCdXR0b24uYnV0dG9uRWwpO1xuICAgICAgICBwcmV2aWV3SGVhZGVyLmFwcGVuZENoaWxkKHJlc2V0QnRuLmJ1dHRvbkVsKTtcbiAgICAgICAgdGVtcGxhdGVDb250YWluZXIuYXBwZW5kQ2hpbGQocHJldmlld0hlYWRlcik7XG4gICAgICAgIFxuICAgICAgICB0ZW1wbGF0ZVNldHRpbmcuY29udHJvbEVsLmFwcGVuZENoaWxkKHRlbXBsYXRlQ29udGFpbmVyKTtcbiAgICAgICAgXG4gICAgICAgIC8vIOaBouWkjem7mOiupOiuvue9riAtIOWIm+W7uuWuueWZqOiuqeaMiemSruWPs+Wvuem9kFxuICAgICAgICBjb25zdCByZXNldENvbnRhaW5lciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuICAgICAgICByZXNldENvbnRhaW5lci5zdHlsZS5kaXNwbGF5ID0gJ2ZsZXgnO1xuICAgICAgICByZXNldENvbnRhaW5lci5zdHlsZS5qdXN0aWZ5Q29udGVudCA9ICdmbGV4LWVuZCc7IC8vIOehruS/neWPs+Wvuem9kFxuICAgICAgICByZXNldENvbnRhaW5lci5zdHlsZS5tYXJnaW5Ub3AgPSAnMjBweCc7XG4gICAgICAgIHJlc2V0Q29udGFpbmVyLnN0eWxlLm1hcmdpbkJvdHRvbSA9ICcxMHB4JztcbiAgICAgICAgY29udGFpbmVyRWwuYXBwZW5kQ2hpbGQocmVzZXRDb250YWluZXIpO1xuICAgICAgICBcbiAgICAgICAgLy8g5oGi5aSN6buY6K6k6K6+572u5oyJ6ZKuXG4gICAgICAgIGNvbnN0IHJlc2V0RGVmYXVsdEJ0biA9IG5ldyBCdXR0b25Db21wb25lbnQocmVzZXRDb250YWluZXIpXG4gICAgICAgICAgICAuc2V0QnV0dG9uVGV4dChnZXRUcmFuc2xhdGlvbignc2V0dGluZ3MucmVzZXRUb0RlZmF1bHQnKSk7XG5cbiAgICAgICAgLy8g5re75Yqg5qC35byP57G7XG4gICAgICAgIHJlc2V0RGVmYXVsdEJ0bi5idXR0b25FbC5hZGRDbGFzcyhUZXh0Q2VudGVyQ1NTKTtcbiAgICAgICAgcmVzZXREZWZhdWx0QnRuLmJ1dHRvbkVsLmFkZENsYXNzKCdkYW5nZXItYnV0dG9uJyk7XG4gICAgICAgIHJlc2V0RGVmYXVsdEJ0bi5idXR0b25FbC5zdHlsZS50ZXh0QWxpZ24gPSAnY2VudGVyJztcbiAgICAgICAgcmVzZXREZWZhdWx0QnRuLmJ1dHRvbkVsLnN0eWxlLmRpc3BsYXkgPSAnZmxleCc7XG4gICAgICAgIHJlc2V0RGVmYXVsdEJ0bi5idXR0b25FbC5zdHlsZS5hbGlnbkl0ZW1zID0gJ2NlbnRlcic7XG4gICAgICAgIHJlc2V0RGVmYXVsdEJ0bi5idXR0b25FbC5zdHlsZS5qdXN0aWZ5Q29udGVudCA9ICdjZW50ZXInO1xuICAgICAgICByZXNldERlZmF1bHRCdG4uYnV0dG9uRWwuc3R5bGUud2lkdGggPSAnMTIwcHgnO1xuICAgICAgICBcbiAgICAgICAgLy8g5re75Yqg6YeN572u5Zu+5qCH5ZKM5Y2x6Zmp5oyJ6ZKu5qC35byPXG4gICAgICAgIHJlc2V0RGVmYXVsdEJ0bi5idXR0b25FbC5wcmVwZW5kKGNyZWF0ZVNwYW4oe2NsczogJ3N2Zy1pY29uIGx1Y2lkZS1yZWZyZXNoLWN3J30pKTtcbiAgICAgICAgXG4gICAgICAgIC8vIOS4uuWFqOWxgOmHjee9ruaMiemSrua3u+WKoOS6i+S7tuWkhOeQhlxuICAgICAgICByZXNldERlZmF1bHRCdG4ub25DbGljayhhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLnNldHRpbmdzTWFuYWdlci5yZXNldFRvRGVmYXVsdHMoKTtcbiAgICAgICAgICAgIHRoaXMuZGlzcGxheSgpO1xuICAgICAgICB9KTtcbiAgICAgICAgXG4gICAgICAgIC8vIOaJi+WKqOa3u+WKoOS7iuaXpeS7u+WKoeaMiemSriAtIOWPs+Wvuem9kOaYvuekulxuICAgICAgICBjb25zdCBhZGRUYXNrQ29udGFpbmVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICAgIGFkZFRhc2tDb250YWluZXIuc3R5bGUuZGlzcGxheSA9ICdmbGV4JztcbiAgICAgICAgYWRkVGFza0NvbnRhaW5lci5zdHlsZS5qdXN0aWZ5Q29udGVudCA9ICdmbGV4LWVuZCc7IC8vIOehruS/neWPs+Wvuem9kFxuICAgICAgICBhZGRUYXNrQ29udGFpbmVyLnN0eWxlLm1hcmdpblRvcCA9ICcyMHB4JztcbiAgICAgICAgY29udGFpbmVyRWwuYXBwZW5kQ2hpbGQoYWRkVGFza0NvbnRhaW5lcik7XG4gICAgICAgIFxuICAgICAgICB0aGlzLmFkZFRhc2tCdXR0b24gPSBuZXcgQnV0dG9uQ29tcG9uZW50KGFkZFRhc2tDb250YWluZXIpXG4gICAgICAgICAgICAuc2V0QnV0dG9uVGV4dChnZXRUcmFuc2xhdGlvbignc2V0dGluZ3MuYWRkVGFza0J1dHRvbicpKVxuICAgICAgICAgICAgLnNldEN0YSgpO1xuXG4gICAgICAgIC8vIOa3u+WKoOagt+W8j+exuyAtIOehruS/neaMiemSruaWh+Wtl+WxheS4rVxuICAgICAgICBpZiAodGhpcy5hZGRUYXNrQnV0dG9uICYmIHRoaXMuYWRkVGFza0J1dHRvbi5idXR0b25FbCkge1xuICAgICAgICAgICAgdGhpcy5hZGRUYXNrQnV0dG9uLmJ1dHRvbkVsLmFkZENsYXNzKFRleHRDZW50ZXJDU1MpO1xuICAgICAgICAgICAgdGhpcy5hZGRUYXNrQnV0dG9uLmJ1dHRvbkVsLnN0eWxlLnRleHRBbGlnbiA9ICdjZW50ZXInO1xuICAgICAgICAgICAgdGhpcy5hZGRUYXNrQnV0dG9uLmJ1dHRvbkVsLnN0eWxlLmRpc3BsYXkgPSAnZmxleCc7XG4gICAgICAgICAgICB0aGlzLmFkZFRhc2tCdXR0b24uYnV0dG9uRWwuc3R5bGUuYWxpZ25JdGVtcyA9ICdjZW50ZXInO1xuICAgICAgICAgICAgdGhpcy5hZGRUYXNrQnV0dG9uLmJ1dHRvbkVsLnN0eWxlLmp1c3RpZnlDb250ZW50ID0gJ2NlbnRlcic7XG4gICAgICAgIH1cblxuICAgICAgICAvLyDmiYvliqjmt7vliqDku7vliqHmjInpkq7kuovku7blpITnkIZcbiAgICAgICAgdGhpcy5hZGRUYXNrQnV0dG9uLm9uQ2xpY2soYXN5bmMgKCkgPT4ge1xuICAgICAgICAgICAgLy8g5qOA5p+l55uu5b2V6K6+572uXG4gICAgICAgICAgICBjb25zdCByb290RGlyID0gdGhpcy5zZXR0aW5nc01hbmFnZXIuZ2V0U2V0dGluZ3MoKS5yb290RGlyO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyDmt7vliqBsb2FkaW5n54q25oCBXG4gICAgICAgICAgICBpZiAodGhpcy5hZGRUYXNrQnV0dG9uICYmIHRoaXMuYWRkVGFza0J1dHRvbi5idXR0b25FbCkge1xuICAgICAgICAgICAgICAgIHRoaXMuYWRkVGFza0J1dHRvbi5idXR0b25FbC5jbGFzc0xpc3QuYWRkKCdsb2FkaW5nJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLmFkZFRhc2tCdXR0b24/LnNldERpc2FibGVkKHRydWUpO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIC8vIOa3u+WKoOS7u+WKoVxuICAgICAgICAgICAgICAgIGF3YWl0IHRoaXMudGFza0dlbmVyYXRvci5hZGRUYXNrTWFudWFsbHkoKTtcbiAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwi5re75Yqg5Lu75Yqh5Ye66ZSZOlwiLCBlKTtcbiAgICAgICAgICAgICAgICBuZXcgTm90aWNlKGDmt7vliqDku7vliqHlpLHotKU6ICR7ZS5tZXNzYWdlIHx8IGV9YCk7XG4gICAgICAgICAgICB9IGZpbmFsbHkge1xuICAgICAgICAgICAgICAgIC8vIOenu+mZpGxvYWRpbmfnirbmgIFcbiAgICAgICAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuYWRkVGFza0J1dHRvbiAmJiB0aGlzLmFkZFRhc2tCdXR0b24uYnV0dG9uRWwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuYWRkVGFza0J1dHRvbi5idXR0b25FbC5jbGFzc0xpc3QucmVtb3ZlKCdsb2FkaW5nJyk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgdGhpcy5hZGRUYXNrQnV0dG9uPy5zZXREaXNhYmxlZChmYWxzZSk7XG4gICAgICAgICAgICAgICAgfSwgNTAwKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgLy8g5re75Yqg5Zu+5qCHXG4gICAgICAgIHRoaXMuYWRkVGFza0J1dHRvbi5idXR0b25FbC5wcmVwZW5kKGNyZWF0ZVNwYW4oe2NsczogJ3N2Zy1pY29uIGx1Y2lkZS1jYWxlbmRhci1wbHVzJ30pKTtcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICog5pu05paw5qih5p2/6aKE6KeIXG4gICAgICovXG4gICAgcHJpdmF0ZSB1cGRhdGVQcmV2aWV3KHByZXZpZXdFbDogSFRNTEVsZW1lbnQgfCBudWxsLCB0ZW1wbGF0ZTogc3RyaW5nKTogdm9pZCB7XG4gICAgICAgIGlmICghcHJldmlld0VsKSByZXR1cm47XG4gICAgICAgIFxuICAgICAgICBjb25zdCByZW5kZXJlZENvbnRlbnQgPSByZW5kZXJUZW1wbGF0ZSh0ZW1wbGF0ZSk7XG4gICAgICAgIC8vIOS9v+eUqE1hcmtkb3duUmVuZGVyZXLpnIDopoHlr7zlhaXnm7jlhbPnu4Tku7ZcbiAgICAgICAgcHJldmlld0VsLmlubmVySFRNTCA9IHJlbmRlcmVkQ29udGVudC5yZXBsYWNlKC9cXG4vZywgJzxicj4nKTtcbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICog5YiH5o2i6aKE6KeI55qE5pi+56S6L+makOiXj1xuICAgICAqL1xuICAgIHByaXZhdGUgdG9nZ2xlUHJldmlldyhwcmV2aWV3RWw6IEhUTUxFbGVtZW50IHwgbnVsbCk6IHZvaWQge1xuICAgICAgICBpZiAoIXByZXZpZXdFbCkgcmV0dXJuO1xuICAgICAgICBcbiAgICAgICAgaWYgKHByZXZpZXdFbC5zdHlsZS5kaXNwbGF5ID09PSAnbm9uZScpIHtcbiAgICAgICAgICAgIHByZXZpZXdFbC5zdHlsZS5kaXNwbGF5ID0gJ2Jsb2NrJztcbiAgICAgICAgICAgIHByZXZpZXdFbC5jbGFzc0xpc3QuYWRkKCd2aXNpYmxlJyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBwcmV2aWV3RWwuc3R5bGUuZGlzcGxheSA9ICdub25lJztcbiAgICAgICAgICAgIHByZXZpZXdFbC5jbGFzc0xpc3QucmVtb3ZlKCd2aXNpYmxlJyk7XG4gICAgICAgIH1cbiAgICB9XG59XG5cbi8qKlxuICog6K6+572u566h55CG5ZmoXG4gKiDotJ/otKPliqDovb3jgIHkv53lrZjlkozmj5Dkvpvorr7nva7orr/pl67mjqXlj6NcbiAqL1xuZXhwb3J0IGNsYXNzIFNldHRpbmdzTWFuYWdlciB7XG4gICAgcHJpdmF0ZSBwbHVnaW46IFBsdWdpbjtcbiAgICBwcml2YXRlIHNldHRpbmdzOiBEYWlseVRhc2tTZXR0aW5ncztcblxuICAgIGNvbnN0cnVjdG9yKHBsdWdpbjogUGx1Z2luKSB7XG4gICAgICAgIHRoaXMucGx1Z2luID0gcGx1Z2luO1xuICAgICAgICB0aGlzLnNldHRpbmdzID0gT2JqZWN0LmFzc2lnbih7fSwgREVGQVVMVF9TRVRUSU5HUyk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICog6I635Y+W5b2T5YmN6K6+572uXG4gICAgICovXG4gICAgZ2V0U2V0dGluZ3MoKTogRGFpbHlUYXNrU2V0dGluZ3Mge1xuICAgICAgICByZXR1cm4gdGhpcy5zZXR0aW5ncztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiDmm7TmlrDorr7nva7lubbkv53lrZhcbiAgICAgKiBAcGFyYW0gc2V0dGluZ3Mg6KaB5pu05paw55qE6K6+572uXG4gICAgICovXG4gICAgYXN5bmMgdXBkYXRlU2V0dGluZ3Moc2V0dGluZ3M6IFBhcnRpYWw8RGFpbHlUYXNrU2V0dGluZ3M+KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIHRoaXMuc2V0dGluZ3MgPSB7XG4gICAgICAgICAgICAuLi50aGlzLnNldHRpbmdzLFxuICAgICAgICAgICAgLi4uc2V0dGluZ3NcbiAgICAgICAgfTtcbiAgICAgICAgYXdhaXQgdGhpcy5zYXZlU2V0dGluZ3MoKTtcbiAgICAgICAgXG4gICAgICAgIC8vIOabtOaWsOW9k+WJjeivreiogFxuICAgICAgICB0aGlzLnVwZGF0ZUN1cnJlbnRMYW5ndWFnZSgpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIOS/neWtmOiuvue9ruWIsOaVsOaNruWtmOWCqFxuICAgICAqL1xuICAgIGFzeW5jIHNhdmVTZXR0aW5ncygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgYXdhaXQgdGhpcy5wbHVnaW4uc2F2ZURhdGEodGhpcy5zZXR0aW5ncyk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICog5Yqg6L296K6+572uXG4gICAgICovXG4gICAgYXN5bmMgbG9hZFNldHRpbmdzKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBsb2FkZWREYXRhID0gYXdhaXQgdGhpcy5wbHVnaW4ubG9hZERhdGEoKTtcbiAgICAgICAgaWYgKGxvYWRlZERhdGEpIHtcbiAgICAgICAgICAgIC8vIOWQiOW5tum7mOiupOiuvue9ruWSjOW3suS/neWtmOeahOiuvue9rlxuICAgICAgICAgICAgdGhpcy5zZXR0aW5ncyA9IHtcbiAgICAgICAgICAgICAgICAuLi5ERUZBVUxUX1NFVFRJTkdTLFxuICAgICAgICAgICAgICAgIC4uLmxvYWRlZERhdGFcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIOehruS/neWcqOWNh+e6p+aPkuS7tuWQju+8jOaWsOWinueahOiuvue9rumhueS5n+aciem7mOiupOWAvFxuICAgICAgICAgICAgdGhpcy5lbnN1cmVTZXR0aW5nc0NvbXBsZXRlbmVzcygpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8g5aaC5p6c5rKh5pyJ5Yqg6L295Yiw5pWw5o2u77yM5L2/55So6buY6K6k6K6+572u5L2G5bCG6Ieq5Yqo55Sf5oiQ5qih5byP5pS55Li65bel5L2c5pelXG4gICAgICAgICAgICB0aGlzLnNldHRpbmdzID0ge1xuICAgICAgICAgICAgICAgIC4uLkRFRkFVTFRfU0VUVElOR1MsXG4gICAgICAgICAgICAgICAgYXV0b0dlbmVyYXRlTW9kZTogQXV0b0dlbmVyYXRlTW9kZS5XT1JLREFZXG4gICAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICAgIFxuICAgICAgICAvLyDmm7TmlrDlvZPliY3or63oqIBcbiAgICAgICAgdGhpcy51cGRhdGVDdXJyZW50TGFuZ3VhZ2UoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiDnoa7kv53orr7nva7lrozmlbTmgKfvvIzkuLrmlrDlop7nmoTorr7nva7pobnmj5Dkvpvpu5jorqTlgLxcbiAgICAgKi9cbiAgICBwcml2YXRlIGVuc3VyZVNldHRpbmdzQ29tcGxldGVuZXNzKCk6IHZvaWQge1xuICAgICAgICBjb25zdCBkZWZhdWx0S2V5cyA9IE9iamVjdC5rZXlzKERFRkFVTFRfU0VUVElOR1MpO1xuICAgICAgICBkZWZhdWx0S2V5cy5mb3JFYWNoKGtleSA9PiB7XG4gICAgICAgICAgICAvLyDlpoLmnpzlvZPliY3orr7nva7kuK3nvLrlsJHmn5DkuKrpu5jorqTorr7nva7pobnvvIzmt7vliqDpu5jorqTlgLxcbiAgICAgICAgICAgIGlmICghKGtleSBpbiB0aGlzLnNldHRpbmdzKSkge1xuICAgICAgICAgICAgICAgICh0aGlzLnNldHRpbmdzIGFzIGFueSlba2V5XSA9IChERUZBVUxUX1NFVFRJTkdTIGFzIGFueSlba2V5XTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICog5oGi5aSN6buY6K6k6K6+572uXG4gICAgICovXG4gICAgYXN5bmMgcmVzZXRUb0RlZmF1bHRzKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICB0aGlzLnNldHRpbmdzID0gT2JqZWN0LmFzc2lnbih7fSwgREVGQVVMVF9TRVRUSU5HUyk7XG4gICAgICAgIGF3YWl0IHRoaXMuc2F2ZVNldHRpbmdzKCk7XG4gICAgICAgIFxuICAgICAgICAvLyDmm7TmlrDlvZPliY3or63oqIBcbiAgICAgICAgdGhpcy51cGRhdGVDdXJyZW50TGFuZ3VhZ2UoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiDmoLnmja7or63oqIDojrflj5blvZPliY3kvb/nlKjnmoTmqKHmnb9cbiAgICAgKiDlpoLmnpzlvZPliY3mqKHmnb/kuI3mmK/pu5jorqTmqKHmnb/vvIzliJnkuI3lho3ljLrliIbor63oqIBcbiAgICAgKi9cbiAgICBnZXRDdXJyZW50VGVtcGxhdGUoKTogc3RyaW5nIHtcbiAgICAgICAgY29uc3QgbGFuZ3VhZ2UgPSB0aGlzLmdldEN1cnJlbnRMYW5ndWFnZSgpO1xuICAgICAgICBcbiAgICAgICAgLy8g5Lit5paH546v5aKDXG4gICAgICAgIGlmIChsYW5ndWFnZSA9PT0gJ3poJykge1xuICAgICAgICAgICAgLy8g5aaC5p6c5Lit5paH5qih5p2/5bey6KKr5L+u5pS577yI5LiN562J5LqO6buY6K6k5qih5p2/77yJ77yM5L2/55So5Lit5paH5qih5p2/XG4gICAgICAgICAgICBpZiAodGhpcy5zZXR0aW5ncy50ZW1wbGF0ZVpoICE9PSBERUZBVUxUX1RFTVBMQVRFX1pIKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuc2V0dGluZ3MudGVtcGxhdGVaaDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIOWmguaenOiLseaWh+aooeadv+W3suiiq+S/ruaUue+8jOS9v+eUqOiLseaWh+aooeadv1xuICAgICAgICAgICAgaWYgKHRoaXMuc2V0dGluZ3MudGVtcGxhdGVFbiAhPT0gREVGQVVMVF9URU1QTEFURV9FTikge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLnNldHRpbmdzLnRlbXBsYXRlRW47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyDlpoLmnpzpg73mmK/pu5jorqTmqKHmnb/vvIzkvb/nlKjkuK3mlofpu5jorqTmqKHmnb9cbiAgICAgICAgICAgIHJldHVybiB0aGlzLnNldHRpbmdzLnRlbXBsYXRlWmg7XG4gICAgICAgIH0gXG4gICAgICAgIC8vIOiLseaWh+eOr+Wig1xuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIC8vIOWmguaenOiLseaWh+aooeadv+W3suiiq+S/ruaUue+8iOS4jeetieS6jum7mOiupOaooeadv++8ie+8jOS9v+eUqOiLseaWh+aooeadv1xuICAgICAgICAgICAgaWYgKHRoaXMuc2V0dGluZ3MudGVtcGxhdGVFbiAhPT0gREVGQVVMVF9URU1QTEFURV9FTikge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLnNldHRpbmdzLnRlbXBsYXRlRW47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyDlpoLmnpzkuK3mlofmqKHmnb/lt7Looqvkv67mlLnvvIzkvb/nlKjkuK3mlofmqKHmnb9cbiAgICAgICAgICAgIGlmICh0aGlzLnNldHRpbmdzLnRlbXBsYXRlWmggIT09IERFRkFVTFRfVEVNUExBVEVfWkgpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5zZXR0aW5ncy50ZW1wbGF0ZVpoO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8g5aaC5p6c6YO95piv6buY6K6k5qih5p2/77yM5L2/55So6Iux5paH6buY6K6k5qih5p2/XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5zZXR0aW5ncy50ZW1wbGF0ZUVuO1xuICAgICAgICB9XG4gICAgfVxuICAgIFxuICAgIC8qKlxuICAgICAqIOiOt+WPluW9k+WJjeivreiogOiuvue9rlxuICAgICAqL1xuICAgIGdldEN1cnJlbnRMYW5ndWFnZSgpOiBzdHJpbmcge1xuICAgICAgICBpZiAodGhpcy5zZXR0aW5ncy5sYW5ndWFnZSA9PT0gTGFuZ3VhZ2UuQVVUTykge1xuICAgICAgICAgICAgLy8g6Ieq5Yqo5qOA5rWL57O757uf6K+t6KiAXG4gICAgICAgICAgICBjb25zdCBzeXN0ZW1MYW5ndWFnZSA9IHdpbmRvdy5uYXZpZ2F0b3IubGFuZ3VhZ2UudG9Mb3dlckNhc2UoKTtcbiAgICAgICAgICAgIHJldHVybiBzeXN0ZW1MYW5ndWFnZS5zdGFydHNXaXRoKCd6aCcpID8gJ3poJyA6ICdlbic7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXMuc2V0dGluZ3MubGFuZ3VhZ2U7XG4gICAgfVxuICAgIFxuICAgIC8qKlxuICAgICAqIOabtOaWsOW9k+WJjeivreiogFxuICAgICAqL1xuICAgIHByaXZhdGUgdXBkYXRlQ3VycmVudExhbmd1YWdlKCk6IHZvaWQge1xuICAgICAgICBjb25zdCBsYW5ndWFnZSA9IHRoaXMuZ2V0Q3VycmVudExhbmd1YWdlKCk7XG4gICAgICAgIHNldEN1cnJlbnRMYW5ndWFnZShsYW5ndWFnZSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICog6I635Y+W5b2T5YmN6K+t6KiA55qE5qih5p2/XG4gICAgICovXG4gICAgZ2V0VGVtcGxhdGVCeUxhbmd1YWdlKCk6IHN0cmluZyB7XG4gICAgICAgIGNvbnN0IGxhbmd1YWdlID0gdGhpcy5nZXRDdXJyZW50TGFuZ3VhZ2UoKTtcbiAgICAgICAgXG4gICAgICAgIC8vIOS4reaWh+eOr+Wig1xuICAgICAgICBpZiAobGFuZ3VhZ2UgPT09ICd6aCcpIHtcbiAgICAgICAgICAgIC8vIOWmguaenOS4reaWh+aooeadv+W3suiiq+S/ruaUue+8iOS4jeetieS6jum7mOiupOaooeadv++8ie+8jOS9v+eUqOS4reaWh+aooeadv1xuICAgICAgICAgICAgaWYgKHRoaXMuc2V0dGluZ3MudGVtcGxhdGVaaCAhPT0gREVGQVVMVF9URU1QTEFURV9aSCkge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLnNldHRpbmdzLnRlbXBsYXRlWmg7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyDlpoLmnpzoi7HmlofmqKHmnb/lt7Looqvkv67mlLnvvIzkvb/nlKjoi7HmlofmqKHmnb9cbiAgICAgICAgICAgIGlmICh0aGlzLnNldHRpbmdzLnRlbXBsYXRlRW4gIT09IERFRkFVTFRfVEVNUExBVEVfRU4pIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5zZXR0aW5ncy50ZW1wbGF0ZUVuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8g5aaC5p6c6YO95piv6buY6K6k5qih5p2/77yM5L2/55So5Lit5paH6buY6K6k5qih5p2/XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5zZXR0aW5ncy50ZW1wbGF0ZVpoO1xuICAgICAgICB9IFxuICAgICAgICAvLyDoi7Hmlofnjq/looNcbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAvLyDlpoLmnpzoi7HmlofmqKHmnb/lt7Looqvkv67mlLnvvIjkuI3nrYnkuo7pu5jorqTmqKHmnb/vvInvvIzkvb/nlKjoi7HmlofmqKHmnb9cbiAgICAgICAgICAgIGlmICh0aGlzLnNldHRpbmdzLnRlbXBsYXRlRW4gIT09IERFRkFVTFRfVEVNUExBVEVfRU4pIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5zZXR0aW5ncy50ZW1wbGF0ZUVuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8g5aaC5p6c5Lit5paH5qih5p2/5bey6KKr5L+u5pS577yM5L2/55So5Lit5paH5qih5p2/XG4gICAgICAgICAgICBpZiAodGhpcy5zZXR0aW5ncy50ZW1wbGF0ZVpoICE9PSBERUZBVUxUX1RFTVBMQVRFX1pIKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuc2V0dGluZ3MudGVtcGxhdGVaaDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIOWmguaenOmDveaYr+m7mOiupOaooeadv++8jOS9v+eUqOiLseaWh+m7mOiupOaooeadv1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuc2V0dGluZ3MudGVtcGxhdGVFbjtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIOajgOafpeaYr+WQpuWtmOWcqOiHquWumuS5ieaooeadv1xuICAgICAqL1xuICAgIGhhc0N1c3RvbVRlbXBsYXRlKCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gISF0aGlzLnNldHRpbmdzLmN1c3RvbVRlbXBsYXRlO1xuICAgIH1cbn0gIl19 \ No newline at end of file diff --git a/src/settings/settings.ts b/src/settings/settings.ts new file mode 100644 index 0000000..b2e49cf --- /dev/null +++ b/src/settings/settings.ts @@ -0,0 +1,843 @@ +import { App, ButtonComponent, DropdownComponent, Notice, Plugin, PluginSettingTab, Setting, TextAreaComponent, TextComponent, ToggleComponent } from 'obsidian'; +import { AutoGenerateMode, DEFAULT_SETTINGS, DEFAULT_TEMPLATE_EN, DEFAULT_TEMPLATE_ZH, DailyTaskSettings, Language } from '../models/settings'; +import { getTranslation, setCurrentLanguage } from '../i18n/i18n'; +import { renderTemplate } from '../utils/templateEngine'; +import { TaskGenerator } from '../taskGenerator'; + +// 前向声明,避免循环导入 +declare class DailyTaskPlugin { + saveData(data: any): Promise; + loadData(): Promise; + app: App; +} + +// CSS 相关代码 +const SettingsSectionCSS = "daily-task-settings-section"; +const ButtonCSS = "daily-task-button"; +const PreviewButtonCSS = "daily-task-preview-button"; +const ResetButtonCSS = "daily-task-reset-button"; +const EditorCSS = "daily-task-editor"; +const VerticalStackCSS = "daily-task-vertical-stack"; +const TextRightCSS = "daily-task-text-right"; +const TextCenterCSS = "daily-task-text-center"; +const ScrollbarSlimCSS = "daily-task-slim-scrollbar"; + +/** + * 添加插件自定义样式 + */ +function addCustomStyles() { + const cssText = ` + .${SettingsSectionCSS} { + margin-top: 24px; + margin-bottom: 24px; + padding: 12px 0; + border-top: 1px solid var(--background-modifier-border); + } + + .${ButtonCSS} { + margin-top: 6px; + margin-bottom: 6px; + } + + .${PreviewButtonCSS}, .${ResetButtonCSS} { + display: inline-block; + text-align: center !important; + width: 100%; + } + + .${EditorCSS} { + height: 200px; + margin-top: 12px; + margin-bottom: 12px; + } + + .${VerticalStackCSS} { + display: flex; + flex-direction: column; + } + + .${TextRightCSS} { + text-align: right; + } + + .${TextCenterCSS} { + text-align: center !important; + } + + .${ScrollbarSlimCSS} .CodeMirror-vscrollbar { + width: 20% !important; + } + + /* 自定义通知样式 */ + .daily-task-success-notice { + background-color: rgba(0, 255, 127, 0.2) !important; + } + + .daily-task-warning-notice { + background-color: rgba(255, 165, 0, 0.2) !important; + } + + .daily-task-error-notice { + background-color: rgba(255, 69, 0, 0.2) !important; + } + `; + + // 添加自定义样式 + const styleElement = document.createElement('style'); + styleElement.textContent = cssText; + document.head.appendChild(styleElement); +} + +/** + * 插件设置标签页 + */ +export class DailyTaskSettingTab extends PluginSettingTab { + plugin: Plugin; + settingsManager: SettingsManager; + taskGenerator: TaskGenerator; + previewEl: HTMLElement | null = null; + addTaskButton: ButtonComponent | null = null; + + // 目录输入框 + rootDirInput: TextComponent | null = null; + + // 标记设置是否已修改但未保存 + dirtySettings: boolean = false; + + // 自动保存目录的方法 + autoSaveRootDir: (value: string) => Promise; + + constructor(app: App, plugin: Plugin) { + super(app, plugin); + this.plugin = plugin; + + // 获取父插件中的设置管理器引用 + this.settingsManager = (plugin as any).settingsManager; + + // 创建任务生成器 + this.taskGenerator = new TaskGenerator(app, this.settingsManager); + } + + display(): void { + const { containerEl } = this; + containerEl.empty(); + containerEl.classList.add('daily-task-setting-tab'); + + const settings = this.settingsManager.getSettings(); + + // 添加顶部间距(增加间距以改善界面美观) + const topSpacing = containerEl.createEl('div'); + topSpacing.style.marginTop = '30px'; + + // 根目录设置 + const rootDirSetting = new Setting(containerEl) + .setName(getTranslation('settings.rootDir')) + .setDesc(getTranslation('settings.rootDir.desc')); + + // 创建输入框容器,使其可以包含额外元素 + const inputContainer = document.createElement('div'); + inputContainer.style.display = 'flex'; + inputContainer.style.width = '100%'; + inputContainer.style.position = 'relative'; + rootDirSetting.controlEl.appendChild(inputContainer); + + this.rootDirInput = new TextComponent(inputContainer) + .setValue(settings.rootDir) + .onChange(async (value) => { + if (value.trim() !== '') { + // 设置已更改,准备自动保存 + this.dirtySettings = true; + + // 启动自动保存定时器 + this.autoSaveRootDir(value); + } + }); + + // 给input元素直接设置placeholder属性 + if (this.rootDirInput && this.rootDirInput.inputEl) { + this.rootDirInput.inputEl.placeholder = 'DailyTasks'; + // 增加宽度 + this.rootDirInput.inputEl.style.width = '100%'; + // 美化输入框样式 + this.rootDirInput.inputEl.style.borderRadius = '4px'; + this.rootDirInput.inputEl.style.padding = '8px 35px 8px 10px'; + this.rootDirInput.inputEl.style.transition = 'all 0.3s ease'; + } + + // 添加自动保存指示器 + const saveIndicator = document.createElement('div'); + saveIndicator.classList.add('save-indicator'); + saveIndicator.style.position = 'absolute'; + saveIndicator.style.right = '10px'; + saveIndicator.style.top = '50%'; + saveIndicator.style.transform = 'translateY(-50%)'; + saveIndicator.style.opacity = '0'; + saveIndicator.style.transition = 'opacity 0.3s ease'; + inputContainer.appendChild(saveIndicator); + + // 创建保存成功图标 + const saveSuccessIcon = createSpan({cls: 'svg-icon lucide-check'}); + saveSuccessIcon.style.color = '#4CAF50'; + saveSuccessIcon.style.width = '18px'; + saveSuccessIcon.style.height = '18px'; + saveIndicator.appendChild(saveSuccessIcon); + + // 记录自动保存定时器 + let autoSaveTimer: number | null = null; + + // 自动保存方法 + this.autoSaveRootDir = async (value: string) => { + // 清除之前的定时器 + if (autoSaveTimer !== null) { + clearTimeout(autoSaveTimer); + } + + // 设置新的定时器,延迟800ms保存(在用户停止输入后) + autoSaveTimer = setTimeout(async () => { + let pathToSave = value.trim(); + if (pathToSave === '') { + pathToSave = 'DailyTasks'; // 默认存放目录 + } + + // 保存设置 + await this.settingsManager.updateSettings({ rootDir: pathToSave }); + this.dirtySettings = false; + + // 显示保存成功指示器 + saveIndicator.style.opacity = '1'; + + // 3秒后隐藏 + setTimeout(() => { + saveIndicator.style.opacity = '0'; + }, 2000); + }, 800) as unknown as number; + }; + + // 自动生成模式 + const autoGenSetting = new Setting(containerEl) + .setName(getTranslation('settings.autoGenerate')) + .setDesc(getTranslation('settings.autoGenerate.desc')); + + // 自定义三选滑块 + const toggleContainer = document.createElement('div'); + toggleContainer.classList.add('mode-toggle-container'); + toggleContainer.style.width = '20%'; // 缩短滑动条长度为原来的20% + toggleContainer.style.marginLeft = 'auto'; // 靠右显示 + + const modes = [ + { value: AutoGenerateMode.NONE, label: getTranslation('settings.mode.none') }, + { value: AutoGenerateMode.DAILY, label: getTranslation('settings.mode.daily') }, + { value: AutoGenerateMode.WORKDAY, label: getTranslation('settings.mode.workday') } + ]; + + // 滑块指示器 + const slider = document.createElement('div'); + slider.classList.add('mode-toggle-slider'); + toggleContainer.appendChild(slider); + + // 更新滑块位置 + const updateSlider = (index: number) => { + slider.style.left = `${index * 33.33}%`; + }; + + // 设置默认模式为仅工作日生成(WorkDay) + let currentModeIndex = modes.findIndex(mode => mode.value === settings.autoGenerateMode); + if (currentModeIndex === -1) { + // 如果没有找到有效的模式,设置为工作日模式 + currentModeIndex = modes.findIndex(mode => mode.value === AutoGenerateMode.WORKDAY); + // 更新设置到工作日模式 + this.settingsManager.updateSettings({ autoGenerateMode: AutoGenerateMode.WORKDAY }); + } + updateSlider(currentModeIndex); + + // 创建选项 + modes.forEach((mode, index) => { + const option = document.createElement('div'); + option.classList.add('mode-toggle-option'); + option.textContent = mode.label; + + if (mode.value === settings.autoGenerateMode) { + option.classList.add('active'); + } + + option.addEventListener('click', async () => { + // 更新视觉状态 + toggleContainer.querySelectorAll('.mode-toggle-option').forEach(el => { + el.classList.remove('active'); + }); + option.classList.add('active'); + + // 更新滑块位置 + updateSlider(index); + + // 保存设置 + await this.settingsManager.updateSettings({ autoGenerateMode: mode.value }); + }); + + toggleContainer.appendChild(option); + }); + + autoGenSetting.controlEl.appendChild(toggleContainer); + + // 语言设置 + new Setting(containerEl) + .setName(getTranslation('settings.language')) + .setDesc(getTranslation('settings.language.desc')) + .addDropdown(dropdown => { + dropdown + .addOption(Language.AUTO, getTranslation('settings.language.auto')) + .addOption(Language.ZH, getTranslation('settings.language.zh')) + .addOption(Language.EN, getTranslation('settings.language.en')) + .setValue(settings.language) + .onChange(async (value) => { + await this.settingsManager.updateSettings({ language: value as Language }); + // 需要重新加载设置页面以更新翻译 + this.display(); + }); + }); + + // 通知显示时间 + new Setting(containerEl) + .setName(getTranslation('settings.notificationDuration')) + .setDesc(getTranslation('settings.notificationDuration.desc')) + .addText(text => { + const component = text + .setValue(settings.successNotificationDuration.toString()) + .onChange(async (value) => { + const duration = parseInt(value); + if (!isNaN(duration) && duration > 0) { + await this.settingsManager.updateSettings({ successNotificationDuration: duration }); + } + }); + + // 直接设置placeholder属性 + component.inputEl.placeholder = '3000'; + + return component; + }); + + // 任务统计功能开关 + new Setting(containerEl) + .setName(getTranslation('settings.taskStatistics')) + .setDesc(getTranslation('settings.taskStatistics.desc')) + .addToggle((toggle) => { + // 设置开关样式 + const toggleEl = toggle + .setValue(settings.taskStatistics) + .onChange(async (value) => { + await this.settingsManager.updateSettings({ taskStatistics: value }); + }); + + // 自定义开关样式 - 使用DOM元素访问 + // @ts-ignore - 添加这行来忽略TypeScript警告 + const toggleControl = toggle.toggleEl || toggle.containerEl.querySelector('.checkbox-container'); + if (toggleControl) { + toggleControl.classList.add('task-statistics-toggle'); + + // 添加图标 - 使用DOM父元素访问 + const toggleContainer = toggleControl.parentElement; + if (toggleContainer) { + const iconEl = createSpan({cls: 'svg-icon lucide-bar-chart-2'}); + iconEl.style.marginRight = '8px'; + iconEl.style.color = 'var(--text-accent)'; + toggleContainer.prepend(iconEl); + + // 添加过渡效果 + toggleContainer.style.transition = 'all 0.3s ease'; + toggleControl.style.transition = 'all 0.3s ease'; + } + } + + return toggleEl; + }); + + // 模板设置 + containerEl.createEl('h3', { text: getTranslation('settings.template') }); + + // 添加模板使用逻辑说明 + const templateDescription = containerEl.createEl('p', { + text: this.settingsManager.getCurrentLanguage() === 'zh' ? + '注意:默认模板会根据当前语言环境自动选择对应语言的内容。如果您自定义模板,将在所有语言环境中使用您的自定义内容。' : + 'Note: Default template automatically adapts to your language environment. If you customize the template, your content will be used in all language environments.' + }); + templateDescription.style.fontSize = '0.85em'; + templateDescription.style.opacity = '0.8'; + templateDescription.style.marginBottom = '15px'; + + // 添加模板变量说明 + const templateVariablesEl = containerEl.createEl('p'); + templateVariablesEl.innerHTML = this.settingsManager.getCurrentLanguage() === 'zh' ? + '可用变量: {{date}} - 日期, {{dateWithIcon}} - 带图标的日期, {{weekday}} - 星期几, {{yearProgress}} - 年进度, {{monthProgress}} - 月进度, {{time}} - 当前时间' : + 'Available variables: {{date}} - Date, {{dateWithIcon}} - Date with icon, {{weekday}} - Day of week, {{yearProgress}} - Year progress, {{monthProgress}} - Month progress, {{time}} - Current time'; + templateVariablesEl.style.fontSize = '0.85em'; + templateVariablesEl.style.marginBottom = '10px'; + + // 单一模板设置 + const templateSetting = new Setting(containerEl) + .setName(getTranslation('settings.template')) + .setClass('template-setting'); + + const templateContainer = document.createElement('div'); + templateContainer.style.width = '100%'; + + // 获取当前模板内容 + const currentTemplate = this.settingsManager.hasCustomTemplate() ? + this.settingsManager.getSettings().customTemplate : + this.settingsManager.getTemplateByLanguage(); + + const textarea = new TextAreaComponent(templateContainer) + .setValue(currentTemplate) + .setPlaceholder(this.settingsManager.getCurrentLanguage() === 'zh' ? + '在此处输入任务模板...' : + 'Enter task template here...') + .onChange(async (value) => { + // 更新为自定义模板 + await this.settingsManager.updateSettings({ + customTemplate: value, + hasCustomTemplate: true + }); + this.updatePreview(this.previewEl, value); + }); + + // 改进TextArea样式 + textarea.inputEl.classList.add('template-editor'); + textarea.inputEl.style.height = '200px'; + textarea.inputEl.style.border = '1px solid var(--background-modifier-border-hover)'; + textarea.inputEl.style.borderRadius = '4px'; + textarea.inputEl.style.padding = '12px'; + textarea.inputEl.style.lineHeight = '1.5'; + textarea.inputEl.style.fontSize = '14px'; + textarea.inputEl.style.fontFamily = 'var(--font-monospace)'; + textarea.inputEl.style.transition = 'all 0.2s ease'; + textarea.inputEl.style.boxShadow = 'inset 0 1px 3px rgba(0, 0, 0, 0.1)'; + textarea.inputEl.style.backgroundColor = 'var(--background-primary)'; + textarea.inputEl.style.color = 'var(--text-normal)'; + textarea.inputEl.style.resize = 'vertical'; + + // 当获取焦点时改变边框样式 + textarea.inputEl.addEventListener('focus', () => { + textarea.inputEl.style.border = '1px solid var(--interactive-accent)'; + textarea.inputEl.style.boxShadow = '0 0 0 2px rgba(var(--interactive-accent-rgb), 0.2), inset 0 1px 3px rgba(0, 0, 0, 0.1)'; + textarea.inputEl.style.outline = 'none'; + }); + + // 失去焦点时恢复原来的边框样式 + textarea.inputEl.addEventListener('blur', () => { + textarea.inputEl.style.border = '1px solid var(--background-modifier-border-hover)'; + textarea.inputEl.style.boxShadow = 'inset 0 1px 3px rgba(0, 0, 0, 0.1)'; + }); + + // 预览标题,使用flex布局居中 + const previewHeader = document.createElement('div'); + previewHeader.classList.add('template-preview-header'); + previewHeader.style.display = 'flex'; + previewHeader.style.justifyContent = 'space-between'; // 改为两端对齐 + previewHeader.style.marginTop = '15px'; + previewHeader.style.marginBottom = '10px'; + previewHeader.style.width = '100%'; + + // 预览按钮容器 - 左侧 + const previewBtnContainer = document.createElement('div'); + previewBtnContainer.style.display = 'flex'; + previewBtnContainer.style.alignItems = 'center'; + + // 重置按钮容器 - 右侧 + const resetBtnContainer = document.createElement('div'); + resetBtnContainer.style.display = 'flex'; + resetBtnContainer.style.alignItems = 'center'; + + // 预览按钮 - 改进样式 + const toggleButton = new ButtonComponent(previewBtnContainer) + .setButtonText(getTranslation('settings.template.preview')); + + // 添加样式类 + toggleButton.buttonEl.addClass(TextCenterCSS); + toggleButton.buttonEl.style.textAlign = 'center'; + toggleButton.buttonEl.style.display = 'flex'; + toggleButton.buttonEl.style.alignItems = 'center'; + toggleButton.buttonEl.style.justifyContent = 'center'; + toggleButton.buttonEl.style.width = '130px'; + toggleButton.buttonEl.style.borderRadius = '4px'; + toggleButton.buttonEl.style.boxShadow = '0 2px 5px rgba(0,0,0,0.1)'; + toggleButton.buttonEl.style.transition = 'all 0.2s ease'; + + // 手动添加眼睛图标 + const eyeIcon = createSpan({cls: 'svg-icon lucide-eye'}); + eyeIcon.style.marginRight = '6px'; + toggleButton.buttonEl.prepend(eyeIcon); + + // 预览区域 + this.previewEl = document.createElement('div'); + this.previewEl.classList.add('template-preview'); + this.previewEl.style.marginTop = '15px'; + this.previewEl.style.padding = '15px'; + this.previewEl.style.border = '1px dashed var(--background-modifier-border)'; + this.previewEl.style.borderRadius = '8px'; + this.previewEl.style.backgroundColor = 'var(--background-secondary)'; + this.previewEl.style.display = 'none'; + this.previewEl.style.maxHeight = '200px'; + this.previewEl.style.overflow = 'auto'; + this.previewEl.style.boxShadow = 'inset 0 0 5px rgba(0,0,0,0.1)'; + this.updatePreview(this.previewEl, currentTemplate); + templateContainer.appendChild(this.previewEl); + + toggleButton.onClick(() => { + this.togglePreview(this.previewEl); + // 切换图标和按钮文本 + if (this.previewEl && this.previewEl.classList.contains('visible')) { + eyeIcon.className = 'svg-icon lucide-eye-off'; + toggleButton.setButtonText(getTranslation('settings.template.hide')); + toggleButton.buttonEl.style.backgroundColor = 'var(--background-modifier-success)'; + } else { + eyeIcon.className = 'svg-icon lucide-eye'; + toggleButton.setButtonText(getTranslation('settings.template.preview')); + toggleButton.buttonEl.style.backgroundColor = ''; + } + }); + + // 重置按钮 - 改进样式 + const resetBtn = new ButtonComponent(resetBtnContainer) + .setButtonText(getTranslation('settings.resetDefault')); + + // 添加样式类 + resetBtn.buttonEl.addClass(TextCenterCSS); + resetBtn.buttonEl.style.textAlign = 'center'; + resetBtn.buttonEl.style.display = 'flex'; + resetBtn.buttonEl.style.alignItems = 'center'; + resetBtn.buttonEl.style.justifyContent = 'center'; + resetBtn.buttonEl.style.width = '150px'; + resetBtn.buttonEl.style.borderRadius = '4px'; + resetBtn.buttonEl.style.boxShadow = '0 2px 5px rgba(0,0,0,0.1)'; + resetBtn.buttonEl.style.transition = 'all 0.2s ease'; + + // 添加重置图标 + const resetIcon = createSpan({cls: 'svg-icon lucide-refresh-cw'}); + resetIcon.style.marginRight = '6px'; + resetBtn.buttonEl.prepend(resetIcon); + + // 添加重置事件 + resetBtn.onClick(async () => { + // 将自定义模板设置为空,回到使用默认模板 + await this.settingsManager.updateSettings({ + customTemplate: '', + hasCustomTemplate: false + }); + + // 获取当前语言的默认模板 + const defaultTemplate = this.settingsManager.getTemplateByLanguage(); + + // 更新输入框和预览 + textarea.setValue(defaultTemplate); + this.updatePreview(this.previewEl, defaultTemplate); + + // 显示成功提示动画 + resetBtn.buttonEl.style.backgroundColor = 'var(--background-modifier-success)'; + setTimeout(() => { + resetBtn.buttonEl.style.backgroundColor = ''; + }, 1000); + }); + + // 将按钮添加到各自的容器 + previewHeader.appendChild(previewBtnContainer); + previewHeader.appendChild(resetBtnContainer); + templateContainer.appendChild(previewHeader); + + templateSetting.controlEl.appendChild(templateContainer); + + // 恢复默认设置 - 创建容器让按钮右对齐 + const resetContainer = document.createElement('div'); + resetContainer.style.display = 'flex'; + resetContainer.style.justifyContent = 'flex-end'; // 确保右对齐 + resetContainer.style.marginTop = '20px'; + resetContainer.style.marginBottom = '10px'; + containerEl.appendChild(resetContainer); + + // 恢复默认设置按钮 + const resetDefaultBtn = new ButtonComponent(resetContainer) + .setButtonText(getTranslation('settings.resetToDefault')); + + // 添加样式类 + resetDefaultBtn.buttonEl.addClass(TextCenterCSS); + resetDefaultBtn.buttonEl.addClass('danger-button'); + resetDefaultBtn.buttonEl.style.textAlign = 'center'; + resetDefaultBtn.buttonEl.style.display = 'flex'; + resetDefaultBtn.buttonEl.style.alignItems = 'center'; + resetDefaultBtn.buttonEl.style.justifyContent = 'center'; + resetDefaultBtn.buttonEl.style.width = '150px'; + + // 添加重置图标和危险按钮样式 + resetDefaultBtn.buttonEl.prepend(createSpan({cls: 'svg-icon lucide-refresh-cw'})); + + // 为全局重置按钮添加事件处理 + resetDefaultBtn.onClick(async () => { + await this.settingsManager.resetToDefaults(); + this.display(); + }); + + // 手动添加今日任务按钮 - 右对齐显示 + const addTaskContainer = document.createElement('div'); + addTaskContainer.style.display = 'flex'; + addTaskContainer.style.justifyContent = 'flex-end'; // 确保右对齐 + addTaskContainer.style.marginTop = '20px'; + containerEl.appendChild(addTaskContainer); + + this.addTaskButton = new ButtonComponent(addTaskContainer) + .setButtonText(getTranslation('settings.addTaskButton')) + .setCta(); + + // 添加样式类 - 确保按钮文字居中 + if (this.addTaskButton && this.addTaskButton.buttonEl) { + this.addTaskButton.buttonEl.addClass(TextCenterCSS); + this.addTaskButton.buttonEl.style.textAlign = 'center'; + this.addTaskButton.buttonEl.style.display = 'flex'; + this.addTaskButton.buttonEl.style.alignItems = 'center'; + this.addTaskButton.buttonEl.style.justifyContent = 'center'; + } + + // 手动添加任务按钮事件处理 + this.addTaskButton.onClick(async () => { + // 检查目录设置 + const rootDir = this.settingsManager.getSettings().rootDir; + + // 添加loading状态 + if (this.addTaskButton && this.addTaskButton.buttonEl) { + this.addTaskButton.buttonEl.classList.add('loading'); + } + this.addTaskButton?.setDisabled(true); + + try { + // 添加任务 + await this.taskGenerator.addTaskManually(); + } catch (e) { + console.error("添加任务出错:", e); + new Notice(`添加任务失败: ${e.message || e}`); + } finally { + // 移除loading状态 + setTimeout(() => { + if (this.addTaskButton && this.addTaskButton.buttonEl) { + this.addTaskButton.buttonEl.classList.remove('loading'); + } + this.addTaskButton?.setDisabled(false); + }, 500); + } + }); + + // 添加图标 + this.addTaskButton.buttonEl.prepend(createSpan({cls: 'svg-icon lucide-calendar-plus'})); + } + + /** + * 更新模板预览 + */ + private updatePreview(previewEl: HTMLElement | null, template: string): void { + if (!previewEl) return; + + const renderedContent = renderTemplate(template); + // 使用MarkdownRenderer需要导入相关组件 + previewEl.innerHTML = renderedContent.replace(/\n/g, '
'); + } + + /** + * 切换预览的显示/隐藏 + */ + private togglePreview(previewEl: HTMLElement | null): void { + if (!previewEl) return; + + if (previewEl.style.display === 'none') { + previewEl.style.display = 'block'; + previewEl.classList.add('visible'); + } else { + previewEl.style.display = 'none'; + previewEl.classList.remove('visible'); + } + } +} + +/** + * 设置管理器 + * 负责加载、保存和提供设置访问接口 + */ +export class SettingsManager { + private plugin: Plugin; + private settings: DailyTaskSettings; + + constructor(plugin: Plugin) { + this.plugin = plugin; + this.settings = Object.assign({}, DEFAULT_SETTINGS); + } + + /** + * 获取当前设置 + */ + getSettings(): DailyTaskSettings { + return this.settings; + } + + /** + * 更新设置并保存 + * @param settings 要更新的设置 + */ + async updateSettings(settings: Partial): Promise { + this.settings = { + ...this.settings, + ...settings + }; + await this.saveSettings(); + + // 更新当前语言 + this.updateCurrentLanguage(); + } + + /** + * 保存设置到数据存储 + */ + async saveSettings(): Promise { + await this.plugin.saveData(this.settings); + } + + /** + * 加载设置 + */ + async loadSettings(): Promise { + const loadedData = await this.plugin.loadData(); + if (loadedData) { + // 合并默认设置和已保存的设置 + this.settings = { + ...DEFAULT_SETTINGS, + ...loadedData + }; + + // 确保在升级插件后,新增的设置项也有默认值 + this.ensureSettingsCompleteness(); + } else { + // 如果没有加载到数据,使用默认设置但将自动生成模式改为工作日 + this.settings = { + ...DEFAULT_SETTINGS, + autoGenerateMode: AutoGenerateMode.WORKDAY + }; + } + + // 更新当前语言 + this.updateCurrentLanguage(); + } + + /** + * 确保设置完整性,为新增的设置项提供默认值 + */ + private ensureSettingsCompleteness(): void { + const defaultKeys = Object.keys(DEFAULT_SETTINGS); + defaultKeys.forEach(key => { + // 如果当前设置中缺少某个默认设置项,添加默认值 + if (!(key in this.settings)) { + (this.settings as any)[key] = (DEFAULT_SETTINGS as any)[key]; + } + }); + } + + /** + * 恢复默认设置 + */ + async resetToDefaults(): Promise { + this.settings = Object.assign({}, DEFAULT_SETTINGS); + await this.saveSettings(); + + // 更新当前语言 + this.updateCurrentLanguage(); + } + + /** + * 根据语言获取当前使用的模板 + * 如果当前模板不是默认模板,则不再区分语言 + */ + getCurrentTemplate(): string { + const language = this.getCurrentLanguage(); + + // 中文环境 + if (language === 'zh') { + // 如果中文模板已被修改(不等于默认模板),使用中文模板 + if (this.settings.templateZh !== DEFAULT_TEMPLATE_ZH) { + return this.settings.templateZh; + } + // 如果英文模板已被修改,使用英文模板 + if (this.settings.templateEn !== DEFAULT_TEMPLATE_EN) { + return this.settings.templateEn; + } + // 如果都是默认模板,使用中文默认模板 + return this.settings.templateZh; + } + // 英文环境 + else { + // 如果英文模板已被修改(不等于默认模板),使用英文模板 + if (this.settings.templateEn !== DEFAULT_TEMPLATE_EN) { + return this.settings.templateEn; + } + // 如果中文模板已被修改,使用中文模板 + if (this.settings.templateZh !== DEFAULT_TEMPLATE_ZH) { + return this.settings.templateZh; + } + // 如果都是默认模板,使用英文默认模板 + return this.settings.templateEn; + } + } + + /** + * 获取当前语言设置 + */ + getCurrentLanguage(): string { + if (this.settings.language === Language.AUTO) { + // 自动检测系统语言 + const systemLanguage = window.navigator.language.toLowerCase(); + return systemLanguage.startsWith('zh') ? 'zh' : 'en'; + } + return this.settings.language; + } + + /** + * 更新当前语言 + */ + private updateCurrentLanguage(): void { + const language = this.getCurrentLanguage(); + setCurrentLanguage(language); + } + + /** + * 获取当前语言的模板 + */ + getTemplateByLanguage(): string { + const language = this.getCurrentLanguage(); + + // 中文环境 + if (language === 'zh') { + // 如果中文模板已被修改(不等于默认模板),使用中文模板 + if (this.settings.templateZh !== DEFAULT_TEMPLATE_ZH) { + return this.settings.templateZh; + } + // 如果英文模板已被修改,使用英文模板 + if (this.settings.templateEn !== DEFAULT_TEMPLATE_EN) { + return this.settings.templateEn; + } + // 如果都是默认模板,使用中文默认模板 + return this.settings.templateZh; + } + // 英文环境 + else { + // 如果英文模板已被修改(不等于默认模板),使用英文模板 + if (this.settings.templateEn !== DEFAULT_TEMPLATE_EN) { + return this.settings.templateEn; + } + // 如果中文模板已被修改,使用中文模板 + if (this.settings.templateZh !== DEFAULT_TEMPLATE_ZH) { + return this.settings.templateZh; + } + // 如果都是默认模板,使用英文默认模板 + return this.settings.templateEn; + } + } + + /** + * 检查是否存在自定义模板 + */ + hasCustomTemplate(): boolean { + return !!this.settings.customTemplate; + } +} \ No newline at end of file diff --git a/src/styles.css b/src/styles.css new file mode 100644 index 0000000..07c4153 --- /dev/null +++ b/src/styles.css @@ -0,0 +1,91 @@ +/* 任务统计样式 */ +.daily-task-statistics { + margin-top: 10px; + margin-bottom: 20px; + padding: 15px; + border-radius: 8px; + background-color: rgba(var(--interactive-accent-rgb), 0.1); + border-left: 4px solid var(--interactive-accent); +} + +.daily-task-statistics h3 { + margin-top: 0; + display: flex; + align-items: center; + font-size: 1.1em; + color: var(--text-accent); +} + +.daily-task-statistics h4 { + margin-top: 12px; + margin-bottom: 8px; + font-size: 0.95em; + color: var(--text-accent); +} + +/* 提示建议样式 */ +.daily-task-suggestions { + padding: 10px 15px; + border-radius: 6px; + background-color: rgba(var(--background-modifier-success-rgb), 0.1); + margin-top: 10px; +} + +/* 任务统计开关样式 */ +.task-statistics-toggle { + position: relative; + transition: all 0.3s ease; +} + +.task-statistics-toggle:hover { + transform: scale(1.05); +} + +.task-statistics-toggle.is-enabled { + background-color: var(--interactive-accent) !important; +} + +/* 自定义滚动条样式 */ +.daily-task-setting-tab ::-webkit-scrollbar { + width: 8px; /* 设置滚动条宽度 */ +} + +.daily-task-setting-tab ::-webkit-scrollbar-track { + background: var(--background-secondary); /* 设置轨道背景 */ + border-radius: 4px; +} + +.daily-task-setting-tab ::-webkit-scrollbar-thumb { + background: var(--interactive-accent); /* 设置滑块颜色 */ + border-radius: 4px; + opacity: 0.7; + transition: background 0.2s ease; +} + +.daily-task-setting-tab ::-webkit-scrollbar-thumb:hover { + background: var(--interactive-accent-hover); /* 滑块悬停颜色 */ + opacity: 1; +} + +/* 应用滚动条样式到所有可滚动元素 */ +.daily-task-slim-scrollbar { + scrollbar-width: thin; +} + +.daily-task-slim-scrollbar::-webkit-scrollbar { + width: 8px; +} + +.daily-task-slim-scrollbar::-webkit-scrollbar-track { + background: var(--background-secondary); + border-radius: 4px; +} + +.daily-task-slim-scrollbar::-webkit-scrollbar-thumb { + background: var(--interactive-accent); + border-radius: 4px; +} + +.daily-task-slim-scrollbar::-webkit-scrollbar-thumb:hover { + background: var(--interactive-accent-hover); +} \ No newline at end of file diff --git a/src/taskGenerator.js b/src/taskGenerator.js new file mode 100644 index 0000000..c3a90d8 --- /dev/null +++ b/src/taskGenerator.js @@ -0,0 +1,262 @@ +import { Notice, TFile } from "obsidian"; +import { getTranslation } from "./i18n/i18n"; +import { appendToFile, ensureFileExists, ensureFolderExists, getTaskFilePath, todayTaskExists, fileContains, getYesterdayDate, getYesterdayTaskFilePath, extractTasksForDate, analyzeTaskCompletion, yesterdayStatisticsExists, generateStatisticsContent } from "./utils/fileUtils"; +import { renderTemplate } from "./utils/templateEngine"; +import { getCurrentDate } from "./utils/dateUtils"; +/** + * 任务生成器 + * 负责创建任务文件和添加任务内容 + */ +export class TaskGenerator { + constructor(app, settingsManager) { + this.app = app; + this.vault = app.vault; + this.settingsManager = settingsManager; + } + /** + * 生成每日任务 + * @param openFile 是否打开文件 + * @param quietMode 静默模式,减少日志输出 + * @returns 成功或失败 + */ + async generateDailyTask(openFile = true, quietMode = false) { + try { + const settings = this.settingsManager.getSettings(); + const rootDir = settings.rootDir.trim() || 'DailyTasks'; // 使用默认目录 + // 获取任务文件路径 + const filePath = getTaskFilePath(rootDir); + if (!quietMode) + console.log(`生成任务文件路径: ${filePath}`); + // 解析年份和月份 + const pathParts = filePath.split('/'); + const year = pathParts.length > 1 ? pathParts[1] : ''; + const monthName = pathParts.length > 2 ? pathParts[2].replace('.md', '') : ''; + const yearFolder = `${rootDir}/${year}`; + // 确保根目录存在 + if (!quietMode) + console.log(`正在确保根目录存在: ${rootDir}`); + const rootCreated = await ensureFolderExists(this.vault, rootDir); + if (!rootCreated) { + console.error(`无法访问或创建根目录: ${rootDir}`); + throw new Error(`无法访问根目录: ${rootDir},可能是存在同名文件或权限问题`); + } + if (!quietMode) + console.log(`根目录确认: ${rootDir}`); + // 确保年份目录存在 + if (!quietMode) + console.log(`正在确保年份目录存在: ${yearFolder}`); + const yearCreated = await ensureFolderExists(this.vault, yearFolder); + if (!yearCreated) { + console.error(`无法访问或创建年份目录: ${yearFolder}`); + throw new Error(`无法访问年份目录: ${yearFolder},可能是存在同名文件或权限问题`); + } + if (!quietMode) + console.log(`年份目录确认: ${yearFolder}`); + // 确保月份文件存在 + if (!quietMode) + console.log(`正在确保月份文件存在: ${filePath} (${monthName})`); + const fileCreated = await ensureFileExists(this.vault, filePath); + if (!fileCreated) { + console.error(`无法访问或创建月份文件: ${filePath}`); + throw new Error(`无法访问月份文件: ${filePath},请检查是否存在同名目录或权限问题`); + } + if (!quietMode) + console.log(`月份文件确认: ${filePath}`); + // 检查今日任务是否已存在 + const date = getCurrentDate(); + // 更改检查方式:不仅检查纯日期,也检查带图标的日期格式 + const dateRegex = new RegExp(`## [^\\n]*${date}[^\\n]*\\n`); + const fileContent = await this.vault.read(this.vault.getAbstractFileByPath(filePath)); + const existingTaskCheck = dateRegex.test(fileContent); + + if (existingTaskCheck) { + console.log(`今日(${date})任务已存在于文件中,跳过创建`); + // 如果需要打开文件 + if (openFile) { + // 显示提示并打开文件 + this.showWarningNotice(`📌 ${getTranslation('notification.taskExists')}`); + const file = this.vault.getAbstractFileByPath(filePath); + if (file && file instanceof TFile) { + const leaf = this.app.workspace.getLeaf(); + await leaf.openFile(file); + } + } + return true; // 任务已存在,视为成功 + } + // 如果开启了任务统计功能,在添加今日任务前添加昨日统计 + let statisticsContent = ''; + if (settings.taskStatistics) { + statisticsContent = await this.generateYesterdayStatistics(rootDir, date, quietMode); + } + // 获取任务模板 - 使用新的模板逻辑 + let template = ''; + if (this.settingsManager.hasCustomTemplate()) { + // 使用用户自定义模板 + template = this.settingsManager.getSettings().customTemplate; + } + else { + // 使用语言相关的默认模板 + template = this.settingsManager.getTemplateByLanguage(); + } + // 渲染模板 + const renderedContent = renderTemplate(template); + // 合并统计信息和今日任务内容 + const fullContent = statisticsContent + ? `${renderedContent}\n\n${statisticsContent}` + : renderedContent; + // 追加到文件 + if (!quietMode) + console.log(`正在向文件追加内容`); + const success = await appendToFile(this.vault, filePath, fullContent); + if (success) { + console.log(`✅ 任务内容追加成功 ${date}`); + // 如果需要打开文件 + if (openFile) { + // 打开创建的文件 + const file = this.vault.getAbstractFileByPath(filePath); + if (file && file instanceof TFile) { + const leaf = this.app.workspace.getLeaf(); + await leaf.openFile(file); + // 延迟一下再显示通知,确保文件已经打开 + setTimeout(() => { + this.showSuccessNotice(`✨ ${getTranslation('notification.taskAdded')}`); + }, 300); + } + else { + throw new Error(`文件创建成功但无法打开: ${filePath}`); + } + } + else { + // 静默模式,只在控制台记录 + console.log(`✨ 今日(${date})任务已静默添加,无需打开文件`); + } + } + else { + throw new Error(`无法向文件追加内容: ${filePath}`); + } + return success; + } + catch (error) { + console.error("Error generating daily task:", error); + // 显示错误通知 + const errorMsg = error instanceof Error ? error.message : String(error); + this.showErrorNotice(`${getTranslation('notification.error')} ${errorMsg}`); + return false; + } + } + /** + * 生成昨日任务统计信息 + * @param rootDir 根目录 + * @param todayDate 今日日期(用于检查是否已存在统计) + * @param quietMode 静默模式 + * @returns 统计信息内容或空字符串 + */ + async generateYesterdayStatistics(rootDir, todayDate, quietMode = false) { + try { + // 获取昨天的日期和文件路径 + const yesterdayDate = getYesterdayDate(); + const yesterdayFilePath = getYesterdayTaskFilePath(rootDir); + + if (!quietMode) console.log(`正在获取昨日(${yesterdayDate})任务统计,文件路径: ${yesterdayFilePath}`); + + // 检查今日的内容中是否已经包含昨日统计 + const todayFilePath = getTaskFilePath(rootDir); + const statsExists = await yesterdayStatisticsExists(this.vault, todayFilePath, todayDate); + + if (statsExists) { + if (!quietMode) console.log('昨日统计信息已存在,跳过生成'); + return ''; + } + + // 提取昨日任务内容 + const yesterdayContent = await extractTasksForDate(this.vault, yesterdayFilePath, yesterdayDate); + + // 如果找不到昨日任务内容 + if (!yesterdayContent) { + if (!quietMode) console.log(`找不到昨日(${yesterdayDate})任务内容,跳过统计`); + return ''; + } + + // 分析任务完成情况 + const taskStats = analyzeTaskCompletion(yesterdayContent); + + if (!quietMode) { + console.log(`昨日任务统计: 总数=${taskStats.totalTasks}, 已完成=${taskStats.completedTasks}`); + console.log(`未完成任务: ${taskStats.unfinishedTasksList.length}个`); + } + + // 如果没有任务,不生成统计信息 + if (taskStats.totalTasks === 0) { + if (!quietMode) console.log('昨日没有任务,跳过统计'); + return ''; + } + + // 确保在所有语言环境下都调用generateStatisticsContent并传递getTranslation函数 + return generateStatisticsContent(taskStats, getTranslation); + + } catch (error) { + console.error(`生成昨日统计信息时出错:`, error); + return ''; // 出错时返回空字符串,不影响今日任务生成 + } + } + /** + * 手动添加今日任务 + * @returns 成功或失败 + */ + async addTaskManually() { + try { + const settings = this.settingsManager.getSettings(); + const rootDir = settings.rootDir.trim() || 'DailyTasks'; // 使用默认目录 + // 检查今日任务是否已存在 + const exists = await todayTaskExists(this.vault, rootDir); + if (exists) { + // 任务已存在,显示提示 + this.showWarningNotice(`📌 ${getTranslation('notification.taskExists')}`); + return false; + } + // 生成任务 + return await this.generateDailyTask(); + } + catch (error) { + console.error("Error adding task manually:", error); + // 显示错误通知 + const errorMsg = error instanceof Error ? error.message : String(error); + this.showErrorNotice(`${getTranslation('notification.error')} ${errorMsg}`); + return false; + } + } + /** + * 显示成功通知 + * @param message 通知内容 + */ + showSuccessNotice(message) { + const notice = new Notice(message, this.settingsManager.getSettings().successNotificationDuration); + // 添加成功样式 + if (notice.noticeEl) { + notice.noticeEl.addClass('daily-task-success-notice'); + } + } + /** + * 显示警告通知 + * @param message 通知内容 + */ + showWarningNotice(message) { + const notice = new Notice(message, this.settingsManager.getSettings().successNotificationDuration); + // 添加警告样式 + if (notice.noticeEl) { + notice.noticeEl.addClass('daily-task-warning-notice'); + } + } + /** + * 显示错误通知 + * @param message 通知内容 + */ + showErrorNotice(message) { + const notice = new Notice(message, this.settingsManager.getSettings().successNotificationDuration); + // 添加错误样式 + if (notice.noticeEl) { + notice.noticeEl.addClass('daily-task-error-notice'); + } + } +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFza0dlbmVyYXRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInRhc2tHZW5lcmF0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFPLE1BQU0sRUFBRSxLQUFLLEVBQVMsTUFBTSxVQUFVLENBQUM7QUFDckQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUU3QyxPQUFPLEVBQUUsWUFBWSxFQUFFLGdCQUFnQixFQUFFLGtCQUFrQixFQUFFLGVBQWUsRUFBRSxlQUFlLEVBQUUsWUFBWSxFQUFFLGdCQUFnQixFQUFFLHdCQUF3QixFQUFFLG1CQUFtQixFQUFFLHFCQUFxQixFQUFFLHlCQUF5QixFQUFFLHlCQUF5QixFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDclIsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ3hELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUVuRDs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sYUFBYTtJQUt0QixZQUFZLEdBQVEsRUFBRSxlQUFnQztRQUNsRCxJQUFJLENBQUMsR0FBRyxHQUFHLEdBQUcsQ0FBQztRQUNmLElBQUksQ0FBQyxLQUFLLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQztRQUN2QixJQUFJLENBQUMsZUFBZSxHQUFHLGVBQWUsQ0FBQztJQUMzQyxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSCxLQUFLLENBQUMsaUJBQWlCLENBQUMsV0FBb0IsSUFBSSxFQUFFLFlBQXFCLEtBQUs7UUFDeEUsSUFBSTtZQUNBLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxlQUFlLENBQUMsV0FBVyxFQUFFLENBQUM7WUFDcEQsTUFBTSxPQUFPLEdBQUcsUUFBUSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsSUFBSSxZQUFZLENBQUMsQ0FBQyxTQUFTO1lBRWxFLFdBQVc7WUFDWCxNQUFNLFFBQVEsR0FBRyxlQUFlLENBQUMsT0FBTyxD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Q3pEO0lBQ0wsQ0FBQztJQUVEOzs7T0FHRztJQUNLLGVBQWUsQ0FBQyxPQUFlO1FBQ25DLE1BQU0sTUFBTSxHQUFHLElBQUksTUFBTSxDQUNyQixPQUFPLEVBQ1AsSUFBSSxDQUFDLGVBQWUsQ0FBQyxXQUFXLEVBQUUsQ0FBQywyQkFBMkIsQ0FDakUsQ0FBQztRQUVGLFNBQVM7UUFDVCxJQUFJLE1BQU0sQ0FBQyxRQUFRLEVBQUU7WUFDakIsTUFBTSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMseUJBQXlCLENBQUMsQ0FBQztTQUN2RDtJQUNMLENBQUM7Q0FDSiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEFwcCwgTm90aWNlLCBURmlsZSwgVmF1bHQgfSBmcm9tIFwib2JzaWRpYW5cIjtcbmltcG9ydCB7IGdldFRyYW5zbGF0aW9uIH0gZnJvbSBcIi4vaTE4bi9pMThuXCI7XG5pbXBvcnQgeyBTZXR0aW5nc01hbmFnZXIgfSBmcm9tIFwiLi9zZXR0aW5ncy9zZXR0aW5nc1wiO1xuaW1wb3J0IHsgYXBwZW5kVG9GaWxlLCBlbnN1cmVGaWxlRXhpc3RzLCBlbnN1cmVGb2xkZXJFeGlzdHMsIGdldFRhc2tGaWxlUGF0aCwgdG9kYXlUYXNrRXhpc3RzLCBmaWxlQ29udGFpbnMsIGdldFllc3RlcmRheURhdGUsIGdldFllc3RlcmRheVRhc2tGaWxlUGF0aCwgZXh0cmFjdFRhc2tzRm9yRGF0ZSwgYW5hbHl6ZVRhc2tDb21wbGV0aW9uLCB5ZXN0ZXJkYXlTdGF0aXN0aWNzRXhpc3RzLCBnZW5lcmF0ZVN0YXRpc3RpY3NDb250ZW50IH0gZnJvbSBcIi4vdXRpbHMvZmlsZVV0aWxzXCI7XG5pbXBvcnQgeyByZW5kZXJUZW1wbGF0ZSB9IGZyb20gXCIuL3V0aWxzL3RlbXBsYXRlRW5naW5lXCI7XG5pbXBvcnQgeyBnZXRDdXJyZW50RGF0ZSB9IGZyb20gXCIuL3V0aWxzL2RhdGVVdGlsc1wiO1xuXG4vKipcbiAqIOS7u+WKoeeUn+aIkOWZqFxuICog6LSf6LSj5Yib5bu65Lu75Yqh5paH5Lu25ZKM5re75Yqg5Lu75Yqh5YaF5a65XG4gKi9cbmV4cG9ydCBjbGFzcyBUYXNrR2VuZXJhdG9yIHtcbiAgICBwcml2YXRlIGFwcDogQXBwO1xuICAgIHByaXZhdGUgdmF1bHQ6IFZhdWx0O1xuICAgIHByaXZhdGUgc2V0dGluZ3NNYW5hZ2VyOiBTZXR0aW5nc01hbmFnZXI7XG5cbiAgICBjb25zdHJ1Y3RvcihhcHA6IEFwcCwgc2V0dGluZ3NNYW5hZ2VyOiBTZXR0aW5nc01hbmFnZXIpIHtcbiAgICAgICAgdGhpcy5hcHAgPSBhcHA7XG4gICAgICAgIHRoaXMudmF1bHQgPSBhcHAudmF1bHQ7XG4gICAgICAgIHRoaXMuc2V0dGluZ3NNYW5hZ2VyID0gc2V0dGluZ3NNYW5hZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIOeUn+aIkOavj+aXpeS7u+WKoVxuICAgICAqIEBwYXJhbSBvcGVuRmlsZSDmmK/lkKbmiZPlvIDmlofku7ZcbiAgICAgKiBAcGFyYW0gcXVpZXRNb2RlIOmdmem7mOaooeW8j++8jOWHj+WwkeaXpeW/l+i+k+WHulxuICAgICAqIEByZXR1cm5zIOaIkOWKn+aIluWksei0pVxuICAgICAqL1xuICAgIGFzeW5jIGdlbmVyYXRlRGFpbHlUYXNrKG9wZW5GaWxlOiBib29sZWFuID0gdHJ1ZSwgcXVpZXRNb2RlOiBib29sZWFuID0gZmFsc2UpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IHNldHRpbmdzID0gdGhpcy5zZXR0aW5nc01hbmFnZXIuZ2V0U2V0dGluZ3MoKTtcbiAgICAgICAgICAgIGNvbnN0IHJvb3REaXIgPSBzZXR0aW5ncy5yb290RGlyLnRyaW0oKSB8fCAnRGFpbHlUYXNrcyc7IC8vIOS9v+eUqOm7mOiupOebruW9lVxuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyDojrflj5bku7vliqHmlofku7bot6/lvoRcbiAgICAgICAgICAgIGNvbnN0IGZpbGVQYXRoID0gZ2V0VGFza0ZpbGVQYXRoKHJvb3REaXIpO1xuICAgICAgICAgICAgaWYgKCFxdWlldE1vZGUpIGNvbnNvbGUubG9nKGDnlJ/miJDku7vliqHmlofku7bot6/lvoQ6ICR7ZmlsZVBhdGh9YCk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIOino+aekOW5tOS7veWSjOaciOS7vVxuICAgICAgICAgICAgY29uc3QgcGF0aFBhcnRzID0gZmlsZVBhdGguc3BsaXQoJy8nKTtcbiAgICAgICAgICAgIGNvbnN0IHllYXIgPSBwYXRoUGFydHMubGVuZ3RoID4gMSA/IHBhdGhQYXJ0c1sxXSA6ICcnO1xuICAgICAgICAgICAgY29uc3QgbW9udGhOYW1lID0gcGF0aFBhcnRzLmxlbmd0aCA+IDIgPyBwYXRoUGFydHNbMl0ucmVwbGFjZSgnLm1kJywgJycpIDogJyc7XG4gICAgICAgICAgICBjb25zdCB5ZWFyRm9sZGVyID0gYCR7cm9vdERpcn0vJHt5ZWFyfWA7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIOehruS/neagueebruW9leWtmOWcqFxuICAgICAgICAgICAgaWYgKCFxdWlldE1vZGUpIGNvbnNvbGUubG9nKGDmraPlnKjnoa7kv53moLnnm67lvZXlrZjlnKg6ICR7cm9vdERpcn1gKTtcbiAgICAgICAgICAgIGNvbnN0IHJvb3RDcmVhdGVkID0gYXdhaXQgZW5zdXJlRm9sZGVyRXhpc3RzKHRoaXMudmF1bHQsIHJvb3REaXIpO1xuICAgICAgICAgICAgaWYgKCFyb290Q3JlYXRlZCkge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoYOaXoOazleiuv+mXruaIluWIm+W7uuagueebruW9lTogJHtyb290RGlyfWApO1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihg5peg5rOV6K6/6Zeu5qC555uu5b2VOiAke3Jvb3REaXJ977yM5Y+v6IO95piv5a2Y5Zyo5ZCM5ZCN5paH5Lu25oiW5p2D6ZmQ6Zeu6aKYYCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoIXF1aWV0TW9kZSkgY29uc29sZS5sb2coYOagueebruW9leehruiupDogJHtyb290RGlyfWApO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyDnoa7kv53lubTku73nm67lvZXlrZjlnKhcbiAgICAgICAgICAgIGlmICghcXVpZXRNb2RlKSBjb25zb2xlLmxvZyhg5q2j5Zyo56Gu5L+d5bm05Lu955uu5b2V5a2Y5ZyoOiAke3llYXJGb2xkZXJ9YCk7XG4gICAgICAgICAgICBjb25zdCB5ZWFyQ3JlYXRlZCA9IGF3YWl0IGVuc3VyZUZvbGRlckV4aXN0cyh0aGlzLnZhdWx0LCB5ZWFyRm9sZGVyKTtcbiAgICAgICAgICAgIGlmICgheWVhckNyZWF0ZWQpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKGDml6Dms5Xorr/pl67miJbliJvlu7rlubTku73nm67lvZU6ICR7eWVhckZvbGRlcn1gKTtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYOaXoOazleiuv+mXruW5tOS7veebruW9lTogJHt5ZWFyRm9sZGVyfe+8jOWPr+iDveaYr+WtmOWcqOWQjOWQjeaWh+S7tuaIluadg+mZkOmXrumimGApO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKCFxdWlldE1vZGUpIGNvbnNvbGUubG9nKGDlubTku73nm67lvZXnoa7orqQ6ICR7eWVhckZvbGRlcn1gKTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgLy8g56Gu5L+d5pyI5Lu95paH5Lu25a2Y5ZyoXG4gICAgICAgICAgICBpZiAoIXF1aWV0TW9kZSkgY29uc29sZS5sb2coYOato+WcqOehruS/neaciOS7veaWh+S7tuWtmOWcqDogJHtmaWxlUGF0aH0gKCR7bW9udGhOYW1lfSlgKTtcbiAgICAgICAgICAgIGNvbnN0IGZpbGVDcmVhdGVkID0gYXdhaXQgZW5zdXJlRmlsZUV4aXN0cyh0aGlzLnZhdWx0LCBmaWxlUGF0aCk7XG4gICAgICAgICAgICBpZiAoIWZpbGVDcmVhdGVkKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihg5peg5rOV6K6/6Zeu5oiW5Yib5bu65pyI5Lu95paH5Lu2OiAke2ZpbGVQYXRofWApO1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihg5peg5rOV6K6/6Zeu5pyI5Lu95paH5Lu2OiAke2ZpbGVQYXRofe+8jOivt+ajgOafpeaYr+WQpuWtmOWcqOWQjOWQjeebruW9leaIluadg+mZkOmXrumimGApO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKCFxdWlldE1vZGUpIGNvbnNvbGUubG9nKGDmnIjku73mlofku7bnoa7orqQ6ICR7ZmlsZVBhdGh9YCk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIOajgOafpeS7iuaXpeS7u+WKoeaYr+WQpuW3suWtmOWcqFxuICAgICAgICAgICAgY29uc3QgZGF0ZSA9IGdldEN1cnJlbnREYXRlKCk7XG4gICAgICAgICAgICBjb25zdCBleGlzdGluZ1Rhc2tDaGVjayA9IGF3YWl0IGZpbGVDb250YWlucyh0aGlzLnZhdWx0LCBmaWxlUGF0aCwgYCMjICR7ZGF0ZX1gKTtcbiAgICAgICAgICAgIGlmIChleGlzdGluZ1Rhc2tDaGVjaykge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGDku4rml6UoJHtkYXRlfSnku7vliqHlt7LlrZjlnKjkuo7mlofku7bkuK3vvIzot7Pov4fliJvlu7pgKTtcbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAvLyDlpoLmnpzpnIDopoHmiZPlvIDmlofku7ZcbiAgICAgICAgICAgICAgICBpZiAob3BlbkZpbGUpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8g5pi+56S65o+Q56S65bm25omT5byA5paH5Lu2XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2hvd1dhcm5pbmdOb3RpY2UoYPCfk4wgJHtnZXRUcmFuc2xhdGlvbignbm90aWZpY2F0aW9uLnRhc2tFeGlzdHMnKX1gKTtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgZmlsZSA9IHRoaXMudmF1bHQuZ2V0QWJzdHJhY3RGaWxlQnlQYXRoKGZpbGVQYXRoKTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGZpbGUgJiYgZmlsZSBpbnN0YW5jZW9mIFRGaWxlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBsZWFmID0gdGhpcy5hcHAud29ya3NwYWNlLmdldExlYWYoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGF3YWl0IGxlYWYub3BlbkZpbGUoZmlsZSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7IC8vIOS7u+WKoeW3suWtmOWcqO+8jOinhuS4uuaIkOWKn1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyDlpoLmnpzlvIDlkK/kuobku7vliqHnu5/orqHlip/og73vvIzlnKjmt7vliqDku4rml6Xku7vliqHliY3mt7vliqDmmKjml6Xnu5/orqFcbiAgICAgICAgICAgIGxldCBzdGF0aXN0aWNzQ29udGVudCA9ICcnO1xuICAgICAgICAgICAgaWYgKHNldHRpbmdzLnRhc2tTdGF0aXN0aWNzKSB7XG4gICAgICAgICAgICAgICAgc3RhdGlzdGljc0NvbnRlbnQgPSBhd2FpdCB0aGlzLmdlbmVyYXRlWWVzdGVyZGF5U3RhdGlzdGljcyhyb290RGlyLCBkYXRlLCBxdWlldE1vZGUpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyDojrflj5bku7vliqHmqKHmnb8gLSDkvb/nlKjmlrDnmoTmqKHmnb/pgLvovpFcbiAgICAgICAgICAgIGxldCB0ZW1wbGF0ZSA9ICcnO1xuICAgICAgICAgICAgaWYgKHRoaXMuc2V0dGluZ3NNYW5hZ2VyLmhhc0N1c3RvbVRlbXBsYXRlKCkpIHtcbiAgICAgICAgICAgICAgICAvLyDkvb/nlKjnlKjmiLfoh6rlrprkuYnmqKHmnb9cbiAgICAgICAgICAgICAgICB0ZW1wbGF0ZSA9IHRoaXMuc2V0dGluZ3NNYW5hZ2VyLmdldFNldHRpbmdzKCkuY3VzdG9tVGVtcGxhdGU7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIC8vIOS9v+eUqOivreiogOebuOWFs+eahOm7mOiupOaooeadv1xuICAgICAgICAgICAgICAgIHRlbXBsYXRlID0gdGhpcy5zZXR0aW5nc01hbmFnZXIuZ2V0VGVtcGxhdGVCeUxhbmd1YWdlKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIOa4suafk+aooeadv1xuICAgICAgICAgICAgY29uc3QgcmVuZGVyZWRDb250ZW50ID0gcmVuZGVyVGVtcGxhdGUodGVtcGxhdGUpO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyDlkIjlubbnu5/orqHkv6Hmga/lkozku4rml6Xku7vliqHlhoXlrrlcbiAgICAgICAgICAgIGNvbnN0IGZ1bGxDb250ZW50ID0gc3RhdGlzdGljc0NvbnRlbnQgXG4gICAgICAgICAgICAgICAgPyBgJHtyZW5kZXJlZENvbnRlbnR9XFxuXFxuJHtzdGF0aXN0aWNzQ29udGVudH1gXG4gICAgICAgICAgICAgICAgOiByZW5kZXJlZENvbnRlbnQ7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIOi/veWKoOWIsOaWh+S7tlxuICAgICAgICAgICAgaWYgKCFxdWlldE1vZGUpIGNvbnNvbGUubG9nKGDmraPlnKjlkJHmlofku7bov73liqDlhoXlrrlgKTtcbiAgICAgICAgICAgIGNvbnN0IHN1Y2Nlc3MgPSBhd2FpdCBhcHBlbmRUb0ZpbGUodGhpcy52YXVsdCwgZmlsZVBhdGgsIGZ1bGxDb250ZW50KTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgaWYgKHN1Y2Nlc3MpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhg4pyFIOS7u+WKoeWGheWuuei/veWKoOaIkOWKnyAke2RhdGV9YCk7XG4gICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgLy8g5aaC5p6c6ZyA6KaB5omT5byA5paH5Lu2XG4gICAgICAgICAgICAgICAgaWYgKG9wZW5GaWxlKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIOaJk+W8gOWIm+W7uueahOaWh+S7tlxuICAgICAgICAgICAgICAgICAgICBjb25zdCBmaWxlID0gdGhpcy52YXVsdC5nZXRBYnN0cmFjdEZpbGVCeVBhdGgoZmlsZVBhdGgpO1xuICAgICAgICAgICAgICAgICAgICBpZiAoZmlsZSAmJiBmaWxlIGluc3RhbmNlb2YgVEZpbGUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGxlYWYgPSB0aGlzLmFwcC53b3Jrc3BhY2UuZ2V0TGVhZigpO1xuICAgICAgICAgICAgICAgICAgICAgICAgYXdhaXQgbGVhZi5vcGVuRmlsZShmaWxlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIFxuICAgICAgICAgICAgICAgICAgICAgICAgLy8g5bu26L+f5LiA5LiL5YaN5pi+56S66YCa55+l77yM56Gu5L+d5paH5Lu25bey57uP5omT5byAXG4gICAgICAgICAgICAgICAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnNob3dTdWNjZXNzTm90aWNlKGDinKggJHtnZXRUcmFuc2xhdGlvbignbm90aWZpY2F0aW9uLnRhc2tBZGRlZCcpfWApO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSwgMzAwKTtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihg5paH5Lu25Yib5bu65oiQ5Yqf5L2G5peg5rOV5omT5byAOiAke2ZpbGVQYXRofWApO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgLy8g6Z2Z6buY5qih5byP77yM5Y+q5Zyo5o6n5Yi25Y+w6K6w5b2VXG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGDinKgg5LuK5pelKCR7ZGF0ZX0p5Lu75Yqh5bey6Z2Z6buY5re75Yqg77yM5peg6ZyA5omT5byA5paH5Lu2YCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYOaXoOazleWQkeaWh+S7tui/veWKoOWGheWuuTogJHtmaWxlUGF0aH1gKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIFxuICAgICAgICAgICAgcmV0dXJuIHN1Y2Nlc3M7XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiRXJyb3IgZ2VuZXJhdGluZyBkYWlseSB0YXNrOlwiLCBlcnJvcik7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIOaYvuekuumUmeivr+mAmuefpVxuICAgICAgICAgICAgY29uc3QgZXJyb3JNc2cgPSBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvcik7XG4gICAgICAgICAgICB0aGlzLnNob3dFcnJvck5vdGljZShgJHtnZXRUcmFuc2xhdGlvbignbm90aWZpY2F0aW9uLmVycm9yJyl9ICR7ZXJyb3JNc2d9YCk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgIH1cbiAgICBcbiAgICAvKipcbiAgICAgKiDnlJ/miJDmmKjml6Xku7vliqHnu5/orqHkv6Hmga9cbiAgICAgKiBAcGFyYW0gcm9vdERpciDmoLnnm67lvZVcbiAgICAgKiBAcGFyYW0gdG9kYXlEYXRlIOS7iuaXpeaXpeacn++8iOeUqOS6juajgOafpeaYr+WQpuW3suWtmOWcqOe7n+iuoe+8iVxuICAgICAqIEBwYXJhbSBxdWlldE1vZGUg6Z2Z6buY5qih5byPXG4gICAgICogQHJldHVybnMg57uf6K6h5L+h5oGv5YaF5a655oiW56m65a2X56ym5LiyXG4gICAgICovXG4gICAgYXN5bmMgZ2VuZXJhdGVZZXN0ZXJkYXlTdGF0aXN0aWNzKHJvb3REaXI6IHN0cmluZywgdG9kYXlEYXRlOiBzdHJpbmcsIHF1aWV0TW9kZTogYm9vbGVhbiA9IGZhbHNlKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIC8vIOiOt+WPluaYqOWkqeeahOaXpeacn+WSjOaWh+S7tui3r+W+hFxuICAgICAgICAgICAgY29uc3QgeWVzdGVyZGF5RGF0ZSA9IGdldFllc3RlcmRheURhdGUoKTtcbiAgICAgICAgICAgIGNvbnN0IHllc3RlcmRheUZpbGVQYXRoID0gZ2V0WWVzdGVyZGF5VGFza0ZpbGVQYXRoKHJvb3REaXIpO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBpZiAoIXF1aWV0TW9kZSkgY29uc29sZS5sb2coYOato+WcqOiOt+WPluaYqOaXpSgke3llc3RlcmRheURhdGV9KeS7u+WKoee7n+iuoe+8jOaWh+S7tui3r+W+hDogJHt5ZXN0ZXJkYXlGaWxlUGF0aH1gKTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgLy8g5qOA5p+l5LuK5pel55qE5YaF5a655Lit5piv5ZCm5bey57uP5YyF5ZCr5pio5pel57uf6K6hXG4gICAgICAgICAgICBjb25zdCB0b2RheUZpbGVQYXRoID0gZ2V0VGFza0ZpbGVQYXRoKHJvb3REaXIpO1xuICAgICAgICAgICAgY29uc3Qgc3RhdHNFeGlzdHMgPSBhd2FpdCB5ZXN0ZXJkYXlTdGF0aXN0aWNzRXhpc3RzKHRoaXMudmF1bHQsIHRvZGF5RmlsZVBhdGgsIHRvZGF5RGF0ZSk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIGlmIChzdGF0c0V4aXN0cykge1xuICAgICAgICAgICAgICAgIGlmICghcXVpZXRNb2RlKSBjb25zb2xlLmxvZygn5pio5pel57uf6K6h5L+h5oGv5bey5a2Y5Zyo77yM6Lez6L+H55Sf5oiQJyk7XG4gICAgICAgICAgICAgICAgcmV0dXJuICcnO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyDmj5Dlj5bmmKjml6Xku7vliqHlhoXlrrlcbiAgICAgICAgICAgIGNvbnN0IHllc3RlcmRheUNvbnRlbnQgPSBhd2FpdCBleHRyYWN0VGFza3NGb3JEYXRlKHRoaXMudmF1bHQsIHllc3RlcmRheUZpbGVQYXRoLCB5ZXN0ZXJkYXlEYXRlKTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgLy8g5aaC5p6c5om+5LiN5Yiw5pio5pel5Lu75Yqh5YaF5a65XG4gICAgICAgICAgICBpZiAoIXllc3RlcmRheUNvbnRlbnQpIHtcbiAgICAgICAgICAgICAgICBpZiAoIXF1aWV0TW9kZSkgY29uc29sZS5sb2coYOaJvuS4jeWIsOaYqOaXpSgke3llc3RlcmRheURhdGV9KeS7u+WKoeWGheWuue+8jOi3s+i/h+e7n+iuoWApO1xuICAgICAgICAgICAgICAgIHJldHVybiAnJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIFxuICAgICAgICAgICAgLy8g5YiG5p6Q5Lu75Yqh5a6M5oiQ5oOF5Ya1XG4gICAgICAgICAgICBjb25zdCB0YXNrU3RhdHMgPSBhbmFseXplVGFza0NvbXBsZXRpb24oeWVzdGVyZGF5Q29udGVudCk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIGlmICghcXVpZXRNb2RlKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coYOaYqOaXpeS7u+WKoee7n+iuoTog5oC75pWwPSR7dGFza1N0YXRzLnRvdGFsVGFza3N9LCDlt7LlrozmiJA9JHt0YXNrU3RhdHMuY29tcGxldGVkVGFza3N9YCk7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coYOacquWujOaIkOS7u+WKoTogJHt0YXNrU3RhdHMudW5maW5pc2hlZFRhc2tzTGlzdC5sZW5ndGh95LiqYCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIOWmguaenOayoeacieS7u+WKoe+8jOS4jeeUn+aIkOe7n+iuoeS/oeaBr1xuICAgICAgICAgICAgaWYgKHRhc2tTdGF0cy50b3RhbFRhc2tzID09PSAwKSB7XG4gICAgICAgICAgICAgICAgaWYgKCFxdWlldE1vZGUpIGNvbnNvbGUubG9nKCfmmKjml6XmsqHmnInku7vliqHvvIzot7Pov4fnu5/orqEnKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gJyc7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIOeUn+aIkOe7n+iuoeS/oeaBr+WGheWuuVxuICAgICAgICAgICAgcmV0dXJuIGdlbmVyYXRlU3RhdGlzdGljc0NvbnRlbnQodGFza1N0YXRzKTtcbiAgICAgICAgICAgIFxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcihg55Sf5oiQ5pio5pel57uf6K6h5L+h5oGv5YaF5a655oiW56m65a2X56ym5Liy77yM5LiN5b2x5ZON5LuK5pel5Lu75Yqh55Sf5oiQXG4gICAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICog5omL5Yqo5re75Yqg5LuK5pel5Lu75YqhXG4gICAgICogQHJldHVybnMg5oiQ5Yqf5oiW5aSx6LSlXG4gICAgICovXG4gICAgYXN5bmMgYWRkVGFza01hbnVhbGx5KCk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3Qgc2V0dGluZ3MgPSB0aGlzLnNldHRpbmdzTWFuYWdlci5nZXRTZXR0aW5ncygpO1xuICAgICAgICAgICAgY29uc3Qgcm9vdERpciA9IHNldHRpbmdzLnJvb3REaXIudHJpbSgpIHx8ICdEYWlseVRhc2tzJzsgLy8g5L2/55So6buY6K6k55uu5b2VXG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIOajgOafpeS7iuaXpeS7u+WKoeaYr+WQpuW3suWtmOWcqFxuICAgICAgICAgICAgY29uc3QgZXhpc3RzID0gYXdhaXQgdG9kYXlUYXNrRXhpc3RzKHRoaXMudmF1bHQsIHJvb3REaXIpO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBpZiAoZXhpc3RzKSB7XG4gICAgICAgICAgICAgICAgLy8g5Lu75Yqh5bey5a2Y5Zyo77yM5pi+56S65o+Q56S6XG4gICAgICAgICAgICAgICAgdGhpcy5zaG93V2FybmluZ05vdGljZShg8J+TjCAke2dldFRyYW5zbGF0aW9uKCdub3RpZmljYXRpb24udGFza0V4aXN0cycpfWApO1xuICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIFxuICAgICAgICAgICAgLy8g55Sf5oiQ5Lu75YqhXG4gICAgICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy5nZW5lcmF0ZURhaWx5VGFzaygpO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIkVycm9yIGFkZGluZyB0YXNrIG1hbnVhbGx5OlwiLCBlcnJvcik7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8vIOaYvuekuumUmeivr+mAmuefpVxuICAgICAgICAgICAgY29uc3QgZXJyb3JNc2cgPSBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvcik7XG4gICAgICAgICAgICB0aGlzLnNob3dFcnJvck5vdGljZShgJHtnZXRUcmFuc2xhdGlvbignbm90aWZpY2F0aW9uLmVycm9yJyl9ICR7ZXJyb3JNc2d9YCk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgIH1cbiAgICBcbiAgICAvKipcbiAgICAgKiDmmL7npLrmiJDlip/pgJrnn6VcbiAgICAgKiBAcGFyYW0gbWVzc2FnZSDpgJrnn6XlhoXlrrlcbiAgICAgKi9cbiAgICBwcml2YXRlIHNob3dTdWNjZXNzTm90aWNlKG1lc3NhZ2U6IHN0cmluZyk6IHZvaWQge1xuICAgICAgICBjb25zdCBub3RpY2UgPSBuZXcgTm90aWNlKFxuICAgICAgICAgICAgbWVzc2FnZSwgXG4gICAgICAgICAgICB0aGlzLnNldHRpbmdzTWFuYWdlci5nZXRTZXR0aW5ncygpLnN1Y2Nlc3NOb3RpZmljYXRpb25EdXJhdGlvblxuICAgICAgICApO1xuICAgICAgICBcbiAgICAgICAgLy8g5re75Yqg5oiQ5Yqf5qC35byPXG4gICAgICAgIGlmIChub3RpY2Uubm90aWNlRWwpIHtcbiAgICAgICAgICAgIG5vdGljZS5ub3RpY2VFbC5hZGRDbGFzcygnZGFpbHktdGFzay1zdWNjZXNzLW5vdGljZScpO1xuICAgICAgICB9XG4gICAgfVxuICAgIFxuICAgIC8qKlxuICAgICAqIOaYvuekuuitpuWRiumAmuefpVxuICAgICAqIEBwYXJhbSBtZXNzYWdlIOmAmuefpeWGheWuuVxuICAgICAqL1xuICAgIHByaXZhdGUgc2hvd1dhcm5pbmdOb3RpY2UobWVzc2FnZTogc3RyaW5nKTogdm9pZCB7XG4gICAgICAgIGNvbnN0IG5vdGljZSA9IG5ldyBOb3RpY2UoXG4gICAgICAgICAgICBtZXNzYWdlLCBcbiAgICAgICAgICAgIHRoaXMuc2V0dGluZ3NNYW5hZ2VyLmdldFNldHRpbmdzKCkuc3VjY2Vzc05vdGlmaWNhdGlvbkR1cmF0aW9uXG4gICAgICAgICk7XG4gICAgICAgIFxuICAgICAgICAvLyDmt7vliqDplJnor6/moLflvI9cbiAgICAgICAgaWYgKG5vdGljZS5ub3RpY2VFbCkge1xuICAgICAgICAgICAgbm90aWNlLm5vdGljZUVsLmFkZENsYXNzKCdkYWlseS10YXNrLXdhcm5pbmctbm90aWNlJyk7XG4gICAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgLyoqXG4gICAgICog5pi+56S66ZSZ6K+v6YCa55+lXG4gICAgICogQHBhcmFtIG1lc3NhZ2Ug6YCa55+l5YaF5a65XG4gICAgICovXG4gICAgcHJpdmF0ZSBzaG93RXJyb3JOb3RpY2UobWVzc2FnZTogc3RyaW5nKTogdm9pZCB7XG4gICAgICAgIGNvbnN0IG5vdGljZSA9IG5ldyBOb3RpY2UoXG4gICAgICAgICAgICBtZXNzYWdlLCBcbiAgICAgICAgICAgIHRoaXMuc2V0dGluZ3NNYW5hZ2VyLmdldFNldHRpbmdzKCkuc3VjY2Vzc05vdGlmaWNhdGlvbkR1cmF0aW9uXG4gICAgICAgICk7XG4gICAgICAgIFxuICAgICAgICAvLyDmt7vliqDplJnor6/moLflvI9cbiAgICAgICAgaWYgKG5vdGljZS5ub3RpY2VFbCkge1xuICAgICAgICAgICAgbm90aWNlLm5vdGljZUVsLmFkZENsYXNzKCdkYWlseS10YXNrLWVycm9yLW5vdGljZScpO1xuICAgICAgICB9XG4gICAgfVxufSJdfQ== \ No newline at end of file diff --git a/src/taskGenerator.ts b/src/taskGenerator.ts new file mode 100644 index 0000000..8f98e50 --- /dev/null +++ b/src/taskGenerator.ts @@ -0,0 +1,295 @@ +import { App, Notice, TFile, Vault } from "obsidian"; +import { getTranslation } from "./i18n/i18n"; +import { SettingsManager } from "./settings/settings"; +import { appendToFile, ensureFileExists, ensureFolderExists, getTaskFilePath, todayTaskExists, fileContains, getYesterdayDate, getYesterdayTaskFilePath, extractTasksForDate, analyzeTaskCompletion, yesterdayStatisticsExists, generateStatisticsContent } from "./utils/fileUtils"; +import { renderTemplate } from "./utils/templateEngine"; +import { getCurrentDate } from "./utils/dateUtils"; + +/** + * 任务生成器 + * 负责创建任务文件和添加任务内容 + */ +export class TaskGenerator { + private app: App; + private vault: Vault; + private settingsManager: SettingsManager; + + constructor(app: App, settingsManager: SettingsManager) { + this.app = app; + this.vault = app.vault; + this.settingsManager = settingsManager; + } + + /** + * 生成每日任务 + * @param openFile 是否打开文件 + * @param quietMode 静默模式,减少日志输出 + * @returns 成功或失败 + */ + async generateDailyTask(openFile: boolean = true, quietMode: boolean = false): Promise { + try { + const settings = this.settingsManager.getSettings(); + const rootDir = settings.rootDir.trim() || 'DailyTasks'; // 使用默认目录 + + // 获取任务文件路径 + const filePath = getTaskFilePath(rootDir); + if (!quietMode) console.log(`生成任务文件路径: ${filePath}`); + + // 解析年份和月份 + const pathParts = filePath.split('/'); + const year = pathParts.length > 1 ? pathParts[1] : ''; + const monthName = pathParts.length > 2 ? pathParts[2].replace('.md', '') : ''; + const yearFolder = `${rootDir}/${year}`; + + // 确保根目录存在 + if (!quietMode) console.log(`正在确保根目录存在: ${rootDir}`); + const rootCreated = await ensureFolderExists(this.vault, rootDir); + if (!rootCreated) { + console.error(`无法访问或创建根目录: ${rootDir}`); + throw new Error(`无法访问根目录: ${rootDir},可能是存在同名文件或权限问题`); + } + if (!quietMode) console.log(`根目录确认: ${rootDir}`); + + // 确保年份目录存在 + if (!quietMode) console.log(`正在确保年份目录存在: ${yearFolder}`); + const yearCreated = await ensureFolderExists(this.vault, yearFolder); + if (!yearCreated) { + console.error(`无法访问或创建年份目录: ${yearFolder}`); + throw new Error(`无法访问年份目录: ${yearFolder},可能是存在同名文件或权限问题`); + } + if (!quietMode) console.log(`年份目录确认: ${yearFolder}`); + + // 确保月份文件存在 + if (!quietMode) console.log(`正在确保月份文件存在: ${filePath} (${monthName})`); + const fileCreated = await ensureFileExists(this.vault, filePath); + if (!fileCreated) { + console.error(`无法访问或创建月份文件: ${filePath}`); + throw new Error(`无法访问月份文件: ${filePath},请检查是否存在同名目录或权限问题`); + } + if (!quietMode) console.log(`月份文件确认: ${filePath}`); + + // 检查今日任务是否已存在 + const date = getCurrentDate(); + // 更改检查方式:不仅检查纯日期,也检查带图标的日期格式 + const dateRegex = new RegExp(`## [^\\n]*${date}[^\\n]*\\n`); + const fileContent = await this.vault.read(this.vault.getAbstractFileByPath(filePath) as TFile); + const existingTaskCheck = dateRegex.test(fileContent); + + if (existingTaskCheck) { + console.log(`今日(${date})任务已存在于文件中,跳过创建`); + + // 如果需要打开文件 + if (openFile) { + // 显示提示并打开文件 + this.showWarningNotice(`📌 ${getTranslation('notification.taskExists')}`); + const file = this.vault.getAbstractFileByPath(filePath); + if (file && file instanceof TFile) { + const leaf = this.app.workspace.getLeaf(); + await leaf.openFile(file); + } + } + + return true; // 任务已存在,视为成功 + } + + // 如果开启了任务统计功能,在添加今日任务前添加昨日统计 + let statisticsContent = ''; + if (settings.taskStatistics) { + statisticsContent = await this.generateYesterdayStatistics(rootDir, date, quietMode); + } + + // 获取任务模板 - 使用新的模板逻辑 + let template = ''; + if (this.settingsManager.hasCustomTemplate()) { + // 使用用户自定义模板 + template = this.settingsManager.getSettings().customTemplate; + } else { + // 使用语言相关的默认模板 + template = this.settingsManager.getTemplateByLanguage(); + } + + // 渲染模板 + const renderedContent = renderTemplate(template); + + // 合并统计信息和今日任务内容 + const fullContent = statisticsContent + ? `${renderedContent}\n\n${statisticsContent}` + : renderedContent; + + // 追加到文件 + if (!quietMode) console.log(`正在向文件追加内容`); + const success = await appendToFile(this.vault, filePath, fullContent); + + if (success) { + console.log(`✅ 任务内容追加成功 ${date}`); + + // 如果需要打开文件 + if (openFile) { + // 打开创建的文件 + const file = this.vault.getAbstractFileByPath(filePath); + if (file && file instanceof TFile) { + const leaf = this.app.workspace.getLeaf(); + await leaf.openFile(file); + + // 延迟一下再显示通知,确保文件已经打开 + setTimeout(() => { + this.showSuccessNotice(`✨ ${getTranslation('notification.taskAdded')}`); + }, 300); + } else { + throw new Error(`文件创建成功但无法打开: ${filePath}`); + } + } else { + // 静默模式,只在控制台记录 + console.log(`✨ 今日(${date})任务已静默添加,无需打开文件`); + } + } else { + throw new Error(`无法向文件追加内容: ${filePath}`); + } + + return success; + } catch (error) { + console.error("Error generating daily task:", error); + + // 显示错误通知 + const errorMsg = error instanceof Error ? error.message : String(error); + this.showErrorNotice(`${getTranslation('notification.error')} ${errorMsg}`); + + return false; + } + } + + /** + * 生成昨日任务统计信息 + * @param rootDir 根目录 + * @param todayDate 今日日期(用于检查是否已存在统计) + * @param quietMode 静默模式 + * @returns 统计信息内容或空字符串 + */ + async generateYesterdayStatistics(rootDir: string, todayDate: string, quietMode: boolean = false): Promise { + try { + // 获取昨天的日期和文件路径 + const yesterdayDate = getYesterdayDate(); + const yesterdayFilePath = getYesterdayTaskFilePath(rootDir); + + if (!quietMode) console.log(`正在获取昨日(${yesterdayDate})任务统计,文件路径: ${yesterdayFilePath}`); + + // 检查今日的内容中是否已经包含昨日统计 + const todayFilePath = getTaskFilePath(rootDir); + const statsExists = await yesterdayStatisticsExists(this.vault, todayFilePath, todayDate); + + if (statsExists) { + if (!quietMode) console.log('昨日统计信息已存在,跳过生成'); + return ''; + } + + // 提取昨日任务内容 + const yesterdayContent = await extractTasksForDate(this.vault, yesterdayFilePath, yesterdayDate); + + // 如果找不到昨日任务内容 + if (!yesterdayContent) { + if (!quietMode) console.log(`找不到昨日(${yesterdayDate})任务内容,跳过统计`); + return ''; + } + + // 分析任务完成情况 + const taskStats = analyzeTaskCompletion(yesterdayContent); + + if (!quietMode) { + console.log(`昨日任务统计: 总数=${taskStats.totalTasks}, 已完成=${taskStats.completedTasks}`); + console.log(`未完成任务: ${taskStats.unfinishedTasksList.length}个`); + } + + // 如果没有任务,不生成统计信息 + if (taskStats.totalTasks === 0) { + if (!quietMode) console.log('昨日没有任务,跳过统计'); + return ''; + } + + // 确保在所有语言环境下都调用generateStatisticsContent + return generateStatisticsContent(taskStats, getTranslation); + + } catch (error) { + console.error(`生成昨日统计信息时出错:`, error); + return ''; // 出错时返回空字符串,不影响今日任务生成 + } + } + + /** + * 手动添加今日任务 + * @returns 成功或失败 + */ + async addTaskManually(): Promise { + try { + const settings = this.settingsManager.getSettings(); + const rootDir = settings.rootDir.trim() || 'DailyTasks'; // 使用默认目录 + + // 检查今日任务是否已存在 + const exists = await todayTaskExists(this.vault, rootDir); + + if (exists) { + // 任务已存在,显示提示 + this.showWarningNotice(`📌 ${getTranslation('notification.taskExists')}`); + return false; + } + + // 生成任务 + return await this.generateDailyTask(); + } catch (error) { + console.error("Error adding task manually:", error); + + // 显示错误通知 + const errorMsg = error instanceof Error ? error.message : String(error); + this.showErrorNotice(`${getTranslation('notification.error')} ${errorMsg}`); + + return false; + } + } + + /** + * 显示成功通知 + * @param message 通知内容 + */ + private showSuccessNotice(message: string): void { + const notice = new Notice( + message, + this.settingsManager.getSettings().successNotificationDuration + ); + + // 添加成功样式 + if (notice.noticeEl) { + notice.noticeEl.addClass('daily-task-success-notice'); + } + } + + /** + * 显示警告通知 + * @param message 通知内容 + */ + private showWarningNotice(message: string): void { + const notice = new Notice( + message, + this.settingsManager.getSettings().successNotificationDuration + ); + + // 添加警告样式 + if (notice.noticeEl) { + notice.noticeEl.addClass('daily-task-warning-notice'); + } + } + + /** + * 显示错误通知 + * @param message 通知内容 + */ + private showErrorNotice(message: string): void { + const notice = new Notice( + message, + this.settingsManager.getSettings().successNotificationDuration + ); + + // 添加错误样式 + if (notice.noticeEl) { + notice.noticeEl.addClass('daily-task-error-notice'); + } + } +} \ No newline at end of file diff --git a/src/utils/dateUtils.js b/src/utils/dateUtils.js new file mode 100644 index 0000000..0480f8e --- /dev/null +++ b/src/utils/dateUtils.js @@ -0,0 +1,156 @@ +import { getLocalizedWeekday, getTranslation } from "../i18n/i18n"; +/** + * 日期处理工具函数 + */ +/** + * 获取当前年份 + * @returns 当前年份,如2025 + */ +export function getCurrentYear() { + return new Date().getFullYear().toString(); +} +/** + * 获取当前月份数字 + * @returns 当前月份,如04 + */ +export function getCurrentMonth() { + const month = (new Date().getMonth() + 1).toString(); + return month.padStart(2, '0'); +} +/** + * 获取当前月份的本地化名称 + * @param isEnglish 是否使用英文 + * @returns 月份名称,如中文环境下的"4月",英文环境下的"April" + */ +export function getLocalizedMonthName(isEnglish = false) { + const monthIndex = new Date().getMonth(); // 0-11 + // 中文月份名称 + const chineseMonths = [ + "1月", "2月", "3月", "4月", "5月", "6月", + "7月", "8月", "9月", "10月", "11月", "12月" + ]; + // 英文月份名称 + const englishMonths = [ + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" + ]; + return isEnglish ? englishMonths[monthIndex] : chineseMonths[monthIndex]; +} +/** + * 获取当天对应的图标 + * @returns 对应日期的图标 + */ +export function getDayIcon() { + const day = new Date().getDate(); // 1-31 + // 为每天分配一个独特的图标 + const dayIcons = [ + "🌑", + "🌒", + "🌓", + "🌔", + "🌕", + "🌖", + "🌗", + "🌘", + "🌟", + "⭐", + "🌈", + "🌞", + "🌤️", + "⛅", + "🌦️", + "🌧️", + "⛈️", + "🌩️", + "🌪️", + "🌫️", + "🌬️", + "🍀", + "🌱", + "🌲", + "🌳", + "🌴", + "🌵", + "🌺", + "🌻", + "🌼", + "🌸", // 31日 + ]; + // 索引从0开始,天数从1开始,所以减1 + return dayIcons[day - 1] || "📅"; // 如果出现意外,返回默认日历图标 +} +/** + * 判断当前是否为英文环境 + * @returns 是否为英文环境 + */ +export function isEnglishEnvironment() { + // 通过翻译系统中的周一测试当前语言 + // 获取"weekday.mon"的翻译,如果是"Monday"则为英文环境 + const mondayText = getTranslation("weekday.mon"); + return mondayText === "Monday"; +} +/** + * 获取当前日期 + * @returns 当前日期,如16 + */ +export function getCurrentDay() { + const day = new Date().getDate().toString(); + return day.padStart(2, '0'); +} +/** + * 获取当前完整日期 + * @returns 完整日期,如2025-04-16 + */ +export function getCurrentDate() { + return `${getCurrentYear()}-${getCurrentMonth()}-${getCurrentDay()}`; +} +/** + * 获取当前带图标的完整日期 + * @returns 带图标的完整日期,如🌕 2025-04-16 + */ +export function getCurrentDateWithIcon() { + const icon = getDayIcon(); + return `${icon} ${getCurrentDate()}`; +} +/** + * 判断当前是否为工作日(周一至周五) + * @returns 是否为工作日 + */ +export function isWorkday() { + const day = new Date().getDay(); + // 0是周日,1-5是周一至周五,6是周六 + return day >= 1 && day <= 5; +} +/** + * 获取当前星期几的本地化名称 + * @returns 本地化的星期名称 + */ +export function getCurrentWeekdayName() { + const day = new Date().getDay(); + return getLocalizedWeekday(day); +} +/** + * 计算当年进度百分比 + * @returns 当年进度百分比 + */ +export function getYearProgress() { + const now = new Date(); + const start = new Date(now.getFullYear(), 0, 1); // 当年1月1日 + const end = new Date(now.getFullYear() + 1, 0, 1); // 下一年1月1日 + const totalDays = (end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24); + const passedDays = (now.getTime() - start.getTime()) / (1000 * 60 * 60 * 24); + return Math.round((passedDays / totalDays) * 100); +} +/** + * 计算当月进度百分比 + * @returns 当月进度百分比 + */ +export function getMonthProgress() { + const now = new Date(); + const start = new Date(now.getFullYear(), now.getMonth(), 1); // 当月1日 + const end = new Date(now.getFullYear(), now.getMonth() + 1, 1); // 下个月1日 + const totalDays = (end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24); + const passedDays = (now.getTime() - start.getTime()) / (1000 * 60 * 60 * 24); + return Math.round((passedDays / totalDays) * 100); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0ZVV0aWxzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZGF0ZVV0aWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxjQUFjLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFFbkU7O0dBRUc7QUFFSDs7O0dBR0c7QUFDSCxNQUFNLFVBQVUsY0FBYztJQUMxQixPQUFPLElBQUksSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUMsUUFBUSxFQUFFLENBQUM7QUFDL0MsQ0FBQztBQUVEOzs7R0FHRztBQUNILE1BQU0sVUFBVSxlQUFlO0lBQzNCLE1BQU0sS0FBSyxHQUFHLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQyxRQUFRLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQyxRQUFRLEVBQUUsQ0FBQztJQUNyRCxPQUFPLEtBQUssQ0FBQyxRQUFRLENBQUMsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0FBQ2xDLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLHFCQUFxQixDQUFDLFlBQXFCLEtBQUs7SUFDNUQsTUFBTSxVQUFVLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDLE9BQU87SUFFakQsU0FBUztJQUNULE1BQU0sYUFBYSxHQUFHO1FBQ2xCLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSTtRQUNsQyxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEtBQUs7S0FDeEMsQ0FBQztJQUVGLFNBQVM7SUFDVCxNQUFNLGFBQWEsR0FBRztRQUNsQixTQUFTLEVBQUUsVUFBVSxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU07UUFDdEQsTUFBTSxFQUFFLFFBQVEsRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFFLFVBQVUsRUFBRSxVQUFVO0tBQ25FLENB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QUM7SUFFN0UsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsVUFBVSxHQUFHLFNBQVMsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFDO0FBQ3RELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBnZXRMb2NhbGl6ZWRXZWVrZGF5LCBnZXRUcmFuc2xhdGlvbiB9IGZyb20gXCIuLi9pMThuL2kxOG5cIjtcclxuXHJcbi8qKlxyXG4gKiDml6XmnJ/lpITnkIblt6Xlhbflh73mlbBcclxuICovXHJcblxyXG4vKipcclxuICog6I635Y+W5b2T5YmN5bm05Lu9XHJcbiAqIEByZXR1cm5zIOW9k+WJjeW5tOS7ve+8jOWmgjIwMjVcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRDdXJyZW50WWVhcigpOiBzdHJpbmcge1xyXG4gICAgcmV0dXJuIG5ldyBEYXRlKCkuZ2V0RnVsbFllYXIoKS50b1N0cmluZygpO1xyXG59XHJcblxyXG4vKipcclxuICog6I635Y+W5b2T5YmN5pyI5Lu95pWw5a2XXHJcbiAqIEByZXR1cm5zIOW9k+WJjeaciOS7ve+8jOWmgjA0XHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q3VycmVudE1vbnRoKCk6IHN0cmluZyB7XHJcbiAgICBjb25zdCBtb250aCA9IChuZXcgRGF0ZSgpLmdldE1vbnRoKCkgKyAxKS50b1N0cmluZygpO1xyXG4gICAgcmV0dXJuIG1vbnRoLnBhZFN0YXJ0KDIsICcwJyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiDojrflj5blvZPliY3mnIjku73nmoTmnKzlnLDljJblkI3np7BcclxuICogQHBhcmFtIGlzRW5nbGlzaCDmmK/lkKbkvb/nlKjoi7HmlodcclxuICogQHJldHVybnMg5pyI5Lu95ZCN56ew77yM5aaC5Lit5paH546v5aKD5LiL55qEXCI05pyIXCLvvIzoi7Hmlofnjq/looPkuIvnmoRcIkFwcmlsXCJcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRMb2NhbGl6ZWRNb250aE5hbWUoaXNFbmdsaXNoOiBib29sZWFuID0gZmFsc2UpOiBzdHJpbmcge1xyXG4gICAgY29uc3QgbW9udGhJbmRleCA9IG5ldyBEYXRlKCkuZ2V0TW9udGgoKTsgLy8gMC0xMVxyXG4gICAgXHJcbiAgICAvLyDkuK3mlofmnIjku73lkI3np7BcclxuICAgIGNvbnN0IGNoaW5lc2VNb250aHMgPSBbXHJcbiAgICAgICAgXCIx5pyIXCIsIFwiMuaciFwiLCBcIjPmnIhcIiwgXCI05pyIXCIsIFwiNeaciFwiLCBcIjbmnIhcIixcclxuICAgICAgICBcIjfmnIhcIiwgXCI45pyIXCIsIFwiOeaciFwiLCBcIjEw5pyIXCIsIFwiMTHmnIhcIiwgXCIxMuaciFwiXHJcbiAgICBdO1xyXG4gICAgXHJcbiAgICAvLyDoi7HmlofmnIjku73lkI3np7BcclxuICAgIGNvbnN0IGVuZ2xpc2hNb250aHMgPSBbXHJcbiAgICAgICAgXCJKYW51YXJ5XCIsIFwiRmVicnVhcnlcIiwgXCJNYXJjaFwiLCBcIkFwcmlsXCIsIFwiTWF5XCIsIFwiSnVuZVwiLFxyXG4gICAgICAgIFwiSnVseVwiLCBcIkF1Z3VzdFwiLCBcIlNlcHRlbWJlclwiLCBcIk9jdG9iZXJcIiwgXCJOb3ZlbWJlclwiLCBcIkRlY2VtYmVyXCJcclxuICAgIF07XHJcbiAgICBcclxuICAgIHJldHVybiBpc0VuZ2xpc2ggPyBlbmdsaXNoTW9udGhzW21vbnRoSW5kZXhdIDogY2hpbmVzZU1vbnRoc1ttb250aEluZGV4XTtcclxufVxyXG5cclxuLyoqXHJcbiAqIOiOt+WPluW9k+WkqeWvueW6lOeahOWbvuagh1xyXG4gKiBAcmV0dXJucyDlr7nlupTml6XmnJ/nmoTlm77moIdcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXREYXlJY29uKCk6IHN0cmluZyB7XHJcbiAgICBjb25zdCBkYXkgPSBuZXcgRGF0ZSgpLmdldERhdGUoKTsgLy8gMS0zMVxyXG4gICAgXHJcbiAgICAvLyDkuLrmr4/lpKnliIbphY3kuIDkuKrni6znibnnmoTlm77moIdcclxuICAgIGNvbnN0IGRheUljb25zID0gW1xyXG4gICAgICAgIFwi8J+MkVwiLCAvLyAx5pelXHJcbiAgICAgICAgXCLwn4ySXCIsIC8vIDLml6VcclxuICAgICAgICBcIvCfjJNcIiwgLy8gM+aXpVxyXG4gICAgICAgIFwi8J+MlFwiLCAvLyA05pelXHJcbiAgICAgICAgXCLwn4yVXCIsIC8vIDXml6VcclxuICAgICAgICBcIvCfjJZcIiwgLy8gNuaXpVxyXG4gICAgICAgIFwi8J+Ml1wiLCAvLyA35pelXHJcbiAgICAgICAgXCLwn4yYXCIsIC8vIDjml6VcclxuICAgICAgICBcIvCfjJ9cIiwgLy8gOeaXpVxyXG4gICAgICAgIFwi4q2QXCIsIC8vIDEw5pelXHJcbiAgICAgICAgXCLwn4yIXCIsIC8vIDEx5pelXHJcbiAgICAgICAgXCLwn4yeXCIsIC8vIDEy5pelXHJcbiAgICAgICAgXCLwn4yk77iPXCIsIC8vIDEz5pelXHJcbiAgICAgICAgXCLim4VcIiwgLy8gMTTml6VcclxuICAgICAgICBcIvCfjKbvuI9cIiwgLy8gMTXml6VcclxuICAgICAgICBcIvCfjKfvuI9cIiwgLy8gMTbml6VcclxuICAgICAgICBcIuKbiO+4j1wiLCAvLyAxN+aXpVxyXG4gICAgICAgIFwi8J+Mqe+4j1wiLCAvLyAxOOaXpVxyXG4gICAgICAgIFwi8J+Mqu+4j1wiLCAvLyAxOeaXpVxyXG4gICAgICAgIFwi8J+Mq++4j1wiLCAvLyAyMOaXpVxyXG4gICAgICAgIFwi8J+MrO+4j1wiLCAvLyAyMeaXpVxyXG4gICAgICAgIFwi8J+NgFwiLCAvLyAyMuaXpVxyXG4gICAgICAgIFwi8J+MsVwiLCAvLyAyM+aXpVxyXG4gICAgICAgIFwi8J+MslwiLCAvLyAyNOaXpVxyXG4gICAgICAgIFwi8J+Ms1wiLCAvLyAyNeaXpVxyXG4gICAgICAgIFwi8J+MtFwiLCAvLyAyNuaXpVxyXG4gICAgICAgIFwi8J+MtVwiLCAvLyAyN+aXpVxyXG4gICAgICAgIFwi8J+MulwiLCAvLyAyOOaXpVxyXG4gICAgICAgIFwi8J+Mu1wiLCAvLyAyOeaXpVxyXG4gICAgICAgIFwi8J+MvFwiLCAvLyAzMOaXpVxyXG4gICAgICAgIFwi8J+MuFwiLCAvLyAzMeaXpVxyXG4gICAgXTtcclxuICAgIFxyXG4gICAgLy8g57Si5byV5LuOMOW8gOWni++8jOWkqeaVsOS7jjHlvIDlp4vvvIzmiYDku6Xlh48xXHJcbiAgICByZXR1cm4gZGF5SWNvbnNbZGF5IC0gMV0gfHwgXCLwn5OFXCI7IC8vIOWmguaenOWHuueOsOaEj+Wklu+8jOi/lOWbnum7mOiupOaXpeWOhuWbvuagh1xyXG59XHJcblxyXG4vKipcclxuICog5Yik5pat5b2T5YmN5piv5ZCm5Li66Iux5paH546v5aKDXHJcbiAqIEByZXR1cm5zIOaYr+WQpuS4uuiLseaWh+eOr+Wig1xyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGlzRW5nbGlzaEVudmlyb25tZW50KCk6IGJvb2xlYW4ge1xyXG4gICAgLy8g6YCa6L+H57+76K+R57O757uf5Lit55qE5ZGo5LiA5rWL6K+V5b2T5YmN6K+t6KiAXHJcbiAgICAvLyDojrflj5ZcIndlZWtkYXkubW9uXCLnmoTnv7vor5HvvIzlpoLmnpzmmK9cIk1vbmRheVwi5YiZ5Li66Iux5paH546v5aKDXHJcbiAgICBjb25zdCBtb25kYXlUZXh0ID0gZ2V0VHJhbnNsYXRpb24oXCJ3ZWVrZGF5Lm1vblwiKTtcclxuICAgIHJldHVybiBtb25kYXlUZXh0ID09PSBcIk1vbmRheVwiO1xyXG59XHJcblxyXG4vKipcclxuICog6I635Y+W5b2T5YmN5pel5pyfXHJcbiAqIEByZXR1cm5zIOW9k+WJjeaXpeacn++8jOWmgjE2XHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q3VycmVudERheSgpOiBzdHJpbmcge1xyXG4gICAgY29uc3QgZGF5ID0gbmV3IERhdGUoKS5nZXREYXRlKCkudG9TdHJpbmcoKTtcclxuICAgIHJldHVybiBkYXkucGFkU3RhcnQoMiwgJzAnKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIOiOt+WPluW9k+WJjeWujOaVtOaXpeacn1xyXG4gKiBAcmV0dXJucyDlrozmlbTml6XmnJ/vvIzlpoIyMDI1LTA0LTE2XHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q3VycmVudERhdGUoKTogc3RyaW5nIHtcclxuICAgIHJldHVybiBgJHtnZXRDdXJyZW50WWVhcigpfS0ke2dldEN1cnJlbnRNb250aCgpfS0ke2dldEN1cnJlbnREYXkoKX1gO1xyXG59XHJcblxyXG4vKipcclxuICog6I635Y+W5b2T5YmN5bim5Zu+5qCH55qE5a6M5pW05pel5pyfXHJcbiAqIEByZXR1cm5zIOW4puWbvuagh+eahOWujOaVtOaXpeacn++8jOWmgvCfjJUgMjAyNS0wNC0xNlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEN1cnJlbnREYXRlV2l0aEljb24oKTogc3RyaW5nIHtcclxuICAgIGNvbnN0IGljb24gPSBnZXREYXlJY29uKCk7XHJcbiAgICByZXR1cm4gYCR7aWNvbn0gJHtnZXRDdXJyZW50RGF0ZSgpfWA7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiDliKTmlq3lvZPliY3mmK/lkKbkuLrlt6XkvZzml6XvvIjlkajkuIDoh7PlkajkupTvvIlcclxuICogQHJldHVybnMg5piv5ZCm5Li65bel5L2c5pelXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNXb3JrZGF5KCk6IGJvb2xlYW4ge1xyXG4gICAgY29uc3QgZGF5ID0gbmV3IERhdGUoKS5nZXREYXkoKTtcclxuICAgIC8vIDDmmK/lkajml6XvvIwxLTXmmK/lkajkuIDoh7PlkajkupTvvIw25piv5ZGo5YWtXHJcbiAgICByZXR1cm4gZGF5ID49IDEgJiYgZGF5IDw9IDU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiDojrflj5blvZPliY3mmJ/mnJ/lh6DnmoTmnKzlnLDljJblkI3np7BcclxuICogQHJldHVybnMg5pys5Zyw5YyW55qE5pif5pyf5ZCN56ewXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q3VycmVudFdlZWtkYXlOYW1lKCk6IHN0cmluZyB7XHJcbiAgICBjb25zdCBkYXkgPSBuZXcgRGF0ZSgpLmdldERheSgpO1xyXG4gICAgcmV0dXJuIGdldExvY2FsaXplZFdlZWtkYXkoZGF5KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIOiuoeeul+W9k+W5tOi/m+W6pueZvuWIhuavlFxyXG4gKiBAcmV0dXJucyDlvZPlubTov5vluqbnmb7liIbmr5RcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRZZWFyUHJvZ3Jlc3MoKTogbnVtYmVyIHtcclxuICAgIGNvbnN0IG5vdyA9IG5ldyBEYXRlKCk7XHJcbiAgICBjb25zdCBzdGFydCA9IG5ldyBEYXRlKG5vdy5nZXRGdWxsWWVhcigpLCAwLCAxKTsgLy8g5b2T5bm0MeaciDHml6VcclxuICAgIGNvbnN0IGVuZCA9IG5ldyBEYXRlKG5vdy5nZXRGdWxsWWVhcigpICsgMSwgMCwgMSk7IC8vIOS4i+S4gOW5tDHmnIgx5pelXHJcbiAgICBcclxuICAgIGNvbnN0IHRvdGFsRGF5cyA9IChlbmQuZ2V0VGltZSgpIC0gc3RhcnQuZ2V0VGltZSgpKSAvICgxMDAwICogNjAgKiA2MCAqIDI0KTtcclxuICAgIGNvbnN0IHBhc3NlZERheXMgPSAobm93LmdldFRpbWUoKSAtIHN0YXJ0LmdldFRpbWUoKSkgLyAoMTAwMCAqIDYwICogNjAgKiAyNCk7XHJcbiAgICBcclxuICAgIHJldHVybiBNYXRoLnJvdW5kKChwYXNzZWREYXlzIC8gdG90YWxEYXlzKSAqIDEwMCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiDorqHnrpflvZPmnIjov5vluqbnmb7liIbmr5RcclxuICogQHJldHVybnMg5b2T5pyI6L+b5bqm55m+5YiG5q+UXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0TW9udGhQcm9ncmVzcygpOiBudW1iZXIge1xyXG4gICAgY29uc3Qgbm93ID0gbmV3IERhdGUoKTtcclxuICAgIGNvbnN0IHN0YXJ0ID0gbmV3IERhdGUobm93LmdldEZ1bGxZZWFyKCksIG5vdy5nZXRNb250aCgpLCAxKTsgLy8g5b2T5pyIMeaXpVxyXG4gICAgY29uc3QgZW5kID0gbmV3IERhdGUobm93LmdldEZ1bGxZZWFyKCksIG5vdy5nZXRNb250aCgpICsgMSwgMSk7IC8vIOS4i+S4quaciDHml6VcclxuICAgIFxyXG4gICAgY29uc3QgdG90YWxEYXlzID0gKGVuZC5nZXRUaW1lKCkgLSBzdGFydC5nZXRUaW1lKCkpIC8gKDEwMDAgKiA2MCAqIDYwICogMjQpO1xyXG4gICAgY29uc3QgcGFzc2VkRGF5cyA9IChub3cuZ2V0VGltZSgpIC0gc3RhcnQuZ2V0VGltZSgpKSAvICgxMDAwICogNjAgKiA2MCAqIDI0KTtcclxuICAgIFxyXG4gICAgcmV0dXJuIE1hdGgucm91bmQoKHBhc3NlZERheXMgLyB0b3RhbERheXMpICogMTAwKTtcclxufSAiXX0= \ No newline at end of file diff --git a/src/utils/dateUtils.ts b/src/utils/dateUtils.ts new file mode 100644 index 0000000..8a5ee0b --- /dev/null +++ b/src/utils/dateUtils.ts @@ -0,0 +1,177 @@ +import { getLocalizedWeekday, getTranslation } from "../i18n/i18n"; + +/** + * 日期处理工具函数 + */ + +/** + * 获取当前年份 + * @returns 当前年份,如2025 + */ +export function getCurrentYear(): string { + return new Date().getFullYear().toString(); +} + +/** + * 获取当前月份数字 + * @returns 当前月份,如04 + */ +export function getCurrentMonth(): string { + const month = (new Date().getMonth() + 1).toString(); + return month.padStart(2, '0'); +} + +/** + * 获取当前月份的本地化名称 + * @param isEnglish 是否使用英文 + * @returns 月份名称,如中文环境下的"4月",英文环境下的"April" + */ +export function getLocalizedMonthName(isEnglish: boolean = false): string { + const monthIndex = new Date().getMonth(); // 0-11 + + // 中文月份名称 + const chineseMonths = [ + "1月", "2月", "3月", "4月", "5月", "6月", + "7月", "8月", "9月", "10月", "11月", "12月" + ]; + + // 英文月份名称 + const englishMonths = [ + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" + ]; + + return isEnglish ? englishMonths[monthIndex] : chineseMonths[monthIndex]; +} + +/** + * 获取当天对应的图标 + * @returns 对应日期的图标 + */ +export function getDayIcon(): string { + const day = new Date().getDate(); // 1-31 + + // 为每天分配一个独特的图标 + const dayIcons = [ + "🌑", // 1日 + "🌒", // 2日 + "🌓", // 3日 + "🌔", // 4日 + "🌕", // 5日 + "🌖", // 6日 + "🌗", // 7日 + "🌘", // 8日 + "🌟", // 9日 + "⭐", // 10日 + "🌈", // 11日 + "🌞", // 12日 + "🌤️", // 13日 + "⛅", // 14日 + "🌦️", // 15日 + "🌧️", // 16日 + "⛈️", // 17日 + "🌩️", // 18日 + "🌪️", // 19日 + "🌫️", // 20日 + "🌬️", // 21日 + "🍀", // 22日 + "🌱", // 23日 + "🌲", // 24日 + "🌳", // 25日 + "🌴", // 26日 + "🌵", // 27日 + "🌺", // 28日 + "🌻", // 29日 + "🌼", // 30日 + "🌸", // 31日 + ]; + + // 索引从0开始,天数从1开始,所以减1 + return dayIcons[day - 1] || "📅"; // 如果出现意外,返回默认日历图标 +} + +/** + * 判断当前是否为英文环境 + * @returns 是否为英文环境 + */ +export function isEnglishEnvironment(): boolean { + // 通过翻译系统中的周一测试当前语言 + // 获取"weekday.mon"的翻译,如果是"Monday"则为英文环境 + const mondayText = getTranslation("weekday.mon"); + return mondayText === "Monday"; +} + +/** + * 获取当前日期 + * @returns 当前日期,如16 + */ +export function getCurrentDay(): string { + const day = new Date().getDate().toString(); + return day.padStart(2, '0'); +} + +/** + * 获取当前完整日期 + * @returns 完整日期,如2025-04-16 + */ +export function getCurrentDate(): string { + return `${getCurrentYear()}-${getCurrentMonth()}-${getCurrentDay()}`; +} + +/** + * 获取当前带图标的完整日期 + * @returns 带图标的完整日期,如🌕 2025-04-16 + */ +export function getCurrentDateWithIcon(): string { + const icon = getDayIcon(); + return `${icon} ${getCurrentDate()}`; +} + +/** + * 判断当前是否为工作日(周一至周五) + * @returns 是否为工作日 + */ +export function isWorkday(): boolean { + const day = new Date().getDay(); + // 0是周日,1-5是周一至周五,6是周六 + return day >= 1 && day <= 5; +} + +/** + * 获取当前星期几的本地化名称 + * @returns 本地化的星期名称 + */ +export function getCurrentWeekdayName(): string { + const day = new Date().getDay(); + return getLocalizedWeekday(day); +} + +/** + * 计算当年进度百分比 + * @returns 当年进度百分比 + */ +export function getYearProgress(): number { + const now = new Date(); + const start = new Date(now.getFullYear(), 0, 1); // 当年1月1日 + const end = new Date(now.getFullYear() + 1, 0, 1); // 下一年1月1日 + + const totalDays = (end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24); + const passedDays = (now.getTime() - start.getTime()) / (1000 * 60 * 60 * 24); + + return Math.round((passedDays / totalDays) * 100); +} + +/** + * 计算当月进度百分比 + * @returns 当月进度百分比 + */ +export function getMonthProgress(): number { + const now = new Date(); + const start = new Date(now.getFullYear(), now.getMonth(), 1); // 当月1日 + const end = new Date(now.getFullYear(), now.getMonth() + 1, 1); // 下个月1日 + + const totalDays = (end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24); + const passedDays = (now.getTime() - start.getTime()) / (1000 * 60 * 60 * 24); + + return Math.round((passedDays / totalDays) * 100); +} \ No newline at end of file diff --git a/src/utils/fileUtils.js b/src/utils/fileUtils.js new file mode 100644 index 0000000..377f371 --- /dev/null +++ b/src/utils/fileUtils.js @@ -0,0 +1,428 @@ +import { normalizePath, TFile, TFolder } from "obsidian"; +import { getCurrentDate, getCurrentYear, getLocalizedMonthName, isEnglishEnvironment } from "./dateUtils"; +import { getTranslation } from '../i18n/i18n'; +/** + * 文件操作工具函数 + */ +/** + * 确保目录存在,如果不存在则创建 + * @param vault Obsidian文件系统 + * @param path 目录路径 + * @returns 是否成功创建或已存在 + */ +export async function ensureFolderExists(vault, path) { + try { + // 确保路径以/结尾,便于处理 + path = path.endsWith('/') ? path : path + '/'; + // 逐级创建目录 + const folders = path.split('/').filter(p => p.length > 0); + let currentPath = ''; + for (const folder of folders) { + // 更新当前路径 + if (currentPath) { + currentPath += '/' + folder; + } + else { + currentPath = folder; + } + // 检查路径是否存在 + console.log(`检查目录: ${currentPath}`); + const existingItem = vault.getAbstractFileByPath(currentPath); + if (!existingItem) { + // 路径不存在,创建文件夹 + console.log(`目录不存在,正在创建: ${currentPath}`); + try { + await vault.createFolder(currentPath); + console.log(`目录创建成功: ${currentPath}`); + } + catch (e) { + // 捕获可能的"文件夹已存在"错误,但继续执行 + // 这里处理可能的竞态条件:检查不存在但创建时已存在的情况 + console.log(`创建目录时出现异常,可能已被其他进程创建: ${e}`); + // 再次检查目录是否存在 + const folderAfterError = vault.getAbstractFileByPath(currentPath); + if (!folderAfterError || !(folderAfterError instanceof TFolder)) { + console.error(`目录创建失败,且无法确认目录已存在: ${currentPath}`); + return false; + } + } + } + else if (!(existingItem instanceof TFolder)) { + // 路径存在但不是文件夹(可能是同名文件) + console.error(`路径 ${currentPath} 已存在但不是文件夹,而是: ${existingItem.constructor.name}`); + return false; + } + else { + // 文件夹已存在,继续检查下一级 + console.log(`目录已存在,无需创建: ${currentPath}`); + } + } + return true; + } + catch (error) { + console.error(`创建目录时出现未预期错误(${path}):`, error); + return false; + } +} +/** + * 确保文件存在,如果不存在则创建 + * @param vault Obsidian文件系统 + * @param path 文件路径 + * @param content 文件内容 + * @returns 是否成功创建或已存在 + */ +export async function ensureFileExists(vault, path, content = '') { + try { + // 先检查这个路径是否有文件或目录 + const existingItem = vault.getAbstractFileByPath(path); + // 如果已存在 + if (existingItem) { + // 检查是否为文件而不是文件夹 + if (existingItem instanceof TFile) { + console.log(`文件已存在,无需创建: ${path}`); + return true; // 文件已存在,跳过创建 + } + else { + // 存在但不是文件(可能是同名文件夹) + console.error(`路径 ${path} 存在但不是文件,无法创建文件,可能是同名目录`); + return false; + } + } + // 文件不存在,检查父文件夹是否存在 + const lastSlashIndex = path.lastIndexOf('/'); + if (lastSlashIndex > 0) { + const parentPath = path.substring(0, lastSlashIndex); + console.log(`检查文件父目录: ${parentPath}`); + const parentExists = await ensureFolderExists(vault, parentPath); + if (!parentExists) { + console.error(`无法确保父目录存在: ${parentPath}`); + return false; + } + } + // 创建文件 + try { + console.log(`开始创建文件: ${path}`); + await vault.create(path, content); + console.log(`文件创建成功: ${path}`); + return true; + } + catch (e) { + // 如果创建时报错,再次检查文件是否已被创建 + // 这可能是由于竞态条件或其他进程同时创建了该文件 + console.log(`创建文件时出现异常: ${e}`); + // 再次检查文件是否存在 + const fileAfterError = vault.getAbstractFileByPath(path); + if (fileAfterError && fileAfterError instanceof TFile) { + console.log(`尽管出现异常,但文件已存在,可能被其他进程创建: ${path}`); + return true; + } + console.error(`文件创建最终失败: ${path}`); + return false; + } + } + catch (error) { + console.error(`创建文件时出现未预期错误(${path}):`, error); + return false; + } +} +/** + * 向文件追加内容 + * @param vault Obsidian文件系统 + * @param path 文件路径 + * @param content 要追加的内容 + * @returns 是否成功追加 + */ +export async function appendToFile(vault, path, content) { + try { + const file = vault.getAbstractFileByPath(path); + if (file && file instanceof TFile) { + // 文件存在,追加内容 + try { + const currentContent = await vault.read(file); + await vault.modify(file, currentContent + "\n\n" + content); + return true; + } + catch (e) { + console.error(`读取或修改文件时出错: ${e}`); + return false; + } + } + else { + // 文件不存在,创建文件并写入内容 + console.log(`文件不存在,尝试创建: ${path}`); + return await ensureFileExists(vault, path, content); + } + } + catch (error) { + console.error(`Error appending to file at ${path}:`, error); + return false; + } +} +/** + * 检查文件中是否包含指定内容 + * @param vault Obsidian文件系统 + * @param path 文件路径 + * @param content 要检查的内容 + * @returns 是否包含指定内容 + */ +export async function fileContains(vault, path, content) { + try { + const file = vault.getAbstractFileByPath(path); + if (file && file instanceof TFile) { + try { + const currentContent = await vault.read(file); + return currentContent.includes(content); + } + catch (e) { + console.error(`读取文件内容时出错: ${e}`); + return false; + } + } + // 文件不存在,显然不包含指定内容 + console.log(`文件不存在,无法检查内容: ${path}`); + return false; + } + catch (error) { + console.error(`Error checking file content at ${path}:`, error); + return false; + } +} +/** + * 根据当前日期生成任务文件路径 + * @param rootDir 根目录 + * @returns 任务文件路径 + */ +export function getTaskFilePath(rootDir) { + const year = getCurrentYear(); + // 根据环境选择不同的月份命名方式 + const isEnglish = isEnglishEnvironment(); + const monthName = getLocalizedMonthName(isEnglish); + // 使用本地化的月份名称生成文件路径 + return normalizePath(`${rootDir}/${year}/${monthName}.md`); +} +/** + * 检查今日任务是否已存在 + * @param vault Obsidian文件系统 + * @param rootDir 根目录 + * @returns 是否已存在 + */ +export async function todayTaskExists(vault, rootDir) { + const taskFilePath = getTaskFilePath(rootDir); + const date = getCurrentDate(); + // 查找文件是否存在以及是否包含今天的日期标题(包括带图标格式) + try { + console.log(`检查今日任务是否存在于: ${taskFilePath}`); + // 更改检查方式:改用正则表达式匹配任何包含当前日期的标题行 + const file = vault.getAbstractFileByPath(taskFilePath); + if (file && file instanceof TFile) { + const content = await vault.read(file); + const dateRegex = new RegExp(`## [^\\n]*${date}[^\\n]*\\n`); + return dateRegex.test(content); + } + return false; + } + catch (error) { + console.error(`检查今日任务时出错:`, error); + return false; + } +} +/** + * 获取前一天的日期 + * @returns 前一天的日期,格式为YYYY-MM-DD + */ +export function getYesterdayDate() { + const today = new Date(); + const yesterday = new Date(today); + yesterday.setDate(today.getDate() - 1); + const year = yesterday.getFullYear(); + const month = (yesterday.getMonth() + 1).toString().padStart(2, '0'); + const day = yesterday.getDate().toString().padStart(2, '0'); + return `${year}-${month}-${day}`; +} +/** + * 获取前一天的任务文件路径 + * @param rootDir 根目录 + * @returns 前一天任务文件的路径 + */ +export function getYesterdayTaskFilePath(rootDir) { + const yesterday = new Date(); + yesterday.setDate(yesterday.getDate() - 1); + const year = yesterday.getFullYear().toString(); + const isEnglish = isEnglishEnvironment(); + // 获取本地化的月份名称 + const month = new Date().getMonth(); + const yesterdayMonth = yesterday.getMonth(); + // 创建一个临时Date对象用于获取前一天的月份 + const tempDate = new Date(); + tempDate.setMonth(yesterdayMonth); + // 判断是否为英文环境,获取对应的月份名称 + const monthName = isEnglish ? + getMonthNameEN(yesterdayMonth) : + getMonthNameZH(yesterdayMonth); + return normalizePath(`${rootDir}/${year}/${monthName}.md`); +} +/** + * 获取中文月份名称 + * @param monthIndex 月份索引(0-11) + * @returns 中文月份名称 + */ +function getMonthNameZH(monthIndex) { + const months = [ + "1月", "2月", "3月", "4月", "5月", "6月", + "7月", "8月", "9月", "10月", "11月", "12月" + ]; + return months[monthIndex]; +} +/** + * 获取英文月份名称 + * @param monthIndex 月份索引(0-11) + * @returns 英文月份名称 + */ +function getMonthNameEN(monthIndex) { + const months = [ + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" + ]; + return months[monthIndex]; +} +/** + * 从文件内容中提取特定日期的任务内容 + * @param {Vault} vault Obsidian保险库 + * @param {string} filePath 文件路径 + * @param {string} date 日期(YYYY-MM-DD格式) + * @returns {Promise} 该日期的任务内容或null + */ +async function extractTasksForDate(vault, filePath, date) { + try { + // 检查文件是否存在 + const file = vault.getAbstractFileByPath(filePath); + if (!file || !(file instanceof TFile)) { + console.log(`找不到文件: ${filePath}`); + return null; + } + + // 读取文件内容 + const content = await vault.read(file); + + // 寻找日期标题,支持带图标格式 + const dateHeaderRegex = new RegExp(`## [^\\n]*${date}[^\\n]*\\n(.*?)(?=\\n## |$)`, 's'); + const match = content.match(dateHeaderRegex); + + if (match && match[1]) { + return match[1].trim(); + } + + console.log(`找不到日期 ${date} 的任务内容`); + return null; + } catch (error) { + console.error(`提取任务内容时出错: ${error}`); + return null; + } +} +/** + * 解析任务内容,统计总任务数和已完成任务数 + * @param taskContent 任务内容文本 + * @returns 任务统计结果 {totalTasks, completedTasks, unfinishedTasksList} + */ +export function analyzeTaskCompletion(taskContent) { + // 默认返回结果 + const result = { + totalTasks: 0, + completedTasks: 0, + unfinishedTasksList: [] + }; + if (!taskContent) { + return result; + } + // 匹配所有任务行(包括已完成和未完成) + const allTasksRegex = /- \[([ x])\] (.+)$/gm; + const completedTasksRegex = /- \[x\] (.+)$/gm; + const unfinishedTasksRegex = /- \[ \] (.+)$/gm; + // 统计总任务数 + const allTasksMatches = [...taskContent.matchAll(allTasksRegex)]; + result.totalTasks = allTasksMatches.length; + // 统计已完成任务数 + const completedTasksMatches = [...taskContent.matchAll(completedTasksRegex)]; + result.completedTasks = completedTasksMatches.length; + // 提取未完成任务内容 + const unfinishedTasksMatches = [...taskContent.matchAll(unfinishedTasksRegex)]; + result.unfinishedTasksList = unfinishedTasksMatches.map(match => match[1].trim()); + return result; +} +/** + * 检查昨日统计信息是否已存在 + * @param vault Obsidian保险库 + * @param filePath 文件路径 + * @param date 今日日期(用于检查昨日统计) + * @returns 是否存在昨日统计信息 + */ +export async function yesterdayStatisticsExists(vault, filePath, date) { + try { + // 检查文件是否存在 + const file = vault.getAbstractFileByPath(filePath); + if (!file || !(file instanceof TFile)) { + console.log(`找不到文件: ${filePath}`); + return false; + } + // 读取文件内容 + const content = await vault.read(file); + // 寻找今日日期下的昨日统计标题,支持带图标格式 + const statisticsTitleRegex = new RegExp(`## [^\\n]*${date}[^\\n]*\\n.*?${getTranslation('statistics.title')}`, 's'); + return statisticsTitleRegex.test(content); + } + catch (error) { + console.error(`检查昨日统计信息时出错: ${error}`); + return false; + } +} +/** + * 生成昨日统计信息内容 + * @param tasks 任务统计对象 + * @param t 翻译函数 + * @returns 格式化的统计信息字符串 + */ +export function generateStatisticsContent(tasks, t) { + let totalTasks; + let completedTasks; + let unfinishedTasks = []; + + // 检查输入类型 + if (Array.isArray(tasks)) { + // 如果是任务列表数组 + totalTasks = tasks.length; + completedTasks = tasks.filter(task => task.isCompleted).length; + unfinishedTasks = tasks.filter(task => !task.isCompleted).map(task => ({ task: task.task })); + } else { + // 如果是统计对象 + totalTasks = tasks.totalTasks; + completedTasks = tasks.completedTasks; + unfinishedTasks = tasks.unfinishedTasksList.map(task => ({ task })); + } + + const completionRate = totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0; + const displayTasks = unfinishedTasks.slice(0, 5); + + let content = `## ${t('statistics.title')}\n---\n`; + content += `- ${t('statistics.totalTasks')}: ${totalTasks}\n`; + content += `- ${t('statistics.completedTasks')}: ${completedTasks}\n`; + content += `- ${t('statistics.completionRate')}: ${completionRate}%\n\n`; + + if (unfinishedTasks.length > 0) { + content += `### ${t('statistics.suggestions')}\n---\n`; + + displayTasks.forEach(item => { + content += `- [ ] ${item.task}\n`; + }); + + if (unfinishedTasks.length > 5) { + const remaining = unfinishedTasks.length - 5; + const moreTasks = remaining === 1 + ? t('statistics.moreTasks.singular') + : `${remaining} ${t('statistics.moreTasks.plural')}`; + content += `- *${moreTasks}*\n`; + } + } + + return content; +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZVV0aWxzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZmlsZVV0aWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxhQUFhLEVBQWlCLEtBQUssRUFBRSxPQUFPLEVBQVMsTUFBTSxVQUFVLENBQUM7QUFDL0UsT0FBTyxFQUFFLGNBQWMsRUFBbUIsY0FBYyxFQUFFLHFCQUFxQixFQUFFLG9CQUFvQixFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQzNILE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFFOUM7O0dBRUc7QUFFSDs7Ozs7R0FLRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsa0JBQWtCLENBQUMsS0FBWSxFQUFFLElBQVk7SUFDL0QsSUFBSTtRQUNBLGdCQUFnQjtRQUNoQixJQUFJLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLEdBQUcsR0FBRyxDQUFDO1FBRTlDLFNBQVM7UUFDVCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDMUQsSUFBSSxXQUFXLEdBQUcsRUFBRSxDQUFDO1FBRXJCLEtBQUssTUFBTSxNQUFNLElBQUksT0FBTyxFQUFFO1lBQzFCLFNBQVM7WUFDVCxJQUFJLFdBQVcsRUFBRTtnQkFDYixXQUFXLElBQUksR0FBRyxHQUFHLE1BQU0sQ0FBQzthQUMvQjtpQkFBTTtnQkFDSCxXQUFXLEdBQUcsTUFBTSxDQUFDO2FBQ3hCO1lBRUQsV0FBVztZQUNYLE9BQU8sQ0FBQyxHQUFHLENBQUMsU0FBUyxXQUFXLEVBQUUsQ0FBQyxDQUFDO1lBQ3BDLE1BQU0sWUFBWSxHQUFHLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxXQUFXLENB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QWM7TUFDMUUsY0FBYyxDQUFDLDJCQUEyQixDQUFDLE9BQU8sY0FBYzs7RUFFcEUsU0FBUyxDQUFDLG1CQUFtQixDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLFFBQVEsY0FBYyxDQUFDLHdCQUF3QixDQUFDOzs7RUFHM0Ysa0JBQWtCO0VBQ2xCLFNBQVMsQ0FBQyxtQkFBbUIsQ0FBQyxNQUFNLEdBQUcsU0FBUyxDQUFDLENBQUMsQ0FBQyxTQUFTLFNBQVMsQ0FBQyxtQkFBbUIsQ0FBQyxNQUFNLEdBQUcsU0FBUyxjQUFjLENBQUMsQ0FBQyxDQUFDLEVBQUU7Q0FDaEksQ0FBQyxDQUFDLENBQUMsRUFBRTtDQUNMLENBQUM7QUFDRixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgbm9ybWFsaXplUGF0aCwgVEFic3RyYWN0RmlsZSwgVEZpbGUsIFRGb2xkZXIsIFZhdWx0IH0gZnJvbSBcIm9ic2lkaWFuXCI7XG5pbXBvcnQgeyBnZXRDdXJyZW50RGF0ZSwgZ2V0Q3VycmVudE1vbnRoLCBnZXRDdXJyZW50WWVhciwgZ2V0TG9jYWxpemVkTW9udGhOYW1lLCBpc0VuZ2xpc2hFbnZpcm9ubWVudCB9IGZyb20gXCIuL2RhdGVVdGlsc1wiO1xuaW1wb3J0IHsgZ2V0VHJhbnNsYXRpb24gfSBmcm9tICcuLi9pMThuL2kxOG4nO1xuXG4vKipcbiAqIOaWh+S7tuaTjeS9nOW3peWFt+WHveaVsFxuICovXG5cbi8qKlxuICog56Gu5L+d55uu5b2V5a2Y5Zyo77yM5aaC5p6c5LiN5a2Y5Zyo5YiZ5Yib5bu6XG4gKiBAcGFyYW0gdmF1bHQgT2JzaWRpYW7mlofku7bns7vnu59cbiAqIEBwYXJhbSBwYXRoIOebruW9lei3r+W+hFxuICogQHJldHVybnMg5piv5ZCm5oiQ5Yqf5Yib5bu65oiW5bey5a2Y5ZyoXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBlbnN1cmVGb2xkZXJFeGlzdHModmF1bHQ6IFZhdWx0LCBwYXRoOiBzdHJpbmcpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICB0cnkge1xuICAgICAgICAvLyDnoa7kv53ot6/lvoTku6Uv57uT5bC+77yM5L6/5LqO5aSE55CGXG4gICAgICAgIHBhdGggPSBwYXRoLmVuZHNXaXRoKCcvJykgPyBwYXRoIDogcGF0aCArICcvJztcblxuICAgICAgICAvLyDpgJDnuqfliJvlu7rnm67lvZVcbiAgICAgICAgY29uc3QgZm9sZGVycyA9IHBhdGguc3BsaXQoJy8nKS5maWx0ZXIocCA9PiBwLmxlbmd0aCA+IDApO1xuICAgICAgICBsZXQgY3VycmVudFBhdGggPSAnJztcbiAgICAgICAgXG4gICAgICAgIGZvciAoY29uc3QgZm9sZGVyIG9mIGZvbGRlcnMpIHtcbiAgICAgICAgICAgIC8vIOabtOaWsOW9k+WJjei3r+W+hFxuICAgICAgICAgICAgaWYgKGN1cnJlbnRQYXRoKSB7XG4gICAgICAgICAgICAgICAgY3VycmVudFBhdGggKz0gJy8nICsgZm9sZGVyO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBjdXJyZW50UGF0aCA9IGZvbGRlcjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIFxuICAgICAgICAgICAgLy8g5qOA5p+l6Lev5b6E5piv5ZCm5a2Y5ZyoXG4gICAgICAgICAgICBjb25zb2xlLmxvZyhg5qOA5p+l55uu5b2VOiAke2N1cnJlbnRQYXRofWApO1xuICAgICAgICAgICAgY29uc3QgZXhpc3RpbmdJdGVtID0gdmF1bHQuZ2V0QWJzdHJhY3RGaWxlQnlQYXRoKGN1cnJlbnRQYXRoKTtcbiAgICAgICAgICAgIFxuICAgICAgICAgICAgaWYgKCFleGlzdGluZ0l0ZW0pIHtcbiAgICAgICAgICAgICAgICAvLyDot6/lvoTkuI3lrZjlnKjvvIzliJvlu7rmlofku7blpLlcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhg55uu5b2V5LiN5a2Y5Zyo77yM5q2j5Zyo5Yib5bu6OiAke2N1cnJlbnRQYXRofWApO1xuICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgIGF3YWl0IHZhdWx0LmNyZWF0ZUZvbGRlcihjdXJyZW50UGF0aCk7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGDnm67lvZXliJvlu7rmiJDlip86ICR7Y3VycmVudFBhdGh9YCk7XG4gICAgICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICAgICAgICAvLyDmjZXojrflj6/og73nmoRcIuaWh+S7tuWkueW3suWtmOWcqFwi6ZSZ6K+v77yM5L2G57un57ut5omn6KGMXG4gICAgICAgICAgICAgICAgICAgIC8vIOi/memHjOWkhOeQhuWPr+iDveeahOernuaAgeadoeS7tu+8muajgOafpeS4jeWtmOWcqOS9huWIm+W7uuaXtuW3suWtmOWcqOeahOaDheWGtVxuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhg5Yib5bu655uu5b2V5pe25Ye6546w5byC5bi477yM5Y+v6IO95bey6KKr5YW25LuW6L+b56iL5Yib5bu6OiAke2V9YCk7XG4gICAgICAgICAgICAgICAgICAgIC8vIOWGjeasoeajgOafpeebruW9leaYr+WQpuWtmOWcqFxuICAgICAgICAgICAgICAgICAgICBjb25zdCBmb2xkZXJBZnRlckVycm9yID0gdmF1bHQuZ2V0QWJzdHJhY3RGaWxlQnlQYXRoKGN1cnJlbnRQYXRoKTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCFmb2xkZXJBZnRlckVycm9yIHx8ICEoZm9sZGVyQWZ0ZXJFcnJvciBpbnN0YW5jZW9mIFRGb2xkZXIpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKGDnm67lvZXliJvlu7rlpLHotKXvvIzkuJTml6Dms5Xnoa7orqTnm67lvZXlt7LlrZjlnKg6ICR7Y3VycmVudFBhdGh9YCk7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgaWYgKCEoZXhpc3RpbmdJdGVtIGluc3RhbmNlb2YgVEZvbGRlcikpIHtcbiAgICAgICAgICAgICAgICAvLyDot6/lvoTlrZjlnKjkvYbkuI3mmK/mlofku7blpLnvvIjlj6/og73mmK/lkIzlkI3mlofku7bvvIlcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKGDot6/lvoQgJHtjdXJyZW50UGF0aH0g5bey5a2Y5Zyo5L2G5LiN5piv5paH5Lu25aS577yM6ICM5pivOiAke2V4aXN0aW5nSXRlbS5jb25zdHJ1Y3Rvci5uYW1lfWApO1xuICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgLy8g5paH5Lu25aS55bey5a2Y5Zyo77yM57un57ut5qOA5p+l5LiL5LiA57qnXG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coYOebruW9leW3suWtmOWcqO+8jOaXoOmcgOWIm+W7ujogJHtjdXJyZW50UGF0aH1gKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihg5Yib5bu655uu5b2V5pe25Ye6546w5pyq6aKE5pyf6ZSZ6K+vKCR7cGF0aH0pOmAsIGVycm9yKTtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbn1cblxuLyoqXG4gKiDnoa7kv53mlofku7blrZjlnKjvvIzlpoLmnpzkuI3lrZjlnKjliJnliJvlu7pcbiAqIEBwYXJhbSB2YXVsdCBPYnNpZGlhbuaWh+S7tuezu+e7n1xuICogQHBhcmFtIHBhdGgg5paH5Lu26Lev5b6EXG4gKiBAcGFyYW0gY29udGVudCDmlofku7blhoXlrrlcbiAqIEByZXR1cm5zIOaYr+WQpuaIkOWKn+WIm+W7uuaIluW3suWtmOWcqFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZW5zdXJlRmlsZUV4aXN0cyh2YXVsdDogVmF1bHQsIHBhdGg6IHN0cmluZywgY29udGVudDogc3RyaW5nID0gJycpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICB0cnkge1xuICAgICAgICAvLyDlhYjmo4Dmn6Xov5nkuKrot6/lvoTmmK/lkKbmnInmlofku7bmiJbnm67lvZVcbiAgICAgICAgY29uc3QgZXhpc3RpbmdJdGVtID0gdmF1bHQuZ2V0QWJzdHJhY3RGaWxlQnlQYXRoKHBhdGgpO1xuICAgICAgICBcbiAgICAgICAgLy8g5aaC5p6c5bey5a2Y5ZyoXG4gICAgICAgIGlmIChleGlzdGluZ0l0ZW0pIHtcbiAgICAgICAgICAgIC8vIOajgOafpeaYr+WQpuS4uuaWh+S7tuiAjOS4jeaYr+aWh+S7tuWkuVxuICAgICAgICAgICAgaWYgKGV4aXN0aW5nSXRlbSBpbnN0YW5jZW9mIFRGaWxlKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coYOaWh+S7tuW3suWtmOWcqO+8jOaXoOmcgOWIm+W7ujogJHtwYXRofWApO1xuICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOyAvLyDmlofku7blt7LlrZjlnKjvvIzot7Pov4fliJvlu7pcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgLy8g5a2Y5Zyo5L2G5LiN5piv5paH5Lu277yI5Y+v6IO95piv5ZCM5ZCN5paH5Lu25aS577yJXG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihg6Lev5b6EICR7cGF0aH0g5a2Y5Zyo5L2G5LiN5piv5paH5Lu277yM5peg5rOV5Yib5bu65paH5Lu277yM5Y+v6IO95piv5ZCM5ZCN55uu5b2VYCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIFxuICAgICAgICAvLyDmlofku7bkuI3lrZjlnKjvvIzmo4Dmn6XniLbmlofku7blpLnmmK/lkKblrZjlnKhcbiAgICAgICAgY29uc3QgbGFzdFNsYXNoSW5kZXggPSBwYXRoLmxhc3RJbmRleE9mKCcvJyk7XG4gICAgICAgIGlmIChsYXN0U2xhc2hJbmRleCA+IDApIHtcbiAgICAgICAgICAgIGNvbnN0IHBhcmVudFBhdGggPSBwYXRoLnN1YnN0cmluZygwLCBsYXN0U2xhc2hJbmRleCk7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhg5qOA5p+l5paH5Lu254i255uu5b2VOiAke3BhcmVudFBhdGh9YCk7XG4gICAgICAgICAgICBjb25zdCBwYXJlbnRFeGlzdHMgPSBhd2FpdCBlbnN1cmVGb2xkZXJFeGlzdHModmF1bHQsIHBhcmVudFBhdGgpO1xuICAgICAgICAgICAgaWYgKCFwYXJlbnRFeGlzdHMpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKGDml6Dms5Xnoa7kv53niLbnm67lvZXlrZjlnKg6ICR7cGFyZW50UGF0aH1gKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyDliJvlu7rmlofku7ZcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGDlvIDlp4vliJvlu7rmlofku7Y6ICR7cGF0aH1gKTtcbiAgICAgICAgICAgIGF3YWl0IHZhdWx0LmNyZWF0ZShwYXRoLCBjb250ZW50KTtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGDmlofku7bliJvlu7rmiJDlip86ICR7cGF0aH1gKTtcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAvLyDlpoLmnpzliJvlu7rml7bmiqXplJnvvIzlho3mrKHmo4Dmn6Xmlofku7bmmK/lkKblt7LooqvliJvlu7pcbiAgICAgICAgICAgIC8vIOi/meWPr+iDveaYr+eUseS6juernuaAgeadoeS7tuaIluWFtuS7lui/m+eoi+WQjOaXtuWIm+W7uuS6huivpeaWh+S7tlxuICAgICAgICAgICAgY29uc29sZS5sb2coYOWIm+W7uuaWh+S7tuaXtuWHuueOsOW8guW4uDogJHtlfWApO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyDlho3mrKHmo4Dmn6Xmlofku7bmmK/lkKblrZjlnKhcbiAgICAgICAgICAgIGNvbnN0IGZpbGVBZnRlckVycm9yID0gdmF1bHQuZ2V0QWJzdHJhY3RGaWxlQnlQYXRoKHBhdGgpO1xuICAgICAgICAgICAgaWYgKGZpbGVBZnRlckVycm9yICYmIGZpbGVBZnRlckVycm9yIGluc3RhbmNlb2YgVEZpbGUpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhg5bC9566h5Ye6546w5byC5bi477yM5L2G5paH5Lu25bey5a2Y5Zyo77yM5Y+v6IO96KKr5YW25LuW6L+b56iL5Yib5bu6OiAke3BhdGh9YCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoYOaWh+S7tuWIm+W7uuacgOe7iOWksei0pTogJHtwYXRofWApO1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihg5Yib5bu65paH5Lu25pe25Ye6546w5pyq6aKE5pyf6ZSZ6K+vKCR7cGF0aH0pOmAsIGVycm9yKTtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbn1cblxuLyoqXG4gKiDlkJHmlofku7bov73liqDlhoXlrrlcbiAqIEBwYXJhbSB2YXVsdCBPYnNpZGlhbuaWh+S7tuezu+e7n1xuICogQHBhcmFtIHBhdGgg5paH5Lu26Lev5b6EXG4gKiBAcGFyYW0gY29udGVudCDopoHov73liqDnmoTlhoXlrrlcbiAqIEByZXR1cm5zIOaYr+WQpuaIkOWKn+i/veWKoFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gYXBwZW5kVG9GaWxlKHZhdWx0OiBWYXVsdCwgcGF0aDogc3RyaW5nLCBjb250ZW50OiBzdHJpbmcpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICB0cnkge1xuICAgICAgICBjb25zdCBmaWxlID0gdmF1bHQuZ2V0QWJzdHJhY3RGaWxlQnlQYXRoKHBhdGgpO1xuICAgICAgICBpZiAoZmlsZSAmJiBmaWxlIGluc3RhbmNlb2YgVEZpbGUpIHtcbiAgICAgICAgICAgIC8vIOaWh+S7tuWtmOWcqO+8jOi/veWKoOWGheWuuVxuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBjb25zdCBjdXJyZW50Q29udGVudCA9IGF3YWl0IHZhdWx0LnJlYWQoZmlsZSk7XG4gICAgICAgICAgICAgICAgYXdhaXQgdmF1bHQubW9kaWZ5KGZpbGUsIGN1cnJlbnRDb250ZW50ICsgXCJcXG5cXG5cIiArIGNvbnRlbnQpO1xuICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoYOivu+WPluaIluS/ruaUueaWh+S7tuaXtuWHuumUmTogJHtlfWApO1xuICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIOaWh+S7tuS4jeWtmOWcqO+8jOWIm+W7uuaWh+S7tuW5tuWGmeWFpeWGheWuuVxuICAgICAgICAgICAgY29uc29sZS5sb2coYOaWh+S7tuS4jeWtmOWcqO+8jOWwneivleWIm+W7ujogJHtwYXRofWApO1xuICAgICAgICAgICAgcmV0dXJuIGF3YWl0IGVuc3VyZUZpbGVFeGlzdHModmF1bHQsIHBhdGgsIGNvbnRlbnQpO1xuICAgICAgICB9XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihgRXJyb3IgYXBwZW5kaW5nIHRvIGZpbGUgYXQgJHtwYXRofTpgLCBlcnJvcik7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG59XG5cbi8qKlxuICog5qOA5p+l5paH5Lu25Lit5piv5ZCm5YyF5ZCr5oyH5a6a5YaF5a65XG4gKiBAcGFyYW0gdmF1bHQgT2JzaWRpYW7mlofku7bns7vnu59cbiAqIEBwYXJhbSBwYXRoIOaWh+S7tui3r+W+hFxuICogQHBhcmFtIGNvbnRlbnQg6KaB5qOA5p+l55qE5YaF5a65XG4gKiBAcmV0dXJucyDmmK/lkKbljIXlkKvmjIflrprlhoXlrrlcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGZpbGVDb250YWlucyh2YXVsdDogVmF1bHQsIHBhdGg6IHN0cmluZywgY29udGVudDogc3RyaW5nKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgdHJ5IHtcbiAgICAgICAgY29uc3QgZmlsZSA9IHZhdWx0LmdldEFic3RyYWN0RmlsZUJ5UGF0aChwYXRoKTtcbiAgICAgICAgaWYgKGZpbGUgJiYgZmlsZSBpbnN0YW5jZW9mIFRGaWxlKSB7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGN1cnJlbnRDb250ZW50ID0gYXdhaXQgdmF1bHQucmVhZChmaWxlKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gY3VycmVudENvbnRlbnQuaW5jbHVkZXMoY29udGVudCk7XG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihg6K+75Y+W5paH5Lu25YaF5a655pe25Ye66ZSZOiAke2V9YCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIC8vIOaWh+S7tuS4jeWtmOWcqO+8jOaYvueEtuS4jeWMheWQq+aMh+WumuWGheWuuVxuICAgICAgICBjb25zb2xlLmxvZyhg5paH5Lu25LiN5a2Y5Zyo77yM5peg5rOV5qOA5p+l5YaF5a65OiAke3BhdGh9YCk7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBjb25zb2xlLmVycm9yKGBFcnJvciBjaGVja2luZyBmaWxlIGNvbnRlbnQgYXQgJHtwYXRofTpgLCBlcnJvcik7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG59XG5cbi8qKlxuICog5qC55o2u5b2T5YmN5pel5pyf55Sf5oiQ5Lu75Yqh5paH5Lu26Lev5b6EXG4gKiBAcGFyYW0gcm9vdERpciDmoLnnm67lvZVcbiAqIEByZXR1cm5zIOS7u+WKoeaWh+S7tui3r+W+hFxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0VGFza0ZpbGVQYXRoKHJvb3REaXI6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgY29uc3QgeWVhciA9IGdldEN1cnJlbnRZZWFyKCk7XG4gICAgXG4gICAgLy8g5qC55o2u546v5aKD6YCJ5oup5LiN5ZCM55qE5pyI5Lu95ZG95ZCN5pa55byPXG4gICAgY29uc3QgaXNFbmdsaXNoID0gaXNFbmdsaXNoRW52aXJvbm1lbnQoKTtcbiAgICBjb25zdCBtb250aE5hbWUgPSBnZXRMb2NhbGl6ZWRNb250aE5hbWUoaXNFbmdsaXNoKTtcbiAgICBcbiAgICAvLyDkvb/nlKjmnKzlnLDljJbnmoTmnIjku73lkI3np7DnlJ/miJDmlofku7bot6/lvoRcbiAgICByZXR1cm4gbm9ybWFsaXplUGF0aChgJHtyb290RGlyfS8ke3llYXJ9LyR7bW9udGhOYW1lfS5tZGApO1xufVxuXG4vKipcbiAqIOajgOafpeS7iuaXpeS7u+WKoeaYr+WQpuW3suWtmOWcqFxuICogQHBhcmFtIHZhdWx0IE9ic2lkaWFu5paH5Lu257O757ufXG4gKiBAcGFyYW0gcm9vdERpciDmoLnnm67lvZVcbiAqIEByZXR1cm5zIOaYr+WQpuW3suWtmOWcqFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gdG9kYXlUYXNrRXhpc3RzKHZhdWx0OiBWYXVsdCwgcm9vdERpcjogc3RyaW5nKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgY29uc3QgdGFza0ZpbGVQYXRoID0gZ2V0VGFza0ZpbGVQYXRoKHJvb3REaXIpO1xuICAgIGNvbnN0IGRhdGUgPSBnZXRDdXJyZW50RGF0ZSgpO1xuICAgIFxuICAgIC8vIOafpeaJvuaWh+S7tuaYr+WQpuWtmOWcqOS7peWPiuaYr+WQpuWMheWQq+S7iuWkqeeahOaXpeacn+agh+mimFxuICAgIHRyeSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGDmo4Dmn6Xku4rml6Xku7vliqHmmK/lkKblrZjlnKjkuo46ICR7dGFza0ZpbGVQYXRofWApO1xuICAgICAgICByZXR1cm4gYXdhaXQgZmlsZUNvbnRhaW5zKHZhdWx0LCB0YXNrRmlsZVBhdGgsIGAjIyAke2RhdGV9YCk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihg5qOA5p+l5LuK5pel5Lu75Yqh5pe25Ye66ZSZOmAsIGVycm9yKTtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbn1cblxuLyoqXG4gKiDojrflj5bliY3kuIDlpKnnmoTml6XmnJ9cbiAqIEByZXR1cm5zIOWJjeS4gOWkqeeahOaXpeacn++8jOagvOW8j+S4ullZWVktTU0tRERcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFllc3RlcmRheURhdGUoKTogc3RyaW5nIHtcbiAgICBjb25zdCB0b2RheSA9IG5ldyBEYXRlKCk7XG4gICAgY29uc3QgeWVzdGVyZGF5ID0gbmV3IERhdGUodG9kYXkpO1xuICAgIHllc3RlcmRheS5zZXREYXRlKHRvZGF5LmdldERhdGUoKSAtIDEpO1xuICAgIFxuICAgIGNvbnN0IHllYXIgPSB5ZXN0ZXJkYXkuZ2V0RnVsbFllYXIoKTtcbiAgICBjb25zdCBtb250aCA9ICh5ZXN0ZXJkYXkuZ2V0TW9udGgoKSArIDEpLnRvU3RyaW5nKCkucGFkU3RhcnQoMiwgJzAnKTtcbiAgICBjb25zdCBkYXkgPSB5ZXN0ZXJkYXkuZ2V0RGF0ZSgpLnRvU3RyaW5nKCkucGFkU3RhcnQoMiwgJzAnKTtcbiAgICBcbiAgICByZXR1cm4gYCR7eWVhcn0tJHttb250aH0tJHtkYXl9YDtcbn1cblxuLyoqXG4gKiDojrflj5bliY3kuIDlpKnnmoTku7vliqHmlofku7bot6/lvoRcbiAqIEBwYXJhbSByb290RGlyIOagueebruW9lVxuICogQHJldHVybnMg5YmN5LiA5aSp5Lu75Yqh5paH5Lu255qE6Lev5b6EXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRZZXN0ZXJkYXlUYXNrRmlsZVBhdGgocm9vdERpcjogc3RyaW5nKTogc3RyaW5nIHtcbiAgICBjb25zdCB5ZXN0ZXJkYXkgPSBuZXcgRGF0ZSgpO1xuICAgIHllc3RlcmRheS5zZXREYXRlKHllc3RlcmRheS5nZXREYXRlKCkgLSAxKTtcbiAgICBcbiAgICBjb25zdCB5ZWFyID0geWVzdGVyZGF5LmdldEZ1bGxZZWFyKCkudG9TdHJpbmcoKTtcbiAgICBjb25zdCBpc0VuZ2xpc2ggPSBpc0VuZ2xpc2hFbnZpcm9ubWVudCgpO1xuICAgIFxuICAgIC8vIOiOt+WPluacrOWcsOWMlueahOaciOS7veWQjeensFxuICAgIGNvbnN0IG1vbnRoID0gbmV3IERhdGUoKS5nZXRNb250aCgpO1xuICAgIGNvbnN0IHllc3RlcmRheU1vbnRoID0geWVzdGVyZGF5LmdldE1vbnRoKCk7XG4gICAgXG4gICAgLy8g5Yib5bu65LiA5Liq5Li05pe2RGF0ZeWvueixoeeUqOS6juiOt+WPluWJjeS4gOWkqeeahOaciOS7vVxuICAgIGNvbnN0IHRlbXBEYXRlID0gbmV3IERhdGUoKTtcbiAgICB0ZW1wRGF0ZS5zZXRNb250aCh5ZXN0ZXJkYXlNb250aCk7XG4gICAgXG4gICAgLy8g5Yik5pat5piv5ZCm5Li66Iux5paH546v5aKD77yM6I635Y+W5a+55bqU55qE5pyI5Lu95ZCN56ewXG4gICAgY29uc3QgbW9udGhOYW1lID0gaXNFbmdsaXNoID8gXG4gICAgICAgIGdldE1vbnRoTmFtZUVOKHllc3RlcmRheU1vbnRoKSA6IFxuICAgICAgICBnZXRNb250aE5hbWVaSCh5ZXN0ZXJkYXlNb250aCk7XG4gICAgXG4gICAgcmV0dXJuIG5vcm1hbGl6ZVBhdGgoYCR7cm9vdERpcn0vJHt5ZWFyfS8ke21vbnRoTmFtZX0ubWRgKTtcbn1cblxuLyoqXG4gKiDojrflj5bkuK3mlofmnIjku73lkI3np7BcbiAqIEBwYXJhbSBtb250aEluZGV4IOaciOS7vee0ouW8le+8iDAtMTHvvIlcbiAqIEByZXR1cm5zIOS4reaWh+aciOS7veWQjeensFxuICovXG5mdW5jdGlvbiBnZXRNb250aE5hbWVaSChtb250aEluZGV4OiBudW1iZXIpOiBzdHJpbmcge1xuICAgIGNvbnN0IG1vbnRocyA9IFtcbiAgICAgICAgXCIx5pyIXCIsIFwiMuaciFwiLCBcIjPmnIhcIiwgXCI05pyIXCIsIFwiNeaciFwiLCBcIjbmnIhcIixcbiAgICAgICAgXCI35pyIXCIsIFwiOOaciFwiLCBcIjnmnIhcIiwgXCIxMOaciFwiLCBcIjEx5pyIXCIsIFwiMTLmnIhcIlxuICAgIF07XG4gICAgcmV0dXJuIG1vbnRoc1ttb250aEluZGV4XTtcbn1cblxuLyoqXG4gKiDojrflj5boi7HmlofmnIjku73lkI3np7BcbiAqIEBwYXJhbSBtb250aEluZGV4IOaciOS7vee0ouW8le+8iDAtMTHvvIlcbiAqIEByZXR1cm5zIOiLseaWh+aciOS7veWQjeensFxuICovXG5mdW5jdGlvbiBnZXRNb250aE5hbWVFTihtb250aEluZGV4OiBudW1iZXIpOiBzdHJpbmcge1xuICAgIGNvbnN0IG1vbnRocyA9IFtcbiAgICAgICAgXCJKYW51YXJ5XCIsIFwiRmVicnVhcnlcIiwgXCJNYXJjaFwiLCBcIkFwcmlsXCIsIFwiTWF5XCIsIFwiSnVuZVwiLFxuICAgICAgICBcIkp1bHlcIiwgXCJBdWd1c3RcIiwgXCJTZXB0ZW1iZXJcIiwgXCJPY3RvYmVyXCIsIFwiTm92ZW1iZXJcIiwgXCJEZWNlbWJlclwiXG4gICAgXTtcbiAgICByZXR1cm4gbW9udGhzW21vbnRoSW5kZXhdO1xufVxuXG4vKipcbiAqIOS7juaWh+S7tuWGheWuueS4reaPkOWPlueJueWumuaXpeacn+eahOS7u+WKoeWGheWuuVxuICogQHBhcmFtIHZhdWx0IE9ic2lkaWFu5L+d6Zmp5bqTXG4gKiBAcGFyYW0gZmlsZVBhdGgg5paH5Lu26Lev5b6EXG4gKiBAcGFyYW0gZGF0ZSDml6XmnJ/vvIhZWVlZLU1NLURE5qC85byP77yJXG4gKiBAcmV0dXJucyDor6Xml6XmnJ/nmoTku7vliqHlhoXlrrnmiJZudWxsXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBleHRyYWN0VGFza3NGb3JEYXRlKHZhdWx0OiBWYXVsdCwgZmlsZVBhdGg6IHN0cmluZywgZGF0ZTogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmcgfCBudWxsPiB7XG4gICAgdHJ5IHtcbiAgICAgICAgLy8g5qOA5p+l5paH5Lu25piv5ZCm5a2Y5ZyoXG4gICAgICAgIGNvbnN0IGZpbGUgPSB2YXVsdC5nZXRBYnN0cmFjdEZpbGVCeVBhdGgoZmlsZVBhdGgpO1xuICAgICAgICBpZiAoIWZpbGUgfHwgIShmaWxlIGluc3RhbmNlb2YgVEZpbGUpKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhg5om+5LiN5Yiw5paH5Lu2OiAke2ZpbGVQYXRofWApO1xuICAgICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIC8vIOivu+WPluaWh+S7tuWGheWuuVxuICAgICAgICBjb25zdCBjb250ZW50ID0gYXdhaXQgdmF1bHQucmVhZChmaWxlKTtcbiAgICAgICAgXG4gICAgICAgIC8vIOWvu+aJvuaXpeacn+agh+mimFxuICAgICAgICBjb25zdCBkYXRlSGVhZGVyUmVnZXggPSBuZXcgUmVnRXhwKGAjIyAke2RhdGV9W15cXG5dKlxcbiguKj8pKD89XFxuIyMgfCQpYCwgJ3MnKTtcbiAgICAgICAgY29uc3QgbWF0Y2ggPSBjb250ZW50Lm1hdGNoKGRhdGVIZWFkZXJSZWdleCk7XG4gICAgICAgIFxuICAgICAgICBpZiAobWF0Y2ggJiYgbWF0Y2hbMV0pIHtcbiAgICAgICAgICAgIHJldHVybiBtYXRjaFsxXS50cmltKCk7XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIGNvbnNvbGUubG9nKGDmib7kuI3liLDml6XmnJ8gJHtkYXRlfSDnmoTku7vliqHlhoXlrrlgKTtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihg5o+Q5Y+W5Lu75Yqh5YaF5a655pe25Ye66ZSZOiAke2Vycm9yfWApO1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG59XG5cbi8qKlxuICog6Kej5p6Q5Lu75Yqh5YaF5a6577yM57uf6K6h5oC75Lu75Yqh5pWw5ZKM5bey5a6M5oiQ5Lu75Yqh5pWwXG4gKiBAcGFyYW0gdGFza0NvbnRlbnQg5Lu75Yqh5YaF5a655paH5pysXG4gKiBAcmV0dXJucyDku7vliqHnu5/orqHnu5Pmnpwge3RvdGFsVGFza3MsIGNvbXBsZXRlZFRhc2tzLCB1bmZpbmlzaGVkVGFza3NMaXN0fVxuICovXG5leHBvcnQgZnVuY3Rpb24gYW5hbHl6ZVRhc2tDb21wbGV0aW9uKHRhc2tDb250ZW50OiBzdHJpbmcpOiB7XG4gICAgdG90YWxUYXNrczogbnVtYmVyO1xuICAgIGNvbXBsZXRlZFRhc2tzOiBudW1iZXI7XG4gICAgdW5maW5pc2hlZFRhc2tzTGlzdDogc3RyaW5nW107XG59IHtcbiAgICAvLyDpu5jorqTov5Tlm57nu5PmnpxcbiAgICBjb25zdCByZXN1bHQgPSB7XG4gICAgICAgIHRvdGFsVGFza3M6IDAsXG4gICAgICAgIGNvbXBsZXRlZFRhc2tzOiAwLFxuICAgICAgICB1bmZpbmlzaGVkVGFza3NMaXN0OiBbXSBhcyBzdHJpbmdbXVxuICAgIH07XG4gICAgXG4gICAgaWYgKCF0YXNrQ29udGVudCkge1xuICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cbiAgICBcbiAgICAvLyDljLnphY3miYDmnInku7vliqHooYzvvIjljIXmi6zlt7LlrozmiJDlkozmnKrlrozmiJDvvIlcbiAgICBjb25zdCBhbGxUYXNrc1JlZ2V4ID0gLy0gXFxbKFsgeF0pXFxdICguKykkL2dtO1xuICAgIGNvbnN0IGNvbXBsZXRlZFRhc2tzUmVnZXggPSAvLSBcXFt4XFxdICguKykkL2dtO1xuICAgIGNvbnN0IHVuZmluaXNoZWRUYXNrc1JlZ2V4ID0gLy0gXFxbIFxcXSAoLispJC9nbTtcbiAgICBcbiAgICAvLyDnu5/orqHmgLvku7vliqHmlbBcbiAgICBjb25zdCBhbGxUYXNrc01hdGNoZXMgPSBbLi4udGFza0NvbnRlbnQubWF0Y2hBbGwoYWxsVGFza3NSZWdleCldO1xuICAgIHJlc3VsdC50b3RhbFRhc2tzID0gYWxsVGFza3NNYXRjaGVzLmxlbmd0aDtcbiAgICBcbiAgICAvLyDnu5/orqHlt7LlrozmiJDku7vliqHmlbBcbiAgICBjb25zdCBjb21wbGV0ZWRUYXNrc01hdGNoZXMgPSBbLi4udGFza0NvbnRlbnQubWF0Y2hBbGwoY29tcGxldGVkVGFza3NSZWdleCldO1xuICAgIHJlc3VsdC5jb21wbGV0ZWRUYXNrcyA9IGNvbXBsZXRlZFRhc2tzTWF0Y2hlcy5sZW5ndGg7XG4gICAgXG4gICAgLy8g5o+Q5Y+W5pyq5a6M5oiQ5Lu75Yqh5YaF5a65XG4gICAgY29uc3QgdW5maW5pc2hlZFRhc2tzTWF0Y2hlcyA9IFsuLi50YXNrQ29udGVudC5tYXRjaEFsbCh1bmZpbmlzaGVkVGFza3NSZWdleCldO1xuICAgIHJlc3VsdC51bmZpbmlzaGVkVGFza3NMaXN0ID0gdW5maW5pc2hlZFRhc2tzTWF0Y2hlcy5tYXAobWF0Y2ggPT4gbWF0Y2hbMV0udHJpbSgpKTtcbiAgICBcbiAgICByZXR1cm4gcmVzdWx0O1xufVxuXG4vKipcbiAqIOajgOafpeaYqOaXpee7n+iuoeS/oeaBr+aYr+WQpuW3suWtmOWcqFxuICogQHBhcmFtIHZhdWx0IE9ic2lkaWFu5L+d6Zmp5bqTXG4gKiBAcGFyYW0gZmlsZVBhdGgg5paH5Lu26Lev5b6EXG4gKiBAcGFyYW0gZGF0ZSDku4rml6Xml6XmnJ/vvIjnlKjkuo7mo4Dmn6XmmKjml6Xnu5/orqHvvIlcbiAqIEByZXR1cm5zIOaYr+WQpuWtmOWcqOaYqOaXpee7n+iuoeS/oeaBr1xuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24geWVzdGVyZGF5U3RhdGlzdGljc0V4aXN0cyh2YXVsdDogVmF1bHQsIGZpbGVQYXRoOiBzdHJpbmcsIGRhdGU6IHN0cmluZyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIHRyeSB7XG4gICAgICAgIC8vIOajgOafpeaWh+S7tuaYr+WQpuWtmOWcqFxuICAgICAgICBjb25zdCBmaWxlID0gdmF1bHQuZ2V0QWJzdHJhY3RGaWxlQnlQYXRoKGZpbGVQYXRoKTtcbiAgICAgICAgaWYgKCFmaWxlIHx8ICEoZmlsZSBpbnN0YW5jZW9mIFRGaWxlKSkge1xuICAgICAgICAgICAgY29uc29sZS5sb2coYOaJvuS4jeWIsOaWh+S7tjogJHtmaWxlUGF0aH1gKTtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICBcbiAgICAgICAgLy8g6K+75Y+W5paH5Lu25YaF5a65XG4gICAgICAgIGNvbnN0IGNvbnRlbnQgPSBhd2FpdCB2YXVsdC5yZWFkKGZpbGUpO1xuICAgICAgICBcbiAgICAgICAgLy8g5a+75om+5LuK5pel5pel5pyf5LiL55qE5pio5pel57uf6K6h5qCH6aKYXG4gICAgICAgIGNvbnN0IHN0YXRpc3RpY3NUaXRsZVJlZ2V4ID0gbmV3IFJlZ0V4cChgIyMgJHtkYXRlfVteXFxuXSpcXG4uKj8ke2dldFRyYW5zbGF0aW9uKCdzdGF0aXN0aWNzLnRpdGxlJyl9YCwgJ3MnKTtcbiAgICAgICAgcmV0dXJuIHN0YXRpc3RpY3NUaXRsZVJlZ2V4LnRlc3QoY29udGVudCk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihg5qOA5p+l5pio5pel57uf6K6h5L+h5oGv5pe25Ye66ZSZOiAke2Vycm9yfWApO1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxufVxuXG4vKipcbiAqIOeUn+aIkOaYqOaXpee7n+iuoeS/oeaBr+WGheWuuVxuICogQHBhcmFtIHRhc2tTdGF0cyDku7vliqHnu5/orqHnu5PmnpxcbiAqIEByZXR1cm5zIOagvOW8j+WMlueahOe7n+iuoeS/oeaBr+Wtl+espuS4slxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2VuZXJhdGVTdGF0aXN0aWNzQ29udGVudCh0YXNrU3RhdHM6IHtcbiAgICB0b3RhbFRhc2tzOiBudW1iZXI7XG4gICAgY29tcGxldGVkVGFza3M6IG51bWJlcjtcbiAgICB1bmZpbmlzaGVkVGFza3NMaXN0OiBzdHJpbmdbXTtcbn0pOiBzdHJpbmcge1xuICAgIC8vIOiuoeeul+WujOaIkOeOh++8iOmBv+WFjemZpOS7pembtumUmeivr++8iVxuICAgIGNvbnN0IGNvbXBsZXRpb25SYXRlID0gdGFza1N0YXRzLnRvdGFsVGFza3MgPiAwXG4gICAgICAgID8gTWF0aC5yb3VuZCgodGFza1N0YXRzLmNvbXBsZXRlZFRhc2tzIC8gdGFza1N0YXRzLnRvdGFsVGFza3MpICogMTAwKVxuICAgICAgICA6IDA7XG4gICAgXG4gICAgLy8g5YeG5aSH5pyq5a6M5oiQ5Lu75Yqh5YiX6KGo77yI5pyA5aSa5pi+56S6NeS4qu+8iVxuICAgIGNvbnN0IHNob3dDb3VudCA9IE1hdGgubWluKHRhc2tTdGF0cy51bmZpbmlzaGVkVGFza3NMaXN0Lmxlbmd0aCwgNSk7XG4gICAgY29uc3QgdW5maW5pc2hlZFRhc2tzU3RyID0gdGFza1N0YXRzLnVuZmluaXNoZWRUYXNrc0xpc3RcbiAgICAgICAgLnNsaWNlKDAsIHNob3dDb3VudClcbiAgICAgICAgLm1hcCh0YXNrID0+IGAtIFsgXSAke3Rhc2t9YClcbiAgICAgICAgLmpvaW4oJ1xcbicpO1xuICAgIFxuICAgIC8vIOaehOW7uue7n+iuoeWGheWuuVxuICAgIHJldHVybiBgIyMjICR7Z2V0VHJhbnNsYXRpb24oJ3N0YXRpc3RpY3MudGl0bGUnKX1cbi0tLVxuXG4tICoqJHtnZXRUcmFuc2xhdGlvbignc3RhdGlzdGljcy50b3RhbFRhc2tzJyl9OioqICR7dGFza1N0YXRzLnRvdGFsVGFza3N9XG4tICoqJHtnZXRUcmFuc2xhdGlvbignc3RhdGlzdGljcy5jb21wbGV0ZWRUYXNrcycpfToqKiAke3Rhc2tTdGF0cy5jb21wbGV0ZWRUYXNrc31cbi0gKioke2dldFRyYW5zbGF0aW9uKCdzdGF0aXN0aWNzLmNvbXBsZXRpb25SYXRlJyl9OioqICR7Y29tcGxldGlvblJhdGV9JVxuXG4ke3Rhc2tTdGF0cy51bmZpbmlzaGVkVGFza3NMaXN0Lmxlbmd0aCA+IDAgPyBgIyMjIyAke2dldFRyYW5zbGF0aW9uKCdzdGF0aXN0aWNzLnN1Z2dlc3Rpb25zJyl9XG4tLS1cblxuJHt1bmZpbmlzaGVkVGFza3NTdHJ9XG4ke3Rhc2tTdGF0cy51bmZpbmlzaGVkVGFza3NMaXN0Lmxlbmd0aCA+IHNob3dDb3VudCA/IGBcXG5fLi4uJHt0YXNrU3RhdHMudW5maW5pc2hlZFRhc2tzTGlzdC5sZW5ndGggLSBzaG93Q291bnR9IG1vcmUgdGFza3NfYCA6ICcnfVxuYCA6ICcnfVxuYDtcbn0gIl19 \ No newline at end of file diff --git a/src/utils/fileUtils.ts b/src/utils/fileUtils.ts new file mode 100644 index 0000000..99e29b8 --- /dev/null +++ b/src/utils/fileUtils.ts @@ -0,0 +1,464 @@ +import { normalizePath, TAbstractFile, TFile, TFolder, Vault } from "obsidian"; +import { getCurrentDate, getCurrentMonth, getCurrentYear, getLocalizedMonthName, isEnglishEnvironment } from "./dateUtils"; +import { getTranslation, TranslationKey } from '../i18n/i18n'; +import { Notice } from 'obsidian'; +import { DEFAULT_SETTINGS } from '../settings/index'; + +/** + * 文件操作工具函数 + */ + +/** + * 确保目录存在,如果不存在则创建 + * @param vault Obsidian文件系统 + * @param path 目录路径 + * @returns 是否成功创建或已存在 + */ +export async function ensureFolderExists(vault: Vault, path: string): Promise { + try { + // 确保路径以/结尾,便于处理 + path = path.endsWith('/') ? path : path + '/'; + + // 逐级创建目录 + const folders = path.split('/').filter(p => p.length > 0); + let currentPath = ''; + + for (const folder of folders) { + // 更新当前路径 + if (currentPath) { + currentPath += '/' + folder; + } else { + currentPath = folder; + } + + // 检查路径是否存在 + console.log(`检查目录: ${currentPath}`); + const existingItem = vault.getAbstractFileByPath(currentPath); + + if (!existingItem) { + // 路径不存在,创建文件夹 + console.log(`目录不存在,正在创建: ${currentPath}`); + try { + await vault.createFolder(currentPath); + console.log(`目录创建成功: ${currentPath}`); + } catch (e) { + // 捕获可能的"文件夹已存在"错误,但继续执行 + // 这里处理可能的竞态条件:检查不存在但创建时已存在的情况 + console.log(`创建目录时出现异常,可能已被其他进程创建: ${e}`); + // 再次检查目录是否存在 + const folderAfterError = vault.getAbstractFileByPath(currentPath); + if (!folderAfterError || !(folderAfterError instanceof TFolder)) { + console.error(`目录创建失败,且无法确认目录已存在: ${currentPath}`); + return false; + } + } + } else if (!(existingItem instanceof TFolder)) { + // 路径存在但不是文件夹(可能是同名文件) + console.error(`路径 ${currentPath} 已存在但不是文件夹,而是: ${existingItem.constructor.name}`); + return false; + } else { + // 文件夹已存在,继续检查下一级 + console.log(`目录已存在,无需创建: ${currentPath}`); + } + } + + return true; + } catch (error) { + console.error(`创建目录时出现未预期错误(${path}):`, error); + return false; + } +} + +/** + * 确保文件存在,如果不存在则创建 + * @param vault Obsidian文件系统 + * @param path 文件路径 + * @param content 文件内容 + * @returns 是否成功创建或已存在 + */ +export async function ensureFileExists(vault: Vault, path: string, content: string = ''): Promise { + try { + // 先检查这个路径是否有文件或目录 + const existingItem = vault.getAbstractFileByPath(path); + + // 如果已存在 + if (existingItem) { + // 检查是否为文件而不是文件夹 + if (existingItem instanceof TFile) { + console.log(`文件已存在,无需创建: ${path}`); + return true; // 文件已存在,跳过创建 + } else { + // 存在但不是文件(可能是同名文件夹) + console.error(`路径 ${path} 存在但不是文件,无法创建文件,可能是同名目录`); + return false; + } + } + + // 文件不存在,检查父文件夹是否存在 + const lastSlashIndex = path.lastIndexOf('/'); + if (lastSlashIndex > 0) { + const parentPath = path.substring(0, lastSlashIndex); + console.log(`检查文件父目录: ${parentPath}`); + const parentExists = await ensureFolderExists(vault, parentPath); + if (!parentExists) { + console.error(`无法确保父目录存在: ${parentPath}`); + return false; + } + } + + // 创建文件 + try { + console.log(`开始创建文件: ${path}`); + await vault.create(path, content); + console.log(`文件创建成功: ${path}`); + return true; + } catch (e) { + // 如果创建时报错,再次检查文件是否已被创建 + // 这可能是由于竞态条件或其他进程同时创建了该文件 + console.log(`创建文件时出现异常: ${e}`); + + // 再次检查文件是否存在 + const fileAfterError = vault.getAbstractFileByPath(path); + if (fileAfterError && fileAfterError instanceof TFile) { + console.log(`尽管出现异常,但文件已存在,可能被其他进程创建: ${path}`); + return true; + } + + console.error(`文件创建最终失败: ${path}`); + return false; + } + } catch (error) { + console.error(`创建文件时出现未预期错误(${path}):`, error); + return false; + } +} + +/** + * 向文件追加内容 + * @param vault Obsidian文件系统 + * @param path 文件路径 + * @param content 要追加的内容 + * @returns 是否成功追加 + */ +export async function appendToFile(vault: Vault, path: string, content: string): Promise { + try { + const file = vault.getAbstractFileByPath(path); + if (file && file instanceof TFile) { + // 文件存在,追加内容 + try { + const currentContent = await vault.read(file); + await vault.modify(file, currentContent + "\n\n" + content); + return true; + } catch (e) { + console.error(`读取或修改文件时出错: ${e}`); + return false; + } + } else { + // 文件不存在,创建文件并写入内容 + console.log(`文件不存在,尝试创建: ${path}`); + return await ensureFileExists(vault, path, content); + } + } catch (error) { + console.error(`Error appending to file at ${path}:`, error); + return false; + } +} + +/** + * 检查文件中是否包含指定内容 + * @param vault Obsidian文件系统 + * @param path 文件路径 + * @param content 要检查的内容 + * @returns 是否包含指定内容 + */ +export async function fileContains(vault: Vault, path: string, content: string): Promise { + try { + const file = vault.getAbstractFileByPath(path); + if (file && file instanceof TFile) { + try { + const currentContent = await vault.read(file); + return currentContent.includes(content); + } catch (e) { + console.error(`读取文件内容时出错: ${e}`); + return false; + } + } + // 文件不存在,显然不包含指定内容 + console.log(`文件不存在,无法检查内容: ${path}`); + return false; + } catch (error) { + console.error(`Error checking file content at ${path}:`, error); + return false; + } +} + +/** + * 根据当前日期生成任务文件路径 + * @param rootDir 根目录 + * @returns 任务文件路径 + */ +export function getTaskFilePath(rootDir: string): string { + const year = getCurrentYear(); + + // 根据环境选择不同的月份命名方式 + const isEnglish = isEnglishEnvironment(); + const monthName = getLocalizedMonthName(isEnglish); + + // 使用本地化的月份名称生成文件路径 + return normalizePath(`${rootDir}/${year}/${monthName}.md`); +} + +/** + * 检查今日任务是否已存在 + * @param vault Obsidian文件系统 + * @param rootDir 根目录 + * @returns 是否已存在 + */ +export async function todayTaskExists(vault: Vault, rootDir: string): Promise { + const taskFilePath = getTaskFilePath(rootDir); + const date = getCurrentDate(); + + // 查找文件是否存在以及是否包含今天的日期标题(包括带图标格式) + try { + console.log(`检查今日任务是否存在于: ${taskFilePath}`); + // 更改检查方式:改用正则表达式匹配任何包含当前日期的标题行 + const file = vault.getAbstractFileByPath(taskFilePath); + if (file && file instanceof TFile) { + const content = await vault.read(file); + const dateRegex = new RegExp(`## [^\\n]*${date}[^\\n]*\\n`); + return dateRegex.test(content); + } + return false; + } catch (error) { + console.error(`检查今日任务时出错:`, error); + return false; + } +} + +/** + * 获取前一天的日期 + * @returns 前一天的日期,格式为YYYY-MM-DD + */ +export function getYesterdayDate(): string { + const today = new Date(); + const yesterday = new Date(today); + yesterday.setDate(today.getDate() - 1); + + const year = yesterday.getFullYear(); + const month = (yesterday.getMonth() + 1).toString().padStart(2, '0'); + const day = yesterday.getDate().toString().padStart(2, '0'); + + return `${year}-${month}-${day}`; +} + +/** + * 获取前一天的任务文件路径 + * @param rootDir 根目录 + * @returns 前一天任务文件的路径 + */ +export function getYesterdayTaskFilePath(rootDir: string): string { + const yesterday = new Date(); + yesterday.setDate(yesterday.getDate() - 1); + + const year = yesterday.getFullYear().toString(); + const isEnglish = isEnglishEnvironment(); + + // 获取本地化的月份名称 + const month = new Date().getMonth(); + const yesterdayMonth = yesterday.getMonth(); + + // 创建一个临时Date对象用于获取前一天的月份 + const tempDate = new Date(); + tempDate.setMonth(yesterdayMonth); + + // 判断是否为英文环境,获取对应的月份名称 + const monthName = isEnglish ? + getMonthNameEN(yesterdayMonth) : + getMonthNameZH(yesterdayMonth); + + return normalizePath(`${rootDir}/${year}/${monthName}.md`); +} + +/** + * 获取中文月份名称 + * @param monthIndex 月份索引(0-11) + * @returns 中文月份名称 + */ +function getMonthNameZH(monthIndex: number): string { + const months = [ + "1月", "2月", "3月", "4月", "5月", "6月", + "7月", "8月", "9月", "10月", "11月", "12月" + ]; + return months[monthIndex]; +} + +/** + * 获取英文月份名称 + * @param monthIndex 月份索引(0-11) + * @returns 英文月份名称 + */ +function getMonthNameEN(monthIndex: number): string { + const months = [ + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" + ]; + return months[monthIndex]; +} + +/** + * 从文件内容中提取特定日期的任务内容 + * @param vault Obsidian保险库 + * @param filePath 文件路径 + * @param date 日期(YYYY-MM-DD格式) + * @returns 该日期的任务内容或null + */ +export async function extractTasksForDate(vault: Vault, filePath: string, date: string): Promise { + try { + // 检查文件是否存在 + const file = vault.getAbstractFileByPath(filePath); + if (!file || !(file instanceof TFile)) { + console.log(`找不到文件: ${filePath}`); + return null; + } + + // 读取文件内容 + const content = await vault.read(file); + + // 寻找日期标题,支持带图标格式 + const dateHeaderRegex = new RegExp(`## [^\\n]*${date}[^\\n]*\\n(.*?)(?=\\n## |$)`, 's'); + const match = content.match(dateHeaderRegex); + + if (match && match[1]) { + return match[1].trim(); + } + + console.log(`找不到日期 ${date} 的任务内容`); + return null; + } catch (error) { + console.error(`提取任务内容时出错: ${error}`); + return null; + } +} + +/** + * 解析任务内容,统计总任务数和已完成任务数 + * @param taskContent 任务内容文本 + * @returns 任务统计结果 {totalTasks, completedTasks, unfinishedTasksList} + */ +export function analyzeTaskCompletion(taskContent: string): { + totalTasks: number; + completedTasks: number; + unfinishedTasksList: string[]; +} { + // 默认返回结果 + const result = { + totalTasks: 0, + completedTasks: 0, + unfinishedTasksList: [] as string[] + }; + + if (!taskContent) { + return result; + } + + // 匹配所有任务行(包括已完成和未完成) + const allTasksRegex = /- \[([ x])\] (.+)$/gm; + const completedTasksRegex = /- \[x\] (.+)$/gm; + const unfinishedTasksRegex = /- \[ \] (.+)$/gm; + + // 统计总任务数 + const allTasksMatches = [...taskContent.matchAll(allTasksRegex)]; + result.totalTasks = allTasksMatches.length; + + // 统计已完成任务数 + const completedTasksMatches = [...taskContent.matchAll(completedTasksRegex)]; + result.completedTasks = completedTasksMatches.length; + + // 提取未完成任务内容 + const unfinishedTasksMatches = [...taskContent.matchAll(unfinishedTasksRegex)]; + result.unfinishedTasksList = unfinishedTasksMatches.map(match => match[1].trim()); + + return result; +} + +/** + * 检查昨日统计信息是否已存在 + * @param vault Obsidian保险库 + * @param filePath 文件路径 + * @param date 今日日期(用于检查昨日统计) + * @returns 是否存在昨日统计信息 + */ +export async function yesterdayStatisticsExists(vault: Vault, filePath: string, date: string): Promise { + try { + // 检查文件是否存在 + const file = vault.getAbstractFileByPath(filePath); + if (!file || !(file instanceof TFile)) { + console.log(`找不到文件: ${filePath}`); + return false; + } + + // 读取文件内容 + const content = await vault.read(file); + + // 寻找今日日期下的昨日统计标题,支持带图标格式 + const statisticsTitleRegex = new RegExp(`## [^\\n]*${date}[^\\n]*\\n.*?${getTranslation('statistics.title')}`, 's'); + return statisticsTitleRegex.test(content); + } catch (error) { + console.error(`检查昨日统计信息时出错: ${error}`); + return false; + } +} + +/** + * 生成昨日统计信息内容 + * @param tasks 任务统计对象或任务列表 + * @param t 翻译函数 + * @returns 格式化的统计信息字符串 + */ +export function generateStatisticsContent( + tasks: { totalTasks: number; completedTasks: number; unfinishedTasksList: string[] } | { task: string; isCompleted: boolean }[], + t: (key: TranslationKey) => string +): string { + let totalTasks: number; + let completedTasks: number; + let unfinishedTasks: { task: string }[] = []; + + // 检查输入类型 + if (Array.isArray(tasks)) { + // 如果是任务列表数组 + totalTasks = tasks.length; + completedTasks = tasks.filter(task => task.isCompleted).length; + unfinishedTasks = tasks.filter(task => !task.isCompleted).map(task => ({ task: task.task })); + } else { + // 如果是统计对象 + totalTasks = tasks.totalTasks; + completedTasks = tasks.completedTasks; + unfinishedTasks = tasks.unfinishedTasksList.map(task => ({ task })); + } + + const completionRate = totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0; + const displayTasks = unfinishedTasks.slice(0, 5); + + let content = `## ${t('statistics.title')}\n---\n`; + content += `- ${t('statistics.totalTasks')}: ${totalTasks}\n`; + content += `- ${t('statistics.completedTasks')}: ${completedTasks}\n`; + content += `- ${t('statistics.completionRate')}: ${completionRate}%\n\n`; + + if (unfinishedTasks.length > 0) { + content += `### ${t('statistics.suggestions')}\n---\n`; + + displayTasks.forEach(item => { + content += `- [ ] ${item.task}\n`; + }); + + if (unfinishedTasks.length > 5) { + const remaining = unfinishedTasks.length - 5; + const moreTasks = remaining === 1 + ? t('statistics.moreTasks.singular') + : `${remaining} ${t('statistics.moreTasks.plural')}`; + content += `- *${moreTasks}*\n`; + } + } + + return content; +} \ No newline at end of file diff --git a/src/utils/templateEngine.js b/src/utils/templateEngine.js new file mode 100644 index 0000000..af82080 --- /dev/null +++ b/src/utils/templateEngine.js @@ -0,0 +1,51 @@ +import { getCurrentDate, getCurrentWeekdayName, getYearProgress, getMonthProgress, getCurrentDateWithIcon } from "./dateUtils"; +/** + * 模板变量渲染引擎 + */ +/** + * 获取当前时间 + * @returns 当前时间,如10:30 + */ +function getCurrentTime() { + const now = new Date(); + const hours = now.getHours().toString().padStart(2, '0'); + const minutes = now.getMinutes().toString().padStart(2, '0'); + return `${hours}:${minutes}`; +} +/** + * 渲染模板内容,替换其中的变量 + * @param template 模板内容 + * @returns 渲染后的内容 + */ +export function renderTemplate(template) { + // 定义变量映射 + const variableMap = { + 'date': getCurrentDate(), + 'dateWithIcon': getCurrentDateWithIcon(), + 'weekday': getCurrentWeekdayName(), + 'yearProgress': getYearProgress(), + 'monthProgress': getMonthProgress(), + 'time': getCurrentTime() + }; + // 替换模板中的变量 + let renderedContent = template; + for (const [variable, value] of Object.entries(variableMap)) { + renderedContent = renderedContent.replace(new RegExp(`{{${variable}}}`, 'g'), value.toString()); + } + return renderedContent; +} +/** + * 获取变量说明 + * @returns 变量说明,包括中英文 + */ +export function getTemplateVariables() { + return { + 'date': '当前日期 / Current date (YYYY-MM-DD)', + 'dateWithIcon': '带图标的当前日期 / Current date with daily icon', + 'weekday': '当前星期 / Current weekday', + 'yearProgress': '年度进度百分比 / Year progress percentage', + 'monthProgress': '月度进度百分比 / Month progress percentage', + 'time': '当前时间 / Current time (HH:MM)' + }; +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVtcGxhdGVFbmdpbmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJ0ZW1wbGF0ZUVuZ2luZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0gsY0FBYyxFQUNkLHFCQUFxQixFQUNyQixlQUFlLEVBQ2YsZ0JBQWdCLEVBQ2hCLHNCQUFzQixFQUN6QixNQUFNLGFBQWEsQ0FBQztBQUVyQjs7R0FFRztBQUVIOzs7R0FHRztBQUNILFNBQVMsY0FBYztJQUNuQixNQUFNLEdBQUcsR0FBRyxJQUFJLElBQUksRUFBRSxDQUFDO0lBQ3ZCLE1BQU0sS0FBSyxHQUFHLEdBQUcsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQ3pELE1BQU0sT0FBTyxHQUFHLEdBQUcsQ0FBQyxVQUFVLEVBQUUsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQzdELE9BQU8sR0FBRyxLQUFLLElBQUksT0FBTyxFQUFFLENBQUM7QUFDakMsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxNQUFNLFVBQVUsY0FBYyxDQUFDLFFBQWdCO0lBQzNDLFNBQVM7SUFDVCxNQUFNLFdBQVcsR0FBb0M7UUFDakQsTUFBTSxFQUFFLGNBQWMsRUFBRTtRQUN4QixjQUFjLEVBQUUsc0JBQXNCLEVBQUU7UUFDeEMsU0FBUyxFQUFFLHFCQUFxQixFQUFFO1FBQ2xDLGNBQWMsRUFBRSxlQUFlLEVBQUU7UUFDakMsZUFBZSxFQUFFLGdCQUFnQixFQUFFO1FBQ25DLE1BQU0sRUFBRSxjQUFjLEVBQUU7S0FDM0IsQ0FBQztJQUVGLFdBQVc7SUFDWCxJQUFJLGVBQWUsR0FBRyxRQUFRLENBQUM7SUFDL0IsS0FBSyxNQUFNLENBQUMsUUFBUSxFQUFFLEtBQUssQ0FBQyxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsV0FBVyxDQUFDLEVBQUU7UUFDekQsZUFBZSxHQUFHLGVBQWUsQ0FBQyxPQUFPLENBQ3JDLElBQUksTUFBTSxDQUFDLEtBQUssUUFBUSxJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQ2xDLEtBQUssQ0FBQyxRQUFRLEVBQUUsQ0FDbkIsQ0FBQztLQUNMO0lBRUQsT0FBTyxlQUFlLENBQUM7QUFDM0IsQ0FBQztBQUVEOzs7R0FHRztBQUNILE1BQU0sVUFBVSxvQkFBb0I7SUFDaEMsT0FBTztRQUNILE1BQU0sRUFBRSxrQ0FBa0M7UUFDMUMsY0FBYyxFQUFFLHlDQUF5QztRQUN6RCxTQUFTLEVBQUUsd0JBQXdCO1FBQ25DLGNBQWMsRUFBRSxvQ0FBb0M7UUFDcEQsZUFBZSxFQUFFLHFDQUFxQztRQUN0RCxNQUFNLEVBQUUsNkJBQTZCO0tBQ3hDLENBQUM7QUFDTixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgXHJcbiAgICBnZXRDdXJyZW50RGF0ZSwgXHJcbiAgICBnZXRDdXJyZW50V2Vla2RheU5hbWUsIFxyXG4gICAgZ2V0WWVhclByb2dyZXNzLCBcclxuICAgIGdldE1vbnRoUHJvZ3Jlc3MsIFxyXG4gICAgZ2V0Q3VycmVudERhdGVXaXRoSWNvbiBcclxufSBmcm9tIFwiLi9kYXRlVXRpbHNcIjtcclxuXHJcbi8qKlxyXG4gKiDmqKHmnb/lj5jph4/muLLmn5PlvJXmk45cclxuICovXHJcblxyXG4vKipcclxuICog6I635Y+W5b2T5YmN5pe26Ze0XHJcbiAqIEByZXR1cm5zIOW9k+WJjeaXtumXtO+8jOWmgjEwOjMwXHJcbiAqL1xyXG5mdW5jdGlvbiBnZXRDdXJyZW50VGltZSgpOiBzdHJpbmcge1xyXG4gICAgY29uc3Qgbm93ID0gbmV3IERhdGUoKTtcclxuICAgIGNvbnN0IGhvdXJzID0gbm93LmdldEhvdXJzKCkudG9TdHJpbmcoKS5wYWRTdGFydCgyLCAnMCcpO1xyXG4gICAgY29uc3QgbWludXRlcyA9IG5vdy5nZXRNaW51dGVzKCkudG9TdHJpbmcoKS5wYWRTdGFydCgyLCAnMCcpO1xyXG4gICAgcmV0dXJuIGAke2hvdXJzfToke21pbnV0ZXN9YDtcclxufVxyXG5cclxuLyoqXHJcbiAqIOa4suafk+aooeadv+WGheWuue+8jOabv+aNouWFtuS4reeahOWPmOmHj1xyXG4gKiBAcGFyYW0gdGVtcGxhdGUg5qih5p2/5YaF5a65XHJcbiAqIEByZXR1cm5zIOa4suafk+WQjueahOWGheWuuVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHJlbmRlclRlbXBsYXRlKHRlbXBsYXRlOiBzdHJpbmcpOiBzdHJpbmcge1xyXG4gICAgLy8g5a6a5LmJ5Y+Y6YeP5pig5bCEXHJcbiAgICBjb25zdCB2YXJpYWJsZU1hcDogUmVjb3JkPHN0cmluZywgc3RyaW5nIHwgbnVtYmVyPiA9IHtcclxuICAgICAgICAnZGF0ZSc6IGdldEN1cnJlbnREYXRlKCksXHJcbiAgICAgICAgJ2RhdGVXaXRoSWNvbic6IGdldEN1cnJlbnREYXRlV2l0aEljb24oKSxcclxuICAgICAgICAnd2Vla2RheSc6IGdldEN1cnJlbnRXZWVrZGF5TmFtZSgpLFxyXG4gICAgICAgICd5ZWFyUHJvZ3Jlc3MnOiBnZXRZZWFyUHJvZ3Jlc3MoKSxcclxuICAgICAgICAnbW9udGhQcm9ncmVzcyc6IGdldE1vbnRoUHJvZ3Jlc3MoKSxcclxuICAgICAgICAndGltZSc6IGdldEN1cnJlbnRUaW1lKClcclxuICAgIH07XHJcbiAgICBcclxuICAgIC8vIOabv+aNouaooeadv+S4reeahOWPmOmHj1xyXG4gICAgbGV0IHJlbmRlcmVkQ29udGVudCA9IHRlbXBsYXRlO1xyXG4gICAgZm9yIChjb25zdCBbdmFyaWFibGUsIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyh2YXJpYWJsZU1hcCkpIHtcclxuICAgICAgICByZW5kZXJlZENvbnRlbnQgPSByZW5kZXJlZENvbnRlbnQucmVwbGFjZShcclxuICAgICAgICAgICAgbmV3IFJlZ0V4cChge3ske3ZhcmlhYmxlfX19YCwgJ2cnKSwgXHJcbiAgICAgICAgICAgIHZhbHVlLnRvU3RyaW5nKClcclxuICAgICAgICApO1xyXG4gICAgfVxyXG4gICAgXHJcbiAgICByZXR1cm4gcmVuZGVyZWRDb250ZW50O1xyXG59XHJcblxyXG4vKipcclxuICog6I635Y+W5Y+Y6YeP6K+05piOXHJcbiAqIEByZXR1cm5zIOWPmOmHj+ivtOaYju+8jOWMheaLrOS4reiLseaWh1xyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFRlbXBsYXRlVmFyaWFibGVzKCk6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4ge1xyXG4gICAgcmV0dXJuIHtcclxuICAgICAgICAnZGF0ZSc6ICflvZPliY3ml6XmnJ8gLyBDdXJyZW50IGRhdGUgKFlZWVktTU0tREQpJyxcclxuICAgICAgICAnZGF0ZVdpdGhJY29uJzogJ+W4puWbvuagh+eahOW9k+WJjeaXpeacnyAvIEN1cnJlbnQgZGF0ZSB3aXRoIGRhaWx5IGljb24nLFxyXG4gICAgICAgICd3ZWVrZGF5JzogJ+W9k+WJjeaYn+acnyAvIEN1cnJlbnQgd2Vla2RheScsXHJcbiAgICAgICAgJ3llYXJQcm9ncmVzcyc6ICflubTluqbov5vluqbnmb7liIbmr5QgLyBZZWFyIHByb2dyZXNzIHBlcmNlbnRhZ2UnLFxyXG4gICAgICAgICdtb250aFByb2dyZXNzJzogJ+aciOW6pui/m+W6pueZvuWIhuavlCAvIE1vbnRoIHByb2dyZXNzIHBlcmNlbnRhZ2UnLFxyXG4gICAgICAgICd0aW1lJzogJ+W9k+WJjeaXtumXtCAvIEN1cnJlbnQgdGltZSAoSEg6TU0pJ1xyXG4gICAgfTtcclxufSAiXX0= \ No newline at end of file diff --git a/src/utils/templateEngine.ts b/src/utils/templateEngine.ts new file mode 100644 index 0000000..5441d6c --- /dev/null +++ b/src/utils/templateEngine.ts @@ -0,0 +1,65 @@ +import { + getCurrentDate, + getCurrentWeekdayName, + getYearProgress, + getMonthProgress, + getCurrentDateWithIcon +} from "./dateUtils"; + +/** + * 模板变量渲染引擎 + */ + +/** + * 获取当前时间 + * @returns 当前时间,如10:30 + */ +function getCurrentTime(): string { + const now = new Date(); + const hours = now.getHours().toString().padStart(2, '0'); + const minutes = now.getMinutes().toString().padStart(2, '0'); + return `${hours}:${minutes}`; +} + +/** + * 渲染模板内容,替换其中的变量 + * @param template 模板内容 + * @returns 渲染后的内容 + */ +export function renderTemplate(template: string): string { + // 定义变量映射 + const variableMap: Record = { + 'date': getCurrentDate(), + 'dateWithIcon': getCurrentDateWithIcon(), + 'weekday': getCurrentWeekdayName(), + 'yearProgress': getYearProgress(), + 'monthProgress': getMonthProgress(), + 'time': getCurrentTime() + }; + + // 替换模板中的变量 + let renderedContent = template; + for (const [variable, value] of Object.entries(variableMap)) { + renderedContent = renderedContent.replace( + new RegExp(`{{${variable}}}`, 'g'), + value.toString() + ); + } + + return renderedContent; +} + +/** + * 获取变量说明 + * @returns 变量说明,包括中英文 + */ +export function getTemplateVariables(): Record { + return { + 'date': '当前日期 / Current date (YYYY-MM-DD)', + 'dateWithIcon': '带图标的当前日期 / Current date with daily icon', + 'weekday': '当前星期 / Current weekday', + 'yearProgress': '年度进度百分比 / Year progress percentage', + 'monthProgress': '月度进度百分比 / Month progress percentage', + 'time': '当前时间 / Current time (HH:MM)' + }; +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..4d44352 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "inlineSourceMap": true, + "inlineSources": true, + "module": "ESNext", + "target": "ES2017", + "allowJs": true, + "noImplicitAny": false, + "moduleResolution": "node", + "importHelpers": true, + "isolatedModules": true, + "strictNullChecks": true, + "skipLibCheck": true, + "lib": [ + "DOM", + "ES5", + "ES6", + "ES7", + "ES2017" + ] + }, + "include": [ + "**/*.ts" + ] +} \ No newline at end of file diff --git a/version-bump.mjs b/version-bump.mjs new file mode 100644 index 0000000..f222d70 --- /dev/null +++ b/version-bump.mjs @@ -0,0 +1,18 @@ +import { readFileSync, writeFileSync } from "fs"; + +const targetVersion = process.argv[2]; +const minAppVersion = process.argv[3]; + +// read minAppVersion from manifest.json if it is not provided +let manifest = JSON.parse(readFileSync("manifest.json", "utf8")); +const { minAppVersion: currentMinAppVersion } = manifest; +manifest.version = targetVersion; +if (minAppVersion) { + manifest.minAppVersion = minAppVersion; +} +writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t")); + +// update versions.json with target version and minAppVersion +let versions = JSON.parse(readFileSync("versions.json", "utf8")); +versions[targetVersion] = minAppVersion || currentMinAppVersion; +writeFileSync("versions.json", JSON.stringify(versions, null, "\t")); \ No newline at end of file diff --git a/versions.json b/versions.json new file mode 100644 index 0000000..0c4d3ac --- /dev/null +++ b/versions.json @@ -0,0 +1,3 @@ +{ + "1.0.0": "0.15.0" +} \ No newline at end of file