From abd795bd9a4518e2ba884cd21ca2c288af67ffdc Mon Sep 17 00:00:00 2001 From: Danila Poyarkov Date: Sun, 24 May 2026 12:15:29 +0300 Subject: [PATCH] chore: format codebase --- .github/workflows/app.yml | 3 + .github/workflows/ci.yml | 3 + package.json | 1 + packages/cli/src/commands/export.ts | 4 +- packages/core/src/canvas/labels/selection.ts | 3 +- packages/core/src/canvas/overlays/ai.ts | 5 +- .../src/canvas/overlays/auto-layout-hover.ts | 16 +- packages/core/src/canvas/renderer/methods.ts | 8 +- packages/core/src/canvas/renderer/pipeline.ts | 6 +- .../src/canvas/renderer/retained-backing.ts | 20 +- packages/core/src/canvas/shapes.ts | 155 +++++++-- packages/core/src/canvas/strokes.ts | 9 +- packages/core/src/canvas/text-derived.ts | 6 +- packages/core/src/canvas/text-outlines.ts | 9 +- packages/core/src/clipboard.ts | 9 +- .../core/src/design-jsx/props-overrides.ts | 5 +- packages/core/src/editor/clipboard.ts | 4 +- .../src/editor/clipboard/paste-replace.ts | 9 +- .../core/src/editor/components/variants.ts | 2 +- .../core/src/editor/structure/selection.ts | 3 +- packages/core/src/editor/undo.ts | 1 - packages/core/src/figma-api/index.ts | 18 +- packages/core/src/icons/svg.ts | 4 +- packages/core/src/io/formats/fig/export.ts | 12 +- .../src/io/formats/jsx/tailwind-classes.ts | 12 +- packages/core/src/io/formats/svg/paths.ts | 2 +- packages/core/src/kiwi/fig/import.ts | 13 +- .../component-props/apply.ts | 3 +- .../component-props/index.ts | 8 +- .../component-props/values.ts | 6 +- .../derived-symbol-data/geometry.ts | 14 +- .../derived-symbol-data/index.ts | 8 +- .../derived-symbol-data/layout.ts | 32 +- .../derived-symbol-data/propagate.ts | 6 +- .../src/kiwi/fig/instance-overrides/index.ts | 8 +- .../kiwi/fig/instance-overrides/resolve.ts | 11 +- .../fig/instance-overrides/sync/fields.ts | 40 ++- .../core/src/kiwi/fig/node-change/guid.ts | 3 +- packages/core/src/kiwi/fig/parse/transfer.ts | 2 +- packages/core/src/kiwi/fig/parse/worker.ts | 5 +- .../core/src/kiwi/schema-runtime/index.ts | 8 +- .../core/src/kiwi/schema-runtime/validate.ts | 4 +- packages/core/src/layout/apply.ts | 10 +- packages/core/src/profiler/hud-renderer.ts | 12 +- packages/core/src/scene-graph/index.ts | 2 - packages/core/src/scene-graph/preview.ts | 3 +- .../core/src/text/derived-text/clipboard.ts | 18 +- packages/core/src/text/outlines.ts | 44 ++- packages/core/src/tools/analyze/clusters.ts | 14 +- packages/core/src/tools/create/svg.ts | 1 - packages/core/src/vector/centerline.ts | 4 +- packages/core/src/vector/index.ts | 4 +- packages/mcp/src/index.ts | 4 +- packages/mcp/src/stdio.ts | 4 +- packages/mcp/src/tool/registration.ts | 7 +- packages/vue/src/canvas/tool-input/use.ts | 13 +- packages/vue/src/canvas/useCanvasInput.ts | 2 +- packages/vue/src/controls/layout/helpers.ts | 3 +- packages/vue/src/controls/okhcl/helpers.ts | 12 +- .../vue/src/shared/input/auto-layout-hover.ts | 25 +- packages/vue/src/shared/input/select/move.ts | 3 +- packages/vue/src/variables/helpers.ts | 2 +- scripts/check-package-metadata.ts | 8 +- scripts/smoke-packages.ts | 5 +- src/app/ai/tools/index.ts | 2 +- src/app/demo/sections/effects.ts | 4 +- src/app/editor/canvas/menu/context.ts | 4 +- src/app/shell/keyboard/registry.ts | 2 +- src/app/shell/menu/editor-actions.ts | 9 +- src/app/shell/menu/shortcut.ts | 6 +- .../ColorPickerPanel/OkhclFields.vue | 24 +- src/components/LayerTree.vue | 315 +++++++++--------- src/components/PagesPanel.vue | 79 ++--- src/components/Toolbar/ToolButton.vue | 7 +- src/components/ui/AppSelect.vue | 7 +- tests/e2e/clipboard/copy-paste.spec.ts | 2 - tests/e2e/code/panel.spec.ts | 7 +- tests/e2e/components/assets-panel.spec.ts | 8 +- tests/e2e/context-menu/basic.spec.ts | 2 +- tests/e2e/editor/auto-layout/drag.spec.ts | 8 +- tests/e2e/layers/reorder.spec.ts | 4 +- .../properties/corner-stroke-toggles.spec.ts | 9 +- tests/e2e/properties/effects.spec.ts | 44 ++- tests/e2e/properties/panel.spec.ts | 5 +- tests/e2e/properties/visibility.spec.ts | 42 ++- tests/e2e/text/editing.spec.ts | 2 - tests/e2e/toolbar/basic.spec.ts | 1 - tests/e2e/variables/dialog.spec.ts | 12 +- tests/engine/app/shell/menu/schema.test.ts | 4 +- tests/engine/clipboard/figma/html.test.ts | 8 +- .../clipboard/openpencil/images.test.ts | 11 +- .../engine/editor/selection/hit-test.test.ts | 5 +- tests/engine/editor/structure/boolean.test.ts | 37 +- .../editor/structure/frame-selection.test.ts | 14 +- tests/engine/figma/api/images.test.ts | 1 - tests/engine/geometry/angles.test.ts | 1 - tests/engine/geometry/bounds.test.ts | 1 - tests/engine/geometry/rotation.test.ts | 1 - tests/engine/io/fig/export/text.test.ts | 47 ++- .../io/fig/import/component-props.test.ts | 26 +- .../io/fig/import/derived-symbol-data.test.ts | 7 +- .../engine/io/fig/import/legacy/text.test.ts | 8 +- .../import/scaled-instance-strokes.test.ts | 9 +- tests/engine/io/fig/import/style-refs.test.ts | 3 +- .../io/fig/roundtrip/glyph-blob.test.ts | 12 +- .../engine/io/fig/roundtrip/variables.test.ts | 22 +- tests/engine/io/subgraph.test.ts | 10 +- tests/engine/profiler/capture-stack.test.ts | 1 - .../engine/profiler/draw-call-counter.test.ts | 1 - tests/engine/profiler/frame-stats.test.ts | 1 - tests/engine/profiler/gpu-timer.test.ts | 1 - tests/engine/profiler/phase-timer.test.ts | 1 - .../render/canvas/boolean-visual.test.ts | 3 +- tests/engine/render/canvas/boolean.test.ts | 12 +- .../render/canvas/corner-smoothing.test.ts | 1 + tests/engine/render/canvas/culling.test.ts | 2 +- .../render/canvas/effects/ordering.test.ts | 2 +- .../render/canvas/font-readiness.test.ts | 2 +- .../render/canvas/renderer-lifecycle.test.ts | 1 + .../render/canvas/retained-backing.test.ts | 1 + tests/engine/render/canvas/text.test.ts | 2 +- .../render/canvas/visual/boolean-matrix.ts | 14 +- .../text/derived-text/clipboard.test.ts | 105 +++--- tests/engine/text/style-runs/mutation.test.ts | 1 - tests/engine/tools/modify/image-fill.test.ts | 1 - tests/engine/vector/blob/mutation.test.ts | 7 +- tests/engine/vector/blob/roundtrip.test.ts | 7 +- tests/engine/vector/normalize.test.ts | 8 +- tests/engine/vector/validate.test.ts | 1 - tests/engine/vue/canvas/render-loop.test.ts | 4 +- .../vue/input/auto-layout-hover.test.ts | 3 +- tests/engine/vue/input/move-threshold.test.ts | 6 +- tests/engine/vue/layout/hug-contents.test.ts | 7 +- tests/helpers/canvas.ts | 5 +- vite.config.ts | 3 +- 135 files changed, 1119 insertions(+), 616 deletions(-) diff --git a/.github/workflows/app.yml b/.github/workflows/app.yml index 96a45e87f..e469287c7 100644 --- a/.github/workflows/app.yml +++ b/.github/workflows/app.yml @@ -22,6 +22,9 @@ jobs: - run: bun install --frozen-lockfile + - name: Format check + run: bun run format:check + - run: bun run build - uses: cloudflare/wrangler-action@v3 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9afdcf0ac..98e3428e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,6 +34,9 @@ jobs: - run: bun install --frozen-lockfile + - name: Format check + run: bun run format:check + - name: Lint & typecheck run: bun run check diff --git a/package.json b/package.json index c5b6c45e4..64e1c53d6 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "lint": "bun run lint:structure && oxlint -c oxlint.json --type-aware --type-check src/ packages/core/src/ packages/vue/src/ packages/cli/src/ packages/mcp/src/", "lint:structure": "oxlint -c oxlint.json vite.config.ts vite/ src/ packages/core/src/ packages/vue/src/ packages/cli/src/ packages/mcp/src/ tests/ scripts/", "format": "oxfmt --write .oxfmtrc.json vite.config.ts vite/ src/ packages/core/src/ packages/cli/src/ packages/mcp/src/ packages/vue/src/ tests scripts/", + "format:check": "bun run format && status=$(git status --porcelain -uall) && test -z \"$status\" || (echo \"$status\" && exit 1)", "check": "bun run build:packages && bun run lint && tsgo --noEmit && bun run check:vue && bun run check:i18n && bun run check:packages && bun run check:arch && bun run test:dupes", "check:i18n": "bun scripts/check-locales.ts", "check:packages": "bun scripts/check-package-metadata.ts", diff --git a/packages/cli/src/commands/export.ts b/packages/cli/src/commands/export.ts index fa4942fd2..f3eb47ee2 100644 --- a/packages/cli/src/commands/export.ts +++ b/packages/cli/src/commands/export.ts @@ -119,7 +119,9 @@ async function exportFromFile(format: string, args: ExportArgs) { } const formatId = format.toLowerCase() - let options: { format?: string; scale?: number; quality?: number; renderThumbnail?: boolean } | undefined + let options: + | { format?: string; scale?: number; quality?: number; renderThumbnail?: boolean } + | undefined if (format === 'JSX') { options = { format: args.style } } else if (format === 'FIG') { diff --git a/packages/core/src/canvas/labels/selection.ts b/packages/core/src/canvas/labels/selection.ts index 76b27f6ef..0de992a5c 100644 --- a/packages/core/src/canvas/labels/selection.ts +++ b/packages/core/src/canvas/labels/selection.ts @@ -1,6 +1,7 @@ import type { Canvas } from 'canvaskit-wasm' import { getAbsolutePosition, getWorldMatrix } from '#core/canvas/coordinate' +import type { SkiaRenderer, RenderOverlays } from '#core/canvas/renderer' import { LABEL_OFFSET_Y, SIZE_PILL_PADDING_X, @@ -12,8 +13,6 @@ import { import { rotatedCorners } from '#core/geometry' import type { SceneNode, SceneGraph } from '#core/scene-graph' -import type { SkiaRenderer, RenderOverlays } from '#core/canvas/renderer' - import { ellipsizeLabelText } from './text' function getOverlayRotation(node: SceneNode, overlays?: RenderOverlays): number { diff --git a/packages/core/src/canvas/overlays/ai.ts b/packages/core/src/canvas/overlays/ai.ts index 106c6754e..beb2d8826 100644 --- a/packages/core/src/canvas/overlays/ai.ts +++ b/packages/core/src/canvas/overlays/ai.ts @@ -1,5 +1,7 @@ import type { Canvas } from 'canvaskit-wasm' +import { drawNodeHighlightRect } from '#core/canvas/highlight-rect' +import type { SkiaRenderer } from '#core/canvas/renderer' import { AI_ACTIVE_COLOR, AI_DONE_COLOR, @@ -8,9 +10,6 @@ import { } from '#core/constants' import type { SceneGraph } from '#core/scene-graph' -import { drawNodeHighlightRect } from '#core/canvas/highlight-rect' -import type { SkiaRenderer } from '#core/canvas/renderer' - export function drawAiOverlays(r: SkiaRenderer, canvas: Canvas, graph: SceneGraph): void { const now = performance.now() diff --git a/packages/core/src/canvas/overlays/auto-layout-hover.ts b/packages/core/src/canvas/overlays/auto-layout-hover.ts index 04fda6f86..74a982c58 100644 --- a/packages/core/src/canvas/overlays/auto-layout-hover.ts +++ b/packages/core/src/canvas/overlays/auto-layout-hover.ts @@ -70,7 +70,13 @@ function toScreenRect(r: SkiaRenderer, [x, y, width, height]: RectTuple) { ) } -function drawStripedRect(r: SkiaRenderer, canvas: Canvas, rectTuple: RectTuple, color: Color, fill: Color) { +function drawStripedRect( + r: SkiaRenderer, + canvas: Canvas, + rectTuple: RectTuple, + color: Color, + fill: Color +) { const [, , width, height] = rectTuple if (width <= 0 || height <= 0) return const rect = toScreenRect(r, rectTuple) @@ -209,13 +215,7 @@ function drawSpacingHover( ) { const rects = gapRects(node, graph) for (const rect of rects) { - drawStripedRect( - r, - canvas, - rect, - AUTO_LAYOUT_HOVER_MAGENTA, - AUTO_LAYOUT_HOVER_MAGENTA_FILL - ) + drawStripedRect(r, canvas, rect, AUTO_LAYOUT_HOVER_MAGENTA, AUTO_LAYOUT_HOVER_MAGENTA_FILL) } if (!showValue || rects.length === 0) return const [x, y, width, height] = rects[0] diff --git a/packages/core/src/canvas/renderer/methods.ts b/packages/core/src/canvas/renderer/methods.ts index 95decd9b0..5dc17eef5 100644 --- a/packages/core/src/canvas/renderer/methods.ts +++ b/packages/core/src/canvas/renderer/methods.ts @@ -1,12 +1,12 @@ import type { ImageFilter, MaskFilter, Canvas, Paint, Path } from 'canvaskit-wasm' -import * as AiOverlays from '#core/canvas/overlays/ai' import * as Effects from '#core/canvas/effects' import * as Fills from '#core/canvas/fills' import * as Labels from '#core/canvas/labels/draw' import * as NodeEditOverlay from '#core/canvas/node-edit-overlay' import type { NodeEditOverlayState } from '#core/canvas/node-edit-overlay' import * as Overlays from '#core/canvas/overlays' +import * as AiOverlays from '#core/canvas/overlays/ai' import * as PenOverlay from '#core/canvas/pen-overlay' import type { SkiaRenderer } from '#core/canvas/renderer' import type { RenderOverlays } from '#core/canvas/renderer/types' @@ -95,7 +95,11 @@ const rendererMethods: ThisType = { Overlays.drawLayoutInsertIndicator(this, canvas, indicator) }, - drawAutoLayoutHover(canvas: Canvas, graph: SceneGraph, hover?: RenderOverlays['autoLayoutHover']) { + drawAutoLayoutHover( + canvas: Canvas, + graph: SceneGraph, + hover?: RenderOverlays['autoLayoutHover'] + ) { Overlays.drawAutoLayoutHover(this, canvas, graph, hover) }, diff --git a/packages/core/src/canvas/renderer/pipeline.ts b/packages/core/src/canvas/renderer/pipeline.ts index 78d8ab867..2b7329884 100644 --- a/packages/core/src/canvas/renderer/pipeline.ts +++ b/packages/core/src/canvas/renderer/pipeline.ts @@ -176,7 +176,11 @@ export function render( canvas.scale(r.dpr, r.dpr) p.beginPhase('render:scene') - if (layer === 'scene' && !hasVolatileOverlays && renderSceneBacking(r, canvas, graph, sceneVersion)) { + if ( + layer === 'scene' && + !hasVolatileOverlays && + renderSceneBacking(r, canvas, graph, sceneVersion) + ) { p.setScenePictureMode('hit', 'backing') } else { canvas.translate(r.panX, r.panY) diff --git a/packages/core/src/canvas/renderer/retained-backing.ts b/packages/core/src/canvas/renderer/retained-backing.ts index 3d25bfe82..d256d5579 100644 --- a/packages/core/src/canvas/renderer/retained-backing.ts +++ b/packages/core/src/canvas/renderer/retained-backing.ts @@ -45,10 +45,7 @@ export function updateSceneBackingPreviewState(r: SkiaRenderer, layer: RenderLay if (layer !== 'scene') return const previous = r.lastSceneViewport const viewportChanged = - !previous || - previous.panX !== r.panX || - previous.panY !== r.panY || - previous.zoom !== r.zoom + !previous || previous.panX !== r.panX || previous.panY !== r.panY || previous.zoom !== r.zoom if (viewportChanged) { const timestamp = now() if (r.sceneBackingLastViewportEventAt > 0) { @@ -85,7 +82,12 @@ function backingScreenCoverageContainsViewport(r: SkiaRenderer): boolean { const scale = r.zoom / backing.zoom const x = r.panX - backing.panX * scale const y = r.panY - backing.panY * scale - return x <= 0 && y <= 0 && x + backing.width * scale >= r.viewportWidth && y + backing.height * scale >= r.viewportHeight + return ( + x <= 0 && + y <= 0 && + x + backing.width * scale >= r.viewportWidth && + y + backing.height * scale >= r.viewportHeight + ) } function backingWorldCoverageContainsLiveViewport(r: SkiaRenderer): boolean { @@ -129,12 +131,7 @@ function drawSceneBacking( const backing = r.sceneBacking if ( !backing || - !backingCoverageContainsLiveViewport( - r, - sceneVersion, - allowStaleZoom, - positionPreviewVersion - ) + !backingCoverageContainsLiveViewport(r, sceneVersion, allowStaleZoom, positionPreviewVersion) ) { return false } @@ -464,4 +461,3 @@ export function renderSceneBacking( positionPreviewVersion ) } - diff --git a/packages/core/src/canvas/shapes.ts b/packages/core/src/canvas/shapes.ts index dc5d18344..8747c9a0e 100644 --- a/packages/core/src/canvas/shapes.ts +++ b/packages/core/src/canvas/shapes.ts @@ -47,7 +47,12 @@ type SmoothCornerPathParams = { arcSectionLength: number } -function smoothCornerRadii(node: SceneNode, width: number, height: number, spread: number): Record { +function smoothCornerRadii( + node: SceneNode, + width: number, + height: number, + spread: number +): Record { const radius = (value: number) => Math.max(0, value + spread) const radii: Record = node.independentCorners ? { @@ -63,7 +68,11 @@ function smoothCornerRadii(node: SceneNode, width: number, height: number, sprea bottomLeft: radius(node.cornerRadius) } - if (radii.topLeft === radii.topRight && radii.topRight === radii.bottomRight && radii.bottomRight === radii.bottomLeft) { + if ( + radii.topLeft === radii.topRight && + radii.topRight === radii.bottomRight && + radii.bottomRight === radii.bottomLeft + ) { const budget = Math.min(width, height) / 2 const clampedRadius = Math.min(radii.topLeft, budget) return { @@ -80,7 +89,10 @@ function smoothCornerRadii(node: SceneNode, width: number, height: number, sprea bottomRight: -1, bottomLeft: -1 } - const adjacentByCorner: Record> = { + const adjacentByCorner: Record< + SmoothCornerKey, + Array<{ corner: SmoothCornerKey; sideLength: number }> + > = { topLeft: [ { corner: 'topRight', sideLength: width }, { corner: 'bottomLeft', sideLength: height } @@ -99,7 +111,9 @@ function smoothCornerRadii(node: SceneNode, width: number, height: number, sprea ] } - for (const corner of (Object.keys(radii) as SmoothCornerKey[]).sort((a, b) => radii[b] - radii[a])) { + for (const corner of (Object.keys(radii) as SmoothCornerKey[]).sort( + (a, b) => radii[b] - radii[a] + )) { const cornerRadius = radii[corner] const budget = Math.min( ...adjacentByCorner[corner].map((adjacent) => { @@ -154,34 +168,115 @@ function smoothCornerPathParams(corner: SmoothCorner, smoothing: number): Smooth } } -function drawTopRightSmoothCorner(path: Path, corner: SmoothCornerPathParams, x: number, y: number) { +function drawTopRightSmoothCorner( + path: Path, + corner: SmoothCornerPathParams, + x: number, + y: number +) { if (corner.radius === 0) { path.lineTo(x + corner.p, y) return } - path.cubicTo(x + corner.a, y, x + corner.a + corner.b, y, x + corner.a + corner.b + corner.c, y + corner.d) - path.arcToRotated(corner.radius, corner.radius, 0, true, false, x + corner.p - corner.d, y + corner.p - corner.a - corner.b - corner.c) - path.cubicTo(x + corner.p, y + corner.p - corner.a - corner.b, x + corner.p, y + corner.p - corner.a, x + corner.p, y + corner.p) + path.cubicTo( + x + corner.a, + y, + x + corner.a + corner.b, + y, + x + corner.a + corner.b + corner.c, + y + corner.d + ) + path.arcToRotated( + corner.radius, + corner.radius, + 0, + true, + false, + x + corner.p - corner.d, + y + corner.p - corner.a - corner.b - corner.c + ) + path.cubicTo( + x + corner.p, + y + corner.p - corner.a - corner.b, + x + corner.p, + y + corner.p - corner.a, + x + corner.p, + y + corner.p + ) } -function drawBottomRightSmoothCorner(path: Path, corner: SmoothCornerPathParams, x: number, y: number) { +function drawBottomRightSmoothCorner( + path: Path, + corner: SmoothCornerPathParams, + x: number, + y: number +) { if (corner.radius === 0) { path.lineTo(x, y + corner.p) return } - path.cubicTo(x, y + corner.a, x, y + corner.a + corner.b, x - corner.d, y + corner.a + corner.b + corner.c) - path.arcToRotated(corner.radius, corner.radius, 0, true, false, x - corner.p + corner.a + corner.b + corner.c, y + corner.p - corner.d) - path.cubicTo(x - corner.p + corner.a + corner.b, y + corner.p, x - corner.p + corner.a, y + corner.p, x - corner.p, y + corner.p) + path.cubicTo( + x, + y + corner.a, + x, + y + corner.a + corner.b, + x - corner.d, + y + corner.a + corner.b + corner.c + ) + path.arcToRotated( + corner.radius, + corner.radius, + 0, + true, + false, + x - corner.p + corner.a + corner.b + corner.c, + y + corner.p - corner.d + ) + path.cubicTo( + x - corner.p + corner.a + corner.b, + y + corner.p, + x - corner.p + corner.a, + y + corner.p, + x - corner.p, + y + corner.p + ) } -function drawBottomLeftSmoothCorner(path: Path, corner: SmoothCornerPathParams, x: number, y: number) { +function drawBottomLeftSmoothCorner( + path: Path, + corner: SmoothCornerPathParams, + x: number, + y: number +) { if (corner.radius === 0) { path.lineTo(x - corner.p, y) return } - path.cubicTo(x - corner.a, y, x - corner.a - corner.b, y, x - corner.a - corner.b - corner.c, y - corner.d) - path.arcToRotated(corner.radius, corner.radius, 0, true, false, x - corner.p + corner.d, y - corner.p + corner.a + corner.b + corner.c) - path.cubicTo(x - corner.p, y - corner.p + corner.a + corner.b, x - corner.p, y - corner.p + corner.a, x - corner.p, y - corner.p) + path.cubicTo( + x - corner.a, + y, + x - corner.a - corner.b, + y, + x - corner.a - corner.b - corner.c, + y - corner.d + ) + path.arcToRotated( + corner.radius, + corner.radius, + 0, + true, + false, + x - corner.p + corner.d, + y - corner.p + corner.a + corner.b + corner.c + ) + path.cubicTo( + x - corner.p, + y - corner.p + corner.a + corner.b, + x - corner.p, + y - corner.p + corner.a, + x - corner.p, + y - corner.p + ) } function drawTopLeftSmoothCorner(path: Path, corner: SmoothCornerPathParams, x: number, y: number) { @@ -189,9 +284,31 @@ function drawTopLeftSmoothCorner(path: Path, corner: SmoothCornerPathParams, x: path.lineTo(x, y - corner.p) return } - path.cubicTo(x, y - corner.a, x, y - corner.a - corner.b, x + corner.d, y - corner.a - corner.b - corner.c) - path.arcToRotated(corner.radius, corner.radius, 0, true, false, x + corner.p - corner.a - corner.b - corner.c, y - corner.p + corner.d) - path.cubicTo(x + corner.p - corner.a - corner.b, y - corner.p, x + corner.p - corner.a, y - corner.p, x + corner.p, y - corner.p) + path.cubicTo( + x, + y - corner.a, + x, + y - corner.a - corner.b, + x + corner.d, + y - corner.a - corner.b - corner.c + ) + path.arcToRotated( + corner.radius, + corner.radius, + 0, + true, + false, + x + corner.p - corner.a - corner.b - corner.c, + y - corner.p + corner.d + ) + path.cubicTo( + x + corner.p - corner.a - corner.b, + y - corner.p, + x + corner.p - corner.a, + y - corner.p, + x + corner.p, + y - corner.p + ) } export function makeSmoothRRectPath( diff --git a/packages/core/src/canvas/strokes.ts b/packages/core/src/canvas/strokes.ts index d4e01af24..3b9b85d67 100644 --- a/packages/core/src/canvas/strokes.ts +++ b/packages/core/src/canvas/strokes.ts @@ -205,7 +205,14 @@ export function drawRRectStrokeWithAlign( stroke: Stroke ): void { if (nodeHasSmoothCorners(node)) { - drawStrokeWithAlign(r, canvas, node, r.ck.LTRBRect(0, 0, node.width, node.height), true, stroke.align) + drawStrokeWithAlign( + r, + canvas, + node, + r.ck.LTRBRect(0, 0, node.width, node.height), + true, + stroke.align + ) return } diff --git a/packages/core/src/canvas/text-derived.ts b/packages/core/src/canvas/text-derived.ts index 825eedbd0..71ce05739 100644 --- a/packages/core/src/canvas/text-derived.ts +++ b/packages/core/src/canvas/text-derived.ts @@ -24,11 +24,7 @@ export function derivedUnderlineRect(node: Pick, baselineY: } } -export function drawFigmaDerivedText( - r: SkiaRenderer, - canvas: Canvas, - node: SceneNode -): boolean { +export function drawFigmaDerivedText(r: SkiaRenderer, canvas: Canvas, node: SceneNode): boolean { if (!node.figmaDerivedTextGlyphs?.length) return false let underlineBaselineY = 0 diff --git a/packages/core/src/canvas/text-outlines.ts b/packages/core/src/canvas/text-outlines.ts index 2fb3182d4..fdac751e3 100644 --- a/packages/core/src/canvas/text-outlines.ts +++ b/packages/core/src/canvas/text-outlines.ts @@ -1,12 +1,17 @@ import type { Path } from 'canvaskit-wasm' import type { SceneNode } from '#core/scene-graph' -import { textNodeToOutlineLayout } from '#core/text/outlines' import type { OutlineCommand } from '#core/text/opentype' +import { textNodeToOutlineLayout } from '#core/text/outlines' import type { SkiaRenderer } from './renderer' -function appendOutlineCommand(path: Path, command: OutlineCommand, xOffset: number, yOffset: number): void { +function appendOutlineCommand( + path: Path, + command: OutlineCommand, + xOffset: number, + yOffset: number +): void { switch (command.type) { case 'M': path.moveTo((command.x ?? 0) + xOffset, (command.y ?? 0) + yOffset) diff --git a/packages/core/src/clipboard.ts b/packages/core/src/clipboard.ts index 29e6c2356..995618698 100644 --- a/packages/core/src/clipboard.ts +++ b/packages/core/src/clipboard.ts @@ -1,10 +1,10 @@ import { inflateSync, deflateSync } from 'fflate' +import { shapeTextForClipboard } from './canvas/text' import { initCodec, getCompiledSchema, getSchemaBytes } from './kiwi/fig/codec' import type { NodeChange as KiwiNodeChange } from './kiwi/fig/codec' import { populateAndApplyOverrides } from './kiwi/fig/instance-overrides' import type { InstanceNodeChange } from './kiwi/fig/instance-overrides' -import { decodeBinarySchema, compileSchema, ByteBuffer } from './kiwi/schema-runtime' import { nodeChangeToProps, sortChildren } from './kiwi/fig/node-change/convert' import { sceneNodeToKiwi, @@ -15,9 +15,9 @@ import { makeCanvasNodeChange, buildFontDigestMap } from './kiwi/fig/node-change/serialize' +import { decodeBinarySchema, compileSchema, ByteBuffer } from './kiwi/schema-runtime' import { randomInt } from './random' import type { SceneGraph, SceneNode } from './scene-graph' -import { shapeTextForClipboard } from './canvas/text' import { buildDerivedTextDataV4 } from './text/derived-text/clipboard' interface FigmaClipboardMeta { @@ -337,7 +337,10 @@ export async function buildFigmaClipboardHTML( if (!source) return change.textAutoResize = 'NONE' change.textUserLayoutVersion = 5 - change.lineHeight = { value: source.lineHeight ?? 100, units: source.lineHeight ? 'PIXELS' : 'PERCENT' } + change.lineHeight = { + value: source.lineHeight ?? 100, + units: source.lineHeight ? 'PIXELS' : 'PERCENT' + } const shaped = await shapeTextForClipboard(source).catch(() => null) change.derivedTextData = await buildDerivedTextDataV4(source, fontDigestMap, shaped, blobs) }) diff --git a/packages/core/src/design-jsx/props-overrides.ts b/packages/core/src/design-jsx/props-overrides.ts index f783c6bd9..3db9bdd73 100644 --- a/packages/core/src/design-jsx/props-overrides.ts +++ b/packages/core/src/design-jsx/props-overrides.ts @@ -395,10 +395,7 @@ function applyTextStyleOverrides(props: Record, o: Partial, - o: Partial -): void { +function applyTextAlignmentOverrides(props: Record, o: Partial): void { const textAlign = props.textAlign ?? props.textAlignHorizontal ?? props.textHorizontalAlignment if (typeof textAlign === 'string') { o.textAlignHorizontal = TEXT_ALIGN_ALIAS_MAP[textAlign.toLowerCase()] ?? 'LEFT' diff --git a/packages/core/src/editor/clipboard.ts b/packages/core/src/editor/clipboard.ts index 0b241c139..104e75af9 100644 --- a/packages/core/src/editor/clipboard.ts +++ b/packages/core/src/editor/clipboard.ts @@ -10,11 +10,11 @@ import type { Vector } from '#core/types' import { createClipboardCopyActions } from './clipboard/copy' import { createClipboardExportActions } from './clipboard/export' import { createClipboardFontActions } from './clipboard/fonts' -import { createClipboardImageActions } from './clipboard/images' import { deleteIds, recreateSnapshots, restoreDeletedEntries } from './clipboard/history' +import { createClipboardImageActions } from './clipboard/images' +import { replaceTargetsWithCreated, selectedReplacementTargets } from './clipboard/paste-replace' import { resolvePasteTarget } from './clipboard/paste-target' import { createClipboardPlacementActions } from './clipboard/placement' -import { replaceTargetsWithCreated, selectedReplacementTargets } from './clipboard/paste-replace' import { collectSubtrees, restoreSubtree, snapshotSubtree } from './clipboard/subtree-history' import type { EditorContext } from './types' diff --git a/packages/core/src/editor/clipboard/paste-replace.ts b/packages/core/src/editor/clipboard/paste-replace.ts index 7d262a15c..76556f2ea 100644 --- a/packages/core/src/editor/clipboard/paste-replace.ts +++ b/packages/core/src/editor/clipboard/paste-replace.ts @@ -1,15 +1,10 @@ +import type { EditorContext } from '#core/editor/types' import { computeAbsoluteBounds } from '#core/geometry' import { computeAllLayouts } from '#core/layout' import type { SceneNode } from '#core/scene-graph' -import type { EditorContext } from '#core/editor/types' +import { deleteIds, type DeletedEntry, recreateSnapshots, restoreDeletedEntries } from './history' import { collectSubtrees, snapshotSubtree } from './subtree-history' -import { - deleteIds, - type DeletedEntry, - recreateSnapshots, - restoreDeletedEntries -} from './history' type CenterNodesAt = (nodeIds: string[], cx: number, cy: number) => void diff --git a/packages/core/src/editor/components/variants.ts b/packages/core/src/editor/components/variants.ts index dfae8f747..6652eec38 100644 --- a/packages/core/src/editor/components/variants.ts +++ b/packages/core/src/editor/components/variants.ts @@ -2,12 +2,12 @@ import { omit } from 'es-toolkit/object' import type { EditorContext } from '#core/editor/types' import { randomHex } from '#core/random' -import { buildVariantName, parseVariantName } from '#core/scene-graph/variant-name' import type { ComponentPropertyDefinition, ComponentPropertyType, SceneNode } from '#core/scene-graph' +import { buildVariantName, parseVariantName } from '#core/scene-graph/variant-name' export type VariantConflict = { values: Record diff --git a/packages/core/src/editor/structure/selection.ts b/packages/core/src/editor/structure/selection.ts index 451aeb560..10bc51663 100644 --- a/packages/core/src/editor/structure/selection.ts +++ b/packages/core/src/editor/structure/selection.ts @@ -11,7 +11,8 @@ export function selectedNodesInSharedParent(ctx: EditorContext, selectedNodes: S if (topLevel.length === 0 || topLevel.some((node) => node.locked)) return null const parentId = topLevel[0].parentId ?? ctx.state.currentPageId - if (!topLevel.every((node) => (node.parentId ?? ctx.state.currentPageId) === parentId)) return null + if (!topLevel.every((node) => (node.parentId ?? ctx.state.currentPageId) === parentId)) + return null const parent = ctx.graph.getNode(parentId) return parent ? { topLevel, parentId, parent } : null diff --git a/packages/core/src/editor/undo.ts b/packages/core/src/editor/undo.ts index d396d1d00..29a77db93 100644 --- a/packages/core/src/editor/undo.ts +++ b/packages/core/src/editor/undo.ts @@ -11,7 +11,6 @@ import { snapshotPage as createPageSnapshot, type PageSnapshot } from './history/snapshot' - import type { EditorContext } from './types' export function createUndoActions(ctx: EditorContext) { diff --git a/packages/core/src/figma-api/index.ts b/packages/core/src/figma-api/index.ts index c7cb3c4f7..4ea7acbfb 100644 --- a/packages/core/src/figma-api/index.ts +++ b/packages/core/src/figma-api/index.ts @@ -219,7 +219,11 @@ export class FigmaAPI implements NodeProxyHost { return (node as BaseNode & { [INTERNAL_ID]: string })[INTERNAL_ID] } - group(nodes: ReadonlyArray, parent: FigmaNodeProxy, index?: number): FigmaGroupNode + group( + nodes: ReadonlyArray, + parent: FigmaNodeProxy, + index?: number + ): FigmaGroupNode group(nodes: ReadonlyArray, parent: BaseNode & ChildrenMixin, index?: number): GroupNode group( nodes: ReadonlyArray, @@ -424,8 +428,16 @@ export class FigmaAPI implements NodeProxyHost { // --- Flatten --- - flatten(nodes: ReadonlyArray, parent?: FigmaNodeProxy, index?: number): FigmaVectorNode - flatten(nodes: ReadonlyArray, parent?: BaseNode & ChildrenMixin, index?: number): VectorNode + flatten( + nodes: ReadonlyArray, + parent?: FigmaNodeProxy, + index?: number + ): FigmaVectorNode + flatten( + nodes: ReadonlyArray, + parent?: BaseNode & ChildrenMixin, + index?: number + ): VectorNode flatten( nodes: ReadonlyArray, parent?: (BaseNode & ChildrenMixin) | FigmaNodeProxy, diff --git a/packages/core/src/icons/svg.ts b/packages/core/src/icons/svg.ts index 3e1a66187..a6591ca22 100644 --- a/packages/core/src/icons/svg.ts +++ b/packages/core/src/icons/svg.ts @@ -118,7 +118,9 @@ export function extractPaths(svgBody: string): IconPathInfo[] { d, fill: resolveAttr(attrValue(tag, 'fill'), groupAttrs.fill, 'currentColor'), stroke: resolveAttr(attrValue(tag, 'stroke'), groupAttrs.stroke, null), - strokeWidth: Number.parseFloat(attrValue(tag, 'stroke-width') ?? groupAttrs.strokeWidth ?? '1'), + strokeWidth: Number.parseFloat( + attrValue(tag, 'stroke-width') ?? groupAttrs.strokeWidth ?? '1' + ), strokeCap: attrValue(tag, 'stroke-linecap') ?? groupAttrs.strokeCap ?? 'butt', strokeJoin: attrValue(tag, 'stroke-linejoin') ?? groupAttrs.strokeJoin ?? 'miter', fillRule: fillRuleAttr === 'evenodd' ? 'EVENODD' : 'NONZERO' diff --git a/packages/core/src/io/formats/fig/export.ts b/packages/core/src/io/formats/fig/export.ts index e00a3a526..9058d278b 100644 --- a/packages/core/src/io/formats/fig/export.ts +++ b/packages/core/src/io/formats/fig/export.ts @@ -4,9 +4,9 @@ import { deflateSync } from 'fflate' import type { SkiaRenderer } from '#core/canvas' import { CANVAS_BG_COLOR, IS_BROWSER, IS_TAURI } from '#core/constants' import { renderThumbnail } from '#core/io/formats/raster' -import { populateAllLazyFigImportRoots } from '#core/kiwi/fig/lazy-import' import { initCodec, getCompiledSchema, getSchemaBytes } from '#core/kiwi/fig/codec' import type { NodeChange } from '#core/kiwi/fig/codec' +import { populateAllLazyFigImportRoots } from '#core/kiwi/fig/lazy-import' import { stringToGuid } from '#core/kiwi/fig/node-change/convert' import { buildFigmaPaintVariableColorMap } from '#core/kiwi/fig/node-change/export-node' import { @@ -91,11 +91,17 @@ async function renderFigThumbnail( ): Promise { if (!pageId) return THUMBNAIL_1X1 if (ck && renderer) { - return renderThumbnail(ck, renderer, graph, pageId, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT) ?? THUMBNAIL_1X1 + return ( + renderThumbnail(ck, renderer, graph, pageId, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT) ?? + THUMBNAIL_1X1 + ) } if (!renderHeadless || IS_BROWSER || IS_TAURI) return THUMBNAIL_1X1 const { headlessRenderThumbnail } = await import('#core/io/formats/raster') - return (await headlessRenderThumbnail(graph, pageId, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT)) ?? THUMBNAIL_1X1 + return ( + (await headlessRenderThumbnail(graph, pageId, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT)) ?? + THUMBNAIL_1X1 + ) } function assignVariableGuids( diff --git a/packages/core/src/io/formats/jsx/tailwind-classes.ts b/packages/core/src/io/formats/jsx/tailwind-classes.ts index cc02bf2de..87e919fdd 100644 --- a/packages/core/src/io/formats/jsx/tailwind-classes.ts +++ b/packages/core/src/io/formats/jsx/tailwind-classes.ts @@ -77,11 +77,7 @@ function applyPadding(style: Record, node: SceneNode): void { else style.padding = `${px(pt)} ${px(pr)} ${px(pb)} ${px(pl)}` } -function applyLayoutStyle( - style: Record, - node: SceneNode, - graph: SceneGraph -): void { +function applyLayoutStyle(style: Record, node: SceneNode, graph: SceneGraph): void { const ctx = getNodeContext(node, graph) if (ctx.isGrid) { @@ -143,11 +139,9 @@ function applyAppearanceStyle(style: Record, node: SceneNode): v function applyTextStyle(style: Record, node: SceneNode): void { if (node.type !== 'TEXT') return style.fontSize = px(node.fontSize) - if (node.fontFamily && node.fontFamily !== DEFAULT_FONT_FAMILY) - style.fontFamily = node.fontFamily + if (node.fontFamily && node.fontFamily !== DEFAULT_FONT_FAMILY) style.fontFamily = node.fontFamily if (node.fontWeight !== 400) style.fontWeight = String(node.fontWeight) - if (node.textAlignHorizontal !== 'LEFT') - style.textAlign = node.textAlignHorizontal.toLowerCase() + if (node.textAlignHorizontal !== 'LEFT') style.textAlign = node.textAlignHorizontal.toLowerCase() const textColor = solidFillColor(node.fills) if (textColor) style.color = textColor } diff --git a/packages/core/src/io/formats/svg/paths.ts b/packages/core/src/io/formats/svg/paths.ts index 3e8ecb0ac..f0b4e1b02 100644 --- a/packages/core/src/io/formats/svg/paths.ts +++ b/packages/core/src/io/formats/svg/paths.ts @@ -1,5 +1,5 @@ -import { polygonVertices } from '#core/geometry' import { nodeHasRadius } from '#core/canvas/shapes' +import { polygonVertices } from '#core/geometry' import type { SceneNode, VectorNetwork, VectorSegment, VectorVertex } from '#core/scene-graph' const CMD_CLOSE = 0 diff --git a/packages/core/src/kiwi/fig/import.ts b/packages/core/src/kiwi/fig/import.ts index e4a470d8b..5a78bda5e 100644 --- a/packages/core/src/kiwi/fig/import.ts +++ b/packages/core/src/kiwi/fig/import.ts @@ -1,10 +1,10 @@ import { isNotNil } from 'es-toolkit/predicate' import { BLACK } from '#core/constants' -import { setLazyFigImportContext } from '#core/kiwi/fig/lazy-import' import type { NodeChange, VariableDataValuesEntry, Color, GUID } from '#core/kiwi/fig/codec' import { populateAndApplyOverrides } from '#core/kiwi/fig/instance-overrides' import type { InstanceNodeChange } from '#core/kiwi/fig/instance-overrides' +import { setLazyFigImportContext } from '#core/kiwi/fig/lazy-import' import { guidToString, nodeChangeToProps, @@ -19,10 +19,14 @@ import type { VariableType, VariableValue } from '#core/scene-graph' type AssetRef = { key: string; version?: string } type AliasRef = { guid?: GUID; assetRef?: AssetRef } -function applyImportedCanvasMetadata(page: ReturnType, canvasNc: NodeChange) { +function applyImportedCanvasMetadata( + page: ReturnType, + canvasNc: NodeChange +) { page.source.format = 'fig' page.source.orderKey = canvasNc.parentIndex?.position ?? null - if (canvasNc.backgroundColor) page.source.fig.rawNodeFields.backgroundColor = structuredClone(canvasNc.backgroundColor) + if (canvasNc.backgroundColor) + page.source.fig.rawNodeFields.backgroundColor = structuredClone(canvasNc.backgroundColor) page.source.fig.rawNodeFields.strokeJoin = canvasNc.strokeJoin page.source.fig.rawNodeFields.strokeWeight = canvasNc.strokeWeight if (canvasNc.pageType) page.source.fig.rawNodeFields.pageType = canvasNc.pageType @@ -476,7 +480,8 @@ export function importNodeChanges( ) }) - if (activeRootIds) rememberLazyFigImportContext(graph, changeMap, guidToNodeId, blobs, activeRootIds) + if (activeRootIds) + rememberLazyFigImportContext(graph, changeMap, guidToNodeId, blobs, activeRootIds) setVariableColorResolver(null) diff --git a/packages/core/src/kiwi/fig/instance-overrides/component-props/apply.ts b/packages/core/src/kiwi/fig/instance-overrides/component-props/apply.ts index 006944d1a..effd6384f 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/component-props/apply.ts +++ b/packages/core/src/kiwi/fig/instance-overrides/component-props/apply.ts @@ -63,7 +63,8 @@ function applySwapProp( val: ComponentPropValue, modified?: Set ): void { - const swapId = propTextCharacters(val) ?? (val.guidValue ? guidToString(val.guidValue) : undefined) + const swapId = + propTextCharacters(val) ?? (val.guidValue ? guidToString(val.guidValue) : undefined) const newCompId = swapId ? ctx.guidToNodeId.get(swapId) : undefined if (!newCompId) return applyPatchAndMark( diff --git a/packages/core/src/kiwi/fig/instance-overrides/component-props/index.ts b/packages/core/src/kiwi/fig/instance-overrides/component-props/index.ts index 8a08de0d5..038e21f00 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/component-props/index.ts +++ b/packages/core/src/kiwi/fig/instance-overrides/component-props/index.ts @@ -1,10 +1,8 @@ -import { - applyInstanceDirectAssignments, - applyOverrideAssignments -} from './assignments' -import { collectAssignmentsMap, collectPropRefsMap } from './maps' import type { OverrideContext } from '#core/kiwi/fig/instance-overrides/types' +import { applyInstanceDirectAssignments, applyOverrideAssignments } from './assignments' +import { collectAssignmentsMap, collectPropRefsMap } from './maps' + /** * Apply all component property assignments (visibility toggles, instance swaps). * diff --git a/packages/core/src/kiwi/fig/instance-overrides/component-props/values.ts b/packages/core/src/kiwi/fig/instance-overrides/component-props/values.ts index 684e90c4c..baf15d4e8 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/component-props/values.ts +++ b/packages/core/src/kiwi/fig/instance-overrides/component-props/values.ts @@ -1,11 +1,10 @@ import type { GUID } from '#core/kiwi/fig/codec' -import { guidToString } from '#core/kiwi/fig/node-change/convert' - import type { ComponentPropAssignment, ComponentPropValue, OverrideContext } from '#core/kiwi/fig/instance-overrides/types' +import { guidToString } from '#core/kiwi/fig/node-change/convert' export function normalizePropName(value: string): string { return value.toLowerCase().replace(/[^a-z0-9]/g, '') @@ -42,7 +41,8 @@ function resolveAssignmentValue( if (variableValue?.symbolIdValue?.guid) return { guidValue: variableValue.symbolIdValue.guid } if (variableValue?.boolValue !== undefined) return { boolValue: variableValue.boolValue } if (variableValue?.textValue !== undefined) return { textValue: variableValue.textValue } - if (variableValue?.textDataValue !== undefined) return { textDataValue: variableValue.textDataValue } + if (variableValue?.textDataValue !== undefined) + return { textDataValue: variableValue.textDataValue } return resolveDefaults ? (ctx.propDefaults.get(key) ?? assignment.value) : assignment.value } diff --git a/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/geometry.ts b/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/geometry.ts index 0d5885b03..8f0dea145 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/geometry.ts +++ b/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/geometry.ts @@ -1,5 +1,5 @@ -import { resolveGeometryPaths } from '#core/kiwi/fig/node-change/convert' import type { DerivedSymbolOverride } from '#core/kiwi/fig/instance-overrides/types' +import { resolveGeometryPaths } from '#core/kiwi/fig/node-change/convert' import type { GeometryPath, SceneNode } from '#core/scene-graph' function scaleGeometryBlobs(geom: GeometryPath[], sx: number, sy: number): GeometryPath[] { @@ -36,12 +36,20 @@ export function resolveDsdGeometry( if (fg.length > 0) result.fillGeometry = fg else if (d.size && target.fillGeometry.length > 0 && target.width > 0 && target.height > 0) { - result.fillGeometry = scaleGeometryBlobs(target.fillGeometry, d.size.x / target.width, d.size.y / target.height) + result.fillGeometry = scaleGeometryBlobs( + target.fillGeometry, + d.size.x / target.width, + d.size.y / target.height + ) } if (sg.length > 0) result.strokeGeometry = sg else if (d.size && target.strokeGeometry.length > 0 && target.width > 0 && target.height > 0) { - result.strokeGeometry = scaleGeometryBlobs(target.strokeGeometry, d.size.x / target.width, d.size.y / target.height) + result.strokeGeometry = scaleGeometryBlobs( + target.strokeGeometry, + d.size.x / target.width, + d.size.y / target.height + ) } return result diff --git a/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/index.ts b/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/index.ts index 9e22f228d..e42a21cb6 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/index.ts +++ b/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/index.ts @@ -1,6 +1,9 @@ import { applyOverridePatch } from '#core/kiwi/fig/instance-overrides/patches' import { resolveOverrideTarget } from '#core/kiwi/fig/instance-overrides/resolve' -import type { DerivedSymbolOverride, OverrideContext } from '#core/kiwi/fig/instance-overrides/types' +import type { + DerivedSymbolOverride, + OverrideContext +} from '#core/kiwi/fig/instance-overrides/types' import { buildDsdLayoutUpdates } from './layout' import { propagateDsdChanges } from './propagate' @@ -45,7 +48,8 @@ function resolveDsdUpdates(ctx: OverrideContext): { modified: Set; sizeS const nodeId = ctx.guidToNodeId.get(ncId) if (!nodeId || (ctx.activeNodeIds && !ctx.activeNodeIds.has(nodeId))) continue - for (const d of derived) applyDsdOverride(ctx, visibleSiblingCount, nodeId, d, modified, sizeSet) + for (const d of derived) + applyDsdOverride(ctx, visibleSiblingCount, nodeId, d, modified, sizeSet) } return { modified, sizeSet } diff --git a/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/layout.ts b/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/layout.ts index 81d5e318c..a046c7c80 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/layout.ts +++ b/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/layout.ts @@ -1,11 +1,18 @@ +import type { + DerivedSymbolOverride, + OverrideContext +} from '#core/kiwi/fig/instance-overrides/types' import { convertLetterSpacing, convertLineHeight } from '#core/kiwi/fig/node-change/convert' import { convertFigmaDerivedTextGlyphs } from '#core/kiwi/fig/node-change/derived-text-glyphs' -import type { DerivedSymbolOverride, OverrideContext } from '#core/kiwi/fig/instance-overrides/types' import type { SceneNode } from '#core/scene-graph' import { resolveDsdGeometry } from './geometry' -function getVisibleSiblingCount(ctx: OverrideContext, cache: Map, parentId: string): number { +function getVisibleSiblingCount( + ctx: OverrideContext, + cache: Map, + parentId: string +): number { const cached = cache.get(parentId) if (cached !== undefined) return cached const count = ctx.graph.getChildren(parentId).filter((child) => child.visible).length @@ -18,12 +25,21 @@ function resolveSizeOnlyPosition( visibleSiblingCount: Map, node: SceneNode ): Pick | null { - if (!node.parentId || getVisibleSiblingCount(ctx, visibleSiblingCount, node.parentId) !== 1 || !node.componentId) return null + if ( + !node.parentId || + getVisibleSiblingCount(ctx, visibleSiblingCount, node.parentId) !== 1 || + !node.componentId + ) + return null const source = ctx.graph.getNode(node.componentId) if (!source) return null const sourceParent = source.parentId ? ctx.graph.getNode(source.parentId) : null if (!sourceParent) return { x: source.x, y: source.y } - const withinParent = source.x >= 0 && source.y >= 0 && source.x + source.width <= sourceParent.width + 0.01 && source.y + source.height <= sourceParent.height + 0.01 + const withinParent = + source.x >= 0 && + source.y >= 0 && + source.x + source.width <= sourceParent.width + 0.01 && + source.y + source.height <= sourceParent.height + 0.01 return withinParent ? { x: source.x, y: source.y } : { x: 0, y: 0 } } @@ -35,9 +51,13 @@ function buildDsdTextUpdates( const updates: Partial = {} if (d.fontSize !== undefined) updates.fontSize = d.fontSize if (d.lineHeight !== undefined) updates.lineHeight = convertLineHeight(d.lineHeight, d.fontSize) - if (d.letterSpacing !== undefined) updates.letterSpacing = convertLetterSpacing(d.letterSpacing, d.fontSize) + if (d.letterSpacing !== undefined) + updates.letterSpacing = convertLetterSpacing(d.letterSpacing, d.fontSize) if (d.strokeWeight !== undefined && target.strokes.length > 0) { - updates.strokes = target.strokes.map((stroke) => ({ ...stroke, weight: d.strokeWeight as number })) + updates.strokes = target.strokes.map((stroke) => ({ + ...stroke, + weight: d.strokeWeight as number + })) } const figmaDerivedTextGlyphs = convertFigmaDerivedTextGlyphs(d.derivedTextData, blobs) if (figmaDerivedTextGlyphs.length > 0) updates.figmaDerivedTextGlyphs = figmaDerivedTextGlyphs diff --git a/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/propagate.ts b/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/propagate.ts index 69714867a..a8e1830a2 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/propagate.ts +++ b/packages/core/src/kiwi/fig/instance-overrides/derived-symbol-data/propagate.ts @@ -17,8 +17,10 @@ function buildCloneUpdates( if (source.x !== clone.x) updates.x = source.x if (source.y !== clone.y) updates.y = source.y if (!ctx.geometryOverrideNodes.has(cloneId)) { - if (source.fillGeometry !== clone.fillGeometry) updates.fillGeometry = copyGeometryPaths(source.fillGeometry) - if (source.strokeGeometry !== clone.strokeGeometry) updates.strokeGeometry = copyGeometryPaths(source.strokeGeometry) + if (source.fillGeometry !== clone.fillGeometry) + updates.fillGeometry = copyGeometryPaths(source.fillGeometry) + if (source.strokeGeometry !== clone.strokeGeometry) + updates.strokeGeometry = copyGeometryPaths(source.strokeGeometry) } if (source.text === clone.text && source.figmaDerivedTextGlyphs) { updates.figmaDerivedTextGlyphs = structuredClone(source.figmaDerivedTextGlyphs) diff --git a/packages/core/src/kiwi/fig/instance-overrides/index.ts b/packages/core/src/kiwi/fig/instance-overrides/index.ts index e307cb659..9914d2b23 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/index.ts +++ b/packages/core/src/kiwi/fig/instance-overrides/index.ts @@ -14,10 +14,10 @@ import { guidToString } from '#core/kiwi/fig/node-change/convert' import type { SceneGraph, SceneNode } from '#core/scene-graph' import { copyFills, copyStyleRuns } from '#core/scene-graph/copy' +import { applyComponentProperties } from './component-props' import { applyConstraintScaling } from './constraints' import { applyDerivedSymbolData } from './derived-symbol-data' import { populateInstances } from './populate' -import { applyComponentProperties } from './component-props' import { preComputeRoots } from './resolve' import { applySymbolOverrides } from './symbol/overrides' import { propagateOverridesTransitively } from './sync' @@ -100,7 +100,11 @@ function propagateResolvedChildPlacementClones(graph: SceneGraph): void { const sourceChild = graph.getNode(source.childIds[i]) const child = graph.getNode(node.childIds[i]) if (!sourceChild || !child) continue - if (sourceChild.overrideKey && child.overrideKey && sourceChild.overrideKey !== child.overrideKey) { + if ( + sourceChild.overrideKey && + child.overrideKey && + sourceChild.overrideKey !== child.overrideKey + ) { continue } const updates: Partial = {} diff --git a/packages/core/src/kiwi/fig/instance-overrides/resolve.ts b/packages/core/src/kiwi/fig/instance-overrides/resolve.ts index 581895a35..4eaa8332d 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/resolve.ts +++ b/packages/core/src/kiwi/fig/instance-overrides/resolve.ts @@ -325,7 +325,10 @@ export function resolveOverrideTarget( * Only renames when the current name matches the root component name (preserves * user-given names). Clears the componentIdRoot cache after changing the tree. */ -function collectStyledStrokeDescendants(ctx: OverrideContext, nodeId: string): SceneNode['strokes'][] { +function collectStyledStrokeDescendants( + ctx: OverrideContext, + nodeId: string +): SceneNode['strokes'][] { const result: SceneNode['strokes'][] = [] const visit = (id: string) => { const node = ctx.graph.getNode(id) @@ -337,7 +340,11 @@ function collectStyledStrokeDescendants(ctx: OverrideContext, nodeId: string): S return result } -function applyStrokeDescendants(ctx: OverrideContext, nodeId: string, strokes: SceneNode['strokes'][]): void { +function applyStrokeDescendants( + ctx: OverrideContext, + nodeId: string, + strokes: SceneNode['strokes'][] +): void { let index = 0 const visit = (id: string) => { const node = ctx.graph.getNode(id) diff --git a/packages/core/src/kiwi/fig/instance-overrides/sync/fields.ts b/packages/core/src/kiwi/fig/instance-overrides/sync/fields.ts index 07a572164..473651dca 100644 --- a/packages/core/src/kiwi/fig/instance-overrides/sync/fields.ts +++ b/packages/core/src/kiwi/fig/instance-overrides/sync/fields.ts @@ -27,12 +27,24 @@ function assignDirectUpdate( updates: Partial ): void { switch (key) { - case 'text': updates.text = source.text; break - case 'visible': updates.visible = source.visible; break - case 'opacity': updates.opacity = source.opacity; break - case 'locked': updates.locked = source.locked; break - case 'layoutGrow': updates.layoutGrow = source.layoutGrow; break - case 'textAutoResize': updates.textAutoResize = source.textAutoResize; break + case 'text': + updates.text = source.text + break + case 'visible': + updates.visible = source.visible + break + case 'opacity': + updates.opacity = source.opacity + break + case 'locked': + updates.locked = source.locked + break + case 'layoutGrow': + updates.layoutGrow = source.layoutGrow + break + case 'textAutoResize': + updates.textAutoResize = source.textAutoResize + break } } @@ -59,10 +71,18 @@ function assignCopiedUpdate( updates: Partial ): void { switch (key) { - case 'fills': updates.fills = copyFills(source.fills); break - case 'strokes': updates.strokes = copyStrokes(source.strokes); break - case 'effects': updates.effects = copyEffects(source.effects); break - case 'styleRuns': updates.styleRuns = copyStyleRuns(source.styleRuns); break + case 'fills': + updates.fills = copyFills(source.fills) + break + case 'strokes': + updates.strokes = copyStrokes(source.strokes) + break + case 'effects': + updates.effects = copyEffects(source.effects) + break + case 'styleRuns': + updates.styleRuns = copyStyleRuns(source.styleRuns) + break } } diff --git a/packages/core/src/kiwi/fig/node-change/guid.ts b/packages/core/src/kiwi/fig/node-change/guid.ts index 590d466ad..d0588bc28 100644 --- a/packages/core/src/kiwi/fig/node-change/guid.ts +++ b/packages/core/src/kiwi/fig/node-change/guid.ts @@ -6,7 +6,8 @@ export function guidToString(guid: GUID): string { export function stringToGuid(str: string): GUID { const match = str.match(/^(?:VariableID:|VariableCollectionId:)?(\d+):(\d+)$/) - if (match) return { sessionID: Number.parseInt(match[1], 10), localID: Number.parseInt(match[2], 10) } + if (match) + return { sessionID: Number.parseInt(match[1], 10), localID: Number.parseInt(match[2], 10) } const [session, local] = str.split(':') return { sessionID: Number.parseInt(session, 10), localID: Number.parseInt(local, 10) } } diff --git a/packages/core/src/kiwi/fig/parse/transfer.ts b/packages/core/src/kiwi/fig/parse/transfer.ts index 54c8b0119..f68019be0 100644 --- a/packages/core/src/kiwi/fig/parse/transfer.ts +++ b/packages/core/src/kiwi/fig/parse/transfer.ts @@ -1,5 +1,5 @@ -import { getLazyFigImportContext, setLazyFigImportContext } from '#core/kiwi/fig/lazy-import' import type { InstanceNodeChange } from '#core/kiwi/fig/instance-overrides' +import { getLazyFigImportContext, setLazyFigImportContext } from '#core/kiwi/fig/lazy-import' import { SceneGraph } from '#core/scene-graph' import type { SceneNode, Variable, VariableCollection, DocumentColorSpace } from '#core/scene-graph' diff --git a/packages/core/src/kiwi/fig/parse/worker.ts b/packages/core/src/kiwi/fig/parse/worker.ts index 3e17b78d7..db66f75f4 100644 --- a/packages/core/src/kiwi/fig/parse/worker.ts +++ b/packages/core/src/kiwi/fig/parse/worker.ts @@ -22,7 +22,10 @@ self.onmessage = (e: MessageEvent) => { const graph = importNodeChanges(nodeChanges, blobs, new Map(images), request.options) graph.figKiwiVersion = figKiwiVersion const serialized = serializeSceneGraph(graph) - ;(self as WorkerScope).postMessage({ graph: serialized }, serializedSceneGraphTransferList(serialized)) + ;(self as WorkerScope).postMessage( + { graph: serialized }, + serializedSceneGraphTransferList(serialized) + ) } catch (err) { self.postMessage({ error: err instanceof Error ? err.message : String(err) }) } diff --git a/packages/core/src/kiwi/schema-runtime/index.ts b/packages/core/src/kiwi/schema-runtime/index.ts index ac279b743..9b8cbc5d4 100644 --- a/packages/core/src/kiwi/schema-runtime/index.ts +++ b/packages/core/src/kiwi/schema-runtime/index.ts @@ -3,4 +3,10 @@ export { ByteBuffer } from './bb' export { compileSchema } from './js' export { decodeBinarySchema, encodeBinarySchema } from './binary' export { parseSchema } from './parser' -export { validateSchema, expectFieldNumber, expectEnumValue, findDefinition, findField } from './validate' +export { + validateSchema, + expectFieldNumber, + expectEnumValue, + findDefinition, + findField +} from './validate' diff --git a/packages/core/src/kiwi/schema-runtime/validate.ts b/packages/core/src/kiwi/schema-runtime/validate.ts index a28f3300d..367a0f4a7 100644 --- a/packages/core/src/kiwi/schema-runtime/validate.ts +++ b/packages/core/src/kiwi/schema-runtime/validate.ts @@ -6,7 +6,9 @@ export function findDefinition(schema: Schema, name: string): Definition | null } export function findField(schema: Schema, definitionName: string, fieldName: string): Field | null { - return findDefinition(schema, definitionName)?.fields.find((field) => field.name === fieldName) ?? null + return ( + findDefinition(schema, definitionName)?.fields.find((field) => field.name === fieldName) ?? null + ) } export function expectFieldNumber( diff --git a/packages/core/src/layout/apply.ts b/packages/core/src/layout/apply.ts index 0f92be791..42c8e3c4e 100644 --- a/packages/core/src/layout/apply.ts +++ b/packages/core/src/layout/apply.ts @@ -36,8 +36,14 @@ function updateChildFromYoga(graph: SceneGraph, child: SceneNode, yogaChild: Yog const derived = child.figmaDerivedLayout graph.updateNode(child.id, { - x: child.type === 'INSTANCE' ? yogaChild.getComputedLeft() : (derived?.x ?? yogaChild.getComputedLeft()), - y: child.type === 'INSTANCE' ? yogaChild.getComputedTop() : (derived?.y ?? yogaChild.getComputedTop()), + x: + child.type === 'INSTANCE' + ? yogaChild.getComputedLeft() + : (derived?.x ?? yogaChild.getComputedLeft()), + y: + child.type === 'INSTANCE' + ? yogaChild.getComputedTop() + : (derived?.y ?? yogaChild.getComputedTop()), width: derived?.width ?? yogaChild.getComputedWidth(), height: derived?.height ?? yogaChild.getComputedHeight() }) diff --git a/packages/core/src/profiler/hud-renderer.ts b/packages/core/src/profiler/hud-renderer.ts index 3fcbb0510..32a7f545b 100644 --- a/packages/core/src/profiler/hud-renderer.ts +++ b/packages/core/src/profiler/hud-renderer.ts @@ -155,8 +155,16 @@ export class HudRenderer { const pictureLabel = stats.scenePictureMode === 'record' ? 'record' : 'picture' const pictureTime = - stats.scenePictureMode === 'record' ? stats.scenePictureRecordTime : stats.scenePictureDrawTime - canvas.drawText(`${pictureLabel}: ${pictureTime.toFixed(1)}ms`, col1, y, this.textPaint, this.hudFont) + stats.scenePictureMode === 'record' + ? stats.scenePictureRecordTime + : stats.scenePictureDrawTime + canvas.drawText( + `${pictureLabel}: ${pictureTime.toFixed(1)}ms`, + col1, + y, + this.textPaint, + this.hudFont + ) canvas.drawText(`flush: ${stats.flushTime.toFixed(1)}ms`, col2, y, this.textPaint, this.hudFont) if (visiblePhases.length > 0) { diff --git a/packages/core/src/scene-graph/index.ts b/packages/core/src/scene-graph/index.ts index 7a2e3e3d0..1208cd0d8 100644 --- a/packages/core/src/scene-graph/index.ts +++ b/packages/core/src/scene-graph/index.ts @@ -325,8 +325,6 @@ export class SceneGraph { 'maxHeight' ]) - - runPreviewUpdates(fn: () => void): void { this.previewMutationDepth++ try { diff --git a/packages/core/src/scene-graph/preview.ts b/packages/core/src/scene-graph/preview.ts index 72e569e03..6bbe85f85 100644 --- a/packages/core/src/scene-graph/preview.ts +++ b/packages/core/src/scene-graph/preview.ts @@ -1,6 +1,5 @@ -import { normalizeVectorNetwork } from './vector-network' - import type { SceneNode } from './types' +import { normalizeVectorNetwork } from './vector-network' type PreviewGraph = { nodes: Map diff --git a/packages/core/src/text/derived-text/clipboard.ts b/packages/core/src/text/derived-text/clipboard.ts index 2dcfc9b1e..ba94bde02 100644 --- a/packages/core/src/text/derived-text/clipboard.ts +++ b/packages/core/src/text/derived-text/clipboard.ts @@ -1,10 +1,8 @@ import { prepareWithSegments, layoutWithLines } from '@chenglou/pretext' import type { NodeChange } from '#core/kiwi/fig/codec' -import type { SceneNode } from '#core/scene-graph' - import { encodePathCommandsBlob } from '#core/kiwi/fig/node-change/path-commands' - +import type { SceneNode } from '#core/scene-graph' import { normalizeFontFamily, weightToFigmaStyle, weightToStyle } from '#core/text/fonts' import { type GlyphOutlineMetrics, getGlyphOutlineMetricsSync } from '#core/text/opentype' @@ -136,7 +134,8 @@ export async function buildDerivedTextDataV4( const normalizedFamily = normalizeFontFamily(node.fontFamily) const key = `${normalizedFamily}|${style}` const lineHeightFallback = node.lineHeight ?? Math.ceil(node.fontSize * 1.2) - const glyphMetrics = getGlyphOutlineMetricsSync(node.fontFamily, style, node.text, node.fontSize) ?? [] + const glyphMetrics = + getGlyphOutlineMetricsSync(node.fontFamily, style, node.text, node.fontSize) ?? [] const fallbackAdvance = node.text.length > 0 ? node.width / Math.max(node.text.length, 1) : 0 const textGlyphs = buildTextGlyphs(node.text, glyphMetrics, fallbackAdvance, node.fontSize) @@ -144,7 +143,10 @@ export async function buildDerivedTextDataV4( const lineBreaks = computeLineBreaks(node, glyphMetrics, textGlyphs, fallbackAdvance, shaped) const lineBreakSet = new Set(lineBreaks) - const shapedByChar = new Map)['glyphs'][number]>() + const shapedByChar = new Map< + number, + (typeof shaped extends null | undefined ? never : NonNullable)['glyphs'][number] + >() if (shaped) { for (const g of shaped.glyphs) shapedByChar.set(g.firstCharacter, g) } @@ -207,7 +209,11 @@ export async function buildDerivedTextDataV4( glyphs, fontMetaData: [ { - key: { family: normalizedFamily, style: weightToFigmaStyle(node.fontWeight, node.italic), postscript: '' }, + key: { + family: normalizedFamily, + style: weightToFigmaStyle(node.fontWeight, node.italic), + postscript: '' + }, fontLineHeight: 1.2, fontDigest: digestMap.get(key), fontStyle: node.italic ? 'ITALIC' : 'NORMAL', diff --git a/packages/core/src/text/outlines.ts b/packages/core/src/text/outlines.ts index 876e5c190..aed765e6b 100644 --- a/packages/core/src/text/outlines.ts +++ b/packages/core/src/text/outlines.ts @@ -2,7 +2,11 @@ import { prepareWithSegments, layoutWithLines } from '@chenglou/pretext' import type { CharacterStyleOverride, SceneNode } from '#core/scene-graph' import { fontManager, weightToStyle } from '#core/text/fonts' -import { fontHasGlyphSync, getGlyphOutlineMetricsSync, type OutlineCommand } from '#core/text/opentype' +import { + fontHasGlyphSync, + getGlyphOutlineMetricsSync, + type OutlineCommand +} from '#core/text/opentype' export type TextOutlineUnsupportedReason = | 'not-text' @@ -29,7 +33,12 @@ export interface TextOutlineLayout { const COMPLEX_SCRIPT_PATTERN = /[\u0590-\u08ff\u0900-\u0dff\ufb1d-\ufdff\ufe70-\ufeff]/ -type TextStyle = Required> +type TextStyle = Required< + Pick< + CharacterStyleOverride, + 'fontFamily' | 'fontSize' | 'fontWeight' | 'italic' | 'letterSpacing' + > +> function baseTextStyle(node: SceneNode): TextStyle { return { @@ -66,7 +75,10 @@ function resolvedGlyphStyle(style: TextStyle, char: string): TextStyle | null { if (fontHasGlyphSync(style.fontFamily, styleName(style), char)) return style const family = fallbackFamilies().find((candidate) => { const next = fallbackStyle(style, candidate) - return fontManager.loadedData(next.fontFamily, styleName(next)) && fontHasGlyphSync(next.fontFamily, styleName(next), char) + return ( + fontManager.loadedData(next.fontFamily, styleName(next)) && + fontHasGlyphSync(next.fontFamily, styleName(next), char) + ) }) return family ? fallbackStyle(style, family) : null } @@ -130,7 +142,12 @@ function glyphAdvance(node: SceneNode, absoluteIndex: number): number | null { const char = node.text[absoluteIndex] const style = resolvedGlyphStyle(textStyleAt(node, absoluteIndex), char) if (!style) return null - const metrics = getGlyphOutlineMetricsSync(style.fontFamily, styleName(style), char, style.fontSize) + const metrics = getGlyphOutlineMetricsSync( + style.fontFamily, + styleName(style), + char, + style.fontSize + ) const glyph = metrics?.[0] return glyph ? glyph.advance + style.letterSpacing : null } @@ -211,7 +228,12 @@ function verticalOffset(node: SceneNode, contentHeight: number): number { } } -function lineGlyphs(node: SceneNode, line: TextLine, baseline: number, xOffset: number): { glyphs: TextOutlineGlyph[]; width: number } | null { +function lineGlyphs( + node: SceneNode, + line: TextLine, + baseline: number, + xOffset: number +): { glyphs: TextOutlineGlyph[]; width: number } | null { const glyphs: TextOutlineGlyph[] = [] let cursorX = xOffset let index = 0 @@ -229,7 +251,12 @@ function lineGlyphs(node: SceneNode, line: TextLine, baseline: number, xOffset: } const segment = line.text.slice(index, end) - const metrics = getGlyphOutlineMetricsSync(style.fontFamily, styleName(style), segment, style.fontSize) + const metrics = getGlyphOutlineMetricsSync( + style.fontFamily, + styleName(style), + segment, + style.fontSize + ) if (!metrics) return null for (const glyph of metrics) { @@ -258,7 +285,10 @@ export function textNodeToOutlineLayout(node: SceneNode): TextOutlineLayout | nu if (!measured) return null const xOffset = lineOffsetX(node, measured.width) maxWidth = Math.max(maxWidth, measured.width) - const placed = xOffset === 0 ? measured.glyphs : measured.glyphs.map((glyph) => ({ ...glyph, x: glyph.x + xOffset })) + const placed = + xOffset === 0 + ? measured.glyphs + : measured.glyphs.map((glyph) => ({ ...glyph, x: glyph.x + xOffset })) glyphs.push(...placed) } diff --git a/packages/core/src/tools/analyze/clusters.ts b/packages/core/src/tools/analyze/clusters.ts index ae2af8d9d..9ccf6a705 100644 --- a/packages/core/src/tools/analyze/clusters.ts +++ b/packages/core/src/tools/analyze/clusters.ts @@ -88,13 +88,13 @@ export const analyzeClusters = defineTool({ [...signatureMap.entries()] .filter(([, nodes]) => nodes.length >= minCount) .map(([signature, nodes]) => { - const avgWidth = nodes.reduce((sum, node) => sum + node.width, 0) / nodes.length - const avgHeight = nodes.reduce((sum, node) => sum + node.height, 0) / nodes.length - const widths = nodes.map((node) => node.width) - const heights = nodes.map((node) => node.height) - const widthRange = Math.max(...widths) - Math.min(...widths) - const heightRange = Math.max(...heights) - Math.min(...heights) - const confidence = calcClusterConfidence(nodes) + const avgWidth = nodes.reduce((sum, node) => sum + node.width, 0) / nodes.length + const avgHeight = nodes.reduce((sum, node) => sum + node.height, 0) / nodes.length + const widths = nodes.map((node) => node.width) + const heights = nodes.map((node) => node.height) + const widthRange = Math.max(...widths) - Math.min(...widths) + const heightRange = Math.max(...heights) - Math.min(...heights) + const confidence = calcClusterConfidence(nodes) return { signature, diff --git a/packages/core/src/tools/create/svg.ts b/packages/core/src/tools/create/svg.ts index 8fd4dd45c..0cc5f71ff 100644 --- a/packages/core/src/tools/create/svg.ts +++ b/packages/core/src/tools/create/svg.ts @@ -4,7 +4,6 @@ import { extractPaths } from '#core/icons/svg' import type { IconPathInfo } from '#core/icons/types' import { parseSVGPath } from '#core/io/formats/svg/parse-path' import { defineTool } from '#core/tools/schema' - import type { Rect } from '#core/types' function parseSvgViewBox(svg: string): Rect | null { diff --git a/packages/core/src/vector/centerline.ts b/packages/core/src/vector/centerline.ts index 02951f3e2..2aa14d135 100644 --- a/packages/core/src/vector/centerline.ts +++ b/packages/core/src/vector/centerline.ts @@ -1,10 +1,10 @@ import type { CanvasKit, Path } from 'canvaskit-wasm' -import { addOpenSegmentsToPath } from './path-helpers' - import type { VectorNetwork } from '#core/scene-graph' import type { Vector } from '#core/types' +import { addOpenSegmentsToPath } from './path-helpers' + export function fitCircleArc( pts: Vector[] ): { cx: number; cy: number; r: number; startAngleDeg: number; sweepDeg: number } | null { diff --git a/packages/core/src/vector/index.ts b/packages/core/src/vector/index.ts index f0ee290c0..b286fc7d5 100644 --- a/packages/core/src/vector/index.ts +++ b/packages/core/src/vector/index.ts @@ -12,8 +12,6 @@ export { import type { CanvasKit, Path } from 'canvaskit-wasm' -import { addOpenSegmentsToPath, addSegmentDirected } from './path-helpers' - import type { HandleMirroring, VectorNetwork, @@ -22,6 +20,8 @@ import type { VectorVertex, WindingRule } from '#core/scene-graph' + +import { addOpenSegmentsToPath, addSegmentDirected } from './path-helpers' export { vectorNetworkToCenterlinePath, fitCircleArc, isClosedThinCrescent } from './centerline' // --- vectorNetworkBlob binary format --- diff --git a/packages/mcp/src/index.ts b/packages/mcp/src/index.ts index d30de022c..2edc7bf84 100644 --- a/packages/mcp/src/index.ts +++ b/packages/mcp/src/index.ts @@ -4,7 +4,9 @@ import { serve } from '@hono/node-server' import { startServer } from './server.js' if (process.argv.includes('--help') || process.argv.includes('-h')) { - process.stdout.write(`openpencil-mcp-http\n\nStart the OpenPencil MCP HTTP and WebSocket server.\n\nOptions:\n --help, -h Show this help message\n`) + process.stdout.write( + `openpencil-mcp-http\n\nStart the OpenPencil MCP HTTP and WebSocket server.\n\nOptions:\n --help, -h Show this help message\n` + ) process.exit(0) } diff --git a/packages/mcp/src/stdio.ts b/packages/mcp/src/stdio.ts index 6ef903b1d..186fbaa1f 100644 --- a/packages/mcp/src/stdio.ts +++ b/packages/mcp/src/stdio.ts @@ -6,7 +6,9 @@ import { MCP_VERSION, registerTools } from './server.js' import { createStdioRpcBridge } from './stdio-bridge.js' if (process.argv.includes('--help') || process.argv.includes('-h')) { - process.stdout.write(`openpencil-mcp\n\nStart the OpenPencil MCP stdio bridge.\n\nOptions:\n --help, -h Show this help message\n`) + process.stdout.write( + `openpencil-mcp\n\nStart the OpenPencil MCP stdio bridge.\n\nOptions:\n --help, -h Show this help message\n` + ) process.exit(0) } diff --git a/packages/mcp/src/tool/registration.ts b/packages/mcp/src/tool/registration.ts index 149b7f88d..d82083af8 100644 --- a/packages/mcp/src/tool/registration.ts +++ b/packages/mcp/src/tool/registration.ts @@ -69,12 +69,15 @@ export function registerTools(mcpServer: McpServer, options: RegisterToolsOption ? `Save the current document to disk. If path is provided, it must be inside ${resolvedRoot}.` : 'Save the current document to disk. Uses the existing file path if available, otherwise prompts for a location.', inputSchema: resolvedRoot - ? z.object({ path: z.string().describe('Optional absolute path for the .fig file').optional() }) + ? z.object({ + path: z.string().describe('Optional absolute path for the .fig file').optional() + }) : z.object({}) }, async (args: { path?: string }) => { try { - const safePath = args.path && resolvedRoot ? resolveSafePath(args.path, resolvedRoot) : undefined + const safePath = + args.path && resolvedRoot ? resolveSafePath(args.path, resolvedRoot) : undefined const result = await sendRpc({ command: 'save_file', args: { path: safePath } }) const res = result as { ok?: boolean; error?: string } if (res.ok === false) return fail(new Error(res.error)) diff --git a/packages/vue/src/canvas/tool-input/use.ts b/packages/vue/src/canvas/tool-input/use.ts index 815ecdac1..a522ef107 100644 --- a/packages/vue/src/canvas/tool-input/use.ts +++ b/packages/vue/src/canvas/tool-input/use.ts @@ -46,7 +46,18 @@ export function handleToolMouseDown({ } if (tool === 'SELECT') { - handleSelectDown(event, cx, cy, sx, sy, editor, hitFns, tryStartRotation, handleTextEditClick, setDrag) + handleSelectDown( + event, + cx, + cy, + sx, + sy, + editor, + hitFns, + tryStartRotation, + handleTextEditClick, + setDrag + ) return } diff --git a/packages/vue/src/canvas/useCanvasInput.ts b/packages/vue/src/canvas/useCanvasInput.ts index 32ec36164..98e665de7 100644 --- a/packages/vue/src/canvas/useCanvasInput.ts +++ b/packages/vue/src/canvas/useCanvasInput.ts @@ -14,8 +14,8 @@ import { createCanvasPointer } from '#vue/canvas/pointer/use' import { createTextEditInput } from '#vue/canvas/text-edit/input' import { handleToolMouseDown } from '#vue/canvas/tool-input/use' import { createCanvasTransformInput } from '#vue/canvas/transform-input/use' -import { createClickCounter } from '#vue/shared/input/click-count' import { resolveAutoLayoutHover } from '#vue/shared/input/auto-layout-hover' +import { createClickCounter } from '#vue/shared/input/click-count' import { handleDrawMove, handleDrawUp } from '#vue/shared/input/draw' import { handleMoveMove, handleMoveUp } from '#vue/shared/input/move' import { handleNodeEditMove } from '#vue/shared/input/node-edit' diff --git a/packages/vue/src/controls/layout/helpers.ts b/packages/vue/src/controls/layout/helpers.ts index 0ae268732..40f5166fa 100644 --- a/packages/vue/src/controls/layout/helpers.ts +++ b/packages/vue/src/controls/layout/helpers.ts @@ -252,7 +252,8 @@ export function createLayoutActions({ updateProp('primaryAxisSizing', 'HUG') } else { if (n.primaryAxisSizing === 'HUG') updateProp('primaryAxisSizing', 'FIXED') - if (isInAutoLayout.value) updateProp('layoutAlignSelf', sizing === 'FILL' ? 'STRETCH' : 'AUTO') + if (isInAutoLayout.value) + updateProp('layoutAlignSelf', sizing === 'FILL' ? 'STRETCH' : 'AUTO') } } diff --git a/packages/vue/src/controls/okhcl/helpers.ts b/packages/vue/src/controls/okhcl/helpers.ts index 52d0bb6ad..761875b58 100644 --- a/packages/vue/src/controls/okhcl/helpers.ts +++ b/packages/vue/src/controls/okhcl/helpers.ts @@ -1,6 +1,5 @@ import type { Ref } from 'vue' -import { BLACK } from '@open-pencil/core/constants' import { getFillOkHCL, getStrokeOkHCL, @@ -10,6 +9,7 @@ import { setNodeStrokeOkHCL } from '@open-pencil/core/color' import type { OkHCLColor } from '@open-pencil/core/color' +import { BLACK } from '@open-pencil/core/constants' import type { Editor } from '@open-pencil/core/editor' import type { SceneNode } from '@open-pencil/core/scene-graph' @@ -30,17 +30,11 @@ export function getStrokeOkHCLColor(node: SceneNode | null, index: number): OkHC } function fallbackFillOkHCL(node: SceneNode, index: number) { - return ( - getFillOkHCLColor(node, index) ?? - rgbaToOkHCL(node.fills[index]?.color ?? BLACK) - ) + return getFillOkHCLColor(node, index) ?? rgbaToOkHCL(node.fills[index]?.color ?? BLACK) } function fallbackStrokeOkHCL(node: SceneNode, index: number) { - return ( - getStrokeOkHCLColor(node, index) ?? - rgbaToOkHCL(node.strokes[index]?.color ?? BLACK) - ) + return getStrokeOkHCLColor(node, index) ?? rgbaToOkHCL(node.strokes[index]?.color ?? BLACK) } export function createOkHCLActions(editor: Editor) { diff --git a/packages/vue/src/shared/input/auto-layout-hover.ts b/packages/vue/src/shared/input/auto-layout-hover.ts index 381196ddf..d0c4c0606 100644 --- a/packages/vue/src/shared/input/auto-layout-hover.ts +++ b/packages/vue/src/shared/input/auto-layout-hover.ts @@ -21,7 +21,11 @@ function isNear(value: number, target: number, tolerance = AUTO_LAYOUT_HOVER_TIC return Math.abs(value - target) <= tolerance } -function resolvePaddingHover(node: SceneNode, localX: number, localY: number): AutoLayoutHover | null { +function resolvePaddingHover( + node: SceneNode, + localX: number, + localY: number +): AutoLayoutHover | null { const centerX = node.width / 2 const centerY = node.height / 2 @@ -40,7 +44,10 @@ function resolvePaddingHover(node: SceneNode, localX: number, localY: number): A if (isNear(localX, centerX) && isNear(localY, tickY)) { return { nodeId: node.id, kind: 'padding-value', side: 'bottom' } } - if (localY >= node.height - Math.min(node.paddingBottom, AUTO_LAYOUT_HOVER_PADDING_REGION_TOLERANCE)) { + if ( + localY >= + node.height - Math.min(node.paddingBottom, AUTO_LAYOUT_HOVER_PADDING_REGION_TOLERANCE) + ) { return { nodeId: node.id, kind: 'padding', side: 'bottom' } } } @@ -60,7 +67,10 @@ function resolvePaddingHover(node: SceneNode, localX: number, localY: number): A if (isNear(localX, tickX) && isNear(localY, centerY)) { return { nodeId: node.id, kind: 'padding-value', side: 'right' } } - if (localX >= node.width - Math.min(node.paddingRight, AUTO_LAYOUT_HOVER_PADDING_REGION_TOLERANCE)) { + if ( + localX >= + node.width - Math.min(node.paddingRight, AUTO_LAYOUT_HOVER_PADDING_REGION_TOLERANCE) + ) { return { nodeId: node.id, kind: 'padding', side: 'right' } } } @@ -124,7 +134,11 @@ function resolveChildrenHover( return null } -export function resolveAutoLayoutHover(cx: number, cy: number, editor: Editor): AutoLayoutHover | null { +export function resolveAutoLayoutHover( + cx: number, + cy: number, + editor: Editor +): AutoLayoutHover | null { if (editor.state.selectedIds.size !== 1) return null const nodeId = [...editor.state.selectedIds][0] const node = editor.graph.getNode(nodeId) @@ -139,7 +153,6 @@ export function resolveAutoLayoutHover(cx: number, cy: number, editor: Editor): return ( resolveSpacingHover(node, children, localX, localY) ?? resolvePaddingHover(node, localX, localY) ?? - resolveChildrenHover(node, children, localX, localY) ?? - { nodeId: node.id, kind: 'frame' } + resolveChildrenHover(node, children, localX, localY) ?? { nodeId: node.id, kind: 'frame' } ) } diff --git a/packages/vue/src/shared/input/select/move.ts b/packages/vue/src/shared/input/select/move.ts index 4e932e5bd..f6e274a16 100644 --- a/packages/vue/src/shared/input/select/move.ts +++ b/packages/vue/src/shared/input/select/move.ts @@ -64,7 +64,8 @@ export function createSelectionMoveDrag( editor: Editor, duplicate: boolean ): DragState { - if (duplicate && editor.state.selectedIds.size > 0) return duplicateAndDrag(cx, cy, sx, sy, editor).drag + if (duplicate && editor.state.selectedIds.size > 0) + return duplicateAndDrag(cx, cy, sx, sy, editor).drag const originals = collectMoveOriginals(editor) diff --git a/packages/vue/src/variables/helpers.ts b/packages/vue/src/variables/helpers.ts index ad16bb676..6b51061be 100644 --- a/packages/vue/src/variables/helpers.ts +++ b/packages/vue/src/variables/helpers.ts @@ -1,7 +1,7 @@ import type { Ref } from 'vue' -import { BLACK } from '@open-pencil/core/constants' import { colorToHexRaw, parseColor } from '@open-pencil/core/color' +import { BLACK } from '@open-pencil/core/constants' import type { Editor } from '@open-pencil/core/editor' import { randomHex } from '@open-pencil/core/random' import type { diff --git a/scripts/check-package-metadata.ts b/scripts/check-package-metadata.ts index fc06abbbf..068f52751 100644 --- a/scripts/check-package-metadata.ts +++ b/scripts/check-package-metadata.ts @@ -31,7 +31,8 @@ function checkRuntimePath(packageName: string, field: string, value: string): vo function walkExports(packageName: string, value: unknown, path: string[] = []): void { if (typeof value === 'string') { const key = path.at(-1) - if (key !== 'types' && key !== 'bun') checkRuntimePath(packageName, `exports.${path.join('.')}`, value) + if (key !== 'types' && key !== 'bun') + checkRuntimePath(packageName, `exports.${path.join('.')}`, value) return } if (!value || typeof value !== 'object') return @@ -59,7 +60,10 @@ for (const packageDir of publicPackages) { walkExports(pkg.name, pkg.exports) - if (pkg.publishConfig && ('exports' in pkg.publishConfig || 'main' in pkg.publishConfig || 'types' in pkg.publishConfig)) { + if ( + pkg.publishConfig && + ('exports' in pkg.publishConfig || 'main' in pkg.publishConfig || 'types' in pkg.publishConfig) + ) { errors.push(`${pkg.name}: publishConfig must not rewrite runtime entrypoints`) } } diff --git a/scripts/smoke-packages.ts b/scripts/smoke-packages.ts index 895bac93e..a8b72af2d 100644 --- a/scripts/smoke-packages.ts +++ b/scripts/smoke-packages.ts @@ -30,7 +30,10 @@ try { const tarballs: string[] = [] for (const packageDir of packageDirs) { - const output = run(['bun', 'pm', 'pack', '--destination', tempDir, '--quiet'], join(rootDir, packageDir)) + const output = run( + ['bun', 'pm', 'pack', '--destination', tempDir, '--quiet'], + join(rootDir, packageDir) + ) const filename = output .split('\n') .map((line) => line.trim()) diff --git a/src/app/ai/tools/index.ts b/src/app/ai/tools/index.ts index 1cc049902..5e1cbce59 100644 --- a/src/app/ai/tools/index.ts +++ b/src/app/ai/tools/index.ts @@ -9,8 +9,8 @@ import type { StepBudget, ToolLogEntry } from '@open-pencil/core/tools' import { makeFigmaFromStore } from '@/app/automation/bridge/figma-factory' import { getActiveEditorStore } from '@/app/editor/active-store' -import { ensureGraphFonts } from '@/app/editor/fonts' import type { EditorStore } from '@/app/editor/active-store' +import { ensureGraphFonts } from '@/app/editor/fonts' export const MAX_AGENT_STEPS = 50 diff --git a/src/app/demo/sections/effects.ts b/src/app/demo/sections/effects.ts index 7d29fee3f..4de4e820c 100644 --- a/src/app/demo/sections/effects.ts +++ b/src/app/demo/sections/effects.ts @@ -413,7 +413,9 @@ export function createEffectsSection(store: EditorStore) { cornerRadius: 22, cornerSmoothing: 0.85, fills: [solid({ r: 0.58, g: 0.27, b: 0.95, a: 0.7 })], - effects: [{ ...dropShadow(0, 0, 28, 0, { r: 0.56, g: 0.33, b: 1, a: 0.72 }), blendMode: 'SCREEN' }] + effects: [ + { ...dropShadow(0, 0, 28, 0, { r: 0.56, g: 0.33, b: 1, a: 0.72 }), blendMode: 'SCREEN' } + ] }) const effectBlendText = store.createShape('TEXT', 184, 34, 88, 34, effectBlendCard) graph.updateNode(effectBlendText, { diff --git a/src/app/editor/canvas/menu/context.ts b/src/app/editor/canvas/menu/context.ts index c67c7281f..1333e68ac 100644 --- a/src/app/editor/canvas/menu/context.ts +++ b/src/app/editor/canvas/menu/context.ts @@ -43,7 +43,9 @@ function copyAction( ): () => void { switch (id) { case 'copy-as-text': - return runAsync(() => actions.clipboardWrite(editor.copySelectionAsText(actions.ids()), 'text')) + return runAsync(() => + actions.clipboardWrite(editor.copySelectionAsText(actions.ids()), 'text') + ) case 'copy-as-svg': return runAsync(() => actions.clipboardWrite(editor.copySelectionAsSVG(actions.ids()), 'SVG')) case 'copy-as-png': diff --git a/src/app/shell/keyboard/registry.ts b/src/app/shell/keyboard/registry.ts index d9abc3d02..7dbb0e612 100644 --- a/src/app/shell/keyboard/registry.ts +++ b/src/app/shell/keyboard/registry.ts @@ -6,13 +6,13 @@ import { editorCommandMetadata } from '@open-pencil/vue' import type { EditorCommandId } from '@open-pencil/vue' import { TOOL_SHORTCUTS } from '@/app/editor/session' -import { appMenuTinykeysShortcut } from '@/app/shell/menu/shortcut' import { isEditing } from '@/app/shell/keyboard/focus' import { bindSpaceHandTool } from '@/app/shell/keyboard/space-tool' import type { KeyboardShortcutOptions, KeyboardShortcutRunOptions } from '@/app/shell/keyboard/types' +import { appMenuTinykeysShortcut } from '@/app/shell/menu/shortcut' type ShortcutAction = (options: KeyboardShortcutRunOptions) => void diff --git a/src/app/shell/menu/editor-actions.ts b/src/app/shell/menu/editor-actions.ts index 0994b417f..7e5f4bc08 100644 --- a/src/app/shell/menu/editor-actions.ts +++ b/src/app/shell/menu/editor-actions.ts @@ -10,7 +10,10 @@ type TextFormatUpdates = { const store = useEditorStore() -export function alignSelected(axis: 'horizontal' | 'vertical', align: 'min' | 'center' | 'max'): void { +export function alignSelected( + axis: 'horizontal' | 'vertical', + align: 'min' | 'center' | 'max' +): void { store.alignNodes([...store.state.selectedIds], axis, align) } @@ -41,7 +44,9 @@ export function toggleSelectedTextUnderline(): void { }) } -export function createSharedEditorMenuActions(setTheme: (theme: 'light' | 'dark' | 'auto') => void) { +export function createSharedEditorMenuActions( + setTheme: (theme: 'light' | 'dark' | 'auto') => void +) { return { 'zoom-in': () => store.applyZoom(-100, window.innerWidth / 2, window.innerHeight / 2), 'zoom-out': () => store.applyZoom(100, window.innerWidth / 2, window.innerHeight / 2), diff --git a/src/app/shell/menu/shortcut.ts b/src/app/shell/menu/shortcut.ts index a99790df6..c0097085c 100644 --- a/src/app/shell/menu/shortcut.ts +++ b/src/app/shell/menu/shortcut.ts @@ -10,7 +10,11 @@ function isActionItem(entry: AppMenuEntry): entry is AppMenuActionItem { function findShortcutInEntries(entries: readonly AppMenuEntry[], id: string): string | undefined { for (const entry of entries) { if (!isActionItem(entry)) continue - if (entry.id === id) return entry.shortcut ?? (entry.command ? editorCommandMetadata(entry.command).shortcut : undefined) + if (entry.id === id) + return ( + entry.shortcut ?? + (entry.command ? editorCommandMetadata(entry.command).shortcut : undefined) + ) const shortcut = entry.sub ? findShortcutInEntries(entry.sub, id) : undefined if (shortcut) return shortcut } diff --git a/src/components/ColorPickerPanel/OkhclFields.vue b/src/components/ColorPickerPanel/OkhclFields.vue index a8e55f882..5ed3dfeeb 100644 --- a/src/components/ColorPickerPanel/OkhclFields.vue +++ b/src/components/ColorPickerPanel/OkhclFields.vue @@ -29,7 +29,13 @@ const ctx = useColorPickerPanelContext() :min="0" :max="0.4" :step="0.001" - :display="{ value: toPercent(ctx.okhcl.okhcl.c), min: 0, max: 40, step: 1, parse: fromPercent }" + :display="{ + value: toPercent(ctx.okhcl.okhcl.c), + min: 0, + max: 40, + step: 1, + parse: fromPercent + }" :gradient-style="ctx.okhclSliderGradient?.okhclChroma ?? undefined" :thumb-fill="colorToCSS(ctx.okhclSliderPreview?.okhclChroma ?? ctx.color)" test-id="color-slider-okhcl-c" @@ -42,7 +48,13 @@ const ctx = useColorPickerPanelContext() :min="0" :max="1" :step="0.001" - :display="{ value: toPercent(ctx.okhcl.okhcl.l), min: 0, max: 100, step: 1, parse: fromPercent }" + :display="{ + value: toPercent(ctx.okhcl.okhcl.l), + min: 0, + max: 100, + step: 1, + parse: fromPercent + }" :gradient-style="ctx.okhclSliderGradient?.okhclLightness ?? undefined" :thumb-fill="colorToCSS(ctx.okhclSliderPreview?.okhclLightness ?? ctx.color)" test-id="color-slider-okhcl-l" @@ -55,7 +67,13 @@ const ctx = useColorPickerPanelContext() :min="0" :max="1" :step="0.001" - :display="{ value: toPercent(ctx.okhcl.okhcl.a ?? 1), min: 0, max: 100, step: 1, parse: fromPercent }" + :display="{ + value: toPercent(ctx.okhcl.okhcl.a ?? 1), + min: 0, + max: 100, + step: 1, + parse: fromPercent + }" checkerboard :gradient-style="`background: linear-gradient(to right, transparent, ${colorToCSS(ctx.color)})`" :thumb-fill="colorToCSS(ctx.color)" diff --git a/src/components/LayerTree.vue b/src/components/LayerTree.vue index 67eb46090..67a100234 100644 --- a/src/components/LayerTree.vue +++ b/src/components/LayerTree.vue @@ -3,12 +3,7 @@ import { useAttrs, watch } from 'vue' import { templateRef } from '@vueuse/core' import { TreeItem, ContextMenuRoot, ContextMenuTrigger, ContextMenuPortal } from 'reka-ui' -import { - LayerTreeRoot, - LayerTreeItem, - useI18n, - useInlineRename -} from '@open-pencil/vue' +import { LayerTreeRoot, LayerTreeItem, useI18n, useInlineRename } from '@open-pencil/vue' import { useEditorStore } from '@/app/editor/active-store' import { nodeIcon, COMPONENT_TYPES } from '@/app/editor/icons' import CanvasMenu from './CanvasMenu.vue' @@ -53,165 +48,165 @@ function onTreeSelect(e: CustomEvent, select: (additive: boolean) => void) {
- + + + + +
diff --git a/src/components/PagesPanel.vue b/src/components/PagesPanel.vue index 0a63aa795..a9e0469c0 100644 --- a/src/components/PagesPanel.vue +++ b/src/components/PagesPanel.vue @@ -53,45 +53,48 @@ function handlePageDblClick(
-
-
-
- - +
+
+
+ + +
+
+
+
+
-
-
-
- -
diff --git a/src/components/Toolbar/ToolButton.vue b/src/components/Toolbar/ToolButton.vue index 055b52979..78351c627 100644 --- a/src/components/Toolbar/ToolButton.vue +++ b/src/components/Toolbar/ToolButton.vue @@ -8,12 +8,7 @@ interface ToolButtonProps extends RequiredTestIdProps { mobile?: boolean } -const { - icon, - active = false, - mobile = false, - testId -} = defineProps() +const { icon, active = false, mobile = false, testId } = defineProps() const emit = defineEmits<{ click: [] diff --git a/src/components/ui/AppSelect.vue b/src/components/ui/AppSelect.vue index 9af13efc8..6515c9fd7 100644 --- a/src/components/ui/AppSelect.vue +++ b/src/components/ui/AppSelect.vue @@ -27,12 +27,7 @@ interface AppSelectProps extends TestIdProps { ui?: AppSelectUi } -const { - options, - placeholder, - ui, - testId = 'app-select-trigger' -} = defineProps>() +const { options, placeholder, ui, testId = 'app-select-trigger' } = defineProps>() const modelValue = defineModel({ required: true }) diff --git a/tests/e2e/clipboard/copy-paste.spec.ts b/tests/e2e/clipboard/copy-paste.spec.ts index a86b44bbb..104c50222 100644 --- a/tests/e2e/clipboard/copy-paste.spec.ts +++ b/tests/e2e/clipboard/copy-paste.spec.ts @@ -1,5 +1,4 @@ import { expect, test, useEditorSetup } from '#tests/e2e/fixtures' - import { getSelectedNodes } from '#tests/helpers/store' const editor = useEditorSetup() @@ -20,7 +19,6 @@ function getSelectedCount() { }) } - test('copy + paste via store duplicates a shape', async () => { await editor.canvas.drawRect(100, 100, 120, 80) await editor.canvas.waitForRender() diff --git a/tests/e2e/code/panel.spec.ts b/tests/e2e/code/panel.spec.ts index cbe4cb6a5..d4ff758ce 100644 --- a/tests/e2e/code/panel.spec.ts +++ b/tests/e2e/code/panel.spec.ts @@ -91,5 +91,10 @@ test('selecting a frame shows Frame in JSX', async () => { test('switching back to Design tab works', async () => { await designTab().click() - await expect(editor.page.getByTestId('design-panel-single').or(editor.page.getByTestId('design-panel-empty')).first()).toBeVisible() + await expect( + editor.page + .getByTestId('design-panel-single') + .or(editor.page.getByTestId('design-panel-empty')) + .first() + ).toBeVisible() }) diff --git a/tests/e2e/components/assets-panel.spec.ts b/tests/e2e/components/assets-panel.spec.ts index 3a9569995..f920cced2 100644 --- a/tests/e2e/components/assets-panel.spec.ts +++ b/tests/e2e/components/assets-panel.spec.ts @@ -140,9 +140,7 @@ test('assets panel groups component sets and inserts the default variant', async await expect(details.getByTestId('asset-details-description')).toContainText( 'Reusable button component' ) - await expect(details.getByTestId('asset-details-library')).toContainText( - 'lk-test-library' - ) + await expect(details.getByTestId('asset-details-library')).toContainText('lk-test-library') await expect(details.getByTestId('asset-details-docs')).toBeVisible() await expect(details.getByTestId('asset-details-property')).toContainText('Type') await page.getByTestId('asset-details-close').click() @@ -233,7 +231,9 @@ test('assets insertion accounts for entered container coordinates', async ({ pag const selected = selectedId ? store.graph.getNode(selectedId) : null if (!selected) return null const abs = store.graph.getAbsolutePosition(selected.id) - const center = store.screenToCanvas(...Object.values(store.viewportCanvasCenter()) as [number, number]) + const center = store.screenToCanvas( + ...(Object.values(store.viewportCanvasCenter()) as [number, number]) + ) return { parentId: selected.parentId, centerX: abs.x + selected.width / 2, diff --git a/tests/e2e/context-menu/basic.spec.ts b/tests/e2e/context-menu/basic.spec.ts index 7e8d106dd..3239f6975 100644 --- a/tests/e2e/context-menu/basic.spec.ts +++ b/tests/e2e/context-menu/basic.spec.ts @@ -227,7 +227,7 @@ test('outline stroke is disabled for fill-only shapes', async () => { await editor.page.keyboard.press('Escape') }) - test('Copy/Paste as submenu exists', async () => { +test('Copy/Paste as submenu exists', async () => { await rightClickShape(130, 130) const submenuTrigger = contextItem('context-copy-paste-as') diff --git a/tests/e2e/editor/auto-layout/drag.spec.ts b/tests/e2e/editor/auto-layout/drag.spec.ts index 5d07f14cd..f8951f5e3 100644 --- a/tests/e2e/editor/auto-layout/drag.spec.ts +++ b/tests/e2e/editor/auto-layout/drag.spec.ts @@ -2,7 +2,9 @@ import { expect, test } from '@playwright/test' import { CanvasHelper } from '#tests/helpers/canvas' -test('dragging selected nested instance content reorders its auto-layout item', async ({ page }) => { +test('dragging selected nested instance content reorders its auto-layout item', async ({ + page +}) => { await page.goto('/') const canvas = new CanvasHelper(page) await canvas.waitForInit() @@ -83,7 +85,9 @@ test('dragging selected nested instance content reorders its auto-layout item', canvas.assertNoErrors() }) -test('auto-layout drag does not show an insert indicator before order changes', async ({ page }) => { +test('auto-layout drag does not show an insert indicator before order changes', async ({ + page +}) => { await page.goto('/') const canvas = new CanvasHelper(page) await canvas.waitForInit() diff --git a/tests/e2e/layers/reorder.spec.ts b/tests/e2e/layers/reorder.spec.ts index e1731b350..c4c0c3e4f 100644 --- a/tests/e2e/layers/reorder.spec.ts +++ b/tests/e2e/layers/reorder.spec.ts @@ -75,7 +75,9 @@ test('dragging a layer into a container expands it and shows the child', async ( await source.dragTo(target, { targetPosition: { x: 70, y: 12 } }) await canvas.waitForRender() - await expect(page.locator(`[data-node-id="${ids.rect}"]`).getByTestId('layers-item')).toBeVisible() + await expect( + page.locator(`[data-node-id="${ids.rect}"]`).getByTestId('layers-item') + ).toBeVisible() expect(await layerOrder(page, ids.frame)).toEqual([ids.rect]) canvas.assertNoErrors() }) diff --git a/tests/e2e/properties/corner-stroke-toggles.spec.ts b/tests/e2e/properties/corner-stroke-toggles.spec.ts index 9e4b7a173..d1b7ccf6f 100644 --- a/tests/e2e/properties/corner-stroke-toggles.spec.ts +++ b/tests/e2e/properties/corner-stroke-toggles.spec.ts @@ -78,14 +78,16 @@ test('stroke sides toggle shows per-side weight inputs', async () => { await expect(toggle).toBeVisible({ timeout: 5000 }) const sectionInputsBefore = await page - .getByTestId('stroke-section').getByTestId('scrub-input') + .getByTestId('stroke-section') + .getByTestId('scrub-input') .count() await toggle.click() await canvas.waitForRender() const sectionInputsAfter = await page - .getByTestId('stroke-section').getByTestId('scrub-input') + .getByTestId('stroke-section') + .getByTestId('scrub-input') .count() expect(sectionInputsAfter).toBeGreaterThan(sectionInputsBefore) @@ -93,7 +95,8 @@ test('stroke sides toggle shows per-side weight inputs', async () => { await canvas.waitForRender() const sectionInputsFinal = await page - .getByTestId('stroke-section').getByTestId('scrub-input') + .getByTestId('stroke-section') + .getByTestId('scrub-input') .count() expect(sectionInputsFinal).toBe(sectionInputsBefore) }) diff --git a/tests/e2e/properties/effects.spec.ts b/tests/e2e/properties/effects.spec.ts index ccc8af122..d00e03a25 100644 --- a/tests/e2e/properties/effects.spec.ts +++ b/tests/e2e/properties/effects.spec.ts @@ -148,7 +148,9 @@ test('blend modes', async () => { width: 324, height: 180, cornerRadius: 20, - fills: [{ type: 'SOLID', color: { r: 0.95, g: 0.95, b: 0.97, a: 1 }, visible: true, opacity: 1 }] + fills: [ + { type: 'SOLID', color: { r: 0.95, g: 0.95, b: 0.97, a: 1 }, visible: true, opacity: 1 } + ] }) store.graph.createNode('RECTANGLE', pageId, { name: 'Multiply Base', @@ -157,7 +159,9 @@ test('blend modes', async () => { width: 96, height: 96, cornerRadius: 20, - fills: [{ type: 'SOLID', color: { r: 1, g: 0.23, b: 0.2, a: 0.95 }, visible: true, opacity: 1 }] + fills: [ + { type: 'SOLID', color: { r: 1, g: 0.23, b: 0.2, a: 0.95 }, visible: true, opacity: 1 } + ] }) store.graph.createNode('RECTANGLE', pageId, { name: 'Multiply Layer', @@ -167,7 +171,9 @@ test('blend modes', async () => { height: 96, cornerRadius: 20, blendMode: 'MULTIPLY', - fills: [{ type: 'SOLID', color: { r: 0.12, g: 0.45, b: 1, a: 0.92 }, visible: true, opacity: 1 }] + fills: [ + { type: 'SOLID', color: { r: 0.12, g: 0.45, b: 1, a: 0.92 }, visible: true, opacity: 1 } + ] }) store.graph.createNode('ELLIPSE', pageId, { name: 'Screen Layer', @@ -176,7 +182,9 @@ test('blend modes', async () => { width: 104, height: 104, blendMode: 'SCREEN', - fills: [{ type: 'SOLID', color: { r: 0.05, g: 0.75, b: 0.45, a: 0.8 }, visible: true, opacity: 1 }] + fills: [ + { type: 'SOLID', color: { r: 0.05, g: 0.75, b: 0.45, a: 0.8 }, visible: true, opacity: 1 } + ] }) store.graph.createNode('RECTANGLE', pageId, { name: 'Overlay Multi Fill', @@ -215,7 +223,9 @@ test('alpha mask stack', async () => { width: 324, height: 180, cornerRadius: 20, - fills: [{ type: 'SOLID', color: { r: 0.08, g: 0.1, b: 0.18, a: 1 }, visible: true, opacity: 1 }] + fills: [ + { type: 'SOLID', color: { r: 0.08, g: 0.1, b: 0.18, a: 1 }, visible: true, opacity: 1 } + ] }) store.graph.createNode('RECTANGLE', frame.id, { name: 'Unmasked Baseline', @@ -224,7 +234,9 @@ test('alpha mask stack', async () => { width: 58, height: 132, cornerRadius: 12, - fills: [{ type: 'SOLID', color: { r: 0.98, g: 0.75, b: 0.18, a: 1 }, visible: true, opacity: 1 }] + fills: [ + { type: 'SOLID', color: { r: 0.98, g: 0.75, b: 0.18, a: 1 }, visible: true, opacity: 1 } + ] }) store.graph.createNode('ELLIPSE', frame.id, { name: 'Alpha Mask', @@ -243,7 +255,9 @@ test('alpha mask stack', async () => { width: 220, height: 38, rotation: -12, - fills: [{ type: 'SOLID', color: { r: 0.08, g: 0.73, b: 0.73, a: 1 }, visible: true, opacity: 1 }] + fills: [ + { type: 'SOLID', color: { r: 0.08, g: 0.73, b: 0.73, a: 1 }, visible: true, opacity: 1 } + ] }) store.graph.createNode('RECTANGLE', frame.id, { name: 'Masked Purple Stripe', @@ -252,7 +266,9 @@ test('alpha mask stack', async () => { width: 222, height: 42, rotation: -12, - fills: [{ type: 'SOLID', color: { r: 0.58, g: 0.27, b: 0.95, a: 1 }, visible: true, opacity: 1 }] + fills: [ + { type: 'SOLID', color: { r: 0.58, g: 0.27, b: 0.95, a: 1 }, visible: true, opacity: 1 } + ] }) store.graph.createNode('RECTANGLE', frame.id, { name: 'Masked Pink Stripe', @@ -282,7 +298,9 @@ test('smoothed corners with blended shadow', async () => { width: 272, height: 168, cornerRadius: 20, - fills: [{ type: 'SOLID', color: { r: 0.08, g: 0.1, b: 0.18, a: 1 }, visible: true, opacity: 1 }] + fills: [ + { type: 'SOLID', color: { r: 0.08, g: 0.1, b: 0.18, a: 1 }, visible: true, opacity: 1 } + ] }) store.graph.createNode('RECTANGLE', pageId, { name: 'Uniform Smooth Radius', @@ -292,7 +310,9 @@ test('smoothed corners with blended shadow', async () => { height: 88, cornerRadius: 28, cornerSmoothing: 0.75, - fills: [{ type: 'SOLID', color: { r: 0.58, g: 0.27, b: 0.95, a: 1 }, visible: true, opacity: 1 }], + fills: [ + { type: 'SOLID', color: { r: 0.58, g: 0.27, b: 0.95, a: 1 }, visible: true, opacity: 1 } + ], effects: [ { type: 'DROP_SHADOW', @@ -317,7 +337,9 @@ test('smoothed corners with blended shadow', async () => { bottomRightRadius: 34, bottomLeftRadius: 10, cornerSmoothing: 1, - fills: [{ type: 'SOLID', color: { r: 0.08, g: 0.73, b: 0.73, a: 1 }, visible: true, opacity: 1 }], + fills: [ + { type: 'SOLID', color: { r: 0.08, g: 0.73, b: 0.73, a: 1 }, visible: true, opacity: 1 } + ], strokes: [ { color: { r: 1, g: 1, b: 1, a: 0.8 }, diff --git a/tests/e2e/properties/panel.spec.ts b/tests/e2e/properties/panel.spec.ts index e57bef461..52e4615ec 100644 --- a/tests/e2e/properties/panel.spec.ts +++ b/tests/e2e/properties/panel.spec.ts @@ -1,5 +1,4 @@ import { expect, test, useEditorSetup } from '#tests/e2e/fixtures' - import { expectDefined } from '#tests/helpers/assert' import { getPageChildren, getSelectedNode } from '#tests/helpers/store' @@ -11,9 +10,7 @@ test('ScrubInput drag changes X position', async () => { const before = await getSelectedNode(editor.page) const initialX = expectDefined(before, 'selected rectangle before drag').x - const xScrub = editor.page - .getByTestId('position-section').getByTestId('scrub-input') - .first() + const xScrub = editor.page.getByTestId('position-section').getByTestId('scrub-input').first() await editor.canvas.dragScrubInput(xScrub, 50) const after = await getSelectedNode(editor.page) diff --git a/tests/e2e/properties/visibility.spec.ts b/tests/e2e/properties/visibility.spec.ts index 66bde4d73..301eab73f 100644 --- a/tests/e2e/properties/visibility.spec.ts +++ b/tests/e2e/properties/visibility.spec.ts @@ -1,32 +1,40 @@ import { expect, test, useEditorSetup } from '#tests/e2e/fixtures' - import { expectDefined } from '#tests/helpers/assert' import { getSelectedNode } from '#tests/helpers/store' const editor = useEditorSetup() - test('fill visibility supports repeat click and undo redo', async () => { await editor.canvas.drawRect(120, 120, 120, 80) await editor.canvas.waitForRender() const fillButton = editor.page.getByTestId('fill-visibility-0') await expect(fillButton).toBeVisible() - expect(expectDefined(await getSelectedNode(editor.page), 'selected node').fills[0]?.visible).toBe(true) + expect(expectDefined(await getSelectedNode(editor.page), 'selected node').fills[0]?.visible).toBe( + true + ) await fillButton.click() await editor.canvas.waitForRender() - expect(expectDefined(await getSelectedNode(editor.page), 'selected node').fills[0]?.visible).toBe(false) + expect(expectDefined(await getSelectedNode(editor.page), 'selected node').fills[0]?.visible).toBe( + false + ) await fillButton.click() await editor.canvas.waitForRender() - expect(expectDefined(await getSelectedNode(editor.page), 'selected node').fills[0]?.visible).toBe(true) + expect(expectDefined(await getSelectedNode(editor.page), 'selected node').fills[0]?.visible).toBe( + true + ) await editor.canvas.undo() - expect(expectDefined(await getSelectedNode(editor.page), 'selected node').fills[0]?.visible).toBe(false) + expect(expectDefined(await getSelectedNode(editor.page), 'selected node').fills[0]?.visible).toBe( + false + ) await editor.canvas.redo() - expect(expectDefined(await getSelectedNode(editor.page), 'selected node').fills[0]?.visible).toBe(true) + expect(expectDefined(await getSelectedNode(editor.page), 'selected node').fills[0]?.visible).toBe( + true + ) }) test('stroke visibility supports repeat click and undo redo', async () => { @@ -35,21 +43,31 @@ test('stroke visibility supports repeat click and undo redo', async () => { const strokeButton = editor.page.getByTestId('stroke-visibility-0') await expect(strokeButton).toBeVisible() - expect(expectDefined(await getSelectedNode(editor.page), 'selected node').strokes[0]?.visible).toBe(true) + expect( + expectDefined(await getSelectedNode(editor.page), 'selected node').strokes[0]?.visible + ).toBe(true) await strokeButton.click() await editor.canvas.waitForRender() - expect(expectDefined(await getSelectedNode(editor.page), 'selected node').strokes[0]?.visible).toBe(false) + expect( + expectDefined(await getSelectedNode(editor.page), 'selected node').strokes[0]?.visible + ).toBe(false) await strokeButton.click() await editor.canvas.waitForRender() - expect(expectDefined(await getSelectedNode(editor.page), 'selected node').strokes[0]?.visible).toBe(true) + expect( + expectDefined(await getSelectedNode(editor.page), 'selected node').strokes[0]?.visible + ).toBe(true) await editor.canvas.undo() - expect(expectDefined(await getSelectedNode(editor.page), 'selected node').strokes[0]?.visible).toBe(false) + expect( + expectDefined(await getSelectedNode(editor.page), 'selected node').strokes[0]?.visible + ).toBe(false) await editor.canvas.redo() - expect(expectDefined(await getSelectedNode(editor.page), 'selected node').strokes[0]?.visible).toBe(true) + expect( + expectDefined(await getSelectedNode(editor.page), 'selected node').strokes[0]?.visible + ).toBe(true) }) test('appearance visibility supports repeat click and undo redo in one step', async () => { diff --git a/tests/e2e/text/editing.spec.ts b/tests/e2e/text/editing.spec.ts index 15c137e44..5ce6575f9 100644 --- a/tests/e2e/text/editing.spec.ts +++ b/tests/e2e/text/editing.spec.ts @@ -1,11 +1,9 @@ import { expect, test, useEditorSetup } from '#tests/e2e/fixtures' - import { expectDefined } from '#tests/helpers/assert' import { getSelectedNode } from '#tests/helpers/store' const editor = useEditorSetup() - function getPageChildren() { return editor.page.evaluate(() => { const store = window.openPencil?.getStore?.() diff --git a/tests/e2e/toolbar/basic.spec.ts b/tests/e2e/toolbar/basic.spec.ts index 27c0fd874..4f6f0619d 100644 --- a/tests/e2e/toolbar/basic.spec.ts +++ b/tests/e2e/toolbar/basic.spec.ts @@ -1,5 +1,4 @@ import { expect, test, useEditorSetup } from '#tests/e2e/fixtures' - import { getPageChildren } from '#tests/helpers/store' import { toolbarFlyoutItemTestId, toolbarFlyoutTestId } from '#tests/helpers/test-ids' diff --git a/tests/e2e/variables/dialog.spec.ts b/tests/e2e/variables/dialog.spec.ts index 8f0a14250..5f3dd0ae1 100644 --- a/tests/e2e/variables/dialog.spec.ts +++ b/tests/e2e/variables/dialog.spec.ts @@ -1,5 +1,4 @@ import { expect, test, useEditorSetup } from '#tests/e2e/fixtures' - import { variablesAddTestId } from '#tests/helpers/test-ids' const editor = useEditorSetup() @@ -57,9 +56,9 @@ test('add variable menu creates non-color variable types', async () => { await editor.page.getByTestId('variables-add-variable').click() await editor.page.getByTestId(variablesAddTestId('STRING')).click() - await expect( - editor.page.getByTestId('variable-row').filter({ hasText: 'New text' }) - ).toHaveCount(1) + await expect(editor.page.getByTestId('variable-row').filter({ hasText: 'New text' })).toHaveCount( + 1 + ) await editor.page.getByTestId('variables-add-variable').click() await editor.page.getByTestId(variablesAddTestId('BOOLEAN')).click() @@ -91,10 +90,7 @@ test('color swatch opens color picker', async () => { await editor.page.getByTestId('variables-section-open').click() await expect(editor.page.getByTestId('variables-dialog')).toBeVisible({ timeout: 3000 }) - const swatch = editor.page - .getByTestId('variable-row') - .first() - .getByTestId('color-picker-swatch') + const swatch = editor.page.getByTestId('variable-row').first().getByTestId('color-picker-swatch') await expect(swatch).toBeVisible({ timeout: 3000 }) await swatch.click() await expect(editor.page.getByTestId('color-picker-popover')).toBeVisible({ timeout: 5000 }) diff --git a/tests/engine/app/shell/menu/schema.test.ts b/tests/engine/app/shell/menu/schema.test.ts index bcb9f612c..88832cc19 100644 --- a/tests/engine/app/shell/menu/schema.test.ts +++ b/tests/engine/app/shell/menu/schema.test.ts @@ -16,7 +16,9 @@ function actionItems(entries: readonly AppMenuEntry[]): AppMenuEntry[] { describe('APP_MENU_SCHEMA', () => { test('does not duplicate shortcuts for command-backed entries', () => { const duplicated = APP_MENU_SCHEMA.flatMap((group) => - actionItems(group.items).filter((entry) => !('type' in entry) && entry.command && entry.shortcut) + actionItems(group.items).filter( + (entry) => !('type' in entry) && entry.command && entry.shortcut + ) ) expect(duplicated).toEqual([]) diff --git a/tests/engine/clipboard/figma/html.test.ts b/tests/engine/clipboard/figma/html.test.ts index 44231392c..f8625026d 100644 --- a/tests/engine/clipboard/figma/html.test.ts +++ b/tests/engine/clipboard/figma/html.test.ts @@ -10,7 +10,9 @@ import { import { expectDefined } from '#tests/helpers/assert' -function expectFigmaEditableTextDefaults(textNode: NonNullable>>['nodes'][number]) { +function expectFigmaEditableTextDefaults( + textNode: NonNullable>>['nodes'][number] +) { expect(textNode.textUserLayoutVersion).toBe(5) expect(textNode.textExplicitLayoutVersion).toBe(1) expect(textNode.textBidiVersion).toBe(1) @@ -73,7 +75,9 @@ describe('buildFigmaClipboardHTML', () => { expectFigmaEditableTextDefaults(textNode) expect(textNode.derivedTextData?.glyphs).toBeDefined() expect(textNode.derivedTextData?.baselines?.length).toBeGreaterThan(0) - expect(textNode.derivedTextData?.logicalIndexToCharacterOffsetMap?.length).toBe(text.text.length + 1) + expect(textNode.derivedTextData?.logicalIndexToCharacterOffsetMap?.length).toBe( + text.text.length + 1 + ) expect(textNode.derivedTextData?.derivedLines).toEqual([{ directionality: 'LTR' }]) }) diff --git a/tests/engine/clipboard/openpencil/images.test.ts b/tests/engine/clipboard/openpencil/images.test.ts index 26ccaf0a4..099254ec7 100644 --- a/tests/engine/clipboard/openpencil/images.test.ts +++ b/tests/engine/clipboard/openpencil/images.test.ts @@ -1,11 +1,15 @@ import { describe, expect, test } from 'bun:test' -import { buildOpenPencilClipboardHTML, FigmaAPI, parseOpenPencilClipboard, SceneGraph } from '@open-pencil/core' +import { + buildOpenPencilClipboardHTML, + FigmaAPI, + parseOpenPencilClipboard, + SceneGraph +} from '@open-pencil/core' +import type { SceneNode } from '@open-pencil/core' import { expectDefined } from '#tests/helpers/assert' -import type { SceneNode } from '@open-pencil/core' - describe('clipboard roundtrip with images', () => { function graphWithImageNode(): { graph: SceneGraph @@ -159,4 +163,3 @@ describe('clipboard roundtrip with images', () => { expect(clipboard.images.get(hash)).toEqual(bytes) }) }) - diff --git a/tests/engine/editor/selection/hit-test.test.ts b/tests/engine/editor/selection/hit-test.test.ts index aa2461c41..2e5b50d19 100644 --- a/tests/engine/editor/selection/hit-test.test.ts +++ b/tests/engine/editor/selection/hit-test.test.ts @@ -21,7 +21,10 @@ describe('editor scoped hit testing', () => { }) editor.state.enteredContainerId = frame.id - editor.setCanvasKit({} as Parameters[0], {} as Parameters[1]) + editor.setCanvasKit( + {} as Parameters[0], + {} as Parameters[1] + ) expect(editor.hitTestAtPoint(130, 130)?.id).toBe(child.id) }) diff --git a/tests/engine/editor/structure/boolean.test.ts b/tests/engine/editor/structure/boolean.test.ts index 77f6f50e0..0d460e6ad 100644 --- a/tests/engine/editor/structure/boolean.test.ts +++ b/tests/engine/editor/structure/boolean.test.ts @@ -7,8 +7,18 @@ describe('booleanOperationSelected', () => { test('wraps selected nodes in a boolean operation container', () => { const editor = createEditor() const pageId = editor.state.currentPageId - const first = editor.graph.createNode('RECTANGLE', pageId, { x: 10, y: 20, width: 30, height: 40 }) - const second = editor.graph.createNode('ELLIPSE', pageId, { x: 80, y: 90, width: 20, height: 10 }) + const first = editor.graph.createNode('RECTANGLE', pageId, { + x: 10, + y: 20, + width: 30, + height: 40 + }) + const second = editor.graph.createNode('ELLIPSE', pageId, { + x: 80, + y: 90, + width: 20, + height: 10 + }) editor.select([first.id, second.id]) editor.booleanOperationSelected('UNION') @@ -26,7 +36,10 @@ describe('booleanOperationSelected', () => { test('does not wrap unsupported text nodes', () => { const editor = createEditor() const pageId = editor.state.currentPageId - const first = editor.graph.createNode('TEXT', pageId, { text: 'Nope', fontFamily: 'Definitely Missing Font' }) + const first = editor.graph.createNode('TEXT', pageId, { + text: 'Nope', + fontFamily: 'Definitely Missing Font' + }) const second = editor.graph.createNode('RECTANGLE', pageId) editor.select([first.id, second.id]) @@ -57,7 +70,16 @@ describe('booleanOperationSelected', () => { const editor = createEditor() const pageId = editor.state.currentPageId const first = editor.graph.createNode('RECTANGLE', pageId, { - fills: [{ type: 'IMAGE', imageHash: 'image', imageScaleMode: 'FILL', color: TRANSPARENT, opacity: 1, visible: true }] + fills: [ + { + type: 'IMAGE', + imageHash: 'image', + imageScaleMode: 'FILL', + color: TRANSPARENT, + opacity: 1, + visible: true + } + ] }) const second = editor.graph.createNode('RECTANGLE', pageId) @@ -82,7 +104,12 @@ describe('booleanOperationSelected', () => { expect(editor.graph.getNode(pageId)?.childIds).toEqual([before.id, booleanId, after.id]) editor.undo.undo() - expect(editor.graph.getNode(pageId)?.childIds).toEqual([before.id, first.id, second.id, after.id]) + expect(editor.graph.getNode(pageId)?.childIds).toEqual([ + before.id, + first.id, + second.id, + after.id + ]) expect(editor.state.selectedIds).toEqual(new Set([first.id, second.id])) editor.undo.redo() diff --git a/tests/engine/editor/structure/frame-selection.test.ts b/tests/engine/editor/structure/frame-selection.test.ts index e681be7cd..fac6b1c5c 100644 --- a/tests/engine/editor/structure/frame-selection.test.ts +++ b/tests/engine/editor/structure/frame-selection.test.ts @@ -45,8 +45,18 @@ describe('frameSelection', () => { test('undo and redo restore frame selection state', () => { const editor = createEditor() const pageId = editor.state.currentPageId - const first = editor.graph.createNode('RECTANGLE', pageId, { x: 20, y: 30, width: 40, height: 50 }) - const second = editor.graph.createNode('ELLIPSE', pageId, { x: 100, y: 120, width: 30, height: 20 }) + const first = editor.graph.createNode('RECTANGLE', pageId, { + x: 20, + y: 30, + width: 40, + height: 50 + }) + const second = editor.graph.createNode('ELLIPSE', pageId, { + x: 100, + y: 120, + width: 30, + height: 20 + }) editor.select([first.id, second.id]) editor.frameSelection() diff --git a/tests/engine/figma/api/images.test.ts b/tests/engine/figma/api/images.test.ts index 8fa84f74b..595589e16 100644 --- a/tests/engine/figma/api/images.test.ts +++ b/tests/engine/figma/api/images.test.ts @@ -46,4 +46,3 @@ describe('FigmaAPI.createImage', () => { expect(hash).toMatch(/^[0-9a-f]{40}$/) }) }) - diff --git a/tests/engine/geometry/angles.test.ts b/tests/engine/geometry/angles.test.ts index 519028563..250a8b7b2 100644 --- a/tests/engine/geometry/angles.test.ts +++ b/tests/engine/geometry/angles.test.ts @@ -21,4 +21,3 @@ describe('degToRad / radToDeg', () => { expect(radToDeg(degToRad(-90))).toBeCloseTo(-90, 10) }) }) - diff --git a/tests/engine/geometry/bounds.test.ts b/tests/engine/geometry/bounds.test.ts index 6c77857b4..200a1f2a3 100644 --- a/tests/engine/geometry/bounds.test.ts +++ b/tests/engine/geometry/bounds.test.ts @@ -59,4 +59,3 @@ describe('computeAbsoluteBounds', () => { expect(result).toEqual({ x: 10, y: 20, width: 60, height: 60 }) }) }) - diff --git a/tests/engine/geometry/rotation.test.ts b/tests/engine/geometry/rotation.test.ts index ef400f61f..8899275b9 100644 --- a/tests/engine/geometry/rotation.test.ts +++ b/tests/engine/geometry/rotation.test.ts @@ -92,4 +92,3 @@ describe('rotatedBBox', () => { expect(pos.bottom - pos.top).toBeCloseTo(neg.bottom - neg.top, 5) }) }) - diff --git a/tests/engine/io/fig/export/text.test.ts b/tests/engine/io/fig/export/text.test.ts index 808713888..b7f2ad519 100644 --- a/tests/engine/io/fig/export/text.test.ts +++ b/tests/engine/io/fig/export/text.test.ts @@ -62,7 +62,8 @@ describe('text node export', () => { await initCodec() const { unzipSync, inflateSync } = await import('fflate') - const { decodeBinarySchema, compileSchema, ByteBuffer } = await import('#core/kiwi/schema-runtime') + const { decodeBinarySchema, compileSchema, ByteBuffer } = + await import('#core/kiwi/schema-runtime') const { parseFigKiwiChunks } = await import('@open-pencil/core') const graph = new SceneGraph() @@ -123,7 +124,8 @@ describe('text node export', () => { await initCodec() const { unzipSync, inflateSync } = await import('fflate') - const { decodeBinarySchema, compileSchema, ByteBuffer } = await import('#core/kiwi/schema-runtime') + const { decodeBinarySchema, compileSchema, ByteBuffer } = + await import('#core/kiwi/schema-runtime') const { parseFigKiwiChunks } = await import('@open-pencil/core') const graph = new SceneGraph() @@ -148,7 +150,10 @@ describe('text node export', () => { } const message = compiled.decodeMessage(inflateSync(chunks?.[1] ?? new Uint8Array())) const nodeChanges = message.nodeChanges as Array> - const textNc = expectDefined(nodeChanges.find((nc) => nc.type === 'TEXT'), 'text node change') + const textNc = expectDefined( + nodeChanges.find((nc) => nc.type === 'TEXT'), + 'text node change' + ) const derivedTextData = textNc.derivedTextData as Record const fontMetaData = expectDefined( derivedTextData.fontMetaData as Array> | undefined, @@ -163,7 +168,8 @@ describe('text node export', () => { await initCodec() const { unzipSync, inflateSync } = await import('fflate') - const { decodeBinarySchema, compileSchema, ByteBuffer } = await import('#core/kiwi/schema-runtime') + const { decodeBinarySchema, compileSchema, ByteBuffer } = + await import('#core/kiwi/schema-runtime') const { parseFigKiwiChunks } = await import('@open-pencil/core') const graph = new SceneGraph() @@ -195,7 +201,10 @@ describe('text node export', () => { } const message = compiled.decodeMessage(inflateSync(chunks?.[1] ?? new Uint8Array())) const nodeChanges = message.nodeChanges as Array> - const textNc = expectDefined(nodeChanges.find((nc) => nc.type === 'TEXT'), 'text node change') + const textNc = expectDefined( + nodeChanges.find((nc) => nc.type === 'TEXT'), + 'text node change' + ) expect(textNc.textAutoResize).toBe('HEIGHT') expect(textNc.lineHeight).toBeUndefined() @@ -206,7 +215,8 @@ describe('text node export', () => { await initCodec() const { unzipSync, inflateSync } = await import('fflate') - const { decodeBinarySchema, compileSchema, ByteBuffer } = await import('#core/kiwi/schema-runtime') + const { decodeBinarySchema, compileSchema, ByteBuffer } = + await import('#core/kiwi/schema-runtime') const { parseFigKiwiChunks } = await import('@open-pencil/core') const graph = new SceneGraph() @@ -253,20 +263,23 @@ describe('text node export', () => { expect(families).toContain('Regular') }) - test.if(runsHeavyTests)('material3.fig text nodes have derivedTextData after round-trip', async () => { - const original = await parseFixture('material3.fig') + test.if(runsHeavyTests)( + 'material3.fig text nodes have derivedTextData after round-trip', + async () => { + const original = await parseFixture('material3.fig') - const textNodes = [...original.getAllNodes()].filter((n) => n.type === 'TEXT') - expect(textNodes.length).toBeGreaterThan(0) + const textNodes = [...original.getAllNodes()].filter((n) => n.type === 'TEXT') + expect(textNodes.length).toBeGreaterThan(0) - const exported = await exportFigFile(original) - const reimported = await parseFigFile(exported.buffer as ArrayBuffer) + const exported = await exportFigFile(original) + const reimported = await parseFigFile(exported.buffer as ArrayBuffer) - const reimportedText = [...reimported.getAllNodes()].filter((n) => n.type === 'TEXT') - expect(reimportedText.length).toBe(textNodes.length) + const reimportedText = [...reimported.getAllNodes()].filter((n) => n.type === 'TEXT') + expect(reimportedText.length).toBe(textNodes.length) - for (const node of reimportedText.slice(0, 10)) { - expect(node.text.length).toBeGreaterThan(0) + for (const node of reimportedText.slice(0, 10)) { + expect(node.text.length).toBeGreaterThan(0) + } } - }) + ) }) diff --git a/tests/engine/io/fig/import/component-props.test.ts b/tests/engine/io/fig/import/component-props.test.ts index 02050bcfc..575669bd0 100644 --- a/tests/engine/io/fig/import/component-props.test.ts +++ b/tests/engine/io/fig/import/component-props.test.ts @@ -1,7 +1,7 @@ import { describe, expect, test } from 'bun:test' -import { importNodeChanges } from '#core/kiwi/fig/import' import type { NodeChange } from '#core/kiwi/fig/codec' +import { importNodeChanges } from '#core/kiwi/fig/import' const documentGuid = { sessionID: 0, localID: 0 } const pageGuid = { sessionID: 0, localID: 1 } @@ -39,7 +39,13 @@ describe('Figma component property import', () => { test('applies text data component prop assignments', () => { const nodeChanges: NodeChange[] = [ { guid: documentGuid, phase: 'CREATED', type: 'DOCUMENT', name: 'Document' }, - { guid: pageGuid, phase: 'CREATED', parentIndex: { guid: documentGuid, position: '!' }, type: 'CANVAS', name: 'Page' }, + { + guid: pageGuid, + phase: 'CREATED', + parentIndex: { guid: documentGuid, position: '!' }, + type: 'CANVAS', + name: 'Page' + }, { guid: componentGuid, phase: 'CREATED', @@ -80,7 +86,13 @@ describe('Figma component property import', () => { test('propagates nested instance swaps through clone chains', () => { const nodeChanges: NodeChange[] = [ { guid: documentGuid, phase: 'CREATED', type: 'DOCUMENT', name: 'Document' }, - { guid: pageGuid, phase: 'CREATED', parentIndex: { guid: documentGuid, position: '!' }, type: 'CANVAS', name: 'Page' }, + { + guid: pageGuid, + phase: 'CREATED', + parentIndex: { guid: documentGuid, position: '!' }, + type: 'CANVAS', + name: 'Page' + }, { guid: strokeStyleGuid, phase: 'CREATED', @@ -177,9 +189,7 @@ describe('Figma component property import', () => { symbolData: { symbolID: mailIconGuid }, size: { x: 16, y: 16 }, transform: { m00: 1, m01: 0, m02: 0, m10: 0, m11: 1, m12: 0 }, - componentPropRefs: [ - { defID: iconPropGuid, componentPropNodeField: 'OVERRIDDEN_SYMBOL_ID' } - ] + componentPropRefs: [{ defID: iconPropGuid, componentPropNodeField: 'OVERRIDDEN_SYMBOL_ID' }] }, { guid: sourceInstanceGuid, @@ -220,7 +230,9 @@ describe('Figma component property import', () => { const graph = importNodeChanges(nodeChanges, [], undefined, { populate: 'all' }) const clone = Array.from(graph.getAllNodes()).find((node) => node.name === 'Menu item clone') - const icon = clone?.childIds.map((id) => graph.getNode(id)).find((node) => node?.type === 'INSTANCE') + const icon = clone?.childIds + .map((id) => graph.getNode(id)) + .find((node) => node?.type === 'INSTANCE') const iconChild = icon?.childIds.map((id) => graph.getNode(id)).find(Boolean) expect(icon?.name).toBe('icon/user') expect(iconChild?.name).toBe('user-path') diff --git a/tests/engine/io/fig/import/derived-symbol-data.test.ts b/tests/engine/io/fig/import/derived-symbol-data.test.ts index a7f9e4929..41ec28726 100644 --- a/tests/engine/io/fig/import/derived-symbol-data.test.ts +++ b/tests/engine/io/fig/import/derived-symbol-data.test.ts @@ -1,8 +1,8 @@ import { describe, expect, test } from 'bun:test' +import type { OverrideContext } from '#core/kiwi/fig/instance-overrides' import { buildDsdLayoutUpdates } from '#core/kiwi/fig/instance-overrides/derived-symbol-data/layout' import { propagateDsdChanges } from '#core/kiwi/fig/instance-overrides/derived-symbol-data/propagate' -import type { OverrideContext } from '#core/kiwi/fig/instance-overrides' import { SceneGraph } from '#core/scene-graph' function pageId(graph: SceneGraph): string { @@ -17,7 +17,10 @@ describe('fig import derived symbol data', () => { figmaDerivedTextGlyphs: [{ commandsBlob: new Uint8Array([0]), x: 0, y: 10, fontSize: 14 }], figmaDerivedLayout: { width: 56, height: 20 } }) - const clone = graph.createNode('TEXT', pageId(graph), { text: 'Account', componentId: source.id }) + const clone = graph.createNode('TEXT', pageId(graph), { + text: 'Account', + componentId: source.id + }) const ctx = { graph, activeNodeIds: new Set([source.id, clone.id]), diff --git a/tests/engine/io/fig/import/legacy/text.test.ts b/tests/engine/io/fig/import/legacy/text.test.ts index 410236bf7..e0b8e66ce 100644 --- a/tests/engine/io/fig/import/legacy/text.test.ts +++ b/tests/engine/io/fig/import/legacy/text.test.ts @@ -111,7 +111,9 @@ describe('fig-import: text properties', () => { styleIdForText: { guid: { sessionID: 1, localID: 20 } } } as Partial) ]) - const styled = graph.getChildren(graph.getPages()[0].id).find((node) => node.text === 'Styled text') + const styled = graph + .getChildren(graph.getPages()[0].id) + .find((node) => node.text === 'Styled text') expect(styled?.fontSize).toBe(16) expect(styled?.fontWeight).toBe(400) expect(styled?.lineHeight).toBe(24) @@ -148,7 +150,9 @@ describe('fig-import: text properties', () => { styleIdForFill: { guid: { sessionID: 1, localID: 30 } } } as Partial) ]) - const styled = graph.getChildren(graph.getPages()[0].id).find((node) => node.name === 'ROUNDED_RECTANGLE_10') + const styled = graph + .getChildren(graph.getPages()[0].id) + .find((node) => node.name === 'ROUNDED_RECTANGLE_10') expect(styled?.fills[0]?.color).toEqual({ r: 0.05882352963089943, g: 0.09019608050584793, diff --git a/tests/engine/io/fig/import/scaled-instance-strokes.test.ts b/tests/engine/io/fig/import/scaled-instance-strokes.test.ts index b065f660a..a9cccc2ff 100644 --- a/tests/engine/io/fig/import/scaled-instance-strokes.test.ts +++ b/tests/engine/io/fig/import/scaled-instance-strokes.test.ts @@ -1,6 +1,7 @@ import { describe, expect, test } from 'bun:test' import { importNodeChanges } from '@open-pencil/core' + import type { NodeChange } from '#core/kiwi/fig/codec' import { canvas, doc, node } from './legacy/helpers' @@ -47,7 +48,9 @@ describe('fig import scaled instance strokes', () => { { populate: 'all' } ) - const instance = Array.from(graph.getAllNodes()).find((sceneNode) => sceneNode.name === 'INSTANCE_20') + const instance = Array.from(graph.getAllNodes()).find( + (sceneNode) => sceneNode.name === 'INSTANCE_20' + ) const vector = instance?.childIds.map((id) => graph.getNode(id)).find(Boolean) expect(vector?.strokes[0]?.weight).toBe(2) @@ -94,7 +97,9 @@ describe('fig import scaled instance strokes', () => { { populate: 'all' } ) - const instance = Array.from(graph.getAllNodes()).find((sceneNode) => sceneNode.name === 'INSTANCE_40') + const instance = Array.from(graph.getAllNodes()).find( + (sceneNode) => sceneNode.name === 'INSTANCE_40' + ) const vector = instance?.childIds.map((id) => graph.getNode(id)).find(Boolean) expect(vector?.strokes[0]?.weight).toBeCloseTo(4 / 3, 6) diff --git a/tests/engine/io/fig/import/style-refs.test.ts b/tests/engine/io/fig/import/style-refs.test.ts index 61b9cf632..83cab6e3e 100644 --- a/tests/engine/io/fig/import/style-refs.test.ts +++ b/tests/engine/io/fig/import/style-refs.test.ts @@ -13,7 +13,8 @@ describe('fig import style refs', () => { visible: true, blendMode: 'NORMAL' as const } - const fields: Record & Pick = { + const fields: Record & + Pick = { styleIdForStrokeFill: { guid: styleGuid }, strokePaints: [ { diff --git a/tests/engine/io/fig/roundtrip/glyph-blob.test.ts b/tests/engine/io/fig/roundtrip/glyph-blob.test.ts index 6900976d5..a26cffac1 100644 --- a/tests/engine/io/fig/roundtrip/glyph-blob.test.ts +++ b/tests/engine/io/fig/roundtrip/glyph-blob.test.ts @@ -3,14 +3,17 @@ import { readFileSync } from 'node:fs' import { resolve } from 'node:path' import { exportFigFile, initCodec, parseFigFile, SceneGraph } from '@open-pencil/core' -import { parseFigBuffer } from '#core/kiwi/fig/parse/core' import { fontManager } from '@open-pencil/core/text' +import { parseFigBuffer } from '#core/kiwi/fig/parse/core' + const FIXTURES = resolve(import.meta.dir, '../../../../fixtures') const INTER_ASSETS = resolve(import.meta.dir, '../../../../../packages/core/assets') function countGlyphBlobs(bytes: Uint8Array) { - const parsed = parseFigBuffer(bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)) + const parsed = parseFigBuffer( + bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) + ) let glyphs = 0 let glyphsWithBlob = 0 const uniqueGlyphBlobs = new Set() @@ -50,7 +53,10 @@ describe('roundtrip: text glyph blobs', () => { const input = countGlyphBlobs(fixtureBytes) const graph = await parseFigFile( - fixtureBytes.buffer.slice(fixtureBytes.byteOffset, fixtureBytes.byteOffset + fixtureBytes.byteLength) + fixtureBytes.buffer.slice( + fixtureBytes.byteOffset, + fixtureBytes.byteOffset + fixtureBytes.byteLength + ) ) const exported = await exportFigFile(graph) const output = countGlyphBlobs(exported) diff --git a/tests/engine/io/fig/roundtrip/variables.test.ts b/tests/engine/io/fig/roundtrip/variables.test.ts index 3fcead3ee..33fcd9c79 100644 --- a/tests/engine/io/fig/roundtrip/variables.test.ts +++ b/tests/engine/io/fig/roundtrip/variables.test.ts @@ -117,17 +117,21 @@ describe('variable roundtrip', () => { expect(Object.keys(reimportedRect.boundVariables)).toContain('strokes/0/color') }) - test.if(runsHeavyTests)('material3.fig variables survive round-trip', async () => { - const original = await parseFixture('material3.fig') + test.if(runsHeavyTests)( + 'material3.fig variables survive round-trip', + async () => { + const original = await parseFixture('material3.fig') - const exported = await exportFigFile(original) - const reimported = await parseFigFile(exported.buffer as ArrayBuffer) + const exported = await exportFigFile(original) + const reimported = await parseFigFile(exported.buffer as ArrayBuffer) - expect(reimported.variables.size).toBe(original.variables.size) - expect(reimported.variableCollections.size).toBeGreaterThanOrEqual( - [...original.variableCollections.values()].filter((c) => c.variableIds.length > 0).length - ) - }, 120_000) + expect(reimported.variables.size).toBe(original.variables.size) + expect(reimported.variableCollections.size).toBeGreaterThanOrEqual( + [...original.variableCollections.values()].filter((c) => c.variableIds.length > 0).length + ) + }, + 120_000 + ) test('pluginID casing is consistent across full codec pipeline', async () => { await initCodec() diff --git a/tests/engine/io/subgraph.test.ts b/tests/engine/io/subgraph.test.ts index 6f3272bdc..7bbfe3f86 100644 --- a/tests/engine/io/subgraph.test.ts +++ b/tests/engine/io/subgraph.test.ts @@ -1,11 +1,10 @@ -import { readFileSync } from 'node:fs' - import { describe, expect, test } from 'bun:test' +import { readFileSync } from 'node:fs' import { exportFigFile, extractExportGraph, parseFigFile } from '@open-pencil/core/io' import { initCodec } from '@open-pencil/core/kiwi' -import { parseFigBuffer } from '@open-pencil/core/kiwi/fig/parse/core' import { guidToString } from '@open-pencil/core/kiwi/fig/node-change/guid' +import { parseFigBuffer } from '@open-pencil/core/kiwi/fig/parse/core' import { SceneGraph } from '@open-pencil/core/scene-graph' describe('export subgraph extraction', () => { @@ -96,7 +95,10 @@ describe('export subgraph extraction', () => { await initCodec() const fixture = new Uint8Array(readFileSync('tests/fixtures/gold-preview.fig')) const graph = await parseFigFile( - fixture.buffer.slice(fixture.byteOffset, fixture.byteOffset + fixture.byteLength) as ArrayBuffer + fixture.buffer.slice( + fixture.byteOffset, + fixture.byteOffset + fixture.byteLength + ) as ArrayBuffer ) const exported = await exportFigFile(graph) const parsed = parseFigBuffer(exported.buffer as ArrayBuffer) diff --git a/tests/engine/profiler/capture-stack.test.ts b/tests/engine/profiler/capture-stack.test.ts index 5cf4f7eee..36091cbe6 100644 --- a/tests/engine/profiler/capture-stack.test.ts +++ b/tests/engine/profiler/capture-stack.test.ts @@ -20,4 +20,3 @@ describe('CaptureStack', () => { expect(roots[0].children[0].drawCalls).toBe(2) }) }) - diff --git a/tests/engine/profiler/draw-call-counter.test.ts b/tests/engine/profiler/draw-call-counter.test.ts index f32e635cd..152dd685b 100644 --- a/tests/engine/profiler/draw-call-counter.test.ts +++ b/tests/engine/profiler/draw-call-counter.test.ts @@ -38,4 +38,3 @@ describe('DrawCallCounter', () => { expect(rawDrawCalls).toBe(3) }) }) - diff --git a/tests/engine/profiler/frame-stats.test.ts b/tests/engine/profiler/frame-stats.test.ts index 7882682e4..c62b7b52e 100644 --- a/tests/engine/profiler/frame-stats.test.ts +++ b/tests/engine/profiler/frame-stats.test.ts @@ -35,4 +35,3 @@ describe('FrameStats', () => { expect(stats.gpuTime).toBe(0) }) }) - diff --git a/tests/engine/profiler/gpu-timer.test.ts b/tests/engine/profiler/gpu-timer.test.ts index 3b0351e68..d67eadbbe 100644 --- a/tests/engine/profiler/gpu-timer.test.ts +++ b/tests/engine/profiler/gpu-timer.test.ts @@ -13,4 +13,3 @@ describe('GPUTimer', () => { timer.destroy() }) }) - diff --git a/tests/engine/profiler/phase-timer.test.ts b/tests/engine/profiler/phase-timer.test.ts index 3bfaeaa66..876b2869e 100644 --- a/tests/engine/profiler/phase-timer.test.ts +++ b/tests/engine/profiler/phase-timer.test.ts @@ -22,4 +22,3 @@ describe('PhaseTimer', () => { timer.endPhase('test') }) }) - diff --git a/tests/engine/render/canvas/boolean-visual.test.ts b/tests/engine/render/canvas/boolean-visual.test.ts index edbb057ce..58037c7e7 100644 --- a/tests/engine/render/canvas/boolean-visual.test.ts +++ b/tests/engine/render/canvas/boolean-visual.test.ts @@ -1,6 +1,5 @@ -import { existsSync } from 'node:fs' - import { describe, expect, test } from 'bun:test' +import { existsSync } from 'node:fs' import { initCanvasKit } from '#cli/headless' diff --git a/tests/engine/render/canvas/boolean.test.ts b/tests/engine/render/canvas/boolean.test.ts index e19a76ee1..62f1b02cf 100644 --- a/tests/engine/render/canvas/boolean.test.ts +++ b/tests/engine/render/canvas/boolean.test.ts @@ -1,10 +1,10 @@ import { describe, expect, test } from 'bun:test' import { initCanvasKit } from '#cli/headless' -import { BLACK } from '#core/constants' import { makeBooleanOperationPath } from '#core/canvas/boolean' -import { makeNodeShapePath, makePolygonPath, makeRRect } from '#core/canvas/shapes' import type { SkiaRenderer } from '#core/canvas/renderer' +import { makeNodeShapePath, makePolygonPath, makeRRect } from '#core/canvas/shapes' +import { BLACK } from '#core/constants' import { createAPI } from '#tests/engine/figma/api/helpers' @@ -145,8 +145,12 @@ describe('boolean operation paths', () => { expect(firstNode).toBeDefined() expect(secondNode).toBeDefined() if (!firstNode || !secondNode) return - firstNode.strokes = [{ type: 'SOLID', color: BLACK, opacity: 1, visible: true, weight: 12, align: 'CENTER' }] - secondNode.strokes = [{ type: 'SOLID', color: BLACK, opacity: 1, visible: true, weight: 12, align: 'CENTER' }] + firstNode.strokes = [ + { type: 'SOLID', color: BLACK, opacity: 1, visible: true, weight: 12, align: 'CENTER' } + ] + secondNode.strokes = [ + { type: 'SOLID', color: BLACK, opacity: 1, visible: true, weight: 12, align: 'CENTER' } + ] const booleanNode = api.union([first, second], api.currentPage) const node = api.graph.getNode(booleanNode.id) diff --git a/tests/engine/render/canvas/corner-smoothing.test.ts b/tests/engine/render/canvas/corner-smoothing.test.ts index 9e232c9d9..5da92e978 100644 --- a/tests/engine/render/canvas/corner-smoothing.test.ts +++ b/tests/engine/render/canvas/corner-smoothing.test.ts @@ -1,4 +1,5 @@ import { describe, expect, mock, test } from 'bun:test' + import type { Canvas } from 'canvaskit-wasm' import { drawNodeFill } from '#core/canvas/fills' diff --git a/tests/engine/render/canvas/culling.test.ts b/tests/engine/render/canvas/culling.test.ts index d8bf0f29c..c0c58c63f 100644 --- a/tests/engine/render/canvas/culling.test.ts +++ b/tests/engine/render/canvas/culling.test.ts @@ -1,7 +1,7 @@ import { describe, expect, mock, test } from 'bun:test' -import { renderNode } from '#core/canvas/scene' import type { SkiaRenderer } from '#core/canvas/renderer' +import { renderNode } from '#core/canvas/scene' import { SceneGraph } from '#core/scene-graph' function pageId(graph: SceneGraph) { diff --git a/tests/engine/render/canvas/effects/ordering.test.ts b/tests/engine/render/canvas/effects/ordering.test.ts index f9b72be10..6c44d76df 100644 --- a/tests/engine/render/canvas/effects/ordering.test.ts +++ b/tests/engine/render/canvas/effects/ordering.test.ts @@ -3,8 +3,8 @@ import { describe, expect, mock, test } from 'bun:test' import type { Canvas } from 'canvaskit-wasm' import { renderShapeUncached } from '#core/canvas/scene' -import { createDefaultNode } from '#core/scene-graph/node-defaults' import type { SceneGraph } from '#core/scene-graph' +import { createDefaultNode } from '#core/scene-graph/node-defaults' import { createMockCanvas, createMockRenderer } from './helpers' diff --git a/tests/engine/render/canvas/font-readiness.test.ts b/tests/engine/render/canvas/font-readiness.test.ts index 93fef7aa5..792d32cf1 100644 --- a/tests/engine/render/canvas/font-readiness.test.ts +++ b/tests/engine/render/canvas/font-readiness.test.ts @@ -1,8 +1,8 @@ import { describe, expect, test } from 'bun:test' import { isNodeFontLoaded } from '#core/canvas/text' -import { fontManager } from '#core/text/fonts' import type { SceneNode } from '#core/scene-graph' +import { fontManager } from '#core/text/fonts' describe('canvas text font readiness', () => { test('requires the exact requested font weight before rendering text', () => { diff --git a/tests/engine/render/canvas/renderer-lifecycle.test.ts b/tests/engine/render/canvas/renderer-lifecycle.test.ts index ca5d87090..e7dbc92a5 100644 --- a/tests/engine/render/canvas/renderer-lifecycle.test.ts +++ b/tests/engine/render/canvas/renderer-lifecycle.test.ts @@ -1,4 +1,5 @@ import { expect, mock, test } from 'bun:test' + import type { Font, Paint, Surface } from 'canvaskit-wasm' import type { SkiaRenderer } from '#core/canvas/renderer' diff --git a/tests/engine/render/canvas/retained-backing.test.ts b/tests/engine/render/canvas/retained-backing.test.ts index 7a6a44606..9fe0e5fec 100644 --- a/tests/engine/render/canvas/retained-backing.test.ts +++ b/tests/engine/render/canvas/retained-backing.test.ts @@ -1,4 +1,5 @@ import { expect, mock, test } from 'bun:test' + import type { Canvas, Image as CKImage, Surface } from 'canvaskit-wasm' import type { SkiaRenderer } from '#core/canvas/renderer' diff --git a/tests/engine/render/canvas/text.test.ts b/tests/engine/render/canvas/text.test.ts index 1c432e098..38782cea6 100644 --- a/tests/engine/render/canvas/text.test.ts +++ b/tests/engine/render/canvas/text.test.ts @@ -10,8 +10,8 @@ import { import { initCanvasKit } from '#cli/headless' import type { SkiaRenderer } from '#core/canvas/renderer' import { renderText } from '#core/canvas/scene' -import type { SceneNode } from '#core/scene-graph' import { buildParagraph } from '#core/canvas/text' +import type { SceneNode } from '#core/scene-graph' import { fontManager } from '#core/text/fonts' import { expectDefined } from '#tests/helpers/assert' diff --git a/tests/engine/render/canvas/visual/boolean-matrix.ts b/tests/engine/render/canvas/visual/boolean-matrix.ts index 517aa875e..6c67f1afc 100644 --- a/tests/engine/render/canvas/visual/boolean-matrix.ts +++ b/tests/engine/render/canvas/visual/boolean-matrix.ts @@ -56,15 +56,23 @@ function makePair(name: CaseName, x: number, y: number) { secondNode.type = 'ELLIPSE' if (name === 'arc') { firstNode.arcData = { startingAngle: 0, endingAngle: Math.PI * 1.5, innerRadius: 0.35 } - secondNode.arcData = { startingAngle: Math.PI * 0.25, endingAngle: Math.PI * 1.75, innerRadius: 0.2 } + secondNode.arcData = { + startingAngle: Math.PI * 0.25, + endingAngle: Math.PI * 1.75, + innerRadius: 0.2 + } } } else if (name === 'line') { firstNode.type = 'LINE' secondNode.type = 'LINE' firstNode.height = 54 secondNode.height = 54 - firstNode.strokes = [{ type: 'SOLID', color: color(1), opacity: 1, visible: true, weight: 14, align: 'CENTER' }] - secondNode.strokes = [{ type: 'SOLID', color: color(2), opacity: 1, visible: true, weight: 14, align: 'CENTER' }] + firstNode.strokes = [ + { type: 'SOLID', color: color(1), opacity: 1, visible: true, weight: 14, align: 'CENTER' } + ] + secondNode.strokes = [ + { type: 'SOLID', color: color(2), opacity: 1, visible: true, weight: 14, align: 'CENTER' } + ] } else if (name === 'polygon' || name === 'star') { firstNode.type = name === 'polygon' ? 'POLYGON' : 'STAR' secondNode.type = name === 'polygon' ? 'POLYGON' : 'STAR' diff --git a/tests/engine/text/derived-text/clipboard.test.ts b/tests/engine/text/derived-text/clipboard.test.ts index 6d34c264c..7dd05c950 100644 --- a/tests/engine/text/derived-text/clipboard.test.ts +++ b/tests/engine/text/derived-text/clipboard.test.ts @@ -34,20 +34,25 @@ describe('clipboard derived text export', () => { const fontDigestMap = await buildFontDigestMap(graph) const blobs: Uint8Array[] = [] - const derived = await buildDerivedTextDataV4(text, fontDigestMap, { - lineHeight: 20, - lineAscent: 15, - lineWidth: 42, - baseline: 16, - glyphs: [ - { firstCharacter: 0, x: 0, y: 16, advance: 8 }, - { firstCharacter: 1, x: 8, y: 16, advance: 8 }, - { firstCharacter: 2, x: 16, y: 16, advance: 8 }, - { firstCharacter: 3, x: 24, y: 16, advance: 8 }, - { firstCharacter: 4, x: 32, y: 16, advance: 10 } - ], - logicalIndexToCharacterOffsetMap: [0, 8, 16, 24, 32, 42] - }, blobs) + const derived = await buildDerivedTextDataV4( + text, + fontDigestMap, + { + lineHeight: 20, + lineAscent: 15, + lineWidth: 42, + baseline: 16, + glyphs: [ + { firstCharacter: 0, x: 0, y: 16, advance: 8 }, + { firstCharacter: 1, x: 8, y: 16, advance: 8 }, + { firstCharacter: 2, x: 16, y: 16, advance: 8 }, + { firstCharacter: 3, x: 24, y: 16, advance: 8 }, + { firstCharacter: 4, x: 32, y: 16, advance: 10 } + ], + logicalIndexToCharacterOffsetMap: [0, 8, 16, 24, 32, 42] + }, + blobs + ) const derivedTextData = expectDefined(derived, 'derived text data') const firstGlyph = expectDefined(derivedTextData.glyphs[0], 'first glyph') @@ -89,37 +94,42 @@ describe('clipboard derived text export', () => { }) const derived = expectDefined( - await buildDerivedTextDataV4(text, await buildFontDigestMap(graph), { - lineHeight: 68, - lineAscent: 54, - lineWidth: 272, - baseline: 54.36, - baselines: [ - { - firstCharacter: 0, - endCharacter: 10, - position: { x: 0, y: 54.36 }, - width: 271.58, - lineY: 0, - lineHeight: 68, - lineAscent: 54 - }, - { - firstCharacter: 10, - endCharacter: 18, - position: { x: 0, y: 122.36 }, - width: 261.9, - lineY: 68, - lineHeight: 68, - lineAscent: 54 - } - ], - glyphs: [ - { firstCharacter: 0, x: 0, y: 54.36, advance: 42 }, - { firstCharacter: 10, x: 0, y: 122.36, advance: 44 } - ], - logicalIndexToCharacterOffsetMap: Array.from({ length: 19 }, () => 0) - }, null), + await buildDerivedTextDataV4( + text, + await buildFontDigestMap(graph), + { + lineHeight: 68, + lineAscent: 54, + lineWidth: 272, + baseline: 54.36, + baselines: [ + { + firstCharacter: 0, + endCharacter: 10, + position: { x: 0, y: 54.36 }, + width: 271.58, + lineY: 0, + lineHeight: 68, + lineAscent: 54 + }, + { + firstCharacter: 10, + endCharacter: 18, + position: { x: 0, y: 122.36 }, + width: 261.9, + lineY: 68, + lineHeight: 68, + lineAscent: 54 + } + ], + glyphs: [ + { firstCharacter: 0, x: 0, y: 54.36, advance: 42 }, + { firstCharacter: 10, x: 0, y: 122.36, advance: 44 } + ], + logicalIndexToCharacterOffsetMap: Array.from({ length: 19 }, () => 0) + }, + null + ), 'derived text' ) @@ -150,7 +160,10 @@ describe('clipboard derived text export', () => { const fontDigestMap = await buildFontDigestMap(graph) const blobs: Uint8Array[] = [] - const derived = expectDefined(await buildDerivedTextDataV4(text, fontDigestMap, null, blobs), 'derived text') + const derived = expectDefined( + await buildDerivedTextDataV4(text, fontDigestMap, null, blobs), + 'derived text' + ) expect(derived.fontMetaData[0].key.style).toBe('Semi Bold') expect(derived.glyphs[0].commandsBlob).toBe(0) diff --git a/tests/engine/text/style-runs/mutation.test.ts b/tests/engine/text/style-runs/mutation.test.ts index 1befc1db2..5efc0ae94 100644 --- a/tests/engine/text/style-runs/mutation.test.ts +++ b/tests/engine/text/style-runs/mutation.test.ts @@ -147,4 +147,3 @@ describe('removeStyleFromRange — edge cases', () => { expect(getStyleAt(result, 7).fontWeight).toBe(700) }) }) - diff --git a/tests/engine/tools/modify/image-fill.test.ts b/tests/engine/tools/modify/image-fill.test.ts index 3ba2c9cd8..d0dd4f0ac 100644 --- a/tests/engine/tools/modify/image-fill.test.ts +++ b/tests/engine/tools/modify/image-fill.test.ts @@ -123,4 +123,3 @@ describe('set_image_fill tool', () => { expect(graph.images.get(result.imageHash)).toEqual(PNG_MAGIC) }) }) - diff --git a/tests/engine/vector/blob/mutation.test.ts b/tests/engine/vector/blob/mutation.test.ts index 3ecad2982..f9e78c69c 100644 --- a/tests/engine/vector/blob/mutation.test.ts +++ b/tests/engine/vector/blob/mutation.test.ts @@ -1,6 +1,10 @@ import { describe, test, expect } from 'bun:test' -import { decodeVectorNetworkBlob, encodeVectorNetworkBlob, type VectorNetwork } from '@open-pencil/core' +import { + decodeVectorNetworkBlob, + encodeVectorNetworkBlob, + type VectorNetwork +} from '@open-pencil/core' // --------------------------------------------------------------------------- // vectorNetworkBlob — precision and handleMirroring round-trip @@ -105,4 +109,3 @@ describe('vectorNetworkBlob — precision and mirroring', () => { expect(Buffer.from(a).equals(Buffer.from(b))).toBe(true) }) }) - diff --git a/tests/engine/vector/blob/roundtrip.test.ts b/tests/engine/vector/blob/roundtrip.test.ts index 4329d0c7f..aefa3350c 100644 --- a/tests/engine/vector/blob/roundtrip.test.ts +++ b/tests/engine/vector/blob/roundtrip.test.ts @@ -1,6 +1,10 @@ import { describe, test, expect } from 'bun:test' -import { encodeVectorNetworkBlob, decodeVectorNetworkBlob, type VectorNetwork } from '@open-pencil/core' +import { + encodeVectorNetworkBlob, + decodeVectorNetworkBlob, + type VectorNetwork +} from '@open-pencil/core' import { lineNetwork, @@ -101,4 +105,3 @@ describe('vectorNetworkBlob round-trip', () => { expect(decoded.regions[1].loops).toEqual([[0, 2]]) }) }) - diff --git a/tests/engine/vector/normalize.test.ts b/tests/engine/vector/normalize.test.ts index eb4a9f3d9..211bcb511 100644 --- a/tests/engine/vector/normalize.test.ts +++ b/tests/engine/vector/normalize.test.ts @@ -1,6 +1,11 @@ import { describe, test, expect } from 'bun:test' -import { decodeVectorNetworkBlob, encodeVectorNetworkBlob, normalizeVectorNetwork, type VectorNetwork } from '@open-pencil/core' +import { + decodeVectorNetworkBlob, + encodeVectorNetworkBlob, + normalizeVectorNetwork, + type VectorNetwork +} from '@open-pencil/core' describe('normalizeVectorNetwork', () => { test('passes through segments that already have tangents', () => { @@ -73,4 +78,3 @@ describe('normalizeVectorNetwork', () => { expect(decoded.regions[0].loops).toEqual([[0, 1, 2]]) }) }) - diff --git a/tests/engine/vector/validate.test.ts b/tests/engine/vector/validate.test.ts index abc1fa986..1de7e9e0f 100644 --- a/tests/engine/vector/validate.test.ts +++ b/tests/engine/vector/validate.test.ts @@ -54,4 +54,3 @@ describe('validateVectorNetwork', () => { expect(errors[0]).toContain('x and y must be numbers') }) }) - diff --git a/tests/engine/vue/canvas/render-loop.test.ts b/tests/engine/vue/canvas/render-loop.test.ts index 7d34632fa..91e37723d 100644 --- a/tests/engine/vue/canvas/render-loop.test.ts +++ b/tests/engine/vue/canvas/render-loop.test.ts @@ -1,9 +1,9 @@ import { describe, expect, test } from 'bun:test' -import { createCanvasRenderLoop } from '#vue/canvas/surface/render-loop' - import type { Editor, EditorEvents } from '@open-pencil/core/editor' +import { createCanvasRenderLoop } from '#vue/canvas/surface/render-loop' + type EditorEventName = keyof EditorEvents type TestEditor = Pick diff --git a/tests/engine/vue/input/auto-layout-hover.test.ts b/tests/engine/vue/input/auto-layout-hover.test.ts index 5cf9f57a0..acc9443f2 100644 --- a/tests/engine/vue/input/auto-layout-hover.test.ts +++ b/tests/engine/vue/input/auto-layout-hover.test.ts @@ -1,9 +1,10 @@ import { describe, expect, test } from 'bun:test' -import { resolveAutoLayoutHover } from '#vue/shared/input/auto-layout-hover' import type { Editor } from '@open-pencil/core/editor' import type { SceneNode } from '@open-pencil/core/scene-graph' +import { resolveAutoLayoutHover } from '#vue/shared/input/auto-layout-hover' + function frame(overrides: Partial = {}): SceneNode { return { id: 'frame', diff --git a/tests/engine/vue/input/move-threshold.test.ts b/tests/engine/vue/input/move-threshold.test.ts index a01e08657..bdf7f0991 100644 --- a/tests/engine/vue/input/move-threshold.test.ts +++ b/tests/engine/vue/input/move-threshold.test.ts @@ -6,7 +6,11 @@ import { handleMoveMove, handleMoveUp, MOVE_DRAG_START_THRESHOLD_PX } from '#vue import { createSelectionMoveDrag } from '#vue/shared/input/select/move' import type { DragMove } from '#vue/shared/input/types' -function setupMoveDrag(): { editor: ReturnType; drag: DragMove; nodeId: string } { +function setupMoveDrag(): { + editor: ReturnType + drag: DragMove + nodeId: string +} { const editor = createEditor() const pageId = editor.state.currentPageId const node = editor.graph.createNode('RECTANGLE', pageId, { diff --git a/tests/engine/vue/layout/hug-contents.test.ts b/tests/engine/vue/layout/hug-contents.test.ts index 92f1bba95..83873b036 100644 --- a/tests/engine/vue/layout/hug-contents.test.ts +++ b/tests/engine/vue/layout/hug-contents.test.ts @@ -1,8 +1,13 @@ import { describe, expect, test } from 'bun:test' -import { widthSizingForNode, heightSizingForNode, sizingOptionsForNode } from '#vue/controls/layout/helpers' import type { SceneNode } from '@open-pencil/core/scene-graph' +import { + widthSizingForNode, + heightSizingForNode, + sizingOptionsForNode +} from '#vue/controls/layout/helpers' + function node(overrides: Partial): SceneNode { return { id: 'node', diff --git a/tests/helpers/canvas.ts b/tests/helpers/canvas.ts index 28e2ea982..5e1c26c78 100644 --- a/tests/helpers/canvas.ts +++ b/tests/helpers/canvas.ts @@ -27,7 +27,10 @@ export class CanvasHelper { } async waitForInit() { - await this.page.getByTestId('canvas-element').and(this.page.locator('[data-ready="1"]')).waitFor({ timeout: 30000 }) + await this.page + .getByTestId('canvas-element') + .and(this.page.locator('[data-ready="1"]')) + .waitFor({ timeout: 30000 }) await this.page.getByTestId('canvas-loading').waitFor({ state: 'hidden', timeout: 30000 }) await this.page.locator('#loader').waitFor({ state: 'detached', timeout: 30000 }) } diff --git a/vite.config.ts b/vite.config.ts index 9c6a0cc24..67f965485 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,7 +1,5 @@ import process from 'node:process' -import packageJson from './package.json' - import tailwindcss from '@tailwindcss/vite' import vue from '@vitejs/plugin-vue' import IconsResolver from 'unplugin-icons/resolver' @@ -9,6 +7,7 @@ import Icons from 'unplugin-icons/vite' import Components from 'unplugin-vue-components/vite' import { defineConfig } from 'vite' +import packageJson from './package.json' import { createOpenPencilAliases } from './vite/aliases' import { localAutomationToken, openPencilAutomationPlugin } from './vite/automation' import { copyCanvasKitAssetsPlugin } from './vite/canvaskit-assets'