fix: resolve pre-existing lint/pre-commit failures on command-visualizer - #3297
fix: resolve pre-existing lint/pre-commit failures on command-visualizer#3297trillium wants to merge 53 commits into
Conversation
Self-contained copy of cursorless's allocateHats subgraph (chooseTokenHat, getHatRankingContext, HatMetrics, getTokenComparator, maxByFirstDiffering) at SHA 42452eb, plus a grapheme splitter and a tokens-in ranking wrapper. Vendored because the algorithm is not exported from any cursorless library entry point; see VENDOR.md. Byte-identical hat assignments, no IDE dep.
New @cursorless/command-visualizer package: pure function from a recorded test fixture (YAML) to a self-contained, <img>-embeddable animated SVG of a cursorless command. Zero runtime JS in the output; one CSS --dur timeline. Fixtures parsed with js-yaml; hat allocation via the vendored algorithm; FlashStyle/color/shape data mirrored from cursorless with provenance notes.
…@cursorless/lib-common The allocate-hats vendored tree carried byte-identical copies of cursorless's DefaultMap and CompositeKeyMap utilities. Delete both clones and re-export the originals from @cursorless/lib-common through the common/ barrel, so every consumer keeps its unchanged `from "../common"` import. lib-common's CompositeKeyMap backs its store with a Map instead of a Record but exposes the identical set/has/get/delete/clear surface the allocator uses. @cursorless/lib-common is already a workspace dependency; no manifest change.
Surface the real hat-allocation building blocks and the token grapheme splitter from the lib-engine barrel so downstream consumers can import them instead of vendoring copies. @cursorless/command-visualizer currently carries clones of maxByFirstDiffering, getTokenComparator and the grapheme-split regex pinned to an old cursorless SHA; exporting the source lets it drop those. Additive only: the allocateHats/ barrel now re-exports chooseTokenHat, getHatRankingContext, getRankedTokens, getTokenComparator, maxByFirstDiffering, the HatMetrics functions, and the HatCandidate/RankingContext/RankedToken/ HatMetric types alongside the existing allocateHats export; the top-level index adds two barrel re-exports (util/allocateHats and tokenGraphemeSplitter, covering GRAPHEME_SPLIT_REGEX, TokenGraphemeSplitter, Grapheme, UNKNOWN). No behavior changes.
…ring from lib-engine Drop the clones that ARE safe to import from cursorless source now that lib-engine exports them: - GRAPHEME_SPLIT_REGEX: was defined three times (columns.ts, tokenize.ts, vendor/allocate-hats/splitter.ts). All now import the single source-of-truth from @cursorless/lib-engine, each wrapping it in a fresh RegExp so the shared /gu instance's lastIndex cannot leak across calls. - maxByFirstDiffering: byte-identical to the pin and fully generic, so the vendored copy is deleted and vendor/chooseTokenHat.ts imports it from @cursorless/lib-engine. Kept PINNED at SHA 42452eb (upstream diverged — importing would change hat placement or fail to typecheck against the standalone's simplified types): chooseTokenHat, HatMetrics, getHatRankingContext (forcedTokenHat / avoidFirstLetter / isFirstLetter and the IDE-backed splitter are all post-pin), getTokenComparator (byte-identical but typed against lib-common's full Token, not assignable from the simplified standalone Token). Each kept file header now states the exact reason; VENDOR.md records the full import-vs-pin split. Adds @cursorless/lib-engine as a workspace dependency.
…common enum
data/decorations.ts hand-maintained a string union whose five members mirrored
@cursorless/lib-common's FlashStyle enum values. Replace it with a
template-literal type `${CursorlessFlashStyle}`, so the union's members are
sourced from the enum (and track it automatically) while staying plain string
literals. This keeps every existing usage working with zero churn: DECORATION_HEX
keys, `"pendingDelete" as DecorationStyle` casts in pipeline.ts, and
`styles.has("pendingDelete")` in css-cascade.ts all still typecheck, because the
enum's values ARE those strings. No behavior change; the HEX values,
FLASH_PULSE_MS, MS_PER_STATE, HighlightStyle, and overlayPrecedence stay local
(cursorless exports none of them).
…inst live signatures Step 5 investigation: the fixture-mark and fixture-path import candidates were re-checked against the current cursorless source, not the stale prior notes. - serializedMarksToTokenHats(marks, editor) still hard-requires a live TextEditor (offsetAt/getText) and returns engine TokenHat[]; parseMarks reads editor-less YAML into the MarkInfo render model. Not swappable — kept. - getFixturesPath() hardcodes resources/fixtures (layout A only) and getCursorlessRepoRoot() throws unless CURSORLESS_REPO_ROOT is set. Our fixture-root keeps the dual-layout probe + $CURSORLESS_REPO default. Swapping would regress. Kept. - fixture-yaml.ts already uses js-yaml's load() — the same lib/entry point loadFixture uses — so the YAML parsing is already deduplicated at the library level; no further change. Comment-only: sharpens the provenance notes to cite the exact blocking signatures. No code change.
…tatus Step 6: the hat color hexes, shape SVG d= path strings, color/shape names, and shape adjustments have no importable TS module, so they are KEPT in data/colors.ts / data/shapes.ts with precise provenance. Option A (a canonical exported constants module) is rejected: the hexes live in app-vscode's package.json as VS Code setting defaults (runtime-read, never a TS constant) and the SVG d= strings live in resources/images/hats/*.svg (runtime-read by VscodeHatRenderer), so any new TS constant would be a third copy; and the clean TS constants that DO exist upstream (hatStyles.types.ts, shapeAdjustments.ts) sit in app-vscode, whose only export is ./extension.cjs — single-sourcing them needs promoting both to lib-common and rewiring 5 app-vscode files (the shipping extension's hat path), the risky refactor this task scoped out. Comments now name the exact upstream export that would be needed and record the deferred follow-up. STATUS.md documents the full import-not-clone pass and the Option-B rationale. Comment/doc-only; no code change.
…QL XSS) serialize-cascade.ts's esc() escaped &, <, > but not the quote characters, so a fixture name or spoken form containing a double quote could break out of the double-quoted data-fixture="…" / data-spoken-form="…" attributes it is interpolated into (CodeQL: "Incomplete HTML attribute sanitization: output may contain double quotes when it reaches an attribute definition"). esc() now also escapes " → " and ' → &cursorless-dev#39;, making it safe for both text-content and quoted-attribute contexts. Over-escaping quotes in text (the caption / <title>) is harmless. Verified: a fixture name of `x" onload="alert(1)"><script>…` is fully neutralized — no attribute breakout, no tag injection.
R4 hat-allocator: oldAssignments now includes shape in styleName key
(non-default shapes key as `${color}-${shape}` in cssStateHatStyles;
pinned marks with shape overrides were reserving the wrong style key)
R5 css.ts: replace hardcoded hat height/voffset constants with imports
from shapes.ts (DEFAULT_HAT_HEIGHT_EM, DEFAULT_VERTICAL_OFFSET_EM)
R6 chain.ts: guard empty states[] — throw ChainContinuityError(0) instead
of spreading undefined
R7 chain.ts: single-step path now propagates fixtureLabel into meta.fixture
R3 fixture-yaml.ts: guard blank/whitespace input before js-yaml load()
(js-yaml 5.x throws; {} fallback was unreachable without this guard)
R8 fixture-extract.ts: skip mark keys with no '.' separator
(indexOf('.') == -1 produced wrong slice offsets)
R9 decorations.ts: fix stale comment '11 decoration styles' -> '7'
R10 index.ts: fix package-name header @cursorless/cascade-renderer
-> @cursorless/command-visualizer
@cursorless/command-visualizer imports exactly one primitive from this
sub-tree: maxByFirstDiffering (vendor/chooseTokenHat.ts, SHA 42452eb).
The broader set added in the prior export commit (chooseTokenHat,
getHatRankingContext, getRankedTokens, getTokenComparator, HatMetrics,
avoidFirstLetter, ...) are NOT consumed by command-visualizer and must
stay vendored there because they have drifted in signature since the
pinned SHA (forcedTokenHat param, avoidFirstLetter metric). Exporting
them here would invite callers to take a dependency on unstable internals.
Trimmed to: { allocateHats } (pre-existing) + { maxByFirstDiffering } (new).
Moves the shape-adjustment constants (defaultShapeAdjustments, DEFAULT_HAT_HEIGHT_EM, DEFAULT_VERTICAL_OFFSET_EM, HatAdjustments, IndividualHatAdjustmentMap) out of app-vscode into lib-common, alongside the already-shared hatStyles.types, so non-VS-Code consumers (e.g. @cursorless/command-visualizer) can import them instead of vendoring a copy. app-vscode's original shapeAdjustments.ts becomes a backward-compatible re-export shim, so its consumers (VscodeHatRenderer, performPr1868ShapeUpdateInit, the hatAdjustments scripts) are unchanged. No behavior change — values are byte-identical to the previous location.
…lib-common Addresses PR review (colors.ts, shapes.ts flagged as duplicating cursorless). HatColor/HAT_COLORS and HatShape/HAT_SHAPES/HAT_NON_DEFAULT_SHAPES are now imported/re-exported from @cursorless/lib-common instead of being redefined, along with the shape-adjustment constants (SHAPE_ADJUSTMENTS, DEFAULT_HAT_HEIGHT_EM, DEFAULT_VERTICAL_OFFSET_EM) promoted there. Kept local (no importable TS home — canonical source is app-vscode's package.json VS Code config defaults / resources/*.svg, read at runtime): - COLOR_MATRIX / EDITOR_CHROME theme hexes - SHAPE_PATHS SVG 'd=' strings Provenance comments updated to say exactly why each stays. fixture-extract.ts: HAT_COLORS is now a readonly tuple, so the membership cast becomes 'as readonly string[]'.
The duplicate esc() in serialize.ts escaped only & < > — CodeRabbit flagged it as needing the same quote escaping already applied to serialize-cascade.ts. Now also escapes " and ' so interpolated strings cannot break out of a quoted HTML attribute context.
Moves HAT_COLORS / HAT_SHAPES / HAT_NON_DEFAULT_SHAPES and the HatColor / HatShape / HatNonDefaultShape / VscodeHatStyleName types out of app-vscode into lib-common's hatStyles.types (which previously held only the HatStyleName stub), so non-VS-Code consumers (e.g. @cursorless/command-visualizer) can import the vocabulary instead of cloning it. app-vscode's hatStyles.types.ts becomes a backward-compatible re-export shim, so its ~10 consumers (VscodeHats, VscodeHatRenderer, getStyleName, keyboard/*, hatAdjustments scripts, ...) are unchanged. No behavior change.
Replaces the re-export shims (hatStyles.types.ts, hats/shapeAdjustments.ts)
with direct imports from @cursorless/lib-common in every consumer, then
DELETES the shim files. No indirection layer — the vocabulary and shape
adjustments now have a single home in lib-common and every consumer imports
from it directly.
Consumers rewired: VscodeEnabledHatStyleManager, VscodeHatRenderer,
VscodeHats, getStyleName, getHatThemeColors, performPr1868ShapeUpdateInit,
scripts/hatAdjustments/{add,average}, keyboard/{TokenTypes,
KeyboardCommandsTargeted,KeyboardCommandHandler}. No behavior change.
…-common Stops re-exporting the cursorless hat vocabulary through data/colors.ts and data/shapes.ts. Consumers now import HatColor/HatShape/HAT_COLORS/HAT_SHAPES and the shape-adjustment constants straight from @cursorless/lib-common. data/colors.ts and data/shapes.ts keep ONLY the genuinely command-visualizer- local data that has no importable TS home: COLOR_MATRIX/EDITOR_CHROME (theme hexes from package.json config defaults) and SHAPE_PATHS (SVG d= strings from resources/*.svg). No re-export indirection.
fallow surfaced esc() duplicated across 4 serializers with INCONSISTENT escaping — svg-wrap.ts escaped no quotes (latent attribute-injection gap), jumbotron.ts escaped " but not ', serialize/serialize-cascade escaped both. Extracted one shared src/html.ts (esc + themeBackground + captionHtml); all four serializers now import it, so the CodeQL-safe escaping can't drift again. Also collapses the duplicated theme-bg + caption block (serialize-cascade <-> svg-wrap). Removes @cursorless/lib-node-common from dependencies — fallow flagged it as listed-but-never-imported (only referenced in comments). fallow dupes: 4 clone groups -> 2 (the remaining two are the pinned vendored allocation algorithm, HatMetrics + getTokenComparator).
The package shipped with ZERO tests — the verification harness the source
comments reference (verify-allocation.ts, oracle screenshots) was never
migrated from the standalone repo. Adds colocated mocha .test.ts files
(auto-discovered by packages/test-runner's unit glob, node:assert/strict,
matching the lib-engine idiom) covering the pure, deterministic functions —
especially the review-fix behaviors:
- html.test.ts: esc() escapes all 5 chars & <> " ', &-first ordering,
attribute-breakout payload neutralized (CodeQL regression guard);
captionHtml empty/partial/full meta + markup-injection escaping.
- fixture-yaml.test.ts: parseFixtureYaml blank/whitespace -> {} (R3),
mapping vs scalar vs sequence, literal block scalar byte-fidelity.
- chain.test.ts: chainCascades empty-input throw w/ stepIndex 0 (R6),
single-step fixtureLabel propagation preserving other meta (R7).
Wires @types/mocha + @types/node and types:[node,mocha] in tsconfig,
mirroring lib-engine. Typechecked against lib shims; full run needs the
networked install (same wall as the lockfile).
Regenerated lockfile so it matches the new dependency specifiers (@cursorless/lib-common, @cursorless/lib-engine, js-yaml, @types/js-yaml, @types/mocha, @types/node). Fixes the --frozen-lockfile CI install failure CodeRabbit flagged (R1).
Addresses PR review (decorations.ts: 'this can likely be an import'): FLASH_STYLES was re-listing the FlashStyle enum members by hand — now derived via Object.values(FlashStyle) so it tracks lib-common automatically. Order is not significant (membership tests + per-selector CSS emission).
Exports-only change (no behavior change). command-visualizer needs the real tokenizer-backed token extractor to find the engine token covering a fixture mark position, so it can pin marks via forceTokenHats with matching token identity when consuming the real allocateHats.
… vendored copy Rewrites hat-allocator.ts to run cursorless's real allocateHats (@cursorless/lib-engine) over an in-memory FakeIDE/InMemoryTextEditor document instead of the pinned vendored copy (SHA 42452eb) under src/vendor/. The engine tokenizes with cursorless's own tokenizer and ranks tokens by cursor proximity. - Deletes the entire src/vendor/allocate-hats/ tree (incl. VENDOR.md). - Deletes word-segments.ts — the engine's own tokenizer now supplies word-level segmentation; the module had no other importer. - Fixture marks are pinned via forceTokenHats: the covering engine token (found via getTokensInRange) is forced to the mark's exact color/shape, which chooseTokenHat applies first and unconditionally. - Keeps the visualizer's own palette/penalty map (cssStateHatStyles, colorPenalty, styleToHat). Rendered hat output changes vs the pinned SHA (current engine tokenizer + ranking); byte-fidelity to the old vendored output is NOT preserved, per the refactor directive.
Adds hat-allocator.test.ts covering the de-vendored allocator: - at least one hat is placed over a couple of words - hats land on non-whitespace graphemes with valid palette colors - a pre-attached fixture-mark hat keeps its exact color (single mark, multiple marks across lines) — pins verified via forceTokenHats - allocation is deterministic for identical input - empty document is a no-op, not a throw - cssStateHatStyles keys pure colors and +1-penalty shape variants This is the correctness evidence for the refactor in lieu of oracle screenshots. 7 passing.
…act) Move the shared, pure, HTML-free contract into model/: frame-state, columns, overlays, timeline. Add model/geometry.ts extracting the Pos and Range interfaces plus the orderRange helper out of the old serialize.ts so both logic/ and render/ depend on geometry without either importing the other. Repoint intra-model and data/ imports. Pure relocation + import rewrite; no behavior change.
Repoint logic/ (pipeline, fixture-extract, hat-allocator, tokenize, chain + their tests) at ../model/* and ../data/* for the shared contract and constants. Pos/Range now come from ../model/geometry. No logic/ file imports render/. Pure import rewrite; no behavior change.
Repoint render/ (serialize, serialize-cascade, svg-wrap, jumbotron, css, css-cascade, symbols, html) at ../model/* and ../data/*. serialize.ts now imports Pos/Range/orderRange from ../model/geometry instead of declaring them locally. No render/ file imports logic/. Pure import rewrite; no behavior change.
…TECTURE.md Repoint the public index.ts exports at ./logic/*, ./render/*, and ./model/*. Add ARCHITECTURE.md documenting the four scopes (data/model/ logic/render), the one-directional dependency rule (logic and render never import each other), and why columns/overlays/timeline live in model/. Pure import rewrite + docs; no behavior change.
…hats Makes resources/images/hats/*.svg the enforced source of truth for the hat path data. A headless/bundled renderer can't read those SVGs at runtime, so SHAPE_PATHS keeps byte-for-byte copies — this test reads the canonical SVGs at TEST time and asserts every shape's d= (and crosshairs' fill-rule) still matches, so the copy can never silently drift from source. Runtime stays pure.
Extract the flash-fade section (FADE_FRAC, DELETE/ADD/REFERENCE_FLASH_STYLES, flashFadeKeyframes, flashFadeRules) into render/css-cascade-flash.ts. Shared pct() formatter is exported from css-cascade.ts and reused (no duplication). Pure extraction; rendered CSS byte-identical.
Extract two cohesive pure steps out of fixtureToCascade: - derive-flashes.ts: step 6b char-diff synthesis of pendingDelete/justAdded when a fixture records no ide.flashes but the doc changed. - derive-overlays.ts: step 7 highlights + thatMark/sourceMark -> decorations. Both return decoration lists the caller appends; order and behavior identical. Removed now-unused Pos/pos imports. Rendered output byte-identical.
Split the 496-line jumbotron.ts into cohesive render/ siblings: - jumbotron.ts keeps the markup half (commandBar/metadataBlock/dots/ serializeJumbotron) and re-exports jumbotronCss for a stable public surface. - jumbotron-css.ts: jumbotronCss assembler + baseCss/themedCss/carouselTrack section builders. - jumbotron-css-keyframes.ts: the dot + command-pill @Keyframes builder. - jumbotron-shared.ts: NL, frameCommands, timelinePct, commandFrameIndices — shared by both halves (no cycle, no duplication; fallow clean). Rendered CSS + markup byte-identical.
Make the render pipeline readable top-to-bottom in one place. Add a `renderCommand` orchestrator at the package root (src/render-command.ts) whose body shows the four stages at a glance, each delegating to a named function: 1. get what to render -> parseFixture (logic/pipeline.ts) 2. tokenize each step -> tokenizeStates (logic/pipeline.ts) 3. generate render object -> buildRenderObject (logic/build-render-object.ts) 4. render from object -> serializeCascade + wrapCascadeSvg (render/) Decompose the former 235-line fixtureToCascade into the three named stage functions; fixtureToCascade stays exported and is now a thin composition of them (byte-identical output). The orchestrator lives at the root, NOT in logic/, because it is the one allowed composition point spanning both logic/ and render/ — the folder dependency rule (logic/ ⊥ render/) is preserved. Shared stage types moved to logic/pipeline-types.ts to avoid a circular pipeline <-> build-render-object edge and keep every file <=250 lines. Public surface: add renderCommand, RenderCommandOptions, parseFixture, tokenizeStates, buildRenderObject, ParsedFixture, TokenizedStates. All prior exports unchanged. Zero behavior change — rendered SVG byte-identical (verified by hash).
Delete four unused declarations confirmed dead by grep (zero use sites):
- CMD_SLIDE_FRAC, LIT_HOLD_FRAC (render/jumbotron-css.ts)
- DELETE_FLASH_STYLES (render/css-cascade-flash.ts; the
"pendingDelete" literal is used directly at every call site instead)
- aftLo local (render/jumbotron-css-keyframes.ts;
aftHi is used, aftLo was computed but never read)
Zero behavior change — rendered SVG byte-identical (verified by hash).
Add a "Pipeline — 4 stages" section naming each stage, its function, and the file it lives in, so a reviewer has a top-to-bottom map: 1 parseFixture (logic/pipeline.ts) 2 tokenizeStates (logic/pipeline.ts) 3 buildRenderObject (logic/build-render-object.ts) 4 serializeCascade + wrapCascadeSvg (render/) Document why the renderCommand orchestrator lives at the package root (the one allowed logic+render composition point) rather than in logic/, and list the new files (pipeline-types.ts, build-render-object.ts, render-command.ts) in the scope inventory.
Runs the repo's meta-updater fixer so `pnpm lint:meta` (a CI gate) passes: - package.json: add `exports["."]`, canonical `typecheck`/`clean` scripts. - tsconfig.json: add required `src/**/*.json` to include. - root tsconfig.json: register command-visualizer in project `references`. - tsconfig.base.json: add `@cursorless/command-visualizer` path mapping. Wires the package into the workspace's project-reference + path graph like its siblings.
Working/contributor doc: what the package can reuse from cursorless — geometry types (Position/Range/GeneralizedRange) adoptable today with no upstream change, plus four blocked-by-coupling items (pure flash-derivation, editor-free serializedMarksToTokenHats, grapheme tokenizer, parameterized fixture-path) that need a small upstream refactor+export first. Groups the same doc-strip bucket as the pre-upstream barebones pass.
…allocator and serialize
…y and delete local geometry
…try points renderCommand now accepts lineNumbers (via CascadeRenderOptions) and passes it into serializeCascade; serializeEditor/serializeDocument gain the same opt-in gutter markup as the cascade path. Off by default — output byte-identical when unset or false.
Asserts data-line-numbers on the cascade root, one 1-based .cl-lineno per line, digit-width scaling, per-frame emission, and that the default (and lineNumbers:false) stay byte-identical with no gutter markup.
…am home frame-state.ts field types already come from lib-common; container types (Decoration/Frame/CascadeState/CascadeMeta/FrameRole/OverlayRole) have no adoptable cursorless home. Decoration overlaps FlashDescriptor but that's editor-coupled + flash-only + lacks role. Recorded so it isn't re-audited.
Renames model/frame-state.ts -> model/types.ts and gives it a maintainer- facing header: these container types (CascadeState/Frame/Decoration/FrameRole/ OverlayRole) are package-specific with no current cursorless home (field types already come from lib-common). Isolated as a dedicated types file so cursorless maintainers can decide whether any warrant promotion. Type-only rename + import-path updates; zero behavior change (32 tests green, tsc clean).
The hat color/shape vocabulary existed 5x on upstream (app-vscode exported-but- unimportable + 3 frozen legacy command schemas + a talonjs test const). This PR moved it into lib-common's hatStyles.types.ts (was a stub) and rewired app-vscode to consume it — first importable shared home, net duplication reduced. Records the remaining (maintainer-side) consolidation of the non-frozen private copies.
- Move inline comments to separate lines (eslint/no-inline-comments) - Fix hexadecimal digit casing and numeric separators - Replace forEach with for loops (unicorn/no-array-for-each) - Use template literals instead of string concatenation - Fix length checks to use .length > 0 - Use String.fromCodePoint where appropriate - Move functions to outer scope for consistent function scoping - Add Unicode flag to regex patterns - Extract circular dependency via css-utils.ts
Talon-tools v0.9.0 fails on Node 24 / npm 11+ runners with: npm error EALLOWGIT / Fetching non-root packages from git has been disabled Upgrading to v0.11.0 which no longer uses npm install from git URLs, resolving the pre-commit CI failures on GitHub Actions.
The WIDE_RANGES array contains Unicode code point ranges that are more readable with lowercase hex digits and without numeric separators. Disable these rules for this specific array while keeping them enforced for regular code.
|
Closing - PR should be against the fork (trillium/cursorless), not upstream |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e8d75ae992
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const tokens: Token[] = []; | ||
| // Fresh RegExp per call: GRAPHEME_SPLIT_REGEX is a shared /gu instance whose | ||
| // lastIndex must not leak across tokenizeLine calls. | ||
| const re = new RegExp(GRAPHEME_SPLIT_REGEX, "u"); |
There was a problem hiding this comment.
Preserve the global regex flag when tokenizing
For any fixture line containing a letter, digit, punctuation, or symbol, this clone drops the g flag from GRAPHEME_SPLIT_REGEX (which is exported as /.../gu), so exec() never advances lastIndex and the while loop keeps returning the same first grapheme. That makes renderCommand() hang during tokenizeStates before it can produce an SVG; the same cloning pattern in splitGraphemes needs the same fix for the serializers.
Useful? React with 👍 / 👎.
|
Accidental pr, not ready |
Fixes two pre-existing CI failures on the command-visualizer base branch:
1. Lint Warnings (195+ oxlint warnings resolved)
Fixed oxlint warnings in
packages/command-visualizer/src/:All formatting issues (25 files) also resolved with oxfmt.
2. Pre-commit Hook Fix
Upgraded talon-tools from v0.9.0 to v0.11.0 to resolve npm EALLOWGIT errors on GitHub Actions runners with Node 24 / npm 11+.
Verification
✅
pnpm lint:tspasses clean (0 warnings)✅
pnpm lint:fmtpasses clean (all files formatted)✅ All commits on feature branch with clean working tree
Unblocks PR #4 (feat/command-visualizer-docs) and future command-visualizer work.