Parser: numeric literals & "..."/$...$ leaves; copy-to-clipboard for input and image#10
Conversation
Agent-Logs-Url: https://github.com/psaegert/expression-tree-gen/sessions/5b75eb54-6f1c-48ba-9bf7-aaf9694c9e14 Co-authored-by: psaegert <36567814+psaegert@users.noreply.github.com>
…mand deprecation Agent-Logs-Url: https://github.com/psaegert/expression-tree-gen/sessions/5b75eb54-6f1c-48ba-9bf7-aaf9694c9e14 Co-authored-by: psaegert <36567814+psaegert@users.noreply.github.com>
|
@copilot The latex in $$ should be rendered though. It's just that it should be put into one node instead if parsed and split into multiple nodes. |
It does render — the earlier screenshot was captured in a sandbox where the jsdelivr CDN was blocked, so the node fell back to raw text. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
There was a problem hiding this comment.
Pull request overview
Adds support for numeric literals and verbatim text / raw LaTeX leaf nodes in the expression parser, and introduces copy-to-clipboard actions for both the input expression and exported render output.
Changes:
- Extend tokenizer/parser to support numeric literals plus
"..."(escaped text) and$...$(raw LaTeX) as leaf tokens. - Update tree rendering to handle the new
literaltoken type and render numeric literals as-is. - Add UI + logic for copying the input expression and copying exported PNG/SVG to the clipboard (sharing the export pipeline with download).
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
infixToPostfix.js |
Tokenizer/parsing updates for numbers and "..." / $...$ literal leaf tokens. |
tree.js |
Renders new literal tokens and numeric literals correctly as LaTeX. |
index.html |
Adds “Copy expression” icon and “Copy” action in export popover with status line. |
canvas.js |
Refactors export generation into reusable builders and implements clipboard copy flows. |
app.css |
Layout/styling updates for new copy buttons and export status text. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| function isLeafToken(token) { | ||
| return isVariableToken(token) || isNumberToken(token) || isLiteralToken(token) || | ||
| (isCustomToken(token) && typeof token.arity === 'number' && token.arity === 0) | ||
| } | ||
|
|
There was a problem hiding this comment.
isLeafToken is defined but never used in this file (only its declaration exists). Consider removing it to avoid dead code, or using it where leaf-token checks are currently duplicated (e.g., in parsePrimary / postfix emission).
| function isLeafToken(token) { | |
| return isVariableToken(token) || isNumberToken(token) || isLiteralToken(token) || | |
| (isCustomToken(token) && typeof token.arity === 'number' && token.arity === 0) | |
| } |
| if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') { | ||
| navigator.clipboard.writeText(text).then(showCopied, function () { | ||
| // Ignore clipboard rejection (e.g. insecure context). | ||
| }) | ||
| } else { |
There was a problem hiding this comment.
The navigator.clipboard.writeText(...) rejection handler is currently a no-op, so in insecure contexts / permission-denied cases you never fall back to the execCommand('copy') path (despite the comment/PR intent). Consider invoking the fallback from the rejection handler (or at least surfacing an error state).
| return navigator.clipboard.writeText(svgString).then(function () { | ||
| setExportStatus('Copied as text', false) | ||
| }) |
There was a problem hiding this comment.
Inside the canWriteClipboardItems() branch, the fallback path calls navigator.clipboard.writeText(svgString) without checking that writeText exists. If a browser supports clipboard.write() but not writeText(), this will throw. Consider guarding writeText (or using another fallback) and setting an appropriate status message if it's unavailable.
| return navigator.clipboard.writeText(svgString).then(function () { | |
| setExportStatus('Copied as text', false) | |
| }) | |
| if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') { | |
| return navigator.clipboard.writeText(svgString).then(function () { | |
| setExportStatus('Copied as text', false) | |
| }) | |
| } | |
| setExportStatus('Clipboard unsupported', true) |
The tokenizer rejected digits, there was no way to put verbatim text or raw LaTeX into a single node, and the UI only supported downloading — not copying — the expression or rendered image.
Parser
sin(x - y + (c / (a + 5)))parses."..."→ single leaf rendered as\text{…}with contents escaped (displayed literally).$...$→ single leaf passed through to KaTeX as raw LaTeX.ALLOWED_CHARACTERS_REGEXgate; the tokenizer is now the single source of truth for valid input, which keeps the new delimiters (",$) from needing regex maintenance and lets it skip any whitespace (not just' ').canvas.jsno longer strips whitespace before tokenizing, so spaces inside"..."are preserved.Copy actions
input.valuevianavigator.clipboard.writeText, with a brief checkmark confirmation andexecCommand('copy')fallback for insecure contexts.ClipboardItem({'image/png': blob}); SVG usesimage/svg+xmlwith atext/plainfallback. A small status line reports success/failure.buildPngBlob/buildSvgStringso download and copy share one production path.Token model
New literal token shape consumed by
tree.js(getTokenArity/Label/Latex):Screenshot