Files
StudyDiary/.obsidian/plugins/generate-timeline/main.js
T
2026-07-23 20:36:13 +08:00

1371 lines
59 KiB
JavaScript
Executable File

/*
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\u5F53\u524D\u6587\u4EF6\u94FE\u63A5\u751F\u6210\u65F6\u95F4\u8F74\u89C6\u56FE",
generateFileFromFileLinks: "\u4ECE\u5F53\u524D\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 Current File Links",
generateFileFromFileLinks: "Generate Timeline File from Current 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;
}
// 辅助函数:检查文件是否在timelines文件夹中
isInTimelinesFolder(file) {
return file.path.startsWith("timelines/") || file.path === "timelines";
}
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) {
if (!this.isInTimelinesFolder(linkedFile)) {
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 filteredFiles = allFiles.filter((file) => !this.isInTimelinesFolder(file));
const items = [];
let queryKey;
let queryValue = null;
const firstColonIndex = metadataQuery.indexOf(":");
if (firstColonIndex > 0) {
queryKey = metadataQuery.substring(0, firstColonIndex).trim();
queryValue = metadataQuery.substring(firstColonIndex + 1).trim();
if (queryValue === "") {
queryValue = null;
}
} else {
queryKey = metadataQuery.trim();
}
for (const file of filteredFiles) {
try {
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 && fmValue !== null) {
if (Array.isArray(fmValue)) {
const cleanedQueryValue = queryValue.startsWith("- ") ? queryValue.substring(2) : queryValue;
match = fmValue.some((item) => {
const itemStr = String(item).trim().toLowerCase();
const queryStr = cleanedQueryValue.toLowerCase();
return itemStr === queryStr || itemStr.includes(queryStr) || queryStr.includes(itemStr);
});
} else {
const fmValueStr = String(fmValue).trim().toLowerCase();
const queryValueStr = queryValue.toLowerCase();
match = fmValueStr === queryValueStr || fmValueStr.includes(queryValueStr) || queryValueStr.includes(fmValueStr);
}
}
} else {
match = Object.prototype.hasOwnProperty.call(frontmatter, queryKey);
}
if (match) {
const item = await this.createTimelineItem(file, void 0, metadataQuery);
if (item) {
items.push(item);
}
}
} catch (error) {
console.error(`\u5904\u7406\u6587\u4EF6 ${file.path} \u65F6\u51FA\u9519:`, error);
continue;
}
}
return this.sortItemsByDate(items);
}
async generateFromFolder(folder) {
if (folder.path === "timelines" || folder.path.startsWith("timelines/")) {
return [];
}
const timelineItems = [];
const processFolder = async (currentFolder) => {
var _a;
if (currentFolder.path === "timelines" || currentFolder.path.startsWith("timelines/")) {
return;
}
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) {
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 filteredFiles = allFiles.filter((file) => !this.isInTimelinesFolder(file));
const items = [];
const normalizedTag = tag.replace(/^#/, "").trim();
const isMatchingTag = (fileTag, searchTag) => {
const cleanFileTag = fileTag.replace(/^#/, "").trim().toLowerCase();
const cleanSearchTag = searchTag.toLowerCase();
return cleanFileTag === cleanSearchTag || cleanFileTag.startsWith(cleanSearchTag + "/") || cleanSearchTag.includes(cleanFileTag);
};
for (const file of filteredFiles) {
try {
const cache = this.app.metadataCache.getFileCache(file);
if (!cache)
continue;
let hasMatchingTag = false;
if (cache.tags && cache.tags.length > 0) {
hasMatchingTag = cache.tags.some((tagObj) => {
return isMatchingTag(tagObj.tag, normalizedTag);
});
}
if (!hasMatchingTag && ((_a = cache.frontmatter) == null ? void 0 : _a.tags)) {
const frontmatterTags = cache.frontmatter.tags;
if (Array.isArray(frontmatterTags)) {
hasMatchingTag = frontmatterTags.some((frontmatterTag) => {
if (typeof frontmatterTag !== "string")
return false;
return isMatchingTag(frontmatterTag, normalizedTag);
});
} else if (typeof frontmatterTags === "string") {
const normalizedTags = frontmatterTags.replace(/[,;]/g, " ").trim();
const tagList = normalizedTags.split(/\s+/);
hasMatchingTag = tagList.some((tagItem) => {
return isMatchingTag(tagItem, normalizedTag);
});
}
}
if (hasMatchingTag) {
const item = await this.createTimelineItem(file, normalizedTag);
if (item) {
items.push(item);
}
}
} catch (error) {
console.error(`\u5904\u7406\u6587\u4EF6 ${file.path} \u65F6\u51FA\u9519:`, error);
continue;
}
}
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 shouldFilterFolder = (folderPath) => {
return folderPath === "timelines" || folderPath.startsWith("timelines/");
};
const pushFolder = (folder) => {
if (!shouldFilterFolder(folder.path)) {
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") {
const normalizedTags = frontmatterTags.replace(/[,;]/g, " ").trim();
normalizedTags.split(/\s+/).forEach((tag) => {
if (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");
}
async openAndGetValue() {
return new Promise((resolve) => {
this.resolve = resolve;
this.open();
});
}
getItems() {
return this.app.vault.getMarkdownFiles().filter(
(file) => !file.path.startsWith("timelines/") && file.path !== "timelines"
);
}
getItemText(file) {
return file.path;
}
onChooseItem(file) {
this.resolve(file);
this.close();
}
};
// 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 = this.app.workspace.getActiveFile();
if (!file || !(file instanceof import_obsidian6.TFile) || file.extension !== "md") {
new import_obsidian6.Notice(this.i18n.errors.generateFileFailed);
return;
}
const cache = this.app.metadataCache.getFileCache(file);
if (!cache || !cache.links || cache.links.length === 0) {
new import_obsidian6.Notice(this.i18n.errors.noFileLinks.replace("{filename}", file.basename));
return;
}
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);
new import_obsidian6.Notice(this.i18n.errors.generateFileFailed);
}
}
});
this.addCommand({
id: "generate-timeline-file-from-file-links",
name: this.i18n.commands.generateFileFromFileLinks,
callback: async () => {
try {
const file = this.app.workspace.getActiveFile();
if (!file || !(file instanceof import_obsidian6.TFile) || file.extension !== "md") {
new import_obsidian6.Notice(this.i18n.errors.generateFileFailed);
return;
}
const cache = this.app.metadataCache.getFileCache(file);
if (!cache || !cache.links || cache.links.length === 0) {
new import_obsidian6.Notice(this.i18n.errors.noFileLinks.replace("{filename}", file.basename));
return;
}
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);
new import_obsidian6.Notice(this.i18n.errors.generateFileFailed);
}
}
});
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);
}
}
});
this.registerEvent(
this.app.workspace.on("file-open", async (file) => {
var _a;
if (file instanceof import_obsidian6.TFile && file.path.startsWith("timelines/") && file.extension === "md") {
const cache = this.app.metadataCache.getFileCache(file);
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(":");
const timeline = new Timeline(this.app, this.settings);
let items = [];
if (type === "folder") {
const folder = this.app.vault.getAbstractFileByPath(value);
if (folder instanceof import_obsidian7.TFolder) {
items = await timeline.generateFromFolder(folder);
}
} else if (type === "tag") {
items = await timeline.generateFromTag(value);
} else if (type === "file") {
const targetFile = this.app.vault.getAbstractFileByPath(value);
if (targetFile instanceof import_obsidian6.TFile) {
items = await timeline.generateFromFileLinks(targetFile);
}
} else if (type === "metadata") {
items = await timeline.generateFromMetadata(value);
}
const content = await timeline.generateTimelineMarkdown(items, `Timeline - ${value}`, { type, value });
await this.app.vault.modify(file, content);
}
}
})
);
} 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 */