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