Skip to content

fix: resolve pre-existing lint/pre-commit failures on command-visualizer - #3297

Closed
trillium wants to merge 53 commits into
cursorless-dev:mainfrom
trillium:fm/cursorless-lint-debt
Closed

fix: resolve pre-existing lint/pre-commit failures on command-visualizer#3297
trillium wants to merge 53 commits into
cursorless-dev:mainfrom
trillium:fm/cursorless-lint-debt

Conversation

@trillium

@trillium trillium commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

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/:

  • Move inline comments to separate lines (eslint/no-inline-comments)
  • Replace Array.forEach with for loops (unicorn/no-array-for-each)
  • Use template literals instead of string concatenation (eslint/prefer-template)
  • Fix function scoping issues (unicorn/consistent-function-scoping)
  • Use String.fromCodePoint where appropriate
  • Add Unicode flag to regex patterns
  • Extract circular dependencies via css-utils.ts
  • Disable hex digit case rules for Unicode constants (readability)

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:ts passes clean (0 warnings)
pnpm lint:fmt passes 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.

trillium added 30 commits July 7, 2026 16:16
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 " → &quot; 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.
trillium added 23 commits July 8, 2026 09:21
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.
…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.
@trillium
trillium requested a review from a team as a code owner August 4, 2026 05:07
@trillium

trillium commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Closing - PR should be against the fork (trillium/cursorless), not upstream

@trillium trillium closed this Aug 4, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

@trillium

trillium commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Accidental pr, not ready

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant