Files
cours/.obsidian/plugins/header-enhancer/main.js
2026-03-21 19:06:04 +01:00

3524 lines
142 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
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 __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/core.ts
function getHeaderLevel(text, startHeaderLevel) {
const match = text.match(/^#+/);
if (!match)
return [0, 0];
let level = match ? match[0].length : 0;
return [level - startHeaderLevel + 1, level];
}
function getNextNumber(cntNums, headerLevel) {
let nextNums = [...cntNums];
if (nextNums.length >= headerLevel) {
nextNums = nextNums.slice(0, headerLevel);
nextNums[nextNums.length - 1]++;
} else {
while (nextNums.length < headerLevel) {
nextNums.push(1);
}
}
return nextNums;
}
function isNeedInsertNumber(text, splitor) {
const match = text.match(/^(#{1,6})\s+(.*)/);
if (!match)
return false;
const contentAfterHash = match[2];
if (splitor == " ") {
return !/^\d+(?:\.\d+)*\s+/.test(contentAfterHash);
} else {
return !contentAfterHash.contains(splitor);
}
}
function isNeedUpdateNumber(nextNumsStr, text, splitor) {
const match = text.match(/^(#{1,6})\s+(.*)/);
if (!match)
return false;
const contentAfterHash = match[2];
let cntNumsStr;
if (splitor == " ") {
const numMatch = contentAfterHash.match(/^(\d+(?:\.\d+)*)\s+/);
if (!numMatch)
return true;
cntNumsStr = numMatch[1];
} else {
const parts = contentAfterHash.split(splitor);
if (parts.length < 2)
return true;
cntNumsStr = parts[0].trim();
}
return nextNumsStr !== cntNumsStr;
}
function removeHeaderNumber(text, splitor) {
const match = text.match(/^(#{1,6})\s+(.*)/);
if (!match)
return text;
const sharp = match[1];
const contentAfterHash = match[2];
if (splitor == " ") {
const header = contentAfterHash.replace(/^\d+(?:\.\d+)*\s+/, "");
return sharp + " " + header;
} else {
if (!contentAfterHash.contains(splitor))
return text;
const parts = contentAfterHash.split(splitor);
const header = parts.slice(1).join(splitor).trim();
return sharp + " " + header;
}
}
function isHeader(text) {
return /^#{1,6} .*/.test(text.trim());
}
function analyzeHeaderLevels(content) {
const lines = content.split("\n");
const usedLevels = /* @__PURE__ */ new Set();
let isCodeBlock = false;
let headerCount = 0;
for (const line of lines) {
if (line.startsWith("```")) {
isCodeBlock = !isCodeBlock;
if (line.slice(3).includes("```")) {
isCodeBlock = !isCodeBlock;
}
}
if (isCodeBlock)
continue;
if (isHeader(line)) {
const match = line.match(/^#+/);
if (match) {
const level = match[0].length;
usedLevels.add(level);
headerCount++;
}
}
}
if (usedLevels.size === 0) {
return {
minLevel: 0,
maxLevel: 0,
usedLevels: [],
isEmpty: true,
headerCount: 0
};
}
const levels = Array.from(usedLevels).sort((a, b) => a - b);
if (levels.length === 1) {
const singleLevel = levels[0];
return {
minLevel: singleLevel,
maxLevel: Math.min(singleLevel + 2, 6),
// 默认扩展2级但不超过H6
usedLevels: levels,
isEmpty: false,
headerCount
};
}
return {
minLevel: levels[0],
maxLevel: levels[levels.length - 1],
usedLevels: levels,
isEmpty: false,
headerCount
};
}
var init_core = __esm({
"src/core.ts"() {
}
});
// src/utils.ts
function getYaml(editor) {
var _a;
const matchResult = editor.getValue().match(YAML_REGEX);
return (_a = matchResult == null ? void 0 : matchResult[0]) != null ? _a : "";
}
function getAutoNumberingYaml(editor) {
var _a;
const yaml = getYaml(editor);
const parsedYaml = (0, import_obsidian.parseYaml)(yaml.slice(4, -4));
return (_a = parsedYaml == null ? void 0 : parsedYaml["header-auto-numbering"]) != null ? _a : "";
}
function setAutoNumberingYaml(editor, value = DEFAULT_YAML_SETTING) {
const yaml = getYaml(editor);
let parsedYaml = (0, import_obsidian.parseYaml)(yaml.slice(4, -4));
if (!parsedYaml) {
parsedYaml = {};
}
parsedYaml["header-auto-numbering"] = value;
const newContent = `---
${(0, import_obsidian.stringifyYaml)(parsedYaml)}---
`;
const startPosition = { line: 0, ch: 0 };
let endOffset = yaml.length;
if (yaml.length > 0) {
const contentAfterYaml = editor.getValue().substring(yaml.length, yaml.length + 1);
if (contentAfterYaml === "\n") {
endOffset += 1;
}
}
const endPosition = editor.offsetToPos(endOffset);
editor.replaceRange(newContent, startPosition, endPosition);
}
var import_obsidian, YAML_REGEX, DEFAULT_YAML_SETTING;
var init_utils = __esm({
"src/utils.ts"() {
import_obsidian = require("obsidian");
YAML_REGEX = /^---\n(?:((?:.|\n)*?)\n)?---(?=\n|$)/;
DEFAULT_YAML_SETTING = [
"state on",
"start-level h2",
"end-level h6",
"start-at 1",
"separator ."
];
}
});
// src/i18n/en.ts
var en_default;
var init_en = __esm({
"src/i18n/en.ts"() {
en_default = {
settings: {
title: "Header Enhancer Settings",
general: "General",
language: {
name: "Language",
desc: "Language for automatic numbering"
},
statusBar: {
name: "Show on Status Bar",
desc: "Show current header level on status bar"
},
sidebar: {
name: "Show on Sidebar",
desc: "Show Header Enhancer icon in the sidebar ribbon"
},
autoDetect: {
name: "Auto Detect Header Level",
desc: "Automatically detect and use the highest and lowest header levels in the document for numbering"
},
headerLevel: {
start: {
name: "Start Header Level",
desc: "The starting level for headers"
},
max: {
name: "Max Header Level",
desc: "Maximum level for headers"
}
},
autoNumbering: {
title: "Header Auto Numbering",
globalToggle: {
name: "Enable Auto Numbering Function",
desc: "Master switch to enable/disable the entire auto numbering functionality. When disabled, no documents will have auto numbering regardless of other settings."
},
globalDisabled: {
title: "Auto Numbering Function Disabled",
description: "The auto numbering function is currently disabled globally. Enable it above to access other auto numbering settings and use the sidebar button to control individual documents."
},
mode: {
name: "Auto Numbering Mode",
desc: "Control how header auto numbering works",
off: "Off",
on: "On",
yaml: "Controlled by YAML"
},
headerLevel: {
name: "Header Level Numbering Method",
toggleLabel: "\u{1F527} Enable Auto Detection",
desc: {
autoDetect: "\u2705 Auto Detection Mode: Intelligently determine numbering range based on document content",
manual: "\u2699\uFE0F Manual Setting Mode: Use fixed level range settings",
yamlControl: "\u{1F4CB} YAML Control Mode: Configure through file frontmatter"
}
},
startNumber: {
name: "Start Number",
desc: "Start numbering at this number",
placeholder: "Enter a number"
},
separator: {
name: "Number Separator",
desc: "Separator between numbers (one of '. , / -')",
placeholder: "Enter separator"
},
headerSeparator: {
name: "Header Separator",
desc: "Separator between header number and text",
error: "You can't change header separator when auto numbering is enabled"
},
updateBacklinks: {
name: "Update Backlinks",
desc: "\u26A0\uFE0F Warning: Automatically update backlinks when headers change. May impact performance in large vaults."
},
endLevelError: "Max header level should be greater than or equal to start header level",
startLevelError: "Start header level should be less than or equal to max header level",
format: {
name: "Your auto numbering format is",
fromLevel: "from",
toLevel: "to",
autoDetect: "Auto Detect",
manual: "Manual",
yamlControlled: "[Controlled by YAML]",
disabled: "[Disabled]"
},
removeConfirmation: {
title: "Turn Off Auto Numbering",
message: "You are about to turn off auto numbering. What would you like to do with existing numbering in your documents?",
warningTitle: "\u26A0\uFE0F Performance Warning",
warningMessage: "This operation will scan all markdown files in your vault. In large vaults, this may take several minutes and temporarily impact Obsidian's performance.",
removeAndTurnOff: "Remove all numbering and turn off",
removeAndTurnOffDesc: "Scan all files and remove header numbering, then disable auto numbering",
turnOffOnly: "Turn off without removing",
turnOffOnlyDesc: "Disable auto numbering but keep existing numbers in documents",
cancel: "Cancel",
processing: "Processing files...",
progressStatus: "Processed {current} of {total} files",
completed: "Successfully removed numbering from {count} files",
error: "Error occurred while processing files: {error}",
noNumberingFound: "No files with header numbering were found",
manualTip: "You can also manually disable auto numbering for individual files using the sidebar button."
},
activationConfirmation: {
title: "Enable Auto Numbering",
message: "You are about to enable auto numbering. What would you like to do with existing documents in your vault?",
warningTitle: "\u26A0\uFE0F Performance Warning",
warningMessage: "This operation will scan all markdown files in your vault. In large vaults, this may take several minutes and temporarily impact Obsidian's performance.",
addToAll: "Add numbering to all documents",
addToAllDesc: "Scan all files and add header numbering to existing documents",
turnOnOnly: "Turn on without adding",
turnOnOnlyDesc: "Enable auto numbering but keep existing documents unchanged",
cancel: "Cancel",
processing: "Adding numbering to files...",
progressStatus: "Processed {current} of {total} files",
completed: "Successfully added numbering to {count} files",
error: "Error occurred while processing files: {error}",
noHeadersFound: "No files with headers were found",
manualTip: "You can also manually enable auto numbering for individual files using the sidebar button."
}
},
headerFont: {
title: "Header Font Settings",
separate: {
name: "Separate Header Font",
desc: "Use different font settings for markdown headers (# ## ###)"
},
preview: {
title: "Header Font Preview",
sample: "Sample Header"
},
family: {
name: "Font Family",
desc: "Header font family (inherit from global font by default)",
options: {
inherit: "Inherit from global font"
}
},
size: {
name: "Font Size",
desc: "Header font size (inherit from global font size by default)",
options: {
inherit: "Inherit from global size",
smaller: "Smaller",
small: "Small",
normal: "Normal",
large: "Large",
larger: "Larger",
xlarge: "Extra Large",
xxlarge: "Extra Extra Large"
}
}
},
titleFont: {
title: "Title Font Settings",
separate: {
name: "Separate Title Font",
desc: "Use different font settings for document titles"
},
preview: {
title: "Title Font Preview",
sample: "Sample Document Title"
},
family: {
name: "Font Family",
desc: "Title font family (inherit from global font by default)",
options: {
inherit: "Inherit from global font"
}
},
size: {
name: "Font Size",
desc: "Title font size (inherit from global font size by default)",
options: {
inherit: "Inherit from global size",
smaller: "Smaller",
small: "Small",
normal: "Normal",
large: "Large",
larger: "Larger",
xlarge: "Extra Large",
xxlarge: "Extra Extra Large"
}
}
},
yamlMode: {
fallback: {
name: "Documents without YAML configuration",
desc: "Choose how to handle documents that don't have YAML configuration",
noNumbering: "No numbering",
useDefault: "Use default settings"
},
defaultStartLevel: {
name: "Default Start Level",
desc: "Default starting header level for documents without YAML"
},
defaultEndLevel: {
name: "Default End Level",
desc: "Default ending header level for documents without YAML"
},
defaultStartNumber: {
name: "Default Start Number",
desc: "Default starting number for documents without YAML"
},
defaultSeparator: {
name: "Default Separator",
desc: "Default number separator for documents without YAML"
}
},
resetSettings: {
name: "Reset Settings",
confirm: "Are you sure you want to reset settings to default?"
},
moreInfo: "More Information",
author: "Author: ",
license: "License: ",
githubRepo: "GitHub Repository: ",
anyQuestion: "Any questions? "
},
autoDetection: {
currentDocument: "Current Document Analysis",
noActiveDocument: "No active document",
noHeaders: "No headers detected",
detected: "Detected levels",
range: "Level range",
mapping: "Number mapping",
totalHeaders: "Total headers",
modes: {
autoDetect: "\u{1F527} Current Mode: Auto Detect - Intelligently determine numbering range based on document content",
yamlControl: "\u2699\uFE0F Current Mode: YAML Control - Configure through file frontmatter",
manual: "\u{1F3AF} Current Mode: Manual - Use fixed level range"
},
info: {
yamlMode: {
title: "\u2699\uFE0F YAML Control Mode",
description: "In this mode, header numbering is controlled by YAML frontmatter in files. Please add the following configuration at the beginning of your document:",
usage: "You can use plugin commands to quickly add or modify these configurations."
},
offMode: {
title: "\u23F8\uFE0F Auto Numbering Disabled",
description: 'Header auto numbering is currently disabled. To enable auto numbering, please select "On" or "Controlled by YAML" mode above.'
}
}
},
statusBar: {
title: "Header Enhancer",
off: "Off",
on: "On",
yaml: "YAML",
auto: "Auto",
autoNoHeaders: "Auto(No Headers)",
globalDisabled: "Global Disabled",
documentEnabled: "Document On",
documentDisabled: "Document Off"
},
commands: {
toggleGlobalAutoNumbering: "Toggle Global Auto Numbering",
toggleDocumentAutoNumbering: "Toggle Document Auto Numbering",
resetAutoNumberingYaml: "Reset Auto Numbering YAML",
removeAutoNumberingYaml: "Remove Auto Numbering YAML",
applyCustomYamlConfig: "Apply Custom YAML Configuration"
},
notices: {
noActiveView: "No active MarkdownView, cannot toggle auto numbering.",
globalDisabledNotice: "Auto numbering is globally disabled. Enable it in settings first.",
globalAutoNumberingEnabled: "Global auto numbering enabled",
globalAutoNumberingDisabled: "Global auto numbering disabled",
autoNumberingEnabledForDocument: "Auto numbering enabled for this document",
autoNumberingDisabledForDocument: "Auto numbering disabled for this document",
yamlAlreadyExists: "auto numbering yaml already exists",
yamlNotExists: "auto numbering yaml not exists",
yamlTemplateInserted: "YAML configuration template inserted successfully"
}
};
}
});
// src/i18n/zh.ts
var zh_default;
var init_zh = __esm({
"src/i18n/zh.ts"() {
zh_default = {
settings: {
title: "\u6807\u9898\u589E\u5F3A\u5668\u8BBE\u7F6E",
general: "\u5E38\u89C4",
language: {
name: "\u8BED\u8A00",
desc: "\u81EA\u52A8\u7F16\u53F7\u7684\u8BED\u8A00"
},
statusBar: {
name: "\u5728\u72B6\u6001\u680F\u663E\u793A",
desc: "\u5728\u72B6\u6001\u680F\u663E\u793A\u5F53\u524D\u6807\u9898\u7EA7\u522B"
},
sidebar: {
name: "\u5728\u4FA7\u8FB9\u680F\u663E\u793A",
desc: "\u5728\u4FA7\u8FB9\u680F\u529F\u80FD\u533A\u663E\u793A\u6807\u9898\u589E\u5F3A\u5668\u56FE\u6807"
},
autoDetect: {
name: "\u81EA\u52A8\u68C0\u6D4B\u6807\u9898\u7EA7\u522B",
desc: "\u6839\u636E\u4E0A\u4E0B\u6587\u81EA\u52A8\u68C0\u6D4B\u6807\u9898\u7EA7\u522B"
},
headerLevel: {
start: {
name: "\u8D77\u59CB\u6807\u9898\u7EA7\u522B",
desc: "\u6807\u9898\u7684\u8D77\u59CB\u7EA7\u522B"
},
max: {
name: "\u6700\u5927\u6807\u9898\u7EA7\u522B",
desc: "\u6807\u9898\u7684\u6700\u5927\u7EA7\u522B"
}
},
autoNumbering: {
title: "\u6807\u9898\u81EA\u52A8\u7F16\u53F7",
globalToggle: {
name: "\u542F\u7528\u81EA\u52A8\u7F16\u53F7\u529F\u80FD",
desc: "\u4E3B\u5F00\u5173\uFF0C\u7528\u4E8E\u542F\u7528/\u7981\u7528\u6574\u4E2A\u81EA\u52A8\u7F16\u53F7\u529F\u80FD\u3002\u7981\u7528\u65F6\uFF0C\u65E0\u8BBA\u5176\u4ED6\u8BBE\u7F6E\u5982\u4F55\uFF0C\u90FD\u4E0D\u4F1A\u6709\u6587\u6863\u8FDB\u884C\u81EA\u52A8\u7F16\u53F7\u3002"
},
globalDisabled: {
title: "\u81EA\u52A8\u7F16\u53F7\u529F\u80FD\u5DF2\u7981\u7528",
description: "\u81EA\u52A8\u7F16\u53F7\u529F\u80FD\u5F53\u524D\u5728\u5168\u5C40\u8303\u56F4\u5185\u88AB\u7981\u7528\u3002\u8BF7\u5728\u4E0A\u65B9\u542F\u7528\u5B83\u4EE5\u8BBF\u95EE\u5176\u4ED6\u81EA\u52A8\u7F16\u53F7\u8BBE\u7F6E\u5E76\u4F7F\u7528\u4FA7\u8FB9\u680F\u6309\u94AE\u63A7\u5236\u5355\u4E2A\u6587\u6863\u3002"
},
mode: {
name: "\u81EA\u52A8\u7F16\u53F7\u6A21\u5F0F",
desc: "\u63A7\u5236\u6807\u9898\u81EA\u52A8\u7F16\u53F7\u7684\u5DE5\u4F5C\u65B9\u5F0F",
off: "\u5173\u95ED",
on: "\u542F\u7528",
yaml: "\u901A\u8FC7YAML\u63A7\u5236"
},
headerLevel: {
name: "\u6807\u9898\u5C42\u7EA7\u7F16\u53F7\u65B9\u5F0F",
toggleLabel: "\u{1F527} \u542F\u7528\u81EA\u52A8\u68C0\u6D4B",
desc: {
autoDetect: "\u2705 \u81EA\u52A8\u68C0\u6D4B\u6A21\u5F0F\uFF1A\u6839\u636E\u6587\u6863\u5185\u5BB9\u667A\u80FD\u786E\u5B9A\u7F16\u53F7\u8303\u56F4",
manual: "\u2699\uFE0F \u624B\u52A8\u8BBE\u7F6E\u6A21\u5F0F\uFF1A\u4F7F\u7528\u56FA\u5B9A\u7684\u5C42\u7EA7\u8303\u56F4\u8BBE\u7F6E",
yamlControl: "\u{1F4CB} YAML\u63A7\u5236\u6A21\u5F0F\uFF1A\u901A\u8FC7\u6587\u4EF6\u524D\u7F6E\u5143\u6570\u636E\u914D\u7F6E"
}
},
startNumber: {
name: "\u8D77\u59CB\u6570\u5B57",
desc: "\u4ECE\u6B64\u6570\u5B57\u5F00\u59CB\u7F16\u53F7",
placeholder: "\u8F93\u5165\u6570\u5B57",
error: "\u8D77\u59CB\u6570\u5B57\u5FC5\u987B\u662F\u6709\u6548\u6570\u5B57"
},
separator: {
name: "\u6570\u5B57\u5206\u9694\u7B26",
desc: "\u6570\u5B57\u4E4B\u95F4\u7684\u5206\u9694\u7B26\uFF08. , / - \u4E2D\u7684\u4E00\u4E2A\uFF09",
placeholder: "\u8F93\u5165\u5206\u9694\u7B26",
error: "\u5206\u9694\u7B26\u5FC5\u987B\u662F\u4EE5\u4E0B\u4E4B\u4E00\uFF1A. , / -"
},
headerSeparator: {
name: "\u6807\u9898\u5206\u9694\u7B26",
desc: "\u6570\u5B57\u548C\u6807\u9898\u6587\u672C\u4E4B\u95F4\u7684\u5206\u9694\u7B26",
error: "\u65E0\u6548\u7684\u6807\u9898\u5206\u9694\u7B26"
},
updateBacklinks: {
name: "\u66F4\u65B0\u53CD\u5411\u94FE\u63A5",
desc: "\u26A0\uFE0F \u8B66\u544A\uFF1A\u5F53\u6807\u9898\u6539\u53D8\u65F6\u81EA\u52A8\u66F4\u65B0\u53CD\u5411\u94FE\u63A5\u3002\u5728\u5927\u578B\u77E5\u8BC6\u5E93\u4E2D\u53EF\u80FD\u4F1A\u5F71\u54CD\u6027\u80FD\u3002"
},
endLevelError: "\u6700\u5927\u6807\u9898\u7EA7\u522B\u5E94\u8BE5\u5927\u4E8E\u6216\u7B49\u4E8E\u8D77\u59CB\u6807\u9898\u7EA7\u522B",
startLevelError: "\u8D77\u59CB\u6807\u9898\u7EA7\u522B\u5E94\u8BE5\u5C0F\u4E8E\u6216\u7B49\u4E8E\u6700\u5927\u6807\u9898\u7EA7\u522B",
format: {
name: "\u5F53\u524D\u683C\u5F0F",
fromLevel: "\u4ECE",
toLevel: "\u5230",
autoDetect: "\u81EA\u52A8\u68C0\u6D4B",
manual: "\u624B\u52A8",
yamlControlled: "\uFF08YAML\u63A7\u5236\uFF09",
disabled: "\uFF08\u5DF2\u5173\u95ED\uFF09"
},
removeConfirmation: {
title: "\u5173\u95ED\u81EA\u52A8\u7F16\u53F7",
message: "\u60A8\u5373\u5C06\u5173\u95ED\u81EA\u52A8\u7F16\u53F7\u529F\u80FD\u3002\u5BF9\u4E8E\u6587\u6863\u4E2D\u73B0\u6709\u7684\u7F16\u53F7\uFF0C\u60A8\u5E0C\u671B\u5982\u4F55\u5904\u7406\uFF1F",
warningTitle: "\u26A0\uFE0F \u6027\u80FD\u8B66\u544A",
warningMessage: "\u6B64\u64CD\u4F5C\u5C06\u626B\u63CF\u77E5\u8BC6\u5E93\u4E2D\u7684\u6240\u6709markdown\u6587\u4EF6\u3002\u5728\u5927\u578B\u77E5\u8BC6\u5E93\u4E2D\uFF0C\u8FD9\u53EF\u80FD\u9700\u8981\u51E0\u5206\u949F\u65F6\u95F4\u5E76\u6682\u65F6\u5F71\u54CDObsidian\u7684\u6027\u80FD\u3002",
removeAndTurnOff: "\u79FB\u9664\u6240\u6709\u7F16\u53F7\u5E76\u5173\u95ED",
removeAndTurnOffDesc: "\u626B\u63CF\u6240\u6709\u6587\u4EF6\u5E76\u79FB\u9664\u6807\u9898\u7F16\u53F7\uFF0C\u7136\u540E\u7981\u7528\u81EA\u52A8\u7F16\u53F7",
turnOffOnly: "\u4EC5\u5173\u95ED\u4E0D\u79FB\u9664",
turnOffOnlyDesc: "\u7981\u7528\u81EA\u52A8\u7F16\u53F7\u4F46\u4FDD\u7559\u6587\u6863\u4E2D\u7684\u73B0\u6709\u7F16\u53F7",
cancel: "\u53D6\u6D88",
processing: "\u6B63\u5728\u5904\u7406\u6587\u4EF6...",
progressStatus: "\u5DF2\u5904\u7406 {current}/{total} \u4E2A\u6587\u4EF6",
completed: "\u6210\u529F\u4ECE {count} \u4E2A\u6587\u4EF6\u4E2D\u79FB\u9664\u7F16\u53F7",
error: "\u5904\u7406\u6587\u4EF6\u65F6\u53D1\u751F\u9519\u8BEF\uFF1A{error}",
noNumberingFound: "\u672A\u627E\u5230\u5305\u542B\u6807\u9898\u7F16\u53F7\u7684\u6587\u4EF6",
manualTip: "\u4F60\u4E5F\u53EF\u4EE5\u901A\u8FC7\u4FA7\u8FB9\u680F\u6309\u94AE\u624B\u52A8\u5173\u95ED\u5355\u4E2A\u6587\u4EF6\u7684\u81EA\u52A8\u7F16\u53F7\u3002"
},
activationConfirmation: {
title: "\u542F\u7528\u81EA\u52A8\u7F16\u53F7",
message: "\u60A8\u5373\u5C06\u542F\u7528\u81EA\u52A8\u7F16\u53F7\u529F\u80FD\u3002\u5BF9\u4E8E\u77E5\u8BC6\u5E93\u4E2D\u73B0\u6709\u7684\u6587\u6863\uFF0C\u60A8\u5E0C\u671B\u5982\u4F55\u5904\u7406\uFF1F",
warningTitle: "\u26A0\uFE0F \u6027\u80FD\u8B66\u544A",
warningMessage: "\u6B64\u64CD\u4F5C\u5C06\u626B\u63CF\u77E5\u8BC6\u5E93\u4E2D\u7684\u6240\u6709markdown\u6587\u4EF6\u3002\u5728\u5927\u578B\u77E5\u8BC6\u5E93\u4E2D\uFF0C\u8FD9\u53EF\u80FD\u9700\u8981\u51E0\u5206\u949F\u65F6\u95F4\u5E76\u6682\u65F6\u5F71\u54CDObsidian\u7684\u6027\u80FD\u3002",
addToAll: "\u4E3A\u6240\u6709\u6587\u6863\u6DFB\u52A0\u7F16\u53F7",
addToAllDesc: "\u626B\u63CF\u6240\u6709\u6587\u4EF6\u5E76\u4E3A\u73B0\u6709\u6587\u6863\u6DFB\u52A0\u6807\u9898\u7F16\u53F7",
turnOnOnly: "\u4EC5\u542F\u7528\u4E0D\u6DFB\u52A0",
turnOnOnlyDesc: "\u542F\u7528\u81EA\u52A8\u7F16\u53F7\u529F\u80FD\u4F46\u4FDD\u6301\u73B0\u6709\u6587\u6863\u4E0D\u53D8",
cancel: "\u53D6\u6D88",
processing: "\u6B63\u5728\u4E3A\u6587\u4EF6\u6DFB\u52A0\u7F16\u53F7...",
progressStatus: "\u5DF2\u5904\u7406 {current}/{total} \u4E2A\u6587\u4EF6",
completed: "\u6210\u529F\u4E3A {count} \u4E2A\u6587\u4EF6\u6DFB\u52A0\u7F16\u53F7",
error: "\u5904\u7406\u6587\u4EF6\u65F6\u53D1\u751F\u9519\u8BEF\uFF1A{error}",
noHeadersFound: "\u672A\u627E\u5230\u5305\u542B\u6807\u9898\u7684\u6587\u4EF6",
manualTip: "\u4F60\u4E5F\u53EF\u4EE5\u901A\u8FC7\u4FA7\u8FB9\u680F\u6309\u94AE\u624B\u52A8\u542F\u7528\u5355\u4E2A\u6587\u4EF6\u7684\u81EA\u52A8\u7F16\u53F7\u3002"
}
},
headerFont: {
title: "\u6807\u9898\u5B57\u4F53\u8BBE\u7F6E",
separate: {
name: "\u72EC\u7ACB\u6807\u9898\u5B57\u4F53",
desc: "\u4E3Amarkdown\u6807\u9898\u4F7F\u7528\u72EC\u7ACB\u7684\u5B57\u4F53\u8BBE\u7F6E (# ## ###)"
},
preview: {
title: "\u6807\u9898\u5B57\u4F53\u9884\u89C8",
sample: "\u793A\u4F8B\u6807\u9898"
},
family: {
name: "\u5B57\u4F53\u65CF",
desc: "\u6807\u9898\u5B57\u4F53\u65CF\uFF08\u9ED8\u8BA4\u7EE7\u627F\u5168\u5C40\u5B57\u4F53\uFF09",
options: {
inherit: "\u7EE7\u627F\u5168\u5C40\u5B57\u4F53"
}
},
size: {
name: "\u5B57\u4F53\u5927\u5C0F",
desc: "\u6807\u9898\u5B57\u4F53\u5927\u5C0F\uFF08\u9ED8\u8BA4\u7EE7\u627F\u5168\u5C40\u5B57\u4F53\u5927\u5C0F\uFF09",
options: {
inherit: "\u7EE7\u627F\u5168\u5C40\u5927\u5C0F",
smaller: "\u8F83\u5C0F",
small: "\u5C0F",
normal: "\u6B63\u5E38",
large: "\u5927",
larger: "\u8F83\u5927",
xlarge: "\u7279\u5927",
xxlarge: "\u8D85\u5927"
}
}
},
titleFont: {
title: "\u6587\u6863\u6807\u9898\u5B57\u4F53\u8BBE\u7F6E",
separate: {
name: "\u72EC\u7ACB\u6587\u6863\u6807\u9898\u5B57\u4F53",
desc: "\u4E3A\u6587\u6863\u6807\u9898\u4F7F\u7528\u72EC\u7ACB\u7684\u5B57\u4F53\u8BBE\u7F6E"
},
preview: {
title: "\u6587\u6863\u6807\u9898\u5B57\u4F53\u9884\u89C8",
sample: "\u793A\u4F8B\u6587\u6863\u6807\u9898"
},
family: {
name: "\u5B57\u4F53\u65CF",
desc: "\u6587\u6863\u6807\u9898\u5B57\u4F53\u65CF\uFF08\u9ED8\u8BA4\u7EE7\u627F\u5168\u5C40\u5B57\u4F53\uFF09",
options: {
inherit: "\u7EE7\u627F\u5168\u5C40\u5B57\u4F53"
}
},
size: {
name: "\u5B57\u4F53\u5927\u5C0F",
desc: "\u6587\u6863\u6807\u9898\u5B57\u4F53\u5927\u5C0F\uFF08\u9ED8\u8BA4\u7EE7\u627F\u5168\u5C40\u5B57\u4F53\u5927\u5C0F\uFF09",
options: {
inherit: "\u7EE7\u627F\u5168\u5C40\u5927\u5C0F",
smaller: "\u8F83\u5C0F",
small: "\u5C0F",
normal: "\u6B63\u5E38",
large: "\u5927",
larger: "\u8F83\u5927",
xlarge: "\u7279\u5927",
xxlarge: "\u8D85\u5927"
}
}
},
yamlMode: {
fallback: {
name: "\u6CA1\u6709YAML\u914D\u7F6E\u7684\u6587\u6863\u5904\u7406\u65B9\u5F0F",
desc: "\u9009\u62E9\u5982\u4F55\u5904\u7406\u6CA1\u6709YAML\u914D\u7F6E\u7684\u6587\u6863",
noNumbering: "\u4E0D\u7F16\u53F7",
useDefault: "\u4F7F\u7528\u9ED8\u8BA4\u8BBE\u7F6E"
},
defaultStartLevel: {
name: "\u9ED8\u8BA4\u8D77\u59CB\u5C42\u7EA7",
desc: "\u6CA1\u6709YAML\u914D\u7F6E\u7684\u6587\u6863\u4F7F\u7528\u7684\u9ED8\u8BA4\u8D77\u59CB\u6807\u9898\u5C42\u7EA7"
},
defaultEndLevel: {
name: "\u9ED8\u8BA4\u7ED3\u675F\u5C42\u7EA7",
desc: "\u6CA1\u6709YAML\u914D\u7F6E\u7684\u6587\u6863\u4F7F\u7528\u7684\u9ED8\u8BA4\u7ED3\u675F\u6807\u9898\u5C42\u7EA7"
},
defaultStartNumber: {
name: "\u9ED8\u8BA4\u8D77\u59CB\u6570\u5B57",
desc: "\u6CA1\u6709YAML\u914D\u7F6E\u7684\u6587\u6863\u4F7F\u7528\u7684\u9ED8\u8BA4\u8D77\u59CB\u7F16\u53F7"
},
defaultSeparator: {
name: "\u9ED8\u8BA4\u5206\u9694\u7B26",
desc: "\u6CA1\u6709YAML\u914D\u7F6E\u7684\u6587\u6863\u4F7F\u7528\u7684\u9ED8\u8BA4\u6570\u5B57\u5206\u9694\u7B26"
}
},
resetSettings: {
name: "\u91CD\u7F6E\u8BBE\u7F6E",
confirm: "\u60A8\u786E\u5B9A\u8981\u5C06\u6240\u6709\u8BBE\u7F6E\u91CD\u7F6E\u4E3A\u9ED8\u8BA4\u503C\u5417\uFF1F"
},
moreInfo: "\u66F4\u591A\u4FE1\u606F",
author: "\u4F5C\u8005\uFF1A",
license: "\u8BB8\u53EF\u8BC1\uFF1A",
githubRepo: "GitHub \u4ED3\u5E93\uFF1A",
anyQuestion: "\u6709\u4EFB\u4F55\u95EE\u9898\uFF1F"
},
autoDetection: {
currentDocument: "\u5F53\u524D\u6587\u6863\u68C0\u6D4B\u7ED3\u679C",
noActiveDocument: "\u6CA1\u6709\u6D3B\u52A8\u6587\u6863",
noHeaders: "\u672A\u68C0\u6D4B\u5230\u6807\u9898",
detected: "\u68C0\u6D4B\u5230\u5C42\u7EA7",
range: "\u5C42\u7EA7\u8303\u56F4",
mapping: "\u7F16\u53F7\u6620\u5C04",
totalHeaders: "\u6807\u9898\u603B\u6570",
modes: {
autoDetect: "\u{1F527} \u5F53\u524D\u6A21\u5F0F\uFF1A\u81EA\u52A8\u68C0\u6D4B - \u5C06\u6839\u636E\u6587\u6863\u5185\u5BB9\u667A\u80FD\u786E\u5B9A\u7F16\u53F7\u8303\u56F4",
yamlControl: "\u2699\uFE0F \u5F53\u524D\u6A21\u5F0F\uFF1AYAML\u63A7\u5236 - \u901A\u8FC7\u6587\u4EF6\u524D\u7F6E\u5143\u6570\u636E\u914D\u7F6E",
manual: "\u{1F3AF} \u5F53\u524D\u6A21\u5F0F\uFF1A\u624B\u52A8\u8BBE\u7F6E - \u4F7F\u7528\u56FA\u5B9A\u7684\u5C42\u7EA7\u8303\u56F4"
},
info: {
yamlMode: {
title: "\u2699\uFE0F YAML\u63A7\u5236\u6A21\u5F0F",
description: "\u5728\u6B64\u6A21\u5F0F\u4E0B\uFF0C\u6807\u9898\u7F16\u53F7\u7531\u6587\u4EF6\u7684YAML\u524D\u7F6E\u5143\u6570\u636E\u63A7\u5236\u3002\u8BF7\u5728\u6587\u6863\u5F00\u5934\u6DFB\u52A0\u5982\u4E0B\u914D\u7F6E\uFF1A",
usage: "\u60A8\u53EF\u4EE5\u4F7F\u7528\u63D2\u4EF6\u547D\u4EE4\u6765\u5FEB\u901F\u6DFB\u52A0\u6216\u4FEE\u6539\u8FD9\u4E9B\u914D\u7F6E\u3002"
},
offMode: {
title: "\u23F8\uFE0F \u81EA\u52A8\u7F16\u53F7\u5DF2\u5173\u95ED",
description: '\u5F53\u524D\u6807\u9898\u81EA\u52A8\u7F16\u53F7\u529F\u80FD\u5DF2\u7981\u7528\u3002\u8981\u542F\u7528\u81EA\u52A8\u7F16\u53F7\uFF0C\u8BF7\u5728\u4E0A\u65B9\u9009\u62E9"\u542F\u7528"\u6216"\u901A\u8FC7YAML\u63A7\u5236"\u6A21\u5F0F\u3002'
}
}
},
statusBar: {
title: "\u6807\u9898\u589E\u5F3A\u5668",
off: "\u5173\u95ED",
on: "\u542F\u7528",
yaml: "YAML",
auto: "\u81EA\u52A8",
autoNoHeaders: "\u81EA\u52A8(\u65E0\u6807\u9898)",
globalDisabled: "\u5168\u5C40\u7981\u7528",
documentEnabled: "\u6587\u6863\u542F\u7528",
documentDisabled: "\u6587\u6863\u5173\u95ED"
},
commands: {
toggleGlobalAutoNumbering: "\u5207\u6362\u5168\u5C40\u81EA\u52A8\u7F16\u53F7",
toggleDocumentAutoNumbering: "\u5207\u6362\u6587\u6863\u81EA\u52A8\u7F16\u53F7",
resetAutoNumberingYaml: "\u91CD\u7F6E\u81EA\u52A8\u7F16\u53F7YAML\u914D\u7F6E",
removeAutoNumberingYaml: "\u79FB\u9664\u81EA\u52A8\u7F16\u53F7YAML\u914D\u7F6E",
applyCustomYamlConfig: "\u4E3A\u5F53\u524D\u6587\u4EF6\u5E94\u7528\u81EA\u5B9A\u4E49\u914D\u7F6E"
},
notices: {
noActiveView: "\u6CA1\u6709\u6D3B\u8DC3\u7684MarkdownView\uFF0C\u65E0\u6CD5\u5207\u6362\u81EA\u52A8\u7F16\u53F7\u3002",
globalDisabledNotice: "\u81EA\u52A8\u7F16\u53F7\u5728\u5168\u5C40\u8303\u56F4\u5185\u88AB\u7981\u7528\u3002\u8BF7\u5148\u5728\u8BBE\u7F6E\u4E2D\u542F\u7528\u3002",
globalAutoNumberingEnabled: "\u5168\u5C40\u81EA\u52A8\u7F16\u53F7\u5DF2\u542F\u7528",
globalAutoNumberingDisabled: "\u5168\u5C40\u81EA\u52A8\u7F16\u53F7\u5DF2\u7981\u7528",
autoNumberingEnabledForDocument: "\u5DF2\u4E3A\u6B64\u6587\u6863\u542F\u7528\u81EA\u52A8\u7F16\u53F7",
autoNumberingDisabledForDocument: "\u5DF2\u4E3A\u6B64\u6587\u6863\u7981\u7528\u81EA\u52A8\u7F16\u53F7",
yamlAlreadyExists: "\u81EA\u52A8\u7F16\u53F7YAML\u914D\u7F6E\u5DF2\u5B58\u5728",
yamlNotExists: "\u81EA\u52A8\u7F16\u53F7YAML\u914D\u7F6E\u4E0D\u5B58\u5728",
yamlTemplateInserted: "YAML\u914D\u7F6E\u6A21\u677F\u5DF2\u6210\u529F\u63D2\u5165"
}
};
}
});
// src/i18n/index.ts
var translations, I18n;
var init_i18n = __esm({
"src/i18n/index.ts"() {
init_en();
init_zh();
translations = {
en: en_default,
zh: zh_default
};
I18n = class {
constructor() {
this.currentLanguage = "en";
}
static getInstance() {
if (!I18n.instance) {
I18n.instance = new I18n();
}
return I18n.instance;
}
setLanguage(lang) {
if (translations[lang]) {
this.currentLanguage = lang;
}
}
t(key, placeholders) {
const keys = key.split(".");
let value = translations[this.currentLanguage];
for (const k of keys) {
if (value && value[k]) {
value = value[k];
} else {
value = translations["en"];
for (const fallbackKey of keys) {
if (value && value[fallbackKey]) {
value = value[fallbackKey];
} else {
return key;
}
}
}
}
let result = typeof value === "string" ? value : key;
if (placeholders) {
for (const [placeholder, replacement] of Object.entries(placeholders)) {
result = result.replace(new RegExp(`\\{${placeholder}\\}`, "g"), replacement);
}
}
return result;
}
};
}
});
// src/config.ts
function getAutoNumberingConfig(setting, editor, getDocumentState, currentFilePath) {
let config = {
state: setting.autoNumberingMode !== "off" /* OFF */,
startLevel: setting.startHeaderLevel,
endLevel: setting.endHeaderLevel,
startNumber: parseInt(setting.autoNumberingStartNumber),
separator: setting.autoNumberingSeparator
};
if (!setting.globalAutoNumberingEnabled) {
config.state = false;
return config;
}
if (getDocumentState && currentFilePath) {
const documentEnabled = getDocumentState(currentFilePath);
if (!documentEnabled) {
config.state = false;
return config;
}
}
if (setting.autoNumberingMode === "yaml" /* YAML_CONTROLLED */) {
config = applyYamlConfig(config, editor, setting);
} else if (setting.isAutoDetectHeaderLevel && setting.autoNumberingMode === "on" /* ON */) {
const content = editor.getValue();
const analysis = analyzeHeaderLevels(content);
if (!analysis.isEmpty) {
config.startLevel = analysis.minLevel;
config.endLevel = analysis.maxLevel;
}
}
return config;
}
function applyYamlConfig(config, editor, setting) {
const yaml = getAutoNumberingYaml(editor);
if (yaml === "") {
if (setting.yamlFallbackMode === "no_numbering" /* NO_NUMBERING */) {
config.state = false;
} else {
config.state = true;
config.startLevel = setting.yamlDefaultStartLevel;
config.endLevel = setting.yamlDefaultEndLevel;
config.startNumber = parseInt(setting.yamlDefaultStartNumber);
config.separator = setting.yamlDefaultSeparator;
}
return config;
}
let hasDeprecatedKeys = false;
const deprecatedKeys = [];
for (const item of yaml) {
const [key, ...valueParts] = item.split(" ");
const value = valueParts.join(" ");
switch (key) {
case "state":
config.state = value === "on";
break;
case "start-level":
config.startLevel = parseInt(value.substring(1));
break;
case "first-level":
hasDeprecatedKeys = true;
deprecatedKeys.push("first-level");
config.startLevel = parseInt(value.substring(1));
break;
case "end-level":
config.endLevel = parseInt(value.substring(1));
break;
case "max":
hasDeprecatedKeys = true;
deprecatedKeys.push("max");
config.endLevel = config.startLevel + parseInt(value) - 1;
break;
case "start-at":
config.startNumber = parseInt(value);
break;
case "separator":
config.separator = value;
break;
}
}
if (hasDeprecatedKeys) {
console.warn(
`[Header Enhancer] Deprecated YAML keys detected: ${deprecatedKeys.join(", ")}. Please update to new format: use "start-level" instead of "first-level", and "end-level" instead of "max". The old format will be removed in a future version.`
);
}
return config;
}
var init_config = __esm({
"src/config.ts"() {
init_setting();
init_utils();
init_core();
}
});
// src/dialogs.ts
var dialogs_exports = {};
__export(dialogs_exports, {
AutoNumberingActivationDialog: () => AutoNumberingActivationDialog,
AutoNumberingRemovalDialog: () => AutoNumberingRemovalDialog
});
var import_obsidian2, AutoNumberingRemovalDialog, AutoNumberingActivationDialog;
var init_dialogs = __esm({
"src/dialogs.ts"() {
import_obsidian2 = require("obsidian");
init_i18n();
init_core();
init_config();
AutoNumberingRemovalDialog = class extends import_obsidian2.Modal {
constructor(app, plugin, onConfirm) {
super(app);
this.progressContainer = null;
this.isProcessing = false;
this.plugin = plugin;
this.onConfirm = onConfirm;
this.setTitle("Auto Numbering Settings");
}
onOpen() {
const i18n = I18n.getInstance();
const { contentEl } = this;
contentEl.empty();
contentEl.addClass("header-enhancer-removal-dialog");
contentEl.createEl("h2", {
text: i18n.t("settings.autoNumbering.removeConfirmation.title"),
cls: "modal-title"
});
contentEl.createEl("p", {
text: i18n.t("settings.autoNumbering.removeConfirmation.message"),
cls: "modal-message"
});
const actionsEl = contentEl.createDiv({ cls: "modal-actions" });
const removeAndTurnOffSetting = new import_obsidian2.Setting(actionsEl).setName(i18n.t("settings.autoNumbering.removeConfirmation.removeAndTurnOff")).setDesc(i18n.t("settings.autoNumbering.removeConfirmation.removeAndTurnOffDesc")).addButton((button) => {
button.setButtonText(i18n.t("settings.autoNumbering.removeConfirmation.removeAndTurnOff")).setCta().onClick(async () => {
if (!this.isProcessing) {
await this.handleRemoveAndTurnOff();
}
});
});
const warningEl = removeAndTurnOffSetting.descEl.createDiv({ cls: "setting-item-warning" });
warningEl.createEl("span", {
text: i18n.t("settings.autoNumbering.removeConfirmation.warningTitle") + " ",
cls: "warning-label"
});
warningEl.createEl("span", {
text: i18n.t("settings.autoNumbering.removeConfirmation.warningMessage"),
cls: "warning-text"
});
const manualTipEl = removeAndTurnOffSetting.descEl.createDiv({ cls: "setting-item-tip" });
manualTipEl.createEl("span", {
text: i18n.t("settings.autoNumbering.removeConfirmation.manualTip"),
cls: "manual-tip-text"
});
new import_obsidian2.Setting(actionsEl).setName(i18n.t("settings.autoNumbering.removeConfirmation.turnOffOnly")).setDesc(i18n.t("settings.autoNumbering.removeConfirmation.turnOffOnlyDesc")).addButton((button) => {
button.setButtonText(i18n.t("settings.autoNumbering.removeConfirmation.turnOffOnly")).onClick(async () => {
if (!this.isProcessing) {
await this.handleTurnOffOnly();
}
});
});
const cancelButtonEl = actionsEl.createDiv({ cls: "modal-cancel" });
new import_obsidian2.Setting(cancelButtonEl).addButton((button) => {
button.setButtonText(i18n.t("settings.autoNumbering.removeConfirmation.cancel")).onClick(() => {
if (!this.isProcessing) {
this.close();
}
});
});
this.progressContainer = contentEl.createDiv({
cls: "progress-container",
attr: { style: "display: none;" }
});
}
async handleRemoveAndTurnOff() {
this.isProcessing = true;
try {
this.showProgress();
await this.removeAllHeaderNumbers();
await this.onConfirm(true);
this.close();
} catch (error) {
const i18n = I18n.getInstance();
new import_obsidian2.Notice(i18n.t("settings.autoNumbering.removeConfirmation.error", {
error: error.message
}));
this.hideProgress();
} finally {
this.isProcessing = false;
}
}
async handleTurnOffOnly() {
this.isProcessing = true;
try {
await this.onConfirm(false);
this.close();
} finally {
this.isProcessing = false;
}
}
showProgress() {
const i18n = I18n.getInstance();
if (this.progressContainer) {
this.progressContainer.style.display = "block";
this.progressContainer.empty();
this.progressContainer.createEl("p", {
text: i18n.t("settings.autoNumbering.removeConfirmation.processing"),
cls: "progress-text"
});
}
this.contentEl.querySelectorAll("button").forEach((button) => {
button.setAttribute("disabled", "true");
});
}
hideProgress() {
if (this.progressContainer) {
this.progressContainer.style.display = "none";
this.progressContainer.empty();
}
this.contentEl.querySelectorAll("button").forEach((button) => {
button.removeAttribute("disabled");
});
}
async removeAllHeaderNumbers() {
const i18n = I18n.getInstance();
const markdownFiles = this.app.vault.getMarkdownFiles();
let processedCount = 0;
let modifiedCount = 0;
const batchSize = 5;
const totalFiles = markdownFiles.length;
for (let batchStart = 0; batchStart < totalFiles; batchStart += batchSize) {
const batch = markdownFiles.slice(batchStart, Math.min(batchStart + batchSize, totalFiles));
for (const file of batch) {
try {
const modified = await this.processFile(file);
if (modified) {
modifiedCount++;
await new Promise((resolve) => setTimeout(resolve, 10));
}
processedCount++;
this.updateProgress(processedCount, totalFiles);
} catch (error) {
console.error(`Error processing file ${file.path}:`, error);
}
await new Promise((resolve) => setTimeout(resolve, 5));
}
await new Promise((resolve) => setTimeout(resolve, 50));
}
if (modifiedCount > 0) {
new import_obsidian2.Notice(i18n.t("settings.autoNumbering.removeConfirmation.completed", {
count: modifiedCount.toString()
}));
} else {
new import_obsidian2.Notice(i18n.t("settings.autoNumbering.removeConfirmation.noNumberingFound"));
}
}
async processFile(file) {
try {
const activeLeaves = this.app.workspace.getLeavesOfType("markdown");
const isCurrentlyOpen = activeLeaves.some((leaf) => {
const view = leaf.view;
return view.file && view.file.path === file.path;
});
const content = await this.app.vault.read(file);
const lines = content.split("\n");
let modified = false;
let isInCodeBlock = false;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (line.startsWith("```")) {
isInCodeBlock = !isInCodeBlock;
continue;
}
if (isInCodeBlock) {
continue;
}
if (isHeader(line)) {
const newLine = removeHeaderNumber(line, this.plugin.settings.autoNumberingHeaderSeparator);
if (newLine !== line) {
lines[i] = newLine;
modified = true;
}
}
}
if (modified) {
await this.app.vault.modify(file, lines.join("\n"));
if (isCurrentlyOpen) {
await new Promise((resolve) => setTimeout(resolve, 20));
}
if (this.plugin.settings.updateBacklinks) {
}
}
return modified;
} catch (error) {
console.error(`Error processing file ${file.path}:`, error);
return false;
}
}
updateProgress(current, total) {
const i18n = I18n.getInstance();
if (this.progressContainer) {
const progressText = this.progressContainer.querySelector(".progress-text");
if (progressText) {
progressText.textContent = i18n.t("settings.autoNumbering.removeConfirmation.progressStatus", {
current: current.toString(),
total: total.toString()
});
}
}
}
onClose() {
this.isProcessing = false;
}
};
AutoNumberingActivationDialog = class extends import_obsidian2.Modal {
constructor(app, plugin, onConfirm) {
super(app);
this.progressContainer = null;
this.isProcessing = false;
this.plugin = plugin;
this.onConfirm = onConfirm;
this.setTitle("Auto Numbering Settings");
}
onOpen() {
const i18n = I18n.getInstance();
const { contentEl } = this;
contentEl.empty();
contentEl.addClass("header-enhancer-activation-dialog");
contentEl.createEl("h2", {
text: i18n.t("settings.autoNumbering.activationConfirmation.title"),
cls: "modal-title"
});
contentEl.createEl("p", {
text: i18n.t("settings.autoNumbering.activationConfirmation.message"),
cls: "modal-message"
});
const actionsEl = contentEl.createDiv({ cls: "modal-actions" });
const addToAllSetting = new import_obsidian2.Setting(actionsEl).setName(i18n.t("settings.autoNumbering.activationConfirmation.addToAll")).setDesc(i18n.t("settings.autoNumbering.activationConfirmation.addToAllDesc")).addButton((button) => {
button.setButtonText(i18n.t("settings.autoNumbering.activationConfirmation.addToAll")).setCta().onClick(async () => {
if (!this.isProcessing) {
await this.handleAddToAll();
}
});
});
const warningEl = addToAllSetting.descEl.createDiv({ cls: "setting-item-warning" });
warningEl.createEl("span", {
text: i18n.t("settings.autoNumbering.activationConfirmation.warningTitle") + " ",
cls: "warning-label"
});
warningEl.createEl("span", {
text: i18n.t("settings.autoNumbering.activationConfirmation.warningMessage"),
cls: "warning-text"
});
const tipEl = addToAllSetting.descEl.createDiv({ cls: "setting-item-tip" });
tipEl.createEl("span", {
text: i18n.t("settings.autoNumbering.activationConfirmation.manualTip"),
cls: "manual-tip-text"
});
new import_obsidian2.Setting(actionsEl).setName(i18n.t("settings.autoNumbering.activationConfirmation.turnOnOnly")).setDesc(i18n.t("settings.autoNumbering.activationConfirmation.turnOnOnlyDesc")).addButton((button) => {
button.setButtonText(i18n.t("settings.autoNumbering.activationConfirmation.turnOnOnly")).onClick(async () => {
if (!this.isProcessing) {
await this.handleTurnOnOnly();
}
});
});
const cancelButtonEl = actionsEl.createDiv({ cls: "modal-cancel" });
new import_obsidian2.Setting(cancelButtonEl).addButton((button) => {
button.setButtonText(i18n.t("settings.autoNumbering.activationConfirmation.cancel")).onClick(() => {
if (!this.isProcessing) {
this.close();
}
});
});
this.progressContainer = contentEl.createDiv({
cls: "progress-container",
attr: { style: "display: none;" }
});
}
async handleAddToAll() {
this.isProcessing = true;
try {
this.showProgress();
await this.addHeaderNumbersToAllFiles();
await this.onConfirm(true);
this.close();
} catch (error) {
const i18n = I18n.getInstance();
new import_obsidian2.Notice(i18n.t("settings.autoNumbering.activationConfirmation.error", {
error: error.message
}));
this.hideProgress();
} finally {
this.isProcessing = false;
}
}
async handleTurnOnOnly() {
this.isProcessing = true;
try {
await this.onConfirm(false);
this.close();
} finally {
this.isProcessing = false;
}
}
showProgress() {
const i18n = I18n.getInstance();
if (this.progressContainer) {
this.progressContainer.style.display = "block";
this.progressContainer.empty();
this.progressContainer.createEl("p", {
text: i18n.t("settings.autoNumbering.activationConfirmation.processing"),
cls: "progress-text"
});
}
this.contentEl.querySelectorAll("button").forEach((button) => {
button.setAttribute("disabled", "true");
});
}
hideProgress() {
if (this.progressContainer) {
this.progressContainer.style.display = "none";
this.progressContainer.empty();
}
this.contentEl.querySelectorAll("button").forEach((button) => {
button.removeAttribute("disabled");
});
}
async addHeaderNumbersToAllFiles() {
const i18n = I18n.getInstance();
const markdownFiles = this.app.vault.getMarkdownFiles();
let processedCount = 0;
let modifiedCount = 0;
const batchSize = 5;
const totalFiles = markdownFiles.length;
for (let batchStart = 0; batchStart < totalFiles; batchStart += batchSize) {
const batch = markdownFiles.slice(batchStart, Math.min(batchStart + batchSize, totalFiles));
for (const file of batch) {
try {
const modified = await this.processFile(file);
if (modified) {
modifiedCount++;
await new Promise((resolve) => setTimeout(resolve, 10));
}
processedCount++;
this.updateProgress(processedCount, totalFiles);
} catch (error) {
console.error(`Error processing file ${file.path}:`, error);
}
await new Promise((resolve) => setTimeout(resolve, 5));
}
await new Promise((resolve) => setTimeout(resolve, 50));
}
if (modifiedCount > 0) {
new import_obsidian2.Notice(i18n.t("settings.autoNumbering.activationConfirmation.completed", {
count: modifiedCount.toString()
}));
} else {
new import_obsidian2.Notice(i18n.t("settings.autoNumbering.activationConfirmation.noHeadersFound"));
}
}
async processFile(file) {
var _a;
try {
const activeLeaves = this.app.workspace.getLeavesOfType("markdown");
const isCurrentlyOpen = activeLeaves.some((leaf) => {
const view = leaf.view;
return view.file && view.file.path === file.path;
});
const content = await this.app.vault.read(file);
const lines = content.split("\n");
let modified = false;
const mockEditor = {
getValue: () => content,
lineCount: () => lines.length,
getLine: (n) => lines[n] || ""
};
const config = getAutoNumberingConfig(this.plugin.settings, mockEditor);
config.state = true;
let insertNumber = [Number(config.startNumber) - 1];
let isInCodeBlock = false;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (line.startsWith("```")) {
isInCodeBlock = !isInCodeBlock;
if (line.slice(3).contains("```")) {
isInCodeBlock = !isInCodeBlock;
}
}
if (isInCodeBlock) {
continue;
}
if (isHeader(line)) {
const headerLevel = ((_a = line.match(/^#+/)) == null ? void 0 : _a[0].length) || 0;
if (headerLevel < config.startLevel || headerLevel > config.endLevel) {
continue;
}
const adjustedLevel = headerLevel - config.startLevel + 1;
insertNumber = getNextNumber(insertNumber, adjustedLevel);
const insertNumberStr = insertNumber.join(config.separator);
if (!line.includes(this.plugin.settings.autoNumberingHeaderSeparator)) {
const newLine = "#".repeat(headerLevel) + " " + insertNumberStr + this.plugin.settings.autoNumberingHeaderSeparator + line.substring(headerLevel + 1);
if (newLine !== line) {
lines[i] = newLine;
modified = true;
}
}
}
}
if (modified) {
await this.app.vault.modify(file, lines.join("\n"));
if (isCurrentlyOpen) {
await new Promise((resolve) => setTimeout(resolve, 20));
}
}
return modified;
} catch (error) {
console.error(`Error processing file ${file.path}:`, error);
return false;
}
}
updateProgress(current, total) {
const i18n = I18n.getInstance();
if (this.progressContainer) {
const progressText = this.progressContainer.querySelector(".progress-text");
if (progressText) {
progressText.textContent = i18n.t("settings.autoNumbering.activationConfirmation.progressStatus", {
current: current.toString(),
total: total.toString()
});
}
}
}
onClose() {
this.isProcessing = false;
}
};
}
});
// src/setting.ts
var import_obsidian3, DEFAULT_SETTINGS, HeaderEnhancerSettingTab;
var init_setting = __esm({
"src/setting.ts"() {
import_obsidian3 = require("obsidian");
init_i18n();
init_core();
DEFAULT_SETTINGS = {
language: "en",
showOnStatusBar: true,
showOnSidebar: true,
isAutoDetectHeaderLevel: false,
// 自动检测文档中的标题层级功能
startHeaderLevel: 1,
endHeaderLevel: 6,
autoNumberingMode: "on" /* ON */,
autoNumberingStartNumber: "1",
autoNumberingSeparator: ".",
autoNumberingHeaderSeparator: " ",
updateBacklinks: false,
// YAML mode specific settings
yamlFallbackMode: "use_default" /* USE_DEFAULT */,
yamlDefaultStartLevel: 2,
yamlDefaultEndLevel: 6,
yamlDefaultStartNumber: "1",
yamlDefaultSeparator: ".",
// Global function enablement - enabled by default for backward compatibility
globalAutoNumberingEnabled: true,
// Per-document state management - empty object as JSON string
perDocumentStates: "{}",
// Header font settings
isSeparateHeaderFont: false,
headerFontFamily: "inherit",
headerFontSize: "inherit",
// Title font settings
isSeparateTitleFont: false,
titleFontFamily: "inherit",
titleFontSize: "inherit"
};
HeaderEnhancerSettingTab = class extends import_obsidian3.PluginSettingTab {
constructor(app, plugin) {
super(app, plugin);
this.formatPreviewSetting = null;
this.autoDetectionPreviewContainer = null;
this.plugin = plugin;
}
display() {
const { containerEl } = this;
const i18n = I18n.getInstance();
containerEl.empty();
this.formatPreviewSetting = null;
this.autoDetectionPreviewContainer = null;
containerEl.createEl("h1", { text: i18n.t("settings.title") });
containerEl.createEl("h2", { text: i18n.t("settings.general") });
new import_obsidian3.Setting(containerEl).setName(i18n.t("settings.language.name")).setDesc(i18n.t("settings.language.desc")).addDropdown((dropdown) => {
dropdown.addOption("en", "English");
dropdown.addOption("zh", "\u4E2D\u6587");
dropdown.setValue(this.plugin.settings.language);
dropdown.onChange(async (value) => {
this.plugin.settings.language = value;
i18n.setLanguage(value);
await this.plugin.saveSettings();
this.plugin.handleShowStateBarChange();
this.display();
});
});
new import_obsidian3.Setting(containerEl).setName(i18n.t("settings.statusBar.name")).setDesc(i18n.t("settings.statusBar.desc")).addToggle((toggle) => {
toggle.setValue(this.plugin.settings.showOnStatusBar).onChange(async (value) => {
this.plugin.settings.showOnStatusBar = value;
await this.plugin.saveSettings();
this.plugin.handleShowStateBarChange();
});
});
new import_obsidian3.Setting(containerEl).setName(i18n.t("settings.sidebar.name")).setDesc(i18n.t("settings.sidebar.desc")).addToggle((toggle) => {
toggle.setValue(this.plugin.settings.showOnSidebar).onChange(async (value) => {
this.plugin.settings.showOnSidebar = value;
await this.plugin.saveSettings();
this.plugin.handleShowSidebarChange();
});
});
containerEl.createEl("h2", { text: i18n.t("settings.autoNumbering.title") });
new import_obsidian3.Setting(containerEl).setName(i18n.t("settings.autoNumbering.globalToggle.name")).setDesc(i18n.t("settings.autoNumbering.globalToggle.desc")).addToggle((toggle) => {
toggle.setValue(this.plugin.settings.globalAutoNumberingEnabled).onChange(async (value) => {
this.plugin.settings.globalAutoNumberingEnabled = value;
await this.plugin.saveSettings();
this.plugin.handleShowStateBarChange();
this.plugin.updateRibbonIconState();
this.display();
});
});
if (!this.plugin.settings.globalAutoNumberingEnabled) {
const globalDisabledInfo = containerEl.createDiv({
cls: "header-enhancer-global-disabled-info"
});
globalDisabledInfo.style.cssText = `
margin: 1.5em 0;
padding: 1.2em;
border: 2px solid var(--text-muted);
border-radius: 8px;
background: var(--background-secondary);
opacity: 0.8;
`;
globalDisabledInfo.innerHTML = `
<div style="font-weight: 600; color: var(--text-muted); margin-bottom: 0.8em; display: flex; align-items: center;">
${i18n.t("settings.autoNumbering.globalDisabled.title")}
</div>
<div style="line-height: 1.6; color: var(--text-muted);">
${i18n.t("settings.autoNumbering.globalDisabled.description")}
</div>
`;
return;
}
new import_obsidian3.Setting(containerEl).setName(i18n.t("settings.autoNumbering.mode.name")).setDesc(i18n.t("settings.autoNumbering.mode.desc")).addDropdown((dropdown) => {
dropdown.addOption("off" /* OFF */, i18n.t("settings.autoNumbering.mode.off"));
dropdown.addOption("on" /* ON */, i18n.t("settings.autoNumbering.mode.on"));
dropdown.addOption("yaml" /* YAML_CONTROLLED */, i18n.t("settings.autoNumbering.mode.yaml"));
dropdown.setValue(this.plugin.settings.autoNumberingMode);
dropdown.onChange(async (value) => {
const newMode = value;
if ((this.plugin.settings.autoNumberingMode === "on" /* ON */ || this.plugin.settings.autoNumberingMode === "yaml" /* YAML_CONTROLLED */) && newMode === "off" /* OFF */) {
const { AutoNumberingRemovalDialog: AutoNumberingRemovalDialog2 } = await Promise.resolve().then(() => (init_dialogs(), dialogs_exports));
const dialog = new AutoNumberingRemovalDialog2(
this.app,
this.plugin,
async (removeExisting) => {
this.plugin.settings.autoNumberingMode = "off" /* OFF */;
await this.plugin.saveSettings();
this.plugin.handleShowStateBarChange();
this.plugin.updateRibbonIconState();
this.display();
}
);
dialog.open();
dropdown.setValue(this.plugin.settings.autoNumberingMode);
} else if (this.plugin.settings.autoNumberingMode === "off" /* OFF */ && newMode === "on" /* ON */) {
const { AutoNumberingActivationDialog: AutoNumberingActivationDialog2 } = await Promise.resolve().then(() => (init_dialogs(), dialogs_exports));
const dialog = new AutoNumberingActivationDialog2(
this.app,
this.plugin,
async (addToAll) => {
this.plugin.settings.autoNumberingMode = newMode;
await this.plugin.saveSettings();
this.plugin.handleShowStateBarChange();
this.plugin.updateRibbonIconState();
this.display();
}
);
dialog.open();
dropdown.setValue(this.plugin.settings.autoNumberingMode);
} else {
this.plugin.settings.autoNumberingMode = newMode;
await this.plugin.saveSettings();
this.plugin.handleShowStateBarChange();
this.plugin.updateRibbonIconState();
this.display();
}
});
});
if (this.plugin.settings.autoNumberingMode === "on" /* ON */) {
this.renderAutoNumberingSettings(containerEl);
} else if (this.plugin.settings.autoNumberingMode === "yaml" /* YAML_CONTROLLED */) {
this.renderYamlModeSettings(containerEl);
} else {
const offInfo = containerEl.createDiv({
cls: "header-enhancer-off-info"
});
offInfo.style.cssText = `
margin: 1.5em 0;
padding: 1.2em;
border: 2px solid var(--text-muted);
border-radius: 8px;
background: var(--background-secondary);
opacity: 0.8;
`;
offInfo.innerHTML = `
<div style="font-weight: 600; color: var(--text-muted); margin-bottom: 0.8em; display: flex; align-items: center;">
${i18n.t("autoDetection.info.offMode.title")}
</div>
<div style="line-height: 1.6; color: var(--text-muted);">
${i18n.t("autoDetection.info.offMode.description")}
</div>
`;
}
containerEl.createEl("h2", { text: i18n.t("settings.headerFont.title") });
new import_obsidian3.Setting(containerEl).setName(i18n.t("settings.headerFont.separate.name")).setDesc(i18n.t("settings.headerFont.separate.desc")).addToggle((toggle) => {
toggle.setValue(this.plugin.settings.isSeparateHeaderFont).onChange(async (value) => {
this.plugin.settings.isSeparateHeaderFont = value;
await this.plugin.saveSettings();
this.plugin.styleManager.applyCSSStyles();
this.display();
});
});
if (this.plugin.settings.isSeparateHeaderFont) {
const previewContainer = containerEl.createDiv({ cls: "header-enhancer-font-preview" });
previewContainer.style.cssText = `
margin: 1em 0;
padding: 1em;
border: 1px solid var(--background-modifier-border);
border-radius: 6px;
background: var(--background-secondary);
`;
previewContainer.createEl("div", {
text: i18n.t("settings.headerFont.preview.title"),
cls: "setting-item-name"
});
const previewContent = previewContainer.createDiv({ cls: "font-preview-content" });
for (let i = 1; i <= 3; i++) {
const tagName = i === 1 ? "h1" : i === 2 ? "h2" : "h3";
const headerEl = previewContent.createEl(tagName, {
text: `${i18n.t("settings.headerFont.preview.sample")} ${i}`,
cls: "header-enhancer-preview-header"
});
this.updateHeaderPreviewStyles(headerEl);
}
}
this.createFontFamilySetting(containerEl, "header");
this.createFontSizeSetting(containerEl, "header");
containerEl.createEl("h2", { text: i18n.t("settings.titleFont.title") });
new import_obsidian3.Setting(containerEl).setName(i18n.t("settings.titleFont.separate.name")).setDesc(i18n.t("settings.titleFont.separate.desc")).addToggle((toggle) => {
toggle.setValue(this.plugin.settings.isSeparateTitleFont).onChange(async (value) => {
this.plugin.settings.isSeparateTitleFont = value;
await this.plugin.saveSettings();
this.plugin.styleManager.applyCSSStyles();
this.display();
});
});
if (this.plugin.settings.isSeparateTitleFont) {
const previewContainer = containerEl.createDiv({ cls: "header-enhancer-title-font-preview" });
previewContainer.style.cssText = `
margin: 1em 0;
padding: 1em;
border: 1px solid var(--background-modifier-border);
border-radius: 6px;
background: var(--background-secondary);
`;
previewContainer.createEl("div", {
text: i18n.t("settings.titleFont.preview.title"),
cls: "setting-item-name"
});
const previewContent = previewContainer.createDiv({ cls: "font-preview-content" });
const titleEl = previewContent.createEl("div", {
text: i18n.t("settings.titleFont.preview.sample"),
cls: "header-enhancer-preview-title"
});
titleEl.style.cssText = "font-size: 1.5em; font-weight: bold; margin: 0.5em 0;";
this.updateTitlePreviewStyles(titleEl);
}
this.createFontFamilySetting(containerEl, "title");
this.createFontSizeSetting(containerEl, "title");
new import_obsidian3.Setting(containerEl).addButton((button) => {
button.setButtonText(i18n.t("settings.resetSettings.name")).onClick(async () => {
if (confirm(
i18n.t("settings.resetSettings.confirm")
)) {
this.plugin.settings = DEFAULT_SETTINGS;
await this.plugin.saveSettings();
this.display();
}
});
});
containerEl.createEl("h2", { text: i18n.t("settings.moreInfo") });
containerEl.createEl("p", { text: i18n.t("settings.author") }).createEl("a", {
text: "Hobee Liu",
href: "https://github.com/HoBeedzc"
});
containerEl.createEl("p", { text: i18n.t("settings.license") }).createEl("a", {
text: "MIT",
href: "https://github.com/HoBeedzc/obsidian-header-enhancer-plugin/blob/master/LICENSE"
});
containerEl.createEl("p", { text: i18n.t("settings.githubRepo") }).createEl("a", {
text: "obsidian-header-enhancer",
href: "https://github.com/HoBeedzc/obsidian-header-enhancer-plugin"
});
containerEl.createEl("p", { text: i18n.t("settings.anyQuestion") }).createEl("a", {
text: "Github Issues",
href: "https://github.com/HoBeedzc/obsidian-header-enhancer-plugin/issues"
});
}
/**
* 渲染自动编号相关的设置项
*/
renderAutoNumberingSettings(containerEl) {
const i18n = I18n.getInstance();
const headerLevelSetting = new import_obsidian3.Setting(containerEl).setName(i18n.t("settings.autoNumbering.headerLevel.name"));
const updateSettingDesc = () => {
if (this.plugin.settings.autoNumberingMode === "yaml" /* YAML_CONTROLLED */) {
headerLevelSetting.setDesc(i18n.t("settings.autoNumbering.headerLevel.desc.yamlControl"));
} else if (this.plugin.settings.isAutoDetectHeaderLevel && this.plugin.settings.autoNumberingMode === "on" /* ON */) {
headerLevelSetting.setDesc(i18n.t("settings.autoNumbering.headerLevel.desc.autoDetect"));
} else {
headerLevelSetting.setDesc(i18n.t("settings.autoNumbering.headerLevel.desc.manual"));
}
};
updateSettingDesc();
headerLevelSetting.addToggle((toggle) => {
toggle.setValue(this.plugin.settings.isAutoDetectHeaderLevel).onChange(async (value) => {
this.plugin.settings.isAutoDetectHeaderLevel = value;
await this.plugin.saveSettings();
updateSettingDesc();
this.display();
}).setDisabled(this.plugin.settings.autoNumberingMode === "yaml" /* YAML_CONTROLLED */);
});
if (this.plugin.settings.isAutoDetectHeaderLevel && this.plugin.settings.autoNumberingMode === "on" /* ON */) {
this.autoDetectionPreviewContainer = containerEl.createDiv({
cls: "header-enhancer-auto-detection-preview"
});
this.autoDetectionPreviewContainer.style.cssText = `
margin: 1em 0;
padding: 1.2em;
border: 2px solid var(--color-green);
border-radius: 8px;
background: var(--background-secondary);
position: relative;
`;
const previewTitle2 = this.autoDetectionPreviewContainer.createDiv();
previewTitle2.style.cssText = `
font-weight: 600;
font-size: 1em;
color: var(--color-green);
margin-bottom: 0.8em;
display: flex;
align-items: center;
`;
previewTitle2.innerHTML = "\u{1F527} \u667A\u80FD\u68C0\u6D4B\u7ED3\u679C";
this.updateAutoDetectionPreview();
} else {
new import_obsidian3.Setting(containerEl).setName(i18n.t("settings.headerLevel.start.name")).setDesc(i18n.t("settings.headerLevel.start.desc")).addDropdown((dropdown) => {
dropdown.addOption("1", "H1");
dropdown.addOption("2", "H2");
dropdown.addOption("3", "H3");
dropdown.addOption("4", "H4");
dropdown.addOption("5", "H5");
dropdown.addOption("6", "H6");
dropdown.setValue(
this.plugin.settings.startHeaderLevel.toString()
);
dropdown.setDisabled(this.plugin.settings.autoNumberingMode === "yaml" /* YAML_CONTROLLED */);
dropdown.onChange(async (value) => {
if (this.checkStartLevel(parseInt(value, 10))) {
this.plugin.settings.startHeaderLevel = parseInt(value, 10);
await this.plugin.saveSettings();
this.updateFormatPreview();
} else {
new import_obsidian3.Notice(
i18n.t("settings.autoNumbering.startLevelError")
);
dropdown.setValue(this.plugin.settings.startHeaderLevel.toString());
}
});
});
new import_obsidian3.Setting(containerEl).setName(i18n.t("settings.headerLevel.max.name")).setDesc(i18n.t("settings.headerLevel.max.desc")).addDropdown((dropdown) => {
dropdown.addOption("1", "H1");
dropdown.addOption("2", "H2");
dropdown.addOption("3", "H3");
dropdown.addOption("4", "H4");
dropdown.addOption("5", "H5");
dropdown.addOption("6", "H6");
dropdown.setValue(
this.plugin.settings.endHeaderLevel.toString()
);
dropdown.setDisabled(this.plugin.settings.autoNumberingMode === "yaml" /* YAML_CONTROLLED */);
dropdown.onChange(async (value) => {
if (this.checkEndLevel(parseInt(value, 10))) {
this.plugin.settings.endHeaderLevel = parseInt(
value,
10
);
await this.plugin.saveSettings();
this.updateFormatPreview();
} else {
new import_obsidian3.Notice(
i18n.t("settings.autoNumbering.endLevelError")
);
dropdown.setValue(this.plugin.settings.endHeaderLevel.toString());
}
});
});
}
new import_obsidian3.Setting(containerEl).setName(i18n.t("settings.autoNumbering.startNumber.name")).setDesc(i18n.t("settings.autoNumbering.startNumber.desc")).addDropdown((dropdown) => {
dropdown.addOption("0", "0");
dropdown.addOption("1", "1");
dropdown.setValue(this.plugin.settings.autoNumberingStartNumber);
dropdown.setDisabled(this.plugin.settings.autoNumberingMode === "yaml" /* YAML_CONTROLLED */);
dropdown.onChange(async (value) => {
this.plugin.settings.autoNumberingStartNumber = value;
await this.plugin.saveSettings();
this.updateFormatPreview();
});
});
new import_obsidian3.Setting(containerEl).setName(i18n.t("settings.autoNumbering.separator.name")).setDesc(i18n.t("settings.autoNumbering.separator.desc")).addDropdown((dropdown) => {
dropdown.addOption(".", ".");
dropdown.addOption(",", ",");
dropdown.addOption("-", "-");
dropdown.addOption("/", "/");
dropdown.setValue(this.plugin.settings.autoNumberingSeparator);
dropdown.setDisabled(this.plugin.settings.autoNumberingMode === "yaml" /* YAML_CONTROLLED */);
dropdown.onChange(async (value) => {
this.plugin.settings.autoNumberingSeparator = value;
await this.plugin.saveSettings();
this.updateFormatPreview();
});
});
new import_obsidian3.Setting(containerEl).setName(i18n.t("settings.autoNumbering.headerSeparator.name")).setDesc(i18n.t("settings.autoNumbering.headerSeparator.desc")).addDropdown((dropdown) => {
dropdown.addOption(" ", "Tab");
dropdown.addOption(" ", "Space");
dropdown.setValue(
this.plugin.settings.autoNumberingHeaderSeparator
);
dropdown.setDisabled(this.plugin.settings.autoNumberingMode === "yaml" /* YAML_CONTROLLED */);
dropdown.onChange(async (value) => {
this.plugin.settings.autoNumberingHeaderSeparator = value;
await this.plugin.saveSettings();
});
});
new import_obsidian3.Setting(containerEl).setName(i18n.t("settings.autoNumbering.updateBacklinks.name")).setDesc(i18n.t("settings.autoNumbering.updateBacklinks.desc")).addToggle((toggle) => {
toggle.setValue(this.plugin.settings.updateBacklinks).onChange(async (value) => {
this.plugin.settings.updateBacklinks = value;
await this.plugin.saveSettings();
}).setDisabled(this.plugin.settings.autoNumberingMode === "yaml" /* YAML_CONTROLLED */);
});
const formatPreviewContainer = containerEl.createDiv({
cls: "header-enhancer-format-preview-container"
});
formatPreviewContainer.style.cssText = `
margin: 1.5em 0;
`;
const previewTitle = formatPreviewContainer.createDiv();
previewTitle.style.cssText = `
font-weight: 600;
font-size: 1.1em;
color: var(--text-accent);
margin-bottom: 1em;
display: flex;
align-items: center;
gap: 0.5em;
`;
previewTitle.innerHTML = `<span style="color: var(--color-accent);">\u{1F3AF}</span> ${i18n.t("settings.autoNumbering.format.name")}`;
const previewContent = formatPreviewContainer.createDiv({
cls: "format-preview-content"
});
this.formatPreviewSetting = {
setDesc: (content) => {
previewContent.innerHTML = content;
}
};
previewContent.innerHTML = this.getFormatPreview();
}
/**
* Create font family setting for header or title
*/
createFontFamilySetting(containerEl, type) {
const i18n = I18n.getInstance();
const isHeader2 = type === "header";
const settingsKey = isHeader2 ? "settings.headerFont" : "settings.titleFont";
new import_obsidian3.Setting(containerEl).setName(i18n.t(`${settingsKey}.family.name`)).setDesc(i18n.t(`${settingsKey}.family.desc`)).addDropdown((dropdown) => {
dropdown.addOption("inherit", i18n.t(`${settingsKey}.family.options.inherit`));
dropdown.addOption("Arial, sans-serif", "Arial");
dropdown.addOption("Helvetica, Arial, sans-serif", "Helvetica");
dropdown.addOption("Verdana, Geneva, sans-serif", "Verdana");
dropdown.addOption("Tahoma, Geneva, sans-serif", "Tahoma");
dropdown.addOption("'Trebuchet MS', Helvetica, sans-serif", "Trebuchet MS");
dropdown.addOption("'Lucida Sans Unicode', 'Lucida Grande', sans-serif", "Lucida Sans");
dropdown.addOption("Impact, Charcoal, sans-serif", "Impact");
dropdown.addOption("'Comic Sans MS', cursive", "Comic Sans MS");
dropdown.addOption("'Times New Roman', Times, serif", "Times New Roman");
dropdown.addOption("Georgia, serif", "Georgia");
dropdown.addOption("'Palatino Linotype', 'Book Antiqua', Palatino, serif", "Palatino");
dropdown.addOption("Garamond, serif", "Garamond");
dropdown.addOption("'Book Antiqua', Palatino, serif", "Book Antiqua");
dropdown.addOption("'Courier New', Courier, monospace", "Courier New");
dropdown.addOption("Consolas, 'Liberation Mono', monospace", "Consolas");
dropdown.addOption("Monaco, 'Lucida Console', monospace", "Monaco");
dropdown.addOption("'JetBrains Mono', Consolas, monospace", "JetBrains Mono");
dropdown.addOption("'Fira Code', Consolas, monospace", "Fira Code");
dropdown.addOption("Menlo, Monaco, monospace", "Menlo");
dropdown.addOption("'Microsoft YaHei', '\u5FAE\u8F6F\u96C5\u9ED1', Arial, sans-serif", "Microsoft YaHei (\u5FAE\u8F6F\u96C5\u9ED1)");
dropdown.addOption("'PingFang SC', '\u82F9\u65B9-\u7B80', 'Helvetica Neue', Arial, sans-serif", "PingFang SC (\u82F9\u65B9-\u7B80)");
dropdown.addOption("'Hiragino Sans GB', '\u51AC\u9752\u9ED1\u4F53\u7B80\u4F53\u4E2D\u6587', 'Microsoft YaHei', sans-serif", "Hiragino Sans GB (\u51AC\u9752\u9ED1\u4F53)");
dropdown.addOption("'Source Han Sans SC', '\u601D\u6E90\u9ED1\u4F53 CN', 'Noto Sans CJK SC', sans-serif", "Source Han Sans SC (\u601D\u6E90\u9ED1\u4F53)");
dropdown.addOption("'Noto Sans SC', '\u601D\u6E90\u9ED1\u4F53', sans-serif", "Noto Sans SC");
dropdown.addOption("SimHei, '\u9ED1\u4F53', sans-serif", "SimHei (\u9ED1\u4F53)");
dropdown.addOption("'WenQuanYi Micro Hei', '\u6587\u6CC9\u9A7F\u5FAE\u7C73\u9ED1', sans-serif", "WenQuanYi Micro Hei (\u6587\u6CC9\u9A7F\u5FAE\u7C73\u9ED1)");
dropdown.addOption("'Songti SC', '\u5B8B\u4F53-\u7B80', SimSun, serif", "Songti SC (\u5B8B\u4F53-\u7B80)");
dropdown.addOption("SimSun, '\u5B8B\u4F53', serif", "SimSun (\u5B8B\u4F53)");
dropdown.addOption("'Source Han Serif SC', '\u601D\u6E90\u5B8B\u4F53 CN', 'Noto Serif CJK SC', serif", "Source Han Serif SC (\u601D\u6E90\u5B8B\u4F53)");
dropdown.addOption("'Noto Serif SC', '\u601D\u6E90\u5B8B\u4F53', serif", "Noto Serif SC");
dropdown.addOption("'STSong', '\u534E\u6587\u5B8B\u4F53', SimSun, serif", "STSong (\u534E\u6587\u5B8B\u4F53)");
dropdown.addOption("'FangSong', '\u4EFF\u5B8B', serif", "FangSong (\u4EFF\u5B8B)");
dropdown.addOption("system-ui, -apple-system, sans-serif", "System UI");
dropdown.addOption("-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif", "System Default");
dropdown.addOption("'PingFang SC', 'Microsoft YaHei', 'Hiragino Sans GB', Arial, sans-serif", "\u4E2D\u82F1\u6587\u65E0\u886C\u7EBF\u6DF7\u5408");
dropdown.addOption("'Songti SC', 'Source Han Serif SC', 'Times New Roman', serif", "\u4E2D\u82F1\u6587\u886C\u7EBF\u6DF7\u5408");
const currentValue = isHeader2 ? this.plugin.settings.headerFontFamily : this.plugin.settings.titleFontFamily;
const isEnabled = isHeader2 ? this.plugin.settings.isSeparateHeaderFont : this.plugin.settings.isSeparateTitleFont;
dropdown.setValue(currentValue);
dropdown.setDisabled(!isEnabled);
dropdown.onChange(async (value) => {
if (isHeader2) {
this.plugin.settings.headerFontFamily = value;
} else {
this.plugin.settings.titleFontFamily = value;
}
await this.plugin.saveSettings();
if (isEnabled) {
this.plugin.styleManager.applyCSSStyles();
if (isHeader2) {
this.updateAllHeaderPreviewStyles();
} else {
this.updateAllTitlePreviewStyles();
}
}
});
});
}
/**
* Create font size setting for header or title
*/
createFontSizeSetting(containerEl, type) {
const i18n = I18n.getInstance();
const isHeader2 = type === "header";
const settingsKey = isHeader2 ? "settings.headerFont" : "settings.titleFont";
new import_obsidian3.Setting(containerEl).setName(i18n.t(`${settingsKey}.size.name`)).setDesc(i18n.t(`${settingsKey}.size.desc`)).addDropdown((dropdown) => {
dropdown.addOption("inherit", i18n.t(`${settingsKey}.size.options.inherit`));
dropdown.addOption("0.8em", i18n.t(`${settingsKey}.size.options.smaller`) + " (0.8em)");
dropdown.addOption("0.9em", i18n.t(`${settingsKey}.size.options.small`) + " (0.9em)");
dropdown.addOption("1em", i18n.t(`${settingsKey}.size.options.normal`) + " (1em)");
dropdown.addOption("1.1em", i18n.t(`${settingsKey}.size.options.large`) + " (1.1em)");
dropdown.addOption("1.2em", i18n.t(`${settingsKey}.size.options.larger`) + " (1.2em)");
dropdown.addOption("1.3em", i18n.t(`${settingsKey}.size.options.xlarge`) + " (1.3em)");
dropdown.addOption("1.5em", i18n.t(`${settingsKey}.size.options.xxlarge`) + " (1.5em)");
dropdown.addOption("12px", "12px");
dropdown.addOption("14px", "14px");
dropdown.addOption("16px", "16px");
dropdown.addOption("18px", "18px");
dropdown.addOption("20px", "20px");
dropdown.addOption("24px", "24px");
dropdown.addOption("28px", "28px");
dropdown.addOption("32px", "32px");
dropdown.addOption("120%", "120%");
dropdown.addOption("140%", "140%");
const currentValue = isHeader2 ? this.plugin.settings.headerFontSize : this.plugin.settings.titleFontSize;
const isEnabled = isHeader2 ? this.plugin.settings.isSeparateHeaderFont : this.plugin.settings.isSeparateTitleFont;
dropdown.setValue(currentValue);
dropdown.setDisabled(!isEnabled);
dropdown.onChange(async (value) => {
if (isHeader2) {
this.plugin.settings.headerFontSize = value;
} else {
this.plugin.settings.titleFontSize = value;
}
await this.plugin.saveSettings();
if (isEnabled) {
this.plugin.styleManager.applyCSSStyles();
if (isHeader2) {
this.updateAllHeaderPreviewStyles();
} else {
this.updateAllTitlePreviewStyles();
}
}
});
});
}
/**
* Update auto detection preview
*/
updateAutoDetectionPreview() {
if (!this.autoDetectionPreviewContainer)
return;
const i18n = I18n.getInstance();
const activeView = this.app.workspace.getActiveViewOfType(import_obsidian3.MarkdownView);
let contentContainer = this.autoDetectionPreviewContainer.querySelector(".preview-content");
if (!contentContainer) {
contentContainer = this.autoDetectionPreviewContainer.createDiv({
cls: "preview-content"
});
}
if (!activeView) {
contentContainer.innerHTML = `<div style="color: var(--text-muted); font-style: italic;">${i18n.t("autoDetection.noActiveDocument")}</div>`;
this.updateFormatPreview();
return;
}
const content = activeView.editor.getValue();
const analysis = analyzeHeaderLevels(content);
contentContainer.innerHTML = `
<div style="line-height: 1.6;">
${this.formatAnalysisResult(analysis)}
</div>
`;
this.updateFormatPreview();
}
/**
* Format analysis result for display
*/
formatAnalysisResult(analysis) {
const i18n = I18n.getInstance();
if (analysis.isEmpty) {
return `<div style="color: var(--text-muted); text-align: center; padding: 1em;">
\u{1F4DD} ${i18n.t("autoDetection.noHeaders")}
</div>`;
}
const levelNames = analysis.usedLevels.map((level) => `<span style="background: var(--tag-background); color: var(--tag-color); padding: 0.2em 0.4em; border-radius: 3px; font-family: monospace;">H${level}</span>`).join(" ");
const mappingInfo = analysis.usedLevels.map(
(level, index) => `<span style="background: var(--background-modifier-hover); padding: 0.2em 0.4em; border-radius: 3px; font-family: monospace;">H${level}\u2192${index + 1}\u7EA7</span>`
).join(" ");
return `
<div style="display: grid; gap: 0.8em;">
<div style="display: flex; align-items: center; gap: 0.8em;">
<strong style="color: var(--text-normal); min-width: 70px;">${i18n.t("autoDetection.detected")}:</strong>
<div style="display: flex; gap: 0.4em; flex-wrap: wrap;">${levelNames}</div>
</div>
<div style="display: flex; align-items: center; gap: 0.8em;">
<strong style="color: var(--text-normal); min-width: 70px;">${i18n.t("autoDetection.range")}:</strong>
<span style="background: var(--color-green-rgb); color: var(--text-on-accent); padding: 0.3em 0.6em; border-radius: 4px; font-weight: 500;">H${analysis.minLevel} - H${analysis.maxLevel}</span>
</div>
<div style="display: flex; align-items: flex-start; gap: 0.8em;">
<strong style="color: var(--text-normal); min-width: 70px; margin-top: 0.2em;">${i18n.t("autoDetection.mapping")}:</strong>
<div style="display: flex; gap: 0.4em; flex-wrap: wrap;">${mappingInfo}</div>
</div>
<div style="display: flex; align-items: center; gap: 0.8em;">
<strong style="color: var(--text-normal); min-width: 70px;">${i18n.t("autoDetection.totalHeaders")}:</strong>
<span style="background: var(--color-blue-rgb); color: var(--text-on-accent); padding: 0.3em 0.6em; border-radius: 4px; font-weight: 500;">${analysis.headerCount}</span>
</div>
</div>
`;
}
/**
* Get format preview string
*/
getFormatPreview() {
const i18n = I18n.getInstance();
switch (this.plugin.settings.autoNumberingMode) {
case "off" /* OFF */:
return `<div style="
color: var(--text-muted);
text-align: center;
padding: 1.5em;
border: 2px dashed var(--background-modifier-border);
border-radius: 8px;
background: var(--background-modifier-hover);
">
<div style="font-size: 1.5em; margin-bottom: 0.5em;">\u23F9\uFE0F</div>
<div style="font-size: 1em; font-weight: 500;">${i18n.t("settings.autoNumbering.format.disabled")}</div>
</div>`;
case "yaml" /* YAML_CONTROLLED */:
return `<div style="
color: var(--text-accent);
text-align: center;
padding: 1.5em;
border: 2px dashed var(--color-blue);
border-radius: 8px;
background: var(--background-modifier-hover);
">
<div style="font-size: 1.5em; margin-bottom: 0.5em;">\u{1F4C4}</div>
<div style="font-size: 1em; font-weight: 500;">${i18n.t("settings.autoNumbering.format.yamlControlled")}</div>
</div>`;
case "on" /* ON */:
default:
const formatExample = this.plugin.settings.autoNumberingStartNumber + this.plugin.settings.autoNumberingSeparator + "1" + this.plugin.settings.autoNumberingSeparator + "1";
let levelInfo;
let statusBadge;
if (this.plugin.settings.isAutoDetectHeaderLevel) {
const activeView = this.app.workspace.getActiveViewOfType(import_obsidian3.MarkdownView);
if (activeView) {
const content = activeView.editor.getValue();
const analysis = analyzeHeaderLevels(content);
if (!analysis.isEmpty) {
levelInfo = `${i18n.t("settings.autoNumbering.format.fromLevel")} H${analysis.minLevel} ${i18n.t("settings.autoNumbering.format.toLevel")} H${analysis.maxLevel}`;
statusBadge = `<span style="
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
color: white;
padding: 0.3em 0.8em;
border-radius: 15px;
font-size: 0.85em;
font-weight: 600;
box-shadow: 0 2px 4px rgba(16, 185, 129, 0.3);
">${i18n.t("settings.autoNumbering.format.autoDetect")}</span>`;
} else {
levelInfo = i18n.t("autoDetection.noHeaders");
statusBadge = `<span style="
background: var(--color-orange);
color: white;
padding: 0.3em 0.8em;
border-radius: 15px;
font-size: 0.85em;
font-weight: 600;
">${i18n.t("settings.autoNumbering.format.autoDetect")}</span>`;
}
} else {
levelInfo = i18n.t("autoDetection.noActiveDocument");
statusBadge = `<span style="
background: var(--text-muted);
color: white;
padding: 0.3em 0.8em;
border-radius: 15px;
font-size: 0.85em;
font-weight: 600;
">${i18n.t("settings.autoNumbering.format.autoDetect")}</span>`;
}
} else {
levelInfo = `${i18n.t("settings.autoNumbering.format.fromLevel")} H${this.plugin.settings.startHeaderLevel} ${i18n.t("settings.autoNumbering.format.toLevel")} H${this.plugin.settings.endHeaderLevel}`;
statusBadge = `<span style="
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 0.3em 0.8em;
border-radius: 15px;
font-size: 0.85em;
font-weight: 600;
box-shadow: 0 2px 4px rgba(102, 126, 234, 0.3);
">${i18n.t("settings.autoNumbering.format.manual")}</span>`;
}
return `
<div style="display: flex; flex-direction: column; gap: 1.5em;">
<!-- Format example -->
<div style="
text-align: center;
">
<div style="
font-family: monospace;
font-size: 1.8em;
font-weight: 700;
color: var(--color-accent);
background: var(--background-primary);
padding: 1em;
border-radius: 8px;
border: 2px solid var(--color-accent);
">${formatExample}</div>
</div>
<!-- Level range and status -->
<div style="
display: flex;
justify-content: space-between;
align-items: center;
">
<div style="
display: flex;
align-items: center;
gap: 0.5em;
font-weight: 500;
color: var(--text-normal);
">
<span style="color: var(--color-accent);">\u{1F4CF}</span>
<span>${levelInfo}</span>
</div>
${statusBadge}
</div>
</div>
`;
}
}
/**
* Update format preview
*/
updateFormatPreview() {
if (this.formatPreviewSetting) {
this.formatPreviewSetting.setDesc(this.getFormatPreview());
}
}
/**
* Validation methods
*/
checkEndLevel(maxLevel) {
return this.plugin.settings.startHeaderLevel <= maxLevel;
}
checkStartLevel(startLevel) {
return startLevel <= this.plugin.settings.endHeaderLevel;
}
checkStartNumber(startNumber) {
const reg = /^[0-9]*$/;
return reg.test(startNumber);
}
checkSeparator(separator) {
if (separator.length != 1) {
return false;
}
const separators = [".", ",", "-", "/"];
return separators.includes(separator);
}
checkHeaderSeparator(_separator) {
if (this.plugin.settings.autoNumberingMode === "on" /* ON */) {
return false;
}
return true;
}
/**
* Update preview styles for a single header element
*/
updateHeaderPreviewStyles(headerEl) {
if (this.plugin.settings.headerFontFamily && this.plugin.settings.headerFontFamily !== "inherit") {
headerEl.style.fontFamily = this.plugin.settings.headerFontFamily;
} else {
headerEl.style.fontFamily = "";
}
if (this.plugin.settings.headerFontSize && this.plugin.settings.headerFontSize !== "inherit") {
headerEl.style.fontSize = this.plugin.settings.headerFontSize;
} else {
headerEl.style.fontSize = "";
}
}
/**
* Update preview styles for title element
*/
updateTitlePreviewStyles(titleEl) {
if (this.plugin.settings.titleFontFamily && this.plugin.settings.titleFontFamily !== "inherit") {
titleEl.style.fontFamily = this.plugin.settings.titleFontFamily;
} else {
titleEl.style.fontFamily = "";
}
if (this.plugin.settings.titleFontSize && this.plugin.settings.titleFontSize !== "inherit") {
titleEl.style.fontSize = this.plugin.settings.titleFontSize;
} else {
titleEl.style.fontSize = "";
}
}
/**
* Update preview styles for all preview headers
*/
updateAllHeaderPreviewStyles() {
const previewHeaders = this.containerEl.querySelectorAll(".header-enhancer-preview-header");
previewHeaders.forEach((headerEl) => {
this.updateHeaderPreviewStyles(headerEl);
});
}
/**
* Update preview styles for all preview titles
*/
updateAllTitlePreviewStyles() {
const previewTitles = this.containerEl.querySelectorAll(".header-enhancer-preview-title");
previewTitles.forEach((titleEl) => {
this.updateTitlePreviewStyles(titleEl);
});
}
/**
* 渲染 YAML 模式的设置项
*/
renderYamlModeSettings(containerEl) {
const i18n = I18n.getInstance();
const yamlExample = `["state on", "start-level h${this.plugin.settings.yamlDefaultStartLevel}", "end-level h${this.plugin.settings.yamlDefaultEndLevel}", "start-at ${this.plugin.settings.yamlDefaultStartNumber}", "separator ${this.plugin.settings.yamlDefaultSeparator}"]`;
const yamlInfo = containerEl.createDiv({
cls: "header-enhancer-yaml-info"
});
yamlInfo.style.cssText = `
margin: 1.5em 0;
padding: 1.2em;
border: 2px solid var(--color-blue);
border-radius: 8px;
background: var(--background-secondary);
`;
yamlInfo.innerHTML = `
<div style="font-weight: 600; color: var(--color-blue); margin-bottom: 0.8em; display: flex; align-items: center;">
${i18n.t("autoDetection.info.yamlMode.title")}
</div>
<div style="line-height: 1.6; color: var(--text-normal);">
${i18n.t("autoDetection.info.yamlMode.description")}<br><br>
<code style="background: var(--code-background); padding: 0.5em; border-radius: 4px; display: block; font-family: monospace;">
---<br>
header-auto-numbering: ${yamlExample}<br>
---
</code><br>
${i18n.t("autoDetection.info.yamlMode.usage")}
</div>
`;
new import_obsidian3.Setting(containerEl).setName(i18n.t("settings.yamlMode.fallback.name")).setDesc(i18n.t("settings.yamlMode.fallback.desc")).addDropdown((dropdown) => {
dropdown.addOption("no_numbering" /* NO_NUMBERING */, i18n.t("settings.yamlMode.fallback.noNumbering"));
dropdown.addOption("use_default" /* USE_DEFAULT */, i18n.t("settings.yamlMode.fallback.useDefault"));
dropdown.setValue(this.plugin.settings.yamlFallbackMode);
dropdown.onChange(async (value) => {
this.plugin.settings.yamlFallbackMode = value;
await this.plugin.saveSettings();
this.display();
});
});
if (this.plugin.settings.yamlFallbackMode === "use_default" /* USE_DEFAULT */) {
new import_obsidian3.Setting(containerEl).setName(i18n.t("settings.yamlMode.defaultStartLevel.name")).setDesc(i18n.t("settings.yamlMode.defaultStartLevel.desc")).addDropdown((dropdown) => {
dropdown.addOption("1", "H1");
dropdown.addOption("2", "H2");
dropdown.addOption("3", "H3");
dropdown.addOption("4", "H4");
dropdown.addOption("5", "H5");
dropdown.addOption("6", "H6");
dropdown.setValue(this.plugin.settings.yamlDefaultStartLevel.toString());
dropdown.onChange(async (value) => {
const numValue = parseInt(value, 10);
if (numValue <= this.plugin.settings.yamlDefaultEndLevel) {
this.plugin.settings.yamlDefaultStartLevel = numValue;
await this.plugin.saveSettings();
this.display();
} else {
new import_obsidian3.Notice(i18n.t("settings.autoNumbering.startLevelError"));
dropdown.setValue(this.plugin.settings.yamlDefaultStartLevel.toString());
}
});
});
new import_obsidian3.Setting(containerEl).setName(i18n.t("settings.yamlMode.defaultEndLevel.name")).setDesc(i18n.t("settings.yamlMode.defaultEndLevel.desc")).addDropdown((dropdown) => {
dropdown.addOption("1", "H1");
dropdown.addOption("2", "H2");
dropdown.addOption("3", "H3");
dropdown.addOption("4", "H4");
dropdown.addOption("5", "H5");
dropdown.addOption("6", "H6");
dropdown.setValue(this.plugin.settings.yamlDefaultEndLevel.toString());
dropdown.onChange(async (value) => {
const numValue = parseInt(value, 10);
if (numValue >= this.plugin.settings.yamlDefaultStartLevel) {
this.plugin.settings.yamlDefaultEndLevel = numValue;
await this.plugin.saveSettings();
this.display();
} else {
new import_obsidian3.Notice(i18n.t("settings.autoNumbering.endLevelError"));
dropdown.setValue(this.plugin.settings.yamlDefaultEndLevel.toString());
}
});
});
new import_obsidian3.Setting(containerEl).setName(i18n.t("settings.yamlMode.defaultStartNumber.name")).setDesc(i18n.t("settings.yamlMode.defaultStartNumber.desc")).addDropdown((dropdown) => {
dropdown.addOption("0", "0");
dropdown.addOption("1", "1");
dropdown.setValue(this.plugin.settings.yamlDefaultStartNumber);
dropdown.onChange(async (value) => {
this.plugin.settings.yamlDefaultStartNumber = value;
await this.plugin.saveSettings();
this.display();
});
});
new import_obsidian3.Setting(containerEl).setName(i18n.t("settings.yamlMode.defaultSeparator.name")).setDesc(i18n.t("settings.yamlMode.defaultSeparator.desc")).addDropdown((dropdown) => {
dropdown.addOption(".", ".");
dropdown.addOption(",", ",");
dropdown.addOption("-", "-");
dropdown.addOption("/", "/");
dropdown.setValue(this.plugin.settings.yamlDefaultSeparator);
dropdown.onChange(async (value) => {
this.plugin.settings.yamlDefaultSeparator = value;
await this.plugin.saveSettings();
this.display();
});
});
}
}
};
}
});
// src/main.ts
var main_exports = {};
__export(main_exports, {
default: () => HeaderEnhancerPlugin
});
module.exports = __toCommonJS(main_exports);
var import_obsidian6 = require("obsidian");
init_core();
init_utils();
init_setting();
init_config();
init_i18n();
// src/backlinks.ts
var import_obsidian4 = require("obsidian");
var BacklinkManager = class {
constructor(app) {
this.app = app;
}
/**
* Find backlinks pointing to a specific heading
* @param targetFile Target file
* @param oldHeading Original heading text (without # symbols)
* @returns List of link updates needed
*/
async findHeadingBacklinks(targetFile, oldHeading) {
const updates = [];
try {
const backlinksMap = this.app.metadataCache.getBacklinksForFile(targetFile);
if (!backlinksMap) {
return updates;
}
let actualMap = backlinksMap;
if (backlinksMap.data && backlinksMap.data instanceof Map) {
actualMap = backlinksMap.data;
}
if (!actualMap || actualMap.size === 0) {
return updates;
}
for (const [sourcePath, references] of actualMap) {
const sourceFile = this.app.vault.getAbstractFileByPath(sourcePath);
if (!(sourceFile instanceof import_obsidian4.TFile))
continue;
const content = await this.app.vault.read(sourceFile);
const lines = content.split("\n");
for (const ref of references) {
if (this.isReferenceCache(ref)) {
const linkUpdate = this.extractHeaderLink(
sourceFile,
ref,
lines,
targetFile.basename,
oldHeading
);
if (linkUpdate) {
updates.push(linkUpdate);
}
}
}
}
} catch (error) {
console.error("Error finding heading backlinks:", error);
new import_obsidian4.Notice("Error finding backlinks: " + error.message);
}
return updates;
}
/**
* Batch update header links in multiple files
* @param updates List of header link updates to apply
* @returns Whether all updates were successful
*/
async updateBacklinks(updates) {
if (updates.length === 0)
return true;
const updatePromises = [];
const backupData = [];
try {
for (const update of updates) {
const originalContent = await this.app.vault.read(update.sourceFile);
backupData.push({ file: update.sourceFile, content: originalContent });
updatePromises.push(this.updateSingleBacklink(update, originalContent));
}
await Promise.all(updatePromises);
return true;
} catch (error) {
console.error("Error updating backlinks:", error);
try {
const rollbackPromises = backupData.map(
(backup) => this.app.vault.modify(backup.file, backup.content)
);
await Promise.all(rollbackPromises);
new import_obsidian4.Notice("Backlink update failed, changes rolled back");
} catch (rollbackError) {
console.error("Failed to rollback changes:", rollbackError);
new import_obsidian4.Notice("Critical error: Failed to rollback backlink changes");
}
return false;
}
}
/**
* Update a single backlink
*/
async updateSingleBacklink(update, originalContent) {
const lines = originalContent.split("\n");
const { line } = update.position.start;
if (line >= lines.length)
return;
const oldLine = lines[line];
const newLine = oldLine.replace(update.oldLink, update.newLink);
if (oldLine !== newLine) {
lines[line] = newLine;
const newContent = lines.join("\n");
await this.app.vault.modify(update.sourceFile, newContent);
}
}
/**
* Extract header link information from a reference
*/
extractHeaderLink(sourceFile, ref, lines, targetFileName, oldHeading) {
const { line, ch } = ref.position.start;
if (line >= lines.length)
return null;
const lineContent = lines[line];
const expectedLinkPrefix = `${targetFileName}#`;
if (ref.link && ref.link.startsWith(expectedLinkPrefix)) {
const linkHeading = ref.link.substring(expectedLinkPrefix.length);
const normalizedLinkHeading = this.normalizeSpaces(linkHeading);
const normalizedOldHeading = this.normalizeSpaces(oldHeading);
if (normalizedLinkHeading === normalizedOldHeading || normalizedLinkHeading.includes(normalizedOldHeading) || normalizedOldHeading.includes(normalizedLinkHeading)) {
const linkUpdate = {
sourceFile,
oldLink: ref.original,
// Use original link text
newLink: ref.original,
// Set to same initially, will be updated in main.ts
position: {
start: { line: ref.position.start.line, ch: ref.position.start.col },
end: { line: ref.position.end.line, ch: ref.position.end.col }
}
};
return linkUpdate;
}
}
return null;
}
/**
* Type guard: check if reference is ReferenceCache type
*/
isReferenceCache(ref) {
return ref && typeof ref === "object" && "position" in ref;
}
/**
* Normalize spaces - convert multiple whitespace characters (including Tab) to single space
*/
normalizeSpaces(text) {
return text.replace(/\s+/g, " ").trim();
}
};
// src/editor/editor-handlers.ts
var import_view = require("@codemirror/view");
var import_state = require("@codemirror/state");
var import_obsidian5 = require("obsidian");
init_core();
init_config();
init_setting();
var EditorHandlers = class {
constructor(plugin) {
this.plugin = plugin;
}
/**
* 注册 CodeMirror 按键处理扩展
*/
registerKeyHandlers() {
return [
// Enter 键处理
import_state.Prec.highest(
import_view.keymap.of([
{
key: "Enter",
run: (view) => {
var _a;
const state = view.state;
const pos = state.selection.main.to;
const currentLine = state.doc.lineAt(pos);
if (!isHeader(currentLine.text)) {
return false;
}
if (this.plugin.settings.autoNumberingMode === "off" /* OFF */) {
return false;
}
const app = this.plugin.app;
const activeView = app.workspace.getActiveViewOfType(import_obsidian5.MarkdownView);
if (activeView) {
const editor = activeView.editor;
const filePath = (_a = activeView.file) == null ? void 0 : _a.path;
const config = getAutoNumberingConfig(
this.plugin.settings,
editor,
filePath ? (path) => this.plugin.getDocumentAutoNumberingState(path) : void 0,
filePath
);
if (!config.state) {
return false;
}
}
this.handlePressEnter(view);
return true;
}
}
])
),
// Backspace 键处理
import_state.Prec.highest(
import_view.keymap.of([
{
key: "Backspace",
run: (view) => {
const state = view.state;
const pos = state.selection.main.to;
const currentLine = state.doc.lineAt(pos);
if (!isHeader(currentLine.text)) {
return false;
}
return this.handlePressBackspace(view);
}
}
])
)
];
}
/**
* 处理 Enter 键按下事件
*/
async handlePressEnter(view) {
var _a;
let state = view.state;
let doc = state.doc;
const pos = state.selection.main.to;
const app = this.plugin.app;
const activeView = app.workspace.getActiveViewOfType(import_obsidian5.MarkdownView);
if (!activeView) {
return false;
}
const currentLine = doc.lineAt(pos);
const editor = activeView.editor;
const filePath = (_a = activeView.file) == null ? void 0 : _a.path;
const config = getAutoNumberingConfig(
this.plugin.settings,
editor,
filePath ? (path) => this.plugin.getDocumentAutoNumberingState(path) : void 0,
filePath
);
if (!isHeader(currentLine.text) || !config.state) {
return false;
}
const textBeforeCursor = currentLine.text.substring(0, pos - currentLine.from);
const textAfterCursor = currentLine.text.substring(pos - currentLine.from);
const changes = [{
from: currentLine.from,
to: currentLine.to,
insert: textBeforeCursor + "\n" + textAfterCursor
}];
view.dispatch({
changes,
selection: { anchor: currentLine.from + textBeforeCursor.length + 1 },
userEvent: "HeaderEnhancer.changeAutoNumbering"
});
setTimeout(async () => {
await this.plugin.handleAddHeaderNumber(activeView);
if (this.plugin.settings.isAutoDetectHeaderLevel) {
this.plugin.handleShowStateBarChange();
}
}, 10);
return true;
}
/**
* 处理 Backspace 键按下事件
*/
handlePressBackspace(view) {
let state = view.state;
let doc = state.doc;
const pos = state.selection.main.to;
const changes = [];
if (!isHeader(doc.lineAt(pos).text)) {
return false;
}
changes.push({
from: pos - 1,
to: pos,
insert: ""
});
if (this.plugin.settings.autoNumberingMode === "on" /* ON */) {
}
view.dispatch({
changes,
selection: { anchor: pos - 1 },
userEvent: "HeaderEnhancer.changeAutoNumbering"
});
return true;
}
};
// src/styles/style-manager.ts
var StyleManager = class {
constructor(settings) {
this.settings = settings;
this.headerFontStyleEl = null;
this.titleFontStyleEl = null;
this.dialogStyleEl = null;
this.ribbonIconStyleEl = null;
}
/**
* Apply all CSS styles
*/
applyCSSStyles() {
this.applyHeaderFontStyles();
this.applyTitleFontStyles();
this.applyDialogStyles();
this.applyRibbonIconStyles();
}
/**
* Remove all CSS styles
*/
removeCSSStyles() {
this.removeHeaderFontStyles();
this.removeTitleFontStyles();
this.removeDialogStyles();
this.removeRibbonIconStyles();
}
/**
* Apply header font styles (# ## ### etc.)
*/
applyHeaderFontStyles() {
this.removeHeaderFontStyles();
if (!this.settings.isSeparateHeaderFont) {
return;
}
this.headerFontStyleEl = document.createElement("style");
this.headerFontStyleEl.id = "header-enhancer-header-font-styles";
let cssRules = "";
const headerSelectors = [
".markdown-preview-view h1",
".markdown-preview-view h2",
".markdown-preview-view h3",
".markdown-preview-view h4",
".markdown-preview-view h5",
".markdown-preview-view h6",
".markdown-source-view.mod-cm6 .HyperMD-header-1",
".markdown-source-view.mod-cm6 .HyperMD-header-2",
".markdown-source-view.mod-cm6 .HyperMD-header-3",
".markdown-source-view.mod-cm6 .HyperMD-header-4",
".markdown-source-view.mod-cm6 .HyperMD-header-5",
".markdown-source-view.mod-cm6 .HyperMD-header-6"
].join(", ");
if (this.settings.headerFontFamily && this.settings.headerFontFamily !== "inherit") {
cssRules += `${headerSelectors} { font-family: ${this.settings.headerFontFamily} !important; }
`;
}
if (this.settings.headerFontSize && this.settings.headerFontSize !== "inherit") {
cssRules += `${headerSelectors} { font-size: ${this.settings.headerFontSize} !important; }
`;
}
this.headerFontStyleEl.textContent = cssRules;
if (cssRules) {
document.head.appendChild(this.headerFontStyleEl);
}
}
/**
* 应用文档标题字体样式
*/
applyTitleFontStyles() {
this.removeTitleFontStyles();
if (!this.settings.isSeparateTitleFont) {
return;
}
this.titleFontStyleEl = document.createElement("style");
this.titleFontStyleEl.id = "header-enhancer-title-font-styles";
let cssRules = "";
const titleSelectors = [
// 标签页中的文件标题 - 只针对特定的标签标题元素
".workspace-tab-header-inner-title",
".workspace-tab-header .workspace-tab-header-inner-title",
".workspace-tabs .workspace-tab-header-inner-title",
// 视图标题栏标题
".workspace-leaf-content .view-header-title",
// 文档内联标题(在文档中显示的主标题)
".inline-title",
".markdown-preview-view .inline-title",
".markdown-source-view .inline-title",
// 文件资源管理器中的文件标题
".nav-file-title-content",
".tree-item-inner.nav-file-title-content",
// Frontmatter 标题显示
'.frontmatter-container .metadata-property[data-property-key="title"] .metadata-property-value'
].join(", ");
if (this.settings.titleFontFamily && this.settings.titleFontFamily !== "inherit") {
cssRules += `${titleSelectors} { font-family: ${this.settings.titleFontFamily} !important; }
`;
}
if (this.settings.titleFontSize && this.settings.titleFontSize !== "inherit") {
cssRules += `${titleSelectors} { font-size: ${this.settings.titleFontSize} !important; }
`;
}
this.titleFontStyleEl.textContent = cssRules;
if (cssRules) {
document.head.appendChild(this.titleFontStyleEl);
}
}
/**
* Apply ribbon icon state styles
*/
applyRibbonIconStyles() {
this.removeRibbonIconStyles();
this.ribbonIconStyleEl = document.createElement("style");
this.ribbonIconStyleEl.id = "header-enhancer-ribbon-icon-styles";
const ribbonIconCSS = `
/* Ribbon icon state styles for Header Enhancer */
.side-dock-ribbon-action[aria-label*="Header Enhancer"].header-enhancer-global-disabled {
opacity: 0.4;
}
.side-dock-ribbon-action[aria-label*="Header Enhancer"].header-enhancer-global-disabled:hover {
opacity: 0.6;
}
.side-dock-ribbon-action[aria-label*="Header Enhancer"].header-enhancer-document-enabled {
color: var(--color-accent);
}
.side-dock-ribbon-action[aria-label*="Header Enhancer"].header-enhancer-document-enabled:hover {
color: var(--color-accent-hover);
}
.side-dock-ribbon-action[aria-label*="Header Enhancer"].header-enhancer-document-disabled {
opacity: 0.7;
color: var(--text-muted);
}
.side-dock-ribbon-action[aria-label*="Header Enhancer"].header-enhancer-document-disabled:hover {
opacity: 1;
color: var(--text-normal);
}
/* Additional visual indicator for global disabled state */
.side-dock-ribbon-action[aria-label*="Header Enhancer"].header-enhancer-global-disabled::before {
content: "";
position: absolute;
top: 2px;
right: 2px;
width: 6px;
height: 6px;
border-radius: 50%;
background-color: var(--text-error);
z-index: 10;
}
`;
this.ribbonIconStyleEl.textContent = ribbonIconCSS;
document.head.appendChild(this.ribbonIconStyleEl);
}
/**
* 应用对话框组件样式
*/
applyDialogStyles() {
this.removeDialogStyles();
this.dialogStyleEl = document.createElement("style");
this.dialogStyleEl.id = "header-enhancer-dialog-styles";
const dialogCSS = `
/* Auto numbering dialog styles - shared between removal and activation */
.header-enhancer-removal-dialog,
.header-enhancer-activation-dialog {
max-width: 500px;
}
.header-enhancer-removal-dialog .modal-title,
.header-enhancer-activation-dialog .modal-title {
margin-bottom: 1em;
padding-bottom: 0.5em;
color: var(--text-accent);
border-bottom: 1px solid var(--background-modifier-border);
}
.header-enhancer-removal-dialog .modal-message,
.header-enhancer-activation-dialog .modal-message {
margin-bottom: 1.5em;
line-height: 1.5;
color: var(--text-normal);
}
.header-enhancer-removal-dialog .modal-actions,
.header-enhancer-activation-dialog .modal-actions {
margin-top: 1em;
padding-top: 1em;
border-top: 1px solid var(--background-modifier-border);
}
.header-enhancer-removal-dialog .modal-actions .setting-item,
.header-enhancer-activation-dialog .modal-actions .setting-item {
margin-bottom: 0.75em;
padding: 0.75em;
background-color: var(--background-secondary);
border: 1px solid var(--background-modifier-border);
border-radius: 6px;
transition: all 0.2s ease;
}
.header-enhancer-removal-dialog .modal-actions .setting-item:hover,
.header-enhancer-activation-dialog .modal-actions .setting-item:hover {
background-color: var(--background-secondary-alt);
border-color: var(--background-modifier-border-hover);
}
/* Warning and tip text styles */
.header-enhancer-removal-dialog .setting-item-warning,
.header-enhancer-removal-dialog .setting-item-tip,
.header-enhancer-activation-dialog .setting-item-warning,
.header-enhancer-activation-dialog .setting-item-tip {
margin-top: 0.5em;
font-size: 0.85em;
line-height: 1.3;
}
.header-enhancer-removal-dialog .warning-label,
.header-enhancer-activation-dialog .warning-label {
color: var(--text-error);
font-weight: 600;
}
.header-enhancer-removal-dialog .warning-text,
.header-enhancer-removal-dialog .progress-text,
.header-enhancer-activation-dialog .warning-text,
.header-enhancer-activation-dialog .progress-text {
color: var(--text-muted);
margin: 0;
}
.header-enhancer-removal-dialog .manual-tip-text,
.header-enhancer-activation-dialog .manual-tip-text {
color: var(--text-muted);
font-style: italic;
}
.header-enhancer-removal-dialog .modal-cancel,
.header-enhancer-activation-dialog .modal-cancel {
margin-top: 1em;
padding-top: 1em;
text-align: center;
border-top: 1px solid var(--background-modifier-border-focus);
}
.header-enhancer-removal-dialog .progress-container,
.header-enhancer-activation-dialog .progress-container {
margin-top: 1em;
padding: 1em;
background-color: var(--background-secondary);
border: 1px solid var(--background-modifier-border);
border-radius: 6px;
text-align: center;
}
.header-enhancer-removal-dialog .progress-text,
.header-enhancer-activation-dialog .progress-text {
font-size: 0.9em;
}
.header-enhancer-removal-dialog button:disabled,
.header-enhancer-activation-dialog button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
`;
this.dialogStyleEl.textContent = dialogCSS;
document.head.appendChild(this.dialogStyleEl);
}
/**
* 移除标题字体样式
*/
removeHeaderFontStyles() {
if (this.headerFontStyleEl) {
this.headerFontStyleEl.remove();
this.headerFontStyleEl = null;
}
}
/**
* 移除文档标题字体样式
*/
removeTitleFontStyles() {
if (this.titleFontStyleEl) {
this.titleFontStyleEl.remove();
this.titleFontStyleEl = null;
}
}
/**
* 移除对话框样式
*/
removeDialogStyles() {
if (this.dialogStyleEl) {
this.dialogStyleEl.remove();
this.dialogStyleEl = null;
}
}
/**
* 移除侧边栏图标样式
*/
removeRibbonIconStyles() {
if (this.ribbonIconStyleEl) {
this.ribbonIconStyleEl.remove();
this.ribbonIconStyleEl = null;
}
}
/**
* 更新设置引用(当设置改变时调用)
*/
updateSettings(newSettings) {
this.settings = newSettings;
}
};
// src/main.ts
var HeaderEnhancerPlugin = class extends import_obsidian6.Plugin {
constructor() {
super(...arguments);
this.perDocumentStatesMap = /* @__PURE__ */ new Map();
}
async onload() {
await this.loadSettings();
this.backlinkManager = new BacklinkManager(this.app);
this.editorHandlers = new EditorHandlers(this);
this.styleManager = new StyleManager(this.settings);
this.styleManager.applyCSSStyles();
this.ribbonIconEl = this.addRibbonIcon(
"heading-glyph",
"Header Enhancer",
async (_evt) => {
const i18n2 = I18n.getInstance();
const app = this.app;
const activeView = app.workspace.getActiveViewOfType(import_obsidian6.MarkdownView);
if (!(activeView == null ? void 0 : activeView.file)) {
new import_obsidian6.Notice(i18n2.t("notices.noActiveView"));
return;
}
const filePath = activeView.file.path;
if (!this.settings.globalAutoNumberingEnabled) {
new import_obsidian6.Notice(i18n2.t("notices.globalDisabledNotice"));
return;
}
const currentState = this.getDocumentAutoNumberingState(filePath);
const newState = !currentState;
await this.toggleDocumentState(activeView, newState);
this.updateAllUIStates();
}
);
this.statusBarItemEl = this.addStatusBarItem();
this.handleShowStateBarChange();
this.handleShowSidebarChange();
this.updateAllUIStates();
const keyHandlers = this.editorHandlers.registerKeyHandlers();
keyHandlers.forEach((handler) => this.registerEditorExtension(handler));
this.registerEvent(
this.app.workspace.on("active-leaf-change", () => {
this.updateAllUIStates();
})
);
this.registerEvent(
this.app.workspace.on("file-open", () => {
setTimeout(() => {
this.updateAllUIStates();
}, 50);
})
);
const i18n = I18n.getInstance();
this.addCommand({
id: "toggle-global-auto-numbering",
name: i18n.t("commands.toggleGlobalAutoNumbering"),
callback: async () => {
const newState = !this.settings.globalAutoNumberingEnabled;
this.settings.globalAutoNumberingEnabled = newState;
await this.saveSettings();
new import_obsidian6.Notice(newState ? i18n.t("notices.globalAutoNumberingEnabled") : i18n.t("notices.globalAutoNumberingDisabled"));
this.updateAllUIStates();
}
});
this.addCommand({
id: "toggle-document-auto-numbering",
name: i18n.t("commands.toggleDocumentAutoNumbering"),
callback: async () => {
const activeView = this.app.workspace.getActiveViewOfType(import_obsidian6.MarkdownView);
if (!(activeView == null ? void 0 : activeView.file)) {
new import_obsidian6.Notice(i18n.t("notices.noActiveView"));
return;
}
const filePath = activeView.file.path;
if (!this.settings.globalAutoNumberingEnabled) {
new import_obsidian6.Notice(i18n.t("notices.globalDisabledNotice"));
return;
}
const currentState = this.getDocumentAutoNumberingState(filePath);
const newState = !currentState;
await this.toggleDocumentState(activeView, newState);
this.updateAllUIStates();
}
});
this.addCommand({
id: "reset-auto-numbering-yaml",
name: i18n.t("commands.resetAutoNumberingYaml"),
callback: () => {
const app = this.app;
const activeView = app.workspace.getActiveViewOfType(import_obsidian6.MarkdownView);
if (!activeView) {
new import_obsidian6.Notice(i18n.t("notices.noActiveView"));
return;
} else {
const editor = activeView.editor;
const yaml = getAutoNumberingYaml(editor);
if (yaml === "") {
new import_obsidian6.Notice(i18n.t("notices.yamlNotExists"));
} else {
const value = [
"state on",
"start-level h2",
"end-level h6",
"start-at 1",
"separator ."
];
setAutoNumberingYaml(editor, value);
}
}
}
});
this.addCommand({
id: "remove-auto-numbering-yaml",
name: i18n.t("commands.removeAutoNumberingYaml"),
callback: () => {
const app = this.app;
const activeView = app.workspace.getActiveViewOfType(import_obsidian6.MarkdownView);
if (!activeView) {
new import_obsidian6.Notice(i18n.t("notices.noActiveView"));
return;
} else {
const editor = activeView.editor;
const yaml = getAutoNumberingYaml(editor);
if (yaml === "") {
new import_obsidian6.Notice(i18n.t("notices.yamlNotExists"));
} else {
setAutoNumberingYaml(editor, []);
}
}
}
});
this.addCommand({
id: "apply-custom-yaml-config",
name: i18n.t("commands.applyCustomYamlConfig"),
callback: () => {
const app = this.app;
const activeView = app.workspace.getActiveViewOfType(import_obsidian6.MarkdownView);
if (!activeView) {
new import_obsidian6.Notice(i18n.t("notices.noActiveView"));
return;
} else {
const editor = activeView.editor;
const yaml = getAutoNumberingYaml(editor);
if (yaml !== "") {
new import_obsidian6.Notice(i18n.t("notices.yamlAlreadyExists"));
} else {
const value = [
"state on",
`start-level h${this.settings.yamlDefaultStartLevel}`,
`end-level h${this.settings.yamlDefaultEndLevel}`,
`start-at ${this.settings.yamlDefaultStartNumber}`,
`separator ${this.settings.yamlDefaultSeparator}`
];
setAutoNumberingYaml(editor, value);
new import_obsidian6.Notice(i18n.t("notices.yamlTemplateInserted"));
}
}
}
});
this.addSettingTab(new HeaderEnhancerSettingTab(this.app, this));
this.registerInterval(
window.setInterval(() => {
}, 5 * 60 * 1e3)
);
}
onunload() {
this.styleManager.removeCSSStyles();
}
async loadSettings() {
this.settings = Object.assign(
{},
DEFAULT_SETTINGS,
await this.loadData()
);
this.migrateSettings();
try {
if (this.settings.perDocumentStates) {
const parsedStates = JSON.parse(this.settings.perDocumentStates);
this.perDocumentStatesMap = new Map(Object.entries(parsedStates));
}
} catch (error) {
console.warn("Failed to parse perDocumentStates, using empty map:", error);
this.perDocumentStatesMap = /* @__PURE__ */ new Map();
}
}
/**
* Migrate settings for backward compatibility
*/
migrateSettings() {
if (this.settings.globalAutoNumberingEnabled === void 0) {
this.settings.globalAutoNumberingEnabled = this.settings.autoNumberingMode !== "off" /* OFF */;
}
if (this.settings.perDocumentStates === void 0) {
this.settings.perDocumentStates = "{}";
}
}
async saveSettings() {
const statesObject = Object.fromEntries(this.perDocumentStatesMap.entries());
this.settings.perDocumentStates = JSON.stringify(statesObject);
await this.saveData(this.settings);
this.styleManager.updateSettings(this.settings);
}
/**
* Get the current document auto numbering state
* Legacy method - now uses unified state evaluation
*/
getDocumentAutoNumberingState(filePath) {
const activeView = this.app.workspace.getActiveViewOfType(import_obsidian6.MarkdownView);
if (!(activeView == null ? void 0 : activeView.file) || activeView.file.path !== filePath) {
return this.getSimpleDocumentState(filePath);
}
return this.getUnifiedDocumentState(activeView.editor, filePath);
}
/**
* Get unified document auto numbering state - includes YAML parsing
* This ensures UI and actual numbering use exactly the same logic
*/
getUnifiedDocumentState(editor, filePath) {
const config = getAutoNumberingConfig(
this.settings,
editor,
(path) => this.getSimpleDocumentState(path),
filePath
);
return config.state;
}
/**
* Get simple document state without YAML parsing (for non-active documents)
*/
getSimpleDocumentState(filePath) {
var _a;
if (!this.settings.globalAutoNumberingEnabled) {
return false;
}
if (this.perDocumentStatesMap.has(filePath)) {
return (_a = this.perDocumentStatesMap.get(filePath)) != null ? _a : false;
}
return this.settings.autoNumberingMode !== "off" /* OFF */;
}
/**
* Set the current document auto numbering state
*/
async setDocumentAutoNumberingState(filePath, enabled) {
this.perDocumentStatesMap.set(filePath, enabled);
await this.saveSettings();
}
/**
* Update all UI states (ribbon icon, status bar)
*/
updateAllUIStates() {
this.updateRibbonIconState();
this.handleShowStateBarChange();
}
/**
* Update ribbon icon state based on global and document settings
*/
updateRibbonIconState() {
const activeView = this.app.workspace.getActiveViewOfType(import_obsidian6.MarkdownView);
if (!(activeView == null ? void 0 : activeView.file)) {
if (!this.settings.globalAutoNumberingEnabled) {
this.ribbonIconEl.addClass("header-enhancer-global-disabled");
this.ribbonIconEl.removeClass("header-enhancer-document-enabled");
this.ribbonIconEl.removeClass("header-enhancer-document-disabled");
this.ribbonIconEl.setAttribute("aria-label", "Header Enhancer (Global Disabled)");
} else {
this.ribbonIconEl.removeClass("header-enhancer-global-disabled");
this.ribbonIconEl.removeClass("header-enhancer-document-enabled");
this.ribbonIconEl.addClass("header-enhancer-document-disabled");
this.ribbonIconEl.setAttribute("aria-label", "Header Enhancer (No Active Document)");
}
return;
}
const filePath = activeView.file.path;
const globalEnabled = this.settings.globalAutoNumberingEnabled;
const documentEnabled = this.getDocumentAutoNumberingState(filePath);
if (!globalEnabled) {
this.ribbonIconEl.addClass("header-enhancer-global-disabled");
this.ribbonIconEl.removeClass("header-enhancer-document-enabled");
this.ribbonIconEl.removeClass("header-enhancer-document-disabled");
this.ribbonIconEl.setAttribute("aria-label", "Header Enhancer (Global Disabled)");
} else if (documentEnabled) {
this.ribbonIconEl.removeClass("header-enhancer-global-disabled");
this.ribbonIconEl.addClass("header-enhancer-document-enabled");
this.ribbonIconEl.removeClass("header-enhancer-document-disabled");
this.ribbonIconEl.setAttribute("aria-label", "Header Enhancer (Document Enabled)");
} else {
this.ribbonIconEl.removeClass("header-enhancer-global-disabled");
this.ribbonIconEl.removeClass("header-enhancer-document-enabled");
this.ribbonIconEl.addClass("header-enhancer-document-disabled");
this.ribbonIconEl.setAttribute("aria-label", "Header Enhancer (Document Disabled)");
}
}
handleShowStateBarChange() {
if (this.settings.showOnStatusBar) {
const i18n = I18n.getInstance();
let autoNumberingStatus;
if (!this.settings.globalAutoNumberingEnabled) {
autoNumberingStatus = i18n.t("statusBar.globalDisabled");
} else {
const activeView = this.app.workspace.getActiveViewOfType(import_obsidian6.MarkdownView);
if (!(activeView == null ? void 0 : activeView.file)) {
autoNumberingStatus = i18n.t("statusBar.off");
} else {
const filePath = activeView.file.path;
const documentEnabled = this.getDocumentAutoNumberingState(filePath);
if (documentEnabled) {
switch (this.settings.autoNumberingMode) {
case "on" /* ON */:
if (this.settings.isAutoDetectHeaderLevel) {
const analysis = analyzeHeaderLevels(activeView.editor.getValue());
if (!analysis.isEmpty) {
autoNumberingStatus = `${i18n.t("statusBar.auto")}(H${analysis.minLevel}-H${analysis.maxLevel})`;
} else {
autoNumberingStatus = i18n.t("statusBar.autoNoHeaders");
}
} else {
autoNumberingStatus = `${i18n.t("statusBar.on")}(H${this.settings.startHeaderLevel}-H${this.settings.endHeaderLevel})`;
}
break;
case "yaml" /* YAML_CONTROLLED */:
autoNumberingStatus = i18n.t("statusBar.yaml");
break;
case "off" /* OFF */:
default:
autoNumberingStatus = i18n.t("statusBar.documentEnabled");
break;
}
} else {
autoNumberingStatus = i18n.t("statusBar.documentDisabled");
}
}
}
this.statusBarItemEl.setText(
`${i18n.t("statusBar.title")}: ${autoNumberingStatus}`
);
this.statusBarItemEl.show();
} else {
this.statusBarItemEl.hide();
}
}
handleShowSidebarChange() {
if (this.settings.showOnSidebar) {
this.ribbonIconEl.show();
} else {
this.ribbonIconEl.hide();
}
}
async handleAddHeaderNumber(view) {
var _a;
const editor = view.editor;
const lineCount = editor.lineCount();
let docCharCount = 0;
const filePath = (_a = view.file) == null ? void 0 : _a.path;
const config = getAutoNumberingConfig(
this.settings,
editor,
filePath ? (path) => this.getDocumentAutoNumberingState(path) : void 0,
filePath
);
if (!config.state) {
return false;
}
const currentFile = view.file;
const headerChanges = [];
if (config.state) {
let insertNumber = [Number(config.startNumber) - 1];
let isCodeBlock = false;
for (let i = 0; i <= lineCount; i++) {
const line = editor.getLine(i);
docCharCount += line.length;
if (line.startsWith("```")) {
isCodeBlock = !isCodeBlock;
if (line.slice(3).contains("```")) {
isCodeBlock = !isCodeBlock;
}
}
if (isCodeBlock) {
continue;
}
if (isHeader(line)) {
const [headerLevel, realHeaderLevel] = getHeaderLevel(
line,
config.startLevel
);
if (headerLevel <= 0) {
continue;
}
insertNumber = getNextNumber(insertNumber, headerLevel);
const insertNumberStr = insertNumber.join(config.separator);
let newLine = null;
let originalHeading = null;
if (isNeedInsertNumber(
line,
this.settings.autoNumberingHeaderSeparator
)) {
originalHeading = line.substring(realHeaderLevel + 1).trim();
newLine = "#".repeat(realHeaderLevel) + " " + insertNumberStr + this.settings.autoNumberingHeaderSeparator + line.substring(realHeaderLevel + 1);
} else if (isNeedUpdateNumber(
insertNumberStr,
line,
this.settings.autoNumberingHeaderSeparator
)) {
const textAfterSeparator = line.split(this.settings.autoNumberingHeaderSeparator)[1];
originalHeading = textAfterSeparator ? textAfterSeparator.trim() : null;
const originNumberLength = line.split(
this.settings.autoNumberingHeaderSeparator
)[0].split(" ")[1].length;
newLine = "#".repeat(realHeaderLevel) + " " + insertNumberStr + line.substring(
realHeaderLevel + originNumberLength + 1
);
}
if (newLine && newLine !== line && originalHeading) {
headerChanges.push({
lineIndex: i,
oldText: line,
newText: newLine,
originalHeading
});
editor.setLine(i, newLine);
}
}
}
if (this.settings.updateBacklinks && headerChanges.length > 0 && currentFile) {
await this.updateBacklinksForChanges(currentFile, headerChanges);
}
}
return true;
}
/**
* Handle backlink updates when headers change
*/
async updateBacklinksForChanges(currentFile, headerChanges) {
try {
for (const change of headerChanges) {
const oldHeading = change.originalHeading;
if (oldHeading && change.oldText !== change.newText) {
const backlinks = await this.backlinkManager.findHeadingBacklinks(
currentFile,
oldHeading.trim()
);
const updates = backlinks.map((link) => {
const fullNewHeading = this.extractFullHeadingWithNumber(change.newText);
const newLink = link.oldLink.replace(
`#${oldHeading.trim()}`,
`#${fullNewHeading}`
);
return {
...link,
newLink
};
});
if (updates.length > 0) {
await this.backlinkManager.updateBacklinks(updates);
}
}
}
} catch (error) {
console.error("Error updating backlinks:", error);
new import_obsidian6.Notice("Failed to update backlinks: " + error.message);
}
}
/**
* Extract plain heading text (remove # symbols and numbering)
*/
extractHeadingText(headerLine) {
const match = headerLine.match(/^#+\s*(?:\d+[\.\-\/,]*\d*\s*[\t\s]*)?\s*(.+)$/);
return match ? match[1].trim() : null;
}
/**
* Extract full heading text with numbering (remove # symbols but keep numbering)
*/
extractFullHeadingWithNumber(headerLine) {
const match = headerLine.match(/^#+\s*(.+)$/);
return match ? match[1].trim() : headerLine;
}
async handleRemoveHeaderNumber(view) {
var _a;
const editor = view.editor;
const lineCount = editor.lineCount();
const filePath = (_a = view.file) == null ? void 0 : _a.path;
const config = getAutoNumberingConfig(
this.settings,
editor,
filePath ? (path) => this.getDocumentAutoNumberingState(path) : void 0,
filePath
);
const currentFile = view.file;
const headerChanges = [];
for (let i = 0; i <= lineCount; i++) {
const line = editor.getLine(i);
if (isHeader(line)) {
const [headerLevel, _] = getHeaderLevel(
line,
config.startLevel
);
if (headerLevel <= 0) {
continue;
}
const newLine = removeHeaderNumber(
line,
this.settings.autoNumberingHeaderSeparator
);
if (newLine !== line) {
const originalHeading = this.extractHeadingText(newLine);
if (originalHeading) {
headerChanges.push({
lineIndex: i,
oldText: line,
newText: newLine,
originalHeading
});
}
editor.setLine(i, newLine);
}
}
}
if (this.settings.updateBacklinks && headerChanges.length > 0 && currentFile) {
await this.updateBacklinksForRemoval(currentFile, headerChanges);
}
return true;
}
/**
* Handle backlink updates when removing header numbers
*/
async updateBacklinksForRemoval(currentFile, headerChanges) {
try {
for (const change of headerChanges) {
const oldFullHeading = this.extractFullHeadingWithNumber(change.oldText);
const newHeading = change.originalHeading;
if (oldFullHeading && newHeading) {
const backlinks = await this.backlinkManager.findHeadingBacklinks(
currentFile,
oldFullHeading
);
const updates = backlinks.map((link) => {
const newLink = link.oldLink.replace(
`#${oldFullHeading}`,
`#${newHeading}`
);
return {
...link,
newLink
};
});
if (updates.length > 0) {
await this.backlinkManager.updateBacklinks(updates);
}
}
}
} catch (error) {
console.error("Error updating backlinks for removal:", error);
new import_obsidian6.Notice("Failed to update backlinks during removal: " + error.message);
}
}
/**
* Toggle document auto-numbering state
* In YAML mode: synchronizes both perDocumentStatesMap and YAML state
* In ON mode: only modifies perDocumentStatesMap
*/
async toggleDocumentState(activeView, newState) {
var _a;
const i18n = I18n.getInstance();
const filePath = (_a = activeView.file) == null ? void 0 : _a.path;
if (!filePath)
return;
if (this.settings.autoNumberingMode === "yaml" /* YAML_CONTROLLED */) {
await this.setDocumentAutoNumberingState(filePath, newState);
const editor = activeView.editor;
const yaml = getAutoNumberingYaml(editor);
if (yaml === "") {
const value = [
`state ${newState ? "on" : "off"}`
];
setAutoNumberingYaml(editor, value);
} else {
const yamlArray = yaml;
let hasState = false;
const newYaml = yamlArray.map((line) => {
if (line.startsWith("state ")) {
hasState = true;
return `state ${newState ? "on" : "off"}`;
}
return line;
});
if (!hasState) {
newYaml.unshift(`state ${newState ? "on" : "off"}`);
}
setAutoNumberingYaml(editor, newYaml);
}
if (newState) {
await this.handleAddHeaderNumber(activeView);
new import_obsidian6.Notice(i18n.t("notices.autoNumberingEnabledForDocument"));
} else {
await this.handleRemoveHeaderNumber(activeView);
new import_obsidian6.Notice(i18n.t("notices.autoNumberingDisabledForDocument"));
}
} else {
await this.setDocumentAutoNumberingState(filePath, newState);
if (newState) {
await this.handleAddHeaderNumber(activeView);
new import_obsidian6.Notice(i18n.t("notices.autoNumberingEnabledForDocument"));
} else {
await this.handleRemoveHeaderNumber(activeView);
new import_obsidian6.Notice(i18n.t("notices.autoNumberingDisabledForDocument"));
}
}
}
};
/* nosourcemap */