UNPKG

insta-toc

Version:

Simultaneously generate, update, and maintain a table of contents for your notes in real time.

198 lines (184 loc) 8.34 kB
import type { SliderComponent, TextComponent } from "obsidian"; import type { CheckboxComponent } from "obsidian-dev-utils/obsidian/components/setting-components/checkbox-component"; import type { TypedDropdownComponent } from "obsidian-dev-utils/obsidian/components/setting-components/typed-dropdown-component"; import { PluginSettingsTabBase, SAVE_TO_FILE_CONTEXT } from "obsidian-dev-utils/obsidian/plugin/plugin-settings-tab-base"; import { SettingEx } from "obsidian-dev-utils/obsidian/setting-ex"; import type InstaTocPlugin from "../Plugin"; import { headingLevelOptions } from "../constants"; import { ComponentMounter, ListInputComponent } from "../svelte"; import type { HeadingLevel, PluginTypes } from "../types"; import type { InstaTocSettings } from "./Settings"; export class SettingTab extends PluginSettingsTabBase<PluginTypes> { public constructor(plugin: InstaTocPlugin) { super(plugin); } private async reloadToc(): Promise<void> { await this.plugin.reload({ forceValidate: true }); } public override display(): void { super.display(); const tabTitle = new SettingEx(this.containerEl).setHeading().setName("Insta ToC Global Settings"); tabTitle.nameEl.classList.add("setting-title"); tabTitle.controlEl.remove(); // Update delay this.addUpdateDelaySetting(); // Global ToC title this.addTocTitleSetting(); // Global ToC Level this.addTocLevelSetting(); // Global ToC Centering this.addTocCenteringSetting(); // Global excluded heading text this.addExcludedHeadingTextSetting(); // Global excluded heading levels this.addExcludedHeadingLevelSetting(); // Global excluded characters this.addExcludedCharacterSetting(); // this.plugin.settingsManager.on("saveSettings", async (newSettings, oldSettings) => { // this.reloadToc(); // }, this); } private addTocLevelSetting(): void { new SettingEx(this.containerEl) .setName("ToC Heading Level") .setDesc( "The global heading level for the Table of Contents title." ) .setClass("insta-toc-heading-level-setting") .addTypedDropdown( (typedDropdown: TypedDropdownComponent<HeadingLevel>) => { typedDropdown.addOptions(headingLevelOptions); this.bind(typedDropdown, "tocTitleLevel", { onChanged: async () => { await this.reloadToc(); } }); } ); } private addUpdateDelaySetting(): void { new SettingEx(this.containerEl).setName("Update delay").setDesc("The delay for each ToC update.").addSlider( (component: SliderComponent) => { component.setLimits(500, 10000, 500).setDynamicTooltip().setInstant(false); this.bind(component, "updateDelay", { onChanged: async () => { await this.reloadToc(); this.plugin.updateModifyEventListener(); } }); } ); } private addTocTitleSetting(): void { new SettingEx(this.containerEl) .setName("ToC Title") .setDesc("The global title for the Table of Contents.") .addText((component: TextComponent) => { this.bind(component, "tocTitle", { onChanged: async () => { await this.reloadToc(); } }); component.inputEl.placeholder = "Table of Contents"; }) .infoEl .classList .add("insta-toc-text-info"); } private addTocCenteringSetting(): void { new SettingEx(this.containerEl) .setName("Center ToC Title") .setDesc("Whether to center the Table of Contents title.") .addCheckbox((checkbox: CheckboxComponent) => { this.bind(checkbox, "tocTitleCentered", { onChanged: async () => await this.reloadToc() }); }); } private addExcludedHeadingTextSetting(): void { new SettingEx(this.containerEl) .setName("Excluded heading text") .setDesc("Add headings to exclude globally.") .addComponentClass(ComponentMounter, async (component) => { await component.setup(ListInputComponent, { plugin: this.plugin, placeholder: "e.g. Table of Contents", initialValues: this.plugin.settings.excludedHeadingText, parseInput: (raw: string) => { const value = raw.trim(); return value ? { ok: true, value } : { ok: false, message: "Heading text cannot be empty." }; }, renderValue: (v: string) => v, onSave: async (values: string[]) => { await this.plugin.settingsManager.editAndSave((settings: InstaTocSettings) => { settings.excludedHeadingText = values; }, SAVE_TO_FILE_CONTEXT); await this.reloadToc(); } }); }); } private addExcludedHeadingLevelSetting(): void { new SettingEx(this.containerEl) .setName("Excluded heading levels") .setDesc( "Add heading levels (1–6) to exclude globally." ) .addComponentClass(ComponentMounter, async (component) => { await component.setup(ListInputComponent, { plugin: this.plugin, placeholder: "1-6", initialValues: this .plugin .settings .excludedHeadingLevels, parseInput: (raw: string) => { const n = Number.parseInt(raw.trim(), 10); if (!Number.isInteger(n) || n < 1 || n > 6) { return { ok: false, message: "Level must be an integer from 1 to 6." }; } return { ok: true, value: n as HeadingLevel }; }, renderValue: (v: HeadingLevel) => `H${v}`, equals: (a: HeadingLevel, b: HeadingLevel) => a === b, sort: (a: HeadingLevel, b: HeadingLevel) => a - b, onSave: async (values: HeadingLevel[]) => { await this.plugin.settingsManager.editAndSave((settings: InstaTocSettings) => { settings.excludedHeadingLevels = values; }, SAVE_TO_FILE_CONTEXT); await this.reloadToc(); } }); }); } private addExcludedCharacterSetting(): void { new SettingEx(this.containerEl) .setName("Excluded characters") .setDesc( "Add characters to strip from headings globally." ) .addComponentClass(ComponentMounter, async (component) => { await component.setup(ListInputComponent, { plugin: this.plugin, placeholder: "e.g. #", initialValues: this.plugin.settings.excludedChars, parseInput: (raw: string) => { const value = raw.trim(); return value ? { ok: true, value } : { ok: false, message: "Character value cannot be empty." }; }, renderValue: (v: string) => v, onSave: async (values: string[]) => { await this.plugin.settingsManager.editAndSave((settings: InstaTocSettings) => { settings.excludedChars = values; }, SAVE_TO_FILE_CONTEXT); await this.reloadToc(); } }); }); } }