@llc3233149/core 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/canvaskit.d.ts +9 -0
- package/dist/canvaskit.d.ts.map +1 -0
- package/dist/canvaskit.js +55 -0
- package/dist/canvaskit.js.map +1 -0
- package/dist/clipboard.d.ts +32 -0
- package/dist/clipboard.d.ts.map +1 -0
- package/dist/clipboard.js +345 -0
- package/dist/clipboard.js.map +1 -0
- package/dist/color.d.ts +28 -0
- package/dist/color.d.ts.map +1 -0
- package/dist/color.js +67 -0
- package/dist/color.js.map +1 -0
- package/dist/constants.d.ts +159 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +224 -0
- package/dist/constants.js.map +1 -0
- package/dist/copy.d.ts +20 -0
- package/dist/copy.d.ts.map +1 -0
- package/dist/copy.js +64 -0
- package/dist/copy.js.map +1 -0
- package/dist/fig-export-worker.d.ts +2 -0
- package/dist/fig-export-worker.d.ts.map +1 -0
- package/dist/fig-export-worker.js +17 -0
- package/dist/fig-export-worker.js.map +1 -0
- package/dist/fig-export.d.ts +13 -0
- package/dist/fig-export.d.ts.map +1 -0
- package/dist/fig-export.js +243 -0
- package/dist/fig-export.js.map +1 -0
- package/dist/figma-api.d.ts +268 -0
- package/dist/figma-api.d.ts.map +1 -0
- package/dist/figma-api.js +1074 -0
- package/dist/figma-api.js.map +1 -0
- package/dist/figma-rest-import.d.ts +122 -0
- package/dist/figma-rest-import.d.ts.map +1 -0
- package/dist/figma-rest-import.js +319 -0
- package/dist/figma-rest-import.js.map +1 -0
- package/dist/fonts.d.ts +24 -0
- package/dist/fonts.d.ts.map +1 -0
- package/dist/fonts.js +356 -0
- package/dist/fonts.js.map +1 -0
- package/dist/geometry.d.ts +14 -0
- package/dist/geometry.d.ts.map +1 -0
- package/dist/geometry.js +47 -0
- package/dist/geometry.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/kiwi/codec.d.ts +292 -0
- package/dist/kiwi/codec.d.ts.map +1 -0
- package/dist/kiwi/codec.js +340 -0
- package/dist/kiwi/codec.js.map +1 -0
- package/dist/kiwi/fig-file.d.ts +4 -0
- package/dist/kiwi/fig-file.d.ts.map +1 -0
- package/dist/kiwi/fig-file.js +236 -0
- package/dist/kiwi/fig-file.js.map +1 -0
- package/dist/kiwi/fig-import.d.ts +4 -0
- package/dist/kiwi/fig-import.d.ts.map +1 -0
- package/dist/kiwi/fig-import.js +248 -0
- package/dist/kiwi/fig-import.js.map +1 -0
- package/dist/kiwi/fig-parse-worker.d.ts +7 -0
- package/dist/kiwi/fig-parse-worker.d.ts.map +1 -0
- package/dist/kiwi/fig-parse-worker.js +184 -0
- package/dist/kiwi/fig-parse-worker.js.map +1 -0
- package/dist/kiwi/instance-overrides.d.ts +66 -0
- package/dist/kiwi/instance-overrides.d.ts.map +1 -0
- package/dist/kiwi/instance-overrides.js +644 -0
- package/dist/kiwi/instance-overrides.js.map +1 -0
- package/dist/kiwi/kiwi-convert.d.ts +19 -0
- package/dist/kiwi/kiwi-convert.d.ts.map +1 -0
- package/dist/kiwi/kiwi-convert.js +678 -0
- package/dist/kiwi/kiwi-convert.js.map +1 -0
- package/dist/kiwi/kiwi-schema/bb.d.ts +25 -0
- package/dist/kiwi/kiwi-schema/bb.d.ts.map +1 -0
- package/dist/kiwi/kiwi-schema/bb.js +225 -0
- package/dist/kiwi/kiwi-schema/bb.js.map +1 -0
- package/dist/kiwi/kiwi-schema/binary.d.ts +5 -0
- package/dist/kiwi/kiwi-schema/binary.d.ts.map +1 -0
- package/dist/kiwi/kiwi-schema/binary.js +86 -0
- package/dist/kiwi/kiwi-schema/binary.js.map +1 -0
- package/dist/kiwi/kiwi-schema/index.d.ts +6 -0
- package/dist/kiwi/kiwi-schema/index.d.ts.map +1 -0
- package/dist/kiwi/kiwi-schema/index.js +5 -0
- package/dist/kiwi/kiwi-schema/index.js.map +1 -0
- package/dist/kiwi/kiwi-schema/js.d.ts +4 -0
- package/dist/kiwi/kiwi-schema/js.d.ts.map +1 -0
- package/dist/kiwi/kiwi-schema/js.js +282 -0
- package/dist/kiwi/kiwi-schema/js.js.map +1 -0
- package/dist/kiwi/kiwi-schema/parser.d.ts +5 -0
- package/dist/kiwi/kiwi-schema/parser.d.ts.map +1 -0
- package/dist/kiwi/kiwi-schema/parser.js +228 -0
- package/dist/kiwi/kiwi-schema/parser.js.map +1 -0
- package/dist/kiwi/kiwi-schema/schema.d.ts +22 -0
- package/dist/kiwi/kiwi-schema/schema.d.ts.map +1 -0
- package/dist/kiwi/kiwi-schema/schema.js +2 -0
- package/dist/kiwi/kiwi-schema/schema.js.map +1 -0
- package/dist/kiwi/kiwi-schema/util.d.ts +3 -0
- package/dist/kiwi/kiwi-schema/util.d.ts.map +1 -0
- package/dist/kiwi/kiwi-schema/util.js +10 -0
- package/dist/kiwi/kiwi-schema/util.js.map +1 -0
- package/dist/kiwi/protocol.d.ts +172 -0
- package/dist/kiwi/protocol.d.ts.map +1 -0
- package/dist/kiwi/protocol.js +214 -0
- package/dist/kiwi/protocol.js.map +1 -0
- package/dist/kiwi/schema.d.ts +3 -0
- package/dist/kiwi/schema.d.ts.map +1 -0
- package/dist/kiwi/schema.js +2285 -0
- package/dist/kiwi/schema.js.map +1 -0
- package/dist/kiwi-serialize.d.ts +17 -0
- package/dist/kiwi-serialize.d.ts.map +1 -0
- package/dist/kiwi-serialize.js +419 -0
- package/dist/kiwi-serialize.js.map +1 -0
- package/dist/layout.d.ts +9 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +533 -0
- package/dist/layout.js.map +1 -0
- package/dist/profiler/draw-call-counter.d.ts +9 -0
- package/dist/profiler/draw-call-counter.d.ts.map +1 -0
- package/dist/profiler/draw-call-counter.js +37 -0
- package/dist/profiler/draw-call-counter.js.map +1 -0
- package/dist/profiler/frame-capture.d.ts +34 -0
- package/dist/profiler/frame-capture.d.ts.map +1 -0
- package/dist/profiler/frame-capture.js +90 -0
- package/dist/profiler/frame-capture.js.map +1 -0
- package/dist/profiler/frame-stats.d.ts +33 -0
- package/dist/profiler/frame-stats.d.ts.map +1 -0
- package/dist/profiler/frame-stats.js +103 -0
- package/dist/profiler/frame-stats.js.map +1 -0
- package/dist/profiler/gpu-timer.d.ts +15 -0
- package/dist/profiler/gpu-timer.d.ts.map +1 -0
- package/dist/profiler/gpu-timer.js +77 -0
- package/dist/profiler/gpu-timer.js.map +1 -0
- package/dist/profiler/hud-renderer.d.ts +22 -0
- package/dist/profiler/hud-renderer.d.ts.map +1 -0
- package/dist/profiler/hud-renderer.js +200 -0
- package/dist/profiler/hud-renderer.js.map +1 -0
- package/dist/profiler/index.d.ts +9 -0
- package/dist/profiler/index.d.ts.map +1 -0
- package/dist/profiler/index.js +8 -0
- package/dist/profiler/index.js.map +1 -0
- package/dist/profiler/phase-timer.d.ts +9 -0
- package/dist/profiler/phase-timer.d.ts.map +1 -0
- package/dist/profiler/phase-timer.js +51 -0
- package/dist/profiler/phase-timer.js.map +1 -0
- package/dist/profiler/render-profiler.d.ts +41 -0
- package/dist/profiler/render-profiler.d.ts.map +1 -0
- package/dist/profiler/render-profiler.js +142 -0
- package/dist/profiler/render-profiler.js.map +1 -0
- package/dist/render/components.d.ts +25 -0
- package/dist/render/components.d.ts.map +1 -0
- package/dist/render/components.js +55 -0
- package/dist/render/components.js.map +1 -0
- package/dist/render/emoji-icons.d.ts +5 -0
- package/dist/render/emoji-icons.d.ts.map +1 -0
- package/dist/render/emoji-icons.js +55 -0
- package/dist/render/emoji-icons.js.map +1 -0
- package/dist/render/export-code.d.ts +9 -0
- package/dist/render/export-code.d.ts.map +1 -0
- package/dist/render/export-code.js +335 -0
- package/dist/render/export-code.js.map +1 -0
- package/dist/render/export-jsx.d.ts +5 -0
- package/dist/render/export-jsx.d.ts.map +1 -0
- package/dist/render/export-jsx.js +621 -0
- package/dist/render/export-jsx.js.map +1 -0
- package/dist/render/index.d.ts +8 -0
- package/dist/render/index.d.ts.map +1 -0
- package/dist/render/index.js +8 -0
- package/dist/render/index.js.map +1 -0
- package/dist/render/jsx-runtime.d.ts +31 -0
- package/dist/render/jsx-runtime.d.ts.map +1 -0
- package/dist/render/jsx-runtime.js +13 -0
- package/dist/render/jsx-runtime.js.map +1 -0
- package/dist/render/mini-react.d.ts +14 -0
- package/dist/render/mini-react.d.ts.map +1 -0
- package/dist/render/mini-react.js +16 -0
- package/dist/render/mini-react.js.map +1 -0
- package/dist/render/render-jsx.d.ts +24 -0
- package/dist/render/render-jsx.d.ts.map +1 -0
- package/dist/render/render-jsx.js +51 -0
- package/dist/render/render-jsx.js.map +1 -0
- package/dist/render/renderer.d.ts +16 -0
- package/dist/render/renderer.d.ts.map +1 -0
- package/dist/render/renderer.js +437 -0
- package/dist/render/renderer.js.map +1 -0
- package/dist/render/tailwind.d.ts +20 -0
- package/dist/render/tailwind.d.ts.map +1 -0
- package/dist/render/tailwind.js +115 -0
- package/dist/render/tailwind.js.map +1 -0
- package/dist/render/tree.d.ts +72 -0
- package/dist/render/tree.d.ts.map +1 -0
- package/dist/render/tree.js +63 -0
- package/dist/render/tree.js.map +1 -0
- package/dist/render-image.d.ts +19 -0
- package/dist/render-image.d.ts.map +1 -0
- package/dist/render-image.js +84 -0
- package/dist/render-image.js.map +1 -0
- package/dist/renderer/effects.d.ts +9 -0
- package/dist/renderer/effects.d.ts.map +1 -0
- package/dist/renderer/effects.js +44 -0
- package/dist/renderer/effects.js.map +1 -0
- package/dist/renderer/fills.d.ts +9 -0
- package/dist/renderer/fills.d.ts.map +1 -0
- package/dist/renderer/fills.js +189 -0
- package/dist/renderer/fills.js.map +1 -0
- package/dist/renderer/index.d.ts +2 -0
- package/dist/renderer/index.d.ts.map +1 -0
- package/dist/renderer/index.js +2 -0
- package/dist/renderer/index.js.map +1 -0
- package/dist/renderer/label-cache.d.ts +43 -0
- package/dist/renderer/label-cache.d.ts.map +1 -0
- package/dist/renderer/label-cache.js +85 -0
- package/dist/renderer/label-cache.js.map +1 -0
- package/dist/renderer/labels.d.ts +6 -0
- package/dist/renderer/labels.d.ts.map +1 -0
- package/dist/renderer/labels.js +126 -0
- package/dist/renderer/labels.js.map +1 -0
- package/dist/renderer/lru-cache.d.ts +20 -0
- package/dist/renderer/lru-cache.d.ts.map +1 -0
- package/dist/renderer/lru-cache.js +65 -0
- package/dist/renderer/lru-cache.js.map +1 -0
- package/dist/renderer/overlays.d.ts +25 -0
- package/dist/renderer/overlays.d.ts.map +1 -0
- package/dist/renderer/overlays.js +783 -0
- package/dist/renderer/overlays.js.map +1 -0
- package/dist/renderer/renderer.d.ts +229 -0
- package/dist/renderer/renderer.d.ts.map +1 -0
- package/dist/renderer/renderer.js +909 -0
- package/dist/renderer/renderer.js.map +1 -0
- package/dist/renderer/rulers.d.ts +8 -0
- package/dist/renderer/rulers.d.ts.map +1 -0
- package/dist/renderer/rulers.js +147 -0
- package/dist/renderer/rulers.js.map +1 -0
- package/dist/renderer/scene.d.ts +11 -0
- package/dist/renderer/scene.d.ts.map +1 -0
- package/dist/renderer/scene.js +392 -0
- package/dist/renderer/scene.js.map +1 -0
- package/dist/renderer/shapes.d.ts +13 -0
- package/dist/renderer/shapes.d.ts.map +1 -0
- package/dist/renderer/shapes.js +160 -0
- package/dist/renderer/shapes.js.map +1 -0
- package/dist/renderer/strokes.d.ts +9 -0
- package/dist/renderer/strokes.d.ts.map +1 -0
- package/dist/renderer/strokes.js +180 -0
- package/dist/renderer/strokes.js.map +1 -0
- package/dist/rpc/commands.d.ts +202 -0
- package/dist/rpc/commands.d.ts.map +1 -0
- package/dist/rpc/commands.js +429 -0
- package/dist/rpc/commands.js.map +1 -0
- package/dist/rpc/index.d.ts +3 -0
- package/dist/rpc/index.d.ts.map +1 -0
- package/dist/rpc/index.js +2 -0
- package/dist/rpc/index.js.map +1 -0
- package/dist/scene-graph.d.ts +318 -0
- package/dist/scene-graph.d.ts.map +1 -0
- package/dist/scene-graph.js +803 -0
- package/dist/scene-graph.js.map +1 -0
- package/dist/snap.d.ts +16 -0
- package/dist/snap.d.ts.map +1 -0
- package/dist/snap.js +97 -0
- package/dist/snap.js.map +1 -0
- package/dist/style-runs.d.ts +20 -0
- package/dist/style-runs.d.ts.map +1 -0
- package/dist/style-runs.js +168 -0
- package/dist/style-runs.js.map +1 -0
- package/dist/svg-export/defs.d.ts +19 -0
- package/dist/svg-export/defs.d.ts.map +1 -0
- package/dist/svg-export/defs.js +191 -0
- package/dist/svg-export/defs.js.map +1 -0
- package/dist/svg-export/index.d.ts +8 -0
- package/dist/svg-export/index.d.ts.map +1 -0
- package/dist/svg-export/index.js +318 -0
- package/dist/svg-export/index.js.map +1 -0
- package/dist/svg-export/paths.d.ts +9 -0
- package/dist/svg-export/paths.d.ts.map +1 -0
- package/dist/svg-export/paths.js +189 -0
- package/dist/svg-export/paths.js.map +1 -0
- package/dist/svg-node.d.ts +8 -0
- package/dist/svg-node.d.ts.map +1 -0
- package/dist/svg-node.js +48 -0
- package/dist/svg-node.js.map +1 -0
- package/dist/text-editor.d.ts +57 -0
- package/dist/text-editor.d.ts.map +1 -0
- package/dist/text-editor.js +337 -0
- package/dist/text-editor.js.map +1 -0
- package/dist/tools/ai-adapter.d.ts +22 -0
- package/dist/tools/ai-adapter.d.ts.map +1 -0
- package/dist/tools/ai-adapter.js +121 -0
- package/dist/tools/ai-adapter.js.map +1 -0
- package/dist/tools/analyze.d.ts +22 -0
- package/dist/tools/analyze.d.ts.map +1 -0
- package/dist/tools/analyze.js +677 -0
- package/dist/tools/analyze.js.map +1 -0
- package/dist/tools/create.d.ts +8 -0
- package/dist/tools/create.d.ts.map +1 -0
- package/dist/tools/create.js +204 -0
- package/dist/tools/create.js.map +1 -0
- package/dist/tools/describe.d.ts +9 -0
- package/dist/tools/describe.d.ts.map +1 -0
- package/dist/tools/describe.js +295 -0
- package/dist/tools/describe.js.map +1 -0
- package/dist/tools/export-code.d.ts +2 -0
- package/dist/tools/export-code.d.ts.map +1 -0
- package/dist/tools/export-code.js +34 -0
- package/dist/tools/export-code.js.map +1 -0
- package/dist/tools/export-tailwind-config.d.ts +27 -0
- package/dist/tools/export-tailwind-config.d.ts.map +1 -0
- package/dist/tools/export-tailwind-config.js +215 -0
- package/dist/tools/export-tailwind-config.js.map +1 -0
- package/dist/tools/icons.d.ts +5 -0
- package/dist/tools/icons.d.ts.map +1 -0
- package/dist/tools/icons.js +192 -0
- package/dist/tools/icons.js.map +1 -0
- package/dist/tools/index.d.ts +7 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +6 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/modify.d.ts +22 -0
- package/dist/tools/modify.d.ts.map +1 -0
- package/dist/tools/modify.js +653 -0
- package/dist/tools/modify.js.map +1 -0
- package/dist/tools/patterns.d.ts +2 -0
- package/dist/tools/patterns.d.ts.map +1 -0
- package/dist/tools/patterns.js +173 -0
- package/dist/tools/patterns.js.map +1 -0
- package/dist/tools/read.d.ts +17 -0
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +379 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/registry.d.ts +3 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +124 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/schema.d.ts +46 -0
- package/dist/tools/schema.d.ts.map +1 -0
- package/dist/tools/schema.js +17 -0
- package/dist/tools/schema.js.map +1 -0
- package/dist/tools/structure.d.ts +18 -0
- package/dist/tools/structure.d.ts.map +1 -0
- package/dist/tools/structure.js +345 -0
- package/dist/tools/structure.js.map +1 -0
- package/dist/tools/variables.d.ts +12 -0
- package/dist/tools/variables.d.ts.map +1 -0
- package/dist/tools/variables.js +190 -0
- package/dist/tools/variables.js.map +1 -0
- package/dist/tools/vector.d.ts +15 -0
- package/dist/tools/vector.d.ts.map +1 -0
- package/dist/tools/vector.js +312 -0
- package/dist/tools/vector.js.map +1 -0
- package/dist/types.d.ts +29 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/undo.d.ts +33 -0
- package/dist/undo.d.ts.map +1 -0
- package/dist/undo.js +98 -0
- package/dist/undo.js.map +1 -0
- package/dist/vector.d.ts +14 -0
- package/dist/vector.d.ts.map +1 -0
- package/dist/vector.js +334 -0
- package/dist/vector.js.map +1 -0
- package/dist/xpath.d.ts +8 -0
- package/dist/xpath.d.ts.map +1 -0
- package/dist/xpath.js +199 -0
- package/dist/xpath.js.map +1 -0
- package/package.json +54 -0
- package/src/canvaskit.ts +66 -0
- package/src/clipboard.ts +444 -0
- package/src/color.ts +83 -0
- package/src/constants.ts +277 -0
- package/src/copy.ts +75 -0
- package/src/fig-export-worker.ts +28 -0
- package/src/fig-export.ts +321 -0
- package/src/figma-api.ts +1297 -0
- package/src/figma-rest-import.ts +443 -0
- package/src/fonts.ts +369 -0
- package/src/geometry.ts +73 -0
- package/src/global.d.ts +20 -0
- package/src/index.ts +247 -0
- package/src/kiwi/codec.ts +614 -0
- package/src/kiwi/fig-file.ts +248 -0
- package/src/kiwi/fig-import.ts +300 -0
- package/src/kiwi/fig-parse-worker.ts +213 -0
- package/src/kiwi/instance-overrides.ts +682 -0
- package/src/kiwi/kiwi-convert.ts +771 -0
- package/src/kiwi/kiwi-schema/bb.ts +244 -0
- package/src/kiwi/kiwi-schema/binary.ts +102 -0
- package/src/kiwi/kiwi-schema/index.ts +5 -0
- package/src/kiwi/kiwi-schema/js.ts +339 -0
- package/src/kiwi/kiwi-schema/parser.ts +287 -0
- package/src/kiwi/kiwi-schema/schema.ts +24 -0
- package/src/kiwi/kiwi-schema/util.ts +10 -0
- package/src/kiwi/protocol.ts +237 -0
- package/src/kiwi/schema.ts +2286 -0
- package/src/kiwi-serialize.ts +467 -0
- package/src/layout.ts +608 -0
- package/src/profiler/draw-call-counter.ts +43 -0
- package/src/profiler/frame-capture.ts +139 -0
- package/src/profiler/frame-stats.ts +122 -0
- package/src/profiler/gpu-timer.ts +93 -0
- package/src/profiler/hud-renderer.ts +242 -0
- package/src/profiler/index.ts +8 -0
- package/src/profiler/phase-timer.ts +62 -0
- package/src/profiler/render-profiler.ts +163 -0
- package/src/render/components.ts +69 -0
- package/src/render/emoji-icons.ts +60 -0
- package/src/render/export-code.ts +366 -0
- package/src/render/export-jsx.ts +633 -0
- package/src/render/index.ts +38 -0
- package/src/render/jsx-runtime.ts +36 -0
- package/src/render/mini-react.ts +30 -0
- package/src/render/render-jsx.ts +74 -0
- package/src/render/renderer.ts +508 -0
- package/src/render/tailwind.ts +114 -0
- package/src/render/tree.ts +137 -0
- package/src/render-image.ts +139 -0
- package/src/renderer/effects.ts +64 -0
- package/src/renderer/fills.ts +248 -0
- package/src/renderer/index.ts +1 -0
- package/src/renderer/label-cache.ts +114 -0
- package/src/renderer/labels.ts +168 -0
- package/src/renderer/lru-cache.ts +71 -0
- package/src/renderer/overlays.ts +1089 -0
- package/src/renderer/renderer.ts +1226 -0
- package/src/renderer/rulers.ts +230 -0
- package/src/renderer/scene.ts +554 -0
- package/src/renderer/shapes.ts +193 -0
- package/src/renderer/strokes.ts +221 -0
- package/src/rpc/commands.ts +667 -0
- package/src/rpc/index.ts +25 -0
- package/src/scene-graph.ts +1254 -0
- package/src/snap.ts +122 -0
- package/src/style-runs.ts +234 -0
- package/src/svg-export/defs.ts +255 -0
- package/src/svg-export/index.ts +427 -0
- package/src/svg-export/paths.ts +212 -0
- package/src/svg-node.ts +62 -0
- package/src/text-editor.ts +349 -0
- package/src/tools/ai-adapter.ts +144 -0
- package/src/tools/analyze.ts +786 -0
- package/src/tools/create.ts +206 -0
- package/src/tools/describe.ts +280 -0
- package/src/tools/export-code.ts +41 -0
- package/src/tools/export-tailwind-config.ts +266 -0
- package/src/tools/icons.ts +213 -0
- package/src/tools/index.ts +6 -0
- package/src/tools/modify.ts +651 -0
- package/src/tools/patterns.ts +189 -0
- package/src/tools/read.ts +401 -0
- package/src/tools/registry.ts +155 -0
- package/src/tools/schema.ts +65 -0
- package/src/tools/structure.ts +348 -0
- package/src/tools/variables.ts +193 -0
- package/src/tools/vector.ts +311 -0
- package/src/types.ts +32 -0
- package/src/undo.ts +126 -0
- package/src/vector.ts +413 -0
- package/src/xpath.ts +259 -0
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message Encoding/Decoding for Figma Multiplayer
|
|
3
|
+
*
|
|
4
|
+
* Uses:
|
|
5
|
+
* - kiwi-schema: Binary serialization (by Evan Wallace, Figma co-founder)
|
|
6
|
+
* - fzstd: Browser-compatible Zstd decompression
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { decompress as zstdDecompress } from 'fzstd'
|
|
10
|
+
|
|
11
|
+
import { parseColor } from '../color'
|
|
12
|
+
import { compileSchema, encodeBinarySchema } from './kiwi-schema'
|
|
13
|
+
import { isZstdCompressed, getKiwiMessageType } from './protocol'
|
|
14
|
+
import figmaSchema from './schema'
|
|
15
|
+
|
|
16
|
+
interface CompiledSchema {
|
|
17
|
+
encodeMessage(message: unknown): Uint8Array
|
|
18
|
+
decodeMessage(data: Uint8Array): unknown
|
|
19
|
+
encodePaint(paint: unknown): Uint8Array
|
|
20
|
+
encodeNodeChange(nodeChange: unknown): Uint8Array
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let compiledSchema: CompiledSchema | null = null
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Initialize the codec (compiles Kiwi schema)
|
|
27
|
+
*/
|
|
28
|
+
export async function initCodec(): Promise<void> {
|
|
29
|
+
if (compiledSchema) return
|
|
30
|
+
compiledSchema = compileSchema(figmaSchema) as CompiledSchema
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function getCompiledSchema() {
|
|
34
|
+
if (!compiledSchema) throw new Error('Codec not initialized')
|
|
35
|
+
return compiledSchema
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function getSchemaBytes(): Uint8Array {
|
|
39
|
+
return encodeBinarySchema(figmaSchema)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Check if codec is initialized
|
|
44
|
+
*/
|
|
45
|
+
export function isCodecReady(): boolean {
|
|
46
|
+
return compiledSchema !== null
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Compress data using Zstd (Bun native)
|
|
51
|
+
*/
|
|
52
|
+
export function compress(data: Uint8Array): Uint8Array {
|
|
53
|
+
return data
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Decompress Zstd data (Bun native)
|
|
58
|
+
*/
|
|
59
|
+
export function decompress(data: Uint8Array): Uint8Array {
|
|
60
|
+
if (!isZstdCompressed(data)) return data
|
|
61
|
+
return zstdDecompress(data)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Encode a message for sending to Figma
|
|
66
|
+
* Handles variable bindings in fillPaints which require custom encoding
|
|
67
|
+
*/
|
|
68
|
+
export function encodeMessage(message: FigmaMessage): Uint8Array {
|
|
69
|
+
if (!compiledSchema) {
|
|
70
|
+
throw new Error('Codec not initialized. Call initCodec() first.')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Check if any nodeChange has variable bindings (fill or stroke)
|
|
74
|
+
const hasVariables = message.nodeChanges?.some(
|
|
75
|
+
(nc) =>
|
|
76
|
+
nc.fillPaints?.some((p) => p.colorVariableBinding) ||
|
|
77
|
+
nc.strokePaints?.some((p) => p.colorVariableBinding)
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if (!hasVariables) {
|
|
81
|
+
// Standard encoding
|
|
82
|
+
const encoded = compiledSchema.encodeMessage(message)
|
|
83
|
+
return compress(encoded)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Need custom encoding for variable bindings
|
|
87
|
+
// Strategy: encode each nodeChange separately, then combine
|
|
88
|
+
const messageWithoutNodes = { ...message, nodeChanges: [] }
|
|
89
|
+
const baseEncoded = compiledSchema.encodeMessage(messageWithoutNodes)
|
|
90
|
+
const baseHex = Buffer.from(baseEncoded).toString('hex')
|
|
91
|
+
|
|
92
|
+
// Encode nodeChanges with variable support
|
|
93
|
+
const nodeChangeBytes: Uint8Array[] = []
|
|
94
|
+
for (const nc of message.nodeChanges || []) {
|
|
95
|
+
const encoded = encodeNodeChangeWithVariables(nc)
|
|
96
|
+
nodeChangeBytes.push(encoded)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Combine: base message + nodeChanges
|
|
100
|
+
// Message structure: type, sessionID, ackID, reconnectSeqNum, nodeChanges[]
|
|
101
|
+
// nodeChanges is field 5
|
|
102
|
+
|
|
103
|
+
// Message structure in kiwi:
|
|
104
|
+
// - Field 1 (type): enum MessageType
|
|
105
|
+
// - Field 2 (sessionID): uint
|
|
106
|
+
// - Field 3 (ackID): uint
|
|
107
|
+
// - Field 4 (nodeChanges): NodeChange[] - this is what we need to replace
|
|
108
|
+
// - Field 25 (reconnectSequenceNumber): uint
|
|
109
|
+
//
|
|
110
|
+
// Empty array: "04 00" (field 4, length 0)
|
|
111
|
+
// We need to replace "04 00" with "04 <count> <nodes>"
|
|
112
|
+
|
|
113
|
+
const emptyArrayPattern = '0400' // field 4, length 0
|
|
114
|
+
const emptyArrayIdx = baseHex.indexOf(emptyArrayPattern)
|
|
115
|
+
|
|
116
|
+
if (emptyArrayIdx === -1) {
|
|
117
|
+
// Fallback to standard encoding
|
|
118
|
+
const encoded = compiledSchema.encodeMessage(message)
|
|
119
|
+
return compress(encoded)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Build nodeChanges array with our encoded nodes
|
|
123
|
+
const ncBytes: number[] = [0x04] // field 4
|
|
124
|
+
ncBytes.push(...encodeVarint(nodeChangeBytes.length)) // array length
|
|
125
|
+
for (const ncArr of nodeChangeBytes) {
|
|
126
|
+
ncBytes.push(...Array.from(ncArr))
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Replace "0400" with our nodeChanges
|
|
130
|
+
const beforeArray = baseHex.slice(0, emptyArrayIdx)
|
|
131
|
+
const afterArray = baseHex.slice(emptyArrayIdx + 4) // skip "0400"
|
|
132
|
+
|
|
133
|
+
const ncHex = Buffer.from(ncBytes).toString('hex')
|
|
134
|
+
const finalHex = beforeArray + ncHex + afterArray
|
|
135
|
+
|
|
136
|
+
const finalBytes = new Uint8Array(finalHex.match(/.{2}/g)?.map((b) => parseInt(b, 16)) ?? [])
|
|
137
|
+
return compress(finalBytes)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Decode a message received from Figma
|
|
142
|
+
*/
|
|
143
|
+
export function decodeMessage(data: Uint8Array): FigmaMessage {
|
|
144
|
+
if (!compiledSchema) {
|
|
145
|
+
throw new Error('Codec not initialized. Call initCodec() first.')
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const decompressed = decompress(data)
|
|
149
|
+
return compiledSchema.decodeMessage(decompressed) as FigmaMessage
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Quick peek at message type without full decoding
|
|
154
|
+
*/
|
|
155
|
+
export function peekMessageType(data: Uint8Array): number | null {
|
|
156
|
+
try {
|
|
157
|
+
const decompressed = decompress(data)
|
|
158
|
+
return getKiwiMessageType(decompressed)
|
|
159
|
+
} catch {
|
|
160
|
+
return null
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Type definitions
|
|
165
|
+
|
|
166
|
+
export type { GUID, Color } from '../types'
|
|
167
|
+
|
|
168
|
+
import type { Color, GUID, Matrix, Vector } from '../types'
|
|
169
|
+
|
|
170
|
+
export type { Matrix, Vector }
|
|
171
|
+
|
|
172
|
+
export interface ParentIndex {
|
|
173
|
+
guid: GUID
|
|
174
|
+
position: string
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export interface VariableBinding {
|
|
178
|
+
variableID: GUID
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export interface Paint {
|
|
182
|
+
type:
|
|
183
|
+
| 'SOLID'
|
|
184
|
+
| 'GRADIENT_LINEAR'
|
|
185
|
+
| 'GRADIENT_RADIAL'
|
|
186
|
+
| 'GRADIENT_ANGULAR'
|
|
187
|
+
| 'GRADIENT_DIAMOND'
|
|
188
|
+
| 'IMAGE'
|
|
189
|
+
color?: Color
|
|
190
|
+
opacity?: number
|
|
191
|
+
visible?: boolean
|
|
192
|
+
blendMode?: string
|
|
193
|
+
stops?: { color: Color; position: number }[]
|
|
194
|
+
transform?: Matrix
|
|
195
|
+
image?: { hash: string }
|
|
196
|
+
imageScaleMode?: string
|
|
197
|
+
colorVariableBinding?: VariableBinding
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export interface Effect {
|
|
201
|
+
type: 'DROP_SHADOW' | 'INNER_SHADOW' | 'LAYER_BLUR' | 'BACKGROUND_BLUR' | 'FOREGROUND_BLUR'
|
|
202
|
+
color?: Color
|
|
203
|
+
offset?: Vector
|
|
204
|
+
radius?: number
|
|
205
|
+
visible?: boolean
|
|
206
|
+
spread?: number
|
|
207
|
+
blendMode?: string
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export interface VariableAnyValue {
|
|
211
|
+
boolValue?: boolean
|
|
212
|
+
textValue?: string
|
|
213
|
+
floatValue?: number
|
|
214
|
+
colorValue?: Color
|
|
215
|
+
alias?: { guid: GUID }
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export interface VariableDataEntry {
|
|
219
|
+
value?: VariableAnyValue
|
|
220
|
+
dataType?: string
|
|
221
|
+
resolvedDataType?: string
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export interface VariableConsumptionEntry {
|
|
225
|
+
nodeField?: number
|
|
226
|
+
variableData?: VariableDataEntry
|
|
227
|
+
variableField?: string
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export interface VariableDataValuesEntry {
|
|
231
|
+
modeID: GUID
|
|
232
|
+
variableData: VariableDataEntry
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export interface NodeChange {
|
|
236
|
+
[key: string]: unknown
|
|
237
|
+
guid?: GUID
|
|
238
|
+
phase?: 'CREATED' | 'REMOVED'
|
|
239
|
+
parentIndex?: ParentIndex
|
|
240
|
+
type?: string
|
|
241
|
+
name?: string
|
|
242
|
+
visible?: boolean
|
|
243
|
+
locked?: boolean
|
|
244
|
+
opacity?: number
|
|
245
|
+
blendMode?: string
|
|
246
|
+
size?: Vector
|
|
247
|
+
transform?: Matrix
|
|
248
|
+
cornerRadius?: number
|
|
249
|
+
fillPaints?: Paint[]
|
|
250
|
+
strokePaints?: Paint[]
|
|
251
|
+
strokeWeight?: number
|
|
252
|
+
strokeAlign?: string
|
|
253
|
+
strokeCap?: string
|
|
254
|
+
strokeJoin?: string
|
|
255
|
+
dashPattern?: number[]
|
|
256
|
+
effects?: Effect[]
|
|
257
|
+
// Layout
|
|
258
|
+
stackMode?: 'NONE' | 'HORIZONTAL' | 'VERTICAL'
|
|
259
|
+
stackSpacing?: number
|
|
260
|
+
stackPadding?: number
|
|
261
|
+
stackPaddingRight?: number
|
|
262
|
+
stackPaddingBottom?: number
|
|
263
|
+
stackCounterAlign?: string
|
|
264
|
+
stackJustify?: string
|
|
265
|
+
stackCounterAlignItems?: string
|
|
266
|
+
stackPrimaryAlignItems?: string
|
|
267
|
+
stackPrimarySizing?: 'FIXED' | 'RESIZE_TO_FIT' | 'RESIZE_TO_FIT_WITH_IMPLICIT_SIZE'
|
|
268
|
+
stackCounterSizing?: 'FIXED' | 'RESIZE_TO_FIT' | 'RESIZE_TO_FIT_WITH_IMPLICIT_SIZE'
|
|
269
|
+
stackVerticalPadding?: number
|
|
270
|
+
stackHorizontalPadding?: number
|
|
271
|
+
stackWrap?: string
|
|
272
|
+
stackPositioning?: string
|
|
273
|
+
stackChildPrimaryGrow?: number
|
|
274
|
+
stackChildAlignSelf?: string
|
|
275
|
+
stackCounterSpacing?: number
|
|
276
|
+
// Frame
|
|
277
|
+
clipsContent?: boolean
|
|
278
|
+
frameMaskDisabled?: boolean
|
|
279
|
+
// Vector
|
|
280
|
+
vectorData?: unknown
|
|
281
|
+
fillGeometry?: Array<{ windingRule?: string; commandsBlob?: number }>
|
|
282
|
+
strokeGeometry?: Array<{ windingRule?: string; commandsBlob?: number }>
|
|
283
|
+
// Text
|
|
284
|
+
fontSize?: number
|
|
285
|
+
fontWeight?: number
|
|
286
|
+
fontName?: { family: string; style: string; postscript?: string }
|
|
287
|
+
textAlignHorizontal?: string
|
|
288
|
+
textAlignVertical?: string
|
|
289
|
+
textAutoResize?: string
|
|
290
|
+
textData?: {
|
|
291
|
+
characters: string
|
|
292
|
+
lines?: Array<{ lineType?: string; styleId?: number; indentationLevel?: number }>
|
|
293
|
+
characterStyleIDs?: number[]
|
|
294
|
+
styleOverrideTable?: NodeChange[]
|
|
295
|
+
}
|
|
296
|
+
derivedTextData?: {
|
|
297
|
+
layoutSize?: Vector
|
|
298
|
+
fontMetaData?: Array<{
|
|
299
|
+
key: { family: string; style: string; postscript?: string }
|
|
300
|
+
fontLineHeight: number
|
|
301
|
+
fontDigest?: Uint8Array | Record<string, number>
|
|
302
|
+
fontStyle?: string
|
|
303
|
+
fontWeight?: number
|
|
304
|
+
}>
|
|
305
|
+
truncationStartIndex?: number
|
|
306
|
+
truncatedHeight?: number
|
|
307
|
+
}
|
|
308
|
+
textUserLayoutVersion?: number
|
|
309
|
+
textDecoration?: string
|
|
310
|
+
lineHeight?: { value: number; units: string }
|
|
311
|
+
letterSpacing?: { value: number; units: string }
|
|
312
|
+
// Symbol/Instance
|
|
313
|
+
symbolData?: { symbolID: GUID }
|
|
314
|
+
// ComponentSet
|
|
315
|
+
isStateGroup?: boolean
|
|
316
|
+
stateGroupPropertyValueOrders?: Array<{ property: string; values: string[] }>
|
|
317
|
+
// Internal
|
|
318
|
+
internalOnly?: boolean
|
|
319
|
+
// Corners
|
|
320
|
+
rectangleTopLeftCornerRadius?: number
|
|
321
|
+
rectangleTopRightCornerRadius?: number
|
|
322
|
+
rectangleBottomLeftCornerRadius?: number
|
|
323
|
+
rectangleBottomRightCornerRadius?: number
|
|
324
|
+
rectangleCornerRadiiIndependent?: boolean
|
|
325
|
+
cornerSmoothing?: number
|
|
326
|
+
// Constraints
|
|
327
|
+
horizontalConstraint?: string
|
|
328
|
+
verticalConstraint?: string
|
|
329
|
+
// Variables
|
|
330
|
+
variableData?: VariableDataEntry
|
|
331
|
+
variableConsumptionMap?: { entries?: VariableConsumptionEntry[] }
|
|
332
|
+
variableSetModes?: Array<{ id: GUID; name: string; sortPosition?: string }>
|
|
333
|
+
variableSetID?: { guid: GUID }
|
|
334
|
+
variableResolvedType?: string
|
|
335
|
+
variableDataValues?: { entries?: VariableDataValuesEntry[] }
|
|
336
|
+
variableScopes?: string[]
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export interface FigmaMessage {
|
|
340
|
+
type: string
|
|
341
|
+
sessionID?: number
|
|
342
|
+
ackID?: number
|
|
343
|
+
reconnectSequenceNumber?: number
|
|
344
|
+
nodeChanges?: NodeChange[]
|
|
345
|
+
blobs?: Array<{ bytes: Uint8Array }>
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Create a NODE_CHANGES message
|
|
350
|
+
*/
|
|
351
|
+
export function createNodeChangesMessage(
|
|
352
|
+
sessionID: number,
|
|
353
|
+
reconnectSequenceNumber: number,
|
|
354
|
+
nodeChanges: NodeChange[],
|
|
355
|
+
ackID = 1
|
|
356
|
+
): FigmaMessage {
|
|
357
|
+
return {
|
|
358
|
+
type: 'NODE_CHANGES',
|
|
359
|
+
sessionID,
|
|
360
|
+
ackID,
|
|
361
|
+
reconnectSequenceNumber,
|
|
362
|
+
nodeChanges
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Create a node change for a new shape
|
|
368
|
+
*/
|
|
369
|
+
export function createNodeChange(opts: {
|
|
370
|
+
sessionID: number
|
|
371
|
+
localID: number
|
|
372
|
+
parentSessionID: number
|
|
373
|
+
parentLocalID: number
|
|
374
|
+
position?: string
|
|
375
|
+
type: string
|
|
376
|
+
name: string
|
|
377
|
+
x: number
|
|
378
|
+
y: number
|
|
379
|
+
width: number
|
|
380
|
+
height: number
|
|
381
|
+
fill?: Color | string
|
|
382
|
+
stroke?: Color | string
|
|
383
|
+
strokeWeight?: number
|
|
384
|
+
cornerRadius?: number
|
|
385
|
+
opacity?: number
|
|
386
|
+
}): NodeChange {
|
|
387
|
+
const change: NodeChange = {
|
|
388
|
+
guid: { sessionID: opts.sessionID, localID: opts.localID },
|
|
389
|
+
phase: 'CREATED',
|
|
390
|
+
parentIndex: {
|
|
391
|
+
guid: { sessionID: opts.parentSessionID, localID: opts.parentLocalID },
|
|
392
|
+
position: opts.position || '!'
|
|
393
|
+
},
|
|
394
|
+
type: opts.type,
|
|
395
|
+
name: opts.name,
|
|
396
|
+
visible: true,
|
|
397
|
+
opacity: opts.opacity ?? 1.0,
|
|
398
|
+
size: { x: opts.width, y: opts.height },
|
|
399
|
+
transform: {
|
|
400
|
+
m00: 1,
|
|
401
|
+
m01: 0,
|
|
402
|
+
m02: opts.x,
|
|
403
|
+
m10: 0,
|
|
404
|
+
m11: 1,
|
|
405
|
+
m12: opts.y
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (opts.fill) {
|
|
410
|
+
const color = typeof opts.fill === 'string' ? parseColor(opts.fill) : opts.fill
|
|
411
|
+
change.fillPaints = [
|
|
412
|
+
{
|
|
413
|
+
type: 'SOLID',
|
|
414
|
+
color,
|
|
415
|
+
opacity: 1.0,
|
|
416
|
+
visible: true,
|
|
417
|
+
blendMode: 'NORMAL'
|
|
418
|
+
}
|
|
419
|
+
]
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (opts.stroke) {
|
|
423
|
+
const color = typeof opts.stroke === 'string' ? parseColor(opts.stroke) : opts.stroke
|
|
424
|
+
change.strokePaints = [
|
|
425
|
+
{
|
|
426
|
+
type: 'SOLID',
|
|
427
|
+
color,
|
|
428
|
+
opacity: 1.0,
|
|
429
|
+
visible: true,
|
|
430
|
+
blendMode: 'NORMAL'
|
|
431
|
+
}
|
|
432
|
+
]
|
|
433
|
+
change.strokeWeight = opts.strokeWeight ?? 1
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (opts.cornerRadius !== undefined) {
|
|
437
|
+
change.cornerRadius = opts.cornerRadius
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return change
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Encode a varint (variable-length integer)
|
|
445
|
+
*/
|
|
446
|
+
function encodeVarint(value: number): number[] {
|
|
447
|
+
const bytes: number[] = []
|
|
448
|
+
while (value > 0x7f) {
|
|
449
|
+
bytes.push((value & 0x7f) | 0x80)
|
|
450
|
+
value >>>= 7
|
|
451
|
+
}
|
|
452
|
+
bytes.push(value)
|
|
453
|
+
return bytes
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Encode a Paint with optional variable binding
|
|
458
|
+
*
|
|
459
|
+
* Figma's variable binding format (discovered via WS traffic analysis):
|
|
460
|
+
* - Field 21 = 1 (binding type)
|
|
461
|
+
* - Field 4 = 1 (flag)
|
|
462
|
+
* - Raw sessionID varint (no field number)
|
|
463
|
+
* - Raw localID varint (no field number)
|
|
464
|
+
* - Terminators: 00 00 02 03 03 04 00 00
|
|
465
|
+
*/
|
|
466
|
+
export function encodePaintWithVariableBinding(
|
|
467
|
+
paint: Paint,
|
|
468
|
+
variableSessionID: number,
|
|
469
|
+
variableLocalID: number
|
|
470
|
+
): Uint8Array {
|
|
471
|
+
if (!compiledSchema) {
|
|
472
|
+
throw new Error('Codec not initialized. Call initCodec() first.')
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const { colorVariableBinding: _, ...basePaint } = paint
|
|
476
|
+
|
|
477
|
+
const baseBytes = compiledSchema.encodePaint(basePaint)
|
|
478
|
+
const baseArray = Array.from(baseBytes)
|
|
479
|
+
|
|
480
|
+
// Remove trailing 00
|
|
481
|
+
if (baseArray[baseArray.length - 1] === 0) {
|
|
482
|
+
baseArray.pop()
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Add variable binding in Figma's exact format:
|
|
486
|
+
// Field 21 (0x15) = 1 (binding type)
|
|
487
|
+
baseArray.push(0x15, 0x01)
|
|
488
|
+
// Field 4 = 1 (flag)
|
|
489
|
+
baseArray.push(0x04, 0x01)
|
|
490
|
+
// Raw varints: sessionID, localID (no field numbers!)
|
|
491
|
+
baseArray.push(...encodeVarint(variableSessionID))
|
|
492
|
+
baseArray.push(...encodeVarint(variableLocalID))
|
|
493
|
+
// Terminators observed in Figma traffic
|
|
494
|
+
baseArray.push(0x00, 0x00, 0x02, 0x03, 0x03, 0x04)
|
|
495
|
+
// Final terminators
|
|
496
|
+
baseArray.push(0x00, 0x00)
|
|
497
|
+
|
|
498
|
+
return new Uint8Array(baseArray)
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Parse a variable ID string (e.g., "VariableID:38448:122296")
|
|
503
|
+
* Returns sessionID and localID
|
|
504
|
+
*/
|
|
505
|
+
export function parseVariableId(variableId: string): GUID | null {
|
|
506
|
+
const match = variableId.match(/VariableID:(\d+):(\d+)/)
|
|
507
|
+
if (!match) return null
|
|
508
|
+
return {
|
|
509
|
+
sessionID: parseInt(match[1] ?? '0', 10),
|
|
510
|
+
localID: parseInt(match[2] ?? '0', 10)
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Encode a NodeChange with variable bindings in fillPaints and/or strokePaints
|
|
516
|
+
* This is needed because kiwi-schema cannot produce Figma's exact variable binding format
|
|
517
|
+
*/
|
|
518
|
+
export function encodeNodeChangeWithVariables(nodeChange: NodeChange): Uint8Array {
|
|
519
|
+
if (!compiledSchema) {
|
|
520
|
+
throw new Error('Codec not initialized. Call initCodec() first.')
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const hasFillBinding = nodeChange.fillPaints?.some((p) => p.colorVariableBinding)
|
|
524
|
+
const hasStrokeBinding = nodeChange.strokePaints?.some((p) => p.colorVariableBinding)
|
|
525
|
+
|
|
526
|
+
if (!hasFillBinding && !hasStrokeBinding) {
|
|
527
|
+
return compiledSchema.encodeNodeChange(nodeChange)
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Create a copy without variable bindings for base encoding
|
|
531
|
+
const cleanNodeChange = { ...nodeChange }
|
|
532
|
+
if (cleanNodeChange.fillPaints) {
|
|
533
|
+
cleanNodeChange.fillPaints = cleanNodeChange.fillPaints.map(
|
|
534
|
+
({ colorVariableBinding: _, ...rest }) => rest
|
|
535
|
+
)
|
|
536
|
+
}
|
|
537
|
+
if (cleanNodeChange.strokePaints) {
|
|
538
|
+
cleanNodeChange.strokePaints = cleanNodeChange.strokePaints.map(
|
|
539
|
+
({ colorVariableBinding: _, ...rest }) => rest
|
|
540
|
+
)
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// Encode clean version
|
|
544
|
+
const baseBytes = compiledSchema.encodeNodeChange(cleanNodeChange)
|
|
545
|
+
let hex = Buffer.from(baseBytes).toString('hex')
|
|
546
|
+
|
|
547
|
+
// Inject fill variable binding (field 38 = 0x26)
|
|
548
|
+
const fillBinding = nodeChange.fillPaints?.[0]?.colorVariableBinding
|
|
549
|
+
if (hasFillBinding && fillBinding) {
|
|
550
|
+
hex = injectVariableBinding(hex, '2601', fillBinding)
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// Inject stroke variable binding (field 39 = 0x27)
|
|
554
|
+
const strokeBinding = nodeChange.strokePaints?.[0]?.colorVariableBinding
|
|
555
|
+
if (hasStrokeBinding && strokeBinding) {
|
|
556
|
+
hex = injectVariableBinding(hex, '2701', strokeBinding)
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
return new Uint8Array(hex.match(/.{2}/g)?.map((b) => parseInt(b, 16)) ?? [])
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Inject variable binding into a paint at the specified marker
|
|
564
|
+
*/
|
|
565
|
+
function injectVariableBinding(
|
|
566
|
+
hex: string,
|
|
567
|
+
marker: string,
|
|
568
|
+
binding: { variableID: GUID }
|
|
569
|
+
): string {
|
|
570
|
+
const markerIdx = hex.indexOf(marker)
|
|
571
|
+
if (markerIdx === -1) return hex
|
|
572
|
+
|
|
573
|
+
// Find visible=true pattern (04 01) after the marker
|
|
574
|
+
const visiblePattern = '0401'
|
|
575
|
+
let patternIdx = hex.indexOf(visiblePattern, markerIdx)
|
|
576
|
+
if (patternIdx === -1) return hex
|
|
577
|
+
|
|
578
|
+
// Move past 0401 to find where to insert
|
|
579
|
+
let insertPoint = patternIdx + visiblePattern.length
|
|
580
|
+
|
|
581
|
+
// Check if blendMode follows (05 01)
|
|
582
|
+
if (hex.slice(insertPoint, insertPoint + 4) === '0501') {
|
|
583
|
+
insertPoint += 4
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// Build variable binding bytes
|
|
587
|
+
const varBytes = [
|
|
588
|
+
0x15,
|
|
589
|
+
0x01, // Field 21 (variableBinding) = 1
|
|
590
|
+
0x04,
|
|
591
|
+
0x01, // Nested field 4 = 1
|
|
592
|
+
...encodeVarint(binding.variableID.sessionID),
|
|
593
|
+
...encodeVarint(binding.variableID.localID),
|
|
594
|
+
0x00,
|
|
595
|
+
0x00,
|
|
596
|
+
0x02,
|
|
597
|
+
0x03,
|
|
598
|
+
0x03,
|
|
599
|
+
0x04,
|
|
600
|
+
0x00,
|
|
601
|
+
0x00 // Terminators
|
|
602
|
+
]
|
|
603
|
+
const varHex = Buffer.from(varBytes).toString('hex')
|
|
604
|
+
|
|
605
|
+
const beforeVar = hex.slice(0, insertPoint)
|
|
606
|
+
// Skip original paint terminator (00) - our varHex includes terminators
|
|
607
|
+
let afterIdx = insertPoint
|
|
608
|
+
if (hex.slice(afterIdx, afterIdx + 2) === '00') {
|
|
609
|
+
afterIdx += 2
|
|
610
|
+
}
|
|
611
|
+
const afterVar = hex.slice(afterIdx)
|
|
612
|
+
|
|
613
|
+
return beforeVar + varHex + afterVar
|
|
614
|
+
}
|