├── .prettierrc.json ├── .prettierignore ├── docs ├── google85979ffadaf6ad3d.html ├── assets │ ├── out-Dnx8NhnY.webm │ ├── main-BVujPvFW.css │ ├── main-bp1Lx8IY.js │ └── main-bp1Lx8IY.js.map └── index.html ├── docs-src ├── google85979ffadaf6ad3d.html ├── assets │ ├── out.webm │ ├── texuni800.webm │ └── GitHub-Mark-64px.png ├── main.ts ├── main.css └── index.html ├── .gitignore ├── extension ├── public │ ├── assets │ │ ├── icon128.png │ │ ├── icon48.png │ │ ├── popup-howto.gif │ │ └── icon.svg │ ├── background.js │ ├── manifest.json │ ├── popup.js │ └── popup.html ├── webstore-assets │ ├── screenshot-1.png │ ├── promotional-tile.png │ └── screenshot-720x450.webm └── render.ts ├── example └── example.html ├── vite.config.docs.ts ├── .github └── workflows │ └── main.yaml ├── README.md ├── vite.config.extension.ts ├── History.md ├── tsconfig.json ├── lib ├── tree.test.ts ├── tree.ts ├── index.spec.ts ├── parser.ts ├── index.ts └── symbols.ts ├── LICENSE └── package.json /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100 3 | } 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | extension/dist 2 | docs/ 3 | docs/index.html 4 | coverage 5 | .vscode 6 | -------------------------------------------------------------------------------- /docs/google85979ffadaf6ad3d.html: -------------------------------------------------------------------------------- 1 | google-site-verification: google85979ffadaf6ad3d.html 2 | -------------------------------------------------------------------------------- /docs-src/google85979ffadaf6ad3d.html: -------------------------------------------------------------------------------- 1 | google-site-verification: google85979ffadaf6ad3d.html 2 | -------------------------------------------------------------------------------- /docs-src/assets/out.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golopot/tex-to-unicode/HEAD/docs-src/assets/out.webm -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | .vscode 4 | extension/dist/ 5 | extension/dist.zip 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /docs-src/assets/texuni800.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golopot/tex-to-unicode/HEAD/docs-src/assets/texuni800.webm -------------------------------------------------------------------------------- /docs/assets/out-Dnx8NhnY.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golopot/tex-to-unicode/HEAD/docs/assets/out-Dnx8NhnY.webm -------------------------------------------------------------------------------- /extension/public/assets/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golopot/tex-to-unicode/HEAD/extension/public/assets/icon128.png -------------------------------------------------------------------------------- /extension/public/assets/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golopot/tex-to-unicode/HEAD/extension/public/assets/icon48.png -------------------------------------------------------------------------------- /docs-src/assets/GitHub-Mark-64px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golopot/tex-to-unicode/HEAD/docs-src/assets/GitHub-Mark-64px.png -------------------------------------------------------------------------------- /extension/public/assets/popup-howto.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golopot/tex-to-unicode/HEAD/extension/public/assets/popup-howto.gif -------------------------------------------------------------------------------- /extension/webstore-assets/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golopot/tex-to-unicode/HEAD/extension/webstore-assets/screenshot-1.png -------------------------------------------------------------------------------- /extension/webstore-assets/promotional-tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golopot/tex-to-unicode/HEAD/extension/webstore-assets/promotional-tile.png -------------------------------------------------------------------------------- /extension/webstore-assets/screenshot-720x450.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golopot/tex-to-unicode/HEAD/extension/webstore-assets/screenshot-720x450.webm -------------------------------------------------------------------------------- /example/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |
\alpha \to \beta \to \gamma
6 |
\int (\partial f / \partial\tau) d \tau
7 |
8 | 9 | 10 | -------------------------------------------------------------------------------- /extension/render.ts: -------------------------------------------------------------------------------- 1 | import { render } from "../lib/index.js"; 2 | import type { Options } from "../lib/index.js"; 3 | 4 | chrome.storage.local.get(["options"], (result: { options?: Options }) => { 5 | if (document.activeElement) { 6 | render(document.activeElement as HTMLElement, { 7 | subscripts: result.options?.subscripts ?? true, 8 | }); 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /vite.config.docs.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import { resolve } from "path"; 3 | 4 | export default defineConfig({ 5 | root: "docs-src", 6 | build: { 7 | outDir: "../docs", 8 | emptyOutDir: true, 9 | rollupOptions: { 10 | input: { 11 | main: resolve(__dirname, "docs-src/index.html"), 12 | }, 13 | }, 14 | sourcemap: true, 15 | target: "es2022", 16 | }, 17 | base: "/tex-to-unicode/", 18 | }); 19 | -------------------------------------------------------------------------------- /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | - push 4 | - pull_request 5 | jobs: 6 | test: 7 | name: Node.js ${{ matrix.node-version }} 8 | runs-on: ubuntu-latest 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | node-version: 13 | - 24 14 | steps: 15 | - uses: actions/checkout@v2 16 | - uses: actions/setup-node@v2 17 | with: 18 | node-version: ${{ matrix.node-version }} 19 | - run: npm install 20 | - run: npm run ci 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TeX to Unicode 2 | 3 | Convert LaTeX to Unicode by pressing "Alt + W" in input and textarea. 4 | 5 | This extension helps you input mathematical Unicode symbols and Greek letters without copy-pasting. 6 | 7 | Website: https://chiawendt.github.io/tex-to-unicode/ 8 | 9 | Chrome Extension: https://chrome.google.com/webstore/detail/tex-to-unicode/kdojinhjlhbmmbfogccabogomjpjijif 10 | 11 | Firefox Extension: https://addons.mozilla.org/en-US/firefox/addon/tex-to-unicode/ 12 | 13 | ## License 14 | 15 | [MIT](LICENSE) 16 | -------------------------------------------------------------------------------- /extension/public/background.js: -------------------------------------------------------------------------------- 1 | chrome.commands.onCommand.addListener(async (command) => { 2 | if (command === "convert") { 3 | const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); 4 | if (!tab.id) { 5 | return; 6 | } 7 | chrome.scripting.executeScript({ 8 | target: { tabId: tab.id }, 9 | files: ["/render.js"], 10 | }); 11 | } 12 | }); 13 | 14 | chrome.runtime.onInstalled.addListener(() => { 15 | chrome.storage.local.set({ options: { subscripts: true } }); 16 | }); 17 | -------------------------------------------------------------------------------- /vite.config.extension.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import { resolve } from "path"; 3 | 4 | export default defineConfig({ 5 | publicDir: "./extension/public", 6 | build: { 7 | outDir: "./extension/dist", 8 | emptyOutDir: true, 9 | rollupOptions: { 10 | input: { 11 | main: resolve(__dirname, "extension/render.ts"), 12 | }, 13 | output: { 14 | entryFileNames: "render.js", 15 | format: "iife", 16 | }, 17 | }, 18 | sourcemap: true, 19 | minify: false, 20 | target: "es2022", 21 | }, 22 | }); 23 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.7.0 4 | 5 | - Refactors 6 | - Change subscript option default to true 7 | 8 | ## 1.6.4 9 | 10 | - Change link url. 11 | 12 | ## 1.3.0 / 2019-06-13 13 | 14 | - Add iota (#9)(Quelklef) 15 | 16 | ## 1.2.1 / 2018-01-07 17 | 18 | - Make convert in textarea ctrl-z-undoable 19 | 20 | ## 1.2.0 21 | 22 | - Make option "subscript" non-default 23 | - Fix parsing error 24 | - Add a link to change shortcut in web extension 25 | 26 | ## 1.1.0 / 2018-07-20 27 | 28 | - Added conversion of subscripts and superscripts 29 | - Changed; Rewrite parser with Parsimmon 30 | 31 | ## 0.0.1 / 2018-01-26 32 | 33 | - First commit 34 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "lib": ["ES2022", "DOM"], 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "types": ["chrome", "node", "jest"], 8 | "declaration": true, 9 | "declarationMap": true, 10 | "noEmit": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "strict": true, 15 | "noImplicitAny": false, 16 | "strictNullChecks": true, 17 | "skipLibCheck": true, 18 | "resolveJsonModule": true, 19 | "isolatedModules": true 20 | }, 21 | "include": ["lib", "extension"] 22 | } 23 | -------------------------------------------------------------------------------- /extension/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "version": "1.7.0", 4 | "name": "TeX to Unicode", 5 | "description": "Convert LaTeX to Unicode by pressing 'Alt + W' ('Control + W' for Mac) in browser inputs.", 6 | "action": { 7 | "default_title": "TeX to Unicode", 8 | "default_popup": "popup.html" 9 | }, 10 | "background": { 11 | "service_worker": "background.js" 12 | }, 13 | "commands": { 14 | "convert": { 15 | "suggested_key": { 16 | "default": "Alt+W", 17 | "mac": "MacCtrl+W" 18 | }, 19 | "description": "Convert selected TeX to unicode." 20 | } 21 | }, 22 | "icons": { 23 | "48": "/assets/icon48.png", 24 | "128": "/assets/icon128.png" 25 | }, 26 | "permissions": ["activeTab", "storage", "scripting"] 27 | } 28 | -------------------------------------------------------------------------------- /lib/tree.test.ts: -------------------------------------------------------------------------------- 1 | import * as tree from "./tree.js"; 2 | 3 | describe("Tree algorithms", () => { 4 | const Node = function (...args) { 5 | const res = { childNodes: Array.from(args) }; 6 | for (const node of args) { 7 | node.parentNode = res; 8 | } 9 | return res; 10 | }; 11 | 12 | test("findNodesBetweenNodes", () => { 13 | const u = { nodeValue: "u" } as Node; 14 | const v = { nodeValue: "v" } as Node; 15 | 16 | Node(Node(u, Node()), Node(), Node(Node(), v)); 17 | 18 | const nodes = tree.findNodesBetweenNodes(u, v); 19 | 20 | expect(nodes).toHaveLength(6); 21 | 22 | expect(nodes[0]).toBe(u); 23 | 24 | expect(nodes[5]).toBe(v); 25 | }); 26 | 27 | test("findLowestCommonAncestor", () => { 28 | const u = { nodeValue: "u" } as Node; 29 | const v = { nodeValue: "v" } as Node; 30 | 31 | const node = Node(Node(u), Node(Node(v))); 32 | 33 | expect(tree.findLowestCommonAncestor(u, v)).toBe(node); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /extension/public/popup.js: -------------------------------------------------------------------------------- 1 | chrome.storage.local.get(["options"], ({ options }) => { 2 | Array.from(document.querySelectorAll(".options input")).map((input) => { 3 | if (input.name === "subscripts") { 4 | input.checked = !!options?.subscripts; 5 | } 6 | }); 7 | }); 8 | 9 | chrome.commands.getAll((commands) => { 10 | for (const e of Array.from(document.querySelectorAll(".shortcut"))) { 11 | e.innerHTML = commands[1].shortcut || "unset"; 12 | } 13 | }); 14 | 15 | document.querySelector("body").addEventListener("click", (event) => { 16 | if (event.target.tagName === "A") { 17 | chrome.tabs.create({ url: event.target.href }); 18 | return false; 19 | } 20 | }); 21 | 22 | document.querySelector(".options").addEventListener("input", () => { 23 | console.log(getOptions()); 24 | chrome.storage.local.set({ options: getOptions() }); 25 | }); 26 | 27 | function getOptions() { 28 | return { 29 | subscripts: document.querySelector(".options [name=subscripts]").checked, 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /docs-src/main.ts: -------------------------------------------------------------------------------- 1 | import "./main.css"; 2 | import * as TexToUnicode from "../lib/index.js"; 3 | 4 | document.addEventListener("DOMContentLoaded", () => { 5 | // Initialize the symbol table 6 | function renderSymbols() { 7 | const symbols = TexToUnicode.symbols; 8 | return Object.entries(symbols) 9 | .map( 10 | ([a, b]) => 11 | `
12 | ${a} 13 | ${b} 14 |
`, 15 | ) 16 | .join("\n"); 17 | } 18 | 19 | const symbolTable = document.querySelector(".symbol-table"); 20 | if (symbolTable) { 21 | symbolTable.innerHTML = renderSymbols(); 22 | } 23 | 24 | // Setup the textarea demo 25 | const textarea = document.querySelector(".try-here") as HTMLTextAreaElement; 26 | if (textarea) { 27 | textarea.addEventListener("keydown", (ev) => { 28 | if (ev.key === "w" && (ev.altKey === true || ev.ctrlKey)) { 29 | TexToUnicode.render(textarea, { subscripts: true }); 30 | } 31 | }); 32 | } 33 | }); 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Chiawen Chen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /lib/tree.ts: -------------------------------------------------------------------------------- 1 | /** Used to find all DOM nodes in window.getSelection() */ 2 | export function findNodesBetweenNodes(u: Node, v: Node): Node[] { 3 | const ancestor = findLowestCommonAncestor(u, v); 4 | const childrenList = findChildrenList(ancestor); 5 | const [i, j] = [childrenList.indexOf(u), childrenList.indexOf(v)].sort(); 6 | return childrenList.slice(i, j + 1); 7 | } 8 | 9 | function findAncestorChain(node: Node): Node[] { 10 | const chain: Node[] = []; 11 | let currentNode: Node | null = node; 12 | chain.push(currentNode); 13 | while (currentNode?.parentNode) { 14 | currentNode = currentNode.parentNode; 15 | chain.push(currentNode); 16 | } 17 | return chain.reverse(); 18 | } 19 | 20 | export function findLowestCommonAncestor(u: Node, v: Node): Node { 21 | const uChain = findAncestorChain(u); 22 | const vChain = findAncestorChain(v); 23 | 24 | let i = 0; 25 | for (; i < uChain.length; i++) { 26 | if (uChain[i] !== vChain[i]) { 27 | break; 28 | } 29 | } 30 | return uChain[i - 1]; 31 | } 32 | 33 | function findChildrenList(node: Node): Node[] { 34 | const list: Node[] = []; 35 | const find = (n: Node | null) => { 36 | if (!n) return; 37 | list.push(n); 38 | for (const child of Array.from(n.childNodes || [])) { 39 | find(child); 40 | } 41 | }; 42 | find(node); 43 | return list; 44 | } 45 | -------------------------------------------------------------------------------- /docs/assets/main-BVujPvFW.css: -------------------------------------------------------------------------------- 1 | body{font-family:Montserrat,sans-serif;font-size:16px;max-width:960px;margin:auto;padding-top:16px;padding-bottom:16px}header{display:flex;justify-content:space-between;font-size:20px;padding:0 8px}h1{font-size:30px;line-height:1.4em;margin:60px 0 32px}h2{font-size:20px}kbd{transform:translateY(-1px);display:inline-block;margin:0 .1em;padding:.1em .6em;font-family:inherit;font-size:11px;line-height:1.5;color:#232629;text-shadow:0 1px 0 white;background-color:#e3e6e8;border:1px solid hsl(210,8%,65%);border-radius:3px;box-shadow:0 1px 1px #0c0d0e26,inset 0 1px #fff;overflow-wrap:break-word}ul{list-style-type:none}ul li{margin-bottom:1.625rem}ul li:before{content:"-";color:#999;position:absolute;margin-left:-1rem}.text-center{text-align:center}.container{display:flex;justify-content:center}.container video{max-width:100%}.getting-started-button{color:inherit;line-height:24px;text-decoration:none;border-radius:8px;display:flex;align-items:center;background-color:#f3b746;padding:6px}.getting-started-button>*:not(:last-child){margin-right:4px}.mt-40{margin-top:40px}.mt-30{margin-top:30px}.try-here{width:516px;height:120px}.symbol-table{display:flex;justify-content:center;column-gap:32px;flex-wrap:wrap}.symbol-entry{display:flex;justify-content:space-between;width:200px;font-family:Roboto Mono,monospace}td:nth-child(2n){width:20px;text-align:right}td:nth-child(odd){padding-left:20px} 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tex-to-unicode", 3 | "type": "module", 4 | "private": true, 5 | "description": "", 6 | "license": "MIT", 7 | "repository": "chiawendt/tex-to-unicode", 8 | "author": { 9 | "name": "Chiawen Chen", 10 | "email": "golopot@gmail.com" 11 | }, 12 | "scripts": { 13 | "dev": "vite build --config vite.config.extension.ts --watch", 14 | "dev:docs": "vite --config vite.config.docs.ts", 15 | "build": "npm run clean && npm run build:extension && npm run build:docs", 16 | "build:extension": "vite build --config vite.config.extension.ts && cd extension/dist && zip -rFS ../dist.zip *", 17 | "build:docs": "vite build --config vite.config.docs.ts && cp docs-src/google85979ffadaf6ad3d.html docs/", 18 | "clean": "rm -rf extension/dist extension/dist.zip docs/*", 19 | "prettier": "prettier --check .", 20 | "tsc": "tsc", 21 | "test": "jest --coverage", 22 | "ci": "npm run prettier && npm run tsc && npm run test && npm run build" 23 | }, 24 | "dependencies": { 25 | "bread-n-butter": "^0.6.0" 26 | }, 27 | "devDependencies": { 28 | "@types/chrome": "^0.0.176", 29 | "@types/jest": "^30.0.0", 30 | "@types/node": "^22.0.0", 31 | "jest": "^30.0.5", 32 | "jest-environment-jsdom": "^30.0.5", 33 | "prettier": "^3.6.2", 34 | "ts-jest": "^29.4.1", 35 | "typescript": "^5.9.2", 36 | "vite": "^7.1.3" 37 | }, 38 | "engines": { 39 | "node": ">=24.6.0" 40 | }, 41 | "jest": { 42 | "preset": "ts-jest/presets/default-esm", 43 | "testEnvironment": "jsdom", 44 | "extensionsToTreatAsEsm": [ 45 | ".ts" 46 | ], 47 | "moduleNameMapper": { 48 | "^(\\.{1,2}/.*)\\.js$": "$1" 49 | }, 50 | "transform": { 51 | "^.+\\.ts$": [ 52 | "ts-jest", 53 | { 54 | "useESM": true 55 | } 56 | ] 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /extension/public/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Popup 5 | 43 | 44 | 45 |

TeX to Unicode

46 |

47 | Move caret to the TeX symbol and press 48 | Alt + w to convert. 49 |

50 | 51 |

Select all if you want to convert all.

52 | 53 |

Options

54 |
55 |
56 | Shortcut: 57 |
58 |
59 | 60 | 61 |
62 |
63 | 64 |
65 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /docs-src/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Montserrat", sans-serif; 3 | font-size: 16px; 4 | max-width: 960px; 5 | margin: auto; 6 | padding-top: 16px; 7 | padding-bottom: 16px; 8 | } 9 | 10 | header { 11 | display: flex; 12 | justify-content: space-between; 13 | font-size: 20px; 14 | padding: 0 8px; 15 | } 16 | 17 | h1 { 18 | font-size: 30px; 19 | line-height: 1.4em; 20 | margin: 60px 0 32px; 21 | } 22 | h2 { 23 | font-size: 20px; 24 | } 25 | 26 | kbd { 27 | transform: translate(0, -1px); 28 | display: inline-block; 29 | margin: 0 0.1em; 30 | padding: 0.1em 0.6em; 31 | font-family: inherit; 32 | font-size: 11px; 33 | line-height: 1.5; 34 | color: hsl(210, 8%, 15%); 35 | text-shadow: 0 1px 0 white; 36 | background-color: hsl(210, 8%, 90%); 37 | border: 1px solid hsl(210, 8%, 65%); 38 | border-radius: 3px; 39 | box-shadow: 40 | 0 1px 1px hsl(210deg 8% 5% / 15%), 41 | inset 0 1px 0 0 hsl(0deg 0% 100%); 42 | overflow-wrap: break-word; 43 | } 44 | 45 | ul { 46 | list-style-type: none; 47 | } 48 | 49 | ul li { 50 | margin-bottom: 1.625rem; 51 | } 52 | 53 | ul li:before { 54 | content: "-"; 55 | color: #999; 56 | position: absolute; 57 | margin-left: -1rem; 58 | } 59 | 60 | .text-center { 61 | text-align: center; 62 | } 63 | .container { 64 | display: flex; 65 | justify-content: center; 66 | } 67 | 68 | .container video { 69 | max-width: 100%; 70 | } 71 | 72 | .getting-started-button { 73 | color: inherit; 74 | line-height: 24px; 75 | text-decoration: none; 76 | border-radius: 8px; 77 | display: flex; 78 | align-items: center; 79 | background-color: rgba(243, 183, 70, 1); 80 | padding: 6px 6px; 81 | } 82 | 83 | .getting-started-button > *:not(:last-child) { 84 | margin-right: 4px; 85 | } 86 | 87 | .mt-40 { 88 | margin-top: 40px; 89 | } 90 | 91 | .mt-30 { 92 | margin-top: 30px; 93 | } 94 | 95 | .try-here { 96 | width: 516px; 97 | height: 120px; 98 | } 99 | 100 | .symbol-table { 101 | display: flex; 102 | justify-content: center; 103 | column-gap: 32px; 104 | flex-wrap: wrap; 105 | } 106 | 107 | .symbol-entry { 108 | display: flex; 109 | justify-content: space-between; 110 | width: 200px; 111 | font-family: "Roboto Mono", monospace; 112 | } 113 | 114 | td:nth-child(2n) { 115 | width: 20px; 116 | text-align: right; 117 | } 118 | 119 | td:nth-child(2n + 1) { 120 | padding-left: 20px; 121 | } 122 | -------------------------------------------------------------------------------- /extension/public/assets/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 24 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 68 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /lib/index.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @jest-environment jsdom 3 | */ 4 | /* eslint-env jest */ 5 | 6 | import { convert, parser, render } from "./index.js"; 7 | 8 | const testCases = [ 9 | { 10 | tex: "a \\alpha, b \\beta", 11 | out: "a α, b \\beta", 12 | selection: [0, 4], 13 | cursor: 3, 14 | }, 15 | { 16 | tex: "\\frac{\\alpha}{\\beta}", 17 | out: "\\frac{α}{β}", 18 | selection: [0, 20], 19 | cursor: 11, 20 | }, 21 | { 22 | tex: "a \\in \\mathbb{R}", 23 | out: "a ∈ ℝ", 24 | selection: [0, 16], 25 | cursor: 5, 26 | }, 27 | { 28 | tex: "\\not\\equiv", 29 | out: "≢", 30 | selection: [10, 10], 31 | cursor: 1, 32 | }, 33 | { 34 | tex: "x_1^{abc}", 35 | out: "x₁ᵃᵇᶜ", 36 | selection: [0, 9], 37 | cursor: 5, 38 | options: { 39 | subscripts: true, 40 | }, 41 | }, 42 | { 43 | tex: "x_1^{abc}", 44 | out: "x_1^{abc}", 45 | options: { 46 | subscripts: false, 47 | }, 48 | }, 49 | { 50 | tex: "x_\\alpha", 51 | out: "x_α", 52 | }, 53 | { 54 | tex: "x_\\alpha", 55 | out: "x_α", 56 | }, 57 | { 58 | tex: "_{a!}", 59 | out: "_{a!}", 60 | }, 61 | { 62 | tex: "\\mathbb{0} \\mathfrak{a}", 63 | out: "𝟘 𝔞", 64 | }, 65 | { 66 | tex: "\\mathcal{T}", 67 | out: "𝒯", 68 | }, 69 | { 70 | tex: "\\mathbb{1 2}", 71 | out: "\\mathbb{1 2}", 72 | }, 73 | { 74 | tex: "\\mathbb\\alpha", 75 | out: "\\mathbb\\alpha", 76 | }, 77 | { 78 | tex: "\\|", 79 | out: "‖", 80 | }, 81 | ]; 82 | 83 | describe("convertText", () => { 84 | for (const t of testCases) { 85 | test(`convert ${t.tex}`, () => { 86 | const r = convert(t.tex, t.selection?.[0] ?? 0, t.selection?.[1] ?? t.tex.length, t.options); 87 | expect({ out: r.text, cursor: r.cursor }).toMatchObject({ 88 | out: t.out, 89 | ...(t.cursor !== undefined ? { cursor: t.cursor } : {}), 90 | }); 91 | }); 92 | } 93 | }); 94 | 95 | describe("Parser", () => { 96 | test("Should successfully parse \\\\^^__", () => { 97 | expect(parser.parse("\\\\^^__").type).toEqual("ParseOK"); 98 | }); 99 | }); 100 | 101 | describe("DOM test", () => { 102 | test("Convert text in textarea", () => { 103 | document.body.innerHTML = ` 104 | 105 | `; 106 | const textarea = document.querySelector("textarea")!; 107 | textarea.select(); 108 | document.execCommand = function fakeExecCommand(_, __, text) { 109 | textarea.value = text || ""; 110 | return false; 111 | }; 112 | render(textarea, {}); 113 | expect(textarea.value).toBe("α → β"); 114 | }); 115 | 116 | // Not testable because jsdom didn't implement document.getSelection() 117 | test.skip("convert text in contenteditable", () => { 118 | document.body.innerHTML = ` 119 |
120 |
\\alpha \\to
121 |
\\beta
122 |
123 | `; 124 | }); 125 | }); 126 | -------------------------------------------------------------------------------- /lib/parser.ts: -------------------------------------------------------------------------------- 1 | import * as bnb from "bread-n-butter"; 2 | 3 | export interface ASTNode { 4 | type: string; 5 | start: number; 6 | end: number; 7 | } 8 | 9 | export interface PlainTextNode extends ASTNode { 10 | type: "PlainText"; 11 | value: string; 12 | } 13 | 14 | export interface CurlyGroupNode extends ASTNode { 15 | type: "CurlyGroup"; 16 | value: string; 17 | } 18 | 19 | export interface NullaryMacroNode extends ASTNode { 20 | type: "NullaryMacro"; 21 | macro: string; 22 | } 23 | 24 | export interface UnaryMacroNode extends ASTNode { 25 | type: "UnaryMacro"; 26 | macro: string; 27 | argument: ASTNode; 28 | } 29 | 30 | export interface SuperscriptNode extends ASTNode { 31 | type: "Superscript"; 32 | content: string; 33 | } 34 | 35 | export interface SubscriptNode extends ASTNode { 36 | type: "Subscript"; 37 | content: string; 38 | } 39 | 40 | export interface ProgramNode extends ASTNode { 41 | type: "Program"; 42 | body: ASTNode[]; 43 | } 44 | 45 | export type TexNode = 46 | | PlainTextNode 47 | | CurlyGroupNode 48 | | NullaryMacroNode 49 | | UnaryMacroNode 50 | | SuperscriptNode 51 | | SubscriptNode 52 | | ProgramNode; 53 | 54 | function makeNode(type: string) { 55 | return function makeNodeWrapper(parser: any) { 56 | return bnb.all(bnb.location, parser, bnb.location).map(function makeNode_([start, value, end]: [ 57 | any, 58 | any, 59 | any, 60 | ]) { 61 | return { 62 | type, 63 | start: start.index, 64 | end: end.index, 65 | // @ts-ignore 66 | ...value, 67 | }; 68 | }); 69 | }; 70 | } 71 | 72 | const Spaces = bnb.match(/\s*/); 73 | 74 | const Superscript = bnb 75 | .all(bnb.match(/\^\s*/), bnb.choice(bnb.match(/{[a-zA-Z0-9+-]+}/), bnb.match(/[a-zA-Z0-9+-]/))) 76 | .map(([, b]: [any, string]) => ({ 77 | content: b, 78 | })) 79 | .thru(makeNode("Superscript")); 80 | 81 | const Subscript = bnb 82 | .all(bnb.match(/_\s*/), bnb.choice(bnb.match(/{[a-zA-Z0-9+-]+}/), bnb.match(/[a-zA-Z0-9+-]/))) 83 | .map(([_, b]: [any, string]) => { 84 | return { 85 | content: b, 86 | }; 87 | }) 88 | .thru(makeNode("Subscript")); 89 | 90 | const NullaryMacro = bnb 91 | .choice(bnb.match(/\\[a-zA-Z]+/), bnb.match(/\\\|/)) 92 | .map((x: string) => { 93 | return { 94 | macro: x, 95 | }; 96 | }) 97 | .thru(makeNode("NullaryMacro")); 98 | 99 | const CurlyGroup = bnb 100 | .match(/\{.*?\}/) 101 | .map((x: string) => ({ value: x })) 102 | .thru(makeNode("CurlyGroup")); 103 | 104 | const UnaryMacro = bnb 105 | .all( 106 | bnb.choice( 107 | bnb.match(/\\mathbb(?![a-zA-Z])/), 108 | bnb.match(/\\mathfrak(?![a-zA-Z])/), 109 | bnb.match(/\\mathcal(?![a-zA-Z])/), 110 | bnb.match(/\\not(?![a-zA-Z])/), 111 | ), 112 | Spaces, 113 | bnb.choice( 114 | CurlyGroup, 115 | NullaryMacro, 116 | bnb 117 | .match(/[a-zA-Z0-9]/) 118 | .map((x) => ({ value: x })) 119 | .thru(makeNode("PlainText")), 120 | ), 121 | ) 122 | .map(([a, _, c]: [string, any, ASTNode]) => ({ 123 | macro: a, 124 | argument: c, 125 | })) 126 | .thru(makeNode("UnaryMacro")); 127 | 128 | const Illegal = bnb 129 | .match(/[\^_\\]/) 130 | .map((r: string) => ({ 131 | value: r, 132 | })) 133 | .thru(makeNode("PlainText")); 134 | 135 | const PlainText = bnb 136 | .match(/[^_^\\]+/) 137 | .map((x: string) => ({ value: x })) 138 | .thru(makeNode("PlainText")); 139 | 140 | export const parser = bnb 141 | .choice(Superscript, Subscript, UnaryMacro, NullaryMacro, Illegal, PlainText) 142 | .repeat() 143 | .map((nodes: ASTNode[]) => { 144 | return { 145 | body: nodes, 146 | }; 147 | }) 148 | .thru(makeNode("Program")); 149 | -------------------------------------------------------------------------------- /docs-src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TeX to Unicode, a browser extension that converts TeX to Unicode 5 | 9 | 10 | 11 | 12 | 13 | 14 | 18 | 19 | 20 | 21 |
22 |
TeX to Unicode
23 |
24 | 25 | Github Logo 26 | 27 |
28 |
29 |
30 |
31 |
32 |

33 | Input Unicode mathematical symbols by LaTeX 34 |

35 |

A Chrome extension

36 |
37 |
38 |
39 | 40 |
41 |

42 | Convert by pressing Alt + w (or CONTROL + 43 | w on Mac). 44 |

45 |
46 | 68 |
69 |
70 | 71 |
72 |

Supported symbols

73 |
74 |
75 |
76 |
77 |
78 |

Features

79 |
80 |
    81 |
  • Convert Tex to Unicode by pressing ALT + W (CONTROL + W on Mac)
  • 82 |
  • Convert underscripts and superscripts (can be enabled by an option)
  • 83 |
  • Works on Stack Exchange, Reddit, Wikipedia, and Gmail
  • 84 |
  • Convert one symbol, or convert all symbols under selection
  • 85 |
  • Free software
  • 86 |
87 |
88 |
89 |
90 |

Demo

91 |
92 | 100 |
101 |
102 | 103 | 104 | 105 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /lib/index.ts: -------------------------------------------------------------------------------- 1 | import { findNodesBetweenNodes } from "./tree.js"; 2 | import { type TexNode, type ASTNode, parser } from "./parser.js"; 3 | import { symbols } from "./symbols.js"; 4 | 5 | export interface Options { 6 | subscripts?: boolean; 7 | } 8 | 9 | /** 10 | * Check if two interval overlaps. 11 | */ 12 | function overlaps([a, b]: [number, number], [c, d]: [number, number]): boolean { 13 | return ( 14 | (c <= a && a < d) || (c <= b - 1 && b - 1 < d) || (a <= c && c < b) || (a <= d - 1 && d - 1 < b) 15 | ); 16 | } 17 | 18 | function debrackets(s: string): string { 19 | s = s.trim(); 20 | if (s[0] === "{" && s[s.length - 1] === "}") { 21 | return debrackets(s.slice(1, s.length - 1)); 22 | } 23 | return s; 24 | } 25 | 26 | function printSource(source: string, node: ASTNode): string { 27 | return source.slice(node.start, node.end); 28 | } 29 | 30 | function printNode(source: string, node: TexNode, options: Options = {}): string { 31 | switch (node.type) { 32 | case "PlainText": 33 | case "CurlyGroup": { 34 | return printSource(source, node); 35 | } 36 | case "UnaryMacro": { 37 | const argumentText = printSource(source, node.argument); 38 | 39 | const key = 40 | node.macro === "\\not" 41 | ? `${node.macro}${debrackets(argumentText)}` 42 | : `${node.macro}{${debrackets(argumentText)}}`; 43 | 44 | return symbols[key] || printSource(source, node); 45 | } 46 | case "NullaryMacro": { 47 | return symbols[node.macro] || printSource(source, node); 48 | } 49 | case "Subscript": 50 | case "Superscript": { 51 | if (!options.subscripts) { 52 | return printSource(source, node); 53 | } 54 | 55 | let r = ""; 56 | for (const c of debrackets(node.content)) { 57 | const h = node.type === "Subscript" ? "_" : "^"; 58 | const v = symbols[`${h}${c}`]; 59 | if (v === undefined) { 60 | return printSource(source, node); 61 | } 62 | r += v; 63 | } 64 | return r; 65 | } 66 | } 67 | 68 | console.error(node); 69 | throw new Error("unhandled case"); 70 | } 71 | 72 | function print( 73 | source: string, 74 | ast: TexNode, 75 | selectStart: number, 76 | selectEnd: number, 77 | options: Options = {}, 78 | ) { 79 | const nodes = (ast as any).body as ASTNode[]; 80 | let cursor = -1; 81 | let output = ""; 82 | for (const node of nodes) { 83 | if (overlaps([selectStart, selectEnd], [node.start, node.end])) { 84 | output += printNode(source, node as TexNode, options); 85 | } else { 86 | output += source.slice(node.start, node.end); 87 | } 88 | 89 | if (node.start < selectEnd && selectEnd <= node.end) { 90 | cursor = node.type !== "PlainText" ? output.length : output.length - (node.end - selectEnd); 91 | } 92 | } 93 | 94 | return { 95 | text: output, 96 | cursor, 97 | }; 98 | } 99 | 100 | /** convert tex to unicode text */ 101 | export function convert( 102 | text: string, 103 | selectStart: number, 104 | selectEnd: number, 105 | options: Options = {}, 106 | ): { text: string; cursor: number } { 107 | selectEnd = Math.min(selectEnd, text.length); 108 | // The parser is not supposed to throw error by design. 109 | const ast = parser.tryParse(text); 110 | return print(text, ast, selectStart, selectEnd, options); 111 | } 112 | 113 | /** Convert TeX in textarea or contentEditable, and then set cursor. */ 114 | export function render(element: HTMLElement, options: Options): void { 115 | if (element.tagName === "INPUT" || element.tagName === "TEXTAREA") { 116 | const textarea = element as HTMLInputElement | HTMLTextAreaElement; 117 | const selectionStart = textarea.selectionStart || 0; 118 | const selectionEnd = textarea.selectionEnd || 0; 119 | const { text, cursor } = convert(textarea.value, selectionStart, selectionEnd, options); 120 | textarea.select(); 121 | element.ownerDocument.execCommand("insertText", false, text); 122 | textarea.selectionStart = textarea.selectionEnd = cursor; 123 | } 124 | // contenteditable elements: ex. Gmail message body. 125 | else if (element.contentEditable) { 126 | const selection = element.ownerDocument.getSelection(); 127 | if (!selection) { 128 | return; 129 | } 130 | if (!selection.anchorNode || !selection.focusNode) { 131 | return; 132 | } 133 | const nodesBetweenNodes = findNodesBetweenNodes(selection.anchorNode, selection.focusNode); 134 | 135 | const [startNode] = nodesBetweenNodes; 136 | const endNode = nodesBetweenNodes[nodesBetweenNodes.length - 1]; 137 | 138 | const selectionIsForward = 139 | startNode === selection.anchorNode && selection.anchorOffset <= selection.focusOffset; 140 | 141 | const [startCursor, endCursor] = selectionIsForward 142 | ? [selection.anchorOffset, selection.focusOffset] 143 | : [selection.focusOffset, selection.anchorOffset]; 144 | 145 | const TEXT_NODE_TYPE = 3; 146 | let _cursor; 147 | for (const node of nodesBetweenNodes) { 148 | if (node.nodeType === TEXT_NODE_TYPE) { 149 | const selectionStart = node === nodesBetweenNodes[0] ? startCursor : 0; 150 | const selectionEnd = 151 | node === nodesBetweenNodes[nodesBetweenNodes.length - 1] 152 | ? endCursor 153 | : node.nodeValue?.length || 0; 154 | const { text, cursor } = convert( 155 | node.nodeValue || "", 156 | selectionStart, 157 | selectionEnd, 158 | options, 159 | ); 160 | node.nodeValue = text; 161 | _cursor = cursor; 162 | } 163 | } 164 | 165 | selection.collapse(endNode, _cursor); 166 | } 167 | } 168 | 169 | export { symbols, parser }; 170 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TeX to Unicode, a browser extension that converts TeX to Unicode 5 | 9 | 10 | 11 | 12 | 13 | 14 | 18 | 19 | 20 | 21 | 22 |
23 |
TeX to Unicode
24 |
25 | 26 | Github Logo 27 | 28 |
29 |
30 |
31 |
32 |
33 |

34 | Input Unicode mathematical symbols by LaTeX 35 |

36 |

A Chrome extension

37 |
38 |
39 |
40 | 41 |
42 |

43 | Convert by pressing Alt + w (or CONTROL + 44 | w on Mac). 45 |

46 |
47 | 69 |
70 |
71 | 72 |
73 |

Supported symbols

74 |
75 |
76 |
77 |
78 |
79 |

Features

80 |
81 |
    82 |
  • Convert Tex to Unicode by pressing ALT + W (CONTROL + W on Mac)
  • 83 |
  • Convert underscripts and superscripts (can be enabled by an option)
  • 84 |
  • Works on Stack Exchange, Reddit, Wikipedia, and Gmail
  • 85 |
  • Convert one symbol, or convert all symbols under selection
  • 86 |
  • Free software
  • 87 |
88 |
89 |
90 |
91 |

Demo

92 |
93 | 101 |
102 |
103 | 104 | 105 | 106 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /lib/symbols.ts: -------------------------------------------------------------------------------- 1 | export const symbols: Record = { 2 | "\\Alpha": "Α", 3 | "\\Beta": "Β", 4 | "\\Gamma": "Γ", 5 | "\\Delta": "Δ", 6 | "\\Epsilon": "Ε", 7 | "\\Zeta": "Ζ", 8 | "\\Eta": "Η", 9 | "\\Theta": "Θ", 10 | "\\Iota": "I", 11 | "\\Kappa": "Κ", 12 | "\\Lambda": "Λ", 13 | "\\Mu": "Μ", 14 | "\\Nu": "Ν", 15 | "\\Xi": "Ξ", 16 | "\\Omicron": "Ο", 17 | "\\Pi": "Π", 18 | "\\Rho": "Ρ", 19 | "\\Sigma": "Σ", 20 | "\\Tau": "Τ", 21 | "\\Upsilon": "Υ", 22 | "\\Phi": "Φ", 23 | "\\Chi": "Χ", 24 | "\\Psi": "Ψ", 25 | "\\Omega": "Ω", 26 | 27 | "\\alpha": "α", 28 | "\\beta": "β", 29 | "\\gamma": "γ", 30 | "\\delta": "δ", 31 | "\\epsilon": "ϵ", 32 | "\\zeta": "ζ", 33 | "\\eta": "η", 34 | "\\theta": "θ", 35 | "\\iota": "ι", 36 | "\\kappa": "κ", 37 | "\\lambda": "λ", 38 | "\\mu": "μ", 39 | "\\nu": "ν", 40 | "\\xi": "ξ", 41 | "\\omicron": "ο", 42 | "\\pi": "π", 43 | "\\rho": "ρ", 44 | "\\sigma": "σ", 45 | "\\tau": "τ", 46 | "\\upsilon": "υ", 47 | "\\phi": "ϕ", 48 | "\\chi": "χ", 49 | "\\psi": "ψ", 50 | "\\omega": "ω", 51 | 52 | "\\varepsilon": "ε", 53 | "\\varnothing": "∅", 54 | "\\varkappa": "ϰ", 55 | "\\varphi": "φ", 56 | "\\varpi": "ϖ", 57 | "\\varrho": "ϱ", 58 | "\\varsigma": "ς", 59 | "\\vartheta": "ϑ", 60 | "\\neq": "≠", 61 | "\\equiv": "≡", 62 | "\\not\\equiv": "≢", 63 | "\\leq": "≤", 64 | "\\geq": "≥", 65 | "\\leqq": "≦", 66 | "\\geqq": "≧", 67 | "\\lneqq": "≨", 68 | "\\gneqq": "≩", 69 | "\\leqslant": "⩽", 70 | "\\geqslant": "⩾", 71 | "\\ll": "≪", 72 | "\\gg": "≫", 73 | "\\nless": "≮", 74 | "\\ngtr": "≯", 75 | "\\nleq": "≰", 76 | "\\ngeq": "≱", 77 | "\\lessequivlnt": "≲", 78 | "\\greaterequivlnt": "≳", 79 | "\\prec": "≺", 80 | "\\succ": "≻", 81 | "\\preccurlyeq": "≼", 82 | "\\succcurlyeq": "≽", 83 | "\\precapprox": "≾", 84 | "\\succapprox": "≿", 85 | "\\nprec": "⊀", 86 | "\\nsucc": "⊁", 87 | "\\sim": "∼", 88 | "\\not\\sim": "≁", 89 | "\\simeq": "≃", 90 | "\\not\\simeq": "≄", 91 | "\\backsim": "∽", 92 | "\\lazysinv": "∾", 93 | "\\wr": "≀", 94 | "\\cong": "≅", 95 | "\\not\\cong": "≇", 96 | "\\approx": "≈", 97 | "\\not\\approx": "≉", 98 | "\\approxeq": "≊", 99 | "\\approxnotequal": "≆", 100 | "\\tildetrpl": "≋", 101 | "\\allequal": "≌", 102 | "\\asymp": "≍", 103 | "\\doteq": "≐", 104 | "\\doteqdot": "≑", 105 | "\\lneq": "⪇", 106 | "\\gneq": "⪈", 107 | "\\preceq": "⪯", 108 | "\\succeq": "⪰", 109 | "\\precneqq": "⪵", 110 | "\\succneqq": "⪶", 111 | "\\emptyset": "∅", 112 | "\\in": "∈", 113 | "\\notin": "∉", 114 | "\\not\\in": "∉", 115 | "\\ni": "∋", 116 | "\\not\\ni": "∌", 117 | "\\subset": "⊂", 118 | "\\subseteq": "⊆", 119 | "\\not\\subset": "⊄", 120 | "\\not\\subseteq": "⊈", 121 | "\\supset": "⊃", 122 | "\\supseteq": "⊇", 123 | "\\not\\supset": "⊅", 124 | "\\not\\supseteq": "⊉", 125 | "\\subsetneq": "⊊", 126 | "\\supsetneq": "⊋", 127 | "\\exists": "∃", 128 | "\\nexists": "∄", 129 | "\\not\\exists": "∄", 130 | "\\forall": "∀", 131 | "\\aleph": "ℵ", 132 | "\\beth": "ℶ", 133 | "\\neg": "¬", 134 | "\\wedge": "∧", 135 | "\\vee": "∨", 136 | "\\veebar": "⊻", 137 | "\\land": "∧", 138 | "\\lor": "∨", 139 | "\\top": "⊤", 140 | "\\bot": "⊥", 141 | "\\cup": "∪", 142 | "\\cap": "∩", 143 | "\\bigcup": "⋃", 144 | "\\bigcap": "⋂", 145 | "\\setminus": "∖", 146 | "\\therefore": "∴", 147 | "\\because": "∵", 148 | "\\Box": "□", 149 | "\\models": "⊨", 150 | "\\vdash": "⊢", 151 | 152 | "\\rightarrow": "→", 153 | "\\Rightarrow": "⇒", 154 | "\\leftarrow": "←", 155 | "\\Leftarrow": "⇐", 156 | "\\uparrow": "↑", 157 | "\\Uparrow": "⇑", 158 | "\\downarrow": "↓", 159 | "\\Downarrow": "⇓", 160 | "\\nwarrow": "↖", 161 | "\\nearrow": "↗", 162 | "\\searrow": "↘", 163 | "\\swarrow": "↙", 164 | "\\mapsto": "↦", 165 | "\\to": "→", 166 | "\\leftrightarrow": "↔", 167 | "\\hookleftarrow": "↩", 168 | "\\Leftrightarrow": "⇔", 169 | "\\rightarrowtail": "↣", 170 | "\\leftarrowtail": "↢", 171 | "\\twoheadrightarrow": "↠", 172 | "\\twoheadleftarrow": "↞", 173 | "\\hookrightarrow": "↪", 174 | "\\rightsquigarrow": "⇝", 175 | "\\rightleftharpoons": "⇌", 176 | "\\leftrightharpoons": "⇋", 177 | "\\rightharpoonup": "⇀", 178 | "\\rightharpoondown": "⇁", 179 | 180 | "\\times": "×", 181 | "\\div": "÷", 182 | "\\infty": "∞", 183 | "\\nabla": "∇", 184 | "\\partial": "∂", 185 | "\\sum": "∑", 186 | "\\prod": "∏", 187 | "\\coprod": "∐", 188 | "\\int": "∫", 189 | "\\iint": "∬", 190 | "\\iiint": "∭", 191 | "\\iiiint": "⨌", 192 | "\\oint": "∮", 193 | "\\surfintegral": "∯", 194 | "\\volintegral": "∰", 195 | "\\Re": "ℜ", 196 | "\\Im": "ℑ", 197 | "\\wp": "℘", 198 | "\\mp": "∓", 199 | "\\langle": "⟨", 200 | "\\rangle": "⟩", 201 | "\\lfloor": "⌊", 202 | "\\rfloor": "⌋", 203 | "\\lceil": "⌈", 204 | "\\rceil": "⌉", 205 | "\\|": "‖", 206 | 207 | "\\mathbb{a}": "𝕒", 208 | "\\mathbb{A}": "𝔸", 209 | "\\mathbb{b}": "𝕓", 210 | "\\mathbb{B}": "𝔹", 211 | "\\mathbb{c}": "𝕔", 212 | "\\mathbb{C}": "ℂ", 213 | "\\mathbb{d}": "𝕕", 214 | "\\mathbb{D}": "𝔻", 215 | "\\mathbb{e}": "𝕖", 216 | "\\mathbb{E}": "𝔼", 217 | "\\mathbb{f}": "𝕗", 218 | "\\mathbb{F}": "𝔽", 219 | "\\mathbb{g}": "𝕘", 220 | "\\mathbb{G}": "𝔾", 221 | "\\mathbb{h}": "𝕙", 222 | "\\mathbb{H}": "ℍ", 223 | "\\mathbb{i}": "𝕚", 224 | "\\mathbb{I}": "𝕀", 225 | "\\mathbb{j}": "𝕛", 226 | "\\mathbb{J}": "𝕁", 227 | "\\mathbb{k}": "𝕜", 228 | "\\mathbb{K}": "𝕂", 229 | "\\mathbb{l}": "𝕝", 230 | "\\mathbb{L}": "𝕃", 231 | "\\mathbb{m}": "𝕞", 232 | "\\mathbb{M}": "𝕄", 233 | "\\mathbb{n}": "𝕟", 234 | "\\mathbb{N}": "ℕ", 235 | "\\mathbb{o}": "𝕠", 236 | "\\mathbb{O}": "𝕆", 237 | "\\mathbb{p}": "𝕡", 238 | "\\mathbb{P}": "ℙ", 239 | "\\mathbb{q}": "𝕢", 240 | "\\mathbb{Q}": "ℚ", 241 | "\\mathbb{r}": "𝕣", 242 | "\\mathbb{R}": "ℝ", 243 | "\\mathbb{s}": "𝕤", 244 | "\\mathbb{S}": "𝕊", 245 | "\\mathbb{t}": "𝕥", 246 | "\\mathbb{T}": "𝕋", 247 | "\\mathbb{u}": "𝕦", 248 | "\\mathbb{U}": "𝕌", 249 | "\\mathbb{v}": "𝕧", 250 | "\\mathbb{V}": "𝕍", 251 | "\\mathbb{x}": "𝕩", 252 | "\\mathbb{X}": "𝕏", 253 | "\\mathbb{y}": "𝕪", 254 | "\\mathbb{Y}": "𝕐", 255 | "\\mathbb{z}": "𝕫", 256 | "\\mathbb{Z}": "ℤ", 257 | "\\mathbb{0}": "𝟘", 258 | "\\mathbb{1}": "𝟙", 259 | "\\mathbb{2}": "𝟚", 260 | "\\mathbb{3}": "𝟛", 261 | "\\mathbb{4}": "𝟜", 262 | "\\mathbb{5}": "𝟝", 263 | "\\mathbb{6}": "𝟞", 264 | "\\mathbb{7}": "𝟟", 265 | "\\mathbb{8}": "𝟠", 266 | "\\mathbb{9}": "𝟡", 267 | 268 | "\\mathfrak{a}": "𝔞", 269 | "\\mathfrak{A}": "𝔄", 270 | "\\mathfrak{b}": "𝔟", 271 | "\\mathfrak{B}": "𝔅", 272 | "\\mathfrak{c}": "𝔠", 273 | "\\mathfrak{C}": "ℭ", 274 | "\\mathfrak{d}": "𝔡", 275 | "\\mathfrak{D}": "𝔇", 276 | "\\mathfrak{e}": "𝔢", 277 | "\\mathfrak{E}": "𝔈", 278 | "\\mathfrak{f}": "𝔣", 279 | "\\mathfrak{F}": "𝔉", 280 | "\\mathfrak{g}": "𝔤", 281 | "\\mathfrak{G}": "𝔊", 282 | "\\mathfrak{h}": "𝔥", 283 | "\\mathfrak{H}": "ℌ", 284 | "\\mathfrak{i}": "𝔦", 285 | "\\mathfrak{I}": "ℑ", 286 | "\\mathfrak{j}": "𝔧", 287 | "\\mathfrak{J}": "𝔍", 288 | "\\mathfrak{k}": "𝔨", 289 | "\\mathfrak{K}": "𝔎", 290 | "\\mathfrak{l}": "𝔩", 291 | "\\mathfrak{L}": "𝔏", 292 | "\\mathfrak{m}": "𝔪", 293 | "\\mathfrak{M}": "𝔐", 294 | "\\mathfrak{n}": "𝔫", 295 | "\\mathfrak{N}": "𝔑", 296 | "\\mathfrak{o}": "𝔬", 297 | "\\mathfrak{O}": "𝔒", 298 | "\\mathfrak{p}": "𝔭", 299 | "\\mathfrak{P}": "𝔓", 300 | "\\mathfrak{q}": "𝔮", 301 | "\\mathfrak{Q}": "𝔔", 302 | "\\mathfrak{r}": "𝔯", 303 | "\\mathfrak{R}": "ℜ", 304 | "\\mathfrak{s}": "𝔰", 305 | "\\mathfrak{S}": "𝔖", 306 | "\\mathfrak{t}": "𝔱", 307 | "\\mathfrak{T}": "𝔗", 308 | "\\mathfrak{u}": "𝔲", 309 | "\\mathfrak{U}": "𝔘", 310 | "\\mathfrak{v}": "𝔳", 311 | "\\mathfrak{V}": "𝔙", 312 | "\\mathfrak{x}": "𝔵", 313 | "\\mathfrak{X}": "𝔛", 314 | "\\mathfrak{y}": "𝔶", 315 | "\\mathfrak{Y}": "𝔜", 316 | "\\mathfrak{z}": "𝔷", 317 | "\\mathfrak{Z}": "ℨ", 318 | 319 | "\\mathcal{a}": "𝒶", 320 | "\\mathcal{A}": "𝒜", 321 | "\\mathcal{b}": "𝒷", 322 | "\\mathcal{B}": "ℬ", 323 | "\\mathcal{c}": "𝒸", 324 | "\\mathcal{C}": "𝒞", 325 | "\\mathcal{d}": "𝒹", 326 | "\\mathcal{D}": "𝒟", 327 | "\\mathcal{e}": "ℯ", 328 | "\\mathcal{E}": "ℰ", 329 | "\\mathcal{f}": "𝒻", 330 | "\\mathcal{F}": "ℱ", 331 | "\\mathcal{g}": "ℊ", 332 | "\\mathcal{G}": "𝒢", 333 | "\\mathcal{h}": "𝒽", 334 | "\\mathcal{H}": "ℋ", 335 | "\\mathcal{i}": "𝒾", 336 | "\\mathcal{I}": "ℐ", 337 | "\\mathcal{j}": "𝒿", 338 | "\\mathcal{J}": "𝒥", 339 | "\\mathcal{k}": "𝓀", 340 | "\\mathcal{K}": "𝒦", 341 | "\\mathcal{l}": "𝓁", 342 | "\\mathcal{L}": "ℒ", 343 | "\\mathcal{m}": "𝓂", 344 | "\\mathcal{M}": "ℳ", 345 | "\\mathcal{n}": "𝓃", 346 | "\\mathcal{N}": "𝒩", 347 | "\\mathcal{o}": "ℴ", 348 | "\\mathcal{O}": "𝒪", 349 | "\\mathcal{p}": "𝓅", 350 | "\\mathcal{P}": "𝒫", 351 | "\\mathcal{q}": "𝓆", 352 | "\\mathcal{Q}": "𝒬", 353 | "\\mathcal{r}": "𝓇", 354 | "\\mathcal{R}": "ℛ", 355 | "\\mathcal{s}": "𝓈", 356 | "\\mathcal{S}": "𝒮", 357 | "\\mathcal{t}": "𝓉", 358 | "\\mathcal{T}": "𝒯", 359 | "\\mathcal{u}": "𝓊", 360 | "\\mathcal{U}": "𝒰", 361 | "\\mathcal{v}": "𝓋", 362 | "\\mathcal{V}": "𝒱", 363 | "\\mathcal{w}": "𝓌", 364 | "\\mathcal{W}": "𝒲", 365 | "\\mathcal{x}": "𝓍", 366 | "\\mathcal{X}": "𝒳", 367 | "\\mathcal{y}": "𝓎", 368 | "\\mathcal{Y}": "𝒴", 369 | "\\mathcal{z}": "𝓏", 370 | "\\mathcal{Z}": "𝒵", 371 | 372 | _0: "₀", 373 | _1: "₁", 374 | _2: "₂", 375 | _3: "₃", 376 | _4: "₄", 377 | _5: "₅", 378 | _6: "₆", 379 | _7: "₇", 380 | _8: "₈", 381 | _9: "₉", 382 | "^0": "⁰", 383 | "^1": "¹", 384 | "^2": "²", 385 | "^3": "³", 386 | "^4": "⁴", 387 | "^5": "⁵", 388 | "^6": "⁶", 389 | "^7": "⁷", 390 | "^8": "⁸", 391 | "^9": "⁹", 392 | 393 | "_+": "₊", 394 | "_-": "₋", 395 | "_(": "₍", 396 | "_)": "₎", 397 | "^+": "⁺", 398 | "^-": "⁻", 399 | "^(": "⁽", 400 | "^)": "⁾", 401 | 402 | _a: "ₐ", 403 | _e: "ₑ", 404 | _h: "ₕ", 405 | _i: "ᵢ", 406 | _j: "ⱼ", 407 | _k: "ₖ", 408 | _l: "ₗ", 409 | _m: "ₘ", 410 | _n: "ₙ", 411 | _o: "ₒ", 412 | _p: "ₚ", 413 | _r: "ᵣ", 414 | _s: "ₛ", 415 | _t: "ₜ", 416 | _u: "ᵤ", 417 | _v: "ᵥ", 418 | _x: "ₓ", 419 | "^a": "ᵃ", 420 | "^b": "ᵇ", 421 | "^c": "ᶜ", 422 | "^d": "ᵈ", 423 | "^e": "ᵉ", 424 | "^f": "ᶠ", 425 | "^g": "ᵍ", 426 | "^h": "ʰ", 427 | "^i": "^i", 428 | "^j": "ʲ", 429 | "^k": "ᵏ", 430 | "^l": "ˡ", 431 | "^m": "ᵐ", 432 | "^n": "ⁿ", 433 | "^o": "ᵒ", 434 | "^p": "ᵖ", 435 | "^r": "ʳ", 436 | "^s": "ˢ", 437 | "^t": "ᵗ", 438 | "^u": "ᵘ", 439 | "^v": "ᵛ", 440 | "^w": "ʷ", 441 | "^x": "ˣ", 442 | "^y": "ʸ", 443 | "^z": "ᶻ", 444 | 445 | "\\pm": "±", 446 | "\\dotplus": "∔", 447 | "\\bullet": "∙", 448 | "\\cdot": "⋅", 449 | "\\oplus": "⊕", 450 | "\\ominus": "⊖", 451 | "\\otimes": "⊗", 452 | "\\oslash": "⊘", 453 | "\\odot": "⊙", 454 | "\\circ": "∘", 455 | "\\surd": "√", 456 | "\\propto": "∝", 457 | "\\angle": "∠", 458 | "\\measuredangle": "∡", 459 | "\\sphericalangle": "∢", 460 | "\\mid": "∣", 461 | "\\nmid": "∤", 462 | "\\not\\mid": "∤", 463 | "\\parallel": "∥", 464 | "\\nparallel": "∦", 465 | "\\not\\parallel": "∦", 466 | "\\flat": "♭", 467 | "\\natural": "♮", 468 | "\\sharp": "♯", 469 | }; 470 | -------------------------------------------------------------------------------- /docs/assets/main-bp1Lx8IY.js: -------------------------------------------------------------------------------- 1 | (function(){const a=document.createElement("link").relList;if(a&&a.supports&&a.supports("modulepreload"))return;for(const n of document.querySelectorAll('link[rel="modulepreload"]'))e(n);new MutationObserver(n=>{for(const o of n)if(o.type==="childList")for(const i of o.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&e(i)}).observe(document,{childList:!0,subtree:!0});function t(n){const o={};return n.integrity&&(o.integrity=n.integrity),n.referrerPolicy&&(o.referrerPolicy=n.referrerPolicy),n.crossOrigin==="use-credentials"?o.credentials="include":n.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function e(n){if(n.ep)return;n.ep=!0;const o=t(n);fetch(n.href,o)}})();function x(r,a){const t=L(r,a),e=z(t),[n,o]=[e.indexOf(r),e.indexOf(a)].sort();return e.slice(n,o+1)}function q(r){const a=[];let t=r;for(a.push(t);t?.parentNode;)t=t.parentNode,a.push(t);return a.reverse()}function L(r,a){const t=q(r),e=q(a);let n=0;for(;n{if(e){a.push(e);for(const n of Array.from(e.childNodes||[]))t(n)}};return t(r),a}class h{constructor(a){this.action=a}parse(a){const t={index:0,line:1,column:1},e=new w({input:a,location:t}),n=this.skip(I).action(e);return n.type==="ActionOK"?{type:"ParseOK",value:n.value}:{type:"ParseFail",location:n.furthest,expected:n.expected}}tryParse(a){const t=this.parse(a);if(t.type==="ParseOK")return t.value;const{expected:e,location:n}=t,{line:o,column:i}=n,c=`parse error at line ${o} column ${i}: expected ${e.join(", ")}`;throw new Error(c)}and(a){return new h(t=>{const e=this.action(t);if(e.type==="ActionFail")return e;t=t.moveTo(e.location);const n=t.merge(e,a.action(t));if(n.type==="ActionOK"){const o=[e.value,n.value];return t.merge(n,t.ok(n.location.index,o))}return n})}skip(a){return this.and(a).map(([t])=>t)}next(a){return this.and(a).map(([,t])=>t)}or(a){return new h(t=>{const e=this.action(t);return e.type==="ActionOK"?e:t.merge(e,a.action(t))})}chain(a){return new h(t=>{const e=this.action(t);if(e.type==="ActionFail")return e;const n=a(e.value);return t=t.moveTo(e.location),t.merge(e,n.action(t))})}map(a){return this.chain(t=>d(a(t)))}thru(a){return a(this)}desc(a){return new h(t=>{const e=this.action(t);return e.type==="ActionOK"?e:{type:"ActionFail",furthest:e.furthest,expected:a}})}wrap(a,t){return a.next(this).skip(t)}trim(a){return this.wrap(a,a)}repeat(a=0,t=1/0){if(!_(a,t))throw new Error(`repeat: bad range (${a} to ${t})`);return a===0?this.repeat(1,t).or(d([])):new h(e=>{const n=[];let o=this.action(e);if(o.type==="ActionFail")return o;for(;o.type==="ActionOK"&&n.length[n]):this.chain(n=>a.next(this).repeat(t-1,e-1).map(o=>[n,...o]))}node(a){return b(k,this,k).map(([t,e,n])=>({type:"ParseNode",name:a,value:e,start:t,end:n}))}}function _(r,a){return r<=a&&r>=0&&a>=0&&Number.isInteger(r)&&r!==1/0&&(Number.isInteger(a)||a===1/0)}const k=new h(r=>r.ok(r.location.index,r.location));function d(r){return new h(a=>a.ok(a.location.index,r))}const I=new h(r=>r.location.index"]):r.ok(r.location.index,""));function s(r){for(const t of r.flags)switch(t){case"i":case"s":case"m":case"u":continue;default:throw new Error("only the regexp flags 'imsu' are supported")}const a=new RegExp(r.source,r.flags+"y");return new h(t=>{const e=t.location.index;a.lastIndex=e;const n=t.input.match(a);if(n){const o=e+n[0].length,i=t.input.slice(e,o);return t.ok(o,i)}return t.fail(e,[String(r)])})}function b(...r){return r.reduce((a,t)=>a.chain(e=>t.map(n=>[...e,n])),d([]))}function p(...r){return r.reduce((a,t)=>a.or(t))}function K(r,a){return[...new Set([...r,...a])]}class w{constructor(a){this.input=a.input,this.location=a.location}moveTo(a){return new w({input:this.input,location:a})}_internal_move(a){if(a===this.location.index)return this.location;const t=this.location.index,e=a,n=this.input.slice(t,e);let{line:o,column:i}=this.location;for(const c of n)c===` 2 | `?(o++,i=1):i++;return{index:a,line:o,column:i}}ok(a,t){return{type:"ActionOK",value:t,location:this._internal_move(a),furthest:{index:-1,line:-1,column:-1},expected:[]}}fail(a,t){return{type:"ActionFail",furthest:this._internal_move(a),expected:t}}merge(a,t){if(t.furthest.index>a.furthest.index)return t;const e=t.furthest.index===a.furthest.index?K(a.expected,t.expected):a.expected;return t.type==="ActionOK"?{type:"ActionOK",location:t.location,value:t.value,furthest:a.furthest,expected:e}:{type:"ActionFail",furthest:a.furthest,expected:e}}}function u(r){return function(t){return b(k,t,k).map(function([n,o,i]){return{type:r,start:n.index,end:i.index,...o}})}}const $=s(/\s*/),C=b(s(/\^\s*/),p(s(/{[a-zA-Z0-9+-]+}/),s(/[a-zA-Z0-9+-]/))).map(([,r])=>({content:r})).thru(u("Superscript")),F=b(s(/_\s*/),p(s(/{[a-zA-Z0-9+-]+}/),s(/[a-zA-Z0-9+-]/))).map(([r,a])=>({content:a})).thru(u("Subscript")),O=p(s(/\\[a-zA-Z]+/),s(/\\\|/)).map(r=>({macro:r})).thru(u("NullaryMacro")),M=s(/\{.*?\}/).map(r=>({value:r})).thru(u("CurlyGroup")),Z=b(p(s(/\\mathbb(?![a-zA-Z])/),s(/\\mathfrak(?![a-zA-Z])/),s(/\\mathcal(?![a-zA-Z])/),s(/\\not(?![a-zA-Z])/)),$,p(M,O,s(/[a-zA-Z0-9]/).map(r=>({value:r})).thru(u("PlainText")))).map(([r,a,t])=>({macro:r,argument:t})).thru(u("UnaryMacro")),B=s(/[\^_\\]/).map(r=>({value:r})).thru(u("PlainText")),j=s(/[^_^\\]+/).map(r=>({value:r})).thru(u("PlainText")),D=p(C,F,Z,O,B,j).repeat().map(r=>({body:r})).thru(u("Program")),g={"\\Alpha":"Α","\\Beta":"Β","\\Gamma":"Γ","\\Delta":"Δ","\\Epsilon":"Ε","\\Zeta":"Ζ","\\Eta":"Η","\\Theta":"Θ","\\Iota":"I","\\Kappa":"Κ","\\Lambda":"Λ","\\Mu":"Μ","\\Nu":"Ν","\\Xi":"Ξ","\\Omicron":"Ο","\\Pi":"Π","\\Rho":"Ρ","\\Sigma":"Σ","\\Tau":"Τ","\\Upsilon":"Υ","\\Phi":"Φ","\\Chi":"Χ","\\Psi":"Ψ","\\Omega":"Ω","\\alpha":"α","\\beta":"β","\\gamma":"γ","\\delta":"δ","\\epsilon":"ϵ","\\zeta":"ζ","\\eta":"η","\\theta":"θ","\\iota":"ι","\\kappa":"κ","\\lambda":"λ","\\mu":"μ","\\nu":"ν","\\xi":"ξ","\\omicron":"ο","\\pi":"π","\\rho":"ρ","\\sigma":"σ","\\tau":"τ","\\upsilon":"υ","\\phi":"ϕ","\\chi":"χ","\\psi":"ψ","\\omega":"ω","\\varepsilon":"ε","\\varnothing":"∅","\\varkappa":"ϰ","\\varphi":"φ","\\varpi":"ϖ","\\varrho":"ϱ","\\varsigma":"ς","\\vartheta":"ϑ","\\neq":"≠","\\equiv":"≡","\\not\\equiv":"≢","\\leq":"≤","\\geq":"≥","\\leqq":"≦","\\geqq":"≧","\\lneqq":"≨","\\gneqq":"≩","\\leqslant":"⩽","\\geqslant":"⩾","\\ll":"≪","\\gg":"≫","\\nless":"≮","\\ngtr":"≯","\\nleq":"≰","\\ngeq":"≱","\\lessequivlnt":"≲","\\greaterequivlnt":"≳","\\prec":"≺","\\succ":"≻","\\preccurlyeq":"≼","\\succcurlyeq":"≽","\\precapprox":"≾","\\succapprox":"≿","\\nprec":"⊀","\\nsucc":"⊁","\\sim":"∼","\\not\\sim":"≁","\\simeq":"≃","\\not\\simeq":"≄","\\backsim":"∽","\\lazysinv":"∾","\\wr":"≀","\\cong":"≅","\\not\\cong":"≇","\\approx":"≈","\\not\\approx":"≉","\\approxeq":"≊","\\approxnotequal":"≆","\\tildetrpl":"≋","\\allequal":"≌","\\asymp":"≍","\\doteq":"≐","\\doteqdot":"≑","\\lneq":"⪇","\\gneq":"⪈","\\preceq":"⪯","\\succeq":"⪰","\\precneqq":"⪵","\\succneqq":"⪶","\\emptyset":"∅","\\in":"∈","\\notin":"∉","\\not\\in":"∉","\\ni":"∋","\\not\\ni":"∌","\\subset":"⊂","\\subseteq":"⊆","\\not\\subset":"⊄","\\not\\subseteq":"⊈","\\supset":"⊃","\\supseteq":"⊇","\\not\\supset":"⊅","\\not\\supseteq":"⊉","\\subsetneq":"⊊","\\supsetneq":"⊋","\\exists":"∃","\\nexists":"∄","\\not\\exists":"∄","\\forall":"∀","\\aleph":"ℵ","\\beth":"ℶ","\\neg":"¬","\\wedge":"∧","\\vee":"∨","\\veebar":"⊻","\\land":"∧","\\lor":"∨","\\top":"⊤","\\bot":"⊥","\\cup":"∪","\\cap":"∩","\\bigcup":"⋃","\\bigcap":"⋂","\\setminus":"∖","\\therefore":"∴","\\because":"∵","\\Box":"□","\\models":"⊨","\\vdash":"⊢","\\rightarrow":"→","\\Rightarrow":"⇒","\\leftarrow":"←","\\Leftarrow":"⇐","\\uparrow":"↑","\\Uparrow":"⇑","\\downarrow":"↓","\\Downarrow":"⇓","\\nwarrow":"↖","\\nearrow":"↗","\\searrow":"↘","\\swarrow":"↙","\\mapsto":"↦","\\to":"→","\\leftrightarrow":"↔","\\hookleftarrow":"↩","\\Leftrightarrow":"⇔","\\rightarrowtail":"↣","\\leftarrowtail":"↢","\\twoheadrightarrow":"↠","\\twoheadleftarrow":"↞","\\hookrightarrow":"↪","\\rightsquigarrow":"⇝","\\rightleftharpoons":"⇌","\\leftrightharpoons":"⇋","\\rightharpoonup":"⇀","\\rightharpoondown":"⇁","\\times":"×","\\div":"÷","\\infty":"∞","\\nabla":"∇","\\partial":"∂","\\sum":"∑","\\prod":"∏","\\coprod":"∐","\\int":"∫","\\iint":"∬","\\iiint":"∭","\\iiiint":"⨌","\\oint":"∮","\\surfintegral":"∯","\\volintegral":"∰","\\Re":"ℜ","\\Im":"ℑ","\\wp":"℘","\\mp":"∓","\\langle":"⟨","\\rangle":"⟩","\\lfloor":"⌊","\\rfloor":"⌋","\\lceil":"⌈","\\rceil":"⌉","\\|":"‖","\\mathbb{a}":"𝕒","\\mathbb{A}":"𝔸","\\mathbb{b}":"𝕓","\\mathbb{B}":"𝔹","\\mathbb{c}":"𝕔","\\mathbb{C}":"ℂ","\\mathbb{d}":"𝕕","\\mathbb{D}":"𝔻","\\mathbb{e}":"𝕖","\\mathbb{E}":"𝔼","\\mathbb{f}":"𝕗","\\mathbb{F}":"𝔽","\\mathbb{g}":"𝕘","\\mathbb{G}":"𝔾","\\mathbb{h}":"𝕙","\\mathbb{H}":"ℍ","\\mathbb{i}":"𝕚","\\mathbb{I}":"𝕀","\\mathbb{j}":"𝕛","\\mathbb{J}":"𝕁","\\mathbb{k}":"𝕜","\\mathbb{K}":"𝕂","\\mathbb{l}":"𝕝","\\mathbb{L}":"𝕃","\\mathbb{m}":"𝕞","\\mathbb{M}":"𝕄","\\mathbb{n}":"𝕟","\\mathbb{N}":"ℕ","\\mathbb{o}":"𝕠","\\mathbb{O}":"𝕆","\\mathbb{p}":"𝕡","\\mathbb{P}":"ℙ","\\mathbb{q}":"𝕢","\\mathbb{Q}":"ℚ","\\mathbb{r}":"𝕣","\\mathbb{R}":"ℝ","\\mathbb{s}":"𝕤","\\mathbb{S}":"𝕊","\\mathbb{t}":"𝕥","\\mathbb{T}":"𝕋","\\mathbb{u}":"𝕦","\\mathbb{U}":"𝕌","\\mathbb{v}":"𝕧","\\mathbb{V}":"𝕍","\\mathbb{x}":"𝕩","\\mathbb{X}":"𝕏","\\mathbb{y}":"𝕪","\\mathbb{Y}":"𝕐","\\mathbb{z}":"𝕫","\\mathbb{Z}":"ℤ","\\mathbb{0}":"𝟘","\\mathbb{1}":"𝟙","\\mathbb{2}":"𝟚","\\mathbb{3}":"𝟛","\\mathbb{4}":"𝟜","\\mathbb{5}":"𝟝","\\mathbb{6}":"𝟞","\\mathbb{7}":"𝟟","\\mathbb{8}":"𝟠","\\mathbb{9}":"𝟡","\\mathfrak{a}":"𝔞","\\mathfrak{A}":"𝔄","\\mathfrak{b}":"𝔟","\\mathfrak{B}":"𝔅","\\mathfrak{c}":"𝔠","\\mathfrak{C}":"ℭ","\\mathfrak{d}":"𝔡","\\mathfrak{D}":"𝔇","\\mathfrak{e}":"𝔢","\\mathfrak{E}":"𝔈","\\mathfrak{f}":"𝔣","\\mathfrak{F}":"𝔉","\\mathfrak{g}":"𝔤","\\mathfrak{G}":"𝔊","\\mathfrak{h}":"𝔥","\\mathfrak{H}":"ℌ","\\mathfrak{i}":"𝔦","\\mathfrak{I}":"ℑ","\\mathfrak{j}":"𝔧","\\mathfrak{J}":"𝔍","\\mathfrak{k}":"𝔨","\\mathfrak{K}":"𝔎","\\mathfrak{l}":"𝔩","\\mathfrak{L}":"𝔏","\\mathfrak{m}":"𝔪","\\mathfrak{M}":"𝔐","\\mathfrak{n}":"𝔫","\\mathfrak{N}":"𝔑","\\mathfrak{o}":"𝔬","\\mathfrak{O}":"𝔒","\\mathfrak{p}":"𝔭","\\mathfrak{P}":"𝔓","\\mathfrak{q}":"𝔮","\\mathfrak{Q}":"𝔔","\\mathfrak{r}":"𝔯","\\mathfrak{R}":"ℜ","\\mathfrak{s}":"𝔰","\\mathfrak{S}":"𝔖","\\mathfrak{t}":"𝔱","\\mathfrak{T}":"𝔗","\\mathfrak{u}":"𝔲","\\mathfrak{U}":"𝔘","\\mathfrak{v}":"𝔳","\\mathfrak{V}":"𝔙","\\mathfrak{x}":"𝔵","\\mathfrak{X}":"𝔛","\\mathfrak{y}":"𝔶","\\mathfrak{Y}":"𝔜","\\mathfrak{z}":"𝔷","\\mathfrak{Z}":"ℨ","\\mathcal{a}":"𝒶","\\mathcal{A}":"𝒜","\\mathcal{b}":"𝒷","\\mathcal{B}":"ℬ","\\mathcal{c}":"𝒸","\\mathcal{C}":"𝒞","\\mathcal{d}":"𝒹","\\mathcal{D}":"𝒟","\\mathcal{e}":"ℯ","\\mathcal{E}":"ℰ","\\mathcal{f}":"𝒻","\\mathcal{F}":"ℱ","\\mathcal{g}":"ℊ","\\mathcal{G}":"𝒢","\\mathcal{h}":"𝒽","\\mathcal{H}":"ℋ","\\mathcal{i}":"𝒾","\\mathcal{I}":"ℐ","\\mathcal{j}":"𝒿","\\mathcal{J}":"𝒥","\\mathcal{k}":"𝓀","\\mathcal{K}":"𝒦","\\mathcal{l}":"𝓁","\\mathcal{L}":"ℒ","\\mathcal{m}":"𝓂","\\mathcal{M}":"ℳ","\\mathcal{n}":"𝓃","\\mathcal{N}":"𝒩","\\mathcal{o}":"ℴ","\\mathcal{O}":"𝒪","\\mathcal{p}":"𝓅","\\mathcal{P}":"𝒫","\\mathcal{q}":"𝓆","\\mathcal{Q}":"𝒬","\\mathcal{r}":"𝓇","\\mathcal{R}":"ℛ","\\mathcal{s}":"𝓈","\\mathcal{S}":"𝒮","\\mathcal{t}":"𝓉","\\mathcal{T}":"𝒯","\\mathcal{u}":"𝓊","\\mathcal{U}":"𝒰","\\mathcal{v}":"𝓋","\\mathcal{V}":"𝒱","\\mathcal{w}":"𝓌","\\mathcal{W}":"𝒲","\\mathcal{x}":"𝓍","\\mathcal{X}":"𝒳","\\mathcal{y}":"𝓎","\\mathcal{Y}":"𝒴","\\mathcal{z}":"𝓏","\\mathcal{Z}":"𝒵",_0:"₀",_1:"₁",_2:"₂",_3:"₃",_4:"₄",_5:"₅",_6:"₆",_7:"₇",_8:"₈",_9:"₉","^0":"⁰","^1":"¹","^2":"²","^3":"³","^4":"⁴","^5":"⁵","^6":"⁶","^7":"⁷","^8":"⁸","^9":"⁹","_+":"₊","_-":"₋","_(":"₍","_)":"₎","^+":"⁺","^-":"⁻","^(":"⁽","^)":"⁾",_a:"ₐ",_e:"ₑ",_h:"ₕ",_i:"ᵢ",_j:"ⱼ",_k:"ₖ",_l:"ₗ",_m:"ₘ",_n:"ₙ",_o:"ₒ",_p:"ₚ",_r:"ᵣ",_s:"ₛ",_t:"ₜ",_u:"ᵤ",_v:"ᵥ",_x:"ₓ","^a":"ᵃ","^b":"ᵇ","^c":"ᶜ","^d":"ᵈ","^e":"ᵉ","^f":"ᶠ","^g":"ᵍ","^h":"ʰ","^i":"^i","^j":"ʲ","^k":"ᵏ","^l":"ˡ","^m":"ᵐ","^n":"ⁿ","^o":"ᵒ","^p":"ᵖ","^r":"ʳ","^s":"ˢ","^t":"ᵗ","^u":"ᵘ","^v":"ᵛ","^w":"ʷ","^x":"ˣ","^y":"ʸ","^z":"ᶻ","\\pm":"±","\\dotplus":"∔","\\bullet":"∙","\\cdot":"⋅","\\oplus":"⊕","\\ominus":"⊖","\\otimes":"⊗","\\oslash":"⊘","\\odot":"⊙","\\circ":"∘","\\surd":"√","\\propto":"∝","\\angle":"∠","\\measuredangle":"∡","\\sphericalangle":"∢","\\mid":"∣","\\nmid":"∤","\\not\\mid":"∤","\\parallel":"∥","\\nparallel":"∦","\\not\\parallel":"∦","\\flat":"♭","\\natural":"♮","\\sharp":"♯"};function R([r,a],[t,e]){return t<=r&&r{function r(){return Object.entries(g).map(([n,o])=>`
3 | ${n} 4 | ${o} 5 |
`).join(` 6 | `)}const a=document.querySelector(".symbol-table");a&&(a.innerHTML=r());const t=document.querySelector(".try-here");t&&t.addEventListener("keydown",e=>{e.key==="w"&&(e.altKey===!0||e.ctrlKey)&&V(t,{subscripts:!0})})}); 7 | //# sourceMappingURL=main-bp1Lx8IY.js.map 8 | -------------------------------------------------------------------------------- /docs/assets/main-bp1Lx8IY.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"main-bp1Lx8IY.js","sources":["../../lib/tree.ts","../../node_modules/bread-n-butter/dist/esm/bread-n-butter.js","../../lib/parser.ts","../../lib/symbols.ts","../../lib/index.ts","../../docs-src/main.ts"],"sourcesContent":["/** Used to find all DOM nodes in window.getSelection() */\nexport function findNodesBetweenNodes(u: Node, v: Node): Node[] {\n const ancestor = findLowestCommonAncestor(u, v);\n const childrenList = findChildrenList(ancestor);\n const [i, j] = [childrenList.indexOf(u), childrenList.indexOf(v)].sort();\n return childrenList.slice(i, j + 1);\n}\n\nfunction findAncestorChain(node: Node): Node[] {\n const chain: Node[] = [];\n let currentNode: Node | null = node;\n chain.push(currentNode);\n while (currentNode?.parentNode) {\n currentNode = currentNode.parentNode;\n chain.push(currentNode);\n }\n return chain.reverse();\n}\n\nexport function findLowestCommonAncestor(u: Node, v: Node): Node {\n const uChain = findAncestorChain(u);\n const vChain = findAncestorChain(v);\n\n let i = 0;\n for (; i < uChain.length; i++) {\n if (uChain[i] !== vChain[i]) {\n break;\n }\n }\n return uChain[i - 1];\n}\n\nfunction findChildrenList(node: Node): Node[] {\n const list: Node[] = [];\n const find = (n: Node | null) => {\n if (!n) return;\n list.push(n);\n for (const child of Array.from(n.childNodes || [])) {\n find(child);\n }\n };\n find(node);\n return list;\n}\n","/**\n * Represents a parsing action; typically not created directly via `new`.\n */\nexport class Parser {\n /**\n * Creates a new custom parser that performs the given parsing action.\n */\n constructor(action) {\n this.action = action;\n }\n /**\n * Returns a parse result with either the value or error information.\n */\n parse(input) {\n const location = { index: 0, line: 1, column: 1 };\n const context = new Context({ input, location });\n const result = this.skip(eof).action(context);\n if (result.type === \"ActionOK\") {\n return {\n type: \"ParseOK\",\n value: result.value,\n };\n }\n return {\n type: \"ParseFail\",\n location: result.furthest,\n expected: result.expected,\n };\n }\n /**\n * Returns the parsed result or throws an error.\n */\n tryParse(input) {\n const result = this.parse(input);\n if (result.type === \"ParseOK\") {\n return result.value;\n }\n const { expected, location } = result;\n const { line, column } = location;\n const message = `parse error at line ${line} column ${column}: ` +\n `expected ${expected.join(\", \")}`;\n throw new Error(message);\n }\n /**\n * Combines two parsers one after the other, yielding the results of both in\n * an array.\n */\n and(parserB) {\n return new Parser((context) => {\n const a = this.action(context);\n if (a.type === \"ActionFail\") {\n return a;\n }\n context = context.moveTo(a.location);\n const b = context.merge(a, parserB.action(context));\n if (b.type === \"ActionOK\") {\n const value = [a.value, b.value];\n return context.merge(b, context.ok(b.location.index, value));\n }\n return b;\n });\n }\n /** Parse both and return the value of the first */\n skip(parserB) {\n return this.and(parserB).map(([a]) => a);\n }\n /** Parse both and return the value of the second */\n next(parserB) {\n return this.and(parserB).map(([, b]) => b);\n }\n /**\n * Try to parse using the current parser. If that fails, parse using the\n * second parser.\n */\n or(parserB) {\n return new Parser((context) => {\n const a = this.action(context);\n if (a.type === \"ActionOK\") {\n return a;\n }\n return context.merge(a, parserB.action(context));\n });\n }\n /**\n * Parse using the current parser. If it succeeds, pass the value to the\n * callback function, which returns the next parser to use.\n */\n chain(fn) {\n return new Parser((context) => {\n const a = this.action(context);\n if (a.type === \"ActionFail\") {\n return a;\n }\n const parserB = fn(a.value);\n context = context.moveTo(a.location);\n return context.merge(a, parserB.action(context));\n });\n }\n /**\n * Yields the value from the parser after being called with the callback.\n */\n map(fn) {\n return this.chain((a) => {\n return ok(fn(a));\n });\n }\n /**\n * Returns the callback called with the parser.\n */\n thru(fn) {\n return fn(this);\n }\n /**\n * Returns a parser which parses the same value, but discards other error\n * messages, using the ones supplied instead.\n */\n desc(expected) {\n return new Parser((context) => {\n const result = this.action(context);\n if (result.type === \"ActionOK\") {\n return result;\n }\n return { type: \"ActionFail\", furthest: result.furthest, expected };\n });\n }\n /**\n * Wraps the current parser with before & after parsers.\n */\n wrap(before, after) {\n return before.next(this).skip(after);\n }\n /**\n * Ignores content before and after the current parser, based on the supplied\n * parser.\n */\n trim(beforeAndAfter) {\n return this.wrap(beforeAndAfter, beforeAndAfter);\n }\n /**\n * Repeats the current parser between min and max times, yielding the results\n * in an array.\n */\n repeat(min = 0, max = Infinity) {\n if (!isRangeValid(min, max)) {\n throw new Error(`repeat: bad range (${min} to ${max})`);\n }\n if (min === 0) {\n return this.repeat(1, max).or(ok([]));\n }\n return new Parser((context) => {\n const items = [];\n let result = this.action(context);\n if (result.type === \"ActionFail\") {\n return result;\n }\n while (result.type === \"ActionOK\" && items.length < max) {\n items.push(result.value);\n if (result.location.index === context.location.index) {\n throw new Error(\"infinite loop detected; don't call .repeat() with parsers that can accept zero characters\");\n }\n context = context.moveTo(result.location);\n result = context.merge(result, this.action(context));\n }\n if (result.type === \"ActionFail\" && items.length < min) {\n return result;\n }\n return context.merge(result, context.ok(context.location.index, items));\n });\n }\n /**\n * Returns a parser that parses between min and max times, separated by the separator\n * parser supplied.\n */\n sepBy(separator, min = 0, max = Infinity) {\n if (!isRangeValid(min, max)) {\n throw new Error(`sepBy: bad range (${min} to ${max})`);\n }\n if (min === 0) {\n return this.sepBy(separator, 1, max).or(ok([]));\n }\n // We also know that min=1 due to previous checks, so we can skip the call\n // to `repeat` here\n if (max === 1) {\n return this.map((x) => [x]);\n }\n return this.chain((first) => {\n return separator\n .next(this)\n .repeat(min - 1, max - 1)\n .map((rest) => {\n return [first, ...rest];\n });\n });\n }\n /**\n * Returns a parser that adds name and start/end location metadata.\n */\n node(name) {\n return all(location, this, location).map(([start, value, end]) => {\n const type = \"ParseNode\";\n return { type, name, value, start, end };\n });\n }\n}\nfunction isRangeValid(min, max) {\n return (min <= max &&\n min >= 0 &&\n max >= 0 &&\n Number.isInteger(min) &&\n min !== Infinity &&\n (Number.isInteger(max) || max === Infinity));\n}\n/**\n * Parser that yields the current `SourceLocation`, containing properties\n * `index`, `line` and `column`.\n */\nexport const location = new Parser((context) => {\n return context.ok(context.location.index, context.location);\n});\n/**\n * Returns a parser that yields the given value and consumes no input.\n */\nexport function ok(value) {\n return new Parser((context) => {\n return context.ok(context.location.index, value);\n });\n}\n/**\n * Returns a parser that fails with the given messages and consumes no input.\n */\nexport function fail(expected) {\n return new Parser((context) => {\n return context.fail(context.location.index, expected);\n });\n}\n/**\n * This parser succeeds if the input has already been fully parsed.\n */\nexport const eof = new Parser((context) => {\n if (context.location.index < context.input.length) {\n return context.fail(context.location.index, [\"\"]);\n }\n return context.ok(context.location.index, \"\");\n});\n/** Returns a parser that matches the exact text supplied. */\nexport function text(string) {\n return new Parser((context) => {\n const start = context.location.index;\n const end = start + string.length;\n if (context.input.slice(start, end) === string) {\n return context.ok(end, string);\n }\n return context.fail(start, [string]);\n });\n}\n/**\n * Returns a parser that matches the entire regular expression at the current\n * parser position.\n */\nexport function match(regexp) {\n for (const flag of regexp.flags) {\n switch (flag) {\n case \"i\": // ignoreCase\n case \"s\": // dotAll\n case \"m\": // multiline\n case \"u\": // unicode\n continue;\n default:\n throw new Error(\"only the regexp flags 'imsu' are supported\");\n }\n }\n const sticky = new RegExp(regexp.source, regexp.flags + \"y\");\n return new Parser((context) => {\n const start = context.location.index;\n sticky.lastIndex = start;\n const match = context.input.match(sticky);\n if (match) {\n const end = start + match[0].length;\n const string = context.input.slice(start, end);\n return context.ok(end, string);\n }\n return context.fail(start, [String(regexp)]);\n });\n}\n/** Parse all items, returning their values in the same order. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function all(...parsers) {\n // TODO: This could be optimized with a custom parser, but I should probably add\n // benchmarking first to see if it really matters enough to rewrite it\n return parsers.reduce((acc, p) => {\n return acc.chain((array) => {\n return p.map((value) => {\n return [...array, value];\n });\n });\n }, ok([]));\n}\n/** Parse using the parsers given, returning the first one that succeeds. */\nexport function choice(...parsers) {\n // TODO: This could be optimized with a custom parser, but I should probably add\n // benchmarking first to see if it really matters enough to rewrite it\n return parsers.reduce((acc, p) => {\n return acc.or(p);\n });\n}\n/**\n * Takes a lazily invoked callback that returns a parser, so you can create\n * recursive parsers.\n */\nexport function lazy(fn) {\n // NOTE: This parsing action overwrites itself on the specified parser. We're\n // assuming that the same parser won't be returned to multiple `lazy` calls. I\n // never heard of such a thing happening in Parsimmon, and it doesn't seem\n // likely to happen here either. I assume this is faster than using variable\n // closure and an `if`-statement here, but I honestly don't know.\n const parser = new Parser((context) => {\n parser.action = fn().action;\n return parser.action(context);\n });\n return parser;\n}\nfunction union(a, b) {\n return [...new Set([...a, ...b])];\n}\n/**\n * Represents the current parsing context.\n */\nclass Context {\n constructor(options) {\n this.input = options.input;\n this.location = options.location;\n }\n /**\n * Returns a new context with the supplied location and the current input.\n */\n moveTo(location) {\n return new Context({\n input: this.input,\n location,\n });\n }\n _internal_move(index) {\n if (index === this.location.index) {\n return this.location;\n }\n const start = this.location.index;\n const end = index;\n const chunk = this.input.slice(start, end);\n let { line, column } = this.location;\n for (const ch of chunk) {\n if (ch === \"\\n\") {\n line++;\n column = 1;\n }\n else {\n column++;\n }\n }\n return { index, line, column };\n }\n /**\n * Represents a successful parse ending before the given `index`, with the\n * specified `value`.\n */\n ok(index, value) {\n return {\n type: \"ActionOK\",\n value,\n location: this._internal_move(index),\n furthest: { index: -1, line: -1, column: -1 },\n expected: [],\n };\n }\n /**\n * Represents a failed parse starting at the given `index`, with the specified\n * list `expected` messages (note: this list usually only has one item).\n */\n fail(index, expected) {\n return {\n type: \"ActionFail\",\n furthest: this._internal_move(index),\n expected,\n };\n }\n /**\n * Merge two sequential `ActionResult`s so that the `expected` and location data\n * is preserved correctly.\n */\n merge(a, b) {\n if (b.furthest.index > a.furthest.index) {\n return b;\n }\n const expected = b.furthest.index === a.furthest.index\n ? union(a.expected, b.expected)\n : a.expected;\n if (b.type === \"ActionOK\") {\n return {\n type: \"ActionOK\",\n location: b.location,\n value: b.value,\n furthest: a.furthest,\n expected,\n };\n }\n return {\n type: \"ActionFail\",\n furthest: a.furthest,\n expected,\n };\n }\n}\n","import * as bnb from \"bread-n-butter\";\n\nexport interface ASTNode {\n type: string;\n start: number;\n end: number;\n}\n\nexport interface PlainTextNode extends ASTNode {\n type: \"PlainText\";\n value: string;\n}\n\nexport interface CurlyGroupNode extends ASTNode {\n type: \"CurlyGroup\";\n value: string;\n}\n\nexport interface NullaryMacroNode extends ASTNode {\n type: \"NullaryMacro\";\n macro: string;\n}\n\nexport interface UnaryMacroNode extends ASTNode {\n type: \"UnaryMacro\";\n macro: string;\n argument: ASTNode;\n}\n\nexport interface SuperscriptNode extends ASTNode {\n type: \"Superscript\";\n content: string;\n}\n\nexport interface SubscriptNode extends ASTNode {\n type: \"Subscript\";\n content: string;\n}\n\nexport interface ProgramNode extends ASTNode {\n type: \"Program\";\n body: ASTNode[];\n}\n\nexport type TexNode =\n | PlainTextNode\n | CurlyGroupNode\n | NullaryMacroNode\n | UnaryMacroNode\n | SuperscriptNode\n | SubscriptNode\n | ProgramNode;\n\nfunction makeNode(type: string) {\n return function makeNodeWrapper(parser: any) {\n return bnb.all(bnb.location, parser, bnb.location).map(function makeNode_([start, value, end]: [\n any,\n any,\n any,\n ]) {\n return {\n type,\n start: start.index,\n end: end.index,\n // @ts-ignore\n ...value,\n };\n });\n };\n}\n\nconst Spaces = bnb.match(/\\s*/);\n\nconst Superscript = bnb\n .all(bnb.match(/\\^\\s*/), bnb.choice(bnb.match(/{[a-zA-Z0-9+-]+}/), bnb.match(/[a-zA-Z0-9+-]/)))\n .map(([, b]: [any, string]) => ({\n content: b,\n }))\n .thru(makeNode(\"Superscript\"));\n\nconst Subscript = bnb\n .all(bnb.match(/_\\s*/), bnb.choice(bnb.match(/{[a-zA-Z0-9+-]+}/), bnb.match(/[a-zA-Z0-9+-]/)))\n .map(([_, b]: [any, string]) => {\n return {\n content: b,\n };\n })\n .thru(makeNode(\"Subscript\"));\n\nconst NullaryMacro = bnb\n .choice(bnb.match(/\\\\[a-zA-Z]+/), bnb.match(/\\\\\\|/))\n .map((x: string) => {\n return {\n macro: x,\n };\n })\n .thru(makeNode(\"NullaryMacro\"));\n\nconst CurlyGroup = bnb\n .match(/\\{.*?\\}/)\n .map((x: string) => ({ value: x }))\n .thru(makeNode(\"CurlyGroup\"));\n\nconst UnaryMacro = bnb\n .all(\n bnb.choice(\n bnb.match(/\\\\mathbb(?![a-zA-Z])/),\n bnb.match(/\\\\mathfrak(?![a-zA-Z])/),\n bnb.match(/\\\\mathcal(?![a-zA-Z])/),\n bnb.match(/\\\\not(?![a-zA-Z])/),\n ),\n Spaces,\n bnb.choice(\n CurlyGroup,\n NullaryMacro,\n bnb\n .match(/[a-zA-Z0-9]/)\n .map((x) => ({ value: x }))\n .thru(makeNode(\"PlainText\")),\n ),\n )\n .map(([a, _, c]: [string, any, ASTNode]) => ({\n macro: a,\n argument: c,\n }))\n .thru(makeNode(\"UnaryMacro\"));\n\nconst Illegal = bnb\n .match(/[\\^_\\\\]/)\n .map((r: string) => ({\n value: r,\n }))\n .thru(makeNode(\"PlainText\"));\n\nconst PlainText = bnb\n .match(/[^_^\\\\]+/)\n .map((x: string) => ({ value: x }))\n .thru(makeNode(\"PlainText\"));\n\nexport const parser = bnb\n .choice(Superscript, Subscript, UnaryMacro, NullaryMacro, Illegal, PlainText)\n .repeat()\n .map((nodes: ASTNode[]) => {\n return {\n body: nodes,\n };\n })\n .thru(makeNode(\"Program\"));\n","export const symbols: Record = {\n \"\\\\Alpha\": \"Α\",\n \"\\\\Beta\": \"Β\",\n \"\\\\Gamma\": \"Γ\",\n \"\\\\Delta\": \"Δ\",\n \"\\\\Epsilon\": \"Ε\",\n \"\\\\Zeta\": \"Ζ\",\n \"\\\\Eta\": \"Η\",\n \"\\\\Theta\": \"Θ\",\n \"\\\\Iota\": \"I\",\n \"\\\\Kappa\": \"Κ\",\n \"\\\\Lambda\": \"Λ\",\n \"\\\\Mu\": \"Μ\",\n \"\\\\Nu\": \"Ν\",\n \"\\\\Xi\": \"Ξ\",\n \"\\\\Omicron\": \"Ο\",\n \"\\\\Pi\": \"Π\",\n \"\\\\Rho\": \"Ρ\",\n \"\\\\Sigma\": \"Σ\",\n \"\\\\Tau\": \"Τ\",\n \"\\\\Upsilon\": \"Υ\",\n \"\\\\Phi\": \"Φ\",\n \"\\\\Chi\": \"Χ\",\n \"\\\\Psi\": \"Ψ\",\n \"\\\\Omega\": \"Ω\",\n\n \"\\\\alpha\": \"α\",\n \"\\\\beta\": \"β\",\n \"\\\\gamma\": \"γ\",\n \"\\\\delta\": \"δ\",\n \"\\\\epsilon\": \"ϵ\",\n \"\\\\zeta\": \"ζ\",\n \"\\\\eta\": \"η\",\n \"\\\\theta\": \"θ\",\n \"\\\\iota\": \"ι\",\n \"\\\\kappa\": \"κ\",\n \"\\\\lambda\": \"λ\",\n \"\\\\mu\": \"μ\",\n \"\\\\nu\": \"ν\",\n \"\\\\xi\": \"ξ\",\n \"\\\\omicron\": \"ο\",\n \"\\\\pi\": \"π\",\n \"\\\\rho\": \"ρ\",\n \"\\\\sigma\": \"σ\",\n \"\\\\tau\": \"τ\",\n \"\\\\upsilon\": \"υ\",\n \"\\\\phi\": \"ϕ\",\n \"\\\\chi\": \"χ\",\n \"\\\\psi\": \"ψ\",\n \"\\\\omega\": \"ω\",\n\n \"\\\\varepsilon\": \"ε\",\n \"\\\\varnothing\": \"∅\",\n \"\\\\varkappa\": \"ϰ\",\n \"\\\\varphi\": \"φ\",\n \"\\\\varpi\": \"ϖ\",\n \"\\\\varrho\": \"ϱ\",\n \"\\\\varsigma\": \"ς\",\n \"\\\\vartheta\": \"ϑ\",\n \"\\\\neq\": \"≠\",\n \"\\\\equiv\": \"≡\",\n \"\\\\not\\\\equiv\": \"≢\",\n \"\\\\leq\": \"≤\",\n \"\\\\geq\": \"≥\",\n \"\\\\leqq\": \"≦\",\n \"\\\\geqq\": \"≧\",\n \"\\\\lneqq\": \"≨\",\n \"\\\\gneqq\": \"≩\",\n \"\\\\leqslant\": \"⩽\",\n \"\\\\geqslant\": \"⩾\",\n \"\\\\ll\": \"≪\",\n \"\\\\gg\": \"≫\",\n \"\\\\nless\": \"≮\",\n \"\\\\ngtr\": \"≯\",\n \"\\\\nleq\": \"≰\",\n \"\\\\ngeq\": \"≱\",\n \"\\\\lessequivlnt\": \"≲\",\n \"\\\\greaterequivlnt\": \"≳\",\n \"\\\\prec\": \"≺\",\n \"\\\\succ\": \"≻\",\n \"\\\\preccurlyeq\": \"≼\",\n \"\\\\succcurlyeq\": \"≽\",\n \"\\\\precapprox\": \"≾\",\n \"\\\\succapprox\": \"≿\",\n \"\\\\nprec\": \"⊀\",\n \"\\\\nsucc\": \"⊁\",\n \"\\\\sim\": \"∼\",\n \"\\\\not\\\\sim\": \"≁\",\n \"\\\\simeq\": \"≃\",\n \"\\\\not\\\\simeq\": \"≄\",\n \"\\\\backsim\": \"∽\",\n \"\\\\lazysinv\": \"∾\",\n \"\\\\wr\": \"≀\",\n \"\\\\cong\": \"≅\",\n \"\\\\not\\\\cong\": \"≇\",\n \"\\\\approx\": \"≈\",\n \"\\\\not\\\\approx\": \"≉\",\n \"\\\\approxeq\": \"≊\",\n \"\\\\approxnotequal\": \"≆\",\n \"\\\\tildetrpl\": \"≋\",\n \"\\\\allequal\": \"≌\",\n \"\\\\asymp\": \"≍\",\n \"\\\\doteq\": \"≐\",\n \"\\\\doteqdot\": \"≑\",\n \"\\\\lneq\": \"⪇\",\n \"\\\\gneq\": \"⪈\",\n \"\\\\preceq\": \"⪯\",\n \"\\\\succeq\": \"⪰\",\n \"\\\\precneqq\": \"⪵\",\n \"\\\\succneqq\": \"⪶\",\n \"\\\\emptyset\": \"∅\",\n \"\\\\in\": \"∈\",\n \"\\\\notin\": \"∉\",\n \"\\\\not\\\\in\": \"∉\",\n \"\\\\ni\": \"∋\",\n \"\\\\not\\\\ni\": \"∌\",\n \"\\\\subset\": \"⊂\",\n \"\\\\subseteq\": \"⊆\",\n \"\\\\not\\\\subset\": \"⊄\",\n \"\\\\not\\\\subseteq\": \"⊈\",\n \"\\\\supset\": \"⊃\",\n \"\\\\supseteq\": \"⊇\",\n \"\\\\not\\\\supset\": \"⊅\",\n \"\\\\not\\\\supseteq\": \"⊉\",\n \"\\\\subsetneq\": \"⊊\",\n \"\\\\supsetneq\": \"⊋\",\n \"\\\\exists\": \"∃\",\n \"\\\\nexists\": \"∄\",\n \"\\\\not\\\\exists\": \"∄\",\n \"\\\\forall\": \"∀\",\n \"\\\\aleph\": \"ℵ\",\n \"\\\\beth\": \"ℶ\",\n \"\\\\neg\": \"¬\",\n \"\\\\wedge\": \"∧\",\n \"\\\\vee\": \"∨\",\n \"\\\\veebar\": \"⊻\",\n \"\\\\land\": \"∧\",\n \"\\\\lor\": \"∨\",\n \"\\\\top\": \"⊤\",\n \"\\\\bot\": \"⊥\",\n \"\\\\cup\": \"∪\",\n \"\\\\cap\": \"∩\",\n \"\\\\bigcup\": \"⋃\",\n \"\\\\bigcap\": \"⋂\",\n \"\\\\setminus\": \"∖\",\n \"\\\\therefore\": \"∴\",\n \"\\\\because\": \"∵\",\n \"\\\\Box\": \"□\",\n \"\\\\models\": \"⊨\",\n \"\\\\vdash\": \"⊢\",\n\n \"\\\\rightarrow\": \"→\",\n \"\\\\Rightarrow\": \"⇒\",\n \"\\\\leftarrow\": \"←\",\n \"\\\\Leftarrow\": \"⇐\",\n \"\\\\uparrow\": \"↑\",\n \"\\\\Uparrow\": \"⇑\",\n \"\\\\downarrow\": \"↓\",\n \"\\\\Downarrow\": \"⇓\",\n \"\\\\nwarrow\": \"↖\",\n \"\\\\nearrow\": \"↗\",\n \"\\\\searrow\": \"↘\",\n \"\\\\swarrow\": \"↙\",\n \"\\\\mapsto\": \"↦\",\n \"\\\\to\": \"→\",\n \"\\\\leftrightarrow\": \"↔\",\n \"\\\\hookleftarrow\": \"↩\",\n \"\\\\Leftrightarrow\": \"⇔\",\n \"\\\\rightarrowtail\": \"↣\",\n \"\\\\leftarrowtail\": \"↢\",\n \"\\\\twoheadrightarrow\": \"↠\",\n \"\\\\twoheadleftarrow\": \"↞\",\n \"\\\\hookrightarrow\": \"↪\",\n \"\\\\rightsquigarrow\": \"⇝\",\n \"\\\\rightleftharpoons\": \"⇌\",\n \"\\\\leftrightharpoons\": \"⇋\",\n \"\\\\rightharpoonup\": \"⇀\",\n \"\\\\rightharpoondown\": \"⇁\",\n\n \"\\\\times\": \"×\",\n \"\\\\div\": \"÷\",\n \"\\\\infty\": \"∞\",\n \"\\\\nabla\": \"∇\",\n \"\\\\partial\": \"∂\",\n \"\\\\sum\": \"∑\",\n \"\\\\prod\": \"∏\",\n \"\\\\coprod\": \"∐\",\n \"\\\\int\": \"∫\",\n \"\\\\iint\": \"∬\",\n \"\\\\iiint\": \"∭\",\n \"\\\\iiiint\": \"⨌\",\n \"\\\\oint\": \"∮\",\n \"\\\\surfintegral\": \"∯\",\n \"\\\\volintegral\": \"∰\",\n \"\\\\Re\": \"ℜ\",\n \"\\\\Im\": \"ℑ\",\n \"\\\\wp\": \"℘\",\n \"\\\\mp\": \"∓\",\n \"\\\\langle\": \"⟨\",\n \"\\\\rangle\": \"⟩\",\n \"\\\\lfloor\": \"⌊\",\n \"\\\\rfloor\": \"⌋\",\n \"\\\\lceil\": \"⌈\",\n \"\\\\rceil\": \"⌉\",\n \"\\\\|\": \"‖\",\n\n \"\\\\mathbb{a}\": \"𝕒\",\n \"\\\\mathbb{A}\": \"𝔸\",\n \"\\\\mathbb{b}\": \"𝕓\",\n \"\\\\mathbb{B}\": \"𝔹\",\n \"\\\\mathbb{c}\": \"𝕔\",\n \"\\\\mathbb{C}\": \"ℂ\",\n \"\\\\mathbb{d}\": \"𝕕\",\n \"\\\\mathbb{D}\": \"𝔻\",\n \"\\\\mathbb{e}\": \"𝕖\",\n \"\\\\mathbb{E}\": \"𝔼\",\n \"\\\\mathbb{f}\": \"𝕗\",\n \"\\\\mathbb{F}\": \"𝔽\",\n \"\\\\mathbb{g}\": \"𝕘\",\n \"\\\\mathbb{G}\": \"𝔾\",\n \"\\\\mathbb{h}\": \"𝕙\",\n \"\\\\mathbb{H}\": \"ℍ\",\n \"\\\\mathbb{i}\": \"𝕚\",\n \"\\\\mathbb{I}\": \"𝕀\",\n \"\\\\mathbb{j}\": \"𝕛\",\n \"\\\\mathbb{J}\": \"𝕁\",\n \"\\\\mathbb{k}\": \"𝕜\",\n \"\\\\mathbb{K}\": \"𝕂\",\n \"\\\\mathbb{l}\": \"𝕝\",\n \"\\\\mathbb{L}\": \"𝕃\",\n \"\\\\mathbb{m}\": \"𝕞\",\n \"\\\\mathbb{M}\": \"𝕄\",\n \"\\\\mathbb{n}\": \"𝕟\",\n \"\\\\mathbb{N}\": \"ℕ\",\n \"\\\\mathbb{o}\": \"𝕠\",\n \"\\\\mathbb{O}\": \"𝕆\",\n \"\\\\mathbb{p}\": \"𝕡\",\n \"\\\\mathbb{P}\": \"ℙ\",\n \"\\\\mathbb{q}\": \"𝕢\",\n \"\\\\mathbb{Q}\": \"ℚ\",\n \"\\\\mathbb{r}\": \"𝕣\",\n \"\\\\mathbb{R}\": \"ℝ\",\n \"\\\\mathbb{s}\": \"𝕤\",\n \"\\\\mathbb{S}\": \"𝕊\",\n \"\\\\mathbb{t}\": \"𝕥\",\n \"\\\\mathbb{T}\": \"𝕋\",\n \"\\\\mathbb{u}\": \"𝕦\",\n \"\\\\mathbb{U}\": \"𝕌\",\n \"\\\\mathbb{v}\": \"𝕧\",\n \"\\\\mathbb{V}\": \"𝕍\",\n \"\\\\mathbb{x}\": \"𝕩\",\n \"\\\\mathbb{X}\": \"𝕏\",\n \"\\\\mathbb{y}\": \"𝕪\",\n \"\\\\mathbb{Y}\": \"𝕐\",\n \"\\\\mathbb{z}\": \"𝕫\",\n \"\\\\mathbb{Z}\": \"ℤ\",\n \"\\\\mathbb{0}\": \"𝟘\",\n \"\\\\mathbb{1}\": \"𝟙\",\n \"\\\\mathbb{2}\": \"𝟚\",\n \"\\\\mathbb{3}\": \"𝟛\",\n \"\\\\mathbb{4}\": \"𝟜\",\n \"\\\\mathbb{5}\": \"𝟝\",\n \"\\\\mathbb{6}\": \"𝟞\",\n \"\\\\mathbb{7}\": \"𝟟\",\n \"\\\\mathbb{8}\": \"𝟠\",\n \"\\\\mathbb{9}\": \"𝟡\",\n\n \"\\\\mathfrak{a}\": \"𝔞\",\n \"\\\\mathfrak{A}\": \"𝔄\",\n \"\\\\mathfrak{b}\": \"𝔟\",\n \"\\\\mathfrak{B}\": \"𝔅\",\n \"\\\\mathfrak{c}\": \"𝔠\",\n \"\\\\mathfrak{C}\": \"ℭ\",\n \"\\\\mathfrak{d}\": \"𝔡\",\n \"\\\\mathfrak{D}\": \"𝔇\",\n \"\\\\mathfrak{e}\": \"𝔢\",\n \"\\\\mathfrak{E}\": \"𝔈\",\n \"\\\\mathfrak{f}\": \"𝔣\",\n \"\\\\mathfrak{F}\": \"𝔉\",\n \"\\\\mathfrak{g}\": \"𝔤\",\n \"\\\\mathfrak{G}\": \"𝔊\",\n \"\\\\mathfrak{h}\": \"𝔥\",\n \"\\\\mathfrak{H}\": \"ℌ\",\n \"\\\\mathfrak{i}\": \"𝔦\",\n \"\\\\mathfrak{I}\": \"ℑ\",\n \"\\\\mathfrak{j}\": \"𝔧\",\n \"\\\\mathfrak{J}\": \"𝔍\",\n \"\\\\mathfrak{k}\": \"𝔨\",\n \"\\\\mathfrak{K}\": \"𝔎\",\n \"\\\\mathfrak{l}\": \"𝔩\",\n \"\\\\mathfrak{L}\": \"𝔏\",\n \"\\\\mathfrak{m}\": \"𝔪\",\n \"\\\\mathfrak{M}\": \"𝔐\",\n \"\\\\mathfrak{n}\": \"𝔫\",\n \"\\\\mathfrak{N}\": \"𝔑\",\n \"\\\\mathfrak{o}\": \"𝔬\",\n \"\\\\mathfrak{O}\": \"𝔒\",\n \"\\\\mathfrak{p}\": \"𝔭\",\n \"\\\\mathfrak{P}\": \"𝔓\",\n \"\\\\mathfrak{q}\": \"𝔮\",\n \"\\\\mathfrak{Q}\": \"𝔔\",\n \"\\\\mathfrak{r}\": \"𝔯\",\n \"\\\\mathfrak{R}\": \"ℜ\",\n \"\\\\mathfrak{s}\": \"𝔰\",\n \"\\\\mathfrak{S}\": \"𝔖\",\n \"\\\\mathfrak{t}\": \"𝔱\",\n \"\\\\mathfrak{T}\": \"𝔗\",\n \"\\\\mathfrak{u}\": \"𝔲\",\n \"\\\\mathfrak{U}\": \"𝔘\",\n \"\\\\mathfrak{v}\": \"𝔳\",\n \"\\\\mathfrak{V}\": \"𝔙\",\n \"\\\\mathfrak{x}\": \"𝔵\",\n \"\\\\mathfrak{X}\": \"𝔛\",\n \"\\\\mathfrak{y}\": \"𝔶\",\n \"\\\\mathfrak{Y}\": \"𝔜\",\n \"\\\\mathfrak{z}\": \"𝔷\",\n \"\\\\mathfrak{Z}\": \"ℨ\",\n\n \"\\\\mathcal{a}\": \"𝒶\",\n \"\\\\mathcal{A}\": \"𝒜\",\n \"\\\\mathcal{b}\": \"𝒷\",\n \"\\\\mathcal{B}\": \"ℬ\",\n \"\\\\mathcal{c}\": \"𝒸\",\n \"\\\\mathcal{C}\": \"𝒞\",\n \"\\\\mathcal{d}\": \"𝒹\",\n \"\\\\mathcal{D}\": \"𝒟\",\n \"\\\\mathcal{e}\": \"ℯ\",\n \"\\\\mathcal{E}\": \"ℰ\",\n \"\\\\mathcal{f}\": \"𝒻\",\n \"\\\\mathcal{F}\": \"ℱ\",\n \"\\\\mathcal{g}\": \"ℊ\",\n \"\\\\mathcal{G}\": \"𝒢\",\n \"\\\\mathcal{h}\": \"𝒽\",\n \"\\\\mathcal{H}\": \"ℋ\",\n \"\\\\mathcal{i}\": \"𝒾\",\n \"\\\\mathcal{I}\": \"ℐ\",\n \"\\\\mathcal{j}\": \"𝒿\",\n \"\\\\mathcal{J}\": \"𝒥\",\n \"\\\\mathcal{k}\": \"𝓀\",\n \"\\\\mathcal{K}\": \"𝒦\",\n \"\\\\mathcal{l}\": \"𝓁\",\n \"\\\\mathcal{L}\": \"ℒ\",\n \"\\\\mathcal{m}\": \"𝓂\",\n \"\\\\mathcal{M}\": \"ℳ\",\n \"\\\\mathcal{n}\": \"𝓃\",\n \"\\\\mathcal{N}\": \"𝒩\",\n \"\\\\mathcal{o}\": \"ℴ\",\n \"\\\\mathcal{O}\": \"𝒪\",\n \"\\\\mathcal{p}\": \"𝓅\",\n \"\\\\mathcal{P}\": \"𝒫\",\n \"\\\\mathcal{q}\": \"𝓆\",\n \"\\\\mathcal{Q}\": \"𝒬\",\n \"\\\\mathcal{r}\": \"𝓇\",\n \"\\\\mathcal{R}\": \"ℛ\",\n \"\\\\mathcal{s}\": \"𝓈\",\n \"\\\\mathcal{S}\": \"𝒮\",\n \"\\\\mathcal{t}\": \"𝓉\",\n \"\\\\mathcal{T}\": \"𝒯\",\n \"\\\\mathcal{u}\": \"𝓊\",\n \"\\\\mathcal{U}\": \"𝒰\",\n \"\\\\mathcal{v}\": \"𝓋\",\n \"\\\\mathcal{V}\": \"𝒱\",\n \"\\\\mathcal{w}\": \"𝓌\",\n \"\\\\mathcal{W}\": \"𝒲\",\n \"\\\\mathcal{x}\": \"𝓍\",\n \"\\\\mathcal{X}\": \"𝒳\",\n \"\\\\mathcal{y}\": \"𝓎\",\n \"\\\\mathcal{Y}\": \"𝒴\",\n \"\\\\mathcal{z}\": \"𝓏\",\n \"\\\\mathcal{Z}\": \"𝒵\",\n\n _0: \"₀\",\n _1: \"₁\",\n _2: \"₂\",\n _3: \"₃\",\n _4: \"₄\",\n _5: \"₅\",\n _6: \"₆\",\n _7: \"₇\",\n _8: \"₈\",\n _9: \"₉\",\n \"^0\": \"⁰\",\n \"^1\": \"¹\",\n \"^2\": \"²\",\n \"^3\": \"³\",\n \"^4\": \"⁴\",\n \"^5\": \"⁵\",\n \"^6\": \"⁶\",\n \"^7\": \"⁷\",\n \"^8\": \"⁸\",\n \"^9\": \"⁹\",\n\n \"_+\": \"₊\",\n \"_-\": \"₋\",\n \"_(\": \"₍\",\n \"_)\": \"₎\",\n \"^+\": \"⁺\",\n \"^-\": \"⁻\",\n \"^(\": \"⁽\",\n \"^)\": \"⁾\",\n\n _a: \"ₐ\",\n _e: \"ₑ\",\n _h: \"ₕ\",\n _i: \"ᵢ\",\n _j: \"ⱼ\",\n _k: \"ₖ\",\n _l: \"ₗ\",\n _m: \"ₘ\",\n _n: \"ₙ\",\n _o: \"ₒ\",\n _p: \"ₚ\",\n _r: \"ᵣ\",\n _s: \"ₛ\",\n _t: \"ₜ\",\n _u: \"ᵤ\",\n _v: \"ᵥ\",\n _x: \"ₓ\",\n \"^a\": \"ᵃ\",\n \"^b\": \"ᵇ\",\n \"^c\": \"ᶜ\",\n \"^d\": \"ᵈ\",\n \"^e\": \"ᵉ\",\n \"^f\": \"ᶠ\",\n \"^g\": \"ᵍ\",\n \"^h\": \"ʰ\",\n \"^i\": \"^i\",\n \"^j\": \"ʲ\",\n \"^k\": \"ᵏ\",\n \"^l\": \"ˡ\",\n \"^m\": \"ᵐ\",\n \"^n\": \"ⁿ\",\n \"^o\": \"ᵒ\",\n \"^p\": \"ᵖ\",\n \"^r\": \"ʳ\",\n \"^s\": \"ˢ\",\n \"^t\": \"ᵗ\",\n \"^u\": \"ᵘ\",\n \"^v\": \"ᵛ\",\n \"^w\": \"ʷ\",\n \"^x\": \"ˣ\",\n \"^y\": \"ʸ\",\n \"^z\": \"ᶻ\",\n\n \"\\\\pm\": \"±\",\n \"\\\\dotplus\": \"∔\",\n \"\\\\bullet\": \"∙\",\n \"\\\\cdot\": \"⋅\",\n \"\\\\oplus\": \"⊕\",\n \"\\\\ominus\": \"⊖\",\n \"\\\\otimes\": \"⊗\",\n \"\\\\oslash\": \"⊘\",\n \"\\\\odot\": \"⊙\",\n \"\\\\circ\": \"∘\",\n \"\\\\surd\": \"√\",\n \"\\\\propto\": \"∝\",\n \"\\\\angle\": \"∠\",\n \"\\\\measuredangle\": \"∡\",\n \"\\\\sphericalangle\": \"∢\",\n \"\\\\mid\": \"∣\",\n \"\\\\nmid\": \"∤\",\n \"\\\\not\\\\mid\": \"∤\",\n \"\\\\parallel\": \"∥\",\n \"\\\\nparallel\": \"∦\",\n \"\\\\not\\\\parallel\": \"∦\",\n \"\\\\flat\": \"♭\",\n \"\\\\natural\": \"♮\",\n \"\\\\sharp\": \"♯\",\n};\n","import { findNodesBetweenNodes } from \"./tree.js\";\nimport { type TexNode, type ASTNode, parser } from \"./parser.js\";\nimport { symbols } from \"./symbols.js\";\n\nexport interface Options {\n subscripts?: boolean;\n}\n\n/**\n * Check if two interval overlaps.\n */\nfunction overlaps([a, b]: [number, number], [c, d]: [number, number]): boolean {\n return (\n (c <= a && a < d) || (c <= b - 1 && b - 1 < d) || (a <= c && c < b) || (a <= d - 1 && d - 1 < b)\n );\n}\n\nfunction debrackets(s: string): string {\n s = s.trim();\n if (s[0] === \"{\" && s[s.length - 1] === \"}\") {\n return debrackets(s.slice(1, s.length - 1));\n }\n return s;\n}\n\nfunction printSource(source: string, node: ASTNode): string {\n return source.slice(node.start, node.end);\n}\n\nfunction printNode(source: string, node: TexNode, options: Options = {}): string {\n switch (node.type) {\n case \"PlainText\":\n case \"CurlyGroup\": {\n return printSource(source, node);\n }\n case \"UnaryMacro\": {\n const argumentText = printSource(source, node.argument);\n\n const key =\n node.macro === \"\\\\not\"\n ? `${node.macro}${debrackets(argumentText)}`\n : `${node.macro}{${debrackets(argumentText)}}`;\n\n return symbols[key] || printSource(source, node);\n }\n case \"NullaryMacro\": {\n return symbols[node.macro] || printSource(source, node);\n }\n case \"Subscript\":\n case \"Superscript\": {\n if (!options.subscripts) {\n return printSource(source, node);\n }\n\n let r = \"\";\n for (const c of debrackets(node.content)) {\n const h = node.type === \"Subscript\" ? \"_\" : \"^\";\n const v = symbols[`${h}${c}`];\n if (v === undefined) {\n return printSource(source, node);\n }\n r += v;\n }\n return r;\n }\n }\n\n console.error(node);\n throw new Error(\"unhandled case\");\n}\n\nfunction print(\n source: string,\n ast: TexNode,\n selectStart: number,\n selectEnd: number,\n options: Options = {},\n) {\n const nodes = (ast as any).body as ASTNode[];\n let cursor = -1;\n let output = \"\";\n for (const node of nodes) {\n if (overlaps([selectStart, selectEnd], [node.start, node.end])) {\n output += printNode(source, node as TexNode, options);\n } else {\n output += source.slice(node.start, node.end);\n }\n\n if (node.start < selectEnd && selectEnd <= node.end) {\n cursor = node.type !== \"PlainText\" ? output.length : output.length - (node.end - selectEnd);\n }\n }\n\n return {\n text: output,\n cursor,\n };\n}\n\n/** convert tex to unicode text */\nexport function convert(\n text: string,\n selectStart: number,\n selectEnd: number,\n options: Options = {},\n): { text: string; cursor: number } {\n selectEnd = Math.min(selectEnd, text.length);\n // The parser is not supposed to throw error by design.\n const ast = parser.tryParse(text);\n return print(text, ast, selectStart, selectEnd, options);\n}\n\n/** Convert TeX in textarea or contentEditable, and then set cursor. */\nexport function render(element: HTMLElement, options: Options): void {\n if (element.tagName === \"INPUT\" || element.tagName === \"TEXTAREA\") {\n const textarea = element as HTMLInputElement | HTMLTextAreaElement;\n const selectionStart = textarea.selectionStart || 0;\n const selectionEnd = textarea.selectionEnd || 0;\n const { text, cursor } = convert(textarea.value, selectionStart, selectionEnd, options);\n textarea.select();\n element.ownerDocument.execCommand(\"insertText\", false, text);\n textarea.selectionStart = textarea.selectionEnd = cursor;\n }\n // contenteditable elements: ex. Gmail message body.\n else if (element.contentEditable) {\n const selection = element.ownerDocument.getSelection();\n if (!selection) {\n return;\n }\n if (!selection.anchorNode || !selection.focusNode) {\n return;\n }\n const nodesBetweenNodes = findNodesBetweenNodes(selection.anchorNode, selection.focusNode);\n\n const [startNode] = nodesBetweenNodes;\n const endNode = nodesBetweenNodes[nodesBetweenNodes.length - 1];\n\n const selectionIsForward =\n startNode === selection.anchorNode && selection.anchorOffset <= selection.focusOffset;\n\n const [startCursor, endCursor] = selectionIsForward\n ? [selection.anchorOffset, selection.focusOffset]\n : [selection.focusOffset, selection.anchorOffset];\n\n const TEXT_NODE_TYPE = 3;\n let _cursor;\n for (const node of nodesBetweenNodes) {\n if (node.nodeType === TEXT_NODE_TYPE) {\n const selectionStart = node === nodesBetweenNodes[0] ? startCursor : 0;\n const selectionEnd =\n node === nodesBetweenNodes[nodesBetweenNodes.length - 1]\n ? endCursor\n : node.nodeValue?.length || 0;\n const { text, cursor } = convert(\n node.nodeValue || \"\",\n selectionStart,\n selectionEnd,\n options,\n );\n node.nodeValue = text;\n _cursor = cursor;\n }\n }\n\n selection.collapse(endNode, _cursor);\n }\n}\n\nexport { symbols, parser };\n","import \"./main.css\";\nimport * as TexToUnicode from \"../lib/index.js\";\n\ndocument.addEventListener(\"DOMContentLoaded\", () => {\n // Initialize the symbol table\n function renderSymbols() {\n const symbols = TexToUnicode.symbols;\n return Object.entries(symbols)\n .map(\n ([a, b]) =>\n `
\n ${a}\n ${b}\n
`,\n )\n .join(\"\\n\");\n }\n\n const symbolTable = document.querySelector(\".symbol-table\");\n if (symbolTable) {\n symbolTable.innerHTML = renderSymbols();\n }\n\n // Setup the textarea demo\n const textarea = document.querySelector(\".try-here\") as HTMLTextAreaElement;\n if (textarea) {\n textarea.addEventListener(\"keydown\", (ev) => {\n if (ev.key === \"w\" && (ev.altKey === true || ev.ctrlKey)) {\n TexToUnicode.render(textarea, { subscripts: true });\n }\n });\n }\n});\n"],"names":["findNodesBetweenNodes","u","v","ancestor","findLowestCommonAncestor","childrenList","findChildrenList","i","j","findAncestorChain","node","chain","currentNode","uChain","vChain","list","find","n","child","Parser","action","input","location","context","Context","result","eof","expected","line","column","message","parserB","a","b","value","fn","ok","before","after","beforeAndAfter","min","max","isRangeValid","items","separator","x","first","rest","name","all","start","end","match","regexp","flag","sticky","string","parsers","acc","p","array","choice","union","options","index","chunk","ch","makeNode","type","parser","bnb.all","bnb.location","Spaces","bnb.match","Superscript","bnb.choice","Subscript","_","NullaryMacro","CurlyGroup","UnaryMacro","c","Illegal","PlainText","nodes","symbols","overlaps","d","debrackets","s","printSource","source","printNode","argumentText","key","r","h","print","ast","selectStart","selectEnd","cursor","output","convert","text","render","element","textarea","selectionStart","selectionEnd","selection","nodesBetweenNodes","startNode","endNode","selectionIsForward","startCursor","endCursor","TEXT_NODE_TYPE","_cursor","renderSymbols","TexToUnicode.symbols","symbolTable","ev","TexToUnicode.render"],"mappings":"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","x_google_ignoreList":[1]} --------------------------------------------------------------------------------