UNPKG

insta-toc

Version:

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

317 lines (243 loc) 7.39 kB
type AnyFn = (...args: any[]) => any; export class App { workspace: any = {}; vault: any = {}; metadataCache: any = {}; fileManager: any = {}; } export class BaseComponent {} export class Plugin { app: App; constructor(app?: App, _manifest?: unknown) { this.app = app ?? new App(); } registerEvent(_ref: unknown): void {} registerMarkdownCodeBlockProcessor(_name: string, _processor: AnyFn): void {} addSettingTab(_tab: unknown): void {} async loadData(): Promise<any> { return {}; } async saveData(_data: unknown): Promise<void> {} } export class PluginSettingTab { app: App; plugin: unknown; containerEl: any = { empty: () => undefined }; constructor(app: App, plugin: unknown) { this.app = app; this.plugin = plugin; } } export class DropdownComponent { addOptions(_options: Record<string, string>): this { return this; } setValue(_value: string): this { return this; } onChange(_handler: AnyFn): this { return this; } } export class SliderComponent { setLimits(_min: number, _max: number, _step: number): this { return this; } setDynamicTooltip(): this { return this; } setInstant(_instant: boolean): this { return this; } setValue(_value: number): this { return this; } onChange(_handler: AnyFn): this { return this; } } export class TextComponent { inputEl: any = { placeholder: "", classList: { add: (..._classes: string[]) => undefined } }; setValue(_value: string): this { return this; } onChange(_handler: AnyFn): this { return this; } } export class TextAreaComponent { private value = ""; inputEl: any = { placeholder: "", classList: { add: (..._classes: string[]) => undefined }, addEventListener: (_event: string, _handler: AnyFn) => undefined }; setValue(value: string): this { this.value = value; return this; } getValue(): string { return this.value; } onChange(_handler: AnyFn): this { return this; } } export class Setting { nameEl: any = { classList: { add: (..._classes: string[]) => undefined } }; controlEl: any = { remove: () => undefined }; infoEl: any = { classList: { add: (..._classes: string[]) => undefined } }; constructor(_containerEl: unknown) {} setHeading(): this { return this; } setName(_name: string): this { return this; } setDesc(_description: string): this { return this; } setTooltip(_tooltip: string): this { return this; } addDropdown(callback: (component: DropdownComponent) => unknown): this { callback(new DropdownComponent()); return this; } addSlider(callback: (component: SliderComponent) => unknown): this { callback(new SliderComponent()); return this; } addText(callback: (component: TextComponent) => unknown): this { callback(new TextComponent()); return this; } addTextArea(callback: (component: TextAreaComponent) => unknown): this { callback(new TextAreaComponent()); return this; } } export class TFile { path = ""; } export class MarkdownView {} export class Notice { message: string; constructor(message: string) { this.message = message; } } export const MarkdownRenderer = { render: async ( _app: App, _source: string, _el: HTMLElement, _sourcePath: string, _component: unknown ): Promise<void> => { return; } }; export function debounce<TArgs extends any[], TResult>( fn: (...args: TArgs) => TResult, _wait: number, _immediate?: boolean ): (...args: TArgs) => TResult { return (...args: TArgs): TResult => fn(...args); } export function parseYaml(yaml: string): any { const result: Record<string, any> = {}; const lines = yaml.split(/\r?\n/); let index = 0; const parseScalar = (value: string): any => { const trimmed = value.trim(); if (trimmed === "true") return true; if (trimmed === "false") return false; if (/^[-+]?\d+$/.test(trimmed)) return Number(trimmed); if ( (trimmed.startsWith("\"") && trimmed.endsWith("\"")) || (trimmed.startsWith("'") && trimmed.endsWith("'")) ) { return trimmed.slice(1, -1); } return trimmed; }; while (index < lines.length) { const line = lines[index]; if (!line || line.trim() === "") { index += 1; continue; } const keyMatch = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/); if (!keyMatch) { throw new Error(`Invalid YAML line: ${line}`); } const [ , key, inlineValue ] = keyMatch; if (inlineValue !== "") { result[key] = parseScalar(inlineValue); index += 1; continue; } index += 1; const block: string[] = []; while (index < lines.length) { const current = lines[index]; if (!current || current.trim() === "") { block.push(current); index += 1; continue; } if (!current.startsWith(" ")) break; block.push(current); index += 1; } const nonEmpty = block.filter((entry) => entry.trim() !== ""); if (nonEmpty.length === 0) { result[key] = {}; continue; } if (nonEmpty.every((entry) => entry.trimStart().startsWith("- "))) { result[key] = nonEmpty.map((entry) => parseScalar(entry.trimStart().slice(2))); continue; } const nested: Record<string, any> = {}; for (const entry of nonEmpty) { const nestedMatch = entry.trim().match(/^([A-Za-z0-9_-]+):\s*(.*)$/); if (!nestedMatch) { throw new Error(`Invalid YAML nested line: ${entry}`); } const [ , nestedKey, nestedValue ] = nestedMatch; nested[nestedKey] = parseScalar(nestedValue); } result[key] = nested; } return result; } export function stringifyYaml(value: unknown): string { if (!value || typeof value !== "object") return ""; const lines: string[] = []; for (const [ key, val ] of Object.entries(value as Record<string, any>)) { if (Array.isArray(val)) { lines.push(`${key}:`); for (const item of val) { lines.push(` - ${String(item)}`); } continue; } if (val && typeof val === "object") { lines.push(`${key}:`); for (const [ nestedKey, nestedVal ] of Object.entries(val)) { lines.push(` ${nestedKey}: ${String(nestedVal)}`); } continue; } lines.push(`${key}: ${String(val)}`); } return `${lines.join("\n")}\n`; } export function htmlToMarkdown(input: string): string { return input.replace(/<[^>]*>/g, ""); } export function setIcon(node: HTMLElement, iconName: string): void { node.dataset.icon = iconName; }