diff --git a/.obsidian/community-plugins.json b/.obsidian/community-plugins.json index dfb711a7..d9f3b53f 100644 --- a/.obsidian/community-plugins.json +++ b/.obsidian/community-plugins.json @@ -56,5 +56,6 @@ "editing-toolbar", "obsidian-banners", "simpread", - "obsidian-timeline" + "obsidian-timeline", + "generate-timeline" ] \ No newline at end of file diff --git a/.obsidian/plugins/cubox-sync/data.json b/.obsidian/plugins/cubox-sync/data.json index f53a631a..2ff5dc7e 100644 --- a/.obsidian/plugins/cubox-sync/data.json +++ b/.obsidian/plugins/cubox-sync/data.json @@ -34,7 +34,7 @@ "contentTemplate": "# {{{title}}}\n\n{{{description}}}\n\n{{{article_title}}} //文章内容标题\n{{{content}}} //文章正文\n\n\n[Read in Cubox]({{{cubox_url}}}) \n[Read Original]({{{url}}}) \n\n---\n\n{{#highlights.length}}\n## Annotations \n\n{{#highlights}}\n> {{{text}}} \n\n{{#note}}\n{{{note}}}\n{{/note}}\n[Link️]({{{cubox_url}}}) \n\n{{/highlights}}\n{{/highlights.length}}", "highlightInContent": true, "dateFormat": "yyyy-MM-dd", - "lastSyncTime": 1755913936658, + "lastSyncTime": 1755915736396, "lastSyncCardId": "7358083230366436785", "lastCardUpdateTime": "2025-08-21T13:52:28.950+0800", "syncing": false diff --git a/.obsidian/plugins/generate-timeline/main.js b/.obsidian/plugins/generate-timeline/main.js new file mode 100644 index 00000000..84ec4dda --- /dev/null +++ b/.obsidian/plugins/generate-timeline/main.js @@ -0,0 +1,1265 @@ +/* +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 __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +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); +var __publicField = (obj, key, value) => { + __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); + return value; +}; + +// src/main.ts +var main_exports = {}; +__export(main_exports, { + MetadataInputModal: () => MetadataInputModal, + default: () => TimelinePlugin +}); +module.exports = __toCommonJS(main_exports); +var import_obsidian6 = require("obsidian"); + +// src/i18n.ts +var zhCN = { + settings: { + resetSettings: "\u91CD\u7F6E\u8BBE\u7F6E", + resetSettingsDesc: "\u5C06\u6240\u6709\u8BBE\u7F6E\u6062\u590D\u4E3A\u9ED8\u8BA4\u503C", + resetSettingsButton: "\u91CD\u7F6E\u4E3A\u9ED8\u8BA4\u503C", + lineWidth: "\u65F6\u95F4\u8F74\u7EBF\u5BBD\u5EA6", + lineWidthDesc: "\u8BBE\u7F6E\u4E3B\u65F6\u95F4\u8F74\u7EBF\u7684\u5BBD\u5EA6\uFF08\u50CF\u7D20\uFF09", + lineColor: "\u65F6\u95F4\u8F74\u7EBF\u989C\u8272", + lineColorDesc: "\u8BBE\u7F6E\u65F6\u95F4\u8F74\u7EBF\u7684\u989C\u8272", + colorPickerPlaceholder: "\u70B9\u51FB\u5DE6\u4FA7\u8272\u76D8\u9009\u62E9\u989C\u8272", + itemSpacing: "\u9879\u76EE\u95F4\u8DDD", + itemSpacingDesc: "\u8BBE\u7F6E\u65F6\u95F4\u8F74\u9879\u76EE\u4E4B\u95F4\u7684\u95F4\u8DDD\uFF08\u50CF\u7D20\uFF09", + cardBackground: "\u5361\u7247\u80CC\u666F\u8272", + cardBackgroundDesc: "\u8BBE\u7F6E\u5185\u5BB9\u5361\u7247\u7684\u80CC\u666F\u989C\u8272", + cardTextColor: "\u5361\u7247\u6587\u5B57\u989C\u8272", + cardTextColorDesc: "\u8BBE\u7F6E\u666E\u901A\u4E8B\u4EF6\u5361\u7247\u7684\u6587\u5B57\u989C\u8272", + cardBorderColor: "\u5361\u7247\u8FB9\u6846\u989C\u8272", + cardBorderColorDesc: "\u8BBE\u7F6E\u666E\u901A\u4E8B\u4EF6\u5361\u7247\u7684\u8FB9\u6846\u989C\u8272", + milestoneCardBackground: "\u91CC\u7A0B\u7891\u5361\u7247\u80CC\u666F\u8272", + milestoneCardBackgroundDesc: "\u8BBE\u7F6E\u91CC\u7A0B\u7891\u4E8B\u4EF6\u5361\u7247\u7684\u80CC\u666F\u989C\u8272", + milestoneCardTextColor: "\u91CC\u7A0B\u7891\u5361\u7247\u6587\u5B57\u989C\u8272", + milestoneCardTextColorDesc: "\u8BBE\u7F6E\u91CC\u7A0B\u7891\u4E8B\u4EF6\u5361\u7247\u7684\u6587\u5B57\u989C\u8272", + milestoneCardBorderColor: "\u91CC\u7A0B\u7891\u5361\u7247\u8FB9\u6846\u989C\u8272", + milestoneCardBorderColorDesc: "\u8BBE\u7F6E\u91CC\u7A0B\u7891\u4E8B\u4EF6\u5361\u7247\u7684\u8FB9\u6846\u989C\u8272", + animationDuration: "\u52A8\u753B\u6301\u7EED\u65F6\u95F4", + animationDurationDesc: "\u8BBE\u7F6E\u60AC\u505C\u52A8\u753B\u7684\u6301\u7EED\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09", + dateAttribute: "\u65E5\u671F\u5C5E\u6027", + dateAttributeDesc: "\u9009\u62E9\u7528\u4E8E\u65F6\u95F4\u8F74\u6392\u5E8F\u7684 frontmatter \u65E5\u671F\u5C5E\u6027\uFF08\u5982\uFF1Acreated, updated, date \u7B49\uFF09", + fileNamePrefix: "\u6587\u4EF6\u540D\u524D\u7F00", + fileNamePrefixDesc: "\u8BBE\u7F6E\u751F\u6210\u7684\u65F6\u95F4\u8F74\u6587\u4EF6\u540D\u524D\u7F00", + fileNameSuffix: "\u6587\u4EF6\u540D\u540E\u7F00", + fileNameSuffixDesc: "\u8BBE\u7F6E\u751F\u6210\u7684\u65F6\u95F4\u8F74\u6587\u4EF6\u540D\u540E\u7F00", + defaultPosition: "\u9ED8\u8BA4\u4F4D\u7F6E", + defaultPositionDesc: "\u9009\u62E9\u65F6\u95F4\u8F74\u89C6\u56FE\u5728\u5DE6\u4FA7\u8FD8\u662F\u53F3\u4FA7\u8FB9\u680F\u663E\u793A", + leftSidebar: "\u5DE6\u4FA7\u8FB9\u680F", + rightSidebar: "\u53F3\u4FA7\u8FB9\u680F", + language: "\u8BED\u8A00", + languageDesc: "\u9009\u62E9\u63D2\u4EF6\u754C\u9762\u8BED\u8A00", + milestoneAttribute: "\u91CC\u7A0B\u7891\u5C5E\u6027", + milestoneAttributeDesc: "\u7528\u4E8E\u6807\u8BB0\u91CC\u7A0B\u7891\u4E8B\u4EF6\u7684 frontmatter \u5C5E\u6027\u540D\u79F0\uFF08\u4F8B\u5982\uFF1Amilestone\uFF09", + milestoneValue: "\u91CC\u7A0B\u7891\u503C", + milestoneValueDesc: "\u91CC\u7A0B\u7891\u5C5E\u6027\u9700\u8981\u5339\u914D\u7684\u503C\uFF08\u7559\u7A7A\u5219\u8868\u793A\u53EA\u8981\u5C5E\u6027\u5B58\u5728\u5373\u53EF\uFF0C\u4F8B\u5982\uFF1Atrue, 1\uFF09" + }, + commands: { + openTimelineView: "\u6253\u5F00\u65F6\u95F4\u8F74\u89C6\u56FE", + generateFromFolder: "\u4ECE\u6587\u4EF6\u5939\u751F\u6210\u65F6\u95F4\u8F74\u89C6\u56FE", + generateFileFromFolder: "\u4ECE\u6587\u4EF6\u5939\u751F\u6210\u65F6\u95F4\u8F74\u6587\u4EF6", + generateFromTag: "\u4ECE\u6807\u7B7E\u751F\u6210\u65F6\u95F4\u8F74\u89C6\u56FE", + generateFileFromTag: "\u4ECE\u6807\u7B7E\u751F\u6210\u65F6\u95F4\u8F74\u6587\u4EF6", + generateFromFileLinks: "\u4ECE\u6587\u4EF6\u94FE\u63A5\u751F\u6210\u65F6\u95F4\u8F74\u89C6\u56FE", + generateFileFromFileLinks: "\u4ECE\u6587\u4EF6\u94FE\u63A5\u751F\u6210\u65F6\u95F4\u8F74\u6587\u4EF6", + generateFromMetadata: "\u4ECE\u5143\u6570\u636E\u751F\u6210\u65F6\u95F4\u8F74\u89C6\u56FE", + generateFileFromMetadata: "\u4ECE\u5143\u6570\u636E\u751F\u6210\u65F6\u95F4\u8F74\u6587\u4EF6" + }, + modal: { + metadataInputTitle: "\u8F93\u5165\u5143\u6570\u636E\u5B57\u7B26\u4E32", + metadataQueryName: "\u5143\u6570\u636E\u67E5\u8BE2", + metadataQueryDesc: "\u4F8B\u5982\uFF1Aauthor: - \u6211 \u6216 status: ongoing", + metadataInputPlaceholder: "\u8F93\u5165\u5143\u6570\u636E...", + submitButton: "\u786E\u5B9A", + cancelButton: "\u53D6\u6D88", + emptyInputNotice: "\u8BF7\u8F93\u5165\u5143\u6570\u636E\u5B57\u7B26\u4E32" + }, + errors: { + renderFailed: "\u65F6\u95F4\u8F74\u89C6\u56FE\u6E32\u67D3\u5931\u8D25", + generateFolderFailed: "\u751F\u6210\u6587\u4EF6\u5939\u65F6\u95F4\u8F74\u89C6\u56FE\u65F6\u51FA\u9519", + generateTagFailed: "\u751F\u6210\u6807\u7B7E\u65F6\u95F4\u8F74\u89C6\u56FE\u65F6\u51FA\u9519", + generateFileFailed: "\u4ECE\u6587\u4EF6\u94FE\u63A5\u751F\u6210\u65F6\u95F4\u8F74\u89C6\u56FE\u65F6\u51FA\u9519", + noTaggedFiles: "\u6CA1\u6709\u627E\u5230\u5305\u542B\u6807\u7B7E #{tag} \u53CA\u5176\u5B50\u6807\u7B7E\u7684\u6587\u4EF6", + noFileLinks: "\u6587\u4EF6 {filename} \u4E2D\u6CA1\u6709\u627E\u5230\u53EF\u7528\u7684\u94FE\u63A5\u6216\u65E5\u671F\u4FE1\u606F", + createViewFailed: "\u65E0\u6CD5\u521B\u5EFA\u65F6\u95F4\u8F74\u89C6\u56FE\uFF1A\u8BF7\u68C0\u67E5\u4FA7\u8FB9\u680F\u7A7A\u95F4", + noMetadataFiles: "\u6CA1\u6709\u627E\u5230\u4E0E\u5143\u6570\u636E\u67E5\u8BE2 \u201C{query}\u201D \u5339\u914D\u7684\u6587\u4EF6", + generateMetadataFailed: "\u4ECE\u5143\u6570\u636E\u751F\u6210\u65F6\u95F4\u8F74\u5931\u8D25" + } +}; +var enUS = { + settings: { + resetSettings: "Reset Settings", + resetSettingsDesc: "Restore all settings to default values", + resetSettingsButton: "Reset to Defaults", + lineWidth: "Timeline Line Width", + lineWidthDesc: "Set the width of the main timeline line (pixels)", + lineColor: "Timeline Line Color", + lineColorDesc: "Set the color of the timeline line", + colorPickerPlaceholder: "Click the color picker on the left", + itemSpacing: "Item Spacing", + itemSpacingDesc: "Set the spacing between timeline items (pixels)", + cardBackground: "Card Background Color", + cardBackgroundDesc: "Set the background color of content cards", + cardTextColor: "Card Text Color", + cardTextColorDesc: "Set the text color of regular event cards", + cardBorderColor: "Card Border Color", + cardBorderColorDesc: "Set the border color of regular event cards", + milestoneCardBackground: "Milestone Card Background", + milestoneCardBackgroundDesc: "Set the background color of milestone event cards", + milestoneCardTextColor: "Milestone Card Text Color", + milestoneCardTextColorDesc: "Set the text color of milestone event cards", + milestoneCardBorderColor: "Milestone Card Border Color", + milestoneCardBorderColorDesc: "Set the border color of milestone event cards", + animationDuration: "Animation Duration", + animationDurationDesc: "Set the duration of hover animations (milliseconds)", + dateAttribute: "Date Attribute", + dateAttributeDesc: "Choose the frontmatter date attribute for timeline sorting (e.g., created, updated, date)", + fileNamePrefix: "File Name Prefix", + fileNamePrefixDesc: "Set the prefix for generated timeline file names", + fileNameSuffix: "File Name Suffix", + fileNameSuffixDesc: "Set the suffix for generated timeline file names", + defaultPosition: "Default Position", + defaultPositionDesc: "Choose whether to display the timeline view in the left or right sidebar", + leftSidebar: "Left Sidebar", + rightSidebar: "Right Sidebar", + language: "Language", + languageDesc: "Select plugin interface language", + milestoneAttribute: "Milestone Attribute", + milestoneAttributeDesc: "The frontmatter attribute name used to mark milestone events (e.g., milestone)", + milestoneValue: "Milestone Value", + milestoneValueDesc: "The value the milestone attribute needs to match (leave empty to match if the attribute exists, e.g., true, 1)" + }, + commands: { + openTimelineView: "Open Timeline View", + generateFromFolder: "Generate Timeline View from any Folder", + generateFileFromFolder: "Generate Timeline File from any Folder", + generateFromTag: "Generate Timeline View from any Tag", + generateFileFromTag: "Generate Timeline File from any Tag", + generateFromFileLinks: "Generate Timeline View from File Links", + generateFileFromFileLinks: "Generate Timeline File from File Links", + generateFromMetadata: "Generate Timeline View from any Metadata", + generateFileFromMetadata: "Generate Timeline File from any Metadata" + }, + modal: { + metadataInputTitle: "Enter Metadata String", + metadataQueryName: "Metadata Query", + metadataQueryDesc: "e.g., author: - Me or status: ongoing", + metadataInputPlaceholder: "Enter metadata...", + submitButton: "Submit", + cancelButton: "Cancel", + emptyInputNotice: "Please enter a metadata string" + }, + errors: { + renderFailed: "Failed to render timeline view", + generateFolderFailed: "Error generating folder timeline view", + generateTagFailed: "Error generating tag timeline view", + generateFileFailed: "Error generating file links timeline view", + noTaggedFiles: "No files found with tag #{tag} or its subtags", + noFileLinks: "No usable links or date information found in file {filename}", + createViewFailed: "Cannot create timeline view: Please check sidebar space", + noMetadataFiles: 'No files found matching metadata query "{query}"', + generateMetadataFailed: "Failed to generate timeline from metadata" + } +}; + +// src/TimelineView.ts +var import_obsidian2 = require("obsidian"); + +// src/Timeline.ts +var import_obsidian = require("obsidian"); +var Timeline = class { + constructor(app, settings) { + __publicField(this, "app"); + __publicField(this, "settings"); + this.app = app; + this.settings = settings; + } + async getFilePreview(file, tag) { + const content = await this.app.vault.cachedRead(file); + const normalizedTag = tag.replace(/^#/, ""); + const tagLines = content.split("\n").filter((line) => { + const tagRegex = new RegExp(`(^|s)#${normalizedTag}(?=s|$|[^w/])`); + return tagRegex.test(line); + }); + if (tagLines.length > 0) { + const firstTagLine = tagLines[0]; + return firstTagLine.slice(0, 100) + (firstTagLine.length > 100 ? "..." : ""); + } + const previewContent = content.replace(/^---[\s\S]*?---/, "").trim(); + return previewContent.slice(0, 50) + (previewContent.length > 50 ? "..." : ""); + } + async createTimelineItem(file, tag, metadataQuery) { + var _a; + const metadata = this.app.metadataCache.getFileCache(file); + const dateValue = (_a = metadata == null ? void 0 : metadata.frontmatter) == null ? void 0 : _a[this.settings.dateAttribute]; + if (dateValue) { + const preview = tag ? await this.getFilePreview(file, tag) : await this.getFilePreview(file, ""); + return { + date: new Date(dateValue), + title: file.basename, + path: file.path, + preview, + isMilestone: this.checkMilestone(metadata == null ? void 0 : metadata.frontmatter) + // 检查是否为里程碑 + }; + } + return null; + } + checkMilestone(frontmatter) { + if (!this.settings.milestoneAttribute || !frontmatter) { + return false; + } + const attributeValue = frontmatter[this.settings.milestoneAttribute]; + if (attributeValue === void 0 || attributeValue === null) { + return false; + } + if (this.settings.milestoneValue === "") { + return true; + } + return String(attributeValue).toLowerCase() === String(this.settings.milestoneValue).toLowerCase(); + } + sortItemsByDate(items) { + return items.sort((a, b) => b.date.getTime() - a.date.getTime()); + } + extractLinkedFiles(content) { + const linkedFiles = []; + const linkRegex = /\[\[([^\]]+)\]\]/g; + let match; + while ((match = linkRegex.exec(content)) !== null) { + const linkPath = match[1].split("|")[0]; + const file = this.app.metadataCache.getFirstLinkpathDest(linkPath, ""); + if (file && file instanceof import_obsidian.TFile) { + linkedFiles.push(file); + } + } + return linkedFiles; + } + async generateFromFileLinks(file) { + const content = await this.app.vault.cachedRead(file); + const linkedFiles = this.extractLinkedFiles(content); + const items = []; + for (const linkedFile of linkedFiles) { + const item = await this.createTimelineItem(linkedFile); + if (item) { + items.push(item); + } + } + return this.sortItemsByDate(items); + } + async generateFromMetadata(metadataQuery) { + const allFiles = this.app.vault.getMarkdownFiles(); + const items = []; + const queryParts = metadataQuery.split(":").map((p) => p.trim()); + const queryKey = queryParts[0]; + const queryValue = queryParts.length > 1 ? queryParts.slice(1).join(":").trim() : null; + for (const file of allFiles) { + const cache = this.app.metadataCache.getFileCache(file); + if (!cache || !cache.frontmatter) + continue; + const frontmatter = cache.frontmatter; + let match = false; + if (queryValue !== null) { + const fmValue = frontmatter[queryKey]; + if (fmValue !== void 0) { + if (Array.isArray(fmValue)) { + const cleanedQueryValue = queryValue.startsWith("- ") ? queryValue.substring(2) : queryValue; + if (fmValue.map(String).map((v) => v.trim()).includes(cleanedQueryValue)) { + match = true; + } + } else if (String(fmValue).trim() === queryValue) { + match = true; + } + } + } else { + if (frontmatter.hasOwnProperty(queryKey)) { + match = true; + } + } + if (match) { + const item = await this.createTimelineItem(file, void 0, metadataQuery); + if (item) { + items.push(item); + } + } + } + return this.sortItemsByDate(items); + } + async generateFromFolder(folder) { + if (folder.path === "timelines") { + return []; + } + const timelineItems = []; + const processFolder = async (currentFolder) => { + var _a; + for (const item of currentFolder.children) { + if (item instanceof import_obsidian.TFile && item.extension === "md") { + const metadata = this.app.metadataCache.getFileCache(item); + const dateValue = (_a = metadata == null ? void 0 : metadata.frontmatter) == null ? void 0 : _a[this.settings.dateAttribute]; + if (dateValue) { + timelineItems.push({ + date: new Date(dateValue), + title: item.basename, + path: item.path, + preview: await this.getFilePreview(item, ""), + isMilestone: this.checkMilestone(metadata == null ? void 0 : metadata.frontmatter) + }); + } + } else if (item instanceof import_obsidian.TFolder && item.path !== "timelines") { + await processFolder(item); + } + } + }; + await processFolder(folder); + return timelineItems.sort((a, b) => b.date.getTime() - a.date.getTime()); + } + async generateFromTag(tag) { + var _a; + const allFiles = this.app.vault.getMarkdownFiles(); + const items = []; + const normalizedTag = tag.replace(/^#/, ""); + for (const file of allFiles) { + const cache = this.app.metadataCache.getFileCache(file); + if (!cache) + continue; + const hasMatchingTag = cache.tags && cache.tags.some((tagObj) => { + const fileTag = tagObj.tag.replace(/^#/, ""); + return fileTag === normalizedTag || // 完全匹配 + fileTag.startsWith(normalizedTag + "/"); + }) || ((_a = cache.frontmatter) == null ? void 0 : _a.tags) && Array.isArray(cache.frontmatter.tags) && cache.frontmatter.tags.some((frontmatterTag) => { + if (typeof frontmatterTag !== "string") + return false; + const fileTag = frontmatterTag.replace(/^#/, ""); + return fileTag === normalizedTag || // 完全匹配 + fileTag.startsWith(normalizedTag + "/"); + }); + if (hasMatchingTag) { + const item = await this.createTimelineItem(file, normalizedTag); + if (item) { + items.push(item); + } + } + } + return this.sortItemsByDate(items); + } + async generateTimelineMarkdown(items, title, source) { + let generatedFromValue = source.value; + if (source.type === "metadata") { + const parts = source.value.split(":"); + const key = parts[0].trim(); + const value = parts.slice(1).join(":").trim(); + generatedFromValue = `${key}:${value}`; + } + let markdown = `--- +generated_from: ${source.type}:${generatedFromValue} +--- + +`; + markdown += `# ${title} + +`; + let currentYear = null; + let currentMonth = null; + for (const item of items) { + const date = item.date; + const year = date.getFullYear(); + const month = date.getMonth(); + if (currentYear !== year) { + markdown += ` +## ${year} + +`; + currentYear = year; + currentMonth = null; + } + if (currentMonth !== month) { + const monthName = this.settings.language === "zh-CN" ? date.toLocaleString("zh-CN", { month: "long" }) : date.toLocaleString("en-US", { month: "long" }); + markdown += ` +### ${monthName} + +`; + currentMonth = month; + } + const dateFormat = this.settings.language === "zh-CN" ? date.toLocaleDateString("zh-CN") : date.toLocaleDateString("en-US"); + markdown += `#### ${dateFormat} - [[${item.title}]] + +`; + } + return markdown; + } +}; + +// src/TimelineView.ts +var VIEW_TYPE_TIMELINE = "timeline-view"; +var CLASS_TIMELINE_HEADER = "timeline-header"; +var CLASS_TIMELINE_CONTAINER = "timeline-container"; +var CLASS_TIMELINE_LINE = "timeline-line"; +var CLASS_TIMELINE_ERA = "timeline-era"; +var CLASS_TIMELINE_ERA_TITLE = "timeline-era-title"; +var CLASS_TIMELINE_ITEM = "timeline-item"; +var CLASS_TIMELINE_CARD = "timeline-card"; +var CLASS_TIMELINE_DATE = "timeline-date"; +var CLASS_TIMELINE_TITLE = "timeline-title"; +var CLASS_TIMELINE_PREVIEW = "timeline-preview"; +var TimelineView = class extends import_obsidian2.ItemView { + constructor(leaf, settings, i18n) { + super(leaf); + __publicField(this, "timeline"); + __publicField(this, "settings"); + __publicField(this, "i18n"); + __publicField(this, "items", []); + __publicField(this, "currentTitle", ""); + this.timeline = new Timeline(this.app, settings); + this.settings = settings; + this.i18n = i18n; + } + getViewType() { + return VIEW_TYPE_TIMELINE; + } + getDisplayText() { + return this.i18n.commands.openTimelineView; + } + async onOpen() { + try { + await this.render(); + } catch (error) { + new import_obsidian2.Notice(this.i18n.errors.renderFailed); + } + } + async updateFromMetadata(metadataQuery) { + try { + this.currentTitle = `\u{1F50D} ${metadataQuery}`; + this.items = await this.timeline.generateFromMetadata(metadataQuery); + if (this.items.length === 0) { + new import_obsidian2.Notice(this.i18n.errors.noMetadataFiles.replace("{query}", metadataQuery)); + return; + } + await this.render(); + } catch (error) { + new import_obsidian2.Notice(this.i18n.errors.generateMetadataFailed); + console.error("Error generating timeline from metadata:", error); + } + } + renderTitle(container) { + const titleContainer = container.createEl("div", { cls: CLASS_TIMELINE_HEADER }); + titleContainer.createEl("h2", { text: this.currentTitle }); + } + groupItemsByYear() { + const itemsByYear = /* @__PURE__ */ new Map(); + this.items.forEach((item) => { + var _a; + const year = item.date.getFullYear(); + if (!itemsByYear.has(year)) { + itemsByYear.set(year, []); + } + (_a = itemsByYear.get(year)) == null ? void 0 : _a.push(item); + }); + return itemsByYear; + } + renderItem(container, item, index, itemsLength) { + const itemClasses = [`${CLASS_TIMELINE_ITEM}${this.getItemClasses(index, itemsLength)}`]; + if (item.isMilestone) { + itemClasses.push("timeline-item-milestone"); + } + const itemEl = container.createEl("div", { + cls: itemClasses.join(" "), + attr: { style: `--timeline-line-color: ${this.settings.lineColor}` } + }); + const card = itemEl.createEl("div", { cls: CLASS_TIMELINE_CARD }); + card.createEl("div", { + cls: CLASS_TIMELINE_DATE, + text: item.date.toLocaleDateString(this.settings.language === "zh-CN" ? "zh-CN" : "en-US") + }); + const titleEl = card.createEl("div", { + cls: CLASS_TIMELINE_TITLE, + attr: { style: `color: ${item.isMilestone ? this.settings.milestoneCardTextColor : this.settings.cardTextColor} !important` } + }); + titleEl.textContent = item.title; + titleEl.dataset.colorSetting = item.isMilestone ? "milestoneCardTextColor" : "cardTextColor"; + if (item.preview) { + const previewEl = card.createEl("div", { + cls: CLASS_TIMELINE_PREVIEW, + attr: { style: `color: ${item.isMilestone ? this.settings.milestoneCardTextColor : this.settings.cardTextColor} !important` } + }); + previewEl.textContent = item.preview; + previewEl.dataset.colorSetting = item.isMilestone ? "milestoneCardTextColor" : "cardTextColor"; + } + titleEl.addEventListener("click", async () => { + const file = this.app.vault.getAbstractFileByPath(item.path); + if (file instanceof import_obsidian2.TFile) { + await this.app.workspace.getLeaf().openFile(file); + } + }); + } + getItemClasses(index, itemsLength) { + return (index === 0 ? " first-item" : "") + (index === itemsLength - 1 ? " last-item" : ""); + } + renderEra(container, year, items) { + const era = container.createEl("div", { cls: CLASS_TIMELINE_ERA }); + era.createEl("div", { cls: CLASS_TIMELINE_ERA_TITLE, text: `${year}` }); + items.forEach((item, index) => this.renderItem(era, item, index, items.length)); + } + async render() { + const container = this.containerEl.children[1]; + container.empty(); + if (this.currentTitle) { + this.renderTitle(container); + } + const timelineContainer = container.createEl("div", { cls: CLASS_TIMELINE_CONTAINER }); + timelineContainer.createEl("div", { cls: CLASS_TIMELINE_LINE }); + const itemsByYear = this.groupItemsByYear(); + Array.from(itemsByYear.entries()).sort(([yearA], [yearB]) => yearB - yearA).forEach(([year, items]) => this.renderEra(timelineContainer, year, items)); + } + async updateFromFolder(folderPath) { + const folder = this.app.vault.getAbstractFileByPath(folderPath); + if (folder) { + this.currentTitle = `\u{1F4C2} ${folder.name}`; + this.items = await this.timeline.generateFromFolder(folder); + await this.render(); + } + } + async updateFromTag(tag) { + try { + this.currentTitle = `\u{1F3F7}\uFE0F ${tag}`; + this.items = await this.timeline.generateFromTag(tag); + if (this.items.length === 0) { + new import_obsidian2.Notice(this.i18n.errors.noTaggedFiles.replace("{tag}", tag)); + return; + } + await this.render(); + } catch (error) { + new import_obsidian2.Notice("\u751F\u6210\u65F6\u95F4\u8F74\u5931\u8D25"); + throw error; + } + } + async updateFromFile(filePath) { + try { + const file = this.app.vault.getAbstractFileByPath(filePath); + if (file && file instanceof import_obsidian2.TFile) { + this.currentTitle = `\u{1F4C4} ${file.basename}`; + this.items = await this.timeline.generateFromFileLinks(file); + if (this.items.length === 0) { + new import_obsidian2.Notice(this.i18n.errors.noFileLinks.replace("{filename}", file.basename)); + return; + } + await this.render(); + } + } catch (error) { + new import_obsidian2.Notice(this.i18n.errors.generateFileFailed); + console.error(error); + } + } + getIcon() { + return "history"; + } +}; + +// src/FolderSuggest.ts +var import_obsidian3 = require("obsidian"); +var FolderSuggestModal = class extends import_obsidian3.FuzzySuggestModal { + constructor(app, settings) { + super(app); + this.settings = settings; + __publicField(this, "resolve"); + this.setPlaceholder(this.settings.language === "zh-CN" ? "\u9009\u62E9\u4E00\u4E2A\u6587\u4EF6\u5939" : "Select a folder"); + } + getItems() { + const folders = []; + const pushFolder = (folder) => { + folders.push(folder); + folder.children.filter((child) => child instanceof import_obsidian3.TFolder).forEach(pushFolder); + }; + const rootFolder = this.app.vault.getRoot(); + pushFolder(rootFolder); + return folders; + } + getItemText(folder) { + return folder.path; + } + onChooseItem(folder) { + this.resolve(folder.path); + this.close(); + } + async openAndGetValue() { + return new Promise((resolve) => { + this.resolve = resolve; + this.open(); + }).then((value) => { + if (value === void 0) { + return null; + } + return value; + }); + } +}; + +// src/TagSuggest.ts +var import_obsidian4 = require("obsidian"); +var TagSuggestModal = class extends import_obsidian4.FuzzySuggestModal { + constructor(app, settings) { + super(app); + this.settings = settings; + __publicField(this, "resolve"); + this.setPlaceholder(this.settings.language === "zh-CN" ? "\u9009\u62E9\u4E00\u4E2A\u6807\u7B7E" : "Select a tag"); + } + getItems() { + const tags = /* @__PURE__ */ new Set(); + const files = this.app.vault.getMarkdownFiles(); + files.forEach((file) => { + var _a, _b; + const cache = this.app.metadataCache.getFileCache(file); + (_a = cache == null ? void 0 : cache.tags) == null ? void 0 : _a.forEach((tag) => { + tags.add(tag.tag.replace("#", "")); + }); + const frontmatterTags = (_b = cache == null ? void 0 : cache.frontmatter) == null ? void 0 : _b.tags; + if (Array.isArray(frontmatterTags)) { + frontmatterTags.forEach((tag) => { + tags.add(tag.replace("#", "")); + }); + } else if (typeof frontmatterTags === "string") { + frontmatterTags.split("-").forEach((tag) => { + tags.add(tag.trim().replace("#", "")); + }); + } + }); + const tagArray = Array.from(tags); + console.log("\u53EF\u7528\u7684\u6807\u7B7E:", tagArray); + return tagArray; + } + getItemText(tag) { + return tag; + } + onChooseItem(tag) { + console.log("\u9009\u62E9\u7684\u6807\u7B7E:", tag); + this.resolve(tag); + this.close(); + } + async openAndGetValue() { + return new Promise((resolve) => { + this.resolve = resolve; + this.open(); + }); + } +}; + +// src/FileSuggest.ts +var import_obsidian5 = require("obsidian"); +var FileSuggestModal = class extends import_obsidian5.FuzzySuggestModal { + constructor(app, settings) { + super(app); + this.settings = settings; + __publicField(this, "resolve"); + this.setPlaceholder(this.settings.language === "zh-CN" ? "\u9009\u62E9\u4E00\u4E2AMarkdown\u6587\u4EF6" : "Select a Markdown file"); + } + getItems() { + return this.app.vault.getMarkdownFiles(); + } + getItemText(file) { + return file.path; + } + onChooseItem(file) { + this.resolve(file); + this.close(); + } + async openAndGetValue() { + return new Promise((resolve) => { + this.resolve = resolve; + this.open(); + }); + } +}; + +// src/TimelineSettings.ts +function getCssVariable(name) { + return `var(${name})`; +} +var DEFAULT_SETTINGS = { + lineWidth: 2, + lineColor: getCssVariable("--interactive-accent"), + itemSpacing: 30, + cardBackground: getCssVariable("--background-primary-alt"), + cardTextColor: getCssVariable("--text-normal"), + cardBorderColor: getCssVariable("--background-modifier-border"), + milestoneCardBackground: "#ffffcc", + milestoneCardTextColor: getCssVariable("--text-normal"), + milestoneCardBorderColor: "#f0e68c", + animationDuration: 200, + dateAttribute: "created", + // 默认使用 frontmatter 中的 date 属性作为时间轴排序依据 + fileNamePrefix: "", + fileNameSuffix: "", + defaultPosition: "right", + // 默认在右侧边栏 + language: "zh-CN", + // 默认使用中文 + milestoneAttribute: "", + // 默认不使用里程碑属性 + milestoneValue: "" + // 默认值为空 +}; + +// src/main.ts +var import_obsidian7 = require("obsidian"); +var TimelinePlugin = class extends import_obsidian6.Plugin { + constructor() { + super(...arguments); + __publicField(this, "settings"); + __publicField(this, "i18n"); + } + async onload() { + await this.loadSettings(); + this.i18n = this.settings.language === "zh-CN" ? zhCN : enUS; + this.addSettingTab(new TimelineSettingTab(this.app, this)); + console.log("\u52A0\u8F7D Timeline \u63D2\u4EF6"); + try { + this.registerView( + VIEW_TYPE_TIMELINE, + (leaf) => new TimelineView(leaf, this.settings, this.i18n) + ); + this.registerEvent( + this.app.workspace.on("file-menu", (menu, abstractFile) => { + var _a; + if (abstractFile instanceof import_obsidian6.TFile && abstractFile.path.startsWith("timelines/") && abstractFile.extension === "md") { + const cache = this.app.metadataCache.getFileCache(abstractFile); + const generatedFrom = (_a = cache == null ? void 0 : cache.frontmatter) == null ? void 0 : _a.generated_from; + if (generatedFrom) { + const [type, ...valueParts] = generatedFrom.split(":"); + const value = valueParts.join(":"); + menu.addItem((item) => { + item.setTitle(this.i18n.commands.openTimelineView).setIcon("clock").onClick(async () => { + await this.activateView(this.settings.defaultPosition); + const view = this.getTimelineView(); + if (view) { + if (type === "folder") { + await view.updateFromFolder(value); + } else if (type === "tag") { + await view.updateFromTag(value); + } else if (type === "file") { + await view.updateFromFile(value); + } else if (type === "metadata") { + await view.updateFromMetadata(value); + } + } + }); + }); + } + } + }) + ); + this.addCommand({ + id: "generate-timeline-view-from-folder", + name: this.i18n.commands.generateFromFolder, + callback: async () => { + try { + const folderPath = await this.selectFolder(); + if (folderPath) { + await this.activateView(this.settings.defaultPosition); + const view = this.getTimelineView(); + if (view) { + await view.updateFromFolder(folderPath); + } + } + } catch (error) { + console.error("\u751F\u6210\u6587\u4EF6\u5939\u65F6\u95F4\u8F74\u89C6\u56FE\u65F6\u51FA\u9519:", error); + } + } + }); + this.addCommand({ + id: "generate-timeline-file-from-folder", + name: this.i18n.commands.generateFileFromFolder, + callback: async () => { + try { + const folderPath = await this.selectFolder(); + if (folderPath) { + const folder = this.app.vault.getAbstractFileByPath(folderPath); + if (!(folder instanceof import_obsidian7.TFolder)) { + throw new Error(`Invalid folder path: ${folderPath}`); + } + const timeline = new Timeline(this.app, this.settings); + const items = await timeline.generateFromFolder(folder); + const content = await timeline.generateTimelineMarkdown(items, `Timeline - ${folderPath}`, { type: "folder", value: folderPath }); + const { folderPath: targetFolder, fileName } = await this.createNestedFolders(folderPath); + const finalFileName = this.generateFileName(fileName || folderPath.split("/").pop() || ""); + const filePath = `${targetFolder}/${finalFileName}.md`; + const existingFile = this.app.vault.getAbstractFileByPath(filePath); + if (existingFile) { + await this.app.vault.delete(existingFile); + } + const file = await this.app.vault.create(filePath, content); + await this.app.workspace.getLeaf().openFile(file); + } + } catch (error) { + console.error("\u751F\u6210\u6587\u4EF6\u5939\u65F6\u95F4\u8F74\u6587\u4EF6\u65F6\u51FA\u9519:", error); + } + } + }); + this.addCommand({ + id: "generate-timeline-view-from-tag", + name: this.i18n.commands.generateFromTag, + callback: async () => { + try { + const tag = await this.selectTag(); + if (tag) { + await this.activateView(this.settings.defaultPosition); + const view = this.getTimelineView(); + if (view) { + await view.updateFromTag(tag); + } + } + } catch (error) { + console.error("\u751F\u6210\u6807\u7B7E\u65F6\u95F4\u8F74\u89C6\u56FE\u65F6\u51FA\u9519:", error); + } + } + }); + this.addCommand({ + id: "generate-timeline-file-from-tag", + name: this.i18n.commands.generateFileFromTag, + callback: async () => { + try { + const tag = await this.selectTag(); + if (tag) { + await this.generateTimelineFileFromTag(tag); + } + } catch (error) { + console.error("\u751F\u6210\u6807\u7B7E\u65F6\u95F4\u8F74\u6587\u4EF6\u65F6\u51FA\u9519:", error); + } + } + }); + this.addCommand({ + id: "generate-timeline-view-from-file-links", + name: this.i18n.commands.generateFromFileLinks, + callback: async () => { + try { + const file = await this.selectFile(); + if (file) { + await this.activateView(this.settings.defaultPosition); + const view = this.getTimelineView(); + if (view) { + const timeline = new Timeline(this.app, this.settings); + const items = await timeline.generateFromFileLinks(file); + view.items = items; + view.currentTitle = `\u{1F517} ${file.basename} `; + await view.render(); + } + } + } catch (error) { + console.error("\u4ECE\u6587\u4EF6\u94FE\u63A5\u751F\u6210\u65F6\u95F4\u8F74\u89C6\u56FE\u65F6\u51FA\u9519:", error); + } + } + }); + this.addCommand({ + id: "generate-timeline-file-from-file-links", + name: this.i18n.commands.generateFileFromFileLinks, + callback: async () => { + try { + const file = await this.selectFile(); + if (file) { + const timeline = new Timeline(this.app, this.settings); + const items = await timeline.generateFromFileLinks(file); + const content = await timeline.generateTimelineMarkdown( + items, + `Timeline - Links in ${file.basename}`, + { type: "file", value: file.path } + ); + const { folderPath } = await this.createNestedFolders("linked-files"); + const finalFileName = this.generateFileName(file.basename); + const filePath = `${folderPath}/${finalFileName}.md`; + const existingFile = this.app.vault.getAbstractFileByPath(filePath); + if (existingFile) { + await this.app.vault.delete(existingFile); + } + const newFile = await this.app.vault.create(filePath, content); + await this.app.workspace.getLeaf().openFile(newFile); + } + } catch (error) { + console.error("\u4ECE\u6587\u4EF6\u94FE\u63A5\u751F\u6210\u65F6\u95F4\u8F74\u6587\u4EF6\u65F6\u51FA\u9519:", error); + } + } + }); + this.addCommand({ + id: "generate-timeline-view-from-metadata", + name: this.i18n.commands.generateFromMetadata, + callback: async () => { + try { + const metadataQuery = await this.selectMetadataString(); + if (metadataQuery) { + await this.activateView(this.settings.defaultPosition); + const view = this.getTimelineView(); + if (view) { + await view.updateFromMetadata(metadataQuery); + } + } + } catch (error) { + console.error("\u4ECE\u5143\u6570\u636E\u751F\u6210\u65F6\u95F4\u8F74\u89C6\u56FE\u65F6\u51FA\u9519:", error); + new import_obsidian6.Notice(this.i18n.errors.generateMetadataFailed); + } + } + }); + this.addCommand({ + id: "generate-timeline-file-from-metadata", + name: this.i18n.commands.generateFileFromMetadata, + callback: async () => { + try { + const metadataQuery = await this.selectMetadataString(); + if (metadataQuery) { + const timeline = new Timeline(this.app, this.settings); + const items = await timeline.generateFromMetadata(metadataQuery); + const content = await timeline.generateTimelineMarkdown( + items, + `Timeline - Metadata: ${metadataQuery}`, + { type: "metadata", value: metadataQuery } + ); + const { folderPath } = await this.createNestedFolders("metadata-search"); + const safeBaseName = metadataQuery.substring(0, 30).replace(/[^a-zA-Z0-9_\-]+/g, "_") || "metadata_query"; + const finalFileName = this.generateFileName(safeBaseName); + const filePath = `${folderPath}/${finalFileName}.md`; + const existingFile = this.app.vault.getAbstractFileByPath(filePath); + if (existingFile) { + await this.app.vault.delete(existingFile); + } + const newFile = await this.app.vault.create(filePath, content); + await this.app.workspace.getLeaf().openFile(newFile); + } + } catch (error) { + console.error("\u4ECE\u5143\u6570\u636E\u751F\u6210\u65F6\u95F4\u8F74\u6587\u4EF6\u65F6\u51FA\u9519:", error); + new import_obsidian6.Notice(this.i18n.errors.generateMetadataFailed); + } + } + }); + } catch (error) { + console.error("\u63D2\u4EF6\u52A0\u8F7D\u65F6\u51FA\u9519:", error); + } + } + async selectFolder() { + const modal = new FolderSuggestModal(this.app, this.settings); + return await modal.openAndGetValue(); + } + async selectFile() { + const modal = new FileSuggestModal(this.app, this.settings); + return await modal.openAndGetValue(); + } + async selectTag() { + const modal = new TagSuggestModal(this.app, this.settings); + return await modal.openAndGetValue(); + } + async selectMetadataString() { + const modal = new MetadataInputModal(this.app, this); + return await modal.openAndGetValue(); + } + async activateView(position = this.settings.defaultPosition) { + const { workspace } = this.app; + const leaves = workspace.getLeavesOfType(VIEW_TYPE_TIMELINE); + if (leaves.length > 0) { + workspace.revealLeaf(leaves[0]); + return leaves[0]; + } + const newLeaf = position === "left" ? workspace.getLeftLeaf(false) : workspace.getRightLeaf(false); + if (!newLeaf) { + new import_obsidian6.Notice("\u65E0\u6CD5\u521B\u5EFA\u65F6\u95F4\u8F74\u89C6\u56FE\uFF1A\u8BF7\u68C0\u67E5\u4FA7\u8FB9\u680F\u7A7A\u95F4"); + return null; + } + await newLeaf.setViewState({ type: VIEW_TYPE_TIMELINE }); + workspace.revealLeaf(newLeaf); + return newLeaf; + } + getTimelineView() { + const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_TIMELINE); + if (leaves.length === 0) { + return null; + } + return leaves[0].view; + } + async loadSettings() { + this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + this.updateStyles(); + } + async saveSettings() { + await this.saveData(this.settings); + this.updateStyles(); + } + updateStyles() { + var _a; + const style = document.createElement("style"); + style.id = "timeline-custom-styles"; + style.textContent = ` + .timeline-line { + width: ${this.settings.lineWidth}px !important; + background: ${this.settings.lineColor} !important; + } + .timeline-item::after { + background: var(--timeline-line-color, ${this.settings.lineColor}) !important; + } + .timeline-item { + margin: ${this.settings.itemSpacing}px 0 !important; + } + .timeline-card { + background: ${this.settings.cardBackground} !important; + color: ${this.settings.cardTextColor} !important; + border: 1px solid ${this.settings.cardBorderColor} !important; + transition: transform ${this.settings.animationDuration}ms !important; + } + .timeline-item-milestone .timeline-card { + background: ${this.settings.milestoneCardBackground} !important; + color: ${this.settings.milestoneCardTextColor} !important; + border: 1px solid ${this.settings.milestoneCardBorderColor} !important; + } + [data-color-setting="cardTextColor"] { + color: ${this.settings.cardTextColor} !important; + } + [data-color-setting="milestoneCardTextColor"] { + color: ${this.settings.milestoneCardTextColor} !important; + } + .timeline-date { + color: ${this.settings.cardTextColor} !important; + } + .timeline-item-milestone .timeline-date { + color: ${this.settings.milestoneCardTextColor} !important; + } + .timeline-era-title { + color: var(--timeline-line-color, ${this.settings.lineColor}) !important; + } + `; + (_a = document.getElementById("timeline-custom-styles")) == null ? void 0 : _a.remove(); + document.head.appendChild(style); + } + async createNestedFolders(tagPath) { + const rootPath = "timelines"; + if (!this.app.vault.getAbstractFileByPath(rootPath)) { + try { + await this.app.vault.createFolder(rootPath); + } catch (error) { + new import_obsidian6.Notice(`\u521B\u5EFA\u6839\u76EE\u5F55\u5931\u8D25: ${rootPath}`); + throw error; + } + } + const parts = tagPath.split("/"); + const fileName = parts.pop(); + let currentPath = rootPath; + for (const part of parts) { + currentPath = (0, import_obsidian6.normalizePath)(`${currentPath}/${part}`); + const folder = this.app.vault.getAbstractFileByPath(currentPath); + if (!folder) { + try { + await this.app.vault.createFolder(currentPath); + } catch (error) { + new import_obsidian6.Notice(`\u521B\u5EFA\u6587\u4EF6\u5939\u5931\u8D25: ${currentPath}`); + throw error; + } + } + } + return { folderPath: currentPath, fileName }; + } + generateFileName(baseName) { + const prefix = this.settings.fileNamePrefix || ""; + const suffix = this.settings.fileNameSuffix || ""; + return `${prefix}${baseName}${suffix}`; + } + async generateTimelineFileFromTag(tag) { + try { + const tagPath = tag.replace("#", ""); + const { folderPath, fileName } = await this.createNestedFolders(tagPath); + const finalFileName = this.generateFileName(fileName || ""); + const filePath = `${folderPath}/${finalFileName}.md`; + const existingFile = this.app.vault.getAbstractFileByPath(filePath); + if (existingFile) { + await this.app.vault.delete(existingFile); + } + const timeline = new Timeline(this.app, this.settings); + const items = await timeline.generateFromTag(tag); + const content = await timeline.generateTimelineMarkdown(items, `Timeline - ${tag}`, { type: "tag", value: tag }); + const file = await this.app.vault.create(filePath, content); + await this.app.workspace.getLeaf().openFile(file); + } catch (error) { + console.error("\u751F\u6210\u6807\u7B7E\u65F6\u95F4\u8F74\u6587\u4EF6\u65F6\u51FA\u9519:", error); + throw error; + } + } +}; +var MetadataInputModal = class extends import_obsidian6.Modal { + constructor(app, plugin) { + super(app); + __publicField(this, "plugin"); + // Add plugin reference + __publicField(this, "value", ""); + __publicField(this, "resolvePromise"); + __publicField(this, "inputEl"); + this.plugin = plugin; + } + async openAndGetValue() { + return new Promise((resolve) => { + this.resolvePromise = resolve; + this.open(); + }); + } + onOpen() { + const { contentEl } = this; + contentEl.empty(); + contentEl.addClass("metadata-input-modal"); + contentEl.createEl("h2", { text: this.plugin.i18n.modal.metadataInputTitle }); + new import_obsidian6.Setting(contentEl).setName(this.plugin.i18n.modal.metadataQueryName).setDesc(this.plugin.i18n.modal.metadataQueryDesc).addText((text) => { + this.inputEl = text; + text.setPlaceholder(this.plugin.i18n.modal.metadataInputPlaceholder).onChange((value) => this.value = value.trim()); + text.inputEl.style.width = "100%"; + text.inputEl.addEventListener("keydown", (evt) => { + if (evt.key === "Enter") { + evt.preventDefault(); + this.submit(); + } + }); + }); + new import_obsidian6.Setting(contentEl).addButton((button) => button.setButtonText(this.plugin.i18n.modal.submitButton).setCta().onClick(() => this.submit())).addButton((button) => button.setButtonText(this.plugin.i18n.modal.cancelButton).onClick(() => this.closeAndResolve(null))); + setTimeout(() => { + if (this.inputEl && this.inputEl.inputEl) { + this.inputEl.inputEl.focus(); + } + }, 50); + } + submit() { + if (this.value) { + this.closeAndResolve(this.value); + } else { + new import_obsidian6.Notice(this.plugin.i18n.modal.emptyInputNotice); + } + } + closeAndResolve(value) { + if (this.resolvePromise) { + this.resolvePromise(value); + } + this.close(); + } + onClose() { + const { contentEl } = this; + contentEl.empty(); + } +}; +var TimelineSettingTab = class extends import_obsidian6.PluginSettingTab { + constructor(app, plugin) { + super(app, plugin); + __publicField(this, "plugin"); + this.plugin = plugin; + } + display() { + const { containerEl } = this; + containerEl.empty(); + new import_obsidian6.Setting(containerEl).setName(this.plugin.i18n.settings.resetSettings).setDesc(this.plugin.i18n.settings.resetSettingsDesc).addButton((button) => button.setButtonText(this.plugin.i18n.settings.resetSettingsButton).onClick(async () => { + this.plugin.settings = Object.assign({}, DEFAULT_SETTINGS); + await this.plugin.saveSettings(); + this.display(); + })); + new import_obsidian6.Setting(containerEl).setName(this.plugin.i18n.settings.language).setDesc(this.plugin.i18n.settings.languageDesc).addDropdown((dropdown) => dropdown.addOption("zh-CN", "\u4E2D\u6587").addOption("en-US", "English").setValue(this.plugin.settings.language).onChange(async (value) => { + this.plugin.settings.language = value; + this.plugin.i18n = value === "zh-CN" ? zhCN : enUS; + await this.plugin.saveSettings(); + this.display(); + })); + new import_obsidian6.Setting(containerEl).setName(this.plugin.i18n.settings.milestoneAttribute).setDesc(this.plugin.i18n.settings.milestoneAttributeDesc).addText((text) => text.setPlaceholder("milestone").setValue(this.plugin.settings.milestoneAttribute).onChange(async (value) => { + this.plugin.settings.milestoneAttribute = value; + await this.plugin.saveSettings(); + })); + new import_obsidian6.Setting(containerEl).setName(this.plugin.i18n.settings.milestoneValue).setDesc(this.plugin.i18n.settings.milestoneValueDesc).addText((text) => text.setPlaceholder("true or 1 (leave empty to match any value)").setValue(this.plugin.settings.milestoneValue).onChange(async (value) => { + this.plugin.settings.milestoneValue = value; + await this.plugin.saveSettings(); + })); + new import_obsidian6.Setting(containerEl).setName(this.plugin.i18n.settings.lineWidth).setDesc(this.plugin.i18n.settings.lineWidthDesc).addSlider((slider) => slider.setLimits(1, 10, 1).setValue(this.plugin.settings.lineWidth).onChange(async (value) => { + this.plugin.settings.lineWidth = value; + await this.plugin.saveSettings(); + })); + new import_obsidian6.Setting(containerEl).setName(this.plugin.i18n.settings.lineColor).setDesc(this.plugin.i18n.settings.lineColorDesc).addColorPicker((color) => color.setValue(this.plugin.settings.lineColor).onChange(async (value) => { + this.plugin.settings.lineColor = value; + await this.plugin.saveSettings(); + })).addText((text) => text.setPlaceholder(this.plugin.i18n.settings.colorPickerPlaceholder).setValue("").onChange(async (value) => { + if (value) { + this.plugin.settings.lineColor = value; + await this.plugin.saveSettings(); + } + })); + new import_obsidian6.Setting(containerEl).setName(this.plugin.i18n.settings.itemSpacing).setDesc(this.plugin.i18n.settings.itemSpacingDesc).addSlider((slider) => slider.setLimits(10, 100, 5).setValue(this.plugin.settings.itemSpacing).onChange(async (value) => { + this.plugin.settings.itemSpacing = value; + await this.plugin.saveSettings(); + })); + new import_obsidian6.Setting(containerEl).setName(this.plugin.i18n.settings.cardBackground).setDesc(this.plugin.i18n.settings.cardBackgroundDesc).addColorPicker( + (color) => color.setValue(this.plugin.settings.cardBackground).onChange(async (value) => { + this.plugin.settings.cardBackground = value; + await this.plugin.saveSettings(); + }) + ).setTooltip(this.plugin.i18n.settings.colorPickerPlaceholder); + new import_obsidian6.Setting(containerEl).setName(this.plugin.i18n.settings.cardTextColor).setDesc(this.plugin.i18n.settings.cardTextColorDesc).addColorPicker( + (color) => color.setValue(this.plugin.settings.cardTextColor).onChange(async (value) => { + this.plugin.settings.cardTextColor = value; + await this.plugin.saveSettings(); + }) + ); + new import_obsidian6.Setting(containerEl).setName(this.plugin.i18n.settings.cardBorderColor).setDesc(this.plugin.i18n.settings.cardBorderColorDesc).addColorPicker( + (color) => color.setValue(this.plugin.settings.cardBorderColor).onChange(async (value) => { + this.plugin.settings.cardBorderColor = value; + await this.plugin.saveSettings(); + }) + ); + new import_obsidian6.Setting(containerEl).setName(this.plugin.i18n.settings.milestoneCardBackground).setDesc(this.plugin.i18n.settings.milestoneCardBackgroundDesc).addColorPicker( + (color) => color.setValue(this.plugin.settings.milestoneCardBackground).onChange(async (value) => { + this.plugin.settings.milestoneCardBackground = value; + await this.plugin.saveSettings(); + }) + ); + new import_obsidian6.Setting(containerEl).setName(this.plugin.i18n.settings.milestoneCardTextColor).setDesc(this.plugin.i18n.settings.milestoneCardTextColorDesc).addColorPicker( + (color) => color.setValue(this.plugin.settings.milestoneCardTextColor).onChange(async (value) => { + this.plugin.settings.milestoneCardTextColor = value; + await this.plugin.saveSettings(); + }) + ); + new import_obsidian6.Setting(containerEl).setName(this.plugin.i18n.settings.milestoneCardBorderColor).setDesc(this.plugin.i18n.settings.milestoneCardBorderColorDesc).addColorPicker( + (color) => color.setValue(this.plugin.settings.milestoneCardBorderColor).onChange(async (value) => { + this.plugin.settings.milestoneCardBorderColor = value; + await this.plugin.saveSettings(); + }) + ); + new import_obsidian6.Setting(containerEl).setName(this.plugin.i18n.settings.animationDuration).setDesc(this.plugin.i18n.settings.animationDurationDesc).addSlider((slider) => slider.setLimits(0, 1e3, 50).setValue(this.plugin.settings.animationDuration).onChange(async (value) => { + this.plugin.settings.animationDuration = value; + await this.plugin.saveSettings(); + })); + new import_obsidian6.Setting(containerEl).setName(this.plugin.i18n.settings.dateAttribute).setDesc(this.plugin.i18n.settings.dateAttributeDesc).addText((text) => text.setPlaceholder("created").setValue(this.plugin.settings.dateAttribute).onChange(async (value) => { + this.plugin.settings.dateAttribute = value; + await this.plugin.saveSettings(); + })); + new import_obsidian6.Setting(containerEl).setName(this.plugin.i18n.settings.fileNamePrefix).setDesc(this.plugin.i18n.settings.fileNamePrefixDesc).addText((text) => text.setPlaceholder("").setValue(this.plugin.settings.fileNamePrefix).onChange(async (value) => { + this.plugin.settings.fileNamePrefix = value; + await this.plugin.saveSettings(); + })); + new import_obsidian6.Setting(containerEl).setName(this.plugin.i18n.settings.fileNameSuffix).setDesc(this.plugin.i18n.settings.fileNameSuffixDesc).addText((text) => text.setPlaceholder("").setValue(this.plugin.settings.fileNameSuffix).onChange(async (value) => { + this.plugin.settings.fileNameSuffix = value; + await this.plugin.saveSettings(); + })); + new import_obsidian6.Setting(containerEl).setName(this.plugin.i18n.settings.defaultPosition).setDesc(this.plugin.i18n.settings.defaultPositionDesc).addDropdown( + (dropdown) => dropdown.addOption("left", this.plugin.i18n.settings.leftSidebar).addOption("right", this.plugin.i18n.settings.rightSidebar).setValue(this.plugin.settings.defaultPosition).onChange(async (value) => { + this.plugin.settings.defaultPosition = value; + await this.plugin.saveSettings(); + }) + ); + } +}; + +/* nosourcemap */ \ No newline at end of file diff --git a/.obsidian/plugins/generate-timeline/manifest.json b/.obsidian/plugins/generate-timeline/manifest.json new file mode 100644 index 00000000..8d4fa783 --- /dev/null +++ b/.obsidian/plugins/generate-timeline/manifest.json @@ -0,0 +1,10 @@ +{ + "id": "generate-timeline", + "name": "Generate Timeline", + "version": "1.2.4", + "minAppVersion": "0.15.0", + "description": "Generate timelines from tag folder or file automatically by any time properties.", + "author": "Shanshuimei", + "authorUrl": "https://github.com/Shanshuimei", + "isDesktopOnly": false +} \ No newline at end of file diff --git a/.obsidian/plugins/generate-timeline/styles.css b/.obsidian/plugins/generate-timeline/styles.css new file mode 100644 index 00000000..e347cb80 --- /dev/null +++ b/.obsidian/plugins/generate-timeline/styles.css @@ -0,0 +1,152 @@ +/* + +This CSS file will be included with your plugin, and +available in the app when your plugin is enabled. + +If your plugin does not need CSS, delete this file. + +*/ + +.timeline-container { + width: 100%; + max-width: 100%; + overflow-x: hidden; + position: relative; +} + +/* 移除原来的时间轴线 */ +.timeline-container::before { + display: none; +} + +/* 时间轴线 */ +.timeline-line { + position: absolute; + left: 10%; + top: 60px; /* 从第一个项目开始 */ + bottom: 30px; /* 到最后一个项目结束 */ + width: 2px; + background: var(--interactive-accent); +} + +/* 时间段分隔 */ +.timeline-era { + position: relative; + margin-bottom: 40px; +} + +.timeline-era-title { + font-size: 1.5em; + margin-bottom: 20px; + margin-left: 20%; + margin-right:10%; +} + +.timeline-era-date { + font-size: 0.8em; + color: var(--text-muted); +} + +/* 时间轴项目 */ +.timeline-item { + position: relative; +} + +/* 连接到主时间轴的横线 */ +.timeline-item::after { + content: ''; + position: absolute; + left: 10%; + width: 15%; + top: 50%; + height: 2px; + background: var(--interactive-accent); + transform: translateY(-50%); +} + +/* 内容卡片 */ +.timeline-card { + position: relative; + z-index: 2; /* 确保卡片在横线上方 */ + background: var(--background-primary-alt); + margin-left: 20%; + margin-right: 10%; + border-radius: 8px; + padding : 15px 15px 15px 15px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + transition: transform 0.2s, box-shadow 0.2s; +} + +.timeline-card:hover { + transform: translateX(5px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); +} + +.timeline-title { + font-size: 1.2em; + margin-bottom: 8px; + color: var(--text-normal); + cursor: pointer; +} + +.timeline-title:hover { + color: var(--text-accent); + text-decoration: underline; +} + +.timeline-date { + font-size: 0.9em; + color: var(--text-muted); + margin-bottom: 8px; +} + +.timeline-preview { + font-size: 0.9em; + color: var(--text-normal); + line-height: 1.5; + border-left: 3px solid var(--timeline-line-color); + padding-left: 10px; + margin-top: 10px; +} + +/* 图片预览 */ +.timeline-image { + width: 100%; + max-height: 200px; + object-fit: cover; + border-radius: 4px; + margin-top: 10px; +} + +.timeline-export-buttons { + display: flex; + gap: 10px; + margin-bottom: 20px; + padding: 10px; +} + +.timeline-export-button { + background-color: var(--interactive-accent); + color: var(--text-on-accent); + border: none; + border-radius: 4px; + padding: 8px 16px; + cursor: pointer; + transition: background-color 0.2s; +} + +.timeline-export-button:hover { + background-color: var(--interactive-accent-hover); +} + +.timeline-header { + text-align: center; + margin-bottom: 20px; + padding: 10px; +} + +.timeline-header h2 { + margin: 0; + color: var(--text-normal); + font-size: 1.5em; +} diff --git a/.obsidian/plugins/recent-files-obsidian/data.json b/.obsidian/plugins/recent-files-obsidian/data.json index 3a53bd90..d897f01b 100644 --- a/.obsidian/plugins/recent-files-obsidian/data.json +++ b/.obsidian/plugins/recent-files-obsidian/data.json @@ -4,6 +4,30 @@ "basename": "2025-08-23", "path": "01-工作日记/2025-08-23.md" }, + { + "basename": "Memos", + "path": "03-学习笔记/Memos.md" + }, + { + "basename": "Calibre电子书平台", + "path": "03-学习笔记/Calibre电子书平台.md" + }, + { + "basename": "微信公众号RSS", + "path": "03-学习笔记/微信公众号RSS.md" + }, + { + "basename": "微信机器人", + "path": "03-学习笔记/微信机器人.md" + }, + { + "basename": "OB插件", + "path": "03-学习笔记/OB插件.md" + }, + { + "basename": "Bitwarden", + "path": "03-学习笔记/Bitwarden.md" + }, { "basename": "2025-08-22", "path": "01-工作日记/2025-08-22.md" @@ -175,30 +199,6 @@ { "basename": "今天2个新睿团体赛全部输1点,虽然无关紧要,但也是运气不佳,仔细想想其实两盘,一盘可以打平,另外一把... (2025-08-18 14-41)", "path": "50-Memos/2025/08/今天2个新睿团体赛全部输1点,虽然无关紧要,但也是运气不佳,仔细想想其实两盘,一盘可以打平,另外一把... (2025-08-18 14-41).md" - }, - { - "basename": "在 Omnivore 停服之际发现新的替代品继续构建自己的「稍后阅读」系统 – 效率火箭火箭君的新博客", - "path": "60-Imports/63-WuCai/2025/08/在 Omnivore 停服之际发现新的替代品继续构建自己的「稍后阅读」系统 – 效率火箭火箭君的新博客.md" - }, - { - "basename": "ONLYOFFICE 协作空间 3.0 新功能详解 - 知乎", - "path": "60-Imports/63-WuCai/2025/08/ONLYOFFICE 协作空间 3.0 新功能详解 - 知乎.md" - }, - { - "basename": "配置Nginx Proxy Manager代理实现各服务https访问-软路由,x86系统,openwrt(x86),Ro", - "path": "60-Imports/63-WuCai/2025/08/配置Nginx Proxy Manager代理实现各服务https访问-软路由,x86系统,openwrt(x86),Ro.md" - }, - { - "basename": "华为手机使用华为自带邮箱exchange登录提示账号密码错误 - Microsoft QA", - "path": "60-Imports/63-WuCai/2025/08/华为手机使用华为自带邮箱exchange登录提示账号密码错误 - Microsoft QA.md" - }, - { - "basename": "2025-08-12", - "path": "01-工作日记/2025-08-12.md" - }, - { - "basename": "2025-08-11", - "path": "01-工作日记/2025-08-11.md" } ], "omittedPaths": [], diff --git a/.obsidian/plugins/rss-reader/data.json b/.obsidian/plugins/rss-reader/data.json index 34be1e62..96fe84de 100644 --- a/.obsidian/plugins/rss-reader/data.json +++ b/.obsidian/plugins/rss-reader/data.json @@ -30703,22 +30703,17 @@ "标兵1桥牌", "21世纪时文", "rss-filters-Favorites", - "国家人文历史", "各种折腾", - "小众软件", "python开发工程师", - "人民网英语", - "少数派", "小风子桥牌", "21世纪双语", "IT技术", - "人文", - "知乎", - "长郡芙蓉中学", "ChinaDaily", + "中国桥牌协会", "标兵桥牌", - "rss-filters", - "中国桥牌协会" + "国家人文历史", + "少数派", + "rss-folders" ], "renamedText": { "filtered_folders": "", diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index aad3539e..1eb0161b 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -90,8 +90,8 @@ "state": { "type": "recent-files", "state": {}, - "icon": "lucide-ghost", - "title": "recent-files" + "icon": "clock", + "title": "Recent Files" } }, { @@ -103,19 +103,9 @@ "icon": "sheets-in-box", "title": "File Tree" } - }, - { - "id": "2ce18fa958a9b527", - "type": "leaf", - "state": { - "type": "file-tree-view", - "state": {}, - "icon": "sheets-in-box", - "title": "File Tree" - } } ], - "currentTab": 5 + "currentTab": 4 } ], "direction": "horizontal", @@ -238,7 +228,7 @@ } } ], - "currentTab": 4 + "currentTab": 6 }, { "id": "ad86bdfc0cc9f06b", @@ -325,7 +315,7 @@ "media-extended:Open media": false, "diary-ics:日记日历订阅": false, "cubox-sync:Cubox": false, - "file-tree-alternative:File Tree Alternative Plugin": false, + "file-tree-alternative:File Tree Alternative Plugin": true, "hidden-folder-obsidian:隐藏文件夹": false, "password-protection:开启密码保护": false, "soundscapes:Soundscapes: My Music": false, @@ -334,8 +324,14 @@ "note-to-mp:复制到公众号": false } }, - "active": "210f6e255abf09ac", + "active": "9203e86001762f28", "lastOpenFiles": [ + "03-学习笔记/Memos.md", + "03-学习笔记/Calibre电子书平台.md", + "03-学习笔记/微信公众号RSS.md", + "03-学习笔记/微信机器人.md", + "03-学习笔记/OB插件.md", + "03-学习笔记/Bitwarden.md", "01-工作日记/2025-08-23.md", "01-工作日记/2025-08-22.md", "50-Memos/2025/08/长沙的街上现在已经很凉爽了! (2025-08-22 20-46).md", @@ -358,12 +354,6 @@ "70-SimpRead/Inbox/7-同步助手 - 文档中心 - 简悦 SimpRead - 如杂志般沉浸式阅读体验的扩展@annote.md", "70-SimpRead/Inbox/1-社交平台信息的高效获取:「微信剪藏助手」盘点与推荐@annote.md", "70-SimpRead/Inbox/0-简悦 x FlowUs:开启知识管理极致体验@annote.md", - "90-学习教程/99-黑曜石玩家指南/黑曜石玩家指南.md", - "90-学习教程/99-黑曜石玩家指南/1-obsidian入门/使用手机撰写日志.md", - "90-学习教程/99-黑曜石玩家指南/1-obsidian入门/软件下载安装.md", - "90-学习教程/99-黑曜石玩家指南/1-obsidian入门/每周定期汇总小结.md", - "90-学习教程/99-黑曜石玩家指南/1-obsidian入门/开箱写日志.md", - "90-学习教程/99-黑曜石玩家指南/1-obsidian入门/本章小结-恭喜入门obsidian.md", "08-Inbox/assets/68691ee74ca1b.png", "90-学习教程", "70-SimpRead/output/Obsidian 和 Logseq 对比 - 拾月/assets", diff --git a/01-工作日记/2025-08-23.md b/01-工作日记/2025-08-23.md index f3329f1c..69374ac7 100644 --- a/01-工作日记/2025-08-23.md +++ b/01-工作日记/2025-08-23.md @@ -29,7 +29,8 @@ excerpt: 2️⃣如果找到掼蛋搭子就掼蛋😅🥚 ``` - +- M1:Clash切换到mihomo1.19.12内核不能使用?! +- ## 3 Tips ## 4 Questions