From 540ea3e28a6b0a56f0225e26208909e7763bf061 Mon Sep 17 00:00:00 2001
From: maigamo <13341529504@163.com>
Date: Wed, 16 Apr 2025 16:46:40 +0800
Subject: [PATCH] v0.0.1 init
---
main.js | 1063 +++++++++++++++++++++++++++++++++++++++++++++++++
main.ts.bak | 332 +++++++++++++++
manifest.json | 10 +
styles.css | 413 +++++++++++++++++++
4 files changed, 1818 insertions(+)
create mode 100644 main.js
create mode 100644 main.ts.bak
create mode 100644 manifest.json
create mode 100644 styles.css
diff --git a/main.js b/main.js
new file mode 100644
index 0000000..ce6487b
--- /dev/null
+++ b/main.js
@@ -0,0 +1,1063 @@
+/*
+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 = `## {{date}}\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
+
+### \u{1F319} \u665A\u95F4\u56DE\u987E
+---
+
+- \u4ECA\u65E5\u5B8C\u6210\u5EA6: __%
+- \u660E\u65E5\u8BA1\u5212:`;
+var DEFAULT_TEMPLATE_EN = `## {{date}} ({{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
+
+### \u{1F319} Evening Review
+---
+
+- Today's completion rate: __%
+- Tomorrow's plan:`;
+var DEFAULT_SETTINGS = {
+ rootDir: "DailyTasks",
+ autoGenerateMode: "none" /* NONE */,
+ language: "auto" /* AUTO */,
+ templateZh: DEFAULT_TEMPLATE_ZH,
+ templateEn: DEFAULT_TEMPLATE_EN,
+ successNotificationDuration: 3e3
+};
+
+// src/settings/settings.ts
+var import_obsidian3 = require("obsidian");
+
+// src/i18n/i18n.ts
+var zhTranslations = {
+ // 设置页面
+ "settings.title": "\u{1F4C5} \u6BCF\u65E5\u4EFB\u52A1\u81EA\u52A8\u751F\u6210\u5668",
+ "settings.rootDir": "\u{1F4C1} \u4EFB\u52A1\u6587\u4EF6\u5B58\u653E\u76EE\u5F55",
+ "settings.rootDir.desc": "\u751F\u6210\u7684\u4EFB\u52A1\u6587\u4EF6\u5C06\u4FDD\u5B58\u5728\u6B64\u76EE\u5F55\u4E0B",
+ "settings.rootDir.saved": "\u2705 \u8DEF\u5F84\u8BBE\u7F6E\u5DF2\u4FDD\u5B58",
+ "settings.save": "\u4FDD\u5B58",
+ "settings.autoGenerate": "\u2699\uFE0F \u81EA\u52A8\u751F\u6210\u6A21\u5F0F",
+ "settings.autoGenerate.desc": "\u8BBE\u7F6E\u4F55\u65F6\u81EA\u52A8\u751F\u6210\u4EFB\u52A1",
+ "settings.mode.none": "\u5173\u95ED",
+ "settings.mode.daily": "\u6BCF\u65E5\u81EA\u52A8\u751F\u6210",
+ "settings.mode.workday": "\u4EC5\u5DE5\u4F5C\u65E5\u81EA\u52A8\u751F\u6210",
+ "settings.language": "\u{1F310} \u754C\u9762\u8BED\u8A00",
+ "settings.language.desc": "\u8BBE\u7F6E\u63D2\u4EF6\u754C\u9762\u8BED\u8A00\uFF08\u9700\u91CD\u542FObsidian\u751F\u6548\uFF09",
+ "settings.language.auto": "\u81EA\u52A8\u68C0\u6D4B",
+ "settings.language.zh": "\u4E2D\u6587",
+ "settings.language.en": "\u82F1\u6587",
+ "settings.animations": "UI\u52A8\u753B\u6548\u679C",
+ "settings.animations.desc": "\u542F\u7528\u6216\u7981\u7528\u754C\u9762\u52A8\u753B\u6548\u679C",
+ "settings.template": "\u{1F4DD} \u4EFB\u52A1\u6A21\u677F\u8BBE\u7F6E",
+ "settings.template.zh": "\u4E2D\u6587\u6A21\u677F\uFF08\u5F53\u524D\u73AF\u5883\u4E3A\u4E2D\u6587\u65F6\u9ED8\u8BA4\u4F7F\u7528\uFF09",
+ "settings.template.en": "\u82F1\u6587\u6A21\u677F\uFF08\u5F53\u524D\u73AF\u5883\u4E3A\u82F1\u6587\u65F6\u9ED8\u8BA4\u4F7F\u7528\uFF09",
+ "settings.template.preview": "\u67E5\u770B\u9884\u89C8",
+ "settings.template.hide": "\u6536\u8D77\u9884\u89C8",
+ "settings.resetDefault": "\u6062\u590D\u9ED8\u8BA4",
+ "settings.addTaskButton": "\u{1F4CB} \u624B\u52A8\u6DFB\u52A0\u4ECA\u65E5\u4EFB\u52A1",
+ "settings.resetToDefault": "\u{1F504} \u6062\u590D\u9ED8\u8BA4\u8BBE\u7F6E",
+ "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",
+ // 通知
+ "notification.taskAdded": "\u5DF2\u6210\u529F\u6DFB\u52A0\u4ECA\u65E5\u4EFB\u52A1",
+ "notification.taskExists": "\u4ECA\u65E5\u4EFB\u52A1\u5DF2\u5B58\u5728",
+ "notification.error": "\u64CD\u4F5C\u51FA\u9519\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"
+};
+var enTranslations = {
+ // 设置页面
+ "settings.title": "\u{1F4C5} Daily Task Auto Generator",
+ "settings.rootDir": "\u{1F4C1} Task Directory",
+ "settings.rootDir.desc": "Generated tasks will be saved in this directory",
+ "settings.rootDir.saved": "\u2705 Path settings saved",
+ "settings.save": "Save",
+ "settings.autoGenerate": "\u2699\uFE0F Auto Generate Mode",
+ "settings.autoGenerate.desc": "Set when to automatically generate tasks",
+ "settings.mode.none": "Off",
+ "settings.mode.daily": "Daily",
+ "settings.mode.workday": "Workdays Only",
+ "settings.language": "\u{1F310} Language",
+ "settings.language.desc": "Set plugin interface language (requires Obsidian restart)",
+ "settings.language.auto": "Auto Detect",
+ "settings.language.zh": "Chinese",
+ "settings.language.en": "English",
+ "settings.animations": "UI Animations",
+ "settings.animations.desc": "Enable or disable interface animations",
+ "settings.template": "\u{1F4DD} Task Template Settings",
+ "settings.template.zh": "Chinese Template (used by default in Chinese environment)",
+ "settings.template.en": "English Template (used by default in English environment)",
+ "settings.template.preview": "Show Preview",
+ "settings.template.hide": "Hide Preview",
+ "settings.resetDefault": "Reset to Default",
+ "settings.addTaskButton": "\u{1F4CB} Add Today's Tasks Manually",
+ "settings.resetToDefault": "\u{1F504} Reset to Default Settings",
+ "settings.notificationDuration": "\u23F1\uFE0F Notification Duration",
+ "settings.notificationDuration.desc": "Duration to show success/failure notifications (milliseconds)",
+ // 通知
+ "notification.taskAdded": "Today's tasks added successfully",
+ "notification.taskExists": "Today's tasks already exist",
+ "notification.error": "Error: ",
+ // 星期
+ "weekday.mon": "Monday",
+ "weekday.tue": "Tuesday",
+ "weekday.wed": "Wednesday",
+ "weekday.thu": "Thursday",
+ "weekday.fri": "Friday",
+ "weekday.sat": "Saturday",
+ "weekday.sun": "Sunday"
+};
+var translations = {
+ "zh": zhTranslations,
+ "en": enTranslations
+};
+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 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 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 getDefaultTemplateVariables() {
+ return {
+ date: getCurrentDate(),
+ weekday: getCurrentWeekdayName(),
+ yearProgress: getYearProgress(),
+ monthProgress: getMonthProgress(),
+ time: new Date().toLocaleTimeString()
+ };
+}
+function renderTemplate(template, variables = getDefaultTemplateVariables()) {
+ return template.replace(/\{\{([^}]+)\}\}/g, (match, variableName) => {
+ const trimmedName = variableName.trim();
+ if (trimmedName in variables) {
+ return String(variables[trimmedName]);
+ }
+ return match;
+ });
+}
+
+// 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;
+ }
+}
+async function fileContains(vault, path, content) {
+ try {
+ const file = vault.getAbstractFileByPath(path);
+ if (file && file instanceof import_obsidian.TFile) {
+ try {
+ const currentContent = await vault.read(file);
+ return currentContent.includes(content);
+ } catch (e) {
+ console.error(`\u8BFB\u53D6\u6587\u4EF6\u5185\u5BB9\u65F6\u51FA\u9519: ${e}`);
+ return false;
+ }
+ }
+ console.log(`\u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u65E0\u6CD5\u68C0\u67E5\u5185\u5BB9: ${path}`);
+ return false;
+ } catch (error) {
+ console.error(`Error checking file content 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}`);
+ return await fileContains(vault, taskFilePath, `## ${date}`);
+ } catch (error) {
+ console.error(`\u68C0\u67E5\u4ECA\u65E5\u4EFB\u52A1\u65F6\u51FA\u9519:`, error);
+ return false;
+ }
+}
+
+// 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 existingTaskCheck = await fileContains(this.vault, filePath, `## ${date}`);
+ 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;
+ }
+ const template = this.settingsManager.getCurrentTemplate();
+ const renderedContent = renderTemplate(template);
+ if (!quietMode)
+ console.log(`\u6B63\u5728\u5411\u6587\u4EF6\u8FFD\u52A0\u5185\u5BB9`);
+ const success = await appendToFile(this.vault, filePath, renderedContent);
+ 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;
+ }
+ }
+ /**
+ * 手动添加今日任务
+ * @returns 成功或失败
+ */
+ async addTaskManually() {
+ try {
+ const settings = this.settingsManager.getSettings();
+ const rootDir = settings.rootDir || "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 DailyTaskSettingTab = class extends import_obsidian3.PluginSettingTab {
+ constructor(app, plugin) {
+ super(app, plugin);
+ this.previewEl = null;
+ this.addTaskButton = null;
+ // 模板预览元素
+ this.zhPreviewEl = null;
+ this.enPreviewEl = 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;
+ });
+ containerEl.createEl("h3", { text: getTranslation("settings.template") });
+ const templateDescription = containerEl.createEl("p", {
+ text: this.settingsManager.getCurrentLanguage() === "zh" ? "\u6CE8\u610F\uFF1A\u63D2\u4EF6\u4F1A\u6839\u636E\u5F53\u524D\u8BED\u8A00\u73AF\u5883\u81EA\u52A8\u9009\u62E9\u5BF9\u5E94\u8BED\u8A00\u7684\u4EFB\u52A1\u6A21\u677F\u3002\u5982\u679C\u60A8\u4FEE\u6539\u4E86\u4EFB\u4E00\u6A21\u677F\uFF0C\u5219\u4F1A\u4F7F\u7528\u60A8\u81EA\u5B9A\u4E49\u7684\u6A21\u677F\uFF0C\u4E0D\u518D\u533A\u5206\u8BED\u8A00\u3002" : "Note: The plugin automatically selects the template based on current language environment. If you modify either template, your customized template will be used regardless of language."
+ });
+ templateDescription.style.fontSize = "0.85em";
+ templateDescription.style.opacity = "0.8";
+ templateDescription.style.marginBottom = "15px";
+ const zhTemplateSetting = new import_obsidian3.Setting(containerEl).setName(getTranslation("settings.template.zh")).setClass("template-setting");
+ const zhTemplateContainer = document.createElement("div");
+ zhTemplateContainer.style.width = "100%";
+ const zhTextarea = new import_obsidian3.TextAreaComponent(zhTemplateContainer).setValue(settings.templateZh).setPlaceholder("\u5728\u6B64\u5904\u8F93\u5165\u4E2D\u6587\u6A21\u677F...").onChange(async (value) => {
+ await this.settingsManager.updateSettings({ templateZh: value });
+ this.updatePreview(this.zhPreviewEl, value);
+ });
+ zhTextarea.inputEl.classList.add("template-editor");
+ const zhPreviewHeader = document.createElement("div");
+ zhPreviewHeader.classList.add("template-preview-header");
+ zhPreviewHeader.style.display = "flex";
+ zhPreviewHeader.style.justifyContent = "center";
+ const zhToggleButton = new import_obsidian3.ButtonComponent(zhPreviewHeader).setButtonText(getTranslation("settings.template.preview"));
+ const eyeIcon = createSpan({ cls: "svg-icon lucide-eye" });
+ zhToggleButton.buttonEl.prepend(eyeIcon);
+ zhToggleButton.buttonEl.style.display = "flex";
+ zhToggleButton.buttonEl.style.alignItems = "center";
+ zhToggleButton.buttonEl.style.justifyContent = "center";
+ zhToggleButton.buttonEl.style.textAlign = "center";
+ zhToggleButton.buttonEl.style.width = "120px";
+ zhToggleButton.onClick(() => {
+ this.togglePreview(this.zhPreviewEl);
+ if (this.zhPreviewEl && this.zhPreviewEl.classList.contains("visible")) {
+ eyeIcon.className = "svg-icon lucide-eye-off";
+ zhToggleButton.setButtonText(getTranslation("settings.template.hide"));
+ } else {
+ eyeIcon.className = "svg-icon lucide-eye";
+ zhToggleButton.setButtonText(getTranslation("settings.template.preview"));
+ }
+ });
+ const zhResetBtn = new import_obsidian3.ButtonComponent(zhPreviewHeader).setButtonText(getTranslation("settings.resetDefault")).onClick(async () => {
+ await this.settingsManager.updateSettings({ templateZh: DEFAULT_TEMPLATE_ZH });
+ zhTextarea.setValue(DEFAULT_TEMPLATE_ZH);
+ this.updatePreview(this.zhPreviewEl, DEFAULT_TEMPLATE_ZH);
+ });
+ zhResetBtn.buttonEl.prepend(createSpan({ cls: "svg-icon lucide-refresh-cw" }));
+ zhResetBtn.buttonEl.style.display = "flex";
+ zhResetBtn.buttonEl.style.alignItems = "center";
+ zhResetBtn.buttonEl.style.justifyContent = "center";
+ zhResetBtn.buttonEl.style.textAlign = "center";
+ zhResetBtn.buttonEl.style.width = "120px";
+ zhTemplateContainer.appendChild(zhPreviewHeader);
+ this.zhPreviewEl = document.createElement("div");
+ this.zhPreviewEl.classList.add("template-preview");
+ this.updatePreview(this.zhPreviewEl, settings.templateZh);
+ zhTemplateContainer.appendChild(this.zhPreviewEl);
+ zhTemplateSetting.controlEl.appendChild(zhTemplateContainer);
+ const enTemplateSetting = new import_obsidian3.Setting(containerEl).setName(getTranslation("settings.template.en")).setClass("template-setting");
+ const enTemplateContainer = document.createElement("div");
+ enTemplateContainer.style.width = "100%";
+ const enTextarea = new import_obsidian3.TextAreaComponent(enTemplateContainer).setValue(settings.templateEn).setPlaceholder("Enter English template here...").onChange(async (value) => {
+ await this.settingsManager.updateSettings({ templateEn: value });
+ this.updatePreview(this.enPreviewEl, value);
+ });
+ enTextarea.inputEl.classList.add("template-editor");
+ const enPreviewHeader = document.createElement("div");
+ enPreviewHeader.classList.add("template-preview-header");
+ enPreviewHeader.style.display = "flex";
+ enPreviewHeader.style.justifyContent = "center";
+ const enToggleButton = new import_obsidian3.ButtonComponent(enPreviewHeader).setButtonText(getTranslation("settings.template.preview"));
+ const enEyeIcon = createSpan({ cls: "svg-icon lucide-eye" });
+ enToggleButton.buttonEl.prepend(enEyeIcon);
+ enToggleButton.buttonEl.style.display = "flex";
+ enToggleButton.buttonEl.style.alignItems = "center";
+ enToggleButton.buttonEl.style.justifyContent = "center";
+ enToggleButton.buttonEl.style.textAlign = "center";
+ enToggleButton.buttonEl.style.width = "120px";
+ enToggleButton.onClick(() => {
+ this.togglePreview(this.enPreviewEl);
+ if (this.enPreviewEl && this.enPreviewEl.classList.contains("visible")) {
+ enEyeIcon.className = "svg-icon lucide-eye-off";
+ enToggleButton.setButtonText(getTranslation("settings.template.hide"));
+ } else {
+ enEyeIcon.className = "svg-icon lucide-eye";
+ enToggleButton.setButtonText(getTranslation("settings.template.preview"));
+ }
+ });
+ const enResetBtn = new import_obsidian3.ButtonComponent(enPreviewHeader).setButtonText(getTranslation("settings.resetDefault")).onClick(async () => {
+ await this.settingsManager.updateSettings({ templateEn: DEFAULT_TEMPLATE_EN });
+ enTextarea.setValue(DEFAULT_TEMPLATE_EN);
+ this.updatePreview(this.enPreviewEl, DEFAULT_TEMPLATE_EN);
+ });
+ enResetBtn.buttonEl.prepend(createSpan({ cls: "svg-icon lucide-refresh-cw" }));
+ enResetBtn.buttonEl.style.display = "flex";
+ enResetBtn.buttonEl.style.alignItems = "center";
+ enResetBtn.buttonEl.style.justifyContent = "center";
+ enResetBtn.buttonEl.style.textAlign = "center";
+ enResetBtn.buttonEl.style.width = "120px";
+ enTemplateContainer.appendChild(enPreviewHeader);
+ this.enPreviewEl = document.createElement("div");
+ this.enPreviewEl.classList.add("template-preview");
+ this.updatePreview(this.enPreviewEl, settings.templateEn);
+ enTemplateContainer.appendChild(this.enPreviewEl);
+ enTemplateSetting.controlEl.appendChild(enTemplateContainer);
+ 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")).onClick(async () => {
+ await this.settingsManager.resetToDefaults();
+ this.display();
+ });
+ resetDefaultBtn.buttonEl.addClass("danger-button");
+ resetDefaultBtn.buttonEl.prepend(createSpan({ cls: "svg-icon lucide-refresh-cw" }));
+ resetDefaultBtn.buttonEl.style.display = "flex";
+ resetDefaultBtn.buttonEl.style.alignItems = "center";
+ resetDefaultBtn.buttonEl.style.justifyContent = "center";
+ 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().onClick(async () => {
+ var _a, _b;
+ const rootDir = this.settingsManager.getSettings().rootDir;
+ (_a = this.addTaskButton) == null ? void 0 : _a.buttonEl.classList.add("loading");
+ (_b = this.addTaskButton) == null ? void 0 : _b.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, _b2;
+ (_a2 = this.addTaskButton) == null ? void 0 : _a2.buttonEl.classList.remove("loading");
+ (_b2 = this.addTaskButton) == null ? void 0 : _b2.setDisabled(false);
+ }, 500);
+ }
+ });
+ this.addTaskButton.buttonEl.prepend(createSpan({ cls: "svg-icon lucide-calendar-plus" }));
+ this.addTaskButton.buttonEl.classList.add("add-task-button");
+ this.addTaskButton.buttonEl.style.display = "flex";
+ this.addTaskButton.buttonEl.style.alignItems = "center";
+ this.addTaskButton.buttonEl.style.justifyContent = "center";
+ }
+ /**
+ * 更新模板预览
+ */
+ updatePreview(previewEl, template) {
+ if (!previewEl)
+ return;
+ const renderedContent = renderTemplate(template);
+ previewEl.innerHTML = renderedContent.replace(/\n/g, "
");
+ }
+ /**
+ * 切换预览的显示/隐藏
+ */
+ togglePreview(previewEl) {
+ if (!previewEl)
+ return;
+ previewEl.classList.toggle("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);
+ }
+};
+
+// 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/main.ts.bak b/main.ts.bak
new file mode 100644
index 0000000..3550266
--- /dev/null
+++ b/main.ts.bak
@@ -0,0 +1,332 @@
+import { App, Plugin, PluginSettingTab, Setting, Notice, TFile, normalizePath } from 'obsidian';
+
+interface DailyTaskSettings {
+ enabled: boolean;
+ targetFolder: string;
+ templateFile: string;
+ schedule: {
+ time: string;
+ days: string[];
+ };
+ autoOpen: boolean;
+}
+
+const DEFAULT_SETTINGS: DailyTaskSettings = {
+ enabled: true,
+ targetFolder: '每日任务',
+ templateFile: '模板/每日任务模板.md',
+ schedule: {
+ time: '06:00',
+ days: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday']
+ },
+ autoOpen: true
+};
+
+export default class DailyTaskAutoGenerator extends Plugin {
+ settings: DailyTaskSettings;
+ timer: number | null = null;
+
+ async onload() {
+ await this.loadSettings();
+
+ this.addSettingTab(new DailyTaskSettingTab(this.app, this));
+
+ this.addRibbonIcon('calendar-plus', '生成今日任务', () => {
+ this.generateTodayTask();
+ });
+
+ this.addCommand({
+ id: 'generate-daily-task',
+ name: '生成今日任务',
+ callback: () => {
+ this.generateTodayTask();
+ }
+ });
+
+ // 设置定时任务
+ this.scheduleNextTask();
+ }
+
+ onunload() {
+ if (this.timer) {
+ window.clearTimeout(this.timer);
+ }
+ }
+
+ async loadSettings() {
+ this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
+ }
+
+ async saveSettings() {
+ await this.saveData(this.settings);
+ this.scheduleNextTask();
+ }
+
+ scheduleNextTask() {
+ if (this.timer) {
+ window.clearTimeout(this.timer);
+ this.timer = null;
+ }
+
+ if (!this.settings.enabled) {
+ return;
+ }
+
+ const now = new Date();
+ const [hours, minutes] = this.settings.schedule.time.split(':').map(Number);
+ const targetTime = new Date(now);
+ targetTime.setHours(hours, minutes, 0, 0);
+
+ // 如果目标时间已过,则设置为明天
+ if (targetTime <= now) {
+ targetTime.setDate(targetTime.getDate() + 1);
+ }
+
+ // 检查目标日期是否是设置的工作日
+ const days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
+ let dayToAdd = 0;
+
+ while (!this.settings.schedule.days.includes(days[targetTime.getDay()])) {
+ targetTime.setDate(targetTime.getDate() + 1);
+ dayToAdd++;
+
+ // 防止无限循环
+ if (dayToAdd > 7) {
+ break;
+ }
+ }
+
+ const timeUntilTask = targetTime.getTime() - now.getTime();
+
+ this.timer = window.setTimeout(() => {
+ this.generateTodayTask();
+ this.scheduleNextTask();
+ }, timeUntilTask);
+ }
+
+ async generateTodayTask() {
+ if (!this.settings.enabled) {
+ new Notice('每日任务自动生成功能已禁用。');
+ return;
+ }
+
+ // 准备文件夹
+ const folderPath = normalizePath(this.settings.targetFolder);
+ try {
+ await this.ensureFolderExists(folderPath);
+ } catch (error) {
+ new Notice(`无法创建目标文件夹: ${error}`);
+ return;
+ }
+
+ // 生成今日日期格式
+ const today = new Date();
+ const fileName = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}.md`;
+ const filePath = `${folderPath}/${fileName}`;
+
+ // 检查文件是否已存在
+ if (await this.app.vault.exists(filePath)) {
+ if (this.settings.autoOpen) {
+ this.openFile(filePath);
+ }
+ new Notice('今日任务文件已存在。');
+ return;
+ }
+
+ // 读取模板
+ try {
+ const templatePath = normalizePath(this.settings.templateFile);
+ if (!await this.app.vault.exists(templatePath)) {
+ new Notice('模板文件不存在!');
+ return;
+ }
+
+ let templateContent = await this.app.vault.read(this.app.vault.getAbstractFileByPath(templatePath) as TFile);
+
+ // 替换模板内容中的日期变量
+ templateContent = this.replaceTemplateVariables(templateContent, today);
+
+ // 创建新的任务文件
+ await this.app.vault.create(filePath, templateContent);
+ new Notice('成功创建今日任务文件!');
+
+ // 自动打开文件
+ if (this.settings.autoOpen) {
+ this.openFile(filePath);
+ }
+ } catch (error) {
+ new Notice(`创建任务文件失败: ${error}`);
+ }
+ }
+
+ replaceTemplateVariables(content: string, date: Date): string {
+ const year = date.getFullYear();
+ const month = String(date.getMonth() + 1).padStart(2, '0');
+ const day = String(date.getDate()).padStart(2, '0');
+
+ // 星期几(中文)
+ const weekdays = ['日', '一', '二', '三', '四', '五', '六'];
+ const weekday = weekdays[date.getDay()];
+
+ // 替换模板变量
+ content = content.replace(/{{date}}/g, `${year}-${month}-${day}`);
+ content = content.replace(/{{year}}/g, year.toString());
+ content = content.replace(/{{month}}/g, month);
+ content = content.replace(/{{day}}/g, day);
+ content = content.replace(/{{weekday}}/g, weekday);
+
+ return content;
+ }
+
+ async ensureFolderExists(path: string): Promise {
+ const folders = path.split('/');
+ let currentPath = '';
+
+ for (const folder of folders) {
+ if (!folder) continue;
+
+ currentPath = currentPath ? `${currentPath}/${folder}` : folder;
+
+ if (!(await this.app.vault.exists(currentPath))) {
+ await this.app.vault.createFolder(currentPath);
+ }
+ }
+ }
+
+ async openFile(path: string): Promise {
+ const file = this.app.vault.getAbstractFileByPath(path);
+ if (file && file instanceof TFile) {
+ await this.app.workspace.getLeaf().openFile(file);
+ }
+ }
+}
+
+class DailyTaskSettingTab extends PluginSettingTab {
+ plugin: DailyTaskAutoGenerator;
+
+ constructor(app: App, plugin: DailyTaskAutoGenerator) {
+ super(app, plugin);
+ this.plugin = plugin;
+ }
+
+ display(): void {
+ const {containerEl} = this;
+ containerEl.empty();
+ containerEl.addClass('daily-task-settings');
+
+ new Setting(containerEl)
+ .setName('启用')
+ .setDesc('启用每日任务自动生成')
+ .addToggle(toggle => toggle
+ .setValue(this.plugin.settings.enabled)
+ .onChange(async (value) => {
+ this.plugin.settings.enabled = value;
+ await this.plugin.saveSettings();
+ }));
+
+ new Setting(containerEl)
+ .setName('目标文件夹')
+ .setDesc('指定任务文件将被创建的文件夹路径')
+ .addText(text => text
+ .setPlaceholder('例如: 任务/每日')
+ .setValue(this.plugin.settings.targetFolder)
+ .onChange(async (value) => {
+ this.plugin.settings.targetFolder = value;
+ await this.plugin.saveSettings();
+ }));
+
+ new Setting(containerEl)
+ .setName('模板文件')
+ .setDesc('指定用于生成任务的模板文件路径')
+ .addText(text => text
+ .setPlaceholder('例如: 模板/每日任务模板.md')
+ .setValue(this.plugin.settings.templateFile)
+ .onChange(async (value) => {
+ this.plugin.settings.templateFile = value;
+ await this.plugin.saveSettings();
+ }));
+
+ new Setting(containerEl)
+ .setName('生成时间')
+ .setDesc('指定每日任务生成的时间 (24小时制)')
+ .addText(text => text
+ .setPlaceholder('HH:MM')
+ .setValue(this.plugin.settings.schedule.time)
+ .onChange(async (value) => {
+ this.plugin.settings.schedule.time = value;
+ await this.plugin.saveSettings();
+ }));
+
+ const daysContainer = containerEl.createDiv();
+ daysContainer.addClass('setting-item');
+ const daysHeading = daysContainer.createDiv();
+ daysHeading.addClass('setting-item-heading');
+ daysHeading.setText('生成日期');
+ const daysDescription = daysContainer.createDiv();
+ daysDescription.addClass('setting-item-description');
+ daysDescription.setText('选择哪些天生成任务');
+
+ const daysGrid = daysContainer.createDiv();
+ daysGrid.style.display = 'grid';
+ daysGrid.style.gridTemplateColumns = 'repeat(auto-fill, minmax(120px, 1fr))';
+ daysGrid.style.gap = '8px';
+ daysGrid.style.marginTop = '8px';
+
+ const days = [
+ { id: 'monday', label: '周一' },
+ { id: 'tuesday', label: '周二' },
+ { id: 'wednesday', label: '周三' },
+ { id: 'thursday', label: '周四' },
+ { id: 'friday', label: '周五' },
+ { id: 'saturday', label: '周六' },
+ { id: 'sunday', label: '周日' }
+ ];
+
+ days.forEach(day => {
+ const dayItem = daysGrid.createDiv();
+ dayItem.addClass('day-checkbox');
+
+ const checkbox = dayItem.createEl('input');
+ checkbox.type = 'checkbox';
+ checkbox.id = day.id;
+ checkbox.checked = this.plugin.settings.schedule.days.includes(day.id);
+
+ const label = dayItem.createEl('label');
+ label.htmlFor = day.id;
+ label.textContent = day.label;
+ label.style.marginLeft = '8px';
+
+ checkbox.addEventListener('change', async () => {
+ if (checkbox.checked) {
+ if (!this.plugin.settings.schedule.days.includes(day.id)) {
+ this.plugin.settings.schedule.days.push(day.id);
+ }
+ } else {
+ this.plugin.settings.schedule.days = this.plugin.settings.schedule.days.filter(d => d !== day.id);
+ }
+ await this.plugin.saveSettings();
+ });
+ });
+
+ new Setting(containerEl)
+ .setName('自动打开')
+ .setDesc('生成任务后自动打开文件')
+ .addToggle(toggle => toggle
+ .setValue(this.plugin.settings.autoOpen)
+ .onChange(async (value) => {
+ this.plugin.settings.autoOpen = value;
+ await this.plugin.saveSettings();
+ }));
+
+ const buttonContainer = containerEl.createDiv();
+ buttonContainer.style.marginTop = '24px';
+ buttonContainer.style.textAlign = 'center';
+
+ const generateButton = buttonContainer.createEl('button');
+ generateButton.addClass('daily-task-btn');
+ generateButton.textContent = '立即生成今日任务';
+ generateButton.addEventListener('click', () => {
+ this.plugin.generateTodayTask();
+ });
+ }
+}
\ No newline at end of file
diff --git a/manifest.json b/manifest.json
new file mode 100644
index 0000000..90704a0
--- /dev/null
+++ b/manifest.json
@@ -0,0 +1,10 @@
+{
+ "id": "daily-task-auto-generator",
+ "name": "Daily Task Auto Generator",
+ "version": "1.0.0",
+ "minAppVersion": "0.15.0",
+ "description": "Automatically generate daily tasks in specified folders with custom templates",
+ "author": "maigamo",
+ "authorUrl": "https://github.com/maigamo",
+ "isDesktopOnly": false
+}
\ No newline at end of file
diff --git a/styles.css b/styles.css
new file mode 100644
index 0000000..c4c67da
--- /dev/null
+++ b/styles.css
@@ -0,0 +1,413 @@
+/* 基础样式 */
+.daily-task-setting-tab {
+ padding: 10px 30px;
+ transition: all 0.3s ease;
+ max-width: 800px;
+ margin: 0 auto;
+}
+
+/* 设置页面标题 */
+.daily-task-setting-tab h2 {
+ margin-bottom: 20px;
+ padding-bottom: 10px;
+}
+
+.daily-task-setting-tab h3 {
+ margin-top: 30px;
+ margin-bottom: 15px;
+ padding-bottom: 5px;
+ border-bottom: 1px solid var(--background-modifier-border);
+}
+
+/* 三选滑块 */
+.mode-toggle-container {
+ display: flex;
+ border: 1px solid var(--background-modifier-border);
+ border-radius: 6px;
+ overflow: hidden;
+ margin: 10px 0;
+ position: relative;
+ min-width: 450px; /* 增加最小宽度以确保中文显示完整 */
+ width: 100%;
+ max-width: 100%;
+}
+
+.mode-toggle-option {
+ flex: 1;
+ text-align: center;
+ padding: 8px 12px;
+ cursor: pointer;
+ z-index: 1;
+ transition: all 0.3s ease;
+ white-space: nowrap;
+ min-width: 130px; /* 增加最小宽度以确保中文显示完整 */
+ font-size: 0.9em;
+}
+
+.mode-toggle-option:hover {
+ background-color: var(--background-modifier-hover);
+}
+
+.mode-toggle-option.active {
+ color: var(--text-on-accent);
+ font-weight: bold;
+}
+
+.mode-toggle-slider {
+ position: absolute;
+ height: 100%;
+ width: 33.33%;
+ background-color: var(--interactive-accent);
+ border-radius: 5px;
+ transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
+}
+
+/* 设置项动画 */
+.setting-item {
+ padding: 16px 0; /* 增加垂直间距 */
+ margin-bottom: 8px;
+ border-bottom: 1px solid var(--background-modifier-border-subtle);
+}
+
+.setting-item:last-child {
+ border-bottom: none;
+}
+
+/* 设置项标题和描述间距 */
+.setting-item-info {
+ padding-right: 20px; /* 增加右侧空间 */
+}
+
+.setting-item-description {
+ margin-top: 6px;
+}
+
+/* 设置控件布局 */
+.setting-item-control {
+ margin-top: 10px; /* 增加控件与描述间距 */
+}
+
+/* 输入框样式优化 */
+.setting-item-control input[type="text"] {
+ min-width: 300px;
+ width: 100%;
+ max-width: 100%;
+ padding: 8px 10px;
+ border-radius: 4px;
+}
+
+/* 按钮基本样式 */
+button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ white-space: nowrap;
+ text-align: center;
+}
+
+/* 保存按钮 - Obsidian紫色 */
+.save-button {
+ background-color: var(--interactive-accent) !important;
+ color: var(--text-on-accent) !important;
+ font-weight: 500;
+}
+
+/* 危险操作按钮 - 红色 */
+.danger-button {
+ background-color: #e53935 !important;
+ color: white !important;
+ font-weight: 500;
+}
+
+/* 添加任务按钮 */
+.add-task-button {
+ display: inline-flex !important;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ color: var(--text-on-accent);
+ background-color: var(--interactive-accent);
+ padding: 8px 16px;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ font-weight: 500;
+ margin-top: 20px; /* 增加顶部间距 */
+ text-align: center;
+}
+
+.add-task-button:hover {
+ transform: scale(1.05);
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+}
+
+.add-task-button:active {
+ transform: scale(0.95);
+}
+
+.add-task-button.loading {
+ position: relative;
+ padding-left: 35px;
+}
+
+.add-task-button.loading:before {
+ content: '';
+ position: absolute;
+ left: 12px;
+ top: 50%;
+ margin-top: -8px;
+ width: 16px;
+ height: 16px;
+ border: 2px solid rgba(255, 255, 255, 0.3);
+ border-top-color: white;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+}
+
+.add-task-button.disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+@keyframes spin {
+ to { transform: rotate(360deg); }
+}
+
+/* 通知动画 */
+.notice {
+ opacity: 0;
+ transform: translateY(20px);
+ animation: fadeInUpNotice 0.3s forwards;
+}
+
+@keyframes fadeInUpNotice {
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+/* 不同类型的通知样式 */
+.daily-task-success-notice {
+ background-color: rgba(46, 160, 67, 0.9) !important;
+ color: white !important;
+ border-left: 4px solid #2ea043 !important;
+ display: flex !important;
+ align-items: center !important;
+ padding-left: 12px !important;
+}
+
+.daily-task-success-notice::before {
+ content: '✓';
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ line-height: 20px;
+ text-align: center;
+ margin-right: 8px;
+ font-weight: bold;
+ background-color: rgba(255, 255, 255, 0.2);
+ border-radius: 50%;
+}
+
+.daily-task-warning-notice {
+ background-color: rgba(209, 154, 10, 0.9) !important;
+ color: white !important;
+ border-left: 4px solid #d19a0a !important;
+ display: flex !important;
+ align-items: center !important;
+ padding-left: 12px !important;
+}
+
+.daily-task-warning-notice::before {
+ content: '⚠';
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ line-height: 20px;
+ text-align: center;
+ margin-right: 8px;
+ font-weight: bold;
+ background-color: rgba(255, 255, 255, 0.2);
+ border-radius: 50%;
+}
+
+.daily-task-error-notice {
+ background-color: rgba(203, 36, 49, 0.9) !important;
+ color: white !important;
+ border-left: 4px solid #cb2431 !important;
+ display: flex !important;
+ align-items: center !important;
+ padding-left: 12px !important;
+}
+
+.daily-task-error-notice::before {
+ content: '✗';
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ line-height: 20px;
+ text-align: center;
+ margin-right: 8px;
+ font-weight: bold;
+ background-color: rgba(255, 255, 255, 0.2);
+ border-radius: 50%;
+}
+
+/* 模板预览容器 */
+.template-preview {
+ background-color: var(--background-primary);
+ border: 1px solid var(--background-modifier-border);
+ border-radius: 5px;
+ padding: 15px;
+ margin-top: 10px;
+ max-height: 200px;
+ overflow-y: auto;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+ display: none;
+}
+
+.template-preview.visible {
+ display: block;
+}
+
+/* 模板编辑区域 */
+.template-editor {
+ height: 150px;
+ width: 100%;
+ font-family: var(--font-monospace);
+ border: 1px solid var(--background-modifier-border);
+ border-radius: 5px;
+ padding: 10px;
+ transition: border 0.3s ease;
+ resize: vertical;
+ min-height: 100px;
+ max-height: 300px;
+ box-sizing: border-box;
+ margin-bottom: 8px; /* 增加底部间距 */
+}
+
+.template-editor:focus {
+ border-color: var(--interactive-accent);
+ box-shadow: 0 0 0 2px rgba(var(--interactive-accent-rgb), 0.2);
+}
+
+/* 模板设置容器样式 */
+.template-setting {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ margin-bottom: 30px; /* 增加模板组之间的间距 */
+}
+
+.template-setting .setting-item-control {
+ width: 100%;
+}
+
+/* 模板容器宽度 */
+.template-setting > div {
+ width: 100%;
+}
+
+/* 元素悬停效果 */
+.clickable-icon, button, .setting-item-control > *:not(.disabled) {
+ transition: transform 0.2s ease, opacity 0.2s ease;
+}
+
+.clickable-icon:hover, button:hover, .setting-item-control > *:not(.disabled):hover {
+ transform: translateY(-2px);
+}
+
+.clickable-icon:active, button:active, .setting-item-control > *:not(.disabled):active {
+ transform: translateY(0);
+}
+
+/* 禁用动画 */
+.daily-task-no-animations * {
+ animation: none !important;
+ transition: none !important;
+ transform: none !important;
+ box-shadow: none !important;
+}
+
+/* 模板预览头部样式 */
+.template-preview-header {
+ display: flex;
+ justify-content: center !important;
+ align-items: center;
+ gap: 12px;
+ margin-top: 10px;
+ margin-bottom: 8px;
+}
+
+.template-preview-header button {
+ font-size: 12px;
+ padding: 6px 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 6px;
+ min-width: 100px;
+ border-radius: 4px;
+ text-align: center;
+}
+
+.template-preview-header .svg-icon {
+ width: 16px;
+ height: 16px;
+}
+
+/* 图标样式 */
+.svg-icon {
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ margin-right: 4px;
+ color: currentColor;
+}
+
+/* 移动端适配 */
+@media (max-width: 768px) {
+ .daily-task-setting-tab {
+ padding: 10px 15px;
+ }
+
+ .mode-toggle-container {
+ min-width: unset;
+ width: 100%;
+ }
+
+ .mode-toggle-option {
+ min-width: unset;
+ padding: 8px 4px;
+ font-size: 0.8em;
+ }
+
+ .setting-item {
+ padding: 12px 0;
+ }
+
+ .setting-item-control input[type="text"] {
+ min-width: unset;
+ width: 100%;
+ }
+
+ .setting-item-control {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ width: 100%;
+ margin-top: 8px;
+ }
+
+ /* 按钮在移动端垂直排列 */
+ .template-preview-header {
+ flex-direction: column;
+ align-items: flex-end;
+ gap: 8px;
+ }
+
+ .template-preview-header button {
+ width: 100%;
+ }
+}
\ No newline at end of file