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