Commit graph

11 commits

Author SHA1 Message Date
Danila Poyarkov aa86873dd7
test: typecheck the test suites and fix what that found (#896)
* build: typecheck the test suites

Tests were in no TypeScript program: no tsconfig included tests/** or
packages/*/tests/**, and bun strips types without checking them, so a
fixture could drop a required field and keep passing until something
read it.

@types/bun moves to the root because it was installed per package only,
and #cli-tests/* joins the paths the root config already carries.

* test: fix the type errors the test suites were hiding

Typechecking the tests turned up 1123 errors. Most were ordinary
strictness, but some were real: `NodeChange` bound to Figma's plugin
typings rather than the Kiwi codec in thirteen .fig tests,
materializeInstance was called with six arguments against five so the
blobs and source children were dropped, CanvasKit pixels were written
to a plain object that never reached WASM, and assertions were made
through accessors that do not exist, so they asserted nothing.

Fixtures that had quietly lost a required field now carry it, nullable
results are narrowed through the existing expectDefined helper rather
than assumed, and stand-ins for CanvasKit and the editor go through one
named helper instead of an unexplained cast at each site.

No test was deleted, skipped, or weakened, and no `any`, non-null
assertion, or ts-expect-error was introduced.

* docs: record what typechecking the tests established

Pins the app program's global types with an assertion rather than a
note, since an unpinned types list lets any root @types package decide
which platform src/** is judged against.

The two environment faults that look like code regressions — Vite's
dependency pre-bundle outliving a package rebuild, and heavy .fig
suites failing under load — go to the development docs, where an
explanation belongs.

* fix: align @types/bun and keep node types resolvable when extended

The root manifest declared a newer @types/bun than every package, which
check:monorepo rejects, and pinning the app program's types left them
unresolvable from a config that extends this one out of tree.

* fix: fail the test typecheck when the compiler itself fails

The gate matched diagnostics by substring, so a compiler or config
failure that named no test file printed a pass while having checked
nothing. Diagnostics are now split by whether they name a file: an
unscoped one is the run failing and stops the gate, a test file's is a
finding, and a source file's stays out by design.

Also drops the parameter planComponentConstruction never read, and
makes the inner-shadow verification script exit non-zero when it
renders no image instead of logging and succeeding.

* chore: merge master into tests-typecheck
2026-10-05 12:42:38 +00:00
Danila Poyarkov d2fd6db141
feat(ai): show what each AI edit changed in its tool call (#812)
* feat(core): add visual diff and patch apply tools

diff_visual renders two nodes at one scale through the existing raster export, compares them with pixelmatch, and returns the diff PNG with the changed ratio and region in source-node coordinates. It takes export_image's scale and maxEdge inputs. FigmaAPI gains a CanvasKit-backed raster codec and a pageId export option, so the app and headless CLI decode pixels and render nodes off the current page.

diff_apply applies diff_create and diff_show patches through the Figma API, validates every node before changing any, and supports dryRun and force. diff_show now simulates changes on a detached copy with the same property code. One serializer and parser back all three. diffDocuments compares two documents page by page by name path.

Image tool results now reach models as media with their metadata as text, for any tool rather than export_image alone. diff_create, diff_jsx, and diff_visual join the default AI tool set, and the diff tools are no longer hidden from WebMCP.

* feat(ai): show what each AI edit changed in its tool call

Reviewing an AI run meant reading tool output or undoing steps to see
what moved. Each document-changing call now rebuilds its page before
and after from snapshots taken around it, and diffs each top-level
layer's JSX with jsdiff, the same patch diff_jsx returns, to find the
layers it changed. After the call returns, the changed region renders
in both states at one size and pixelmatch highlights the difference.
The tool card opens on a Changes view with a before/after slider, the
pixel highlight, and a CodeMirror merge view of the JSX. Records are
saved with the conversation next to attachments.

Calls snapshot their page individually instead of through one shared
variable, so concurrent calls in a step no longer overwrite each
other's undo state. Core gains graphFromPageSnapshot for rebuilding a
past page state, diffPageLayersJSX and jsxPatch (now shared with
diff_jsx), renderRegionToImage for rendering two states of one region
pixel for pixel, and comparePNGs on the raster codec. Settings > Chat >
Change previews sets the stored image size or turns images off.

* feat(cli): add diff commands and agent diff guidance

openpencil diff create, jsx, show, apply, and visual run the Core diff tools on a file or the running app; apply writes back with --write or --output like eval. diff files compares two documents page by page and exits 1 when they differ.

The chat prompt asks the agent to edit in place and to verify risky edits against a reference copy with diff_jsx, diff_create, and diff_visual. The skill, CLI reference, MCP tool table, and a new Comparing Designs page document the commands and tools.

* feat(ai): render tool calls as summarized, highlighted cards

Every tool call showed only a status and its output as a JSON string,
so render calls hid their JSX, export_image dumped base64, and long
runs filled the transcript with identical rows.

A call now shows a one-line summary read from its input and chips that
select and zoom to the layers it touched, switching to the run's page
when needed. Expanded, it shows the JSX or script it wrote and its
JSON input and output in a read-only CodeMirror view, and exported
images inline. Render calls can be expanded while their input streams,
so the JSX appears alongside the canvas preview. Consecutive calls
beyond three fold into one row that keeps the latest call visible.

CodeMirror loads with the first expanded call. The code theme gains a
monospace fallback because the editor font variable is not always
emitted.

* feat(ai): let the chat AI diff its run against the starting state

The diff tools compare two nodes, so checking an edit meant cloning a
reference first, which the agent rarely did. diff_changes compares the
current page, or one node under it, with the page as it was before the
run first edited it, in diff_create's patch format. The app keeps that
page snapshot per run and exposes it through FigmaAPI.changeBaseline;
MCP and WebMCP have no run, so the tool is offered only to the AI chat,
where it is enabled by default and the prompt asks for it before
reporting.

* feat(core): diff and patch node trees as JSX attributes

diff_create, diff_show, diff_apply, and diffDocuments used a hand-rolled
`key: value` property format that covered about fifteen properties,
matched children by name path, and could not see moves.

Nodes are now projected to the attributes the JSX export prints, and
jsondiffpatch matches children (by ID or by name path) and detects
moves. Patches list `-`/`+` attribute lines per node plus moved, added,
and removed children. diff_apply checks every hunk first, applies
attribute changes through the renderer's prop handling, and changes only
the fields an attribute moves, so IDs, instance links, and other state
survive. diff_show takes JSX attributes instead of a JSON props object.

design-jsx gains sceneNodeAttributes, parseJSXAttributes, and
jsxNodeFields for this, and the export round-trip property table is
shared so every case is also diffed and applied. `diff files` loads its
documents in order so node IDs, and so its patches, are deterministic.

* feat(ai): report diff_changes as a patch diff_apply can replay

diff_changes printed a unified diff of the JSX, which agents could read
but not apply. It now diffs the run's baseline against the live page
with the patch engine, matching nodes by ID, so a rename is a changed
name and the output replays on the starting state with diff_apply. The
chat's Changes view keeps the JSX line diff, which is for people.

* fix(core): keep diff_apply atomic and diff files honest about differences

- Added nodes render before anything else changes; if one fails, for
  example on a missing component, the rendered ones are deleted and
  nothing else is committed.
- A hunk with an attribute the renderer ignores fails instead of
  reporting "unchanged".
- diffDocuments reports `changed` from page statuses, and a page only
  one document has gets its status but no patch, since patches do not
  add or remove pages. diff files uses it, so an added empty page no
  longer reads as a match.
- diff files rejects a --page neither document has and a --depth that
  is not a non-negative integer, exiting 2; diff_create's depth is
  validated the same way.

* refactor(ai): drop the unused tool JSON slot and place the JSX summary comment

* refactor(ai): find a tool change's clipping region with jsdiff

clipChangedJSX scanned both JSX sources character by character for their common start and end. diffLines gives the unchanged lines before the first change and after the last; the app now declares the diff dependency Core already uses.
2026-10-03 23:32:37 +04:00
Danila Poyarkov 46aa00d3d1 refactor(tools): default interface exposure to inclusion
Share typed MCP, AI, and WebMCP exclusions across adapters. Preserve the browser tool inventory through explicit exclusions and keep execution support and user permissions independent.
2026-09-15 17:12:31 +03:00
Danila Poyarkov 4a9bad5cec refactor(tools)!: centralize schemas and execution contracts
Define native Valibot inputs and execution/exposure metadata on each tool. Derive effects and default capabilities, consume upstream Standard Schema conversion, and validate finite numeric inputs consistently across adapters.

Move atomic execution to Core and restore failures from Scene Graph checkpoints without relying on a property diff. Preserve topology, collections, indexes and surviving object identities during rollback.

BREAKING CHANGE: custom tools use input schemas and execution metadata instead of params, ParamDef and independently declared mutation flags. Direct tool execution validates inputs before invoking the handler.
2026-09-15 10:55:27 +03:00
Danila Poyarkov 0f070f9f97 refactor(ai): share mutation layout reconciliation 2026-09-02 02:06:08 +03:00
Danila Poyarkov 1ed7de6056 chore(lint): reject broad unknown type assertions
- Add AST-based lint checks for broad unknown object assertions and local JsonObject aliases
- Centralize JsonObject in core and package-local MCP RPC JSON typing
- Replace baseline Record<string, unknown> assertions with named shared/domain types
2026-05-25 00:26:02 +03:00
Danila Poyarkov 432f945da6 chore(tests): narrow no-empty-function exceptions
- Replace simple no-op test callbacks with explicit undefined returns
- Remove no-empty-function exceptions for autosave, stroke picker, font settings, pen, tauri, AI adapter, and undo helper tests
- Validate check plus affected engine and E2E tests
2026-05-06 12:32:54 +03:00
Danila Poyarkov 6ec18f1dfb chore(tests): remove targeted non-null assertions
- Replace low-count E2E non-null assertions with store guards and optional expectations
- Use expectDefined for tool adapter and XPath node lookups
- Validate affected engine and E2E tests
2026-05-06 03:13:58 +03:00
Danila Poyarkov 9e4d7bee5d chore(lint): fix unused test code
- Remove stale imports and unused locals from split engine and e2e tests
- Drop the targeted no-unused-vars test allowances
- Keep check and affected test suites warning-free
2026-05-06 02:53:19 +03:00
Danila Poyarkov 654aa421e9 chore(lint): reduce test rule exceptions
- Re-enable duplicate imports, radix, prefer-const, nested ternary, and silent catch checks in tests
- Drop the redundant spec-file override covered by the broader test override
- Fix the remaining test offenders surfaced by the stricter rules
2026-05-06 02:31:07 +03:00
Danila Poyarkov 930cf39216 test: organize prefixed test files into folders
- Move top-level engine and e2e prefixed test files under domain folders
- Update fixture path helpers after moving render and pen tests
- Add lint coverage to prevent new top-level prefixed test files
- Refresh testing docs for the new fig and layout paths
2026-05-06 02:13:18 +03:00
Renamed from tests/engine/tools-ai-adapter.test.ts (Browse further)