Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
61033a9
feat(command-visualizer): vendor cursorless hat-allocation algorithm
trillium Jul 7, 2026
196d0da
feat(command-visualizer): animated SVG command-cascade renderer
trillium Jul 7, 2026
5227117
docs(command-visualizer): PR draft and integration status
trillium Jul 7, 2026
e62bb67
refactor(command-visualizer): import DefaultMap/CompositeKeyMap from …
trillium Jul 7, 2026
7144364
feat(lib-engine): export hat-allocation and grapheme-splitter primitives
trillium Jul 7, 2026
ae71112
refactor(command-visualizer): import grapheme regex + maxByFirstDiffe…
trillium Jul 7, 2026
4d8b374
refactor(command-visualizer): derive FlashStyle from @cursorless/lib-…
trillium Jul 7, 2026
78c07c7
docs(command-visualizer): re-verify fixture-mark/path dedup notes aga…
trillium Jul 7, 2026
daf3180
docs(command-visualizer): colors/shapes dedup decision (Option B) + s…
trillium Jul 7, 2026
538c714
fix(command-visualizer): escape quotes in HTML attribute values (Code…
trillium Jul 7, 2026
5e3142c
fix(command-visualizer): address CodeRabbit review findings R3-R10
trillium Jul 8, 2026
f1845e6
refactor(lib-engine): trim allocateHats barrel to only consumed exports
trillium Jul 8, 2026
c281f46
feat(lib-common): promote hat shapeAdjustments from app-vscode
trillium Jul 8, 2026
dd1536e
refactor(command-visualizer): import hat color/shape vocabulary from …
trillium Jul 8, 2026
0ff300c
fix(command-visualizer): escape quotes in serialize.ts esc() (CodeQL)
trillium Jul 8, 2026
62927fd
feat(lib-common): promote hat color/shape vocabulary from app-vscode
trillium Jul 8, 2026
349f20a
refactor(app-vscode): import hat vocabulary directly from lib-common
trillium Jul 8, 2026
f77df27
refactor(command-visualizer): import hat vocabulary directly from lib…
trillium Jul 8, 2026
c9c7b34
refactor(command-visualizer): consolidate HTML helpers, drop unused dep
trillium Jul 8, 2026
2f3920a
test(command-visualizer): add unit tests for the deterministic core
trillium Jul 8, 2026
fcef30d
chore: update pnpm-lock.yaml for command-visualizer deps (R1)
trillium Jul 8, 2026
2c2af8f
refactor(command-visualizer): derive FLASH_STYLES from lib-common enum
trillium Jul 8, 2026
dd1ab12
feat(lib-engine): export getTokensInRange from allocateHats barrel
trillium Jul 8, 2026
23db201
refactor(command-visualizer): allocate hats via real lib-engine, drop…
trillium Jul 8, 2026
d15af2d
test(command-visualizer): smoke-test real-engine hat allocation
trillium Jul 8, 2026
bc06e1e
refactor(command-visualizer): extract model/ scope (shared pure contr…
trillium Jul 8, 2026
dbd5966
refactor(command-visualizer): rewire logic/ imports to model/
trillium Jul 8, 2026
81f984f
refactor(command-visualizer): rewire render/ imports to model/
trillium Jul 8, 2026
46a4be3
refactor(command-visualizer): wire public index to scopes + add ARCHI…
trillium Jul 8, 2026
2c5f85b
test(command-visualizer): guard SHAPE_PATHS against resources/images/…
trillium Jul 8, 2026
80d3d26
refactor(command-visualizer): split css-cascade.ts under 250-line limit
trillium Jul 8, 2026
1c24251
refactor(command-visualizer): split pipeline.ts under 250-line limit
trillium Jul 8, 2026
7d0729d
refactor(command-visualizer): split jumbotron.ts under 250-line limit
trillium Jul 8, 2026
2ab06b9
refactor(command-visualizer): legible 4-stage pipeline orchestrator
trillium Jul 8, 2026
bf3803d
refactor(command-visualizer): remove dead consts flagged in prior split
trillium Jul 8, 2026
ee59ec2
docs(command-visualizer): map the 4-stage pipeline in ARCHITECTURE.md
trillium Jul 8, 2026
1da17a8
chore(command-visualizer): conform manifest + tsconfig to meta-updater
trillium Jul 8, 2026
fd7e416
refactor(command-visualizer): rename DecorationStyle to OverlayStyleName
trillium Jul 8, 2026
c3ac046
refactor(command-visualizer): idiomatic casts, camelCase fn, flow-nar…
trillium Jul 8, 2026
1675212
docs(command-visualizer): strip dangling internal citations from comm…
trillium Jul 8, 2026
f997326
docs(command-visualizer): add README, keep ARCHITECTURE, drop STATUS/…
trillium Jul 8, 2026
e7fa2b0
docs(command-visualizer): track upstream reuse opportunities
trillium Jul 8, 2026
f45849b
refactor(command-visualizer): adopt lib-common Position/Range in hat-…
trillium Jul 8, 2026
281e2d0
refactor(command-visualizer): adopt lib-common GeneralizedRange famil…
trillium Jul 8, 2026
975e058
feat(command-visualizer): thread lineNumbers option through render en…
trillium Jul 8, 2026
4e9d004
test(command-visualizer): cover opt-in line-number gutter
trillium Jul 8, 2026
3d4c577
docs(command-visualizer): record frame-state types checked, no upstre…
trillium Jul 8, 2026
9ef30e9
refactor(command-visualizer): move render-model types to model/types.ts
trillium Jul 8, 2026
af06ece
docs(command-visualizer): record hat-vocabulary consolidation landscape
trillium Jul 8, 2026
c86aca3
fix: resolve lint warnings in command-visualizer package
trillium Aug 4, 2026
3a25781
fix: bump talon-tools to v0.11.0 to fix npm EALLOWGIT issue
trillium Aug 4, 2026
5482bc4
fix: disable hex case/numeric separators rules for Unicode ranges
trillium Aug 4, 2026
e8d75ae
refactor: extract CSS utility functions to resolve circular dependency
trillium Aug 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ repos:
- id: ruff-format

- repo: https://github.com/cursorless-dev/talon-tools
rev: v0.9.0
rev: v0.11.0
hooks:
- id: talon-fmt
- id: tree-sitter-fmt
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import { pickBy } from "lodash-es";
import vscode from "vscode";
import type {
HatColor,
HatShape,
HatStyleInfo,
HatStyleMap,
Listener,
VscodeHatStyleName,
} from "@cursorless/lib-common";
import {
HAT_COLORS,
HAT_NON_DEFAULT_SHAPES,
Notifier,
} from "@cursorless/lib-common";
import { Notifier } from "@cursorless/lib-common";
import type { HatColor, HatShape, VscodeHatStyleName } from "./hatStyles.types";
import { HAT_COLORS, HAT_NON_DEFAULT_SHAPES } from "./hatStyles.types";

export interface ExtendedHatStyleInfo extends HatStyleInfo {
color: HatColor;
Expand Down
32 changes: 0 additions & 32 deletions packages/app-vscode/src/ide/vscode/hatStyles.types.ts

This file was deleted.

20 changes: 11 additions & 9 deletions packages/app-vscode/src/ide/vscode/hats/VscodeHatRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,23 @@ import path from "node:path";
import { isEqual } from "lodash-es";
import vscode from "vscode";
import type {
HatShape,
IndividualHatAdjustmentMap,
Listener,
Messages,
PathChangeListener,
VscodeHatStyleName,
} from "@cursorless/lib-common";
import {
DEFAULT_HAT_HEIGHT_EM,
DEFAULT_VERTICAL_OFFSET_EM,
defaultShapeAdjustments,
getErrorMessage,
HAT_SHAPES,
Notifier,
} from "@cursorless/lib-common";
import { getErrorMessage, Notifier } from "@cursorless/lib-common";
import { walkFiles } from "@cursorless/lib-node-common";
import type { VscodeApi } from "@cursorless/lib-vscode-common";
import type { HatShape, VscodeHatStyleName } from "../hatStyles.types";
import { HAT_SHAPES } from "../hatStyles.types";
import { vscodeGetConfigurationString } from "../VscodeConfiguration";
import type {
ExtendedHatStyleMap,
Expand All @@ -20,12 +28,6 @@ import type {
import type { FontMeasurements } from "./FontMeasurements";
import { getHatThemeColors } from "./getHatThemeColors";
import { performPr1868ShapeUpdateInit } from "./performPr1868ShapeUpdateInit";
import type { IndividualHatAdjustmentMap } from "./shapeAdjustments";
import {
DEFAULT_HAT_HEIGHT_EM,
DEFAULT_VERTICAL_OFFSET_EM,
defaultShapeAdjustments,
} from "./shapeAdjustments";

const CURSORLESS_HAT_SHAPES_SUFFIX = ".svg";

Expand Down
2 changes: 1 addition & 1 deletion packages/app-vscode/src/ide/vscode/hats/VscodeHats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import type {
Listener,
Range,
TextEditor,
VscodeHatStyleName,
} from "@cursorless/lib-common";
import { Notifier } from "@cursorless/lib-common";
import type { VscodeApi } from "@cursorless/lib-vscode-common";
import { toVscodeRange } from "@cursorless/lib-vscode-common";
import type { VscodeHatStyleName } from "../hatStyles.types";
import { VscodeEnabledHatStyleManager } from "../VscodeEnabledHatStyleManager";
import type { VscodeIDE } from "../VscodeIDE";
import type { VscodeTextEditor } from "../VscodeTextEditor";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import vscode from "vscode";
import type { HatColor } from "../hatStyles.types";
import type { HatColor } from "@cursorless/lib-common";

interface OldDecorationColorSetting {
dark: string;
Expand Down
2 changes: 1 addition & 1 deletion packages/app-vscode/src/ide/vscode/hats/getStyleName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type {
HatColor,
HatShape,
VscodeHatStyleName,
} from "../hatStyles.types";
} from "@cursorless/lib-common";

export function getStyleName(
color: HatColor,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import vscode from "vscode";
import type { Messages } from "@cursorless/lib-common";
import type {
IndividualHatAdjustmentMap,
Messages,
} from "@cursorless/lib-common";
import { showInfo } from "@cursorless/lib-common";
import type { VscodeApi } from "@cursorless/lib-vscode-common";
import type { ExtendedHatStyleMap } from "../VscodeEnabledHatStyleManager";
import type { IndividualHatAdjustmentMap } from "./shapeAdjustments";

/**
* We set this key in global state the first time they user gets the new shapes from #1868. We use this to
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { isString } from "lodash-es";
import vscode from "vscode";
import type {
HatColor,
HatShape,
Modifier,
PartialMark,
SurroundingPairName,
} from "@cursorless/lib-common";
import { surroundingPairsDelimiters } from "@cursorless/lib-engine";
import type { HatColor, HatShape } from "../ide/vscode/hatStyles.types";
import type {
SimpleKeyboardActionDescriptor,
SpecificKeyboardActionDescriptor,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import vscode from "vscode";
import type {
ActionDescriptor,
HatColor,
HatShape,
Modifier,
PartialMark,
PartialPrimitiveTargetDescriptor,
Expand All @@ -10,7 +12,6 @@ import type {
import { LATEST_VERSION } from "@cursorless/lib-common";
import { runCursorlessCommand } from "@cursorless/lib-vscode-common";
import { getStyleName } from "../ide/vscode/hats/getStyleName";
import type { HatColor, HatShape } from "../ide/vscode/hatStyles.types";
import type { SimpleKeyboardActionDescriptor } from "./KeyboardActionType";
import type { KeyboardCommandsModal } from "./KeyboardCommandsModal";
import type { KeyboardHandler } from "./KeyboardHandler";
Expand Down
3 changes: 2 additions & 1 deletion packages/app-vscode/src/keyboard/TokenTypes.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type {
HatColor,
HatShape,
SimpleScopeTypeType,
SurroundingPairName,
} from "@cursorless/lib-common";
import type { HatColor, HatShape } from "../ide/vscode/hatStyles.types";
import type {
PolymorphicKeyboardActionDescriptor,
SimpleKeyboardActionDescriptor,
Expand Down
5 changes: 2 additions & 3 deletions packages/app-vscode/src/scripts/hatAdjustments/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import { sum } from "lodash-es";
import type {
HatAdjustments,
IndividualHatAdjustmentMap,
} from "../../ide/vscode/hats/shapeAdjustments";
import { defaultShapeAdjustments } from "../../ide/vscode/hats/shapeAdjustments";
import { HAT_SHAPES } from "../../ide/vscode/hatStyles.types";
} from "@cursorless/lib-common";
import { defaultShapeAdjustments, HAT_SHAPES } from "@cursorless/lib-common";
import { postProcessValue } from "./lib";

/**
Expand Down
5 changes: 2 additions & 3 deletions packages/app-vscode/src/scripts/hatAdjustments/average.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
import type {
HatAdjustments,
IndividualHatAdjustmentMap,
} from "../../ide/vscode/hats/shapeAdjustments";
import { defaultShapeAdjustments } from "../../ide/vscode/hats/shapeAdjustments";
import { HAT_SHAPES } from "../../ide/vscode/hatStyles.types";
} from "@cursorless/lib-common";
import { defaultShapeAdjustments, HAT_SHAPES } from "@cursorless/lib-common";
import { postProcessValue } from "./lib";

/**
Expand Down
98 changes: 98 additions & 0 deletions packages/command-visualizer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# @cursorless/command-visualizer

Turn recorded cursorless test fixtures into **animated SVG visualizations** of
commands — before/during/after cascades with real hat allocation, flash
sequencing, selections, and a command "jumbotron". The output is a
self-contained SVG string embeddable anywhere a plain `<img>` works (GitHub
READMEs via camo, docs pages, tutorials).

The renderer is a pure function: fixture YAML in → self-contained SVG string
out. There is zero runtime JavaScript in the output, no DOM or browser
dependency to render, and every animation rides one CSS `--dur` timeline, so
outputs are deterministic and testable by seeking.

```ts
import { renderCommand } from "@cursorless/command-visualizer";

const svg = renderCommand(fixtureYamlText, "recorded/foo/bar.yml", {
theme: "dark",
});
```

## The animation model

```
pre (bumper) → [ step.initial → step.during → step.final ]* → post (reset)
```

- **The during phase is the execution beat:** the command pill activates, its
step dot gains the issued ring, and flashes initiate — all in the same
instant. Flash-less commands (pure selections) show their final state in this
beat, matching the editor's instantaneous selection.
- **Chains are contract-checked:** `finalState.i` must equal
`initialState.{i+1}`, and the boundary renders as ONE merged frame already
wearing the next step's hats — one animated change, exactly as in the editor.
- **Durations are parameters** (`initial` / `during` / `bumper` ms, plus a float
`scale`); defaults are `[2000, 1000, 500]`.
- Fixtures with no recorded `ide.flashes` (e.g. the tutorial corpus) get flashes
**derived from the edit diff**; recorded flashes are used verbatim when
present.

Flash classes follow the action source exactly: pre-edit flashes
(`pendingDelete`, `referenced`, `pendingModification0`/`1`) precede the edit and
`justAdded` follows it. The 100ms `pendingEditDecorationTime` pulse is pinned
N-invariantly (independent of the number of frames). Pre-edit flashes render
sequenced (reference → delete) rather than in parallel — a deliberate
readability divergence from the editor.

## Architecture

Source is organized into four scopes with a strict one-directional dependency
rule (`render/` and `logic/` never import each other). The layering diagram, the
4-stage pipeline table, and the grep commands that verify the dependency rule
live in [`src/ARCHITECTURE.md`](./src/ARCHITECTURE.md).

## Reuse of cursorless source

This package deliberately imports from cursorless packages rather than cloning,
and documents the cases where a local copy is the faithful choice.

Imported from cursorless (not cloned):

- `DefaultMap` / `CompositeKeyMap`, `FlashStyle` — from `@cursorless/lib-common`.
- `GRAPHEME_SPLIT_REGEX`, `maxByFirstDiffering`, the `allocateHats` primitives —
from `@cursorless/lib-engine`.
- Fixture YAML is parsed with `js-yaml` (the same library `@cursorless/lib-node-common`'s
`loadFixture.ts` uses).

Kept local, with provenance, where importing would regress or balloon scope:

- **`data/decorations.ts`** — `FlashStyle` is a string union derived from
lib-common's `FlashStyle` enum; it composes with `HighlightStyle` into the
`OverlayStyleName` keys. The `DECORATION_HEX` values, pulse timing, and
precedence are local (cursorless does not export them). Note: this package's
`OverlayStyleName` is unrelated to lib-common's `DecorationStyle` (a
border-geometry interface) — the rename avoids that collision.
- **`data/colors.ts` / `data/shapes.ts`** — the color hexes live in
`app-vscode/package.json` as VS Code setting defaults (read at runtime, never a
TS constant) and the shape SVG `d=` path strings live in
`resources/images/hats/*.svg` (read at runtime by `VscodeHatRenderer`). There
is no importable TS module, so these are mirrored here with provenance comments
naming the exact upstream source.
- **`logic/fixture-root.ts`** — keeps its dual directory-layout probe and
`$CURSORLESS_REPO` override; lib-node-common's `getFixturesPath` hardcodes one
layout and throws unless `CURSORLESS_REPO_ROOT` is set.
- **`logic/fixture-extract.ts`** — `parseMarks` / `buildLines` read plain YAML
with no editor, whereas lib-common's `serializedMarksToTokenHats` needs a live
`TextEditor` and returns engine `TokenHat[]`.

## Development

```sh
# typecheck
tsc -p packages/command-visualizer/tsconfig.json --noEmit
```

The renderer resolves recorded fixtures from a cursorless checkout. Set
`CURSORLESS_REPO` to point at your checkout if it is not at
`$HOME/code/cursorless`.
Loading
Loading