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
text/typescript
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;
}