@sarjallab09/figma-intelligence 1.1.0 → 1.2.1
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/README.md +69 -36
- package/dist/bin/cli.js +2 -0
- package/dist/design-bridge/bridge.js +2 -0
- package/dist/figma-bridge-plugin/bridge-relay.js +2 -0
- package/dist/figma-bridge-plugin/code.js +1 -0
- package/{figma-bridge-plugin → dist/figma-bridge-plugin}/package-lock.json +0 -3
- package/dist/figma-bridge-plugin/ui.html +4970 -0
- package/dist/figma-intelligence-layer/dist/index.js +2 -0
- package/dist/scripts/clean-existing-chunks.js +2 -0
- package/dist/scripts/connect-ai-tool.js +2 -0
- package/dist/scripts/convert-hub-pdfs.js +2 -0
- package/dist/scripts/figma-mcp-status.js +2 -0
- package/dist/scripts/register-codex-mcp.js +2 -0
- package/dist/scripts/test-copilot-chat.js +2 -0
- package/package.json +11 -8
- package/bin/cli.js +0 -859
- package/design-bridge/bridge.js +0 -196
- package/design-bridge/lib/assets.js +0 -367
- package/design-bridge/lib/prompt.js +0 -85
- package/design-bridge/lib/server.js +0 -66
- package/design-bridge/lib/stitch.js +0 -37
- package/design-bridge/lib/tokens.js +0 -82
- package/design-bridge/package-lock.json +0 -579
- package/figma-bridge-plugin/README.md +0 -97
- package/figma-bridge-plugin/anthropic-chat-runner.js +0 -192
- package/figma-bridge-plugin/bridge-relay.js +0 -2505
- package/figma-bridge-plugin/chat-runner.js +0 -485
- package/figma-bridge-plugin/code.js +0 -1534
- package/figma-bridge-plugin/codex-runner.js +0 -505
- package/figma-bridge-plugin/component-schemas.js +0 -110
- package/figma-bridge-plugin/content-context.js +0 -869
- package/figma-bridge-plugin/create-button.js +0 -216
- package/figma-bridge-plugin/gemini-cli-runner.js +0 -291
- package/figma-bridge-plugin/gemini-runner.js +0 -187
- package/figma-bridge-plugin/html-to-figma.js +0 -927
- package/figma-bridge-plugin/knowledge-hub/.gitkeep +0 -0
- package/figma-bridge-plugin/knowledge-hub/uspec-references/anatomy-spec.md +0 -159
- package/figma-bridge-plugin/knowledge-hub/uspec-references/api-spec.md +0 -162
- package/figma-bridge-plugin/knowledge-hub/uspec-references/color-spec.md +0 -148
- package/figma-bridge-plugin/knowledge-hub/uspec-references/full-spec-template.md +0 -314
- package/figma-bridge-plugin/knowledge-hub/uspec-references/property-spec.md +0 -175
- package/figma-bridge-plugin/knowledge-hub/uspec-references/screen-reader-spec.md +0 -180
- package/figma-bridge-plugin/knowledge-hub/uspec-references/structure-spec.md +0 -165
- package/figma-bridge-plugin/perplexity-runner.js +0 -188
- package/figma-bridge-plugin/references/SKILL.md +0 -178
- package/figma-bridge-plugin/references/anatomy-spec.md +0 -159
- package/figma-bridge-plugin/references/api-spec.md +0 -162
- package/figma-bridge-plugin/references/color-spec.md +0 -148
- package/figma-bridge-plugin/references/full-spec-template.md +0 -314
- package/figma-bridge-plugin/references/property-spec.md +0 -175
- package/figma-bridge-plugin/references/screen-reader-spec.md +0 -180
- package/figma-bridge-plugin/references/structure-spec.md +0 -165
- package/figma-bridge-plugin/shared-prompt-config.js +0 -645
- package/figma-bridge-plugin/spec-helpers/build-table.js +0 -269
- package/figma-bridge-plugin/spec-helpers/classify-elements.js +0 -189
- package/figma-bridge-plugin/spec-helpers/index.js +0 -35
- package/figma-bridge-plugin/spec-helpers/parse-figma-link.js +0 -49
- package/figma-bridge-plugin/spec-helpers/position-markers.js +0 -158
- package/figma-bridge-plugin/stitch-auth.js +0 -322
- package/figma-bridge-plugin/stitch-runner.js +0 -1427
- package/figma-bridge-plugin/token-resolver.js +0 -107
- package/figma-bridge-plugin/ui.html +0 -4542
- package/figma-intelligence-layer/.env.example +0 -39
- package/figma-intelligence-layer/docs/local-image-generation.md +0 -60
- package/figma-intelligence-layer/examples/comfyui-workflow-template.example.json +0 -101
- package/figma-intelligence-layer/jest.config.js +0 -14
- package/figma-intelligence-layer/mcp-config.json +0 -19
- package/figma-intelligence-layer/package-lock.json +0 -5892
- package/figma-intelligence-layer/scripts/setup-comfyui-local.sh +0 -67
- package/figma-intelligence-layer/scripts/start-comfyui.sh +0 -33
- package/figma-intelligence-layer/src/index.ts +0 -2233
- package/figma-intelligence-layer/src/shared/auto-layout-validator.ts +0 -404
- package/figma-intelligence-layer/src/shared/cache.ts +0 -187
- package/figma-intelligence-layer/src/shared/color-operations.ts +0 -533
- package/figma-intelligence-layer/src/shared/color-utils.ts +0 -138
- package/figma-intelligence-layer/src/shared/component-script-builder.ts +0 -413
- package/figma-intelligence-layer/src/shared/component-templates.ts +0 -2767
- package/figma-intelligence-layer/src/shared/concept-taxonomy.ts +0 -694
- package/figma-intelligence-layer/src/shared/decision-log.ts +0 -128
- package/figma-intelligence-layer/src/shared/design-system-context.ts +0 -568
- package/figma-intelligence-layer/src/shared/design-system-intelligence.ts +0 -131
- package/figma-intelligence-layer/src/shared/design-system-matcher.ts +0 -184
- package/figma-intelligence-layer/src/shared/design-system-normalizers.ts +0 -196
- package/figma-intelligence-layer/src/shared/design-system-tokens.ts +0 -295
- package/figma-intelligence-layer/src/shared/dtcg-validator.ts +0 -530
- package/figma-intelligence-layer/src/shared/enrichment-pipeline.ts +0 -671
- package/figma-intelligence-layer/src/shared/figma-bridge.ts +0 -1418
- package/figma-intelligence-layer/src/shared/font-config.ts +0 -126
- package/figma-intelligence-layer/src/shared/icon-catalog.ts +0 -360
- package/figma-intelligence-layer/src/shared/icon-fetch.ts +0 -80
- package/figma-intelligence-layer/src/shared/prototype-script-builder.ts +0 -162
- package/figma-intelligence-layer/src/shared/response-compression.ts +0 -440
- package/figma-intelligence-layer/src/shared/semantic-token-catalog.ts +0 -324
- package/figma-intelligence-layer/src/shared/token-binder.ts +0 -505
- package/figma-intelligence-layer/src/shared/token-math.ts +0 -427
- package/figma-intelligence-layer/src/shared/token-naming.ts +0 -468
- package/figma-intelligence-layer/src/shared/token-utils.ts +0 -420
- package/figma-intelligence-layer/src/shared/types.ts +0 -346
- package/figma-intelligence-layer/src/shared/typography-presets.ts +0 -94
- package/figma-intelligence-layer/src/shared/unsplash.ts +0 -165
- package/figma-intelligence-layer/src/shared/vision-client.ts +0 -607
- package/figma-intelligence-layer/src/shared/vision-provider-anthropic.ts +0 -334
- package/figma-intelligence-layer/src/shared/vision-provider-openai.ts +0 -446
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/a11y-annotate-handler.ts +0 -782
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/a11y-annotate-renderer.ts +0 -496
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/a11y-annotation-kit.ts +0 -230
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/colorblind-sim.ts +0 -66
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/index.ts +0 -810
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/keyboard-sr-order-analyzer.ts +0 -1191
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/keyboard-sr-order-figma-page.ts +0 -1346
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/keyboard-sr-order-handler.ts +0 -148
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/vpat-figma-page.ts +0 -499
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/vpat-report.ts +0 -910
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/wcag-checker.ts +0 -989
- package/figma-intelligence-layer/src/tools/phase1-vision/a11y-audit/wcag-criteria.ts +0 -1160
- package/figma-intelligence-layer/src/tools/phase1-vision/design-from-ref/index.ts +0 -424
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/component-recognizer.ts +0 -38
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/ds-matcher.ts +0 -111
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/font-matcher.ts +0 -114
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/icon-resolver.ts +0 -103
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/index.ts +0 -1060
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/layout-segmenter.ts +0 -18
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/token-inferencer.ts +0 -39
- package/figma-intelligence-layer/src/tools/phase1-vision/screen-cloner/vision-pipeline.ts +0 -58
- package/figma-intelligence-layer/src/tools/phase1-vision/sketch-to-design/index.ts +0 -298
- package/figma-intelligence-layer/src/tools/phase1-vision/visual-audit/index.ts +0 -197
- package/figma-intelligence-layer/src/tools/phase2-accuracy/component-audit/index.ts +0 -494
- package/figma-intelligence-layer/src/tools/phase2-accuracy/intent-translator/index.ts +0 -356
- package/figma-intelligence-layer/src/tools/phase2-accuracy/layout-intelligence/container-patterns.ts +0 -123
- package/figma-intelligence-layer/src/tools/phase2-accuracy/layout-intelligence/index.ts +0 -663
- package/figma-intelligence-layer/src/tools/phase2-accuracy/lint-rules/built-in-rules.yaml +0 -56
- package/figma-intelligence-layer/src/tools/phase2-accuracy/lint-rules/index.ts +0 -614
- package/figma-intelligence-layer/src/tools/phase2-accuracy/lint-rules/rule-engine.ts +0 -113
- package/figma-intelligence-layer/src/tools/phase2-accuracy/theme-generator/color-theory.ts +0 -178
- package/figma-intelligence-layer/src/tools/phase2-accuracy/theme-generator/index.ts +0 -470
- package/figma-intelligence-layer/src/tools/phase2-accuracy/variant-expander/index.ts +0 -429
- package/figma-intelligence-layer/src/tools/phase2-accuracy/variant-expander/token-override-maps.ts +0 -226
- package/figma-intelligence-layer/src/tools/phase3-generation/ai-image-insert/index.ts +0 -535
- package/figma-intelligence-layer/src/tools/phase3-generation/component-archaeologist/index.ts +0 -660
- package/figma-intelligence-layer/src/tools/phase3-generation/component-archaeologist/pattern-fingerprints.ts +0 -209
- package/figma-intelligence-layer/src/tools/phase3-generation/composition-builder/index.ts +0 -540
- package/figma-intelligence-layer/src/tools/phase3-generation/figma-animated-build.ts +0 -391
- package/figma-intelligence-layer/src/tools/phase3-generation/page-architect/index.ts +0 -2019
- package/figma-intelligence-layer/src/tools/phase3-generation/page-architect/screen-templates.ts +0 -131
- package/figma-intelligence-layer/src/tools/phase3-generation/prototype-map/index.ts +0 -381
- package/figma-intelligence-layer/src/tools/phase3-generation/prototype-wire/index.ts +0 -565
- package/figma-intelligence-layer/src/tools/phase3-generation/swarm-build/index.ts +0 -764
- package/figma-intelligence-layer/src/tools/phase3-generation/system-drift/index.ts +0 -535
- package/figma-intelligence-layer/src/tools/phase3-generation/unsplash-search/index.ts +0 -84
- package/figma-intelligence-layer/src/tools/phase3-generation/url-to-frame/index.ts +0 -401
- package/figma-intelligence-layer/src/tools/phase4-sync/animation-specifier/code-generators/css-animations.ts +0 -68
- package/figma-intelligence-layer/src/tools/phase4-sync/animation-specifier/code-generators/framer-motion.ts +0 -78
- package/figma-intelligence-layer/src/tools/phase4-sync/animation-specifier/code-generators/swift-animations.ts +0 -93
- package/figma-intelligence-layer/src/tools/phase4-sync/animation-specifier/index.ts +0 -596
- package/figma-intelligence-layer/src/tools/phase4-sync/ci-check/index.ts +0 -462
- package/figma-intelligence-layer/src/tools/phase4-sync/export-tokens/index.ts +0 -1470
- package/figma-intelligence-layer/src/tools/phase4-sync/generate-component-code/index.ts +0 -829
- package/figma-intelligence-layer/src/tools/phase4-sync/handoff-spec/index.ts +0 -702
- package/figma-intelligence-layer/src/tools/phase4-sync/icon-library-sync/index.ts +0 -483
- package/figma-intelligence-layer/src/tools/phase4-sync/sync-from-code/index.ts +0 -501
- package/figma-intelligence-layer/src/tools/phase4-sync/sync-from-code/storybook-parser.ts +0 -106
- package/figma-intelligence-layer/src/tools/phase4-sync/watch-docs/index.ts +0 -676
- package/figma-intelligence-layer/src/tools/phase4-sync/webhook-listener/index.ts +0 -560
- package/figma-intelligence-layer/src/tools/phase5-governance/apg-doc/index.ts +0 -1043
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/component-detection.ts +0 -620
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/anatomy.ts +0 -331
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/color-tokens.ts +0 -77
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/properties.ts +0 -54
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/snapshot.ts +0 -287
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/spacing.ts +0 -71
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/states.ts +0 -43
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/extractors/typography.ts +0 -71
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/index.ts +0 -221
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/_default.ts +0 -166
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/accordion.ts +0 -232
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/alert.ts +0 -234
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/avatar-group.ts +0 -270
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/avatar.ts +0 -249
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/badge.ts +0 -231
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/banner.ts +0 -293
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/breadcrumb.ts +0 -240
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/button.ts +0 -243
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/calendar.ts +0 -307
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/card.ts +0 -143
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/checkbox.ts +0 -227
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/chip.ts +0 -233
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/combobox.ts +0 -282
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/datepicker.ts +0 -276
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/divider.ts +0 -223
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/drawer.ts +0 -255
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/dropdown-menu.ts +0 -289
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/empty-state.ts +0 -261
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/file-uploader.ts +0 -290
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/form.ts +0 -265
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/grid.ts +0 -238
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/icon.ts +0 -255
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/index.ts +0 -128
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/inline-edit.ts +0 -286
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/inline-message.ts +0 -255
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/input.ts +0 -330
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/link.ts +0 -247
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/list.ts +0 -250
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/menu.ts +0 -247
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/modal.ts +0 -144
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/navbar.ts +0 -264
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/navigation.ts +0 -251
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/number-input.ts +0 -261
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/pagination.ts +0 -248
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/popover.ts +0 -270
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/progress.ts +0 -251
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/radio.ts +0 -142
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/range-slider.ts +0 -282
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/rating.ts +0 -250
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/search.ts +0 -258
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/segmented-control.ts +0 -265
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/select.ts +0 -319
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/skeleton.ts +0 -256
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/slider.ts +0 -232
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/spinner.ts +0 -239
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/status-dot.ts +0 -252
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/stepper.ts +0 -270
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/table.ts +0 -244
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/tabs.ts +0 -143
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/tag.ts +0 -243
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/textarea.ts +0 -259
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/time-picker.ts +0 -293
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/toast.ts +0 -144
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/toggle.ts +0 -289
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/toolbar.ts +0 -267
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/tooltip.ts +0 -232
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/treeview.ts +0 -257
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/knowledge/typography.ts +0 -319
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/legacy-compat.ts +0 -121
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/renderers/anatomy-diagram.ts +0 -430
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/renderers/figma-page.ts +0 -312
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/renderers/json.ts +0 -129
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/renderers/markdown.ts +0 -78
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/renderers/visual-doc.ts +0 -2333
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/accessibility.ts +0 -100
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/anatomy.ts +0 -32
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/color-tokens.ts +0 -59
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/content-guidance.ts +0 -18
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/design-tokens.ts +0 -53
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/interaction-rules.ts +0 -19
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/overview.ts +0 -91
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/properties-api.ts +0 -71
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/qa-criteria.ts +0 -19
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/related-components.ts +0 -110
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/responsive.ts +0 -19
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/size-specs.ts +0 -67
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/spacing-structure.ts +0 -58
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/state-specs.ts +0 -79
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/states.ts +0 -50
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/type-hierarchy.ts +0 -33
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/typography.ts +0 -55
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/usage-guidelines.ts +0 -73
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/sections/variants.ts +0 -81
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec/types.ts +0 -409
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec-sheet/index.ts +0 -198
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec-sheet/renderer.ts +0 -701
- package/figma-intelligence-layer/src/tools/phase5-governance/component-spec-sheet/types.ts +0 -88
- package/figma-intelligence-layer/src/tools/phase5-governance/decision-log/index.ts +0 -135
- package/figma-intelligence-layer/src/tools/phase5-governance/design-decision-log/index.ts +0 -491
- package/figma-intelligence-layer/src/tools/phase5-governance/ds-primitives/index.ts +0 -416
- package/figma-intelligence-layer/src/tools/phase5-governance/ds-scaffolder/index.ts +0 -722
- package/figma-intelligence-layer/src/tools/phase5-governance/ds-variables/index.ts +0 -449
- package/figma-intelligence-layer/src/tools/phase5-governance/health-report/index.ts +0 -393
- package/figma-intelligence-layer/src/tools/phase5-governance/taxonomy-docs/index.ts +0 -406
- package/figma-intelligence-layer/src/tools/phase5-governance/taxonomy-docs/renderers/figma-page.ts +0 -292
- package/figma-intelligence-layer/src/tools/phase5-governance/taxonomy-docs/renderers/json.ts +0 -24
- package/figma-intelligence-layer/src/tools/phase5-governance/taxonomy-docs/renderers/markdown.ts +0 -172
- package/figma-intelligence-layer/src/tools/phase5-governance/taxonomy-docs/renderers/naming-guide.ts +0 -409
- package/figma-intelligence-layer/src/tools/phase5-governance/token-analytics/index.ts +0 -594
- package/figma-intelligence-layer/src/tools/phase5-governance/token-docs/index.ts +0 -710
- package/figma-intelligence-layer/src/tools/phase5-governance/token-migrate/index.ts +0 -458
- package/figma-intelligence-layer/src/tools/phase5-governance/token-naming/index.ts +0 -134
- package/figma-intelligence-layer/tests/apg-doc.test.ts +0 -101
- package/figma-intelligence-layer/tests/design-system-context.test.ts +0 -152
- package/figma-intelligence-layer/tests/design-system-matcher.test.ts +0 -144
- package/figma-intelligence-layer/tests/figma-bridge.test.ts +0 -83
- package/figma-intelligence-layer/tests/generate-image-and-insert.test.ts +0 -56
- package/figma-intelligence-layer/tests/screen-cloner-regression.test.ts +0 -69
- package/figma-intelligence-layer/tests/smoke.test.ts +0 -174
- package/figma-intelligence-layer/tests/spec-generator.test.ts +0 -127
- package/figma-intelligence-layer/tests/token-migrate.test.ts +0 -21
- package/figma-intelligence-layer/tests/token-naming.test.ts +0 -30
- package/figma-intelligence-layer/tsconfig.json +0 -19
- package/scripts/clean-existing-chunks.js +0 -179
- package/scripts/connect-ai-tool.js +0 -490
- package/scripts/convert-hub-pdfs.js +0 -425
- package/scripts/figma-mcp-status.js +0 -349
- package/scripts/register-codex-mcp.js +0 -96
- /package/{design-bridge → dist/design-bridge}/.env.example +0 -0
- /package/{design-bridge → dist/design-bridge}/package.json +0 -0
- /package/{figma-bridge-plugin → dist/figma-bridge-plugin}/manifest.json +0 -0
- /package/{figma-bridge-plugin → dist/figma-bridge-plugin}/package.json +0 -0
- /package/{figma-intelligence-layer → dist/figma-intelligence-layer}/package.json +0 -0
|
@@ -1,927 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* html-to-figma.js — Converts HTML/CSS output from Google Stitch
|
|
3
|
-
* into a single Figma Plugin API `execute` code string that creates
|
|
4
|
-
* the entire frame tree with correct auto-layout properties.
|
|
5
|
-
*
|
|
6
|
-
* Uses htmlparser2 (already in node_modules) for robust HTML parsing.
|
|
7
|
-
*
|
|
8
|
-
* Key auto-layout rules enforced:
|
|
9
|
-
* - ONE root frame per task (FIXED/FIXED sizing)
|
|
10
|
-
* - Every frame has layoutSizingHorizontal + layoutSizingVertical
|
|
11
|
-
* - Children of VERTICAL parent: H=FILL, V=HUG
|
|
12
|
-
* - Children of HORIZONTAL parent: V=FILL, H=HUG (or FILL if flex-1)
|
|
13
|
-
* - Structural wrappers: fills=[] (transparent)
|
|
14
|
-
* - Property order: layoutMode → sizing → padding → spacing → fills
|
|
15
|
-
* - appendChild() BEFORE setting FILL on children
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
const { parseDocument } = require("htmlparser2");
|
|
19
|
-
|
|
20
|
-
// ── Color helpers ──────────────────────────────────────────────────────────────
|
|
21
|
-
|
|
22
|
-
function parseColor(str) {
|
|
23
|
-
if (!str || str === "transparent") return null;
|
|
24
|
-
str = str.trim().toLowerCase();
|
|
25
|
-
|
|
26
|
-
const named = {
|
|
27
|
-
white: "ffffff", black: "000000", red: "ff0000", green: "008000",
|
|
28
|
-
blue: "0000ff", gray: "808080", grey: "808080", yellow: "ffff00",
|
|
29
|
-
orange: "ffa500", purple: "800080", pink: "ffc0cb", cyan: "00ffff",
|
|
30
|
-
navy: "000080", teal: "008080", maroon: "800000", olive: "808000",
|
|
31
|
-
silver: "c0c0c0", lime: "00ff00", aqua: "00ffff", fuchsia: "ff00ff",
|
|
32
|
-
indigo: "4b0082", coral: "ff7f50", salmon: "fa8072", tomato: "ff6347",
|
|
33
|
-
wheat: "f5deb3", ivory: "fffff0", linen: "faf0e6", beige: "f5f5dc",
|
|
34
|
-
snow: "fffafa", honeydew: "f0fff0", azure: "f0ffff", ghostwhite: "f8f8ff",
|
|
35
|
-
whitesmoke: "f5f5f5", aliceblue: "f0f8ff", lavender: "e6e6fa",
|
|
36
|
-
};
|
|
37
|
-
if (named[str]) str = "#" + named[str];
|
|
38
|
-
|
|
39
|
-
const hexMatch = str.match(/^#([0-9a-f]{3,8})$/);
|
|
40
|
-
if (hexMatch) {
|
|
41
|
-
let hex = hexMatch[1];
|
|
42
|
-
if (hex.length === 3) hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
|
|
43
|
-
if (hex.length === 4) hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]+hex[3]+hex[3];
|
|
44
|
-
const r = parseInt(hex.slice(0,2), 16) / 255;
|
|
45
|
-
const g = parseInt(hex.slice(2,4), 16) / 255;
|
|
46
|
-
const b = parseInt(hex.slice(4,6), 16) / 255;
|
|
47
|
-
const a = hex.length === 8 ? parseInt(hex.slice(6,8), 16) / 255 : 1;
|
|
48
|
-
return { r, g, b, a };
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const rgbMatch = str.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*([\d.]+))?\s*\)/);
|
|
52
|
-
if (rgbMatch) {
|
|
53
|
-
return {
|
|
54
|
-
r: parseInt(rgbMatch[1]) / 255,
|
|
55
|
-
g: parseInt(rgbMatch[2]) / 255,
|
|
56
|
-
b: parseInt(rgbMatch[3]) / 255,
|
|
57
|
-
a: rgbMatch[4] !== undefined ? parseFloat(rgbMatch[4]) : 1,
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function colorToFigma(c) {
|
|
65
|
-
if (!c) return "{r:1,g:1,b:1}";
|
|
66
|
-
return `{r:${c.r.toFixed(3)},g:${c.g.toFixed(3)},b:${c.b.toFixed(3)}}`;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function rgbaToHex(c) {
|
|
70
|
-
if (!c) return "#FFFFFF";
|
|
71
|
-
const r = Math.round(c.r * 255).toString(16).padStart(2, "0");
|
|
72
|
-
const g = Math.round(c.g * 255).toString(16).padStart(2, "0");
|
|
73
|
-
const b = Math.round(c.b * 255).toString(16).padStart(2, "0");
|
|
74
|
-
return `#${r}${g}${b}`.toUpperCase();
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// ── CSS parsing helpers ────────────────────────────────────────────────────────
|
|
78
|
-
|
|
79
|
-
function parseInlineStyle(styleStr) {
|
|
80
|
-
const styles = {};
|
|
81
|
-
if (!styleStr) return styles;
|
|
82
|
-
for (const decl of styleStr.split(";")) {
|
|
83
|
-
const colon = decl.indexOf(":");
|
|
84
|
-
if (colon < 0) continue;
|
|
85
|
-
const prop = decl.slice(0, colon).trim().toLowerCase();
|
|
86
|
-
const val = decl.slice(colon + 1).trim();
|
|
87
|
-
if (prop && val) styles[prop] = val;
|
|
88
|
-
}
|
|
89
|
-
return styles;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function parseStyleBlock(css) {
|
|
93
|
-
const rules = new Map();
|
|
94
|
-
if (!css) return rules;
|
|
95
|
-
css = css.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
96
|
-
const ruleRegex = /([^{}]+)\{([^}]*)\}/g;
|
|
97
|
-
let m;
|
|
98
|
-
while ((m = ruleRegex.exec(css)) !== null) {
|
|
99
|
-
const selectors = m[1].trim().split(",").map(s => s.trim());
|
|
100
|
-
const styles = parseInlineStyle(m[2]);
|
|
101
|
-
for (const sel of selectors) {
|
|
102
|
-
const existing = rules.get(sel) || {};
|
|
103
|
-
rules.set(sel, { ...existing, ...styles });
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return rules;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// ── Tailwind CSS class → inline style resolver ────────────────────────────────
|
|
110
|
-
|
|
111
|
-
const TW_COLORS = {
|
|
112
|
-
"slate-50":"#f8fafc","slate-100":"#f1f5f9","slate-200":"#e2e8f0","slate-300":"#cbd5e1","slate-400":"#94a3b8","slate-500":"#64748b","slate-600":"#475569","slate-700":"#334155","slate-800":"#1e293b","slate-900":"#0f172a","slate-950":"#020617",
|
|
113
|
-
"gray-50":"#f9fafb","gray-100":"#f3f4f6","gray-200":"#e5e7eb","gray-300":"#d1d5db","gray-400":"#9ca3af","gray-500":"#6b7280","gray-600":"#4b5563","gray-700":"#374151","gray-800":"#1f2937","gray-900":"#111827","gray-950":"#030712",
|
|
114
|
-
"zinc-50":"#fafafa","zinc-100":"#f4f4f5","zinc-200":"#e4e4e7","zinc-300":"#d4d4d8","zinc-400":"#a1a1aa","zinc-500":"#71717a","zinc-600":"#52525b","zinc-700":"#3f3f46","zinc-800":"#27272a","zinc-900":"#18181b","zinc-950":"#09090b",
|
|
115
|
-
"neutral-50":"#fafafa","neutral-100":"#f5f5f5","neutral-200":"#e5e5e5","neutral-300":"#d4d4d4","neutral-400":"#a3a3a3","neutral-500":"#737373","neutral-600":"#525252","neutral-700":"#404040","neutral-800":"#262626","neutral-900":"#171717","neutral-950":"#0a0a0a",
|
|
116
|
-
"stone-50":"#fafaf9","stone-100":"#f5f5f4","stone-200":"#e7e5e4","stone-300":"#d6d3d1","stone-400":"#a8a29e","stone-500":"#78716c","stone-600":"#57534e","stone-700":"#44403c","stone-800":"#292524","stone-900":"#1c1917","stone-950":"#0c0a09",
|
|
117
|
-
"red-50":"#fef2f2","red-100":"#fee2e2","red-200":"#fecaca","red-300":"#fca5a5","red-400":"#f87171","red-500":"#ef4444","red-600":"#dc2626","red-700":"#b91c1c","red-800":"#991b1b","red-900":"#7f1d1d","red-950":"#450a0a",
|
|
118
|
-
"orange-50":"#fff7ed","orange-100":"#ffedd5","orange-200":"#fed7aa","orange-300":"#fdba74","orange-400":"#fb923c","orange-500":"#f97316","orange-600":"#ea580c","orange-700":"#c2410c","orange-800":"#9a3412","orange-900":"#7c2d12",
|
|
119
|
-
"amber-50":"#fffbeb","amber-100":"#fef3c7","amber-200":"#fde68a","amber-300":"#fcd34d","amber-400":"#fbbf24","amber-500":"#f59e0b","amber-600":"#d97706","amber-700":"#b45309","amber-800":"#92400e","amber-900":"#78350f",
|
|
120
|
-
"yellow-50":"#fefce8","yellow-100":"#fef9c3","yellow-200":"#fef08a","yellow-300":"#fde047","yellow-400":"#facc15","yellow-500":"#eab308","yellow-600":"#ca8a04","yellow-700":"#a16207","yellow-800":"#854d0e","yellow-900":"#713f12",
|
|
121
|
-
"lime-50":"#f7fee7","lime-100":"#ecfccb","lime-200":"#d9f99d","lime-300":"#bef264","lime-400":"#a3e635","lime-500":"#84cc16","lime-600":"#65a30d","lime-700":"#4d7c0f","lime-800":"#3f6212","lime-900":"#365314",
|
|
122
|
-
"green-50":"#f0fdf4","green-100":"#dcfce7","green-200":"#bbf7d0","green-300":"#86efac","green-400":"#4ade80","green-500":"#22c55e","green-600":"#16a34a","green-700":"#15803d","green-800":"#166534","green-900":"#14532d",
|
|
123
|
-
"emerald-50":"#ecfdf5","emerald-100":"#d1fae5","emerald-200":"#a7f3d0","emerald-300":"#6ee7b7","emerald-400":"#34d399","emerald-500":"#10b981","emerald-600":"#059669","emerald-700":"#047857","emerald-800":"#065f46","emerald-900":"#064e3b",
|
|
124
|
-
"teal-50":"#f0fdfa","teal-100":"#ccfbf1","teal-200":"#99f6e4","teal-300":"#5eead4","teal-400":"#2dd4bf","teal-500":"#14b8a6","teal-600":"#0d9488","teal-700":"#0f766e","teal-800":"#115e59","teal-900":"#134e4a",
|
|
125
|
-
"cyan-50":"#ecfeff","cyan-100":"#cffafe","cyan-200":"#a5f3fc","cyan-300":"#67e8f9","cyan-400":"#22d3ee","cyan-500":"#06b6d4","cyan-600":"#0891b2","cyan-700":"#0e7490","cyan-800":"#155e75","cyan-900":"#164e63",
|
|
126
|
-
"sky-50":"#f0f9ff","sky-100":"#e0f2fe","sky-200":"#bae6fd","sky-300":"#7dd3fc","sky-400":"#38bdf8","sky-500":"#0ea5e9","sky-600":"#0284c7","sky-700":"#0369a1","sky-800":"#075985","sky-900":"#0c4a6e",
|
|
127
|
-
"blue-50":"#eff6ff","blue-100":"#dbeafe","blue-200":"#bfdbfe","blue-300":"#93c5fd","blue-400":"#60a5fa","blue-500":"#3b82f6","blue-600":"#2563eb","blue-700":"#1d4ed8","blue-800":"#1e40af","blue-900":"#1e3a8a","blue-950":"#172554",
|
|
128
|
-
"indigo-50":"#eef2ff","indigo-100":"#e0e7ff","indigo-200":"#c7d2fe","indigo-300":"#a5b4fc","indigo-400":"#818cf8","indigo-500":"#6366f1","indigo-600":"#4f46e5","indigo-700":"#4338ca","indigo-800":"#3730a3","indigo-900":"#312e81",
|
|
129
|
-
"violet-50":"#f5f3ff","violet-100":"#ede9fe","violet-200":"#ddd6fe","violet-300":"#c4b5fd","violet-400":"#a78bfa","violet-500":"#8b5cf6","violet-600":"#7c3aed","violet-700":"#6d28d9","violet-800":"#5b21b6","violet-900":"#4c1d95",
|
|
130
|
-
"purple-50":"#faf5ff","purple-100":"#f3e8ff","purple-200":"#e9d5ff","purple-300":"#d8b4fe","purple-400":"#c084fc","purple-500":"#a855f7","purple-600":"#9333ea","purple-700":"#7e22ce","purple-800":"#6b21a8","purple-900":"#581c87",
|
|
131
|
-
"fuchsia-50":"#fdf4ff","fuchsia-100":"#fae8ff","fuchsia-200":"#f5d0fe","fuchsia-300":"#f0abfc","fuchsia-400":"#e879f9","fuchsia-500":"#d946ef","fuchsia-600":"#c026d3","fuchsia-700":"#a21caf","fuchsia-800":"#86198f","fuchsia-900":"#701a75",
|
|
132
|
-
"pink-50":"#fdf2f8","pink-100":"#fce7f3","pink-200":"#fbcfe8","pink-300":"#f9a8d4","pink-400":"#f472b6","pink-500":"#ec4899","pink-600":"#db2777","pink-700":"#be185d","pink-800":"#9d174d","pink-900":"#831843",
|
|
133
|
-
"rose-50":"#fff1f2","rose-100":"#ffe4e6","rose-200":"#fecdd3","rose-300":"#fda4af","rose-400":"#fb7185","rose-500":"#f43f5e","rose-600":"#e11d48","rose-700":"#be123c","rose-800":"#9f1239","rose-900":"#881337",
|
|
134
|
-
"white":"#ffffff","black":"#000000","transparent":"transparent",
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
const TW_SPACING = {
|
|
138
|
-
"0":0,"0.5":2,"1":4,"1.5":6,"2":8,"2.5":10,"3":12,"3.5":14,"4":16,"5":20,"6":24,"7":28,"8":32,"9":36,"10":40,"11":44,"12":48,"14":56,"16":64,"20":80,"24":96,"28":112,"32":128,"36":144,"40":160,"44":176,"48":192,"52":208,"56":224,"60":240,"64":256,"72":288,"80":320,"96":384,
|
|
139
|
-
"px":1,"full":"100%","screen":"100vh",
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
const TW_RADIUS = { "none":0,"sm":2,"":4,"md":6,"lg":8,"xl":12,"2xl":16,"3xl":24,"full":9999 };
|
|
143
|
-
|
|
144
|
-
const TW_FONT_SIZE = {
|
|
145
|
-
"xs":[12,16],"sm":[14,20],"base":[16,24],"lg":[18,28],"xl":[20,28],"2xl":[24,32],"3xl":[30,36],"4xl":[36,40],"5xl":[48,1],"6xl":[60,1],"7xl":[72,1],"8xl":[96,1],"9xl":[128,1],
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
const TW_FONT_WEIGHT = {
|
|
149
|
-
"thin":100,"extralight":200,"light":300,"normal":400,"medium":500,"semibold":600,"bold":700,"extrabold":800,"black":900,
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
function tailwindToStyles(classes) {
|
|
153
|
-
const styles = {};
|
|
154
|
-
if (!classes) return styles;
|
|
155
|
-
const list = classes.split(/\s+/).filter(Boolean);
|
|
156
|
-
|
|
157
|
-
for (const cls of list) {
|
|
158
|
-
const base = cls.includes(":") ? cls.split(":").pop() : cls;
|
|
159
|
-
|
|
160
|
-
if (base === "flex") { styles.display = "flex"; continue; }
|
|
161
|
-
if (base === "inline-flex") { styles.display = "inline-flex"; continue; }
|
|
162
|
-
if (base === "grid") { styles.display = "grid"; continue; }
|
|
163
|
-
if (base === "block") { styles.display = "block"; continue; }
|
|
164
|
-
if (base === "inline-block") { styles.display = "inline-block"; continue; }
|
|
165
|
-
if (base === "hidden") { styles.display = "none"; continue; }
|
|
166
|
-
|
|
167
|
-
if (base === "flex-row") { styles["flex-direction"] = "row"; continue; }
|
|
168
|
-
if (base === "flex-col") { styles["flex-direction"] = "column"; continue; }
|
|
169
|
-
if (base === "flex-row-reverse") { styles["flex-direction"] = "row-reverse"; continue; }
|
|
170
|
-
if (base === "flex-col-reverse") { styles["flex-direction"] = "column-reverse"; continue; }
|
|
171
|
-
if (base === "flex-wrap") { styles["flex-wrap"] = "wrap"; continue; }
|
|
172
|
-
if (base === "flex-1") { styles.flex = "1 1 0%"; continue; }
|
|
173
|
-
if (base === "flex-auto") { styles.flex = "1 1 auto"; continue; }
|
|
174
|
-
if (base === "flex-none") { styles.flex = "none"; continue; }
|
|
175
|
-
if (base === "flex-grow" || base === "grow") { styles["flex-grow"] = "1"; continue; }
|
|
176
|
-
if (base === "flex-shrink-0" || base === "shrink-0") { styles["flex-shrink"] = "0"; continue; }
|
|
177
|
-
|
|
178
|
-
if (base === "justify-center") { styles["justify-content"] = "center"; continue; }
|
|
179
|
-
if (base === "justify-between") { styles["justify-content"] = "space-between"; continue; }
|
|
180
|
-
if (base === "justify-around") { styles["justify-content"] = "space-around"; continue; }
|
|
181
|
-
if (base === "justify-evenly") { styles["justify-content"] = "space-evenly"; continue; }
|
|
182
|
-
if (base === "justify-start") { styles["justify-content"] = "flex-start"; continue; }
|
|
183
|
-
if (base === "justify-end") { styles["justify-content"] = "flex-end"; continue; }
|
|
184
|
-
if (base === "items-center") { styles["align-items"] = "center"; continue; }
|
|
185
|
-
if (base === "items-start") { styles["align-items"] = "flex-start"; continue; }
|
|
186
|
-
if (base === "items-end") { styles["align-items"] = "flex-end"; continue; }
|
|
187
|
-
if (base === "items-stretch") { styles["align-items"] = "stretch"; continue; }
|
|
188
|
-
if (base === "items-baseline") { styles["align-items"] = "baseline"; continue; }
|
|
189
|
-
if (base === "self-center") { styles["align-self"] = "center"; continue; }
|
|
190
|
-
if (base === "self-start") { styles["align-self"] = "flex-start"; continue; }
|
|
191
|
-
if (base === "self-end") { styles["align-self"] = "flex-end"; continue; }
|
|
192
|
-
|
|
193
|
-
let gapMatch = base.match(/^gap-(\[.*?\]|[\w.]+)$/);
|
|
194
|
-
if (gapMatch) { styles.gap = resolveTwSpacing(gapMatch[1]); continue; }
|
|
195
|
-
gapMatch = base.match(/^gap-x-([\w.]+)$/);
|
|
196
|
-
if (gapMatch) { styles["column-gap"] = resolveTwSpacing(gapMatch[1]); continue; }
|
|
197
|
-
gapMatch = base.match(/^gap-y-([\w.]+)$/);
|
|
198
|
-
if (gapMatch) { styles["row-gap"] = resolveTwSpacing(gapMatch[1]); continue; }
|
|
199
|
-
|
|
200
|
-
let whMatch = base.match(/^w-(\[.*?\]|[\w./]+)$/);
|
|
201
|
-
if (whMatch) { styles.width = resolveTwDimension(whMatch[1]); continue; }
|
|
202
|
-
whMatch = base.match(/^h-(\[.*?\]|[\w./]+)$/);
|
|
203
|
-
if (whMatch) { styles.height = resolveTwDimension(whMatch[1]); continue; }
|
|
204
|
-
whMatch = base.match(/^min-h-(\[.*?\]|[\w./]+)$/);
|
|
205
|
-
if (whMatch) { styles["min-height"] = resolveTwDimension(whMatch[1]); continue; }
|
|
206
|
-
whMatch = base.match(/^max-w-(\[.*?\]|[\w]+)$/);
|
|
207
|
-
if (whMatch) { styles["max-width"] = resolveTwMaxWidth(whMatch[1]); continue; }
|
|
208
|
-
if (base === "w-full") { styles.width = "100%"; continue; }
|
|
209
|
-
if (base === "h-full") { styles.height = "100%"; continue; }
|
|
210
|
-
if (base === "min-h-screen") { styles["min-height"] = "100vh"; continue; }
|
|
211
|
-
if (base === "h-screen") { styles.height = "100vh"; continue; }
|
|
212
|
-
if (base === "w-screen") { styles.width = "100vw"; continue; }
|
|
213
|
-
|
|
214
|
-
let padMatch = base.match(/^p-(\[.*?\]|[\w.]+)$/);
|
|
215
|
-
if (padMatch) { const v = resolveTwSpacing(padMatch[1]); styles.padding = v; continue; }
|
|
216
|
-
padMatch = base.match(/^px-(\[.*?\]|[\w.]+)$/);
|
|
217
|
-
if (padMatch) { const v = resolveTwSpacing(padMatch[1]); styles["padding-left"] = v; styles["padding-right"] = v; continue; }
|
|
218
|
-
padMatch = base.match(/^py-(\[.*?\]|[\w.]+)$/);
|
|
219
|
-
if (padMatch) { const v = resolveTwSpacing(padMatch[1]); styles["padding-top"] = v; styles["padding-bottom"] = v; continue; }
|
|
220
|
-
padMatch = base.match(/^pt-(\[.*?\]|[\w.]+)$/);
|
|
221
|
-
if (padMatch) { styles["padding-top"] = resolveTwSpacing(padMatch[1]); continue; }
|
|
222
|
-
padMatch = base.match(/^pr-(\[.*?\]|[\w.]+)$/);
|
|
223
|
-
if (padMatch) { styles["padding-right"] = resolveTwSpacing(padMatch[1]); continue; }
|
|
224
|
-
padMatch = base.match(/^pb-(\[.*?\]|[\w.]+)$/);
|
|
225
|
-
if (padMatch) { styles["padding-bottom"] = resolveTwSpacing(padMatch[1]); continue; }
|
|
226
|
-
padMatch = base.match(/^pl-(\[.*?\]|[\w.]+)$/);
|
|
227
|
-
if (padMatch) { styles["padding-left"] = resolveTwSpacing(padMatch[1]); continue; }
|
|
228
|
-
|
|
229
|
-
let mrgMatch = base.match(/^m([trblxy]?)-(\[.*?\]|[\w.]+)$/);
|
|
230
|
-
if (mrgMatch) {
|
|
231
|
-
const v = resolveTwSpacing(mrgMatch[2]);
|
|
232
|
-
const side = mrgMatch[1];
|
|
233
|
-
if (!side) styles.margin = v;
|
|
234
|
-
else if (side === "t") styles["margin-top"] = v;
|
|
235
|
-
else if (side === "r") styles["margin-right"] = v;
|
|
236
|
-
else if (side === "b") styles["margin-bottom"] = v;
|
|
237
|
-
else if (side === "l") styles["margin-left"] = v;
|
|
238
|
-
else if (side === "x") { styles["margin-left"] = v; styles["margin-right"] = v; }
|
|
239
|
-
else if (side === "y") { styles["margin-top"] = v; styles["margin-bottom"] = v; }
|
|
240
|
-
continue;
|
|
241
|
-
}
|
|
242
|
-
if (base === "mx-auto") { styles["margin-left"] = "auto"; styles["margin-right"] = "auto"; continue; }
|
|
243
|
-
|
|
244
|
-
let bgMatch = base.match(/^bg-([\w]+-\d+|white|black|transparent)$/);
|
|
245
|
-
if (bgMatch) { const hex = TW_COLORS[bgMatch[1]]; if (hex) styles["background-color"] = hex; continue; }
|
|
246
|
-
bgMatch = base.match(/^bg-\[(#[0-9a-fA-F]{3,8})\]$/);
|
|
247
|
-
if (bgMatch) { styles["background-color"] = bgMatch[1]; continue; }
|
|
248
|
-
|
|
249
|
-
let tcMatch = base.match(/^text-([\w]+-\d+|white|black)$/);
|
|
250
|
-
if (tcMatch) { const hex = TW_COLORS[tcMatch[1]]; if (hex) styles.color = hex; continue; }
|
|
251
|
-
tcMatch = base.match(/^text-\[(#[0-9a-fA-F]{3,8})\]$/);
|
|
252
|
-
if (tcMatch) { styles.color = tcMatch[1]; continue; }
|
|
253
|
-
|
|
254
|
-
let fsMatch = base.match(/^text-(xs|sm|base|lg|xl|[2-9]xl)$/);
|
|
255
|
-
if (fsMatch) { const pair = TW_FONT_SIZE[fsMatch[1]]; if (pair) { styles["font-size"] = pair[0] + "px"; styles["line-height"] = pair[1] + "px"; } continue; }
|
|
256
|
-
fsMatch = base.match(/^text-\[(\d+(?:px|rem))\]$/);
|
|
257
|
-
if (fsMatch) { styles["font-size"] = fsMatch[1]; continue; }
|
|
258
|
-
|
|
259
|
-
let fwMatch = base.match(/^font-(thin|extralight|light|normal|medium|semibold|bold|extrabold|black)$/);
|
|
260
|
-
if (fwMatch) { styles["font-weight"] = String(TW_FONT_WEIGHT[fwMatch[1]]); continue; }
|
|
261
|
-
|
|
262
|
-
if (base === "text-center") { styles["text-align"] = "center"; continue; }
|
|
263
|
-
if (base === "text-right") { styles["text-align"] = "right"; continue; }
|
|
264
|
-
if (base === "text-left") { styles["text-align"] = "left"; continue; }
|
|
265
|
-
|
|
266
|
-
let brMatch = base.match(/^rounded(?:-(none|sm|md|lg|xl|2xl|3xl|full))?$/);
|
|
267
|
-
if (brMatch) { styles["border-radius"] = (TW_RADIUS[brMatch[1] || ""] || 4) + "px"; continue; }
|
|
268
|
-
brMatch = base.match(/^rounded-\[(\d+(?:px|rem))\]$/);
|
|
269
|
-
if (brMatch) { styles["border-radius"] = brMatch[1]; continue; }
|
|
270
|
-
|
|
271
|
-
if (base === "border") { styles.border = "1px solid #e5e7eb"; continue; }
|
|
272
|
-
let borderMatch = base.match(/^border-(\d+)$/);
|
|
273
|
-
if (borderMatch) { styles.border = borderMatch[1] + "px solid #e5e7eb"; continue; }
|
|
274
|
-
borderMatch = base.match(/^border-([\w]+-\d+)$/);
|
|
275
|
-
if (borderMatch) { const hex = TW_COLORS[borderMatch[1]]; if (hex) styles["border-color"] = hex; continue; }
|
|
276
|
-
|
|
277
|
-
if (base === "shadow") { styles["box-shadow"] = "0 1px 3px rgba(0,0,0,0.1)"; continue; }
|
|
278
|
-
if (base === "shadow-sm") { styles["box-shadow"] = "0 1px 2px rgba(0,0,0,0.05)"; continue; }
|
|
279
|
-
if (base === "shadow-md") { styles["box-shadow"] = "0 4px 6px rgba(0,0,0,0.1)"; continue; }
|
|
280
|
-
if (base === "shadow-lg") { styles["box-shadow"] = "0 10px 15px rgba(0,0,0,0.1)"; continue; }
|
|
281
|
-
if (base === "shadow-xl") { styles["box-shadow"] = "0 20px 25px rgba(0,0,0,0.1)"; continue; }
|
|
282
|
-
if (base === "shadow-none") { styles["box-shadow"] = "none"; continue; }
|
|
283
|
-
|
|
284
|
-
let opMatch = base.match(/^opacity-(\d+)$/);
|
|
285
|
-
if (opMatch) { styles.opacity = String(parseInt(opMatch[1]) / 100); continue; }
|
|
286
|
-
|
|
287
|
-
if (base === "overflow-hidden") { styles.overflow = "hidden"; continue; }
|
|
288
|
-
if (base === "overflow-auto") { styles.overflow = "auto"; continue; }
|
|
289
|
-
|
|
290
|
-
if (base === "relative") { styles.position = "relative"; continue; }
|
|
291
|
-
if (base === "absolute") { styles.position = "absolute"; continue; }
|
|
292
|
-
|
|
293
|
-
let lhMatch = base.match(/^leading-(tight|snug|normal|relaxed|loose|\d+)$/);
|
|
294
|
-
if (lhMatch) { const lhMap = { tight: "1.25", snug: "1.375", normal: "1.5", relaxed: "1.625", loose: "2" }; styles["line-height"] = lhMap[lhMatch[1]] || lhMatch[1]; continue; }
|
|
295
|
-
|
|
296
|
-
if (base === "tracking-tight") { styles["letter-spacing"] = "-0.025em"; continue; }
|
|
297
|
-
if (base === "tracking-wide") { styles["letter-spacing"] = "0.025em"; continue; }
|
|
298
|
-
|
|
299
|
-
if (base === "uppercase") { styles["text-transform"] = "uppercase"; continue; }
|
|
300
|
-
if (base === "lowercase") { styles["text-transform"] = "lowercase"; continue; }
|
|
301
|
-
if (base === "capitalize") { styles["text-transform"] = "capitalize"; continue; }
|
|
302
|
-
if (base === "truncate") { styles.overflow = "hidden"; styles["text-overflow"] = "ellipsis"; styles["white-space"] = "nowrap"; continue; }
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
return styles;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
function resolveTwSpacing(val) {
|
|
309
|
-
if (val.startsWith("[") && val.endsWith("]")) return val.slice(1, -1);
|
|
310
|
-
const px = TW_SPACING[val];
|
|
311
|
-
if (typeof px === "number") return px + "px";
|
|
312
|
-
if (typeof px === "string") return px;
|
|
313
|
-
const num = parseFloat(val);
|
|
314
|
-
if (!isNaN(num)) return (num * 4) + "px";
|
|
315
|
-
return "0px";
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
function resolveTwDimension(val) {
|
|
319
|
-
if (val.startsWith("[") && val.endsWith("]")) return val.slice(1, -1);
|
|
320
|
-
if (val === "full") return "100%";
|
|
321
|
-
if (val === "screen") return "100vh";
|
|
322
|
-
if (val === "auto") return "auto";
|
|
323
|
-
const fracMatch = val.match(/^(\d+)\/(\d+)$/);
|
|
324
|
-
if (fracMatch) return ((parseInt(fracMatch[1]) / parseInt(fracMatch[2])) * 100).toFixed(4) + "%";
|
|
325
|
-
return resolveTwSpacing(val);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
function resolveTwMaxWidth(val) {
|
|
329
|
-
const map = { sm:"384px", md:"448px", lg:"512px", xl:"576px", "2xl":"672px", "3xl":"768px", "4xl":"896px", "5xl":"1024px", "6xl":"1152px", "7xl":"1280px", full:"100%", screen:"100vw" };
|
|
330
|
-
return map[val] || resolveTwSpacing(val);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
function parseDimension(val) {
|
|
334
|
-
if (!val || val === "auto" || val === "none") return null;
|
|
335
|
-
const num = parseFloat(val);
|
|
336
|
-
if (isNaN(num)) return null;
|
|
337
|
-
if (val.includes("rem")) return num * 16;
|
|
338
|
-
if (val.includes("em")) return num * 16;
|
|
339
|
-
if (val.includes("%")) return null; // percentage not a fixed px
|
|
340
|
-
return num;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
function parseBoxShorthand(val) {
|
|
344
|
-
if (!val) return { top: 0, right: 0, bottom: 0, left: 0 };
|
|
345
|
-
const parts = val.split(/\s+/).map(parseDimension).map(v => v || 0);
|
|
346
|
-
if (parts.length === 1) return { top: parts[0], right: parts[0], bottom: parts[0], left: parts[0] };
|
|
347
|
-
if (parts.length === 2) return { top: parts[0], right: parts[1], bottom: parts[0], left: parts[1] };
|
|
348
|
-
if (parts.length === 3) return { top: parts[0], right: parts[1], bottom: parts[2], left: parts[1] };
|
|
349
|
-
return { top: parts[0], right: parts[1], bottom: parts[2], left: parts[3] };
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
function parseBorder(val) {
|
|
353
|
-
if (!val || val === "none" || val === "0") return null;
|
|
354
|
-
const parts = val.split(/\s+/);
|
|
355
|
-
const width = parseDimension(parts[0]) || 1;
|
|
356
|
-
const color = parseColor(parts[2] || parts[1]) || { r: 0, g: 0, b: 0, a: 1 };
|
|
357
|
-
return { width, color };
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// ── DOM helpers ─────────────────────────────────────────────────────────────────
|
|
361
|
-
|
|
362
|
-
const TEXT_TAGS = new Set(["p", "h1", "h2", "h3", "h4", "h5", "h6", "span", "label", "a", "strong", "em", "b", "i", "li", "td", "th", "caption", "figcaption", "small", "code", "pre"]);
|
|
363
|
-
const FRAME_TAGS = new Set(["div", "section", "article", "main", "header", "footer", "nav", "aside", "form", "ul", "ol", "table", "tr", "thead", "tbody", "fieldset", "details", "summary"]);
|
|
364
|
-
const SKIP_TAGS = new Set(["script", "style", "link", "meta", "head", "noscript", "template"]);
|
|
365
|
-
const HEADING_SIZES = { h1: 32, h2: 28, h3: 24, h4: 20, h5: 18, h6: 16 };
|
|
366
|
-
|
|
367
|
-
function getTextContent(node) {
|
|
368
|
-
if (node.type === "text") return node.data || "";
|
|
369
|
-
if (!node.children) return "";
|
|
370
|
-
return node.children.map(getTextContent).join("").trim();
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
function resolveStyles(node, cssRules) {
|
|
374
|
-
let merged = {};
|
|
375
|
-
if (node.name && cssRules.has(node.name)) merged = { ...merged, ...cssRules.get(node.name) };
|
|
376
|
-
const classStr = node.attribs?.class || "";
|
|
377
|
-
merged = { ...merged, ...tailwindToStyles(classStr) };
|
|
378
|
-
const classes = classStr.split(/\s+/).filter(Boolean);
|
|
379
|
-
for (const cls of classes) {
|
|
380
|
-
if (cssRules.has(`.${cls}`)) merged = { ...merged, ...cssRules.get(`.${cls}`) };
|
|
381
|
-
}
|
|
382
|
-
const id = node.attribs?.id;
|
|
383
|
-
if (id && cssRules.has(`#${id}`)) merged = { ...merged, ...cssRules.get(`#${id}`) };
|
|
384
|
-
merged = { ...merged, ...parseInlineStyle(node.attribs?.style) };
|
|
385
|
-
return merged;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
function escStr(s) {
|
|
389
|
-
return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "");
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// ── Code generator ──────────────────────────────────────────────────────────────
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* State object passed through the recursive code generation.
|
|
396
|
-
*/
|
|
397
|
-
function createCodeGenState() {
|
|
398
|
-
return {
|
|
399
|
-
frameCounter: 0,
|
|
400
|
-
textCounter: 0,
|
|
401
|
-
lines: [],
|
|
402
|
-
fontsNeeded: new Set(),
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* Determine the Figma font style name from CSS font-weight.
|
|
408
|
-
*/
|
|
409
|
-
function fontStyleFromWeight(weight) {
|
|
410
|
-
const w = parseInt(weight) || 400;
|
|
411
|
-
if (w >= 800) return "Bold";
|
|
412
|
-
if (w >= 600) return "Semi Bold";
|
|
413
|
-
if (w >= 500) return "Medium";
|
|
414
|
-
return "Regular";
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* Compute layoutSizing for a child based on parent's layoutMode.
|
|
419
|
-
*/
|
|
420
|
-
function computeSizing(styles, parentLayoutMode, tag) {
|
|
421
|
-
let hSizing = "HUG";
|
|
422
|
-
let vSizing = "HUG";
|
|
423
|
-
|
|
424
|
-
if (parentLayoutMode === "VERTICAL") {
|
|
425
|
-
hSizing = "FILL";
|
|
426
|
-
vSizing = "HUG";
|
|
427
|
-
} else if (parentLayoutMode === "HORIZONTAL") {
|
|
428
|
-
hSizing = "HUG";
|
|
429
|
-
vSizing = "FILL";
|
|
430
|
-
// flex-1 / flex-grow → FILL horizontally too
|
|
431
|
-
if (styles.flex === "1 1 0%" || styles["flex-grow"] === "1") {
|
|
432
|
-
hSizing = "FILL";
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
// w-full in any parent → FILL
|
|
437
|
-
if (styles.width === "100%") hSizing = "FILL";
|
|
438
|
-
if (styles.height === "100%") vSizing = "FILL";
|
|
439
|
-
|
|
440
|
-
// Explicit fixed dimensions → FIXED
|
|
441
|
-
const w = parseDimension(styles.width);
|
|
442
|
-
const h = parseDimension(styles.height);
|
|
443
|
-
if (w && styles.width !== "100%") hSizing = "FIXED";
|
|
444
|
-
if (h && styles.height !== "100%" && styles.height !== "100vh") vSizing = "FIXED";
|
|
445
|
-
|
|
446
|
-
return { hSizing, vSizing, fixedW: w, fixedH: h };
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
/**
|
|
450
|
-
* Generate Figma Plugin API code for a text node.
|
|
451
|
-
*/
|
|
452
|
-
function genTextCode(state, text, styles, parentVar, parentLayoutMode, tag) {
|
|
453
|
-
if (!text) return;
|
|
454
|
-
|
|
455
|
-
const varName = `t${state.textCounter++}`;
|
|
456
|
-
const fontSize = parseDimension(styles["font-size"]) || HEADING_SIZES[tag] || 14;
|
|
457
|
-
const fontWeight = styles["font-weight"];
|
|
458
|
-
const isHeading = tag && tag.startsWith("h") && HEADING_SIZES[tag];
|
|
459
|
-
const isBold = fontWeight === "bold" || parseInt(fontWeight) >= 600 || isHeading || tag === "strong" || tag === "b";
|
|
460
|
-
const color = parseColor(styles.color) || { r: 0, g: 0, b: 0, a: 1 };
|
|
461
|
-
const textAlign = styles["text-align"] || "left";
|
|
462
|
-
|
|
463
|
-
const fontStyle = isBold ? "Bold" : (parseInt(fontWeight) >= 500 ? "Medium" : "Regular");
|
|
464
|
-
state.fontsNeeded.add(fontStyle);
|
|
465
|
-
|
|
466
|
-
const textTransform = styles["text-transform"];
|
|
467
|
-
let displayText = text;
|
|
468
|
-
if (textTransform === "uppercase") displayText = text.toUpperCase();
|
|
469
|
-
else if (textTransform === "lowercase") displayText = text.toLowerCase();
|
|
470
|
-
|
|
471
|
-
// Sizing: text in vertical parent → FILL/HUG, in horizontal → HUG/HUG
|
|
472
|
-
let hSizing = parentLayoutMode === "VERTICAL" ? "FILL" : "HUG";
|
|
473
|
-
let vSizing = "HUG";
|
|
474
|
-
|
|
475
|
-
state.lines.push(`const ${varName} = figma.createText();`);
|
|
476
|
-
state.lines.push(`${parentVar}.appendChild(${varName});`);
|
|
477
|
-
state.lines.push(`${varName}.fontName = {family:"Inter",style:"${fontStyle}"};`);
|
|
478
|
-
state.lines.push(`${varName}.characters = "${escStr(displayText)}";`);
|
|
479
|
-
state.lines.push(`${varName}.fontSize = ${fontSize};`);
|
|
480
|
-
state.lines.push(`${varName}.fills = [{type:"SOLID",color:${colorToFigma(color)}}];`);
|
|
481
|
-
|
|
482
|
-
const alignMap = { center: "CENTER", right: "RIGHT", left: "LEFT" };
|
|
483
|
-
if (textAlign !== "left") {
|
|
484
|
-
state.lines.push(`${varName}.textAlignHorizontal = "${alignMap[textAlign] || "LEFT"}";`);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
state.lines.push(`${varName}.layoutSizingHorizontal = "${hSizing}";`);
|
|
488
|
-
state.lines.push(`${varName}.layoutSizingVertical = "${vSizing}";`);
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
/**
|
|
492
|
-
* Recursively generate Figma code for a DOM node.
|
|
493
|
-
*/
|
|
494
|
-
function domToCode(node, cssRules, state, parentVar, parentLayoutMode) {
|
|
495
|
-
if (!node) return;
|
|
496
|
-
if (node.type === "text") {
|
|
497
|
-
const txt = (node.data || "").trim();
|
|
498
|
-
if (txt) genTextCode(state, txt, {}, parentVar, parentLayoutMode, "span");
|
|
499
|
-
return;
|
|
500
|
-
}
|
|
501
|
-
if (SKIP_TAGS.has(node.name)) return;
|
|
502
|
-
|
|
503
|
-
const tag = (node.name || "").toLowerCase();
|
|
504
|
-
const styles = resolveStyles(node, cssRules);
|
|
505
|
-
const children = (node.children || []).filter(c =>
|
|
506
|
-
c.type === "tag" || (c.type === "text" && (c.data || "").trim())
|
|
507
|
-
);
|
|
508
|
-
|
|
509
|
-
// Skip hidden elements
|
|
510
|
-
if (styles.display === "none") return;
|
|
511
|
-
|
|
512
|
-
// ── IMG / SVG → rectangle placeholder ──
|
|
513
|
-
if (tag === "img" || tag === "svg" || tag === "video") {
|
|
514
|
-
const varName = `f${state.frameCounter++}`;
|
|
515
|
-
const w = parseDimension(styles.width) || parseDimension(node.attribs?.width) || 200;
|
|
516
|
-
const h = parseDimension(styles.height) || parseDimension(node.attribs?.height) || 150;
|
|
517
|
-
const bg = parseColor(styles["background-color"]) || { r: 0.9, g: 0.9, b: 0.9, a: 1 };
|
|
518
|
-
const radius = parseDimension(styles["border-radius"]) || 0;
|
|
519
|
-
const alt = node.attribs?.alt || "image";
|
|
520
|
-
|
|
521
|
-
state.lines.push(`const ${varName} = figma.createFrame();`);
|
|
522
|
-
state.lines.push(`${parentVar}.appendChild(${varName});`);
|
|
523
|
-
state.lines.push(`${varName}.name = "${escStr(alt.slice(0, 40))}";`);
|
|
524
|
-
state.lines.push(`${varName}.resize(${w}, ${h});`);
|
|
525
|
-
state.lines.push(`${varName}.fills = [{type:"SOLID",color:${colorToFigma(bg)}}];`);
|
|
526
|
-
if (radius) state.lines.push(`${varName}.cornerRadius = ${radius};`);
|
|
527
|
-
|
|
528
|
-
// Sizing based on parent
|
|
529
|
-
if (parentLayoutMode === "VERTICAL") {
|
|
530
|
-
state.lines.push(`${varName}.layoutSizingHorizontal = "FILL";`);
|
|
531
|
-
state.lines.push(`${varName}.layoutSizingVertical = "FIXED";`);
|
|
532
|
-
} else {
|
|
533
|
-
state.lines.push(`${varName}.layoutSizingHorizontal = "FIXED";`);
|
|
534
|
-
state.lines.push(`${varName}.layoutSizingVertical = "FIXED";`);
|
|
535
|
-
}
|
|
536
|
-
return;
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
// ── INPUT / TEXTAREA / SELECT ──
|
|
540
|
-
if (tag === "input" || tag === "textarea" || tag === "select") {
|
|
541
|
-
const varName = `f${state.frameCounter++}`;
|
|
542
|
-
const placeholder = node.attribs?.placeholder || node.attribs?.value || tag;
|
|
543
|
-
const h = tag === "textarea" ? (parseDimension(styles.height) || 80) : 48;
|
|
544
|
-
const bg = parseColor(styles["background-color"]) || { r: 1, g: 1, b: 1, a: 1 };
|
|
545
|
-
const border = parseBorder(styles.border);
|
|
546
|
-
const radius = parseDimension(styles["border-radius"]) || 8;
|
|
547
|
-
|
|
548
|
-
state.lines.push(`const ${varName} = figma.createFrame();`);
|
|
549
|
-
state.lines.push(`${parentVar}.appendChild(${varName});`);
|
|
550
|
-
state.lines.push(`${varName}.name = "${escStr(node.attribs?.name || tag)}";`);
|
|
551
|
-
state.lines.push(`${varName}.layoutMode = "HORIZONTAL";`);
|
|
552
|
-
state.lines.push(`${varName}.layoutSizingHorizontal = "FILL";`);
|
|
553
|
-
state.lines.push(`${varName}.layoutSizingVertical = "FIXED";`);
|
|
554
|
-
state.lines.push(`${varName}.resize(200, ${h});`);
|
|
555
|
-
state.lines.push(`${varName}.counterAxisAlignItems = "CENTER";`);
|
|
556
|
-
state.lines.push(`${varName}.paddingLeft = 16; ${varName}.paddingRight = 16;`);
|
|
557
|
-
state.lines.push(`${varName}.paddingTop = 8; ${varName}.paddingBottom = 8;`);
|
|
558
|
-
state.lines.push(`${varName}.cornerRadius = ${radius};`);
|
|
559
|
-
state.lines.push(`${varName}.fills = [{type:"SOLID",color:${colorToFigma(bg)}}];`);
|
|
560
|
-
if (border) {
|
|
561
|
-
state.lines.push(`${varName}.strokes = [{type:"SOLID",color:${colorToFigma(border.color)}}];`);
|
|
562
|
-
state.lines.push(`${varName}.strokeWeight = ${border.width};`);
|
|
563
|
-
} else {
|
|
564
|
-
state.lines.push(`${varName}.strokes = [{type:"SOLID",color:{r:0.82,g:0.82,b:0.82}}];`);
|
|
565
|
-
state.lines.push(`${varName}.strokeWeight = 1;`);
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
// Placeholder text
|
|
569
|
-
state.fontsNeeded.add("Regular");
|
|
570
|
-
genTextCode(state, placeholder, { color: "#9CA3AF", "font-size": "16px" }, varName, "HORIZONTAL", "span");
|
|
571
|
-
return;
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
// ── BUTTON ──
|
|
575
|
-
if (tag === "button" || (tag === "a" && (styles.display === "inline-block" || styles.display === "inline-flex"))) {
|
|
576
|
-
const varName = `f${state.frameCounter++}`;
|
|
577
|
-
const text = getTextContent(node) || "Button";
|
|
578
|
-
const bg = parseColor(styles["background-color"]) || { r: 0.2, g: 0.4, b: 1, a: 1 };
|
|
579
|
-
const color = parseColor(styles.color) || { r: 1, g: 1, b: 1, a: 1 };
|
|
580
|
-
const radius = parseDimension(styles["border-radius"]) || 8;
|
|
581
|
-
const padding = parseBoxShorthand(styles.padding);
|
|
582
|
-
const fontSize = parseDimension(styles["font-size"]) || 16;
|
|
583
|
-
const border = parseBorder(styles.border);
|
|
584
|
-
|
|
585
|
-
// Determine if full-width button
|
|
586
|
-
const isFullWidth = styles.width === "100%" || (parentLayoutMode === "VERTICAL" && styles.width === "100%");
|
|
587
|
-
const hSizing = isFullWidth ? "FILL" : "HUG";
|
|
588
|
-
|
|
589
|
-
state.lines.push(`const ${varName} = figma.createFrame();`);
|
|
590
|
-
state.lines.push(`${parentVar}.appendChild(${varName});`);
|
|
591
|
-
state.lines.push(`${varName}.name = "${escStr(text.slice(0, 30))}";`);
|
|
592
|
-
state.lines.push(`${varName}.layoutMode = "HORIZONTAL";`);
|
|
593
|
-
state.lines.push(`${varName}.layoutSizingHorizontal = "${hSizing}";`);
|
|
594
|
-
state.lines.push(`${varName}.layoutSizingVertical = "HUG";`);
|
|
595
|
-
state.lines.push(`${varName}.primaryAxisAlignItems = "CENTER";`);
|
|
596
|
-
state.lines.push(`${varName}.counterAxisAlignItems = "CENTER";`);
|
|
597
|
-
state.lines.push(`${varName}.paddingLeft = ${padding.left || 24}; ${varName}.paddingRight = ${padding.right || 24};`);
|
|
598
|
-
state.lines.push(`${varName}.paddingTop = ${padding.top || 12}; ${varName}.paddingBottom = ${padding.bottom || 12};`);
|
|
599
|
-
state.lines.push(`${varName}.cornerRadius = ${radius};`);
|
|
600
|
-
state.lines.push(`${varName}.fills = [{type:"SOLID",color:${colorToFigma(bg)}}];`);
|
|
601
|
-
if (border) {
|
|
602
|
-
state.lines.push(`${varName}.strokes = [{type:"SOLID",color:${colorToFigma(border.color)}}];`);
|
|
603
|
-
state.lines.push(`${varName}.strokeWeight = ${border.width};`);
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
const fontWeight = styles["font-weight"];
|
|
607
|
-
const isBold = fontWeight === "bold" || parseInt(fontWeight) >= 600;
|
|
608
|
-
genTextCode(state, text, {
|
|
609
|
-
color: rgbaToHex(color),
|
|
610
|
-
"font-size": fontSize + "px",
|
|
611
|
-
"font-weight": isBold ? "600" : "500",
|
|
612
|
-
}, varName, "HORIZONTAL", "span");
|
|
613
|
-
return;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
// ── HR → divider line ──
|
|
617
|
-
if (tag === "hr") {
|
|
618
|
-
const varName = `f${state.frameCounter++}`;
|
|
619
|
-
const divColor = parseColor(styles["border-color"]) || { r: 0.9, g: 0.9, b: 0.9, a: 1 };
|
|
620
|
-
state.lines.push(`const ${varName} = figma.createFrame();`);
|
|
621
|
-
state.lines.push(`${parentVar}.appendChild(${varName});`);
|
|
622
|
-
state.lines.push(`${varName}.name = "Divider";`);
|
|
623
|
-
state.lines.push(`${varName}.resize(100, 1);`);
|
|
624
|
-
state.lines.push(`${varName}.layoutSizingHorizontal = "FILL";`);
|
|
625
|
-
state.lines.push(`${varName}.layoutSizingVertical = "FIXED";`);
|
|
626
|
-
state.lines.push(`${varName}.fills = [{type:"SOLID",color:${colorToFigma(divColor)}}];`);
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
// ── Pure text leaf ──
|
|
631
|
-
if (TEXT_TAGS.has(tag) && !children.some(c => c.type === "tag" && FRAME_TAGS.has(c.name))) {
|
|
632
|
-
const text = getTextContent(node);
|
|
633
|
-
if (text) genTextCode(state, text, styles, parentVar, parentLayoutMode, tag);
|
|
634
|
-
return;
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
// ── Container frame (div, section, nav, etc.) ──
|
|
638
|
-
if (FRAME_TAGS.has(tag) || (tag && children.length > 0)) {
|
|
639
|
-
const varName = `f${state.frameCounter++}`;
|
|
640
|
-
|
|
641
|
-
const display = styles.display || "";
|
|
642
|
-
const flexDir = styles["flex-direction"] || "";
|
|
643
|
-
const isRow = display.includes("flex") && (flexDir === "row" || flexDir === "row-reverse" || !flexDir);
|
|
644
|
-
const isCol = display.includes("flex") && (flexDir === "column" || flexDir === "column-reverse");
|
|
645
|
-
|
|
646
|
-
let layoutMode = "VERTICAL";
|
|
647
|
-
if (isRow) layoutMode = "HORIZONTAL";
|
|
648
|
-
if (isCol) layoutMode = "VERTICAL";
|
|
649
|
-
|
|
650
|
-
const gap = parseDimension(styles.gap) || parseDimension(styles["row-gap"]) || 0;
|
|
651
|
-
const padding = parseBoxShorthand(styles.padding);
|
|
652
|
-
// Merge individual padding properties (from Tailwind px-*, py-*, etc.)
|
|
653
|
-
if (styles["padding-top"]) padding.top = parseDimension(styles["padding-top"]) || padding.top;
|
|
654
|
-
if (styles["padding-bottom"]) padding.bottom = parseDimension(styles["padding-bottom"]) || padding.bottom;
|
|
655
|
-
if (styles["padding-left"]) padding.left = parseDimension(styles["padding-left"]) || padding.left;
|
|
656
|
-
if (styles["padding-right"]) padding.right = parseDimension(styles["padding-right"]) || padding.right;
|
|
657
|
-
|
|
658
|
-
const bg = parseColor(styles["background-color"]);
|
|
659
|
-
const border = parseBorder(styles.border);
|
|
660
|
-
const radius = parseDimension(styles["border-radius"]) || 0;
|
|
661
|
-
const overflow = styles.overflow;
|
|
662
|
-
|
|
663
|
-
// Alignment
|
|
664
|
-
const justifyContent = styles["justify-content"] || "";
|
|
665
|
-
const alignItems = styles["align-items"] || "";
|
|
666
|
-
|
|
667
|
-
let primaryAlign = "MIN";
|
|
668
|
-
if (justifyContent === "center") primaryAlign = "CENTER";
|
|
669
|
-
if (justifyContent === "flex-end" || justifyContent === "end") primaryAlign = "MAX";
|
|
670
|
-
if (justifyContent === "space-between") primaryAlign = "SPACE_BETWEEN";
|
|
671
|
-
|
|
672
|
-
let counterAlign = "MIN";
|
|
673
|
-
if (alignItems === "center") counterAlign = "CENTER";
|
|
674
|
-
if (alignItems === "flex-end" || alignItems === "end") counterAlign = "MAX";
|
|
675
|
-
if (alignItems === "stretch") counterAlign = "MIN"; // stretch is default in Figma when children are FILL
|
|
676
|
-
|
|
677
|
-
// Sizing based on parent layout
|
|
678
|
-
const { hSizing, vSizing, fixedW, fixedH } = computeSizing(styles, parentLayoutMode, tag);
|
|
679
|
-
|
|
680
|
-
// Name
|
|
681
|
-
const name = node.attribs?.["aria-label"]
|
|
682
|
-
|| node.attribs?.id
|
|
683
|
-
|| (node.attribs?.class || "").split(/\s+/).filter(c => !c.includes(":") && c.length > 1)[0]
|
|
684
|
-
|| tag;
|
|
685
|
-
|
|
686
|
-
// Generate code
|
|
687
|
-
state.lines.push(`const ${varName} = figma.createFrame();`);
|
|
688
|
-
state.lines.push(`${parentVar}.appendChild(${varName});`);
|
|
689
|
-
state.lines.push(`${varName}.name = "${escStr(name.slice(0, 40))}";`);
|
|
690
|
-
|
|
691
|
-
// layoutMode FIRST (before padding/spacing)
|
|
692
|
-
state.lines.push(`${varName}.layoutMode = "${layoutMode}";`);
|
|
693
|
-
|
|
694
|
-
// Sizing — AFTER appendChild
|
|
695
|
-
state.lines.push(`${varName}.layoutSizingHorizontal = "${hSizing}";`);
|
|
696
|
-
state.lines.push(`${varName}.layoutSizingVertical = "${vSizing}";`);
|
|
697
|
-
|
|
698
|
-
// For FIXED sizing, set explicit dimensions
|
|
699
|
-
if (hSizing === "FIXED" && fixedW) {
|
|
700
|
-
state.lines.push(`${varName}.resize(${fixedW}, ${fixedH || 100});`);
|
|
701
|
-
} else if (vSizing === "FIXED" && fixedH) {
|
|
702
|
-
state.lines.push(`${varName}.resize(${fixedW || 100}, ${fixedH});`);
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
// Padding
|
|
706
|
-
if (padding.top || padding.bottom || padding.left || padding.right) {
|
|
707
|
-
state.lines.push(`${varName}.paddingTop = ${padding.top}; ${varName}.paddingBottom = ${padding.bottom};`);
|
|
708
|
-
state.lines.push(`${varName}.paddingLeft = ${padding.left}; ${varName}.paddingRight = ${padding.right};`);
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
// Spacing
|
|
712
|
-
if (gap) state.lines.push(`${varName}.itemSpacing = ${gap};`);
|
|
713
|
-
|
|
714
|
-
// Alignment
|
|
715
|
-
if (primaryAlign !== "MIN") state.lines.push(`${varName}.primaryAxisAlignItems = "${primaryAlign}";`);
|
|
716
|
-
if (counterAlign !== "MIN") state.lines.push(`${varName}.counterAxisAlignItems = "${counterAlign}";`);
|
|
717
|
-
|
|
718
|
-
// Visual properties
|
|
719
|
-
if (bg) {
|
|
720
|
-
state.lines.push(`${varName}.fills = [{type:"SOLID",color:${colorToFigma(bg)}}];`);
|
|
721
|
-
} else {
|
|
722
|
-
// Structural wrapper — transparent (no white fill)
|
|
723
|
-
state.lines.push(`${varName}.fills = [];`);
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
if (radius) state.lines.push(`${varName}.cornerRadius = ${radius};`);
|
|
727
|
-
|
|
728
|
-
if (border) {
|
|
729
|
-
state.lines.push(`${varName}.strokes = [{type:"SOLID",color:${colorToFigma(border.color)}}];`);
|
|
730
|
-
state.lines.push(`${varName}.strokeWeight = ${border.width};`);
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
if (overflow === "hidden") state.lines.push(`${varName}.clipsContent = true;`);
|
|
734
|
-
|
|
735
|
-
// Shadow
|
|
736
|
-
if (styles["box-shadow"] && styles["box-shadow"] !== "none") {
|
|
737
|
-
const shadowMatch = styles["box-shadow"].match(/([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+rgba?\(([^)]+)\)/);
|
|
738
|
-
if (shadowMatch) {
|
|
739
|
-
const sx = parseFloat(shadowMatch[1]) || 0;
|
|
740
|
-
const sy = parseFloat(shadowMatch[2]) || 0;
|
|
741
|
-
const sr = parseFloat(shadowMatch[3]) || 4;
|
|
742
|
-
const parts = shadowMatch[4].split(",").map(s => s.trim());
|
|
743
|
-
const a = parts.length >= 4 ? parseFloat(parts[3]) : 0.1;
|
|
744
|
-
state.lines.push(`${varName}.effects = [{type:"DROP_SHADOW",color:{r:0,g:0,b:0,a:${a}},offset:{x:${sx},y:${sy}},radius:${sr},spread:0,visible:true}];`);
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
// Recurse into children
|
|
749
|
-
for (const child of children) {
|
|
750
|
-
if (child.type === "text") {
|
|
751
|
-
const txt = (child.data || "").trim();
|
|
752
|
-
if (txt) genTextCode(state, txt, {}, varName, layoutMode, "span");
|
|
753
|
-
} else if (child.type === "tag") {
|
|
754
|
-
domToCode(child, cssRules, state, varName, layoutMode);
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
return;
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
// Fallback: text content
|
|
761
|
-
const fallbackText = getTextContent(node);
|
|
762
|
-
if (fallbackText) genTextCode(state, fallbackText, styles, parentVar, parentLayoutMode, tag);
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
// ── Main entry point ───────────────────────────────────────────────────────────
|
|
766
|
-
|
|
767
|
-
/**
|
|
768
|
-
* Convert HTML + CSS strings into a single Figma Plugin API execute code string.
|
|
769
|
-
*
|
|
770
|
-
* @param {string} html — HTML output from Stitch
|
|
771
|
-
* @param {string} css — Optional external CSS
|
|
772
|
-
* @param {string} screenName — Name for the root frame
|
|
773
|
-
* @param {string} deviceType — "MOBILE" or "DESKTOP"
|
|
774
|
-
* @returns {{ code: string, commandCount: number }}
|
|
775
|
-
*/
|
|
776
|
-
function htmlToFigmaExecuteCode(html, css, screenName, deviceType) {
|
|
777
|
-
// Parse embedded <style> blocks
|
|
778
|
-
const styleBlocks = [];
|
|
779
|
-
const styleRegex = /<style[^>]*>([\s\S]*?)<\/style>/gi;
|
|
780
|
-
let styleMatch;
|
|
781
|
-
while ((styleMatch = styleRegex.exec(html)) !== null) {
|
|
782
|
-
styleBlocks.push(styleMatch[1]);
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
const allCss = [css, ...styleBlocks].filter(Boolean).join("\n");
|
|
786
|
-
const cssRules = parseStyleBlock(allCss);
|
|
787
|
-
|
|
788
|
-
// Parse HTML DOM
|
|
789
|
-
const doc = parseDocument(html);
|
|
790
|
-
|
|
791
|
-
// Find <body> or first meaningful container
|
|
792
|
-
let root = null;
|
|
793
|
-
function findBody(node) {
|
|
794
|
-
if (node.name === "body") { root = node; return; }
|
|
795
|
-
if (node.children) node.children.forEach(findBody);
|
|
796
|
-
}
|
|
797
|
-
findBody(doc);
|
|
798
|
-
|
|
799
|
-
if (!root) {
|
|
800
|
-
root = { type: "tag", name: "div", attribs: {}, children: doc.children || [] };
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
// Get body children (skip script/style/meta)
|
|
804
|
-
const bodyChildren = (root.children || []).filter(c =>
|
|
805
|
-
(c.type === "tag" && !SKIP_TAGS.has(c.name)) || (c.type === "text" && (c.data || "").trim())
|
|
806
|
-
);
|
|
807
|
-
|
|
808
|
-
// Screen dimensions
|
|
809
|
-
const isMobile = deviceType === "MOBILE";
|
|
810
|
-
const screenW = isMobile ? 390 : 1440;
|
|
811
|
-
const screenH = isMobile ? 844 : 900;
|
|
812
|
-
|
|
813
|
-
// Root frame styles (from <body> or its single child)
|
|
814
|
-
const rootStyles = resolveStyles(root, cssRules);
|
|
815
|
-
let rootBg = parseColor(rootStyles["background-color"]) || { r: 1, g: 1, b: 1, a: 1 };
|
|
816
|
-
|
|
817
|
-
// Single-child optimization: if body has one container child, merge its styles into root
|
|
818
|
-
let effectiveChildren = bodyChildren;
|
|
819
|
-
let rootLayoutMode = "VERTICAL";
|
|
820
|
-
let rootGap = 0;
|
|
821
|
-
let rootPadding = { top: 0, right: 0, bottom: 0, left: 0 };
|
|
822
|
-
let rootPrimaryAlign = "MIN";
|
|
823
|
-
let rootCounterAlign = "MIN";
|
|
824
|
-
|
|
825
|
-
if (bodyChildren.length === 1 && bodyChildren[0].type === "tag" && FRAME_TAGS.has(bodyChildren[0].name)) {
|
|
826
|
-
const singleChild = bodyChildren[0];
|
|
827
|
-
const childStyles = resolveStyles(singleChild, cssRules);
|
|
828
|
-
|
|
829
|
-
// Merge child's layout properties into root
|
|
830
|
-
const display = childStyles.display || "";
|
|
831
|
-
const flexDir = childStyles["flex-direction"] || "";
|
|
832
|
-
if (display.includes("flex") && (flexDir === "row" || flexDir === "row-reverse")) {
|
|
833
|
-
rootLayoutMode = "HORIZONTAL";
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
rootGap = parseDimension(childStyles.gap) || 0;
|
|
837
|
-
rootPadding = parseBoxShorthand(childStyles.padding);
|
|
838
|
-
if (childStyles["padding-top"]) rootPadding.top = parseDimension(childStyles["padding-top"]) || rootPadding.top;
|
|
839
|
-
if (childStyles["padding-bottom"]) rootPadding.bottom = parseDimension(childStyles["padding-bottom"]) || rootPadding.bottom;
|
|
840
|
-
if (childStyles["padding-left"]) rootPadding.left = parseDimension(childStyles["padding-left"]) || rootPadding.left;
|
|
841
|
-
if (childStyles["padding-right"]) rootPadding.right = parseDimension(childStyles["padding-right"]) || rootPadding.right;
|
|
842
|
-
|
|
843
|
-
const childBg = parseColor(childStyles["background-color"]);
|
|
844
|
-
if (childBg) rootBg = childBg;
|
|
845
|
-
|
|
846
|
-
const jc = childStyles["justify-content"] || "";
|
|
847
|
-
if (jc === "center") rootPrimaryAlign = "CENTER";
|
|
848
|
-
if (jc === "space-between") rootPrimaryAlign = "SPACE_BETWEEN";
|
|
849
|
-
if (jc === "flex-end") rootPrimaryAlign = "MAX";
|
|
850
|
-
|
|
851
|
-
const ai = childStyles["align-items"] || "";
|
|
852
|
-
if (ai === "center") rootCounterAlign = "CENTER";
|
|
853
|
-
if (ai === "flex-end") rootCounterAlign = "MAX";
|
|
854
|
-
|
|
855
|
-
// Use the single child's children as effective children
|
|
856
|
-
effectiveChildren = (singleChild.children || []).filter(c =>
|
|
857
|
-
(c.type === "tag" && !SKIP_TAGS.has(c.name)) || (c.type === "text" && (c.data || "").trim())
|
|
858
|
-
);
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
// Initialize code generation state
|
|
862
|
-
const state = createCodeGenState();
|
|
863
|
-
const rootVar = `f${state.frameCounter++}`;
|
|
864
|
-
|
|
865
|
-
// Root frame code
|
|
866
|
-
state.lines.push(`// Root screen frame`);
|
|
867
|
-
state.lines.push(`const ${rootVar} = figma.createFrame();`);
|
|
868
|
-
state.lines.push(`${rootVar}.name = "${escStr((screenName || "Stitch Screen").slice(0, 50))}";`);
|
|
869
|
-
state.lines.push(`${rootVar}.resize(${screenW}, ${screenH});`);
|
|
870
|
-
state.lines.push(`${rootVar}.layoutMode = "${rootLayoutMode}";`);
|
|
871
|
-
state.lines.push(`${rootVar}.layoutSizingHorizontal = "FIXED";`);
|
|
872
|
-
state.lines.push(`${rootVar}.layoutSizingVertical = "FIXED";`);
|
|
873
|
-
state.lines.push(`${rootVar}.primaryAxisSizingMode = "FIXED";`);
|
|
874
|
-
state.lines.push(`${rootVar}.counterAxisSizingMode = "FIXED";`);
|
|
875
|
-
|
|
876
|
-
if (rootPadding.top || rootPadding.bottom || rootPadding.left || rootPadding.right) {
|
|
877
|
-
state.lines.push(`${rootVar}.paddingTop = ${rootPadding.top}; ${rootVar}.paddingBottom = ${rootPadding.bottom};`);
|
|
878
|
-
state.lines.push(`${rootVar}.paddingLeft = ${rootPadding.left}; ${rootVar}.paddingRight = ${rootPadding.right};`);
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
if (rootGap) state.lines.push(`${rootVar}.itemSpacing = ${rootGap};`);
|
|
882
|
-
if (rootPrimaryAlign !== "MIN") state.lines.push(`${rootVar}.primaryAxisAlignItems = "${rootPrimaryAlign}";`);
|
|
883
|
-
if (rootCounterAlign !== "MIN") state.lines.push(`${rootVar}.counterAxisAlignItems = "${rootCounterAlign}";`);
|
|
884
|
-
|
|
885
|
-
state.lines.push(`${rootVar}.fills = [{type:"SOLID",color:${colorToFigma(rootBg)}}];`);
|
|
886
|
-
|
|
887
|
-
// Generate code for all children
|
|
888
|
-
for (const child of effectiveChildren) {
|
|
889
|
-
if (child.type === "text") {
|
|
890
|
-
const txt = (child.data || "").trim();
|
|
891
|
-
if (txt) genTextCode(state, txt, {}, rootVar, rootLayoutMode, "span");
|
|
892
|
-
} else if (child.type === "tag") {
|
|
893
|
-
domToCode(child, cssRules, state, rootVar, rootLayoutMode);
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
// Build final code with font loading at top
|
|
898
|
-
const fontLines = [];
|
|
899
|
-
if (state.fontsNeeded.size > 0) {
|
|
900
|
-
const fontLoads = [...state.fontsNeeded].map(style =>
|
|
901
|
-
`figma.loadFontAsync({family:"Inter",style:"${style}"})`
|
|
902
|
-
);
|
|
903
|
-
fontLines.push(`await Promise.all([${fontLoads.join(",")}]);`);
|
|
904
|
-
fontLines.push("");
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
// Navigate to the created frame
|
|
908
|
-
state.lines.push("");
|
|
909
|
-
state.lines.push(`figma.currentPage.selection = [${rootVar}];`);
|
|
910
|
-
state.lines.push(`figma.viewport.scrollAndZoomIntoView([${rootVar}]);`);
|
|
911
|
-
state.lines.push(`return {id: ${rootVar}.id, name: ${rootVar}.name};`);
|
|
912
|
-
|
|
913
|
-
const code = [...fontLines, ...state.lines].join("\n");
|
|
914
|
-
|
|
915
|
-
return {
|
|
916
|
-
code,
|
|
917
|
-
commandCount: state.frameCounter + state.textCounter,
|
|
918
|
-
};
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
// Legacy export for backward compatibility
|
|
922
|
-
function htmlToFigmaCommands(html, css, screenName) {
|
|
923
|
-
const result = htmlToFigmaExecuteCode(html, css, screenName, "DESKTOP");
|
|
924
|
-
return [{ method: "execute", params: { code: result.code } }];
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
module.exports = { htmlToFigmaCommands, htmlToFigmaExecuteCode, parseColor, rgbaToHex, parseInlineStyle, parseDimension };
|