@earendil-works/pi-tui 0.84.4 → 0.85.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -4
- package/dist/alt-screen-search.d.ts +21 -1
- package/dist/alt-screen-search.d.ts.map +1 -1
- package/dist/alt-screen-search.js +202 -42
- package/dist/alt-screen-search.js.map +1 -1
- package/dist/components/box.d.ts +3 -1
- package/dist/components/box.d.ts.map +1 -1
- package/dist/components/box.js +29 -0
- package/dist/components/box.js.map +1 -1
- package/dist/components/editor.d.ts +6 -1
- package/dist/components/editor.d.ts.map +1 -1
- package/dist/components/editor.js +98 -18
- package/dist/components/editor.js.map +1 -1
- package/dist/components/input.d.ts +12 -1
- package/dist/components/input.d.ts.map +1 -1
- package/dist/components/input.js +44 -5
- package/dist/components/input.js.map +1 -1
- package/dist/components/loader.d.ts +2 -0
- package/dist/components/loader.d.ts.map +1 -1
- package/dist/components/loader.js +10 -3
- package/dist/components/loader.js.map +1 -1
- package/dist/components/mouse-region.d.ts +12 -0
- package/dist/components/mouse-region.d.ts.map +1 -0
- package/dist/components/mouse-region.js +21 -0
- package/dist/components/mouse-region.js.map +1 -0
- package/dist/components/scroll-view.d.ts +6 -3
- package/dist/components/scroll-view.d.ts.map +1 -1
- package/dist/components/scroll-view.js +8 -2
- package/dist/components/scroll-view.js.map +1 -1
- package/dist/components/select-list.d.ts +4 -1
- package/dist/components/select-list.d.ts.map +1 -1
- package/dist/components/select-list.js +49 -2
- package/dist/components/select-list.js.map +1 -1
- package/dist/components/settings-list.d.ts +5 -1
- package/dist/components/settings-list.d.ts.map +1 -1
- package/dist/components/settings-list.js +55 -5
- package/dist/components/settings-list.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/latex.d.ts.map +1 -1
- package/dist/latex.js +14 -0
- package/dist/latex.js.map +1 -1
- package/dist/layout.d.ts +3 -1
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +32 -9
- package/dist/layout.js.map +1 -1
- package/dist/terminal-image.d.ts.map +1 -1
- package/dist/terminal-image.js +1 -4
- package/dist/terminal-image.js.map +1 -1
- package/dist/terminal.d.ts +6 -0
- package/dist/terminal.d.ts.map +1 -1
- package/dist/terminal.js +17 -4
- package/dist/terminal.js.map +1 -1
- package/dist/tui-alt-screen.d.ts +31 -1
- package/dist/tui-alt-screen.d.ts.map +1 -1
- package/dist/tui-alt-screen.js +342 -41
- package/dist/tui-alt-screen.js.map +1 -1
- package/dist/tui-main-screen.d.ts.map +1 -1
- package/dist/tui-main-screen.js +6 -5
- package/dist/tui-main-screen.js.map +1 -1
- package/dist/tui.d.ts +84 -6
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +123 -9
- package/dist/tui.js.map +1 -1
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +9 -0
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-list.js","sourceRoot":"","sources":["../../src/components/settings-list.ts"],"names":[],"mappings":"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","sourcesContent":["import { fuzzyFilter } from \"../fuzzy.ts\";\nimport { getKeybindings } from \"../keybindings.ts\";\nimport type { Component } from \"../tui.ts\";\nimport { truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"../utils.ts\";\nimport { Input } from \"./input.ts\";\n\nexport interface SettingItem {\n\t/** Unique identifier for this setting */\n\tid: string;\n\t/** Display label (left side) */\n\tlabel: string;\n\t/** Optional description shown when selected */\n\tdescription?: string;\n\t/** Current value to display (right side) */\n\tcurrentValue: string;\n\t/** If provided, Enter/Space cycles through these values */\n\tvalues?: string[];\n\t/** If provided, Enter opens this submenu. Receives current value and done callback.\n\t * done() accepts an optional selectedValue and an optional navigateTo id to move the cursor after close. */\n\tsubmenu?: (\n\t\tcurrentValue: string,\n\t\tdone: (selectedValue?: string, options?: { navigateTo?: string }) => void,\n\t) => Component;\n}\n\nexport interface SettingsListTheme {\n\tlabel: (text: string, selected: boolean) => string;\n\tvalue: (text: string, selected: boolean) => string;\n\tdescription: (text: string) => string;\n\tcursor: string;\n\thint: (text: string) => string;\n}\n\nexport interface SettingsListOptions {\n\tenableSearch?: boolean;\n}\n\nexport class SettingsList implements Component {\n\tprivate items: SettingItem[];\n\tprivate filteredItems: SettingItem[];\n\tprivate theme: SettingsListTheme;\n\tprivate selectedIndex = 0;\n\tprivate maxVisible: number;\n\tprivate onChange: (id: string, newValue: string) => void;\n\tprivate onCancel: () => void;\n\tprivate searchInput?: Input;\n\tprivate searchEnabled: boolean;\n\n\t// Submenu state\n\tprivate submenuComponent: Component | null = null;\n\tprivate submenuItemIndex: number | null = null;\n\tprivate navigateAfterClose: string | null = null;\n\n\tconstructor(\n\t\titems: SettingItem[],\n\t\tmaxVisible: number,\n\t\ttheme: SettingsListTheme,\n\t\tonChange: (id: string, newValue: string) => void,\n\t\tonCancel: () => void,\n\t\toptions: SettingsListOptions = {},\n\t) {\n\t\tthis.items = items;\n\t\tthis.filteredItems = items;\n\t\tthis.maxVisible = maxVisible;\n\t\tthis.theme = theme;\n\t\tthis.onChange = onChange;\n\t\tthis.onCancel = onCancel;\n\t\tthis.searchEnabled = options.enableSearch ?? false;\n\t\tif (this.searchEnabled) {\n\t\t\tthis.searchInput = new Input();\n\t\t}\n\t}\n\n\t/** Update an item's currentValue */\n\tupdateValue(id: string, newValue: string): void {\n\t\tconst item = this.items.find((i) => i.id === id);\n\t\tif (item) {\n\t\t\titem.currentValue = newValue;\n\t\t}\n\t}\n\n\t/** Move selection to the item with the given id (no-op if not found). */\n\tselectItem(id: string): void {\n\t\tconst items = this.searchEnabled ? this.filteredItems : this.items;\n\t\tconst index = items.findIndex((i) => i.id === id);\n\t\tif (index !== -1) {\n\t\t\tthis.selectedIndex = index;\n\t\t}\n\t}\n\n\tinvalidate(): void {\n\t\tthis.submenuComponent?.invalidate?.();\n\t}\n\n\trender(width: number): string[] {\n\t\t// If submenu is active, render it instead\n\t\tif (this.submenuComponent) {\n\t\t\treturn this.submenuComponent.render(width);\n\t\t}\n\n\t\treturn this.renderMainList(width);\n\t}\n\n\tprivate renderMainList(width: number): string[] {\n\t\tconst lines: string[] = [];\n\n\t\tif (this.searchEnabled && this.searchInput) {\n\t\t\tlines.push(...this.searchInput.render(width));\n\t\t\tlines.push(\"\");\n\t\t}\n\n\t\tif (this.items.length === 0) {\n\t\t\tlines.push(this.theme.hint(\" No settings available\"));\n\t\t\tif (this.searchEnabled) {\n\t\t\t\tthis.addHintLine(lines, width);\n\t\t\t}\n\t\t\treturn lines;\n\t\t}\n\n\t\tconst displayItems = this.searchEnabled ? this.filteredItems : this.items;\n\t\tif (displayItems.length === 0) {\n\t\t\tlines.push(truncateToWidth(this.theme.hint(\" No matching settings\"), width));\n\t\t\tthis.addHintLine(lines, width);\n\t\t\treturn lines;\n\t\t}\n\n\t\t// Calculate visible range with scrolling\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), displayItems.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, displayItems.length);\n\n\t\t// Calculate max label width for alignment\n\t\tconst maxLabelWidth = Math.min(36, Math.max(...this.items.map((item) => visibleWidth(item.label))));\n\n\t\t// Render visible items\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = displayItems[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst prefix = isSelected ? this.theme.cursor : \" \";\n\t\t\tconst prefixWidth = visibleWidth(prefix);\n\n\t\t\t// Pad label to align values\n\t\t\tconst labelPadded = item.label + \" \".repeat(Math.max(0, maxLabelWidth - visibleWidth(item.label)));\n\t\t\tconst labelText = this.theme.label(labelPadded, isSelected);\n\n\t\t\t// Calculate space for value\n\t\t\tconst separator = \" \";\n\t\t\tconst usedWidth = prefixWidth + maxLabelWidth + visibleWidth(separator);\n\t\t\tconst valueMaxWidth = width - usedWidth - 2;\n\n\t\t\tconst valueText = this.theme.value(truncateToWidth(item.currentValue, valueMaxWidth, \"\"), isSelected);\n\n\t\t\tlines.push(truncateToWidth(prefix + labelText + separator + valueText, width));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < displayItems.length) {\n\t\t\tconst scrollText = ` (${this.selectedIndex + 1}/${displayItems.length})`;\n\t\t\tlines.push(this.theme.hint(truncateToWidth(scrollText, width - 2, \"\")));\n\t\t}\n\n\t\t// Add description for selected item\n\t\tconst selectedItem = displayItems[this.selectedIndex];\n\t\tif (selectedItem?.description) {\n\t\t\tlines.push(\"\");\n\t\t\tconst wrappedDesc = wrapTextWithAnsi(selectedItem.description, width - 4);\n\t\t\tfor (const line of wrappedDesc) {\n\t\t\t\tlines.push(this.theme.description(` ${line}`));\n\t\t\t}\n\t\t}\n\n\t\t// Add hint\n\t\tthis.addHintLine(lines, width);\n\n\t\treturn lines;\n\t}\n\n\thandleInput(data: string): void {\n\t\t// If submenu is active, delegate all input to it\n\t\t// The submenu's onCancel (triggered by escape) will call done() which closes it\n\t\tif (this.submenuComponent) {\n\t\t\tthis.submenuComponent.handleInput?.(data);\n\t\t\treturn;\n\t\t}\n\n\t\t// Main list input handling\n\t\tconst kb = getKeybindings();\n\t\tconst displayItems = this.searchEnabled ? this.filteredItems : this.items;\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tif (displayItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? displayItems.length - 1 : this.selectedIndex - 1;\n\t\t} else if (kb.matches(data, \"tui.select.down\")) {\n\t\t\tif (displayItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === displayItems.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t} else if (\n\t\t\tkb.matches(data, \"tui.select.confirm\") ||\n\t\t\t(data === \" \" && (!this.searchEnabled || this.searchInput?.getValue().length === 0))\n\t\t) {\n\t\t\tthis.activateItem();\n\t\t} else if (kb.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onCancel();\n\t\t} else if (this.searchEnabled && this.searchInput) {\n\t\t\tthis.searchInput.handleInput(data);\n\t\t\tthis.applyFilter(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate activateItem(): void {\n\t\tconst item = this.searchEnabled ? this.filteredItems[this.selectedIndex] : this.items[this.selectedIndex];\n\t\tif (!item) return;\n\n\t\tif (item.submenu) {\n\t\t\t// Open submenu, passing current value so it can pre-select correctly\n\t\t\tthis.submenuItemIndex = this.selectedIndex;\n\t\t\tthis.submenuComponent = item.submenu(\n\t\t\t\titem.currentValue,\n\t\t\t\t(selectedValue?: string, options?: { navigateTo?: string }) => {\n\t\t\t\t\tif (selectedValue !== undefined) {\n\t\t\t\t\t\titem.currentValue = selectedValue;\n\t\t\t\t\t\tthis.onChange(item.id, selectedValue);\n\t\t\t\t\t}\n\t\t\t\t\tif (options?.navigateTo) {\n\t\t\t\t\t\tthis.navigateAfterClose = options.navigateTo;\n\t\t\t\t\t}\n\t\t\t\t\tthis.closeSubmenu();\n\t\t\t\t},\n\t\t\t);\n\t\t} else if (item.values && item.values.length > 0) {\n\t\t\t// Cycle through values\n\t\t\tconst currentIndex = item.values.indexOf(item.currentValue);\n\t\t\tconst nextIndex = (currentIndex + 1) % item.values.length;\n\t\t\tconst newValue = item.values[nextIndex];\n\t\t\titem.currentValue = newValue;\n\t\t\tthis.onChange(item.id, newValue);\n\t\t}\n\t}\n\n\tprivate closeSubmenu(): void {\n\t\tthis.submenuComponent = null;\n\t\tif (this.navigateAfterClose !== null) {\n\t\t\tconst id = this.navigateAfterClose;\n\t\t\tthis.navigateAfterClose = null;\n\t\t\tthis.submenuItemIndex = null;\n\t\t\tthis.selectItem(id);\n\t\t\t// Open the target item's submenu automatically\n\t\t\tthis.activateItem();\n\t\t} else if (this.submenuItemIndex !== null) {\n\t\t\t// Restore selection to the item that opened the submenu\n\t\t\tthis.selectedIndex = this.submenuItemIndex;\n\t\t\tthis.submenuItemIndex = null;\n\t\t}\n\t}\n\n\tprivate applyFilter(query: string): void {\n\t\tthis.filteredItems = fuzzyFilter(this.items, query, (item) => item.label);\n\t\tthis.selectedIndex = 0;\n\t}\n\n\tprivate addHintLine(lines: string[], width: number): void {\n\t\tlines.push(\"\");\n\t\tlines.push(\n\t\t\ttruncateToWidth(\n\t\t\t\tthis.theme.hint(\n\t\t\t\t\tthis.searchEnabled\n\t\t\t\t\t\t? \" Type to search · Enter/Space to change · Esc to cancel\"\n\t\t\t\t\t\t: \" Enter/Space to change · Esc to cancel\",\n\t\t\t\t),\n\t\t\t\twidth,\n\t\t\t),\n\t\t);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"settings-list.js","sourceRoot":"","sources":["../../src/components/settings-list.ts"],"names":[],"mappings":"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","sourcesContent":["import { fuzzyFilter } from \"../fuzzy.ts\";\nimport { getKeybindings } from \"../keybindings.ts\";\nimport type { Component, TuiMouseEvent, TuiMouseEventResult } from \"../tui.ts\";\nimport { truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"../utils.ts\";\nimport { Input } from \"./input.ts\";\n\nexport interface SettingItem {\n\t/** Unique identifier for this setting */\n\tid: string;\n\t/** Display label (left side) */\n\tlabel: string;\n\t/** Optional description shown when selected */\n\tdescription?: string;\n\t/** Current value to display (right side) */\n\tcurrentValue: string;\n\t/** If provided, Enter/Space cycles through these values */\n\tvalues?: string[];\n\t/** If provided, Enter opens this submenu. Receives current value and done callback.\n\t * done() accepts an optional selectedValue and an optional navigateTo id to move the cursor after close. */\n\tsubmenu?: (\n\t\tcurrentValue: string,\n\t\tdone: (selectedValue?: string, options?: { navigateTo?: string }) => void,\n\t) => Component;\n}\n\nexport interface SettingsListTheme {\n\tlabel: (text: string, selected: boolean) => string;\n\tvalue: (text: string, selected: boolean) => string;\n\tdescription: (text: string) => string;\n\tcursor: string;\n\thint: (text: string) => string;\n}\n\nexport interface SettingsListOptions {\n\tenableSearch?: boolean;\n}\n\nexport class SettingsList implements Component {\n\tprivate items: SettingItem[];\n\tprivate filteredItems: SettingItem[];\n\tprivate theme: SettingsListTheme;\n\tprivate selectedIndex = 0;\n\tprivate mousePressedIndex: number | undefined;\n\tprivate maxVisible: number;\n\tprivate onChange: (id: string, newValue: string) => void;\n\tprivate onCancel: () => void;\n\tprivate searchInput?: Input;\n\tprivate searchEnabled: boolean;\n\n\t// Submenu state\n\tprivate submenuComponent: Component | null = null;\n\tprivate submenuItemIndex: number | null = null;\n\tprivate navigateAfterClose: string | null = null;\n\n\tconstructor(\n\t\titems: SettingItem[],\n\t\tmaxVisible: number,\n\t\ttheme: SettingsListTheme,\n\t\tonChange: (id: string, newValue: string) => void,\n\t\tonCancel: () => void,\n\t\toptions: SettingsListOptions = {},\n\t) {\n\t\tthis.items = items;\n\t\tthis.filteredItems = items;\n\t\tthis.maxVisible = maxVisible;\n\t\tthis.theme = theme;\n\t\tthis.onChange = onChange;\n\t\tthis.onCancel = onCancel;\n\t\tthis.searchEnabled = options.enableSearch ?? false;\n\t\tif (this.searchEnabled) {\n\t\t\tthis.searchInput = new Input();\n\t\t}\n\t}\n\n\t/** Update an item's currentValue */\n\tupdateValue(id: string, newValue: string): void {\n\t\tconst item = this.items.find((i) => i.id === id);\n\t\tif (item) {\n\t\t\titem.currentValue = newValue;\n\t\t}\n\t}\n\n\t/** Move selection to the item with the given id (no-op if not found). */\n\tselectItem(id: string): void {\n\t\tconst items = this.searchEnabled ? this.filteredItems : this.items;\n\t\tconst index = items.findIndex((i) => i.id === id);\n\t\tif (index !== -1) {\n\t\t\tthis.selectedIndex = index;\n\t\t}\n\t}\n\n\tinvalidate(): void {\n\t\tthis.submenuComponent?.invalidate?.();\n\t}\n\n\trender(width: number): string[] {\n\t\t// If submenu is active, render it instead\n\t\tif (this.submenuComponent) {\n\t\t\treturn this.submenuComponent.render(width);\n\t\t}\n\n\t\treturn this.renderMainList(width);\n\t}\n\n\tprivate renderMainList(width: number): string[] {\n\t\tconst lines: string[] = [];\n\n\t\tif (this.searchEnabled && this.searchInput) {\n\t\t\tlines.push(...this.searchInput.render(width));\n\t\t\tlines.push(\"\");\n\t\t}\n\n\t\tif (this.items.length === 0) {\n\t\t\tlines.push(this.theme.hint(\" No settings available\"));\n\t\t\tif (this.searchEnabled) {\n\t\t\t\tthis.addHintLine(lines, width);\n\t\t\t}\n\t\t\treturn lines;\n\t\t}\n\n\t\tconst displayItems = this.getDisplayItems();\n\t\tif (displayItems.length === 0) {\n\t\t\tlines.push(truncateToWidth(this.theme.hint(\" No matching settings\"), width));\n\t\t\tthis.addHintLine(lines, width);\n\t\t\treturn lines;\n\t\t}\n\n\t\t// Calculate visible range with scrolling\n\t\tconst { startIndex, endIndex } = this.getVisibleRange(displayItems);\n\n\t\t// Calculate max label width for alignment\n\t\tconst maxLabelWidth = Math.min(36, Math.max(...this.items.map((item) => visibleWidth(item.label))));\n\n\t\t// Render visible items\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = displayItems[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst prefix = isSelected ? this.theme.cursor : \" \";\n\t\t\tconst prefixWidth = visibleWidth(prefix);\n\n\t\t\t// Pad label to align values\n\t\t\tconst labelPadded = item.label + \" \".repeat(Math.max(0, maxLabelWidth - visibleWidth(item.label)));\n\t\t\tconst labelText = this.theme.label(labelPadded, isSelected);\n\n\t\t\t// Calculate space for value\n\t\t\tconst separator = \" \";\n\t\t\tconst usedWidth = prefixWidth + maxLabelWidth + visibleWidth(separator);\n\t\t\tconst valueMaxWidth = width - usedWidth - 2;\n\n\t\t\tconst valueText = this.theme.value(truncateToWidth(item.currentValue, valueMaxWidth, \"\"), isSelected);\n\n\t\t\tlines.push(truncateToWidth(prefix + labelText + separator + valueText, width));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < displayItems.length) {\n\t\t\tconst scrollText = ` (${this.selectedIndex + 1}/${displayItems.length})`;\n\t\t\tlines.push(this.theme.hint(truncateToWidth(scrollText, width - 2, \"\")));\n\t\t}\n\n\t\t// Add description for selected item\n\t\tconst selectedItem = displayItems[this.selectedIndex];\n\t\tif (selectedItem?.description) {\n\t\t\tlines.push(\"\");\n\t\t\tconst wrappedDesc = wrapTextWithAnsi(selectedItem.description, width - 4);\n\t\t\tfor (const line of wrappedDesc) {\n\t\t\t\tlines.push(this.theme.description(` ${line}`));\n\t\t\t}\n\t\t}\n\n\t\t// Add hint\n\t\tthis.addHintLine(lines, width);\n\n\t\treturn lines;\n\t}\n\n\thandleMouse(event: TuiMouseEvent): TuiMouseEventResult | undefined {\n\t\tif (this.submenuComponent) {\n\t\t\tconst result = this.submenuComponent.handleMouse?.(event);\n\t\t\treturn result ? { ...result, focus: true } : undefined;\n\t\t}\n\n\t\tif (this.searchEnabled && this.searchInput) {\n\t\t\tif (event.y === 0) {\n\t\t\t\tconst result = this.searchInput.handleMouse?.(event);\n\t\t\t\treturn result ? { ...result, focus: true } : undefined;\n\t\t\t}\n\t\t\tif (event.y === 1) return undefined;\n\t\t}\n\n\t\tconst displayItems = this.getDisplayItems();\n\t\tif (displayItems.length === 0) return undefined;\n\t\tif (event.type === \"wheel\" && event.wheelDelta) {\n\t\t\tconst delta = event.wheelDelta < 0 ? -1 : 1;\n\t\t\tconst previousIndex = this.selectedIndex;\n\t\t\tthis.selectedIndex = Math.max(0, Math.min(displayItems.length - 1, this.selectedIndex + delta));\n\t\t\treturn { handled: true, render: this.selectedIndex !== previousIndex };\n\t\t}\n\t\t// Hover must not change selection: the visible range is centered on it.\n\t\tif (event.button !== \"left\" || (event.type !== \"press\" && event.type !== \"click\")) return undefined;\n\n\t\tconst rowOffset = this.searchEnabled ? 2 : 0;\n\t\tconst { startIndex, endIndex } = this.getVisibleRange(displayItems);\n\t\tconst itemIndex = startIndex + event.y - rowOffset;\n\t\tif (itemIndex < startIndex || itemIndex >= endIndex) return undefined;\n\t\tif (event.type === \"press\") {\n\t\t\tthis.mousePressedIndex = itemIndex;\n\t\t\tthis.selectedIndex = itemIndex;\n\t\t\treturn { handled: true, focus: true };\n\t\t}\n\t\tif (event.type === \"click\") {\n\t\t\tthis.selectedIndex = this.mousePressedIndex ?? itemIndex;\n\t\t\tthis.mousePressedIndex = undefined;\n\t\t\tthis.activateItem();\n\t\t\treturn { handled: true };\n\t\t}\n\t\treturn undefined;\n\t}\n\n\thandleInput(data: string): void {\n\t\t// If submenu is active, delegate all input to it\n\t\t// The submenu's onCancel (triggered by escape) will call done() which closes it\n\t\tif (this.submenuComponent) {\n\t\t\tthis.submenuComponent.handleInput?.(data);\n\t\t\treturn;\n\t\t}\n\n\t\t// Main list input handling\n\t\tconst kb = getKeybindings();\n\t\tconst displayItems = this.getDisplayItems();\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tif (displayItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? displayItems.length - 1 : this.selectedIndex - 1;\n\t\t} else if (kb.matches(data, \"tui.select.down\")) {\n\t\t\tif (displayItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === displayItems.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t} else if (\n\t\t\tkb.matches(data, \"tui.select.confirm\") ||\n\t\t\t(data === \" \" && (!this.searchEnabled || this.searchInput?.getValue().length === 0))\n\t\t) {\n\t\t\tthis.activateItem();\n\t\t} else if (kb.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onCancel();\n\t\t} else if (this.searchEnabled && this.searchInput) {\n\t\t\tthis.searchInput.handleInput(data);\n\t\t\tthis.applyFilter(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate getDisplayItems(): SettingItem[] {\n\t\treturn this.searchEnabled ? this.filteredItems : this.items;\n\t}\n\n\tprivate getVisibleRange(displayItems: readonly SettingItem[]): { startIndex: number; endIndex: number } {\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), displayItems.length - this.maxVisible),\n\t\t);\n\t\treturn { startIndex, endIndex: Math.min(startIndex + this.maxVisible, displayItems.length) };\n\t}\n\n\tprivate activateItem(): void {\n\t\tconst item = this.getDisplayItems()[this.selectedIndex];\n\t\tif (!item) return;\n\n\t\tif (item.submenu) {\n\t\t\t// Open submenu, passing current value so it can pre-select correctly\n\t\t\tthis.submenuItemIndex = this.selectedIndex;\n\t\t\tthis.submenuComponent = item.submenu(\n\t\t\t\titem.currentValue,\n\t\t\t\t(selectedValue?: string, options?: { navigateTo?: string }) => {\n\t\t\t\t\tif (selectedValue !== undefined) {\n\t\t\t\t\t\titem.currentValue = selectedValue;\n\t\t\t\t\t\tthis.onChange(item.id, selectedValue);\n\t\t\t\t\t}\n\t\t\t\t\tif (options?.navigateTo) {\n\t\t\t\t\t\tthis.navigateAfterClose = options.navigateTo;\n\t\t\t\t\t}\n\t\t\t\t\tthis.closeSubmenu();\n\t\t\t\t},\n\t\t\t);\n\t\t} else if (item.values && item.values.length > 0) {\n\t\t\t// Cycle through values\n\t\t\tconst currentIndex = item.values.indexOf(item.currentValue);\n\t\t\tconst nextIndex = (currentIndex + 1) % item.values.length;\n\t\t\tconst newValue = item.values[nextIndex];\n\t\t\titem.currentValue = newValue;\n\t\t\tthis.onChange(item.id, newValue);\n\t\t}\n\t}\n\n\tprivate closeSubmenu(): void {\n\t\tthis.submenuComponent = null;\n\t\tif (this.navigateAfterClose !== null) {\n\t\t\tconst id = this.navigateAfterClose;\n\t\t\tthis.navigateAfterClose = null;\n\t\t\tthis.submenuItemIndex = null;\n\t\t\tthis.selectItem(id);\n\t\t\t// Open the target item's submenu automatically\n\t\t\tthis.activateItem();\n\t\t} else if (this.submenuItemIndex !== null) {\n\t\t\t// Restore selection to the item that opened the submenu\n\t\t\tthis.selectedIndex = this.submenuItemIndex;\n\t\t\tthis.submenuItemIndex = null;\n\t\t}\n\t}\n\n\tprivate applyFilter(query: string): void {\n\t\tthis.filteredItems = fuzzyFilter(this.items, query, (item) => item.label);\n\t\tthis.selectedIndex = 0;\n\t}\n\n\tprivate addHintLine(lines: string[], width: number): void {\n\t\tlines.push(\"\");\n\t\tlines.push(\n\t\t\ttruncateToWidth(\n\t\t\t\tthis.theme.hint(\n\t\t\t\t\tthis.searchEnabled\n\t\t\t\t\t\t? \" Type to search · Enter/Space to change · Esc to cancel\"\n\t\t\t\t\t\t: \" Enter/Space to change · Esc to cancel\",\n\t\t\t\t),\n\t\t\t\twidth,\n\t\t\t),\n\t\t);\n\t}\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export { Image, type ImageOptions, type ImageTheme } from "./components/image.ts
|
|
|
8
8
|
export { Input } from "./components/input.ts";
|
|
9
9
|
export { Loader, type LoaderIndicatorOptions } from "./components/loader.ts";
|
|
10
10
|
export { type DefaultTextStyle, Markdown, type MarkdownOptions, type MarkdownTheme } from "./components/markdown.ts";
|
|
11
|
+
export { MouseRegion, type MouseRegionHandler } from "./components/mouse-region.ts";
|
|
11
12
|
export { ScrollView, type ScrollViewOptions, type ScrollViewScrollbar, type ScrollViewScrollToOptions, } from "./components/scroll-view.ts";
|
|
12
13
|
export { type SelectItem, SelectList, type SelectListLayoutOptions, type SelectListTheme, type SelectListTruncatePrimaryContext, } from "./components/select-list.ts";
|
|
13
14
|
export { type SettingItem, SettingsList, type SettingsListTheme } from "./components/settings-list.ts";
|
|
@@ -24,7 +25,7 @@ export { StdinBuffer, type StdinBufferEventMap, type StdinBufferOptions } from "
|
|
|
24
25
|
export { ProcessTerminal, type Terminal } from "./terminal.ts";
|
|
25
26
|
export { parseOsc11BackgroundColor, parseTerminalColorSchemeReport, type RgbColor, type TerminalColorScheme, } from "./terminal-colors.ts";
|
|
26
27
|
export { allocateImageId, type CellDimensions, calculateImageRows, deleteAllKittyImages, deleteKittyImage, detectCapabilities, encodeITerm2, encodeKitty, getCapabilities, getCellDimensions, getGifDimensions, getImageDimensions, getJpegDimensions, getPngDimensions, getWebpDimensions, hyperlink, type ImageDimensions, type ImageProtocol, type ImageRenderOptions, imageFallback, renderImage, resetCapabilitiesCache, setCapabilities, setCapabilityOverrides, setCellDimensions, type TerminalCapabilities, } from "./terminal-image.ts";
|
|
27
|
-
export { type Component, Container, CURSOR_MARKER, compositeTuiLine, type Focusable, isFocusable, isViewportTUI, type OverlayAnchor, type OverlayHandle, type OverlayMargin, type OverlayOptions, type OverlayUnfocusOptions, type SizeValue, type TUI, type TuiInputListener, type TuiInputListenerResult, type TuiMode, type TuiStopOptions, type ViewportTUI, } from "./tui.ts";
|
|
28
|
+
export { type Component, Container, CURSOR_MARKER, compositeTuiLine, type Focusable, isFocusable, isViewportTUI, type OverlayAnchor, type OverlayBounds, type OverlayHandle, type OverlayMargin, type OverlayOptions, type OverlayUnfocusOptions, type SizeValue, type TUI, type TuiInputListener, type TuiInputListenerResult, type TuiMode, type TuiMouseButton, type TuiMouseEvent, type TuiMouseEventResult, type TuiMouseEventType, type TuiStopOptions, type ViewportTUI, } from "./tui.ts";
|
|
28
29
|
export { TuiAltScreen, type TuiAltScreenOptions } from "./tui-alt-screen.ts";
|
|
29
30
|
export { TuiMainScreen, type TuiMainScreenRenderState } from "./tui-main-screen.ts";
|
|
30
31
|
export { getOsc8LinkAtColumn, sliceByColumn, stripTerminalSequences, truncateToWidth, visibleWidth, wrapTextWithAnsi, } from "./utils.ts";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// Core TUI interfaces and classes\n\nexport { Marked, type Token, type Tokens } from \"marked\";\n// Autocomplete support\nexport {\n\ttype AutocompleteItem,\n\ttype AutocompleteProvider,\n\ttype AutocompleteSuggestions,\n\tCombinedAutocompleteProvider,\n\ttype SlashCommand,\n} from \"./autocomplete.ts\";\n// Components\nexport { Box } from \"./components/box.ts\";\nexport { CancellableLoader } from \"./components/cancellable-loader.ts\";\nexport { Editor, type EditorOptions, type EditorTheme } from \"./components/editor.ts\";\nexport { HStack } from \"./components/h-stack.ts\";\nexport { Image, type ImageOptions, type ImageTheme } from \"./components/image.ts\";\nexport { Input } from \"./components/input.ts\";\nexport { Loader, type LoaderIndicatorOptions } from \"./components/loader.ts\";\nexport { type DefaultTextStyle, Markdown, type MarkdownOptions, type MarkdownTheme } from \"./components/markdown.ts\";\nexport {\n\tScrollView,\n\ttype ScrollViewOptions,\n\ttype ScrollViewScrollbar,\n\ttype ScrollViewScrollToOptions,\n} from \"./components/scroll-view.ts\";\nexport {\n\ttype SelectItem,\n\tSelectList,\n\ttype SelectListLayoutOptions,\n\ttype SelectListTheme,\n\ttype SelectListTruncatePrimaryContext,\n} from \"./components/select-list.ts\";\nexport { type SettingItem, SettingsList, type SettingsListTheme } from \"./components/settings-list.ts\";\nexport { Spacer } from \"./components/spacer.ts\";\nexport { Text } from \"./components/text.ts\";\nexport { TruncatedText } from \"./components/truncated-text.ts\";\nexport {\n\ttype StackChild,\n\ttype StackEntry,\n\ttype StackEntryOptions,\n\ttype StackOptions,\n\tVStack,\n} from \"./components/v-stack.ts\";\n// Editor component interface (for custom editors)\nexport type { EditorComponent } from \"./editor-component.ts\";\n// Fuzzy matching\nexport { type FuzzyMatch, fuzzyFilter, fuzzyMatch } from \"./fuzzy.ts\";\n// Keybindings\nexport {\n\tgetKeybindings,\n\ttype Keybinding,\n\ttype KeybindingConflict,\n\ttype KeybindingDefinition,\n\ttype KeybindingDefinitions,\n\ttype Keybindings,\n\ttype KeybindingsConfig,\n\tKeybindingsManager,\n\tsetKeybindings,\n\tTUI_KEYBINDINGS,\n} from \"./keybindings.ts\";\n// Keyboard input handling\nexport {\n\tdecodeKittyPrintable,\n\tisKeyRelease,\n\tisKeyRepeat,\n\tisKittyProtocolActive,\n\tKey,\n\ttype KeyEventType,\n\ttype KeyId,\n\tmatchesKey,\n\tparseKey,\n\tsetKittyProtocolActive,\n} from \"./keys.ts\";\n// LaTeX rendering\nexport { type RenderLatexOptions, renderLatex } from \"./latex.ts\";\n// Input buffering for batch splitting\nexport { StdinBuffer, type StdinBufferEventMap, type StdinBufferOptions } from \"./stdin-buffer.ts\";\n// Terminal interface and implementations\nexport { ProcessTerminal, type Terminal } from \"./terminal.ts\";\n// Terminal colors\nexport {\n\tparseOsc11BackgroundColor,\n\tparseTerminalColorSchemeReport,\n\ttype RgbColor,\n\ttype TerminalColorScheme,\n} from \"./terminal-colors.ts\";\n// Terminal image support\nexport {\n\tallocateImageId,\n\ttype CellDimensions,\n\tcalculateImageRows,\n\tdeleteAllKittyImages,\n\tdeleteKittyImage,\n\tdetectCapabilities,\n\tencodeITerm2,\n\tencodeKitty,\n\tgetCapabilities,\n\tgetCellDimensions,\n\tgetGifDimensions,\n\tgetImageDimensions,\n\tgetJpegDimensions,\n\tgetPngDimensions,\n\tgetWebpDimensions,\n\thyperlink,\n\ttype ImageDimensions,\n\ttype ImageProtocol,\n\ttype ImageRenderOptions,\n\timageFallback,\n\trenderImage,\n\tresetCapabilitiesCache,\n\tsetCapabilities,\n\tsetCapabilityOverrides,\n\tsetCellDimensions,\n\ttype TerminalCapabilities,\n} from \"./terminal-image.ts\";\nexport {\n\ttype Component,\n\tContainer,\n\tCURSOR_MARKER,\n\tcompositeTuiLine,\n\ttype Focusable,\n\tisFocusable,\n\tisViewportTUI,\n\ttype OverlayAnchor,\n\ttype OverlayHandle,\n\ttype OverlayMargin,\n\ttype OverlayOptions,\n\ttype OverlayUnfocusOptions,\n\ttype SizeValue,\n\ttype TUI,\n\ttype TuiInputListener,\n\ttype TuiInputListenerResult,\n\ttype TuiMode,\n\ttype TuiStopOptions,\n\ttype ViewportTUI,\n} from \"./tui.ts\";\nexport { TuiAltScreen, type TuiAltScreenOptions } from \"./tui-alt-screen.ts\";\nexport { TuiMainScreen, type TuiMainScreenRenderState } from \"./tui-main-screen.ts\";\n// Utilities\nexport {\n\tgetOsc8LinkAtColumn,\n\tsliceByColumn,\n\tstripTerminalSequences,\n\ttruncateToWidth,\n\tvisibleWidth,\n\twrapTextWithAnsi,\n} from \"./utils.ts\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// Core TUI interfaces and classes\n\nexport { Marked, type Token, type Tokens } from \"marked\";\n// Autocomplete support\nexport {\n\ttype AutocompleteItem,\n\ttype AutocompleteProvider,\n\ttype AutocompleteSuggestions,\n\tCombinedAutocompleteProvider,\n\ttype SlashCommand,\n} from \"./autocomplete.ts\";\n// Components\nexport { Box } from \"./components/box.ts\";\nexport { CancellableLoader } from \"./components/cancellable-loader.ts\";\nexport { Editor, type EditorOptions, type EditorTheme } from \"./components/editor.ts\";\nexport { HStack } from \"./components/h-stack.ts\";\nexport { Image, type ImageOptions, type ImageTheme } from \"./components/image.ts\";\nexport { Input } from \"./components/input.ts\";\nexport { Loader, type LoaderIndicatorOptions } from \"./components/loader.ts\";\nexport { type DefaultTextStyle, Markdown, type MarkdownOptions, type MarkdownTheme } from \"./components/markdown.ts\";\nexport { MouseRegion, type MouseRegionHandler } from \"./components/mouse-region.ts\";\nexport {\n\tScrollView,\n\ttype ScrollViewOptions,\n\ttype ScrollViewScrollbar,\n\ttype ScrollViewScrollToOptions,\n} from \"./components/scroll-view.ts\";\nexport {\n\ttype SelectItem,\n\tSelectList,\n\ttype SelectListLayoutOptions,\n\ttype SelectListTheme,\n\ttype SelectListTruncatePrimaryContext,\n} from \"./components/select-list.ts\";\nexport { type SettingItem, SettingsList, type SettingsListTheme } from \"./components/settings-list.ts\";\nexport { Spacer } from \"./components/spacer.ts\";\nexport { Text } from \"./components/text.ts\";\nexport { TruncatedText } from \"./components/truncated-text.ts\";\nexport {\n\ttype StackChild,\n\ttype StackEntry,\n\ttype StackEntryOptions,\n\ttype StackOptions,\n\tVStack,\n} from \"./components/v-stack.ts\";\n// Editor component interface (for custom editors)\nexport type { EditorComponent } from \"./editor-component.ts\";\n// Fuzzy matching\nexport { type FuzzyMatch, fuzzyFilter, fuzzyMatch } from \"./fuzzy.ts\";\n// Keybindings\nexport {\n\tgetKeybindings,\n\ttype Keybinding,\n\ttype KeybindingConflict,\n\ttype KeybindingDefinition,\n\ttype KeybindingDefinitions,\n\ttype Keybindings,\n\ttype KeybindingsConfig,\n\tKeybindingsManager,\n\tsetKeybindings,\n\tTUI_KEYBINDINGS,\n} from \"./keybindings.ts\";\n// Keyboard input handling\nexport {\n\tdecodeKittyPrintable,\n\tisKeyRelease,\n\tisKeyRepeat,\n\tisKittyProtocolActive,\n\tKey,\n\ttype KeyEventType,\n\ttype KeyId,\n\tmatchesKey,\n\tparseKey,\n\tsetKittyProtocolActive,\n} from \"./keys.ts\";\n// LaTeX rendering\nexport { type RenderLatexOptions, renderLatex } from \"./latex.ts\";\n// Input buffering for batch splitting\nexport { StdinBuffer, type StdinBufferEventMap, type StdinBufferOptions } from \"./stdin-buffer.ts\";\n// Terminal interface and implementations\nexport { ProcessTerminal, type Terminal } from \"./terminal.ts\";\n// Terminal colors\nexport {\n\tparseOsc11BackgroundColor,\n\tparseTerminalColorSchemeReport,\n\ttype RgbColor,\n\ttype TerminalColorScheme,\n} from \"./terminal-colors.ts\";\n// Terminal image support\nexport {\n\tallocateImageId,\n\ttype CellDimensions,\n\tcalculateImageRows,\n\tdeleteAllKittyImages,\n\tdeleteKittyImage,\n\tdetectCapabilities,\n\tencodeITerm2,\n\tencodeKitty,\n\tgetCapabilities,\n\tgetCellDimensions,\n\tgetGifDimensions,\n\tgetImageDimensions,\n\tgetJpegDimensions,\n\tgetPngDimensions,\n\tgetWebpDimensions,\n\thyperlink,\n\ttype ImageDimensions,\n\ttype ImageProtocol,\n\ttype ImageRenderOptions,\n\timageFallback,\n\trenderImage,\n\tresetCapabilitiesCache,\n\tsetCapabilities,\n\tsetCapabilityOverrides,\n\tsetCellDimensions,\n\ttype TerminalCapabilities,\n} from \"./terminal-image.ts\";\nexport {\n\ttype Component,\n\tContainer,\n\tCURSOR_MARKER,\n\tcompositeTuiLine,\n\ttype Focusable,\n\tisFocusable,\n\tisViewportTUI,\n\ttype OverlayAnchor,\n\ttype OverlayBounds,\n\ttype OverlayHandle,\n\ttype OverlayMargin,\n\ttype OverlayOptions,\n\ttype OverlayUnfocusOptions,\n\ttype SizeValue,\n\ttype TUI,\n\ttype TuiInputListener,\n\ttype TuiInputListenerResult,\n\ttype TuiMode,\n\ttype TuiMouseButton,\n\ttype TuiMouseEvent,\n\ttype TuiMouseEventResult,\n\ttype TuiMouseEventType,\n\ttype TuiStopOptions,\n\ttype ViewportTUI,\n} from \"./tui.ts\";\nexport { TuiAltScreen, type TuiAltScreenOptions } from \"./tui-alt-screen.ts\";\nexport { TuiMainScreen, type TuiMainScreenRenderState } from \"./tui-main-screen.ts\";\n// Utilities\nexport {\n\tgetOsc8LinkAtColumn,\n\tsliceByColumn,\n\tstripTerminalSequences,\n\ttruncateToWidth,\n\tvisibleWidth,\n\twrapTextWithAnsi,\n} from \"./utils.ts\";\n"]}
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ export { Image } from "./components/image.js";
|
|
|
11
11
|
export { Input } from "./components/input.js";
|
|
12
12
|
export { Loader } from "./components/loader.js";
|
|
13
13
|
export { Markdown } from "./components/markdown.js";
|
|
14
|
+
export { MouseRegion } from "./components/mouse-region.js";
|
|
14
15
|
export { ScrollView, } from "./components/scroll-view.js";
|
|
15
16
|
export { SelectList, } from "./components/select-list.js";
|
|
16
17
|
export { SettingsList } from "./components/settings-list.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAElC,OAAO,EAAE,MAAM,EAA2B,MAAM,QAAQ,CAAC;AACzD,uBAAuB;AACvB,OAAO,EAIN,4BAA4B,GAE5B,MAAM,mBAAmB,CAAC;AAC3B,aAAa;AACb,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,MAAM,EAAwC,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAsC,MAAM,uBAAuB,CAAC;AAClF,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAA+B,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAyB,QAAQ,EAA4C,MAAM,0BAA0B,CAAC;AACrH,OAAO,EACN,UAAU,GAIV,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAEN,UAAU,GAIV,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAoB,YAAY,EAA0B,MAAM,+BAA+B,CAAC;AACvG,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAKN,MAAM,GACN,MAAM,yBAAyB,CAAC;AAGjC,iBAAiB;AACjB,OAAO,EAAmB,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtE,cAAc;AACd,OAAO,EACN,cAAc,EAOd,kBAAkB,EAClB,cAAc,EACd,eAAe,GACf,MAAM,kBAAkB,CAAC;AAC1B,0BAA0B;AAC1B,OAAO,EACN,oBAAoB,EACpB,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,GAAG,EAGH,UAAU,EACV,QAAQ,EACR,sBAAsB,GACtB,MAAM,WAAW,CAAC;AACnB,kBAAkB;AAClB,OAAO,EAA2B,WAAW,EAAE,MAAM,YAAY,CAAC;AAClE,sCAAsC;AACtC,OAAO,EAAE,WAAW,EAAqD,MAAM,mBAAmB,CAAC;AACnG,yCAAyC;AACzC,OAAO,EAAE,eAAe,EAAiB,MAAM,eAAe,CAAC;AAC/D,kBAAkB;AAClB,OAAO,EACN,yBAAyB,EACzB,8BAA8B,GAG9B,MAAM,sBAAsB,CAAC;AAC9B,yBAAyB;AACzB,OAAO,EACN,eAAe,EAEf,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,SAAS,EAIT,aAAa,EACb,WAAW,EACX,sBAAsB,EACtB,eAAe,EACf,sBAAsB,EACtB,iBAAiB,GAEjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEN,SAAS,EACT,aAAa,EACb,gBAAgB,EAEhB,WAAW,EACX,aAAa,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAElC,OAAO,EAAE,MAAM,EAA2B,MAAM,QAAQ,CAAC;AACzD,uBAAuB;AACvB,OAAO,EAIN,4BAA4B,GAE5B,MAAM,mBAAmB,CAAC;AAC3B,aAAa;AACb,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,MAAM,EAAwC,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAsC,MAAM,uBAAuB,CAAC;AAClF,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAA+B,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAyB,QAAQ,EAA4C,MAAM,0BAA0B,CAAC;AACrH,OAAO,EAAE,WAAW,EAA2B,MAAM,8BAA8B,CAAC;AACpF,OAAO,EACN,UAAU,GAIV,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAEN,UAAU,GAIV,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAoB,YAAY,EAA0B,MAAM,+BAA+B,CAAC;AACvG,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAKN,MAAM,GACN,MAAM,yBAAyB,CAAC;AAGjC,iBAAiB;AACjB,OAAO,EAAmB,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtE,cAAc;AACd,OAAO,EACN,cAAc,EAOd,kBAAkB,EAClB,cAAc,EACd,eAAe,GACf,MAAM,kBAAkB,CAAC;AAC1B,0BAA0B;AAC1B,OAAO,EACN,oBAAoB,EACpB,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,GAAG,EAGH,UAAU,EACV,QAAQ,EACR,sBAAsB,GACtB,MAAM,WAAW,CAAC;AACnB,kBAAkB;AAClB,OAAO,EAA2B,WAAW,EAAE,MAAM,YAAY,CAAC;AAClE,sCAAsC;AACtC,OAAO,EAAE,WAAW,EAAqD,MAAM,mBAAmB,CAAC;AACnG,yCAAyC;AACzC,OAAO,EAAE,eAAe,EAAiB,MAAM,eAAe,CAAC;AAC/D,kBAAkB;AAClB,OAAO,EACN,yBAAyB,EACzB,8BAA8B,GAG9B,MAAM,sBAAsB,CAAC;AAC9B,yBAAyB;AACzB,OAAO,EACN,eAAe,EAEf,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,SAAS,EAIT,aAAa,EACb,WAAW,EACX,sBAAsB,EACtB,eAAe,EACf,sBAAsB,EACtB,iBAAiB,GAEjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEN,SAAS,EACT,aAAa,EACb,gBAAgB,EAEhB,WAAW,EACX,aAAa,GAkBb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,YAAY,EAA4B,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAiC,MAAM,sBAAsB,CAAC;AACpF,YAAY;AACZ,OAAO,EACN,mBAAmB,EACnB,aAAa,EACb,sBAAsB,EACtB,eAAe,EACf,YAAY,EACZ,gBAAgB,GAChB,MAAM,YAAY,CAAC","sourcesContent":["// Core TUI interfaces and classes\n\nexport { Marked, type Token, type Tokens } from \"marked\";\n// Autocomplete support\nexport {\n\ttype AutocompleteItem,\n\ttype AutocompleteProvider,\n\ttype AutocompleteSuggestions,\n\tCombinedAutocompleteProvider,\n\ttype SlashCommand,\n} from \"./autocomplete.ts\";\n// Components\nexport { Box } from \"./components/box.ts\";\nexport { CancellableLoader } from \"./components/cancellable-loader.ts\";\nexport { Editor, type EditorOptions, type EditorTheme } from \"./components/editor.ts\";\nexport { HStack } from \"./components/h-stack.ts\";\nexport { Image, type ImageOptions, type ImageTheme } from \"./components/image.ts\";\nexport { Input } from \"./components/input.ts\";\nexport { Loader, type LoaderIndicatorOptions } from \"./components/loader.ts\";\nexport { type DefaultTextStyle, Markdown, type MarkdownOptions, type MarkdownTheme } from \"./components/markdown.ts\";\nexport { MouseRegion, type MouseRegionHandler } from \"./components/mouse-region.ts\";\nexport {\n\tScrollView,\n\ttype ScrollViewOptions,\n\ttype ScrollViewScrollbar,\n\ttype ScrollViewScrollToOptions,\n} from \"./components/scroll-view.ts\";\nexport {\n\ttype SelectItem,\n\tSelectList,\n\ttype SelectListLayoutOptions,\n\ttype SelectListTheme,\n\ttype SelectListTruncatePrimaryContext,\n} from \"./components/select-list.ts\";\nexport { type SettingItem, SettingsList, type SettingsListTheme } from \"./components/settings-list.ts\";\nexport { Spacer } from \"./components/spacer.ts\";\nexport { Text } from \"./components/text.ts\";\nexport { TruncatedText } from \"./components/truncated-text.ts\";\nexport {\n\ttype StackChild,\n\ttype StackEntry,\n\ttype StackEntryOptions,\n\ttype StackOptions,\n\tVStack,\n} from \"./components/v-stack.ts\";\n// Editor component interface (for custom editors)\nexport type { EditorComponent } from \"./editor-component.ts\";\n// Fuzzy matching\nexport { type FuzzyMatch, fuzzyFilter, fuzzyMatch } from \"./fuzzy.ts\";\n// Keybindings\nexport {\n\tgetKeybindings,\n\ttype Keybinding,\n\ttype KeybindingConflict,\n\ttype KeybindingDefinition,\n\ttype KeybindingDefinitions,\n\ttype Keybindings,\n\ttype KeybindingsConfig,\n\tKeybindingsManager,\n\tsetKeybindings,\n\tTUI_KEYBINDINGS,\n} from \"./keybindings.ts\";\n// Keyboard input handling\nexport {\n\tdecodeKittyPrintable,\n\tisKeyRelease,\n\tisKeyRepeat,\n\tisKittyProtocolActive,\n\tKey,\n\ttype KeyEventType,\n\ttype KeyId,\n\tmatchesKey,\n\tparseKey,\n\tsetKittyProtocolActive,\n} from \"./keys.ts\";\n// LaTeX rendering\nexport { type RenderLatexOptions, renderLatex } from \"./latex.ts\";\n// Input buffering for batch splitting\nexport { StdinBuffer, type StdinBufferEventMap, type StdinBufferOptions } from \"./stdin-buffer.ts\";\n// Terminal interface and implementations\nexport { ProcessTerminal, type Terminal } from \"./terminal.ts\";\n// Terminal colors\nexport {\n\tparseOsc11BackgroundColor,\n\tparseTerminalColorSchemeReport,\n\ttype RgbColor,\n\ttype TerminalColorScheme,\n} from \"./terminal-colors.ts\";\n// Terminal image support\nexport {\n\tallocateImageId,\n\ttype CellDimensions,\n\tcalculateImageRows,\n\tdeleteAllKittyImages,\n\tdeleteKittyImage,\n\tdetectCapabilities,\n\tencodeITerm2,\n\tencodeKitty,\n\tgetCapabilities,\n\tgetCellDimensions,\n\tgetGifDimensions,\n\tgetImageDimensions,\n\tgetJpegDimensions,\n\tgetPngDimensions,\n\tgetWebpDimensions,\n\thyperlink,\n\ttype ImageDimensions,\n\ttype ImageProtocol,\n\ttype ImageRenderOptions,\n\timageFallback,\n\trenderImage,\n\tresetCapabilitiesCache,\n\tsetCapabilities,\n\tsetCapabilityOverrides,\n\tsetCellDimensions,\n\ttype TerminalCapabilities,\n} from \"./terminal-image.ts\";\nexport {\n\ttype Component,\n\tContainer,\n\tCURSOR_MARKER,\n\tcompositeTuiLine,\n\ttype Focusable,\n\tisFocusable,\n\tisViewportTUI,\n\ttype OverlayAnchor,\n\ttype OverlayBounds,\n\ttype OverlayHandle,\n\ttype OverlayMargin,\n\ttype OverlayOptions,\n\ttype OverlayUnfocusOptions,\n\ttype SizeValue,\n\ttype TUI,\n\ttype TuiInputListener,\n\ttype TuiInputListenerResult,\n\ttype TuiMode,\n\ttype TuiMouseButton,\n\ttype TuiMouseEvent,\n\ttype TuiMouseEventResult,\n\ttype TuiMouseEventType,\n\ttype TuiStopOptions,\n\ttype ViewportTUI,\n} from \"./tui.ts\";\nexport { TuiAltScreen, type TuiAltScreenOptions } from \"./tui-alt-screen.ts\";\nexport { TuiMainScreen, type TuiMainScreenRenderState } from \"./tui-main-screen.ts\";\n// Utilities\nexport {\n\tgetOsc8LinkAtColumn,\n\tsliceByColumn,\n\tstripTerminalSequences,\n\ttruncateToWidth,\n\tvisibleWidth,\n\twrapTextWithAnsi,\n} from \"./utils.ts\";\n"]}
|
package/dist/latex.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"latex.d.ts","sourceRoot":"","sources":["../src/latex.ts"],"names":[],"mappings":"AAw0CA,MAAM,WAAW,kBAAkB;IAClC,wFAAwF;IACxF,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,MAAM,GAAG,SAAS,CAkBhG","sourcesContent":["import { visibleWidth } from \"./utils.ts\";\n\nconst SYMBOLS: Readonly<Record<string, string>> = {\n\talpha: \"α\",\n\tbeta: \"β\",\n\tgamma: \"γ\",\n\tdelta: \"δ\",\n\tepsilon: \"ϵ\",\n\tvarepsilon: \"ε\",\n\tzeta: \"ζ\",\n\teta: \"η\",\n\ttheta: \"θ\",\n\tvartheta: \"ϑ\",\n\tiota: \"ι\",\n\tkappa: \"κ\",\n\tvarkappa: \"ϰ\",\n\tlambda: \"λ\",\n\tmu: \"μ\",\n\tnu: \"ν\",\n\txi: \"ξ\",\n\tpi: \"π\",\n\tvarpi: \"ϖ\",\n\trho: \"ρ\",\n\tvarrho: \"ϱ\",\n\tsigma: \"σ\",\n\tvarsigma: \"ς\",\n\ttau: \"τ\",\n\tupsilon: \"υ\",\n\tphi: \"ϕ\",\n\tvarphi: \"φ\",\n\tchi: \"χ\",\n\tpsi: \"ψ\",\n\tomega: \"ω\",\n\tGamma: \"Γ\",\n\tDelta: \"Δ\",\n\tTheta: \"Θ\",\n\tLambda: \"Λ\",\n\tXi: \"Ξ\",\n\tPi: \"Π\",\n\tSigma: \"Σ\",\n\tUpsilon: \"Υ\",\n\tPhi: \"Φ\",\n\tPsi: \"Ψ\",\n\tOmega: \"Ω\",\n\tpm: \"±\",\n\tmp: \"∓\",\n\ttimes: \"×\",\n\tdiv: \"÷\",\n\tcdot: \"·\",\n\tast: \"∗\",\n\tstar: \"⋆\",\n\tcirc: \"∘\",\n\tbullet: \"•\",\n\toplus: \"⊕\",\n\tominus: \"⊖\",\n\totimes: \"⊗\",\n\toslash: \"⊘\",\n\todot: \"⊙\",\n\tbigcirc: \"○\",\n\tdagger: \"†\",\n\tddagger: \"‡\",\n\tamalg: \"⨿\",\n\tuplus: \"⊎\",\n\tsqcap: \"⊓\",\n\tsqcup: \"⊔\",\n\ttriangleleft: \"◁\",\n\ttriangleright: \"▷\",\n\twr: \"≀\",\n\tcap: \"∩\",\n\tcup: \"∪\",\n\tbigcap: \"⋂\",\n\tbigcup: \"⋃\",\n\tbigwedge: \"⋀\",\n\tbigvee: \"⋁\",\n\tbigsqcup: \"⨆\",\n\tbiguplus: \"⨄\",\n\tbigoplus: \"⨁\",\n\tbigotimes: \"⨂\",\n\tbigodot: \"⨀\",\n\tsetminus: \"∖\",\n\tin: \"∈\",\n\tnotin: \"∉\",\n\tni: \"∋\",\n\tsubset: \"⊂\",\n\tsupset: \"⊃\",\n\tsubseteq: \"⊆\",\n\tsupseteq: \"⊇\",\n\tsqsubset: \"⊏\",\n\tsqsupset: \"⊐\",\n\tsqsubseteq: \"⊑\",\n\tsqsupseteq: \"⊒\",\n\tprec: \"≺\",\n\tpreceq: \"≼\",\n\tsucc: \"≻\",\n\tsucceq: \"≽\",\n\tll: \"≪\",\n\tgg: \"≫\",\n\tle: \"≤\",\n\tleq: \"≤\",\n\tleqslant: \"≤\",\n\tge: \"≥\",\n\tgeq: \"≥\",\n\tgeqslant: \"≥\",\n\tne: \"≠\",\n\tneq: \"≠\",\n\tequiv: \"≡\",\n\tapprox: \"≈\",\n\tsim: \"∼\",\n\tsimeq: \"≃\",\n\tcong: \"≅\",\n\tasymp: \"≍\",\n\tdoteq: \"≐\",\n\tpropto: \"∝\",\n\tparallel: \"∥\",\n\tperp: \"⊥\",\n\tmid: \"∣\",\n\tvdash: \"⊢\",\n\tdashv: \"⊣\",\n\tmodels: \"⊨\",\n\tVdash: \"⊩\",\n\tVvdash: \"⊪\",\n\tnvdash: \"⊬\",\n\tnvDash: \"⊭\",\n\tforall: \"∀\",\n\texists: \"∃\",\n\tnexists: \"∄\",\n\tneg: \"¬\",\n\tland: \"∧\",\n\twedge: \"∧\",\n\tlor: \"∨\",\n\tvee: \"∨\",\n\tto: \"→\",\n\trightarrow: \"→\",\n\tlongrightarrow: \"→\",\n\tleftarrow: \"←\",\n\tlongleftarrow: \"←\",\n\tgets: \"←\",\n\tleftrightarrow: \"↔\",\n\tlongleftrightarrow: \"↔\",\n\thookleftarrow: \"↩\",\n\thookrightarrow: \"↪\",\n\ttwoheadleftarrow: \"↞\",\n\ttwoheadrightarrow: \"↠\",\n\tleftharpoonup: \"↼\",\n\tleftharpoondown: \"↽\",\n\trightharpoonup: \"⇀\",\n\trightharpoondown: \"⇁\",\n\trightleftharpoons: \"⇌\",\n\tleftrightharpoons: \"⇋\",\n\tnearrow: \"↗\",\n\tsearrow: \"↘\",\n\tswarrow: \"↙\",\n\tnwarrow: \"↖\",\n\trightsquigarrow: \"⇝\",\n\tleadsto: \"⇝\",\n\tRightarrow: \"⇒\",\n\tLongrightarrow: \"⇒\",\n\tLeftarrow: \"⇐\",\n\tLongleftarrow: \"⇐\",\n\tLeftrightarrow: \"⇔\",\n\tLongleftrightarrow: \"⇔\",\n\timplies: \"⇒\",\n\tiff: \"⇔\",\n\tmapsto: \"↦\",\n\tlongmapsto: \"↦\",\n\tuparrow: \"↑\",\n\tdownarrow: \"↓\",\n\tpartial: \"∂\",\n\tnabla: \"∇\",\n\tint: \"∫\",\n\tiint: \"∬\",\n\tiiint: \"∭\",\n\toint: \"∮\",\n\tsum: \"∑\",\n\tprod: \"∏\",\n\tcoprod: \"∐\",\n\tinfty: \"∞\",\n\temptyset: \"∅\",\n\tvarnothing: \"∅\",\n\tangle: \"∠\",\n\ttherefore: \"∴\",\n\tbecause: \"∵\",\n\taleph: \"ℵ\",\n\tbeth: \"ℶ\",\n\tgimel: \"ℷ\",\n\tdaleth: \"ℸ\",\n\ttop: \"⊤\",\n\tbot: \"⊥\",\n\ttriangle: \"△\",\n\tsquare: \"□\",\n\tlozenge: \"◊\",\n\tcheckmark: \"✓\",\n\tcomplement: \"∁\",\n\twp: \"℘\",\n\tprime: \"′\",\n\tldots: \"…\",\n\tdots: \"…\",\n\tcdots: \"⋯\",\n\tvdots: \"⋮\",\n\tddots: \"⋱\",\n\tell: \"ℓ\",\n\thbar: \"ℏ\",\n\tIm: \"ℑ\",\n\tRe: \"ℜ\",\n\tlangle: \"⟨\",\n\trangle: \"⟩\",\n\tvert: \"|\",\n\tlvert: \"|\",\n\trvert: \"|\",\n\tVert: \"‖\",\n\tlVert: \"‖\",\n\trVert: \"‖\",\n\tlbrace: \"{\",\n\trbrace: \"}\",\n\tbackslash: \"\\\\\",\n\tlfloor: \"⌊\",\n\trfloor: \"⌋\",\n\tlceil: \"⌈\",\n\trceil: \"⌉\",\n\tcolon: \":\",\n};\n\nconst NAMED_OPERATORS = new Set([\n\t\"arccos\",\n\t\"arcsin\",\n\t\"arctan\",\n\t\"arg\",\n\t\"cos\",\n\t\"cosh\",\n\t\"cot\",\n\t\"coth\",\n\t\"csc\",\n\t\"deg\",\n\t\"det\",\n\t\"dim\",\n\t\"exp\",\n\t\"gcd\",\n\t\"hom\",\n\t\"inf\",\n\t\"ker\",\n\t\"lg\",\n\t\"lim\",\n\t\"liminf\",\n\t\"limsup\",\n\t\"ln\",\n\t\"log\",\n\t\"max\",\n\t\"min\",\n\t\"Pr\",\n\t\"sec\",\n\t\"sin\",\n\t\"sinh\",\n\t\"sup\",\n\t\"tan\",\n\t\"tanh\",\n]);\n\nconst LIMIT_OPERATORS = new Set([\n\t\"argmax\",\n\t\"argmin\",\n\t\"inf\",\n\t\"injlim\",\n\t\"lim\",\n\t\"liminf\",\n\t\"limsup\",\n\t\"max\",\n\t\"min\",\n\t\"projlim\",\n\t\"sup\",\n]);\n\nconst DISPLAY_LIMIT_SYMBOLS = new Set([\n\t\"bigcap\",\n\t\"bigcup\",\n\t\"bigodot\",\n\t\"bigoplus\",\n\t\"bigotimes\",\n\t\"bigsqcup\",\n\t\"biguplus\",\n\t\"bigvee\",\n\t\"bigwedge\",\n\t\"coprod\",\n\t\"int\",\n\t\"iint\",\n\t\"iiint\",\n\t\"oint\",\n\t\"prod\",\n\t\"sum\",\n]);\n\nconst RELATION_COMMANDS = new Set([\n\t\"Leftarrow\",\n\t\"Leftrightarrow\",\n\t\"Longleftarrow\",\n\t\"Longleftrightarrow\",\n\t\"Longrightarrow\",\n\t\"Rightarrow\",\n\t\"Vdash\",\n\t\"Vvdash\",\n\t\"approx\",\n\t\"asymp\",\n\t\"cong\",\n\t\"dashv\",\n\t\"doteq\",\n\t\"downarrow\",\n\t\"equiv\",\n\t\"ge\",\n\t\"geq\",\n\t\"geqslant\",\n\t\"gets\",\n\t\"gg\",\n\t\"hookleftarrow\",\n\t\"hookrightarrow\",\n\t\"iff\",\n\t\"implies\",\n\t\"in\",\n\t\"leadsto\",\n\t\"le\",\n\t\"leftarrow\",\n\t\"leftharpoondown\",\n\t\"leftharpoonup\",\n\t\"leftrightarrow\",\n\t\"leftrightharpoons\",\n\t\"leq\",\n\t\"leqslant\",\n\t\"ll\",\n\t\"longleftarrow\",\n\t\"longleftrightarrow\",\n\t\"longmapsto\",\n\t\"longrightarrow\",\n\t\"mapsto\",\n\t\"mid\",\n\t\"models\",\n\t\"ne\",\n\t\"nearrow\",\n\t\"neq\",\n\t\"ni\",\n\t\"notin\",\n\t\"nvdash\",\n\t\"nvDash\",\n\t\"nwarrow\",\n\t\"parallel\",\n\t\"perp\",\n\t\"prec\",\n\t\"preceq\",\n\t\"propto\",\n\t\"rightharpoondown\",\n\t\"rightharpoonup\",\n\t\"rightleftharpoons\",\n\t\"rightarrow\",\n\t\"rightsquigarrow\",\n\t\"searrow\",\n\t\"sim\",\n\t\"simeq\",\n\t\"sqsubset\",\n\t\"sqsubseteq\",\n\t\"sqsupset\",\n\t\"sqsupseteq\",\n\t\"subset\",\n\t\"subseteq\",\n\t\"succ\",\n\t\"succeq\",\n\t\"supset\",\n\t\"supseteq\",\n\t\"swarrow\",\n\t\"to\",\n\t\"triangleleft\",\n\t\"triangleright\",\n\t\"twoheadleftarrow\",\n\t\"twoheadrightarrow\",\n\t\"uparrow\",\n\t\"vdash\",\n]);\n\nconst NEGATED_SYMBOLS: Readonly<Record<string, string>> = {\n\t\"<\": \"≮\",\n\t\">\": \"≯\",\n\t\"=\": \"≠\",\n\t\"∈\": \"∉\",\n\t\"∋\": \"∌\",\n\t\"∣\": \"∤\",\n\t\"∥\": \"∦\",\n\t\"∼\": \"≁\",\n\t\"≃\": \"≄\",\n\t\"≅\": \"≇\",\n\t\"≈\": \"≉\",\n\t\"≡\": \"≢\",\n\t\"≤\": \"≰\",\n\t\"≥\": \"≱\",\n\t\"≺\": \"⊀\",\n\t\"≻\": \"⊁\",\n\t\"⊂\": \"⊄\",\n\t\"⊃\": \"⊅\",\n\t\"⊆\": \"⊈\",\n\t\"⊇\": \"⊉\",\n\t\"⊢\": \"⊬\",\n\t\"⊨\": \"⊭\",\n\t\"↔\": \"↮\",\n\t\"←\": \"↚\",\n\t\"→\": \"↛\",\n\t\"⇒\": \"⇏\",\n\t\"⇐\": \"⇍\",\n\t\"⇔\": \"⇎\",\n\t\"≼\": \"⋠\",\n\t\"≽\": \"⋡\",\n};\n\nconst BLACKBOARD: Readonly<Record<string, string>> = {\n\tC: \"ℂ\",\n\tH: \"ℍ\",\n\tN: \"ℕ\",\n\tP: \"ℙ\",\n\tQ: \"ℚ\",\n\tR: \"ℝ\",\n\tZ: \"ℤ\",\n};\n\nconst SUPERSCRIPTS: Readonly<Record<string, string>> = {\n\t\"0\": \"⁰\",\n\t\"1\": \"¹\",\n\t\"2\": \"²\",\n\t\"3\": \"³\",\n\t\"4\": \"⁴\",\n\t\"5\": \"⁵\",\n\t\"6\": \"⁶\",\n\t\"7\": \"⁷\",\n\t\"8\": \"⁸\",\n\t\"9\": \"⁹\",\n\t\"+\": \"⁺\",\n\t\"-\": \"⁻\",\n\t\"=\": \"⁼\",\n\t\"(\": \"⁽\",\n\t\")\": \"⁾\",\n\ta: \"ᵃ\",\n\tb: \"ᵇ\",\n\tc: \"ᶜ\",\n\td: \"ᵈ\",\n\te: \"ᵉ\",\n\tf: \"ᶠ\",\n\tg: \"ᵍ\",\n\th: \"ʰ\",\n\ti: \"ⁱ\",\n\tj: \"ʲ\",\n\tk: \"ᵏ\",\n\tl: \"ˡ\",\n\tm: \"ᵐ\",\n\tn: \"ⁿ\",\n\to: \"ᵒ\",\n\tp: \"ᵖ\",\n\tr: \"ʳ\",\n\ts: \"ˢ\",\n\tt: \"ᵗ\",\n\tu: \"ᵘ\",\n\tv: \"ᵛ\",\n\tw: \"ʷ\",\n\tx: \"ˣ\",\n\ty: \"ʸ\",\n\tz: \"ᶻ\",\n};\n\nconst SUBSCRIPTS: Readonly<Record<string, string>> = {\n\t\"0\": \"₀\",\n\t\"1\": \"₁\",\n\t\"2\": \"₂\",\n\t\"3\": \"₃\",\n\t\"4\": \"₄\",\n\t\"5\": \"₅\",\n\t\"6\": \"₆\",\n\t\"7\": \"₇\",\n\t\"8\": \"₈\",\n\t\"9\": \"₉\",\n\t\"+\": \"₊\",\n\t\"-\": \"₋\",\n\t\"=\": \"₌\",\n\t\"(\": \"₍\",\n\t\")\": \"₎\",\n\ta: \"ₐ\",\n\te: \"ₑ\",\n\th: \"ₕ\",\n\ti: \"ᵢ\",\n\tj: \"ⱼ\",\n\tk: \"ₖ\",\n\tl: \"ₗ\",\n\tm: \"ₘ\",\n\tn: \"ₙ\",\n\to: \"ₒ\",\n\tp: \"ₚ\",\n\tr: \"ᵣ\",\n\ts: \"ₛ\",\n\tt: \"ₜ\",\n\tu: \"ᵤ\",\n\tv: \"ᵥ\",\n\tx: \"ₓ\",\n};\n\nconst SPACING_COMMANDS = new Set([\n\t\",\",\n\t\":\",\n\t\";\",\n\t\" \",\n\t\">\",\n\t\"enspace\",\n\t\"enskip\",\n\t\"medspace\",\n\t\"quad\",\n\t\"qquad\",\n\t\"thickspace\",\n\t\"thinspace\",\n]);\nconst NEGATIVE_SPACING_COMMANDS = new Set([\"!\", \"negmedspace\", \"negthickspace\", \"negthinspace\"]);\nconst NEGATIVE_SPACE = \"\\u0000\";\nconst IGNORED_COMMANDS = new Set([\n\t\"displaystyle\",\n\t\"limits\",\n\t\"nolimits\",\n\t\"scriptstyle\",\n\t\"scriptscriptstyle\",\n\t\"textstyle\",\n]);\nconst SIZE_COMMANDS = new Set([\n\t\"big\",\n\t\"Big\",\n\t\"bigg\",\n\t\"Bigg\",\n\t\"bigl\",\n\t\"Bigl\",\n\t\"biggl\",\n\t\"Biggl\",\n\t\"bigr\",\n\t\"Bigr\",\n\t\"biggr\",\n\t\"Biggr\",\n]);\nconst PLAIN_WRAPPERS = new Set([\n\t\"emph\",\n\t\"mathcal\",\n\t\"mathbf\",\n\t\"mathfrak\",\n\t\"mathit\",\n\t\"mathrm\",\n\t\"mathnormal\",\n\t\"mathscr\",\n\t\"mathsf\",\n\t\"mathtt\",\n\t\"mathup\",\n\t\"mbox\",\n\t\"overbrace\",\n\t\"pmb\",\n\t\"smash\",\n\t\"substack\",\n\t\"text\",\n\t\"textbf\",\n\t\"textit\",\n\t\"textmd\",\n\t\"textnormal\",\n\t\"textrm\",\n\t\"textsc\",\n\t\"textsf\",\n\t\"textsl\",\n\t\"texttt\",\n\t\"textup\",\n\t\"underbrace\",\n\t\"bm\",\n\t\"boldsymbol\",\n]);\nconst ACCENTS: Readonly<Record<string, string>> = {\n\tacute: \"\\u0301\",\n\tbar: \"\\u0305\",\n\tbreve: \"\\u0306\",\n\tcheck: \"\\u030c\",\n\tddot: \"\\u0308\",\n\tdot: \"\\u0307\",\n\tgrave: \"\\u0300\",\n\that: \"\\u0302\",\n\tmathring: \"\\u030a\",\n\toverleftarrow: \"\\u20d6\",\n\toverleftrightarrow: \"\\u20e1\",\n\toverline: \"\\u0305\",\n\toverrightarrow: \"\\u20d7\",\n\ttilde: \"\\u0303\",\n\tunderline: \"\\u0332\",\n\tvec: \"\\u20d7\",\n\twidehat: \"\\u0302\",\n\twidetilde: \"\\u0303\",\n};\n\nfunction replaceCharacters(value: string, replacements: Readonly<Record<string, string>>): string | undefined {\n\tlet result = \"\";\n\tfor (const character of value) {\n\t\tconst replacement = replacements[character];\n\t\tif (replacement === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\t\tresult += replacement;\n\t}\n\treturn result;\n}\n\nfunction formatScript(value: string, kind: \"sub\" | \"sup\"): string {\n\tvalue = value.trim();\n\tconst replacements = kind === \"sub\" ? SUBSCRIPTS : SUPERSCRIPTS;\n\tconst unicode = replaceCharacters(value.replace(/\\s*([=+-])\\s*/g, \"$1\"), replacements);\n\tif (unicode !== undefined) {\n\t\treturn unicode;\n\t}\n\n\tconst prefix = kind === \"sub\" ? \"_\" : \"^\";\n\tif (Array.from(value).length === 1 || (kind === \"sub\" && /^[A-Za-z]+$/.test(value))) {\n\t\treturn `${prefix}${value}`;\n\t}\n\treturn `${prefix}(${value})`;\n}\n\nfunction formatFraction(numerator: string, denominator: string): string {\n\tnumerator = numerator.trim();\n\tdenominator = denominator.trim();\n\tconst simpleNumerator = /^[\\p{L}\\p{N}.]+$/u.test(numerator);\n\tconst simpleDenominator = /^[\\p{N}.]+$/u.test(denominator) || Array.from(denominator).length === 1;\n\treturn `${simpleNumerator ? numerator : `(${numerator})`}/${simpleDenominator ? denominator : `(${denominator})`}`;\n}\n\nfunction formatRoot(value: string, symbol = \"√\"): string {\n\tvalue = value.trim();\n\treturn /^[\\p{L}\\p{N}.]+$/u.test(value) ? `${symbol}${value}` : `${symbol}(${value})`;\n}\n\nconst NAMED_OPERATOR_START = \"\\u{f0004}\";\nconst NAMED_OPERATOR_END = \"\\u{f0005}\";\nconst NAMED_OPERATOR_LEFT_SPACING_PATTERN = /(?<=[\\p{L}\\p{N})\\]}\\u{f0001}])\\u{f0004}/gu;\nconst NAMED_OPERATOR_RIGHT_SPACING_PATTERN = /\\u{f0005}(?=[\\p{L}\\p{N}√\\u{f0000}])/gu;\n\nfunction normalizeOutput(value: string): string {\n\treturn value\n\t\t.replace(NAMED_OPERATOR_LEFT_SPACING_PATTERN, \" \")\n\t\t.replaceAll(NAMED_OPERATOR_START, \"\")\n\t\t.replace(NAMED_OPERATOR_RIGHT_SPACING_PATTERN, \" \")\n\t\t.replaceAll(NAMED_OPERATOR_END, \"\")\n\t\t.split(\"\\n\")\n\t\t.map((line) => line.replace(/[ \\t]+/g, \" \").trim())\n\t\t.filter((line, index, lines) => line.length > 0 || (index > 0 && index < lines.length - 1))\n\t\t.join(\"\\n\")\n\t\t.trim();\n}\n\ninterface FractionNode {\n\ttype: \"fraction\";\n\tnumerator: string;\n\tdenominator: string;\n}\n\ninterface OperatorNode {\n\ttype: \"operator\";\n\toperator: string;\n\tlower?: string;\n\tupper?: string;\n}\n\ninterface MatrixNode {\n\ttype: \"matrix\";\n\tlines: string[];\n\tbaseline: number;\n}\n\ntype LayoutNode = FractionNode | OperatorNode | MatrixNode;\n\ninterface Layout {\n\tlines: string[];\n\twidth: number;\n\tbaseline: number;\n}\n\nconst LAYOUT_MARKER_START = \"\\u{f0000}\";\nconst LAYOUT_MARKER_END = \"\\u{f0001}\";\nconst LAYOUT_MARKER_PATTERN = /\\u{f0000}(\\d+)\\u{f0001}/gu;\nconst TRAILING_LAYOUT_MARKER_PATTERN = /\\u{f0000}(\\d+)\\u{f0001}$/u;\nconst PROTECTED_SPACE = \"\\u{f0002}\";\n\nfunction padLayoutLine(line: string, width: number, centered = false): string {\n\tconst padding = Math.max(0, width - visibleWidth(line));\n\tconst left = centered ? Math.floor(padding / 2) : 0;\n\treturn `${\" \".repeat(left)}${line}${\" \".repeat(padding - left)}`;\n}\n\nfunction joinLayouts(layouts: readonly Layout[]): Layout {\n\tif (layouts.length === 0) {\n\t\treturn { lines: [\"\"], width: 0, baseline: 0 };\n\t}\n\tconst baseline = Math.max(...layouts.map((layout) => layout.baseline));\n\tconst below = Math.max(...layouts.map((layout) => layout.lines.length - layout.baseline - 1));\n\tconst lines: string[] = [];\n\tfor (let row = 0; row <= baseline + below; row++) {\n\t\tlet line = \"\";\n\t\tfor (const layout of layouts) {\n\t\t\tconst sourceRow = row - baseline + layout.baseline;\n\t\t\tline +=\n\t\t\t\tsourceRow >= 0 && sourceRow < layout.lines.length\n\t\t\t\t\t? padLayoutLine(layout.lines[sourceRow] ?? \"\", layout.width)\n\t\t\t\t\t: \" \".repeat(layout.width);\n\t\t}\n\t\tlines.push(line.trimEnd());\n\t}\n\treturn {\n\t\tlines,\n\t\twidth: layouts.reduce((width, layout) => width + layout.width, 0),\n\t\tbaseline,\n\t};\n}\n\nfunction renderLayout(source: string, nodes: readonly LayoutNode[]): Layout {\n\tconst renderedLines: string[] = [];\n\tlet firstBaseline = 0;\n\tfor (const sourceLine of source.split(\"\\n\")) {\n\t\tconst layouts: Layout[] = [];\n\t\tlet position = 0;\n\t\tlet previousNode: LayoutNode | undefined;\n\t\tfor (const match of sourceLine.matchAll(LAYOUT_MARKER_PATTERN)) {\n\t\t\tconst index = match.index;\n\t\t\tconst node = nodes[Number(match[1])];\n\t\t\tif (!node) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (index > position) {\n\t\t\t\tconst sliced = sourceLine.slice(position, index);\n\t\t\t\tconst trimmed = (previousNode ? sliced.trimStart() : sliced).trimEnd();\n\t\t\t\tconst preserveLeadingSpace = previousNode?.type === \"matrix\" && /^\\s/.test(sliced);\n\t\t\t\tconst preserveTrailingSpace = node.type === \"matrix\" && /\\s$/.test(sliced);\n\t\t\t\tconst text = trimmed\n\t\t\t\t\t? `${preserveLeadingSpace ? \" \" : \"\"}${trimmed}${preserveTrailingSpace ? \" \" : \"\"}`\n\t\t\t\t\t: preserveLeadingSpace || preserveTrailingSpace\n\t\t\t\t\t\t? \" \"\n\t\t\t\t\t\t: \"\";\n\t\t\t\tlayouts.push({ lines: [text], width: visibleWidth(text), baseline: 0 });\n\t\t\t}\n\t\t\tif (node.type === \"fraction\") {\n\t\t\t\tconst numerator = renderLayout(node.numerator, nodes);\n\t\t\t\tconst denominator = renderLayout(node.denominator, nodes);\n\t\t\t\tconst contentWidth = Math.max(numerator.width, denominator.width, 1);\n\t\t\t\tconst width = contentWidth + 2;\n\t\t\t\tlayouts.push({\n\t\t\t\t\tlines: [\n\t\t\t\t\t\t...numerator.lines.map((line) => padLayoutLine(line, width, true)),\n\t\t\t\t\t\t` ${\"─\".repeat(contentWidth)} `,\n\t\t\t\t\t\t...denominator.lines.map((line) => padLayoutLine(line, width, true)),\n\t\t\t\t\t],\n\t\t\t\t\twidth,\n\t\t\t\t\tbaseline: numerator.lines.length,\n\t\t\t\t});\n\t\t\t} else if (node.type === \"operator\") {\n\t\t\t\tconst contentWidth = Math.max(\n\t\t\t\t\tvisibleWidth(node.operator),\n\t\t\t\t\tnode.lower === undefined ? 0 : visibleWidth(node.lower),\n\t\t\t\t\tnode.upper === undefined ? 0 : visibleWidth(node.upper),\n\t\t\t\t);\n\t\t\t\tconst lines: string[] = [];\n\t\t\t\tif (node.upper !== undefined) {\n\t\t\t\t\tlines.push(`${padLayoutLine(node.upper, contentWidth, true)} `);\n\t\t\t\t}\n\t\t\t\tlines.push(`${padLayoutLine(node.operator, contentWidth, true)} `);\n\t\t\t\tif (node.lower !== undefined) {\n\t\t\t\t\tlines.push(`${padLayoutLine(node.lower, contentWidth, true)} `);\n\t\t\t\t}\n\t\t\t\tlayouts.push({\n\t\t\t\t\tlines,\n\t\t\t\t\twidth: contentWidth + 1,\n\t\t\t\t\tbaseline: node.upper === undefined ? 0 : 1,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tconst width = Math.max(0, ...node.lines.map((line) => visibleWidth(line)));\n\t\t\t\tlayouts.push({\n\t\t\t\t\tlines: node.lines.map((line) => padLayoutLine(line, width)),\n\t\t\t\t\twidth,\n\t\t\t\t\tbaseline: node.baseline,\n\t\t\t\t});\n\t\t\t}\n\t\t\tposition = index + match[0].length;\n\t\t\tpreviousNode = node;\n\t\t}\n\t\tif (position < sourceLine.length) {\n\t\t\tconst sliced = sourceLine.slice(position);\n\t\t\tconst trimmed = previousNode ? sliced.trimStart() : sliced;\n\t\t\tconst text = previousNode?.type === \"matrix\" && /^\\s/.test(sliced) ? ` ${trimmed}` : trimmed;\n\t\t\tlayouts.push({ lines: [text], width: visibleWidth(text), baseline: 0 });\n\t\t}\n\t\tconst lineLayout = joinLayouts(layouts);\n\t\tif (renderedLines.length === 0) {\n\t\t\tfirstBaseline = lineLayout.baseline;\n\t\t}\n\t\trenderedLines.push(...lineLayout.lines);\n\t}\n\treturn {\n\t\tlines: renderedLines,\n\t\twidth: Math.max(0, ...renderedLines.map((line) => visibleWidth(line))),\n\t\tbaseline: firstBaseline,\n\t};\n}\n\nclass LatexParser {\n\tprivate readonly source: string;\n\tprivate readonly layoutNodes: LayoutNode[];\n\tprivate readonly display: boolean;\n\tprivate position = 0;\n\tprivate supported = true;\n\tprivate stackFractions = true;\n\n\tconstructor(source: string, layoutNodes: LayoutNode[], display: boolean) {\n\t\tthis.source = source;\n\t\tthis.layoutNodes = layoutNodes;\n\t\tthis.display = display;\n\t}\n\n\trender(): string | undefined {\n\t\tconst rendered = this.parseSequence();\n\t\tif (!this.supported || this.position !== this.source.length) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn normalizeOutput(rendered);\n\t}\n\n\tprivate parseSequence(endCharacter?: string): string {\n\t\tlet result = \"\";\n\t\twhile (this.position < this.source.length) {\n\t\t\tconst character = this.source[this.position];\n\t\t\tif (endCharacter && character === endCharacter) {\n\t\t\t\tthis.position++;\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tif (character === \"}\") {\n\t\t\t\tthis.supported = false;\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tif (character === \"{\") {\n\t\t\t\tthis.position++;\n\t\t\t\tresult += this.parseSequence(\"}\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (character === \"\\\\\") {\n\t\t\t\tconst command = this.parseCommand();\n\t\t\t\tif (command === NEGATIVE_SPACE) {\n\t\t\t\t\tresult = result.trimEnd();\n\t\t\t\t\tif (result.endsWith(NAMED_OPERATOR_END)) {\n\t\t\t\t\t\tresult = result.slice(0, -NAMED_OPERATOR_END.length);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tresult += command;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (character === \"^\" || character === \"_\") {\n\t\t\t\tthis.position++;\n\t\t\t\tresult = result.trimEnd();\n\t\t\t\tconst script = formatScript(this.parseRequiredArgument(false), character === \"_\" ? \"sub\" : \"sup\");\n\t\t\t\tif (result.endsWith(NAMED_OPERATOR_END)) {\n\t\t\t\t\tresult = `${result.slice(0, -NAMED_OPERATOR_END.length)}${script}${NAMED_OPERATOR_END}`;\n\t\t\t\t} else {\n\t\t\t\t\tresult += script;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (/\\s/.test(character)) {\n\t\t\t\tresult += this.parseWhitespace();\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (character === \"=\" || character === \"<\" || character === \">\") {\n\t\t\t\tresult = `${result.trimEnd()} ${character} `;\n\t\t\t\tthis.position++;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (character === \"&\") {\n\t\t\t\tthis.position++;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (character === \"~\") {\n\t\t\t\tthis.position++;\n\t\t\t\tresult += \" \";\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (character === \".\") {\n\t\t\t\tconst marker = TRAILING_LAYOUT_MARKER_PATTERN.exec(result);\n\t\t\t\tconst node = marker ? this.layoutNodes[Number(marker[1])] : undefined;\n\t\t\t\tif (node?.type === \"matrix\") {\n\t\t\t\t\tconst lastLine = node.lines.length - 1;\n\t\t\t\t\tnode.lines[lastLine] = `${node.lines[lastLine] ?? \"\"}${character}`;\n\t\t\t\t\tthis.position++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tresult += character;\n\t\t\tthis.position++;\n\t\t}\n\n\t\tif (endCharacter) {\n\t\t\tthis.supported = false;\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate parseWhitespace(): string {\n\t\twhile (this.position < this.source.length && /\\s/.test(this.source[this.position] ?? \"\")) {\n\t\t\tthis.position++;\n\t\t}\n\t\treturn \" \";\n\t}\n\n\tprivate parseCommand(): string {\n\t\tthis.position++;\n\t\tif (this.position >= this.source.length) {\n\t\t\tthis.supported = false;\n\t\t\treturn \"\";\n\t\t}\n\n\t\tlet command = \"\";\n\t\tconst first = this.source[this.position] ?? \"\";\n\t\tif (first === \"\\n\" || first === \"\\r\") {\n\t\t\tthis.position++;\n\t\t\tif (first === \"\\r\" && this.source[this.position] === \"\\n\") {\n\t\t\t\tthis.position++;\n\t\t\t}\n\t\t\treturn \" \";\n\t\t}\n\t\tif (/[A-Za-z]/.test(first)) {\n\t\t\tconst start = this.position;\n\t\t\twhile (this.position < this.source.length && /[A-Za-z]/.test(this.source[this.position] ?? \"\")) {\n\t\t\t\tthis.position++;\n\t\t\t}\n\t\t\tcommand = this.source.slice(start, this.position);\n\t\t} else {\n\t\t\tcommand = first;\n\t\t\tthis.position++;\n\t\t}\n\n\t\tif (command === \"\\\\\") {\n\t\t\treturn \"\\n\";\n\t\t}\n\t\tif (SPACING_COMMANDS.has(command)) {\n\t\t\treturn \" \";\n\t\t}\n\t\tif (NEGATIVE_SPACING_COMMANDS.has(command)) {\n\t\t\treturn NEGATIVE_SPACE;\n\t\t}\n\t\tif (IGNORED_COMMANDS.has(command)) {\n\t\t\treturn \"\";\n\t\t}\n\t\tif (\n\t\t\tcommand === \"{\" ||\n\t\t\tcommand === \"}\" ||\n\t\t\tcommand === \"$\" ||\n\t\t\tcommand === \"%\" ||\n\t\t\tcommand === \"#\" ||\n\t\t\tcommand === \"_\" ||\n\t\t\tcommand === \"&\"\n\t\t) {\n\t\t\treturn command;\n\t\t}\n\t\tif (command === \"|\") {\n\t\t\treturn \"‖\";\n\t\t}\n\t\tif (command === \"not\") {\n\t\t\tconst value = this.parseRequiredArgument(false).trim();\n\t\t\tconst negated = NEGATED_SYMBOLS[value];\n\t\t\tif (negated !== undefined) {\n\t\t\t\treturn ` ${negated} `;\n\t\t\t}\n\t\t\tconst characters = Array.from(value);\n\t\t\tif (characters.length === 0) {\n\t\t\t\tthis.supported = false;\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t\treturn ` ${characters[0]}\\u0338${characters.slice(1).join(\"\")} `;\n\t\t}\n\t\tif (LIMIT_OPERATORS.has(command)) {\n\t\t\treturn this.parseOperator(command, \"bracket\", true, true);\n\t\t}\n\n\t\tconst symbol = SYMBOLS[command];\n\t\tif (symbol !== undefined) {\n\t\t\tif (DISPLAY_LIMIT_SYMBOLS.has(command)) {\n\t\t\t\treturn this.parseOperator(symbol, \"script\", true);\n\t\t\t}\n\t\t\treturn command === \"cdot\" || command === \"times\" || RELATION_COMMANDS.has(command) ? ` ${symbol} ` : symbol;\n\t\t}\n\t\tif (NAMED_OPERATORS.has(command)) {\n\t\t\treturn `${NAMED_OPERATOR_START}${command}${NAMED_OPERATOR_END}`;\n\t\t}\n\t\tif (SIZE_COMMANDS.has(command)) {\n\t\t\treturn \"\";\n\t\t}\n\t\tif (command === \"left\" || command === \"middle\" || command === \"right\") {\n\t\t\tif (this.source[this.position] === \".\") {\n\t\t\t\tthis.position++;\n\t\t\t}\n\t\t\treturn \"\";\n\t\t}\n\t\tif (command === \"frac\" || command === \"dfrac\" || command === \"tfrac\") {\n\t\t\tconst shouldStack = this.display && this.stackFractions && command !== \"tfrac\";\n\t\t\tconst numerator = this.parseRequiredArgument(!shouldStack);\n\t\t\tconst denominator = this.parseRequiredArgument(!shouldStack);\n\t\t\tif (shouldStack) {\n\t\t\t\tconst index =\n\t\t\t\t\tthis.layoutNodes.push({\n\t\t\t\t\t\ttype: \"fraction\",\n\t\t\t\t\t\tnumerator: normalizeOutput(numerator),\n\t\t\t\t\t\tdenominator: normalizeOutput(denominator),\n\t\t\t\t\t}) - 1;\n\t\t\t\treturn `${LAYOUT_MARKER_START}${index}${LAYOUT_MARKER_END}`;\n\t\t\t}\n\t\t\treturn formatFraction(numerator, denominator);\n\t\t}\n\t\tif (command === \"sqrt\") {\n\t\t\tconst degree = this.parseOptionalArgument()?.trim();\n\t\t\tconst value = this.parseRequiredArgument();\n\t\t\tif (degree === undefined || degree === \"2\") {\n\t\t\t\treturn formatRoot(value);\n\t\t\t}\n\t\t\tif (degree === \"3\") {\n\t\t\t\treturn formatRoot(value, \"∛\");\n\t\t\t}\n\t\t\tif (degree === \"4\") {\n\t\t\t\treturn formatRoot(value, \"∜\");\n\t\t\t}\n\t\t\treturn `${formatScript(degree, \"sup\")}${formatRoot(value)}`;\n\t\t}\n\t\tif (command === \"boxed\" || command === \"fbox\") {\n\t\t\treturn `[${this.parseRequiredArgument().trim()}]`;\n\t\t}\n\t\tif (command === \"binom\" || command === \"dbinom\" || command === \"tbinom\") {\n\t\t\treturn `(${this.parseRequiredArgument()} choose ${this.parseRequiredArgument()})`;\n\t\t}\n\t\tconst accent = ACCENTS[command];\n\t\tif (accent !== undefined) {\n\t\t\tconst value = this.parseRequiredArgument();\n\t\t\treturn Array.from(value).length === 1 ? `${value}${accent}` : `${command}(${value})`;\n\t\t}\n\t\tif (command === \"mathbb\") {\n\t\t\tconst value = this.parseRequiredArgument();\n\t\t\treturn Array.from(value, (character) => BLACKBOARD[character] ?? character).join(\"\");\n\t\t}\n\t\tif (command === \"operatorname\") {\n\t\t\tconst starred = this.source[this.position] === \"*\";\n\t\t\tif (starred) {\n\t\t\t\tthis.position++;\n\t\t\t}\n\t\t\tconst operator = normalizeOutput(this.parseRequiredArgument()).trim();\n\t\t\treturn this.parseOperator(operator, \"bracket\", starred, true);\n\t\t}\n\t\tif (command === \"mod\" || command === \"bmod\") {\n\t\t\treturn \" mod \";\n\t\t}\n\t\tif (command === \"pmod\" || command === \"pod\") {\n\t\t\tconst value = this.parseRequiredArgument().trim();\n\t\t\treturn command === \"pmod\" ? ` (mod ${value})` : ` (${value})`;\n\t\t}\n\t\tif (command === \"overset\" || command === \"stackrel\") {\n\t\t\tconst upper = this.parseRequiredArgument();\n\t\t\tconst value = this.parseRequiredArgument().trim();\n\t\t\treturn `${value}${formatScript(upper, \"sup\")}`;\n\t\t}\n\t\tif (command === \"underset\") {\n\t\t\tconst lower = this.parseRequiredArgument();\n\t\t\tconst value = this.parseRequiredArgument().trim();\n\t\t\treturn `${value}${formatScript(lower, \"sub\")}`;\n\t\t}\n\t\tif (PLAIN_WRAPPERS.has(command)) {\n\t\t\tconst value = this.parseRequiredArgument();\n\t\t\treturn command.startsWith(\"text\") || command === \"mbox\" ? value : value.trim();\n\t\t}\n\t\tif (command === \"begin\") {\n\t\t\treturn this.parseEnvironment();\n\t\t}\n\t\tif (command === \"end\") {\n\t\t\tthis.supported = false;\n\t\t\treturn \"\";\n\t\t}\n\n\t\tthis.supported = false;\n\t\treturn `\\\\${command}`;\n\t}\n\n\tprivate parseOperator(\n\t\toperator: string,\n\t\tinlineLowerStyle: \"bracket\" | \"script\",\n\t\tdisplayLimits: boolean,\n\t\tspaced = false,\n\t): string {\n\t\tlet useDisplayLimits = displayLimits;\n\t\tlet modifierPosition = this.position;\n\t\twhile (modifierPosition < this.source.length && /[ \\t]/.test(this.source[modifierPosition] ?? \"\")) {\n\t\t\tmodifierPosition++;\n\t\t}\n\t\tconst modifier = /^\\\\(limits|nolimits)(?![A-Za-z])/.exec(this.source.slice(modifierPosition));\n\t\tif (modifier) {\n\t\t\tuseDisplayLimits = modifier[1] === \"limits\";\n\t\t\tthis.position = modifierPosition + modifier[0].length;\n\t\t}\n\n\t\tlet lower: string | undefined;\n\t\tlet upper: string | undefined;\n\t\twhile (true) {\n\t\t\tlet scriptPosition = this.position;\n\t\t\twhile (scriptPosition < this.source.length && /[ \\t]/.test(this.source[scriptPosition] ?? \"\")) {\n\t\t\t\tscriptPosition++;\n\t\t\t}\n\t\t\tconst kind = this.source[scriptPosition];\n\t\t\tif (kind !== \"_\" && kind !== \"^\") {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tthis.position = scriptPosition + 1;\n\t\t\tconst value = normalizeOutput(this.parseRequiredArgument(false)).replaceAll(\" \", \"\");\n\t\t\tif (kind === \"_\") {\n\t\t\t\tif (lower !== undefined) {\n\t\t\t\t\tthis.supported = false;\n\t\t\t\t}\n\t\t\t\tlower = value;\n\t\t\t} else {\n\t\t\t\tif (upper !== undefined) {\n\t\t\t\t\tthis.supported = false;\n\t\t\t\t}\n\t\t\t\tupper = value;\n\t\t\t}\n\t\t}\n\n\t\tif (this.display && useDisplayLimits && (lower !== undefined || upper !== undefined)) {\n\t\t\tconst index = this.layoutNodes.push({ type: \"operator\", operator, lower, upper }) - 1;\n\t\t\treturn `${LAYOUT_MARKER_START}${index}${LAYOUT_MARKER_END}`;\n\t\t}\n\n\t\tlet rendered = operator;\n\t\tif (lower !== undefined) {\n\t\t\trendered += inlineLowerStyle === \"bracket\" ? `[${lower}]` : formatScript(lower, \"sub\");\n\t\t}\n\t\tif (upper !== undefined) {\n\t\t\trendered += formatScript(upper, \"sup\");\n\t\t}\n\t\treturn spaced ? ` ${rendered} ` : rendered;\n\t}\n\n\tprivate parseRequiredArgument(stackFractions = true): string {\n\t\tconst previousStackFractions = this.stackFractions;\n\t\tthis.stackFractions = previousStackFractions && stackFractions;\n\t\tconst value = this.parseRequiredArgumentValue();\n\t\tthis.stackFractions = previousStackFractions;\n\t\treturn value;\n\t}\n\n\tprivate parseRequiredArgumentValue(): string {\n\t\twhile (this.position < this.source.length && /\\s/.test(this.source[this.position] ?? \"\")) {\n\t\t\tthis.position++;\n\t\t}\n\t\tif (this.position >= this.source.length) {\n\t\t\tthis.supported = false;\n\t\t\treturn \"\";\n\t\t}\n\t\tif (this.source[this.position] === \"{\") {\n\t\t\tthis.position++;\n\t\t\treturn this.parseSequence(\"}\");\n\t\t}\n\t\tif (this.source[this.position] === \"\\\\\") {\n\t\t\treturn this.parseCommand();\n\t\t}\n\t\tconst value = this.source[this.position] ?? \"\";\n\t\tthis.position++;\n\t\treturn value;\n\t}\n\n\tprivate parseOptionalArgument(): string | undefined {\n\t\twhile (this.position < this.source.length && /[ \\t]/.test(this.source[this.position] ?? \"\")) {\n\t\t\tthis.position++;\n\t\t}\n\t\tif (this.source[this.position] !== \"[\") {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst end = this.source.indexOf(\"]\", this.position + 1);\n\t\tif (end < 0) {\n\t\t\tthis.supported = false;\n\t\t\treturn undefined;\n\t\t}\n\t\tconst value = this.source.slice(this.position + 1, end);\n\t\tthis.position = end + 1;\n\t\treturn this.renderNested(value);\n\t}\n\n\tprivate readRawGroup(): string | undefined {\n\t\twhile (this.position < this.source.length && /[ \\t]/.test(this.source[this.position] ?? \"\")) {\n\t\t\tthis.position++;\n\t\t}\n\t\tif (this.source[this.position] !== \"{\") {\n\t\t\tthis.supported = false;\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst start = ++this.position;\n\t\tlet depth = 1;\n\t\twhile (this.position < this.source.length) {\n\t\t\tconst character = this.source[this.position];\n\t\t\tif (character === \"\\\\\") {\n\t\t\t\tthis.position += 2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (character === \"{\") depth++;\n\t\t\tif (character === \"}\") depth--;\n\t\t\tif (depth === 0) {\n\t\t\t\tconst value = this.source.slice(start, this.position);\n\t\t\t\tthis.position++;\n\t\t\t\treturn value;\n\t\t\t}\n\t\t\tthis.position++;\n\t\t}\n\t\tthis.supported = false;\n\t\treturn undefined;\n\t}\n\n\tprivate splitEnvironmentRows(body: string): string[] {\n\t\treturn body.split(/\\\\\\\\(?:\\[[^\\]\\n]*\\])?/);\n\t}\n\n\tprivate parseEnvironment(): string {\n\t\tconst environment = this.readRawGroup();\n\t\tif (!environment) {\n\t\t\treturn \"\";\n\t\t}\n\t\tconst endMarker = `\\\\end{${environment}}`;\n\t\tconst end = this.source.indexOf(endMarker, this.position);\n\t\tif (end < 0) {\n\t\t\tthis.supported = false;\n\t\t\treturn \"\";\n\t\t}\n\t\tconst body = this.source.slice(this.position, end);\n\t\tthis.position = end + endMarker.length;\n\n\t\tif (environment === \"equation\" || environment === \"equation*\" || environment === \"displaymath\") {\n\t\t\treturn this.renderNested(body).trim();\n\t\t}\n\n\t\tif (\n\t\t\tenvironment === \"aligned\" ||\n\t\t\tenvironment === \"align\" ||\n\t\t\tenvironment === \"align*\" ||\n\t\t\tenvironment === \"alignedat\" ||\n\t\t\tenvironment === \"alignat\" ||\n\t\t\tenvironment === \"alignat*\" ||\n\t\t\tenvironment === \"gather\" ||\n\t\t\tenvironment === \"gathered\" ||\n\t\t\tenvironment === \"multline\" ||\n\t\t\tenvironment === \"multline*\" ||\n\t\t\tenvironment === \"split\"\n\t\t) {\n\t\t\tconst alignedAt = [\"alignedat\", \"alignat\", \"alignat*\"].includes(environment);\n\t\t\tconst alignedBody = alignedAt ? body.replace(/^\\s*\\{[^}]*\\}/, \"\") : body;\n\t\t\treturn this.splitEnvironmentRows(alignedBody)\n\t\t\t\t.map((row) => {\n\t\t\t\t\tconst cells = row.split(\"&\");\n\t\t\t\t\tconst source = alignedAt\n\t\t\t\t\t\t? Array.from({ length: Math.ceil(cells.length / 2) }, (_, index) =>\n\t\t\t\t\t\t\t\tcells.slice(index * 2, index * 2 + 2).join(\"\"),\n\t\t\t\t\t\t\t).join(\" \")\n\t\t\t\t\t\t: cells.join(\"\");\n\t\t\t\t\treturn this.renderNested(source).trim();\n\t\t\t\t})\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\"\\n\");\n\t\t}\n\n\t\tif (environment === \"cases\" || environment === \"cases*\") {\n\t\t\tconst rows = this.splitEnvironmentRows(body)\n\t\t\t\t.map((row) => row.split(\"&\").map((cell) => this.renderNested(cell, false).trim()))\n\t\t\t\t.filter((row) => row.some(Boolean));\n\t\t\treturn rows\n\t\t\t\t.map((row, index) => {\n\t\t\t\t\tconst value = (row[0] ?? \"\").replace(/,\\s*$/, \"\");\n\t\t\t\t\tconst condition = row[1] ?? \"\";\n\t\t\t\t\tconst delimiter = index === 0 ? \"⎧\" : index === rows.length - 1 ? \"⎩\" : \"⎨\";\n\t\t\t\t\tconst conditionPrefix = /^(?:if|when|for|otherwise)\\b/i.test(condition) ? \" \" : \" if \";\n\t\t\t\t\treturn `${delimiter} ${value}${condition ? `${conditionPrefix}${condition}` : \"\"}`;\n\t\t\t\t})\n\t\t\t\t.join(\"\\n\");\n\t\t}\n\n\t\tif (\n\t\t\t[\"array\", \"matrix\", \"smallmatrix\", \"pmatrix\", \"bmatrix\", \"Bmatrix\", \"vmatrix\", \"Vmatrix\"].includes(environment)\n\t\t) {\n\t\t\tconst matrixBody = environment === \"array\" ? body.replace(/^\\s*\\{[^}]*\\}/, \"\") : body;\n\t\t\treturn this.renderMatrix(environment, matrixBody);\n\t\t}\n\n\t\tthis.supported = false;\n\t\treturn body;\n\t}\n\n\tprivate renderMatrix(environment: string, body: string): string {\n\t\tconst matrix = this.splitEnvironmentRows(body)\n\t\t\t.map((row) => row.split(\"&\").map((cell) => this.renderNested(cell, false).trim()))\n\t\t\t.filter((row) => row.some(Boolean));\n\t\tconst columnCount = Math.max(0, ...matrix.map((row) => row.length));\n\t\tconst columnWidths = Array.from({ length: columnCount }, (_, column) =>\n\t\t\tMath.max(0, ...matrix.map((row) => visibleWidth(row[column] ?? \"\"))),\n\t\t);\n\t\tconst rows = matrix.map((row) =>\n\t\t\tArray.from({ length: columnCount }, (_, column) => {\n\t\t\t\tconst cell = row[column] ?? \"\";\n\t\t\t\treturn `${cell}${PROTECTED_SPACE.repeat(Math.max(0, (columnWidths[column] ?? 0) - visibleWidth(cell)))}`;\n\t\t\t}).join(\" │ \"),\n\t\t);\n\n\t\tlet lines: string[];\n\t\tif (environment === \"array\" || environment === \"matrix\" || environment === \"smallmatrix\") {\n\t\t\tlines = rows;\n\t\t} else {\n\t\t\tconst delimiters: Readonly<Record<string, readonly [string, string, string, string, string, string]>> = {\n\t\t\t\tpmatrix: [\"⎛\", \"⎞\", \"⎜\", \"⎟\", \"⎝\", \"⎠\"],\n\t\t\t\tbmatrix: [\"⎡\", \"⎤\", \"⎢\", \"⎥\", \"⎣\", \"⎦\"],\n\t\t\t\tBmatrix: [\"⎧\", \"⎫\", \"⎨\", \"⎬\", \"⎩\", \"⎭\"],\n\t\t\t\tvmatrix: [\"│\", \"│\", \"│\", \"│\", \"│\", \"│\"],\n\t\t\t\tVmatrix: [\"║\", \"║\", \"║\", \"║\", \"║\", \"║\"],\n\t\t\t};\n\t\t\tconst delimiter = delimiters[environment];\n\t\t\tif (!delimiter) {\n\t\t\t\tthis.supported = false;\n\t\t\t\treturn rows.join(\"\\n\");\n\t\t\t}\n\t\t\tlines = rows.map((row, index) => {\n\t\t\t\tconst left = index === 0 ? delimiter[0] : index === rows.length - 1 ? delimiter[4] : delimiter[2];\n\t\t\t\tconst right = index === 0 ? delimiter[1] : index === rows.length - 1 ? delimiter[5] : delimiter[3];\n\t\t\t\treturn `${left} ${row} ${right}`;\n\t\t\t});\n\t\t}\n\n\t\tif (lines.length <= 1) {\n\t\t\treturn lines[0] ?? \"\";\n\t\t}\n\t\tconst index = this.layoutNodes.push({ type: \"matrix\", lines, baseline: 0 }) - 1;\n\t\treturn `${LAYOUT_MARKER_START}${index}${LAYOUT_MARKER_END}`;\n\t}\n\n\tprivate renderNested(source: string, stackFractions = true): string {\n\t\tconst rendered = new LatexParser(source, this.layoutNodes, this.display && stackFractions).render();\n\t\tif (rendered === undefined) {\n\t\t\tthis.supported = false;\n\t\t\treturn source;\n\t\t}\n\t\treturn rendered;\n\t}\n}\n\nexport interface RenderLatexOptions {\n\t/** Stack fractions and operator limits vertically for display math (default: false). */\n\tdisplay?: boolean;\n}\n\n/**\n * Render a basic LaTeX math expression as terminal-friendly Unicode text.\n * Returns undefined when the expression contains unsupported or malformed syntax.\n */\nexport function renderLatex(source: string, options: RenderLatexOptions = {}): string | undefined {\n\tconst layoutNodes: LayoutNode[] = [];\n\tconst rendered = new LatexParser(source, layoutNodes, options.display === true).render();\n\tif (rendered === undefined) {\n\t\treturn undefined;\n\t}\n\tif (layoutNodes.length === 0) {\n\t\treturn rendered.replaceAll(PROTECTED_SPACE, \" \");\n\t}\n\tconst lines = renderLayout(rendered, layoutNodes).lines;\n\tconst indentation = Math.min(\n\t\t...lines.filter((line) => line.trim()).map((line) => line.length - line.trimStart().length),\n\t);\n\treturn lines\n\t\t.map((line) => line.slice(indentation).trimEnd())\n\t\t.join(\"\\n\")\n\t\t.trimEnd()\n\t\t.replaceAll(PROTECTED_SPACE, \" \");\n}\n"]}
|
|
1
|
+
{"version":3,"file":"latex.d.ts","sourceRoot":"","sources":["../src/latex.ts"],"names":[],"mappings":"AAs1CA,MAAM,WAAW,kBAAkB;IAClC,wFAAwF;IACxF,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,MAAM,GAAG,SAAS,CAkBhG","sourcesContent":["import { visibleWidth } from \"./utils.ts\";\n\nconst SYMBOLS: Readonly<Record<string, string>> = {\n\talpha: \"α\",\n\tbeta: \"β\",\n\tgamma: \"γ\",\n\tdelta: \"δ\",\n\tepsilon: \"ϵ\",\n\tvarepsilon: \"ε\",\n\tzeta: \"ζ\",\n\teta: \"η\",\n\ttheta: \"θ\",\n\tvartheta: \"ϑ\",\n\tiota: \"ι\",\n\tkappa: \"κ\",\n\tvarkappa: \"ϰ\",\n\tlambda: \"λ\",\n\tmu: \"μ\",\n\tnu: \"ν\",\n\txi: \"ξ\",\n\tpi: \"π\",\n\tvarpi: \"ϖ\",\n\trho: \"ρ\",\n\tvarrho: \"ϱ\",\n\tsigma: \"σ\",\n\tvarsigma: \"ς\",\n\ttau: \"τ\",\n\tupsilon: \"υ\",\n\tphi: \"ϕ\",\n\tvarphi: \"φ\",\n\tchi: \"χ\",\n\tpsi: \"ψ\",\n\tomega: \"ω\",\n\tGamma: \"Γ\",\n\tDelta: \"Δ\",\n\tTheta: \"Θ\",\n\tLambda: \"Λ\",\n\tXi: \"Ξ\",\n\tPi: \"Π\",\n\tSigma: \"Σ\",\n\tUpsilon: \"Υ\",\n\tPhi: \"Φ\",\n\tPsi: \"Ψ\",\n\tOmega: \"Ω\",\n\tpm: \"±\",\n\tmp: \"∓\",\n\ttimes: \"×\",\n\tdiv: \"÷\",\n\tcdot: \"·\",\n\tast: \"∗\",\n\tstar: \"⋆\",\n\tcirc: \"∘\",\n\tbullet: \"•\",\n\toplus: \"⊕\",\n\tominus: \"⊖\",\n\totimes: \"⊗\",\n\toslash: \"⊘\",\n\todot: \"⊙\",\n\tbigcirc: \"○\",\n\tdagger: \"†\",\n\tddagger: \"‡\",\n\tamalg: \"⨿\",\n\tuplus: \"⊎\",\n\tsqcap: \"⊓\",\n\tsqcup: \"⊔\",\n\tbowtie: \"⋈\",\n\tJoin: \"⋈\",\n\tltimes: \"⋉\",\n\trtimes: \"⋊\",\n\tleftouterjoin: \"⟕\",\n\trightouterjoin: \"⟖\",\n\tfullouterjoin: \"⟗\",\n\ttriangleleft: \"◁\",\n\ttriangleright: \"▷\",\n\twr: \"≀\",\n\tcap: \"∩\",\n\tcup: \"∪\",\n\tbigcap: \"⋂\",\n\tbigcup: \"⋃\",\n\tbigwedge: \"⋀\",\n\tbigvee: \"⋁\",\n\tbigsqcup: \"⨆\",\n\tbiguplus: \"⨄\",\n\tbigoplus: \"⨁\",\n\tbigotimes: \"⨂\",\n\tbigodot: \"⨀\",\n\tsetminus: \"∖\",\n\tin: \"∈\",\n\tnotin: \"∉\",\n\tni: \"∋\",\n\tsubset: \"⊂\",\n\tsupset: \"⊃\",\n\tsubseteq: \"⊆\",\n\tsupseteq: \"⊇\",\n\tsqsubset: \"⊏\",\n\tsqsupset: \"⊐\",\n\tsqsubseteq: \"⊑\",\n\tsqsupseteq: \"⊒\",\n\tprec: \"≺\",\n\tpreceq: \"≼\",\n\tsucc: \"≻\",\n\tsucceq: \"≽\",\n\tll: \"≪\",\n\tgg: \"≫\",\n\tle: \"≤\",\n\tleq: \"≤\",\n\tleqslant: \"≤\",\n\tge: \"≥\",\n\tgeq: \"≥\",\n\tgeqslant: \"≥\",\n\tne: \"≠\",\n\tneq: \"≠\",\n\tequiv: \"≡\",\n\tapprox: \"≈\",\n\tsim: \"∼\",\n\tsimeq: \"≃\",\n\tcong: \"≅\",\n\tasymp: \"≍\",\n\tdoteq: \"≐\",\n\tpropto: \"∝\",\n\tparallel: \"∥\",\n\tperp: \"⊥\",\n\tmid: \"∣\",\n\tvdash: \"⊢\",\n\tdashv: \"⊣\",\n\tmodels: \"⊨\",\n\tVdash: \"⊩\",\n\tVvdash: \"⊪\",\n\tnvdash: \"⊬\",\n\tnvDash: \"⊭\",\n\tforall: \"∀\",\n\texists: \"∃\",\n\tnexists: \"∄\",\n\tneg: \"¬\",\n\tland: \"∧\",\n\twedge: \"∧\",\n\tlor: \"∨\",\n\tvee: \"∨\",\n\tto: \"→\",\n\trightarrow: \"→\",\n\tlongrightarrow: \"→\",\n\tleftarrow: \"←\",\n\tlongleftarrow: \"←\",\n\tgets: \"←\",\n\tleftrightarrow: \"↔\",\n\tlongleftrightarrow: \"↔\",\n\thookleftarrow: \"↩\",\n\thookrightarrow: \"↪\",\n\ttwoheadleftarrow: \"↞\",\n\ttwoheadrightarrow: \"↠\",\n\tleftharpoonup: \"↼\",\n\tleftharpoondown: \"↽\",\n\trightharpoonup: \"⇀\",\n\trightharpoondown: \"⇁\",\n\trightleftharpoons: \"⇌\",\n\tleftrightharpoons: \"⇋\",\n\tnearrow: \"↗\",\n\tsearrow: \"↘\",\n\tswarrow: \"↙\",\n\tnwarrow: \"↖\",\n\trightsquigarrow: \"⇝\",\n\tleadsto: \"⇝\",\n\tRightarrow: \"⇒\",\n\tLongrightarrow: \"⇒\",\n\tLeftarrow: \"⇐\",\n\tLongleftarrow: \"⇐\",\n\tLeftrightarrow: \"⇔\",\n\tLongleftrightarrow: \"⇔\",\n\timplies: \"⇒\",\n\tiff: \"⇔\",\n\tmapsto: \"↦\",\n\tlongmapsto: \"↦\",\n\tuparrow: \"↑\",\n\tdownarrow: \"↓\",\n\tpartial: \"∂\",\n\tnabla: \"∇\",\n\tint: \"∫\",\n\tiint: \"∬\",\n\tiiint: \"∭\",\n\toint: \"∮\",\n\tsum: \"∑\",\n\tprod: \"∏\",\n\tcoprod: \"∐\",\n\tinfty: \"∞\",\n\temptyset: \"∅\",\n\tvarnothing: \"∅\",\n\tangle: \"∠\",\n\ttherefore: \"∴\",\n\tbecause: \"∵\",\n\taleph: \"ℵ\",\n\tbeth: \"ℶ\",\n\tgimel: \"ℷ\",\n\tdaleth: \"ℸ\",\n\ttop: \"⊤\",\n\tbot: \"⊥\",\n\ttriangle: \"△\",\n\tsquare: \"□\",\n\tlozenge: \"◊\",\n\tcheckmark: \"✓\",\n\tcomplement: \"∁\",\n\twp: \"℘\",\n\tprime: \"′\",\n\tldots: \"…\",\n\tdots: \"…\",\n\tcdots: \"⋯\",\n\tvdots: \"⋮\",\n\tddots: \"⋱\",\n\tell: \"ℓ\",\n\thbar: \"ℏ\",\n\tIm: \"ℑ\",\n\tRe: \"ℜ\",\n\tlangle: \"⟨\",\n\trangle: \"⟩\",\n\tvert: \"|\",\n\tlvert: \"|\",\n\trvert: \"|\",\n\tVert: \"‖\",\n\tlVert: \"‖\",\n\trVert: \"‖\",\n\tlbrace: \"{\",\n\trbrace: \"}\",\n\tbackslash: \"\\\\\",\n\tlfloor: \"⌊\",\n\trfloor: \"⌋\",\n\tlceil: \"⌈\",\n\trceil: \"⌉\",\n\tcolon: \":\",\n};\n\nconst NAMED_OPERATORS = new Set([\n\t\"arccos\",\n\t\"arcsin\",\n\t\"arctan\",\n\t\"arg\",\n\t\"cos\",\n\t\"cosh\",\n\t\"cot\",\n\t\"coth\",\n\t\"csc\",\n\t\"deg\",\n\t\"det\",\n\t\"dim\",\n\t\"exp\",\n\t\"gcd\",\n\t\"hom\",\n\t\"inf\",\n\t\"ker\",\n\t\"lg\",\n\t\"lim\",\n\t\"liminf\",\n\t\"limsup\",\n\t\"ln\",\n\t\"log\",\n\t\"max\",\n\t\"min\",\n\t\"Pr\",\n\t\"sec\",\n\t\"sin\",\n\t\"sinh\",\n\t\"sup\",\n\t\"tan\",\n\t\"tanh\",\n]);\n\nconst LIMIT_OPERATORS = new Set([\n\t\"argmax\",\n\t\"argmin\",\n\t\"inf\",\n\t\"injlim\",\n\t\"lim\",\n\t\"liminf\",\n\t\"limsup\",\n\t\"max\",\n\t\"min\",\n\t\"projlim\",\n\t\"sup\",\n]);\n\nconst DISPLAY_LIMIT_SYMBOLS = new Set([\n\t\"bigcap\",\n\t\"bigcup\",\n\t\"bigodot\",\n\t\"bigoplus\",\n\t\"bigotimes\",\n\t\"bigsqcup\",\n\t\"biguplus\",\n\t\"bigvee\",\n\t\"bigwedge\",\n\t\"coprod\",\n\t\"int\",\n\t\"iint\",\n\t\"iiint\",\n\t\"oint\",\n\t\"prod\",\n\t\"sum\",\n]);\n\nconst RELATION_COMMANDS = new Set([\n\t\"Leftarrow\",\n\t\"Leftrightarrow\",\n\t\"Longleftarrow\",\n\t\"Longleftrightarrow\",\n\t\"Longrightarrow\",\n\t\"Rightarrow\",\n\t\"Join\",\n\t\"Vdash\",\n\t\"Vvdash\",\n\t\"approx\",\n\t\"asymp\",\n\t\"bowtie\",\n\t\"cong\",\n\t\"dashv\",\n\t\"fullouterjoin\",\n\t\"doteq\",\n\t\"downarrow\",\n\t\"equiv\",\n\t\"ge\",\n\t\"geq\",\n\t\"geqslant\",\n\t\"gets\",\n\t\"gg\",\n\t\"hookleftarrow\",\n\t\"hookrightarrow\",\n\t\"iff\",\n\t\"implies\",\n\t\"in\",\n\t\"leadsto\",\n\t\"le\",\n\t\"leftarrow\",\n\t\"leftharpoondown\",\n\t\"leftharpoonup\",\n\t\"leftrightarrow\",\n\t\"leftrightharpoons\",\n\t\"leftouterjoin\",\n\t\"leq\",\n\t\"leqslant\",\n\t\"ll\",\n\t\"longleftarrow\",\n\t\"longleftrightarrow\",\n\t\"longmapsto\",\n\t\"longrightarrow\",\n\t\"ltimes\",\n\t\"mapsto\",\n\t\"mid\",\n\t\"models\",\n\t\"ne\",\n\t\"nearrow\",\n\t\"neq\",\n\t\"ni\",\n\t\"notin\",\n\t\"nvdash\",\n\t\"nvDash\",\n\t\"nwarrow\",\n\t\"parallel\",\n\t\"perp\",\n\t\"prec\",\n\t\"preceq\",\n\t\"propto\",\n\t\"rightharpoondown\",\n\t\"rightharpoonup\",\n\t\"rightleftharpoons\",\n\t\"rightouterjoin\",\n\t\"rightarrow\",\n\t\"rightsquigarrow\",\n\t\"rtimes\",\n\t\"searrow\",\n\t\"sim\",\n\t\"simeq\",\n\t\"sqsubset\",\n\t\"sqsubseteq\",\n\t\"sqsupset\",\n\t\"sqsupseteq\",\n\t\"subset\",\n\t\"subseteq\",\n\t\"succ\",\n\t\"succeq\",\n\t\"supset\",\n\t\"supseteq\",\n\t\"swarrow\",\n\t\"to\",\n\t\"triangleleft\",\n\t\"triangleright\",\n\t\"twoheadleftarrow\",\n\t\"twoheadrightarrow\",\n\t\"uparrow\",\n\t\"vdash\",\n]);\n\nconst NEGATED_SYMBOLS: Readonly<Record<string, string>> = {\n\t\"<\": \"≮\",\n\t\">\": \"≯\",\n\t\"=\": \"≠\",\n\t\"∈\": \"∉\",\n\t\"∋\": \"∌\",\n\t\"∣\": \"∤\",\n\t\"∥\": \"∦\",\n\t\"∼\": \"≁\",\n\t\"≃\": \"≄\",\n\t\"≅\": \"≇\",\n\t\"≈\": \"≉\",\n\t\"≡\": \"≢\",\n\t\"≤\": \"≰\",\n\t\"≥\": \"≱\",\n\t\"≺\": \"⊀\",\n\t\"≻\": \"⊁\",\n\t\"⊂\": \"⊄\",\n\t\"⊃\": \"⊅\",\n\t\"⊆\": \"⊈\",\n\t\"⊇\": \"⊉\",\n\t\"⊢\": \"⊬\",\n\t\"⊨\": \"⊭\",\n\t\"↔\": \"↮\",\n\t\"←\": \"↚\",\n\t\"→\": \"↛\",\n\t\"⇒\": \"⇏\",\n\t\"⇐\": \"⇍\",\n\t\"⇔\": \"⇎\",\n\t\"≼\": \"⋠\",\n\t\"≽\": \"⋡\",\n};\n\nconst BLACKBOARD: Readonly<Record<string, string>> = {\n\tC: \"ℂ\",\n\tH: \"ℍ\",\n\tN: \"ℕ\",\n\tP: \"ℙ\",\n\tQ: \"ℚ\",\n\tR: \"ℝ\",\n\tZ: \"ℤ\",\n};\n\nconst SUPERSCRIPTS: Readonly<Record<string, string>> = {\n\t\"0\": \"⁰\",\n\t\"1\": \"¹\",\n\t\"2\": \"²\",\n\t\"3\": \"³\",\n\t\"4\": \"⁴\",\n\t\"5\": \"⁵\",\n\t\"6\": \"⁶\",\n\t\"7\": \"⁷\",\n\t\"8\": \"⁸\",\n\t\"9\": \"⁹\",\n\t\"+\": \"⁺\",\n\t\"-\": \"⁻\",\n\t\"=\": \"⁼\",\n\t\"(\": \"⁽\",\n\t\")\": \"⁾\",\n\ta: \"ᵃ\",\n\tb: \"ᵇ\",\n\tc: \"ᶜ\",\n\td: \"ᵈ\",\n\te: \"ᵉ\",\n\tf: \"ᶠ\",\n\tg: \"ᵍ\",\n\th: \"ʰ\",\n\ti: \"ⁱ\",\n\tj: \"ʲ\",\n\tk: \"ᵏ\",\n\tl: \"ˡ\",\n\tm: \"ᵐ\",\n\tn: \"ⁿ\",\n\to: \"ᵒ\",\n\tp: \"ᵖ\",\n\tr: \"ʳ\",\n\ts: \"ˢ\",\n\tt: \"ᵗ\",\n\tu: \"ᵘ\",\n\tv: \"ᵛ\",\n\tw: \"ʷ\",\n\tx: \"ˣ\",\n\ty: \"ʸ\",\n\tz: \"ᶻ\",\n};\n\nconst SUBSCRIPTS: Readonly<Record<string, string>> = {\n\t\"0\": \"₀\",\n\t\"1\": \"₁\",\n\t\"2\": \"₂\",\n\t\"3\": \"₃\",\n\t\"4\": \"₄\",\n\t\"5\": \"₅\",\n\t\"6\": \"₆\",\n\t\"7\": \"₇\",\n\t\"8\": \"₈\",\n\t\"9\": \"₉\",\n\t\"+\": \"₊\",\n\t\"-\": \"₋\",\n\t\"=\": \"₌\",\n\t\"(\": \"₍\",\n\t\")\": \"₎\",\n\ta: \"ₐ\",\n\te: \"ₑ\",\n\th: \"ₕ\",\n\ti: \"ᵢ\",\n\tj: \"ⱼ\",\n\tk: \"ₖ\",\n\tl: \"ₗ\",\n\tm: \"ₘ\",\n\tn: \"ₙ\",\n\to: \"ₒ\",\n\tp: \"ₚ\",\n\tr: \"ᵣ\",\n\ts: \"ₛ\",\n\tt: \"ₜ\",\n\tu: \"ᵤ\",\n\tv: \"ᵥ\",\n\tx: \"ₓ\",\n};\n\nconst SPACING_COMMANDS = new Set([\n\t\",\",\n\t\":\",\n\t\";\",\n\t\" \",\n\t\">\",\n\t\"enspace\",\n\t\"enskip\",\n\t\"medspace\",\n\t\"quad\",\n\t\"qquad\",\n\t\"thickspace\",\n\t\"thinspace\",\n]);\nconst NEGATIVE_SPACING_COMMANDS = new Set([\"!\", \"negmedspace\", \"negthickspace\", \"negthinspace\"]);\nconst NEGATIVE_SPACE = \"\\u0000\";\nconst IGNORED_COMMANDS = new Set([\n\t\"displaystyle\",\n\t\"limits\",\n\t\"nolimits\",\n\t\"scriptstyle\",\n\t\"scriptscriptstyle\",\n\t\"textstyle\",\n]);\nconst SIZE_COMMANDS = new Set([\n\t\"big\",\n\t\"Big\",\n\t\"bigg\",\n\t\"Bigg\",\n\t\"bigl\",\n\t\"Bigl\",\n\t\"biggl\",\n\t\"Biggl\",\n\t\"bigr\",\n\t\"Bigr\",\n\t\"biggr\",\n\t\"Biggr\",\n]);\nconst PLAIN_WRAPPERS = new Set([\n\t\"emph\",\n\t\"mathcal\",\n\t\"mathbf\",\n\t\"mathfrak\",\n\t\"mathit\",\n\t\"mathrm\",\n\t\"mathnormal\",\n\t\"mathscr\",\n\t\"mathsf\",\n\t\"mathtt\",\n\t\"mathup\",\n\t\"mbox\",\n\t\"overbrace\",\n\t\"pmb\",\n\t\"smash\",\n\t\"substack\",\n\t\"text\",\n\t\"textbf\",\n\t\"textit\",\n\t\"textmd\",\n\t\"textnormal\",\n\t\"textrm\",\n\t\"textsc\",\n\t\"textsf\",\n\t\"textsl\",\n\t\"texttt\",\n\t\"textup\",\n\t\"underbrace\",\n\t\"bm\",\n\t\"boldsymbol\",\n]);\nconst ACCENTS: Readonly<Record<string, string>> = {\n\tacute: \"\\u0301\",\n\tbar: \"\\u0305\",\n\tbreve: \"\\u0306\",\n\tcheck: \"\\u030c\",\n\tddot: \"\\u0308\",\n\tdot: \"\\u0307\",\n\tgrave: \"\\u0300\",\n\that: \"\\u0302\",\n\tmathring: \"\\u030a\",\n\toverleftarrow: \"\\u20d6\",\n\toverleftrightarrow: \"\\u20e1\",\n\toverline: \"\\u0305\",\n\toverrightarrow: \"\\u20d7\",\n\ttilde: \"\\u0303\",\n\tunderline: \"\\u0332\",\n\tvec: \"\\u20d7\",\n\twidehat: \"\\u0302\",\n\twidetilde: \"\\u0303\",\n};\n\nfunction replaceCharacters(value: string, replacements: Readonly<Record<string, string>>): string | undefined {\n\tlet result = \"\";\n\tfor (const character of value) {\n\t\tconst replacement = replacements[character];\n\t\tif (replacement === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\t\tresult += replacement;\n\t}\n\treturn result;\n}\n\nfunction formatScript(value: string, kind: \"sub\" | \"sup\"): string {\n\tvalue = value.trim();\n\tconst replacements = kind === \"sub\" ? SUBSCRIPTS : SUPERSCRIPTS;\n\tconst unicode = replaceCharacters(value.replace(/\\s*([=+-])\\s*/g, \"$1\"), replacements);\n\tif (unicode !== undefined) {\n\t\treturn unicode;\n\t}\n\n\tconst prefix = kind === \"sub\" ? \"_\" : \"^\";\n\tif (Array.from(value).length === 1 || (kind === \"sub\" && /^[A-Za-z]+$/.test(value))) {\n\t\treturn `${prefix}${value}`;\n\t}\n\treturn `${prefix}(${value})`;\n}\n\nfunction formatFraction(numerator: string, denominator: string): string {\n\tnumerator = numerator.trim();\n\tdenominator = denominator.trim();\n\tconst simpleNumerator = /^[\\p{L}\\p{N}.]+$/u.test(numerator);\n\tconst simpleDenominator = /^[\\p{N}.]+$/u.test(denominator) || Array.from(denominator).length === 1;\n\treturn `${simpleNumerator ? numerator : `(${numerator})`}/${simpleDenominator ? denominator : `(${denominator})`}`;\n}\n\nfunction formatRoot(value: string, symbol = \"√\"): string {\n\tvalue = value.trim();\n\treturn /^[\\p{L}\\p{N}.]+$/u.test(value) ? `${symbol}${value}` : `${symbol}(${value})`;\n}\n\nconst NAMED_OPERATOR_START = \"\\u{f0004}\";\nconst NAMED_OPERATOR_END = \"\\u{f0005}\";\nconst NAMED_OPERATOR_LEFT_SPACING_PATTERN = /(?<=[\\p{L}\\p{N})\\]}\\u{f0001}])\\u{f0004}/gu;\nconst NAMED_OPERATOR_RIGHT_SPACING_PATTERN = /\\u{f0005}(?=[\\p{L}\\p{N}√\\u{f0000}])/gu;\n\nfunction normalizeOutput(value: string): string {\n\treturn value\n\t\t.replace(NAMED_OPERATOR_LEFT_SPACING_PATTERN, \" \")\n\t\t.replaceAll(NAMED_OPERATOR_START, \"\")\n\t\t.replace(NAMED_OPERATOR_RIGHT_SPACING_PATTERN, \" \")\n\t\t.replaceAll(NAMED_OPERATOR_END, \"\")\n\t\t.split(\"\\n\")\n\t\t.map((line) => line.replace(/[ \\t]+/g, \" \").trim())\n\t\t.filter((line, index, lines) => line.length > 0 || (index > 0 && index < lines.length - 1))\n\t\t.join(\"\\n\")\n\t\t.trim();\n}\n\ninterface FractionNode {\n\ttype: \"fraction\";\n\tnumerator: string;\n\tdenominator: string;\n}\n\ninterface OperatorNode {\n\ttype: \"operator\";\n\toperator: string;\n\tlower?: string;\n\tupper?: string;\n}\n\ninterface MatrixNode {\n\ttype: \"matrix\";\n\tlines: string[];\n\tbaseline: number;\n}\n\ntype LayoutNode = FractionNode | OperatorNode | MatrixNode;\n\ninterface Layout {\n\tlines: string[];\n\twidth: number;\n\tbaseline: number;\n}\n\nconst LAYOUT_MARKER_START = \"\\u{f0000}\";\nconst LAYOUT_MARKER_END = \"\\u{f0001}\";\nconst LAYOUT_MARKER_PATTERN = /\\u{f0000}(\\d+)\\u{f0001}/gu;\nconst TRAILING_LAYOUT_MARKER_PATTERN = /\\u{f0000}(\\d+)\\u{f0001}$/u;\nconst PROTECTED_SPACE = \"\\u{f0002}\";\n\nfunction padLayoutLine(line: string, width: number, centered = false): string {\n\tconst padding = Math.max(0, width - visibleWidth(line));\n\tconst left = centered ? Math.floor(padding / 2) : 0;\n\treturn `${\" \".repeat(left)}${line}${\" \".repeat(padding - left)}`;\n}\n\nfunction joinLayouts(layouts: readonly Layout[]): Layout {\n\tif (layouts.length === 0) {\n\t\treturn { lines: [\"\"], width: 0, baseline: 0 };\n\t}\n\tconst baseline = Math.max(...layouts.map((layout) => layout.baseline));\n\tconst below = Math.max(...layouts.map((layout) => layout.lines.length - layout.baseline - 1));\n\tconst lines: string[] = [];\n\tfor (let row = 0; row <= baseline + below; row++) {\n\t\tlet line = \"\";\n\t\tfor (const layout of layouts) {\n\t\t\tconst sourceRow = row - baseline + layout.baseline;\n\t\t\tline +=\n\t\t\t\tsourceRow >= 0 && sourceRow < layout.lines.length\n\t\t\t\t\t? padLayoutLine(layout.lines[sourceRow] ?? \"\", layout.width)\n\t\t\t\t\t: \" \".repeat(layout.width);\n\t\t}\n\t\tlines.push(line.trimEnd());\n\t}\n\treturn {\n\t\tlines,\n\t\twidth: layouts.reduce((width, layout) => width + layout.width, 0),\n\t\tbaseline,\n\t};\n}\n\nfunction renderLayout(source: string, nodes: readonly LayoutNode[]): Layout {\n\tconst renderedLines: string[] = [];\n\tlet firstBaseline = 0;\n\tfor (const sourceLine of source.split(\"\\n\")) {\n\t\tconst layouts: Layout[] = [];\n\t\tlet position = 0;\n\t\tlet previousNode: LayoutNode | undefined;\n\t\tfor (const match of sourceLine.matchAll(LAYOUT_MARKER_PATTERN)) {\n\t\t\tconst index = match.index;\n\t\t\tconst node = nodes[Number(match[1])];\n\t\t\tif (!node) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (index > position) {\n\t\t\t\tconst sliced = sourceLine.slice(position, index);\n\t\t\t\tconst trimmed = (previousNode ? sliced.trimStart() : sliced).trimEnd();\n\t\t\t\tconst preserveLeadingSpace = previousNode?.type === \"matrix\" && /^\\s/.test(sliced);\n\t\t\t\tconst preserveTrailingSpace = node.type === \"matrix\" && /\\s$/.test(sliced);\n\t\t\t\tconst text = trimmed\n\t\t\t\t\t? `${preserveLeadingSpace ? \" \" : \"\"}${trimmed}${preserveTrailingSpace ? \" \" : \"\"}`\n\t\t\t\t\t: preserveLeadingSpace || preserveTrailingSpace\n\t\t\t\t\t\t? \" \"\n\t\t\t\t\t\t: \"\";\n\t\t\t\tlayouts.push({ lines: [text], width: visibleWidth(text), baseline: 0 });\n\t\t\t}\n\t\t\tif (node.type === \"fraction\") {\n\t\t\t\tconst numerator = renderLayout(node.numerator, nodes);\n\t\t\t\tconst denominator = renderLayout(node.denominator, nodes);\n\t\t\t\tconst contentWidth = Math.max(numerator.width, denominator.width, 1);\n\t\t\t\tconst width = contentWidth + 2;\n\t\t\t\tlayouts.push({\n\t\t\t\t\tlines: [\n\t\t\t\t\t\t...numerator.lines.map((line) => padLayoutLine(line, width, true)),\n\t\t\t\t\t\t` ${\"─\".repeat(contentWidth)} `,\n\t\t\t\t\t\t...denominator.lines.map((line) => padLayoutLine(line, width, true)),\n\t\t\t\t\t],\n\t\t\t\t\twidth,\n\t\t\t\t\tbaseline: numerator.lines.length,\n\t\t\t\t});\n\t\t\t} else if (node.type === \"operator\") {\n\t\t\t\tconst contentWidth = Math.max(\n\t\t\t\t\tvisibleWidth(node.operator),\n\t\t\t\t\tnode.lower === undefined ? 0 : visibleWidth(node.lower),\n\t\t\t\t\tnode.upper === undefined ? 0 : visibleWidth(node.upper),\n\t\t\t\t);\n\t\t\t\tconst lines: string[] = [];\n\t\t\t\tif (node.upper !== undefined) {\n\t\t\t\t\tlines.push(`${padLayoutLine(node.upper, contentWidth, true)} `);\n\t\t\t\t}\n\t\t\t\tlines.push(`${padLayoutLine(node.operator, contentWidth, true)} `);\n\t\t\t\tif (node.lower !== undefined) {\n\t\t\t\t\tlines.push(`${padLayoutLine(node.lower, contentWidth, true)} `);\n\t\t\t\t}\n\t\t\t\tlayouts.push({\n\t\t\t\t\tlines,\n\t\t\t\t\twidth: contentWidth + 1,\n\t\t\t\t\tbaseline: node.upper === undefined ? 0 : 1,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tconst width = Math.max(0, ...node.lines.map((line) => visibleWidth(line)));\n\t\t\t\tlayouts.push({\n\t\t\t\t\tlines: node.lines.map((line) => padLayoutLine(line, width)),\n\t\t\t\t\twidth,\n\t\t\t\t\tbaseline: node.baseline,\n\t\t\t\t});\n\t\t\t}\n\t\t\tposition = index + match[0].length;\n\t\t\tpreviousNode = node;\n\t\t}\n\t\tif (position < sourceLine.length) {\n\t\t\tconst sliced = sourceLine.slice(position);\n\t\t\tconst trimmed = previousNode ? sliced.trimStart() : sliced;\n\t\t\tconst text = previousNode?.type === \"matrix\" && /^\\s/.test(sliced) ? ` ${trimmed}` : trimmed;\n\t\t\tlayouts.push({ lines: [text], width: visibleWidth(text), baseline: 0 });\n\t\t}\n\t\tconst lineLayout = joinLayouts(layouts);\n\t\tif (renderedLines.length === 0) {\n\t\t\tfirstBaseline = lineLayout.baseline;\n\t\t}\n\t\trenderedLines.push(...lineLayout.lines);\n\t}\n\treturn {\n\t\tlines: renderedLines,\n\t\twidth: Math.max(0, ...renderedLines.map((line) => visibleWidth(line))),\n\t\tbaseline: firstBaseline,\n\t};\n}\n\nclass LatexParser {\n\tprivate readonly source: string;\n\tprivate readonly layoutNodes: LayoutNode[];\n\tprivate readonly display: boolean;\n\tprivate position = 0;\n\tprivate supported = true;\n\tprivate stackFractions = true;\n\n\tconstructor(source: string, layoutNodes: LayoutNode[], display: boolean) {\n\t\tthis.source = source;\n\t\tthis.layoutNodes = layoutNodes;\n\t\tthis.display = display;\n\t}\n\n\trender(): string | undefined {\n\t\tconst rendered = this.parseSequence();\n\t\tif (!this.supported || this.position !== this.source.length) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn normalizeOutput(rendered);\n\t}\n\n\tprivate parseSequence(endCharacter?: string): string {\n\t\tlet result = \"\";\n\t\twhile (this.position < this.source.length) {\n\t\t\tconst character = this.source[this.position];\n\t\t\tif (endCharacter && character === endCharacter) {\n\t\t\t\tthis.position++;\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tif (character === \"}\") {\n\t\t\t\tthis.supported = false;\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tif (character === \"{\") {\n\t\t\t\tthis.position++;\n\t\t\t\tresult += this.parseSequence(\"}\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (character === \"\\\\\") {\n\t\t\t\tconst command = this.parseCommand();\n\t\t\t\tif (command === NEGATIVE_SPACE) {\n\t\t\t\t\tresult = result.trimEnd();\n\t\t\t\t\tif (result.endsWith(NAMED_OPERATOR_END)) {\n\t\t\t\t\t\tresult = result.slice(0, -NAMED_OPERATOR_END.length);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tresult += command;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (character === \"^\" || character === \"_\") {\n\t\t\t\tthis.position++;\n\t\t\t\tresult = result.trimEnd();\n\t\t\t\tconst script = formatScript(this.parseRequiredArgument(false), character === \"_\" ? \"sub\" : \"sup\");\n\t\t\t\tif (result.endsWith(NAMED_OPERATOR_END)) {\n\t\t\t\t\tresult = `${result.slice(0, -NAMED_OPERATOR_END.length)}${script}${NAMED_OPERATOR_END}`;\n\t\t\t\t} else {\n\t\t\t\t\tresult += script;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (/\\s/.test(character)) {\n\t\t\t\tresult += this.parseWhitespace();\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (character === \"=\" || character === \"<\" || character === \">\") {\n\t\t\t\tresult = `${result.trimEnd()} ${character} `;\n\t\t\t\tthis.position++;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (character === \"&\") {\n\t\t\t\tthis.position++;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (character === \"~\") {\n\t\t\t\tthis.position++;\n\t\t\t\tresult += \" \";\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (character === \".\") {\n\t\t\t\tconst marker = TRAILING_LAYOUT_MARKER_PATTERN.exec(result);\n\t\t\t\tconst node = marker ? this.layoutNodes[Number(marker[1])] : undefined;\n\t\t\t\tif (node?.type === \"matrix\") {\n\t\t\t\t\tconst lastLine = node.lines.length - 1;\n\t\t\t\t\tnode.lines[lastLine] = `${node.lines[lastLine] ?? \"\"}${character}`;\n\t\t\t\t\tthis.position++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tresult += character;\n\t\t\tthis.position++;\n\t\t}\n\n\t\tif (endCharacter) {\n\t\t\tthis.supported = false;\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate parseWhitespace(): string {\n\t\twhile (this.position < this.source.length && /\\s/.test(this.source[this.position] ?? \"\")) {\n\t\t\tthis.position++;\n\t\t}\n\t\treturn \" \";\n\t}\n\n\tprivate parseCommand(): string {\n\t\tthis.position++;\n\t\tif (this.position >= this.source.length) {\n\t\t\tthis.supported = false;\n\t\t\treturn \"\";\n\t\t}\n\n\t\tlet command = \"\";\n\t\tconst first = this.source[this.position] ?? \"\";\n\t\tif (first === \"\\n\" || first === \"\\r\") {\n\t\t\tthis.position++;\n\t\t\tif (first === \"\\r\" && this.source[this.position] === \"\\n\") {\n\t\t\t\tthis.position++;\n\t\t\t}\n\t\t\treturn \" \";\n\t\t}\n\t\tif (/[A-Za-z]/.test(first)) {\n\t\t\tconst start = this.position;\n\t\t\twhile (this.position < this.source.length && /[A-Za-z]/.test(this.source[this.position] ?? \"\")) {\n\t\t\t\tthis.position++;\n\t\t\t}\n\t\t\tcommand = this.source.slice(start, this.position);\n\t\t} else {\n\t\t\tcommand = first;\n\t\t\tthis.position++;\n\t\t}\n\n\t\tif (command === \"\\\\\") {\n\t\t\treturn \"\\n\";\n\t\t}\n\t\tif (SPACING_COMMANDS.has(command)) {\n\t\t\treturn \" \";\n\t\t}\n\t\tif (NEGATIVE_SPACING_COMMANDS.has(command)) {\n\t\t\treturn NEGATIVE_SPACE;\n\t\t}\n\t\tif (IGNORED_COMMANDS.has(command)) {\n\t\t\treturn \"\";\n\t\t}\n\t\tif (\n\t\t\tcommand === \"{\" ||\n\t\t\tcommand === \"}\" ||\n\t\t\tcommand === \"$\" ||\n\t\t\tcommand === \"%\" ||\n\t\t\tcommand === \"#\" ||\n\t\t\tcommand === \"_\" ||\n\t\t\tcommand === \"&\"\n\t\t) {\n\t\t\treturn command;\n\t\t}\n\t\tif (command === \"|\") {\n\t\t\treturn \"‖\";\n\t\t}\n\t\tif (command === \"not\") {\n\t\t\tconst value = this.parseRequiredArgument(false).trim();\n\t\t\tconst negated = NEGATED_SYMBOLS[value];\n\t\t\tif (negated !== undefined) {\n\t\t\t\treturn ` ${negated} `;\n\t\t\t}\n\t\t\tconst characters = Array.from(value);\n\t\t\tif (characters.length === 0) {\n\t\t\t\tthis.supported = false;\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t\treturn ` ${characters[0]}\\u0338${characters.slice(1).join(\"\")} `;\n\t\t}\n\t\tif (LIMIT_OPERATORS.has(command)) {\n\t\t\treturn this.parseOperator(command, \"bracket\", true, true);\n\t\t}\n\n\t\tconst symbol = SYMBOLS[command];\n\t\tif (symbol !== undefined) {\n\t\t\tif (DISPLAY_LIMIT_SYMBOLS.has(command)) {\n\t\t\t\treturn this.parseOperator(symbol, \"script\", true);\n\t\t\t}\n\t\t\treturn command === \"cdot\" || command === \"times\" || RELATION_COMMANDS.has(command) ? ` ${symbol} ` : symbol;\n\t\t}\n\t\tif (NAMED_OPERATORS.has(command)) {\n\t\t\treturn `${NAMED_OPERATOR_START}${command}${NAMED_OPERATOR_END}`;\n\t\t}\n\t\tif (SIZE_COMMANDS.has(command)) {\n\t\t\treturn \"\";\n\t\t}\n\t\tif (command === \"left\" || command === \"middle\" || command === \"right\") {\n\t\t\tif (this.source[this.position] === \".\") {\n\t\t\t\tthis.position++;\n\t\t\t}\n\t\t\treturn \"\";\n\t\t}\n\t\tif (command === \"frac\" || command === \"dfrac\" || command === \"tfrac\") {\n\t\t\tconst shouldStack = this.display && this.stackFractions && command !== \"tfrac\";\n\t\t\tconst numerator = this.parseRequiredArgument(!shouldStack);\n\t\t\tconst denominator = this.parseRequiredArgument(!shouldStack);\n\t\t\tif (shouldStack) {\n\t\t\t\tconst index =\n\t\t\t\t\tthis.layoutNodes.push({\n\t\t\t\t\t\ttype: \"fraction\",\n\t\t\t\t\t\tnumerator: normalizeOutput(numerator),\n\t\t\t\t\t\tdenominator: normalizeOutput(denominator),\n\t\t\t\t\t}) - 1;\n\t\t\t\treturn `${LAYOUT_MARKER_START}${index}${LAYOUT_MARKER_END}`;\n\t\t\t}\n\t\t\treturn formatFraction(numerator, denominator);\n\t\t}\n\t\tif (command === \"sqrt\") {\n\t\t\tconst degree = this.parseOptionalArgument()?.trim();\n\t\t\tconst value = this.parseRequiredArgument();\n\t\t\tif (degree === undefined || degree === \"2\") {\n\t\t\t\treturn formatRoot(value);\n\t\t\t}\n\t\t\tif (degree === \"3\") {\n\t\t\t\treturn formatRoot(value, \"∛\");\n\t\t\t}\n\t\t\tif (degree === \"4\") {\n\t\t\t\treturn formatRoot(value, \"∜\");\n\t\t\t}\n\t\t\treturn `${formatScript(degree, \"sup\")}${formatRoot(value)}`;\n\t\t}\n\t\tif (command === \"boxed\" || command === \"fbox\") {\n\t\t\treturn `[${this.parseRequiredArgument().trim()}]`;\n\t\t}\n\t\tif (command === \"binom\" || command === \"dbinom\" || command === \"tbinom\") {\n\t\t\treturn `(${this.parseRequiredArgument()} choose ${this.parseRequiredArgument()})`;\n\t\t}\n\t\tconst accent = ACCENTS[command];\n\t\tif (accent !== undefined) {\n\t\t\tconst value = this.parseRequiredArgument();\n\t\t\treturn Array.from(value).length === 1 ? `${value}${accent}` : `${command}(${value})`;\n\t\t}\n\t\tif (command === \"mathbb\") {\n\t\t\tconst value = this.parseRequiredArgument();\n\t\t\treturn Array.from(value, (character) => BLACKBOARD[character] ?? character).join(\"\");\n\t\t}\n\t\tif (command === \"operatorname\") {\n\t\t\tconst starred = this.source[this.position] === \"*\";\n\t\t\tif (starred) {\n\t\t\t\tthis.position++;\n\t\t\t}\n\t\t\tconst operator = normalizeOutput(this.parseRequiredArgument()).trim();\n\t\t\treturn this.parseOperator(operator, \"bracket\", starred, true);\n\t\t}\n\t\tif (command === \"mod\" || command === \"bmod\") {\n\t\t\treturn \" mod \";\n\t\t}\n\t\tif (command === \"pmod\" || command === \"pod\") {\n\t\t\tconst value = this.parseRequiredArgument().trim();\n\t\t\treturn command === \"pmod\" ? ` (mod ${value})` : ` (${value})`;\n\t\t}\n\t\tif (command === \"overset\" || command === \"stackrel\") {\n\t\t\tconst upper = this.parseRequiredArgument();\n\t\t\tconst value = this.parseRequiredArgument().trim();\n\t\t\treturn `${value}${formatScript(upper, \"sup\")}`;\n\t\t}\n\t\tif (command === \"underset\") {\n\t\t\tconst lower = this.parseRequiredArgument();\n\t\t\tconst value = this.parseRequiredArgument().trim();\n\t\t\treturn `${value}${formatScript(lower, \"sub\")}`;\n\t\t}\n\t\tif (PLAIN_WRAPPERS.has(command)) {\n\t\t\tconst value = this.parseRequiredArgument();\n\t\t\treturn command.startsWith(\"text\") || command === \"mbox\" ? value : value.trim();\n\t\t}\n\t\tif (command === \"begin\") {\n\t\t\treturn this.parseEnvironment();\n\t\t}\n\t\tif (command === \"end\") {\n\t\t\tthis.supported = false;\n\t\t\treturn \"\";\n\t\t}\n\n\t\tthis.supported = false;\n\t\treturn `\\\\${command}`;\n\t}\n\n\tprivate parseOperator(\n\t\toperator: string,\n\t\tinlineLowerStyle: \"bracket\" | \"script\",\n\t\tdisplayLimits: boolean,\n\t\tspaced = false,\n\t): string {\n\t\tlet useDisplayLimits = displayLimits;\n\t\tlet modifierPosition = this.position;\n\t\twhile (modifierPosition < this.source.length && /[ \\t]/.test(this.source[modifierPosition] ?? \"\")) {\n\t\t\tmodifierPosition++;\n\t\t}\n\t\tconst modifier = /^\\\\(limits|nolimits)(?![A-Za-z])/.exec(this.source.slice(modifierPosition));\n\t\tif (modifier) {\n\t\t\tuseDisplayLimits = modifier[1] === \"limits\";\n\t\t\tthis.position = modifierPosition + modifier[0].length;\n\t\t}\n\n\t\tlet lower: string | undefined;\n\t\tlet upper: string | undefined;\n\t\twhile (true) {\n\t\t\tlet scriptPosition = this.position;\n\t\t\twhile (scriptPosition < this.source.length && /[ \\t]/.test(this.source[scriptPosition] ?? \"\")) {\n\t\t\t\tscriptPosition++;\n\t\t\t}\n\t\t\tconst kind = this.source[scriptPosition];\n\t\t\tif (kind !== \"_\" && kind !== \"^\") {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tthis.position = scriptPosition + 1;\n\t\t\tconst value = normalizeOutput(this.parseRequiredArgument(false)).replaceAll(\" \", \"\");\n\t\t\tif (kind === \"_\") {\n\t\t\t\tif (lower !== undefined) {\n\t\t\t\t\tthis.supported = false;\n\t\t\t\t}\n\t\t\t\tlower = value;\n\t\t\t} else {\n\t\t\t\tif (upper !== undefined) {\n\t\t\t\t\tthis.supported = false;\n\t\t\t\t}\n\t\t\t\tupper = value;\n\t\t\t}\n\t\t}\n\n\t\tif (this.display && useDisplayLimits && (lower !== undefined || upper !== undefined)) {\n\t\t\tconst index = this.layoutNodes.push({ type: \"operator\", operator, lower, upper }) - 1;\n\t\t\treturn `${LAYOUT_MARKER_START}${index}${LAYOUT_MARKER_END}`;\n\t\t}\n\n\t\tlet rendered = operator;\n\t\tif (lower !== undefined) {\n\t\t\trendered += inlineLowerStyle === \"bracket\" ? `[${lower}]` : formatScript(lower, \"sub\");\n\t\t}\n\t\tif (upper !== undefined) {\n\t\t\trendered += formatScript(upper, \"sup\");\n\t\t}\n\t\treturn spaced ? ` ${rendered} ` : rendered;\n\t}\n\n\tprivate parseRequiredArgument(stackFractions = true): string {\n\t\tconst previousStackFractions = this.stackFractions;\n\t\tthis.stackFractions = previousStackFractions && stackFractions;\n\t\tconst value = this.parseRequiredArgumentValue();\n\t\tthis.stackFractions = previousStackFractions;\n\t\treturn value;\n\t}\n\n\tprivate parseRequiredArgumentValue(): string {\n\t\twhile (this.position < this.source.length && /\\s/.test(this.source[this.position] ?? \"\")) {\n\t\t\tthis.position++;\n\t\t}\n\t\tif (this.position >= this.source.length) {\n\t\t\tthis.supported = false;\n\t\t\treturn \"\";\n\t\t}\n\t\tif (this.source[this.position] === \"{\") {\n\t\t\tthis.position++;\n\t\t\treturn this.parseSequence(\"}\");\n\t\t}\n\t\tif (this.source[this.position] === \"\\\\\") {\n\t\t\treturn this.parseCommand();\n\t\t}\n\t\tconst value = this.source[this.position] ?? \"\";\n\t\tthis.position++;\n\t\treturn value;\n\t}\n\n\tprivate parseOptionalArgument(): string | undefined {\n\t\twhile (this.position < this.source.length && /[ \\t]/.test(this.source[this.position] ?? \"\")) {\n\t\t\tthis.position++;\n\t\t}\n\t\tif (this.source[this.position] !== \"[\") {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst end = this.source.indexOf(\"]\", this.position + 1);\n\t\tif (end < 0) {\n\t\t\tthis.supported = false;\n\t\t\treturn undefined;\n\t\t}\n\t\tconst value = this.source.slice(this.position + 1, end);\n\t\tthis.position = end + 1;\n\t\treturn this.renderNested(value);\n\t}\n\n\tprivate readRawGroup(): string | undefined {\n\t\twhile (this.position < this.source.length && /[ \\t]/.test(this.source[this.position] ?? \"\")) {\n\t\t\tthis.position++;\n\t\t}\n\t\tif (this.source[this.position] !== \"{\") {\n\t\t\tthis.supported = false;\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst start = ++this.position;\n\t\tlet depth = 1;\n\t\twhile (this.position < this.source.length) {\n\t\t\tconst character = this.source[this.position];\n\t\t\tif (character === \"\\\\\") {\n\t\t\t\tthis.position += 2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (character === \"{\") depth++;\n\t\t\tif (character === \"}\") depth--;\n\t\t\tif (depth === 0) {\n\t\t\t\tconst value = this.source.slice(start, this.position);\n\t\t\t\tthis.position++;\n\t\t\t\treturn value;\n\t\t\t}\n\t\t\tthis.position++;\n\t\t}\n\t\tthis.supported = false;\n\t\treturn undefined;\n\t}\n\n\tprivate splitEnvironmentRows(body: string): string[] {\n\t\treturn body.split(/\\\\\\\\(?:\\[[^\\]\\n]*\\])?/);\n\t}\n\n\tprivate parseEnvironment(): string {\n\t\tconst environment = this.readRawGroup();\n\t\tif (!environment) {\n\t\t\treturn \"\";\n\t\t}\n\t\tconst endMarker = `\\\\end{${environment}}`;\n\t\tconst end = this.source.indexOf(endMarker, this.position);\n\t\tif (end < 0) {\n\t\t\tthis.supported = false;\n\t\t\treturn \"\";\n\t\t}\n\t\tconst body = this.source.slice(this.position, end);\n\t\tthis.position = end + endMarker.length;\n\n\t\tif (environment === \"equation\" || environment === \"equation*\" || environment === \"displaymath\") {\n\t\t\treturn this.renderNested(body).trim();\n\t\t}\n\n\t\tif (\n\t\t\tenvironment === \"aligned\" ||\n\t\t\tenvironment === \"align\" ||\n\t\t\tenvironment === \"align*\" ||\n\t\t\tenvironment === \"alignedat\" ||\n\t\t\tenvironment === \"alignat\" ||\n\t\t\tenvironment === \"alignat*\" ||\n\t\t\tenvironment === \"gather\" ||\n\t\t\tenvironment === \"gathered\" ||\n\t\t\tenvironment === \"multline\" ||\n\t\t\tenvironment === \"multline*\" ||\n\t\t\tenvironment === \"split\"\n\t\t) {\n\t\t\tconst alignedAt = [\"alignedat\", \"alignat\", \"alignat*\"].includes(environment);\n\t\t\tconst alignedBody = alignedAt ? body.replace(/^\\s*\\{[^}]*\\}/, \"\") : body;\n\t\t\treturn this.splitEnvironmentRows(alignedBody)\n\t\t\t\t.map((row) => {\n\t\t\t\t\tconst cells = row.split(\"&\");\n\t\t\t\t\tconst source = alignedAt\n\t\t\t\t\t\t? Array.from({ length: Math.ceil(cells.length / 2) }, (_, index) =>\n\t\t\t\t\t\t\t\tcells.slice(index * 2, index * 2 + 2).join(\"\"),\n\t\t\t\t\t\t\t).join(\" \")\n\t\t\t\t\t\t: cells.join(\"\");\n\t\t\t\t\treturn this.renderNested(source).trim();\n\t\t\t\t})\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\"\\n\");\n\t\t}\n\n\t\tif (environment === \"cases\" || environment === \"cases*\") {\n\t\t\tconst rows = this.splitEnvironmentRows(body)\n\t\t\t\t.map((row) => row.split(\"&\").map((cell) => this.renderNested(cell, false).trim()))\n\t\t\t\t.filter((row) => row.some(Boolean));\n\t\t\treturn rows\n\t\t\t\t.map((row, index) => {\n\t\t\t\t\tconst value = (row[0] ?? \"\").replace(/,\\s*$/, \"\");\n\t\t\t\t\tconst condition = row[1] ?? \"\";\n\t\t\t\t\tconst delimiter = index === 0 ? \"⎧\" : index === rows.length - 1 ? \"⎩\" : \"⎨\";\n\t\t\t\t\tconst conditionPrefix = /^(?:if|when|for|otherwise)\\b/i.test(condition) ? \" \" : \" if \";\n\t\t\t\t\treturn `${delimiter} ${value}${condition ? `${conditionPrefix}${condition}` : \"\"}`;\n\t\t\t\t})\n\t\t\t\t.join(\"\\n\");\n\t\t}\n\n\t\tif (\n\t\t\t[\"array\", \"matrix\", \"smallmatrix\", \"pmatrix\", \"bmatrix\", \"Bmatrix\", \"vmatrix\", \"Vmatrix\"].includes(environment)\n\t\t) {\n\t\t\tconst matrixBody = environment === \"array\" ? body.replace(/^\\s*\\{[^}]*\\}/, \"\") : body;\n\t\t\treturn this.renderMatrix(environment, matrixBody);\n\t\t}\n\n\t\tthis.supported = false;\n\t\treturn body;\n\t}\n\n\tprivate renderMatrix(environment: string, body: string): string {\n\t\tconst matrix = this.splitEnvironmentRows(body)\n\t\t\t.map((row) => row.split(\"&\").map((cell) => this.renderNested(cell, false).trim()))\n\t\t\t.filter((row) => row.some(Boolean));\n\t\tconst columnCount = Math.max(0, ...matrix.map((row) => row.length));\n\t\tconst columnWidths = Array.from({ length: columnCount }, (_, column) =>\n\t\t\tMath.max(0, ...matrix.map((row) => visibleWidth(row[column] ?? \"\"))),\n\t\t);\n\t\tconst rows = matrix.map((row) =>\n\t\t\tArray.from({ length: columnCount }, (_, column) => {\n\t\t\t\tconst cell = row[column] ?? \"\";\n\t\t\t\treturn `${cell}${PROTECTED_SPACE.repeat(Math.max(0, (columnWidths[column] ?? 0) - visibleWidth(cell)))}`;\n\t\t\t}).join(\" │ \"),\n\t\t);\n\n\t\tlet lines: string[];\n\t\tif (environment === \"array\" || environment === \"matrix\" || environment === \"smallmatrix\") {\n\t\t\tlines = rows;\n\t\t} else {\n\t\t\tconst delimiters: Readonly<Record<string, readonly [string, string, string, string, string, string]>> = {\n\t\t\t\tpmatrix: [\"⎛\", \"⎞\", \"⎜\", \"⎟\", \"⎝\", \"⎠\"],\n\t\t\t\tbmatrix: [\"⎡\", \"⎤\", \"⎢\", \"⎥\", \"⎣\", \"⎦\"],\n\t\t\t\tBmatrix: [\"⎧\", \"⎫\", \"⎨\", \"⎬\", \"⎩\", \"⎭\"],\n\t\t\t\tvmatrix: [\"│\", \"│\", \"│\", \"│\", \"│\", \"│\"],\n\t\t\t\tVmatrix: [\"║\", \"║\", \"║\", \"║\", \"║\", \"║\"],\n\t\t\t};\n\t\t\tconst delimiter = delimiters[environment];\n\t\t\tif (!delimiter) {\n\t\t\t\tthis.supported = false;\n\t\t\t\treturn rows.join(\"\\n\");\n\t\t\t}\n\t\t\tlines = rows.map((row, index) => {\n\t\t\t\tconst left = index === 0 ? delimiter[0] : index === rows.length - 1 ? delimiter[4] : delimiter[2];\n\t\t\t\tconst right = index === 0 ? delimiter[1] : index === rows.length - 1 ? delimiter[5] : delimiter[3];\n\t\t\t\treturn `${left} ${row} ${right}`;\n\t\t\t});\n\t\t}\n\n\t\tif (lines.length <= 1) {\n\t\t\treturn lines[0] ?? \"\";\n\t\t}\n\t\tconst index = this.layoutNodes.push({ type: \"matrix\", lines, baseline: 0 }) - 1;\n\t\treturn `${LAYOUT_MARKER_START}${index}${LAYOUT_MARKER_END}`;\n\t}\n\n\tprivate renderNested(source: string, stackFractions = true): string {\n\t\tconst rendered = new LatexParser(source, this.layoutNodes, this.display && stackFractions).render();\n\t\tif (rendered === undefined) {\n\t\t\tthis.supported = false;\n\t\t\treturn source;\n\t\t}\n\t\treturn rendered;\n\t}\n}\n\nexport interface RenderLatexOptions {\n\t/** Stack fractions and operator limits vertically for display math (default: false). */\n\tdisplay?: boolean;\n}\n\n/**\n * Render a basic LaTeX math expression as terminal-friendly Unicode text.\n * Returns undefined when the expression contains unsupported or malformed syntax.\n */\nexport function renderLatex(source: string, options: RenderLatexOptions = {}): string | undefined {\n\tconst layoutNodes: LayoutNode[] = [];\n\tconst rendered = new LatexParser(source, layoutNodes, options.display === true).render();\n\tif (rendered === undefined) {\n\t\treturn undefined;\n\t}\n\tif (layoutNodes.length === 0) {\n\t\treturn rendered.replaceAll(PROTECTED_SPACE, \" \");\n\t}\n\tconst lines = renderLayout(rendered, layoutNodes).lines;\n\tconst indentation = Math.min(\n\t\t...lines.filter((line) => line.trim()).map((line) => line.length - line.trimStart().length),\n\t);\n\treturn lines\n\t\t.map((line) => line.slice(indentation).trimEnd())\n\t\t.join(\"\\n\")\n\t\t.trimEnd()\n\t\t.replaceAll(PROTECTED_SPACE, \" \");\n}\n"]}
|
package/dist/latex.js
CHANGED
|
@@ -62,6 +62,13 @@ const SYMBOLS = {
|
|
|
62
62
|
uplus: "⊎",
|
|
63
63
|
sqcap: "⊓",
|
|
64
64
|
sqcup: "⊔",
|
|
65
|
+
bowtie: "⋈",
|
|
66
|
+
Join: "⋈",
|
|
67
|
+
ltimes: "⋉",
|
|
68
|
+
rtimes: "⋊",
|
|
69
|
+
leftouterjoin: "⟕",
|
|
70
|
+
rightouterjoin: "⟖",
|
|
71
|
+
fullouterjoin: "⟗",
|
|
65
72
|
triangleleft: "◁",
|
|
66
73
|
triangleright: "▷",
|
|
67
74
|
wr: "≀",
|
|
@@ -290,12 +297,15 @@ const RELATION_COMMANDS = new Set([
|
|
|
290
297
|
"Longleftrightarrow",
|
|
291
298
|
"Longrightarrow",
|
|
292
299
|
"Rightarrow",
|
|
300
|
+
"Join",
|
|
293
301
|
"Vdash",
|
|
294
302
|
"Vvdash",
|
|
295
303
|
"approx",
|
|
296
304
|
"asymp",
|
|
305
|
+
"bowtie",
|
|
297
306
|
"cong",
|
|
298
307
|
"dashv",
|
|
308
|
+
"fullouterjoin",
|
|
299
309
|
"doteq",
|
|
300
310
|
"downarrow",
|
|
301
311
|
"equiv",
|
|
@@ -316,6 +326,7 @@ const RELATION_COMMANDS = new Set([
|
|
|
316
326
|
"leftharpoonup",
|
|
317
327
|
"leftrightarrow",
|
|
318
328
|
"leftrightharpoons",
|
|
329
|
+
"leftouterjoin",
|
|
319
330
|
"leq",
|
|
320
331
|
"leqslant",
|
|
321
332
|
"ll",
|
|
@@ -323,6 +334,7 @@ const RELATION_COMMANDS = new Set([
|
|
|
323
334
|
"longleftrightarrow",
|
|
324
335
|
"longmapsto",
|
|
325
336
|
"longrightarrow",
|
|
337
|
+
"ltimes",
|
|
326
338
|
"mapsto",
|
|
327
339
|
"mid",
|
|
328
340
|
"models",
|
|
@@ -342,8 +354,10 @@ const RELATION_COMMANDS = new Set([
|
|
|
342
354
|
"rightharpoondown",
|
|
343
355
|
"rightharpoonup",
|
|
344
356
|
"rightleftharpoons",
|
|
357
|
+
"rightouterjoin",
|
|
345
358
|
"rightarrow",
|
|
346
359
|
"rightsquigarrow",
|
|
360
|
+
"rtimes",
|
|
347
361
|
"searrow",
|
|
348
362
|
"sim",
|
|
349
363
|
"simeq",
|