- Move newly split tests under explicit domain subfolders
- Update nested helper imports after the moves
- Add a lint guard against repeating existing sibling domains as filename prefixes
- Extract color picker fill comparison helpers
- Guard derived text assertions without optional chains
- Split resize math into smaller helper functions
- Remove the remaining complexity test exceptions
- Move auto-layout tests into focused files by layout behavior
- Preserve sizing, wrapping, text measurement, and positioning coverage
- Remove the last max-lines test exception
- Move scene graph behavior tests into focused files
- Share basic scene graph helper builders from basic/helpers.ts
- Remove scene graph basic from max-lines exceptions
- Move FigmaAPI tests into focused files by feature area
- Share API construction from figma/api/helpers.ts
- Remove FigmaAPI coverage from max-lines exceptions
- Move Kiwi serialization fix tests into one file per regression area
- Share codec setup and Kiwi helpers from serialize-fixes/helpers.ts
- Remove serialize fixes from max-lines exceptions
- Replace remaining empty mock callbacks with explicit undefined returns
- Remove the last no-empty-function test exceptions
- Validate check and affected renderer/text tests
- 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
- Expose window.openPencil.getStore() instead of a direct store property
- Update E2E helpers and specs to use the bridge getter
- Add lint coverage preventing direct window.openPencil.store access
- Remove remaining test non-null assertions and delete the broad test override
- Add explicit guards for canvas, design panel, auto-layout, SVG, Kiwi, hit-test, scene graph, text editor, and clipboard tests
- Validate check plus affected engine and E2E coverage
- Route browser globals through src/app/window-api.ts instead of private __OPEN_PENCIL fields
- Move E2E tests to the public window.openPencil test API
- Add an oxlint rule banning direct window.__OPEN_PENCIL* access
- 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
- Remove the broad no-unused-vars test override
- Scope unused-variable allowances to existing legacy and fixture-heavy test files
- Keep new tests subject to unused variable warnings by default
- Remove broad test exceptions for no-empty-function and complexity
- Scope no-op mock allowances to files that intentionally define fake APIs
- Scope complexity allowances to the remaining high-coverage integration-style tests
- Remove the broad test exception for inline named type literals
- Replace duplicated Color, Vector, and Rect shapes with shared core types
- Keep affected engine tests passing
- Remove the broad test exception for consistent type imports
- Mark test-only core and helper imports as type imports where appropriate
- Keep targeted validation for affected engine tests
- Replace broad test no-console and max-lines disables with file-specific overrides
- Keep empty-function disabled broadly for test mocks and no-op fixtures
- Preserve clean check output while reducing global test lint carve-outs
- 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
- 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
- Merge structural lint rules into oxlint.json and remove the second config file
- Add #tests/* path aliases and rewrite deep test helper imports
- Ban new deep parent-relative imports through the shared lint config
- Add structural lint rules for broad Function usage and globalThis deletion outside tests
- Type design JSX component factories with an explicit callable signature
- Replace remaining broad Kiwi serialization test records with unknown values
- Enforce explicit-any and TypeScript suppression bans in tests
- Remove remaining broad any casts from engine and e2e coverage
- Split markdown ambient declarations from typed browser globals
- Treat explicit any and non-null assertions as errors outside existing test and adapter exceptions
- Run the unknown Record double-cast guard in the main lint config too
* feat(mcp): add list_available_fonts tool for font discovery
The existing list_fonts tool reports only fonts currently used in the
current page (it walks every TEXT node and groups by fontFamily).
Agents authoring new text have no way to discover what fonts the host
can render — they have to guess and hope set_font succeeds.
This adds list_available_fonts, which returns the set of font
families the host advertises. On desktop this is the system fonts
font-kit enumerates plus any bundled fonts the host registers; in
non-desktop runtimes that don't expose enumeration, it returns an
empty list with an explanatory note rather than throwing.
The optional `family` filter mirrors list_fonts for a substring
search, so agents can do `list_available_fonts({family: "mono"})` to
narrow before set_font.
Wired through:
- packages/core/src/figma-api/index.ts: adds optional
listAvailableFontFamilies hook on FigmaAPI (same pattern as
exportImage). Optional so non-desktop hosts aren't forced to
implement it.
- packages/core/src/tools/read.ts: defines the tool.
- packages/core/src/tools/registry.ts: registers it in CORE_TOOLS so
the MCP server advertises it.
- src/automation/figma-factory.ts: provides the desktop
implementation by delegating to engine/fonts.ts#listFamilies which
already wraps the Tauri list_system_fonts sidecar.
* refactor(mcp): reshape to listAvailableFontsAsync matching Figma API
Per review feedback on #210: FigmaAPI mirrors the Figma Plugin API, and
Figma already exposes figma.listAvailableFontsAsync(): Promise<Font[]>
where Font = { fontName: { family, style } }. Replacing the custom
listAvailableFontFamilies hook with the Figma-shaped method means eval'd
plugin code and any future plugin-compat layer get font enumeration for
free. Follows the same pattern as loadFontAsync on FigmaAPI.
- packages/core/src/figma-api/proxy.ts: export FigmaFont type
({ fontName: FigmaFontName }) alongside FigmaFontName.
- packages/core/src/figma-api/index.ts: drop optional
listAvailableFontFamilies; add listAvailableFontsAsync() as a real
method (non-optional) with a safe [] default, mirroring loadFontAsync.
- packages/core/src/tools/read.ts: list_available_fonts execute() now
consumes Font[] and dedupes families via a Set. Drops the
host-capability fallback note — the new method always returns an
array, so a non-desktop host just yields {count: 0, fonts: []}.
- src/engine/fonts.ts: add listFonts() returning TauriFontFamily[]
({family, styles[]}) so the factory can emit one Font per (family,
style) pair.
- src/automation/figma-factory.ts: override listAvailableFontsAsync,
flattening {family, styles[]}[] to Font[].
- oxlint.json: add listFonts to the engine/fonts allowlist.
Tool public shape unchanged (still returns {count, fonts: string[]})
so agents calling list_available_fonts see no change.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add IS_BROWSER constant to core/constants.ts, rewrite IS_TAURI to use it
- Replace 9 raw 'typeof window' checks across canvaskit, fig-export,
figma-api, fig-file, fonts, SafariBanner, use-chat
- Promote no-typeof-window-check lint rule from warn to error
- Extract array mutation helpers (updateArrayItem, removeArrayItem,
toggleArrayVisibility) into useMultiProps — DRY up FillSection and
StrokeSection (removes 20+ duplicated for-loops)
Replace ssrLoadModule hack with simple child_process.spawn.
Same approach as production (Tauri sidecar) — just run the MCP
server entry point directly. No re-evaluation on HMR, no
WebSocket reconnect storms, no globalThis guards.
- New oxlint rule: open-pencil/no-silent-catch — errors on empty catch blocks
- Replace all 8 empty catch blocks with console.warn() logging
- Add worker timeout (30s) and main-thread fallback for .fig parsing
- Fix null crash in renderer filter/picture cache cleanup
- Buffer copy before worker transfer for safe fallback
Three oxlint JS plugin rules in lint/plugin.js:
- no-inline-named-types: configurable shape→name map catches inline
{ x: number; y: number } etc. and suggests Vector, Color, GUID,
Rect, Matrix from @open-pencil/core
- no-structuredclone-scene-arrays: flags structuredClone on
fills/strokes/effects — use typed copy helpers instead
- no-math-random: bans Math.random() in favor of crypto
Fixed 40 violations across 13 files: replaced inline type literals
with named imports, switched figma-api.ts structuredClone calls to
copyFills/copyStrokes/copyEffects.