@nac3/forge-cli 0.2.0-alpha.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/LICENSE +45 -0
- package/README.md +371 -0
- package/dist/bin/yf.d.ts +5 -0
- package/dist/bin/yf.d.ts.map +1 -0
- package/dist/bin/yf.js +86 -0
- package/dist/bin/yf.js.map +1 -0
- package/dist/chat/claude.d.ts +100 -0
- package/dist/chat/claude.d.ts.map +1 -0
- package/dist/chat/claude.js +228 -0
- package/dist/chat/claude.js.map +1 -0
- package/dist/chat/ingest_session.d.ts +97 -0
- package/dist/chat/ingest_session.d.ts.map +1 -0
- package/dist/chat/ingest_session.js +99 -0
- package/dist/chat/ingest_session.js.map +1 -0
- package/dist/chat/panel.d.ts +15 -0
- package/dist/chat/panel.d.ts.map +1 -0
- package/dist/chat/panel.js +1526 -0
- package/dist/chat/panel.js.map +1 -0
- package/dist/chat/persistence.d.ts +37 -0
- package/dist/chat/persistence.d.ts.map +1 -0
- package/dist/chat/persistence.js +91 -0
- package/dist/chat/persistence.js.map +1 -0
- package/dist/chat/server.d.ts +34 -0
- package/dist/chat/server.d.ts.map +1 -0
- package/dist/chat/server.js +1540 -0
- package/dist/chat/server.js.map +1 -0
- package/dist/chat/spec_extract.d.ts +35 -0
- package/dist/chat/spec_extract.d.ts.map +1 -0
- package/dist/chat/spec_extract.js +152 -0
- package/dist/chat/spec_extract.js.map +1 -0
- package/dist/chat/spec_plan.d.ts +65 -0
- package/dist/chat/spec_plan.d.ts.map +1 -0
- package/dist/chat/spec_plan.js +160 -0
- package/dist/chat/spec_plan.js.map +1 -0
- package/dist/chat/spec_scaffold.d.ts +95 -0
- package/dist/chat/spec_scaffold.d.ts.map +1 -0
- package/dist/chat/spec_scaffold.js +220 -0
- package/dist/chat/spec_scaffold.js.map +1 -0
- package/dist/chat/tools/git.d.ts +59 -0
- package/dist/chat/tools/git.d.ts.map +1 -0
- package/dist/chat/tools/git.js +313 -0
- package/dist/chat/tools/git.js.map +1 -0
- package/dist/chat/tools/github.d.ts +59 -0
- package/dist/chat/tools/github.d.ts.map +1 -0
- package/dist/chat/tools/github.js +310 -0
- package/dist/chat/tools/github.js.map +1 -0
- package/dist/chat/tools/lifecycle.d.ts +82 -0
- package/dist/chat/tools/lifecycle.d.ts.map +1 -0
- package/dist/chat/tools/lifecycle.js +295 -0
- package/dist/chat/tools/lifecycle.js.map +1 -0
- package/dist/chat/tools/manual.d.ts +26 -0
- package/dist/chat/tools/manual.d.ts.map +1 -0
- package/dist/chat/tools/manual.js +164 -0
- package/dist/chat/tools/manual.js.map +1 -0
- package/dist/chat/tools/reader.d.ts +80 -0
- package/dist/chat/tools/reader.d.ts.map +1 -0
- package/dist/chat/tools/reader.js +471 -0
- package/dist/chat/tools/reader.js.map +1 -0
- package/dist/chat/tools.d.ts +106 -0
- package/dist/chat/tools.d.ts.map +1 -0
- package/dist/chat/tools.js +587 -0
- package/dist/chat/tools.js.map +1 -0
- package/dist/codegen/e2e.d.ts +106 -0
- package/dist/codegen/e2e.d.ts.map +1 -0
- package/dist/codegen/e2e.js +931 -0
- package/dist/codegen/e2e.js.map +1 -0
- package/dist/codegen/v3_flow_emit.d.ts +70 -0
- package/dist/codegen/v3_flow_emit.d.ts.map +1 -0
- package/dist/codegen/v3_flow_emit.js +225 -0
- package/dist/codegen/v3_flow_emit.js.map +1 -0
- package/dist/commands/_stub.d.ts +2 -0
- package/dist/commands/_stub.d.ts.map +1 -0
- package/dist/commands/_stub.js +21 -0
- package/dist/commands/_stub.js.map +1 -0
- package/dist/commands/app.d.ts +31 -0
- package/dist/commands/app.d.ts.map +1 -0
- package/dist/commands/app.js +331 -0
- package/dist/commands/app.js.map +1 -0
- package/dist/commands/chat.d.ts +18 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/chat.js +76 -0
- package/dist/commands/chat.js.map +1 -0
- package/dist/commands/deploy.d.ts +21 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +121 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/doctor.d.ts +14 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +280 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/figma.d.ts +32 -0
- package/dist/commands/figma.d.ts.map +1 -0
- package/dist/commands/figma.js +141 -0
- package/dist/commands/figma.js.map +1 -0
- package/dist/commands/gen-flow-tests.d.ts +8 -0
- package/dist/commands/gen-flow-tests.d.ts.map +1 -0
- package/dist/commands/gen-flow-tests.js +78 -0
- package/dist/commands/gen-flow-tests.js.map +1 -0
- package/dist/commands/gen-tests.d.ts +9 -0
- package/dist/commands/gen-tests.d.ts.map +1 -0
- package/dist/commands/gen-tests.js +118 -0
- package/dist/commands/gen-tests.js.map +1 -0
- package/dist/commands/license.d.ts +14 -0
- package/dist/commands/license.d.ts.map +1 -0
- package/dist/commands/license.js +182 -0
- package/dist/commands/license.js.map +1 -0
- package/dist/commands/log.d.ts +19 -0
- package/dist/commands/log.d.ts.map +1 -0
- package/dist/commands/log.js +101 -0
- package/dist/commands/log.js.map +1 -0
- package/dist/commands/migrate.d.ts +118 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +1410 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/mobile.d.ts +27 -0
- package/dist/commands/mobile.d.ts.map +1 -0
- package/dist/commands/mobile.js +90 -0
- package/dist/commands/mobile.js.map +1 -0
- package/dist/commands/new.d.ts +32 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +107 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/pilot.d.ts +8 -0
- package/dist/commands/pilot.d.ts.map +1 -0
- package/dist/commands/pilot.js +104 -0
- package/dist/commands/pilot.js.map +1 -0
- package/dist/commands/projects.d.ts +21 -0
- package/dist/commands/projects.d.ts.map +1 -0
- package/dist/commands/projects.js +238 -0
- package/dist/commands/projects.js.map +1 -0
- package/dist/commands/publish.d.ts +35 -0
- package/dist/commands/publish.d.ts.map +1 -0
- package/dist/commands/publish.js +194 -0
- package/dist/commands/publish.js.map +1 -0
- package/dist/commands/repo.d.ts +59 -0
- package/dist/commands/repo.d.ts.map +1 -0
- package/dist/commands/repo.js +178 -0
- package/dist/commands/repo.js.map +1 -0
- package/dist/commands/review-screens.d.ts +28 -0
- package/dist/commands/review-screens.d.ts.map +1 -0
- package/dist/commands/review-screens.js +345 -0
- package/dist/commands/review-screens.js.map +1 -0
- package/dist/commands/scenarios.d.ts +23 -0
- package/dist/commands/scenarios.d.ts.map +1 -0
- package/dist/commands/scenarios.js +304 -0
- package/dist/commands/scenarios.js.map +1 -0
- package/dist/commands/ship.d.ts +18 -0
- package/dist/commands/ship.d.ts.map +1 -0
- package/dist/commands/ship.js +41 -0
- package/dist/commands/ship.js.map +1 -0
- package/dist/commands/test.d.ts +29 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +62 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/tunnel.d.ts +22 -0
- package/dist/commands/tunnel.d.ts.map +1 -0
- package/dist/commands/tunnel.js +77 -0
- package/dist/commands/tunnel.js.map +1 -0
- package/dist/commands/validate.d.ts +14 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +51 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/commands/vault.d.ts +32 -0
- package/dist/commands/vault.d.ts.map +1 -0
- package/dist/commands/vault.js +489 -0
- package/dist/commands/vault.js.map +1 -0
- package/dist/commands/voice.d.ts +16 -0
- package/dist/commands/voice.d.ts.map +1 -0
- package/dist/commands/voice.js +69 -0
- package/dist/commands/voice.js.map +1 -0
- package/dist/core/cascade_router.d.ts +90 -0
- package/dist/core/cascade_router.d.ts.map +1 -0
- package/dist/core/cascade_router.js +131 -0
- package/dist/core/cascade_router.js.map +1 -0
- package/dist/core/cf_tunnel.d.ts +52 -0
- package/dist/core/cf_tunnel.d.ts.map +1 -0
- package/dist/core/cf_tunnel.js +134 -0
- package/dist/core/cf_tunnel.js.map +1 -0
- package/dist/core/gha_dispatcher.d.ts +48 -0
- package/dist/core/gha_dispatcher.d.ts.map +1 -0
- package/dist/core/gha_dispatcher.js +198 -0
- package/dist/core/gha_dispatcher.js.map +1 -0
- package/dist/core/logger.d.ts +89 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +245 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/mode.d.ts +26 -0
- package/dist/core/mode.d.ts.map +1 -0
- package/dist/core/mode.js +122 -0
- package/dist/core/mode.js.map +1 -0
- package/dist/core/pairing.d.ts +40 -0
- package/dist/core/pairing.d.ts.map +1 -0
- package/dist/core/pairing.js +145 -0
- package/dist/core/pairing.js.map +1 -0
- package/dist/core/pilot_setup.d.ts +29 -0
- package/dist/core/pilot_setup.d.ts.map +1 -0
- package/dist/core/pilot_setup.js +119 -0
- package/dist/core/pilot_setup.js.map +1 -0
- package/dist/core/polar.d.ts +81 -0
- package/dist/core/polar.d.ts.map +1 -0
- package/dist/core/polar.js +175 -0
- package/dist/core/polar.js.map +1 -0
- package/dist/core/project_picker.d.ts +56 -0
- package/dist/core/project_picker.d.ts.map +1 -0
- package/dist/core/project_picker.js +86 -0
- package/dist/core/project_picker.js.map +1 -0
- package/dist/core/projects.d.ts +58 -0
- package/dist/core/projects.d.ts.map +1 -0
- package/dist/core/projects.js +146 -0
- package/dist/core/projects.js.map +1 -0
- package/dist/core/projects_sync.d.ts +80 -0
- package/dist/core/projects_sync.d.ts.map +1 -0
- package/dist/core/projects_sync.js +278 -0
- package/dist/core/projects_sync.js.map +1 -0
- package/dist/core/remote_runner.d.ts +70 -0
- package/dist/core/remote_runner.d.ts.map +1 -0
- package/dist/core/remote_runner.js +133 -0
- package/dist/core/remote_runner.js.map +1 -0
- package/dist/core/repo_state.d.ts +24 -0
- package/dist/core/repo_state.d.ts.map +1 -0
- package/dist/core/repo_state.js +109 -0
- package/dist/core/repo_state.js.map +1 -0
- package/dist/core/target.d.ts +31 -0
- package/dist/core/target.d.ts.map +1 -0
- package/dist/core/target.js +121 -0
- package/dist/core/target.js.map +1 -0
- package/dist/deploy/aws.d.ts +43 -0
- package/dist/deploy/aws.d.ts.map +1 -0
- package/dist/deploy/aws.js +173 -0
- package/dist/deploy/aws.js.map +1 -0
- package/dist/figma/api.d.ts +35 -0
- package/dist/figma/api.d.ts.map +1 -0
- package/dist/figma/api.js +40 -0
- package/dist/figma/api.js.map +1 -0
- package/dist/figma/decorator.d.ts +74 -0
- package/dist/figma/decorator.d.ts.map +1 -0
- package/dist/figma/decorator.js +210 -0
- package/dist/figma/decorator.js.map +1 -0
- package/dist/figma/heuristics.d.ts +29 -0
- package/dist/figma/heuristics.d.ts.map +1 -0
- package/dist/figma/heuristics.js +110 -0
- package/dist/figma/heuristics.js.map +1 -0
- package/dist/figma/normalize.d.ts +33 -0
- package/dist/figma/normalize.d.ts.map +1 -0
- package/dist/figma/normalize.js +101 -0
- package/dist/figma/normalize.js.map +1 -0
- package/dist/figma/tokens.d.ts +23 -0
- package/dist/figma/tokens.d.ts.map +1 -0
- package/dist/figma/tokens.js +111 -0
- package/dist/figma/tokens.js.map +1 -0
- package/dist/figma/types.d.ts +118 -0
- package/dist/figma/types.d.ts.map +1 -0
- package/dist/figma/types.js +12 -0
- package/dist/figma/types.js.map +1 -0
- package/dist/i18n/index.d.ts +48 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +135 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/types.d.ts +52 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/i18n/types.js +85 -0
- package/dist/i18n/types.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/lan/mdns_packet.d.ts +74 -0
- package/dist/lan/mdns_packet.d.ts.map +1 -0
- package/dist/lan/mdns_packet.js +247 -0
- package/dist/lan/mdns_packet.js.map +1 -0
- package/dist/lan/mdns_service.d.ts +102 -0
- package/dist/lan/mdns_service.d.ts.map +1 -0
- package/dist/lan/mdns_service.js +206 -0
- package/dist/lan/mdns_service.js.map +1 -0
- package/dist/license/activate.d.ts +33 -0
- package/dist/license/activate.d.ts.map +1 -0
- package/dist/license/activate.js +135 -0
- package/dist/license/activate.js.map +1 -0
- package/dist/license/fingerprint.d.ts +2 -0
- package/dist/license/fingerprint.d.ts.map +1 -0
- package/dist/license/fingerprint.js +29 -0
- package/dist/license/fingerprint.js.map +1 -0
- package/dist/license/hito4_client.d.ts +24 -0
- package/dist/license/hito4_client.d.ts.map +1 -0
- package/dist/license/hito4_client.js +103 -0
- package/dist/license/hito4_client.js.map +1 -0
- package/dist/license/index.d.ts +22 -0
- package/dist/license/index.d.ts.map +1 -0
- package/dist/license/index.js +125 -0
- package/dist/license/index.js.map +1 -0
- package/dist/license/types.d.ts +38 -0
- package/dist/license/types.d.ts.map +1 -0
- package/dist/license/types.js +9 -0
- package/dist/license/types.js.map +1 -0
- package/dist/migrate/ai-apply.d.ts +198 -0
- package/dist/migrate/ai-apply.d.ts.map +1 -0
- package/dist/migrate/ai-apply.js +833 -0
- package/dist/migrate/ai-apply.js.map +1 -0
- package/dist/migrate/ai-decorator.d.ts +87 -0
- package/dist/migrate/ai-decorator.d.ts.map +1 -0
- package/dist/migrate/ai-decorator.js +203 -0
- package/dist/migrate/ai-decorator.js.map +1 -0
- package/dist/migrate/apply.d.ts +28 -0
- package/dist/migrate/apply.d.ts.map +1 -0
- package/dist/migrate/apply.js +119 -0
- package/dist/migrate/apply.js.map +1 -0
- package/dist/migrate/audit.d.ts +9 -0
- package/dist/migrate/audit.d.ts.map +1 -0
- package/dist/migrate/audit.js +197 -0
- package/dist/migrate/audit.js.map +1 -0
- package/dist/migrate/diff.d.ts +28 -0
- package/dist/migrate/diff.d.ts.map +1 -0
- package/dist/migrate/diff.js +154 -0
- package/dist/migrate/diff.js.map +1 -0
- package/dist/migrate/html-orchestrator.d.ts +81 -0
- package/dist/migrate/html-orchestrator.d.ts.map +1 -0
- package/dist/migrate/html-orchestrator.js +233 -0
- package/dist/migrate/html-orchestrator.js.map +1 -0
- package/dist/migrate/html-walker.d.ts +93 -0
- package/dist/migrate/html-walker.d.ts.map +1 -0
- package/dist/migrate/html-walker.js +288 -0
- package/dist/migrate/html-walker.js.map +1 -0
- package/dist/migrate/js-template-walker.d.ts +118 -0
- package/dist/migrate/js-template-walker.d.ts.map +1 -0
- package/dist/migrate/js-template-walker.js +644 -0
- package/dist/migrate/js-template-walker.js.map +1 -0
- package/dist/migrate/manifest-validator.d.ts +30 -0
- package/dist/migrate/manifest-validator.d.ts.map +1 -0
- package/dist/migrate/manifest-validator.js +261 -0
- package/dist/migrate/manifest-validator.js.map +1 -0
- package/dist/migrate/overrides.d.ts +58 -0
- package/dist/migrate/overrides.d.ts.map +1 -0
- package/dist/migrate/overrides.js +193 -0
- package/dist/migrate/overrides.js.map +1 -0
- package/dist/migrate/plugin-scope.d.ts +42 -0
- package/dist/migrate/plugin-scope.d.ts.map +1 -0
- package/dist/migrate/plugin-scope.js +94 -0
- package/dist/migrate/plugin-scope.js.map +1 -0
- package/dist/migrate/types.d.ts +45 -0
- package/dist/migrate/types.d.ts.map +1 -0
- package/dist/migrate/types.js +9 -0
- package/dist/migrate/types.js.map +1 -0
- package/dist/migrate/verb-inference.d.ts +37 -0
- package/dist/migrate/verb-inference.d.ts.map +1 -0
- package/dist/migrate/verb-inference.js +274 -0
- package/dist/migrate/verb-inference.js.map +1 -0
- package/dist/nac3/attrs.d.ts +87 -0
- package/dist/nac3/attrs.d.ts.map +1 -0
- package/dist/nac3/attrs.js +134 -0
- package/dist/nac3/attrs.js.map +1 -0
- package/dist/nac3/scenario_dsl.d.ts +71 -0
- package/dist/nac3/scenario_dsl.d.ts.map +1 -0
- package/dist/nac3/scenario_dsl.js +191 -0
- package/dist/nac3/scenario_dsl.js.map +1 -0
- package/dist/nac3/tokens.d.ts +126 -0
- package/dist/nac3/tokens.d.ts.map +1 -0
- package/dist/nac3/tokens.js +138 -0
- package/dist/nac3/tokens.js.map +1 -0
- package/dist/reader/parsers/csv.d.ts +42 -0
- package/dist/reader/parsers/csv.d.ts.map +1 -0
- package/dist/reader/parsers/csv.js +221 -0
- package/dist/reader/parsers/csv.js.map +1 -0
- package/dist/reader/parsers/docx.d.ts +31 -0
- package/dist/reader/parsers/docx.d.ts.map +1 -0
- package/dist/reader/parsers/docx.js +51 -0
- package/dist/reader/parsers/docx.js.map +1 -0
- package/dist/reader/parsers/epub.d.ts +39 -0
- package/dist/reader/parsers/epub.d.ts.map +1 -0
- package/dist/reader/parsers/epub.js +265 -0
- package/dist/reader/parsers/epub.js.map +1 -0
- package/dist/reader/parsers/html.d.ts +40 -0
- package/dist/reader/parsers/html.d.ts.map +1 -0
- package/dist/reader/parsers/html.js +386 -0
- package/dist/reader/parsers/html.js.map +1 -0
- package/dist/reader/parsers/md.d.ts +30 -0
- package/dist/reader/parsers/md.d.ts.map +1 -0
- package/dist/reader/parsers/md.js +199 -0
- package/dist/reader/parsers/md.js.map +1 -0
- package/dist/reader/parsers/pdf.d.ts +39 -0
- package/dist/reader/parsers/pdf.d.ts.map +1 -0
- package/dist/reader/parsers/pdf.js +220 -0
- package/dist/reader/parsers/pdf.js.map +1 -0
- package/dist/reader/parsers/rtf.d.ts +37 -0
- package/dist/reader/parsers/rtf.d.ts.map +1 -0
- package/dist/reader/parsers/rtf.js +347 -0
- package/dist/reader/parsers/rtf.js.map +1 -0
- package/dist/reader/parsers/source.d.ts +32 -0
- package/dist/reader/parsers/source.d.ts.map +1 -0
- package/dist/reader/parsers/source.js +122 -0
- package/dist/reader/parsers/source.js.map +1 -0
- package/dist/reader/parsers/txt.d.ts +25 -0
- package/dist/reader/parsers/txt.d.ts.map +1 -0
- package/dist/reader/parsers/txt.js +56 -0
- package/dist/reader/parsers/txt.js.map +1 -0
- package/dist/reader/parsers/xlsx.d.ts +33 -0
- package/dist/reader/parsers/xlsx.d.ts.map +1 -0
- package/dist/reader/parsers/xlsx.js +143 -0
- package/dist/reader/parsers/xlsx.js.map +1 -0
- package/dist/reader/registry.d.ts +39 -0
- package/dist/reader/registry.d.ts.map +1 -0
- package/dist/reader/registry.js +172 -0
- package/dist/reader/registry.js.map +1 -0
- package/dist/reader/search.d.ts +27 -0
- package/dist/reader/search.d.ts.map +1 -0
- package/dist/reader/search.js +77 -0
- package/dist/reader/search.js.map +1 -0
- package/dist/reader/state.d.ts +56 -0
- package/dist/reader/state.d.ts.map +1 -0
- package/dist/reader/state.js +179 -0
- package/dist/reader/state.js.map +1 -0
- package/dist/reader/types.d.ts +119 -0
- package/dist/reader/types.d.ts.map +1 -0
- package/dist/reader/types.js +23 -0
- package/dist/reader/types.js.map +1 -0
- package/dist/ship/run.d.ts +26 -0
- package/dist/ship/run.d.ts.map +1 -0
- package/dist/ship/run.js +123 -0
- package/dist/ship/run.js.map +1 -0
- package/dist/template/index.d.ts +50 -0
- package/dist/template/index.d.ts.map +1 -0
- package/dist/template/index.js +140 -0
- package/dist/template/index.js.map +1 -0
- package/dist/ui/colors.d.ts +13 -0
- package/dist/ui/colors.d.ts.map +1 -0
- package/dist/ui/colors.js +26 -0
- package/dist/ui/colors.js.map +1 -0
- package/dist/validate/index.d.ts +19 -0
- package/dist/validate/index.d.ts.map +1 -0
- package/dist/validate/index.js +181 -0
- package/dist/validate/index.js.map +1 -0
- package/dist/vault/catalog.d.ts +55 -0
- package/dist/vault/catalog.d.ts.map +1 -0
- package/dist/vault/catalog.js +424 -0
- package/dist/vault/catalog.js.map +1 -0
- package/dist/vault/crypto.d.ts +82 -0
- package/dist/vault/crypto.d.ts.map +1 -0
- package/dist/vault/crypto.js +173 -0
- package/dist/vault/crypto.js.map +1 -0
- package/dist/vault/git_askpass.d.ts +26 -0
- package/dist/vault/git_askpass.d.ts.map +1 -0
- package/dist/vault/git_askpass.js +104 -0
- package/dist/vault/git_askpass.js.map +1 -0
- package/dist/vault/migrator.d.ts +57 -0
- package/dist/vault/migrator.d.ts.map +1 -0
- package/dist/vault/migrator.js +204 -0
- package/dist/vault/migrator.js.map +1 -0
- package/dist/vault/redactor.d.ts +73 -0
- package/dist/vault/redactor.d.ts.map +1 -0
- package/dist/vault/redactor.js +182 -0
- package/dist/vault/redactor.js.map +1 -0
- package/dist/vault/store.d.ts +132 -0
- package/dist/vault/store.d.ts.map +1 -0
- package/dist/vault/store.js +335 -0
- package/dist/vault/store.js.map +1 -0
- package/dist/version.d.ts +8 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +8 -0
- package/dist/version.js.map +1 -0
- package/dist/voice/chunker.d.ts +43 -0
- package/dist/voice/chunker.d.ts.map +1 -0
- package/dist/voice/chunker.js +133 -0
- package/dist/voice/chunker.js.map +1 -0
- package/dist/voice/config.d.ts +14 -0
- package/dist/voice/config.d.ts.map +1 -0
- package/dist/voice/config.js +51 -0
- package/dist/voice/config.js.map +1 -0
- package/dist/voice/intents.d.ts +71 -0
- package/dist/voice/intents.d.ts.map +1 -0
- package/dist/voice/intents.js +0 -0
- package/dist/voice/intents.js.map +1 -0
- package/dist/voice/providers/elevenlabs.d.ts +53 -0
- package/dist/voice/providers/elevenlabs.d.ts.map +1 -0
- package/dist/voice/providers/elevenlabs.js +159 -0
- package/dist/voice/providers/elevenlabs.js.map +1 -0
- package/dist/voice/providers/google.d.ts +56 -0
- package/dist/voice/providers/google.d.ts.map +1 -0
- package/dist/voice/providers/google.js +253 -0
- package/dist/voice/providers/google.js.map +1 -0
- package/dist/voice/providers/whisper.d.ts +44 -0
- package/dist/voice/providers/whisper.d.ts.map +1 -0
- package/dist/voice/providers/whisper.js +179 -0
- package/dist/voice/providers/whisper.js.map +1 -0
- package/dist/voice/registry.d.ts +35 -0
- package/dist/voice/registry.d.ts.map +1 -0
- package/dist/voice/registry.js +48 -0
- package/dist/voice/registry.js.map +1 -0
- package/dist/voice/router.d.ts +62 -0
- package/dist/voice/router.d.ts.map +1 -0
- package/dist/voice/router.js +175 -0
- package/dist/voice/router.js.map +1 -0
- package/dist/voice/types.d.ts +116 -0
- package/dist/voice/types.d.ts.map +1 -0
- package/dist/voice/types.js +22 -0
- package/dist/voice/types.js.map +1 -0
- package/dist/voice/voiceprint/enrollment.d.ts +36 -0
- package/dist/voice/voiceprint/enrollment.d.ts.map +1 -0
- package/dist/voice/voiceprint/enrollment.js +71 -0
- package/dist/voice/voiceprint/enrollment.js.map +1 -0
- package/dist/voice/voiceprint/identify.d.ts +16 -0
- package/dist/voice/voiceprint/identify.d.ts.map +1 -0
- package/dist/voice/voiceprint/identify.js +20 -0
- package/dist/voice/voiceprint/identify.js.map +1 -0
- package/dist/voice/voiceprint/liveness.d.ts +90 -0
- package/dist/voice/voiceprint/liveness.d.ts.map +1 -0
- package/dist/voice/voiceprint/liveness.js +251 -0
- package/dist/voice/voiceprint/liveness.js.map +1 -0
- package/dist/voice/voiceprint/match.d.ts +54 -0
- package/dist/voice/voiceprint/match.d.ts.map +1 -0
- package/dist/voice/voiceprint/match.js +88 -0
- package/dist/voice/voiceprint/match.js.map +1 -0
- package/dist/voice/voiceprint/providers/local-mfcc-stub.d.ts +44 -0
- package/dist/voice/voiceprint/providers/local-mfcc-stub.d.ts.map +1 -0
- package/dist/voice/voiceprint/providers/local-mfcc-stub.js +92 -0
- package/dist/voice/voiceprint/providers/local-mfcc-stub.js.map +1 -0
- package/dist/voice/voiceprint/store.d.ts +60 -0
- package/dist/voice/voiceprint/store.d.ts.map +1 -0
- package/dist/voice/voiceprint/store.js +155 -0
- package/dist/voice/voiceprint/store.js.map +1 -0
- package/dist/voice/voiceprint/trust.d.ts +90 -0
- package/dist/voice/voiceprint/trust.d.ts.map +1 -0
- package/dist/voice/voiceprint/trust.js +150 -0
- package/dist/voice/voiceprint/trust.js.map +1 -0
- package/dist/voice/voiceprint/types.d.ts +100 -0
- package/dist/voice/voiceprint/types.d.ts.map +1 -0
- package/dist/voice/voiceprint/types.js +23 -0
- package/dist/voice/voiceprint/types.js.map +1 -0
- package/dist/voice/wake.d.ts +64 -0
- package/dist/voice/wake.d.ts.map +1 -0
- package/dist/voice/wake.js +143 -0
- package/dist/voice/wake.js.map +1 -0
- package/docs/manuals/manual.ar.html +91 -0
- package/docs/manuals/manual.de.html +100 -0
- package/docs/manuals/manual.en.html +118 -0
- package/docs/manuals/manual.es.html +120 -0
- package/docs/manuals/manual.fr.html +102 -0
- package/docs/manuals/manual.hi.html +93 -0
- package/docs/manuals/manual.it.html +93 -0
- package/docs/manuals/manual.ja.html +97 -0
- package/docs/manuals/manual.pt.html +103 -0
- package/docs/manuals/manual.zh.html +89 -0
- package/package.json +94 -0
- package/src/i18n/catalogs/ar.json +86 -0
- package/src/i18n/catalogs/de.json +86 -0
- package/src/i18n/catalogs/en.json +86 -0
- package/src/i18n/catalogs/es.json +86 -0
- package/src/i18n/catalogs/fr.json +86 -0
- package/src/i18n/catalogs/hi.json +86 -0
- package/src/i18n/catalogs/it.json +86 -0
- package/src/i18n/catalogs/ja.json +86 -0
- package/src/i18n/catalogs/pt.json +86 -0
- package/src/i18n/catalogs/zh.json +86 -0
- package/templates/react-app/README.md +43 -0
- package/templates/react-app/index.html +12 -0
- package/templates/react-app/package.json +35 -0
- package/templates/react-app/src/App.tsx +106 -0
- package/templates/react-app/src/main.tsx +21 -0
- package/templates/react-app/src/nac/manifest.ts +46 -0
- package/templates/react-app/src/styles.css +68 -0
- package/templates/react-app/tsconfig.json +19 -0
- package/templates/react-app/vite.config.ts +12 -0
- package/templates/react-app/yujin.forge.json +15 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Figma -> NAC-3 decorator. Bridge between the normalised Figma
|
|
3
|
+
* tree (src/figma/normalize.ts) and emitted, NAC-3-decorated source
|
|
4
|
+
* code (React JSX or vanilla HTML).
|
|
5
|
+
*
|
|
6
|
+
* Pipeline:
|
|
7
|
+
* 1. Build a prompt describing the page + plugin slug + the list
|
|
8
|
+
* of NormalisedElement entries with their roles + verbs.
|
|
9
|
+
* 2. Ask Claude to emit a single JSON object containing:
|
|
10
|
+
* - decorated source per screen (the JSX or HTML the dev
|
|
11
|
+
* downloads),
|
|
12
|
+
* - the NAC manifest fragment,
|
|
13
|
+
* - the CSS file with the resolved design tokens.
|
|
14
|
+
* 3. Hand the result back to the CLI wrapper which writes the
|
|
15
|
+
* files + invokes the same nac-bridge + register wiring the
|
|
16
|
+
* AI HTML decorator uses.
|
|
17
|
+
*
|
|
18
|
+
* The Claude call is a single DI seam (decorator? on the opts) so
|
|
19
|
+
* tests can stub the network. Mirrors the seam already used by
|
|
20
|
+
* aiDecorateFile (G4).
|
|
21
|
+
*
|
|
22
|
+
* ASCII-only.
|
|
23
|
+
*/
|
|
24
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
25
|
+
const DEFAULT_MODEL = 'claude-sonnet-4-6';
|
|
26
|
+
const PRICING = {
|
|
27
|
+
'claude-sonnet-4-6': { in: 3.00, out: 15.00 },
|
|
28
|
+
'claude-haiku-4-5': { in: 0.80, out: 4.00 },
|
|
29
|
+
'claude-haiku-4-5-20251001': { in: 0.80, out: 4.00 },
|
|
30
|
+
'claude-opus-4-7': { in: 15.00, out: 75.00 },
|
|
31
|
+
};
|
|
32
|
+
function resolveAnthropicModelId(id) {
|
|
33
|
+
if (id === 'claude-haiku-4-5')
|
|
34
|
+
return 'claude-haiku-4-5-20251001';
|
|
35
|
+
return id;
|
|
36
|
+
}
|
|
37
|
+
function computeCost(model, usage) {
|
|
38
|
+
const p = PRICING[model] ?? PRICING['claude-sonnet-4-6'];
|
|
39
|
+
return (usage.input * p.in + usage.output * p.out) / 1_000_000;
|
|
40
|
+
}
|
|
41
|
+
const SYSTEM_PROMPT = `You are a NAC-3 (Native Agent Contract) decorator. Given a normalised Figma file (screens + elements with provisional NAC roles + verb hints), you emit:
|
|
42
|
+
(a) per-screen source code in the requested framework with data-nac-id / data-nac-role / data-nac-action attributes already in place,
|
|
43
|
+
(b) the NAC manifest fragment with one element entry per decorated node,
|
|
44
|
+
(c) a CSS file with the design tokens exposed as :root custom properties.
|
|
45
|
+
|
|
46
|
+
ROLE TAXONOMY (carry from input -- never downgrade):
|
|
47
|
+
- action: a clickable element. Always carries data-nac-action="<verb>".
|
|
48
|
+
- field: a value-holding element (input / textarea / output panel).
|
|
49
|
+
- region: a container; no verb.
|
|
50
|
+
- option / tab / navigation: same shape as action but with the role spelled out.
|
|
51
|
+
- heading / paragraph: presentational text. Do not assign data-nac-role; emit plain HTML.
|
|
52
|
+
|
|
53
|
+
ID PATTERN: "<plugin_slug>.<role>.<thing>", snake_case, lowercase. Example: "site.action.sign_in".
|
|
54
|
+
|
|
55
|
+
VERB NAMING: short imperative (submit, login, save, delete, edit, cancel, search, navigate). Use the verb_hint from the input when present; only deviate when the hint clearly does not fit.
|
|
56
|
+
|
|
57
|
+
OUTPUT FORMAT -- you reply with a single JSON object, no prose, no Markdown fences:
|
|
58
|
+
{
|
|
59
|
+
"framework": "react|html|vue",
|
|
60
|
+
"plugin_slug": "<slug>",
|
|
61
|
+
"screens": [
|
|
62
|
+
{ "slug": "/route", "basename": "Route.tsx", "source": "<full source>" }
|
|
63
|
+
],
|
|
64
|
+
"manifest_json": "<the manifest object stringified>",
|
|
65
|
+
"css": "<the design tokens CSS file body>"
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
Rules:
|
|
69
|
+
1. Emit ONE file per screen. For react use .tsx; for html use .html; for vue use .vue.
|
|
70
|
+
2. Preserve the design tokens by referencing the CSS custom properties (e.g. color: var(--color-primary)) instead of hex literals.
|
|
71
|
+
3. data-nac-role + data-nac-id on every interactive element. data-nac-action on action role only.
|
|
72
|
+
4. The <body> (or <main> in JSX scaffolds) carries data-nac-plugin="<plugin_slug>" exactly.
|
|
73
|
+
5. Decorated source is buildable as-is (imports resolved against the framework default: react/react-dom for tsx).
|
|
74
|
+
6. manifest_json field is the JSON manifest as a STRING (escape inner quotes).
|
|
75
|
+
`;
|
|
76
|
+
export async function decorateFigma(input, caller) {
|
|
77
|
+
const framework = input.framework || 'react';
|
|
78
|
+
const model = input.model || DEFAULT_MODEL;
|
|
79
|
+
const pluginSlug = input.pluginSlug || derivePluginSlug(input.file.source.name);
|
|
80
|
+
const callerImpl = caller || makeAnthropicCaller();
|
|
81
|
+
const userPrompt = buildUserPrompt(input.file, framework, pluginSlug);
|
|
82
|
+
const sys = SYSTEM_PROMPT;
|
|
83
|
+
const out = await callerImpl({ model, systemPrompt: sys, userPrompt });
|
|
84
|
+
let parsed;
|
|
85
|
+
try {
|
|
86
|
+
parsed = JSON.parse(extractJsonObject(out.text));
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
throw new Error('Figma decorator: Claude returned non-parseable JSON. First 400 chars: ' + out.text.slice(0, 400));
|
|
90
|
+
}
|
|
91
|
+
if (!parsed || typeof parsed !== 'object' || !Array.isArray(parsed.screens)) {
|
|
92
|
+
throw new Error('Figma decorator: response missing the "screens" array.');
|
|
93
|
+
}
|
|
94
|
+
const screens = parsed.screens.map((s) => ({
|
|
95
|
+
slug: String(s.slug || '/'),
|
|
96
|
+
basename: String(s.basename || 'index.html'),
|
|
97
|
+
source: String(s.source || ''),
|
|
98
|
+
}));
|
|
99
|
+
return {
|
|
100
|
+
framework,
|
|
101
|
+
plugin_slug: String(parsed.plugin_slug || pluginSlug),
|
|
102
|
+
screens,
|
|
103
|
+
manifest_json: String(parsed.manifest_json || ''),
|
|
104
|
+
css: String(parsed.css || ''),
|
|
105
|
+
meta: {
|
|
106
|
+
model,
|
|
107
|
+
input_tokens: out.input_tokens,
|
|
108
|
+
output_tokens: out.output_tokens,
|
|
109
|
+
cost_usd: computeCost(model, { input: out.input_tokens, output: out.output_tokens }),
|
|
110
|
+
latency_ms: out.latency_ms,
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function buildUserPrompt(file, framework, pluginSlug) {
|
|
115
|
+
/* The prompt body is JSON-friendly: a single object with the
|
|
116
|
+
project header + per-screen element lists. We do NOT inline the
|
|
117
|
+
Figma DOCUMENT tree -- it is too noisy and the normaliser
|
|
118
|
+
already discarded the layout-only nodes. */
|
|
119
|
+
const slim = {
|
|
120
|
+
framework,
|
|
121
|
+
plugin_slug: pluginSlug,
|
|
122
|
+
project: file.source.name,
|
|
123
|
+
tokens: file.tokens,
|
|
124
|
+
screens: file.screens.map((s) => ({
|
|
125
|
+
slug: s.slug,
|
|
126
|
+
name: s.name,
|
|
127
|
+
elements: s.elements.map((e) => ({
|
|
128
|
+
figma_node_id: e.figma_node_id,
|
|
129
|
+
name: e.name,
|
|
130
|
+
role: e.role,
|
|
131
|
+
verb_hint: e.verb_hint,
|
|
132
|
+
text: e.text,
|
|
133
|
+
depth: e.depth,
|
|
134
|
+
})),
|
|
135
|
+
})),
|
|
136
|
+
};
|
|
137
|
+
return 'Normalised Figma input (JSON):\n\n' + JSON.stringify(slim, null, 2) +
|
|
138
|
+
'\n\nDecorate per the rules above. Emit a single JSON object as defined in the system prompt.';
|
|
139
|
+
}
|
|
140
|
+
function derivePluginSlug(projectName) {
|
|
141
|
+
const base = (projectName || 'app').toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '');
|
|
142
|
+
return base || 'app';
|
|
143
|
+
}
|
|
144
|
+
function extractJsonObject(text) {
|
|
145
|
+
const fenced = text.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
|
|
146
|
+
if (fenced && fenced[1])
|
|
147
|
+
return fenced[1].trim();
|
|
148
|
+
const trimmed = text.trim();
|
|
149
|
+
if (trimmed.startsWith('{') && trimmed.endsWith('}'))
|
|
150
|
+
return trimmed;
|
|
151
|
+
const i = trimmed.indexOf('{');
|
|
152
|
+
const j = trimmed.lastIndexOf('}');
|
|
153
|
+
if (i >= 0 && j > i)
|
|
154
|
+
return trimmed.slice(i, j + 1);
|
|
155
|
+
return trimmed;
|
|
156
|
+
}
|
|
157
|
+
const _client = {};
|
|
158
|
+
function client() {
|
|
159
|
+
if (!_client.c)
|
|
160
|
+
_client.c = new Anthropic();
|
|
161
|
+
return _client.c;
|
|
162
|
+
}
|
|
163
|
+
function makeAnthropicCaller() {
|
|
164
|
+
return async ({ model, systemPrompt, userPrompt }) => {
|
|
165
|
+
if (!process.env.ANTHROPIC_API_KEY) {
|
|
166
|
+
throw new Error('ANTHROPIC_API_KEY env variable missing -- set it before running `yf figma`.');
|
|
167
|
+
}
|
|
168
|
+
const t0 = Date.now();
|
|
169
|
+
const skipTemp = /^claude-opus-4-7/.test(model);
|
|
170
|
+
const payload = {
|
|
171
|
+
model: resolveAnthropicModelId(model),
|
|
172
|
+
max_tokens: 16000,
|
|
173
|
+
system: systemPrompt,
|
|
174
|
+
messages: [{ role: 'user', content: userPrompt }],
|
|
175
|
+
};
|
|
176
|
+
if (!skipTemp)
|
|
177
|
+
payload.temperature = 0;
|
|
178
|
+
const resp = await client().messages.create(payload);
|
|
179
|
+
const block = resp.content.find((b) => b.type === 'text');
|
|
180
|
+
return {
|
|
181
|
+
text: block?.text || '',
|
|
182
|
+
input_tokens: resp.usage.input_tokens || 0,
|
|
183
|
+
output_tokens: resp.usage.output_tokens || 0,
|
|
184
|
+
latency_ms: Date.now() - t0,
|
|
185
|
+
};
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
/* Exported for tests + reuse by the CLI wrapper. */
|
|
189
|
+
export { derivePluginSlug, buildUserPrompt };
|
|
190
|
+
/* Convenience: pretty-format the per-screen + token summary for the
|
|
191
|
+
CLI report (`yf figma`). Pure. */
|
|
192
|
+
export function summariseDecoration(result, file) {
|
|
193
|
+
const lines = [];
|
|
194
|
+
lines.push('framework: ' + result.framework);
|
|
195
|
+
lines.push('plugin: ' + result.plugin_slug);
|
|
196
|
+
lines.push('screens: ' + result.screens.length);
|
|
197
|
+
for (const s of result.screens) {
|
|
198
|
+
const original = file.screens.find((x) => x.slug === s.slug);
|
|
199
|
+
const elCount = original ? original.elements.length : 0;
|
|
200
|
+
lines.push(' - ' + s.slug + ' ' + s.basename + ' (' + elCount + ' input element(s))');
|
|
201
|
+
}
|
|
202
|
+
lines.push('tokens: ' + Object.keys(file.tokens.colors).length + ' color, '
|
|
203
|
+
+ Object.keys(file.tokens.fonts).length + ' font, '
|
|
204
|
+
+ Object.keys(file.tokens.spacing).length + ' spacing');
|
|
205
|
+
lines.push('tokens: ' + result.meta.input_tokens + ' in / ' + result.meta.output_tokens + ' out');
|
|
206
|
+
lines.push('cost: $' + result.meta.cost_usd.toFixed(4));
|
|
207
|
+
lines.push('latency: ' + result.meta.latency_ms + 'ms');
|
|
208
|
+
return lines.join('\n');
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=decorator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decorator.js","sourceRoot":"","sources":["../../src/figma/decorator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Figma-node-type -> NAC-3 role heuristic mapper.
|
|
3
|
+
*
|
|
4
|
+
* The Figma file format does not declare whether a node is "an
|
|
5
|
+
* interactive button" or "a static heading" -- adopters use FRAMES
|
|
6
|
+
* + INSTANCES with naming conventions. We approximate by:
|
|
7
|
+
*
|
|
8
|
+
* - Mapping the node type first (e.g. INSTANCE -> action candidate
|
|
9
|
+
* when its component name contains "button").
|
|
10
|
+
* - Looking at the layer name for verbs / role hints
|
|
11
|
+
* ("/icons/email" -> not an action; "Login" -> action).
|
|
12
|
+
* - Allowing per-project overrides via figma-name-overrides.json
|
|
13
|
+
* (loaded by the CLI wrapper, not this helper).
|
|
14
|
+
*
|
|
15
|
+
* Pure functions. Easy to unit-test against synthetic Figma JSON.
|
|
16
|
+
*
|
|
17
|
+
* ASCII-only.
|
|
18
|
+
*/
|
|
19
|
+
import type { FigmaNode, NacRole } from './types.js';
|
|
20
|
+
export interface MappedRole {
|
|
21
|
+
role: NacRole;
|
|
22
|
+
verb_hint?: string;
|
|
23
|
+
/** Optional short reason for the audit trail. */
|
|
24
|
+
reason: string;
|
|
25
|
+
}
|
|
26
|
+
/** Returns a role mapping for a node, or null when the node should
|
|
27
|
+
* be skipped (pure decoration: VECTOR, RECTANGLE, IMAGE etc). */
|
|
28
|
+
export declare function mapNodeRole(node: FigmaNode, parentName?: string): MappedRole | null;
|
|
29
|
+
//# sourceMappingURL=heuristics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heuristics.d.ts","sourceRoot":"","sources":["../../src/figma/heuristics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;kEACkE;AAClE,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CA8DnF"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Figma-node-type -> NAC-3 role heuristic mapper.
|
|
3
|
+
*
|
|
4
|
+
* The Figma file format does not declare whether a node is "an
|
|
5
|
+
* interactive button" or "a static heading" -- adopters use FRAMES
|
|
6
|
+
* + INSTANCES with naming conventions. We approximate by:
|
|
7
|
+
*
|
|
8
|
+
* - Mapping the node type first (e.g. INSTANCE -> action candidate
|
|
9
|
+
* when its component name contains "button").
|
|
10
|
+
* - Looking at the layer name for verbs / role hints
|
|
11
|
+
* ("/icons/email" -> not an action; "Login" -> action).
|
|
12
|
+
* - Allowing per-project overrides via figma-name-overrides.json
|
|
13
|
+
* (loaded by the CLI wrapper, not this helper).
|
|
14
|
+
*
|
|
15
|
+
* Pure functions. Easy to unit-test against synthetic Figma JSON.
|
|
16
|
+
*
|
|
17
|
+
* ASCII-only.
|
|
18
|
+
*/
|
|
19
|
+
/** Returns a role mapping for a node, or null when the node should
|
|
20
|
+
* be skipped (pure decoration: VECTOR, RECTANGLE, IMAGE etc). */
|
|
21
|
+
export function mapNodeRole(node, parentName) {
|
|
22
|
+
/* Layer-name heuristics fire first, regardless of node type, because
|
|
23
|
+
adopters often signal intent via naming (e.g. "btn/primary",
|
|
24
|
+
"input/email"). */
|
|
25
|
+
const name = (node.name || '').toLowerCase();
|
|
26
|
+
const parent = (parentName || '').toLowerCase();
|
|
27
|
+
if (matchesAny(name, ['btn', 'button', 'submit', 'cta'])
|
|
28
|
+
|| matchesAny(parent, ['buttons', 'cta'])) {
|
|
29
|
+
return {
|
|
30
|
+
role: 'action',
|
|
31
|
+
verb_hint: inferVerb(name) || 'submit',
|
|
32
|
+
reason: 'name hint button',
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (matchesAny(name, ['input', 'field', 'textfield', 'textarea', 'search'])) {
|
|
36
|
+
return { role: 'field', reason: 'name hint field' };
|
|
37
|
+
}
|
|
38
|
+
if (matchesAny(name, ['nav', 'menu', 'navbar', 'navigation'])) {
|
|
39
|
+
/* The NAV itself is a navigation group; its children are nav items. */
|
|
40
|
+
return { role: 'navigation', reason: 'name hint navigation' };
|
|
41
|
+
}
|
|
42
|
+
if (matchesAny(name, ['tab', 'tabbar'])) {
|
|
43
|
+
return { role: 'tab', reason: 'name hint tab' };
|
|
44
|
+
}
|
|
45
|
+
if (matchesAny(name, ['option', 'radio', 'checkbox'])) {
|
|
46
|
+
return { role: 'option', reason: 'name hint option' };
|
|
47
|
+
}
|
|
48
|
+
if (matchesAny(name, ['heading', 'h1', 'h2', 'h3', 'title'])) {
|
|
49
|
+
return { role: 'heading', reason: 'name hint heading' };
|
|
50
|
+
}
|
|
51
|
+
/* Fall back to node type. */
|
|
52
|
+
switch (node.type) {
|
|
53
|
+
case 'INSTANCE':
|
|
54
|
+
/* Without a name hint, assume INSTANCE is decorative -- adopters
|
|
55
|
+
override per-component via figma-name-overrides.json. */
|
|
56
|
+
return null;
|
|
57
|
+
case 'TEXT': {
|
|
58
|
+
/* Distinguish heading-sized text from paragraph by font size.
|
|
59
|
+
A heading is anything >= 20px (Figma default heading sizes
|
|
60
|
+
tend to be 24/28/32). */
|
|
61
|
+
const size = node.style?.fontSize ?? 0;
|
|
62
|
+
if (size >= 20)
|
|
63
|
+
return { role: 'heading', reason: 'TEXT fontSize >= 20' };
|
|
64
|
+
return { role: 'paragraph', reason: 'TEXT default' };
|
|
65
|
+
}
|
|
66
|
+
case 'FRAME':
|
|
67
|
+
case 'GROUP':
|
|
68
|
+
case 'COMPONENT':
|
|
69
|
+
case 'COMPONENT_SET':
|
|
70
|
+
return { role: 'region', reason: 'container node' };
|
|
71
|
+
case 'RECTANGLE':
|
|
72
|
+
case 'VECTOR':
|
|
73
|
+
case 'ELLIPSE':
|
|
74
|
+
case 'LINE':
|
|
75
|
+
case 'POLYGON':
|
|
76
|
+
case 'STAR':
|
|
77
|
+
case 'IMAGE':
|
|
78
|
+
return null;
|
|
79
|
+
default:
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function matchesAny(haystack, needles) {
|
|
84
|
+
for (const n of needles) {
|
|
85
|
+
if (haystack.includes(n))
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
const VERB_KEYWORDS = [
|
|
91
|
+
{ match: /\b(submit|send|save|confirm)\b/, verb: 'submit' },
|
|
92
|
+
{ match: /\b(login|signin|sign[\s_-]?in)\b/, verb: 'login' },
|
|
93
|
+
{ match: /\b(signup|register|sign[\s_-]?up)\b/, verb: 'signup' },
|
|
94
|
+
{ match: /\b(logout|signout|sign[\s_-]?out)\b/, verb: 'logout' },
|
|
95
|
+
{ match: /\b(delete|remove|trash)\b/, verb: 'delete' },
|
|
96
|
+
{ match: /\b(edit|update)\b/, verb: 'edit' },
|
|
97
|
+
{ match: /\b(cancel|dismiss|close)\b/, verb: 'cancel' },
|
|
98
|
+
{ match: /\b(search|find)\b/, verb: 'search' },
|
|
99
|
+
{ match: /\b(add|create|new)\b/, verb: 'create' },
|
|
100
|
+
{ match: /\b(open)\b/, verb: 'open' },
|
|
101
|
+
{ match: /\b(reset)\b/, verb: 'reset' },
|
|
102
|
+
];
|
|
103
|
+
function inferVerb(name) {
|
|
104
|
+
for (const { match, verb } of VERB_KEYWORDS) {
|
|
105
|
+
if (match.test(name))
|
|
106
|
+
return verb;
|
|
107
|
+
}
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=heuristics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heuristics.js","sourceRoot":"","sources":["../../src/figma/heuristics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree normaliser: Figma DOCUMENT -> flat list of screens, each
|
|
3
|
+
* with a flat list of NormalisedElement.
|
|
4
|
+
*
|
|
5
|
+
* Walk strategy:
|
|
6
|
+
* - The DOCUMENT root has CANVAS children (one per Figma page).
|
|
7
|
+
* - We pick top-level FRAMEs inside each CANVAS as "screens".
|
|
8
|
+
* (Figma adopters typically design one screen = one FRAME.)
|
|
9
|
+
* - Inside each screen we depth-first walk, mapping each node
|
|
10
|
+
* via heuristics.mapNodeRole. Skipped nodes do not appear in
|
|
11
|
+
* `elements`.
|
|
12
|
+
*
|
|
13
|
+
* Pure: no I/O, no LLM. Easy to feed fixtures.
|
|
14
|
+
*
|
|
15
|
+
* ASCII-only.
|
|
16
|
+
*/
|
|
17
|
+
import type { FigmaFile, NormalisedFile } from './types.js';
|
|
18
|
+
export interface NormaliseOptions {
|
|
19
|
+
/** Filter screens by Figma page name (CANVAS). When unset, every
|
|
20
|
+
CANVAS contributes its top-level FRAMEs. */
|
|
21
|
+
pageName?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function normaliseFigmaFile(file: FigmaFile, opts?: NormaliseOptions): NormalisedFile;
|
|
24
|
+
/**
|
|
25
|
+
* Layer name -> URL slug. Honours explicit path-like names
|
|
26
|
+
* ("/login" stays "/login"); everything else gets lowercased +
|
|
27
|
+
* underscores stripped.
|
|
28
|
+
*
|
|
29
|
+
* Exported for tests + reuse by the CLI when building the React
|
|
30
|
+
* Router scaffold.
|
|
31
|
+
*/
|
|
32
|
+
export declare function slugify(name: string): string;
|
|
33
|
+
//# sourceMappingURL=normalize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/figma/normalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EACV,SAAS,EAGT,cAAc,EAEf,MAAM,YAAY,CAAC;AAIpB,MAAM,WAAW,gBAAgB;IAC/B;mDAC+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,GAAE,gBAAqB,GAAG,cAAc,CAwB/F;AAmCD;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAc5C"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree normaliser: Figma DOCUMENT -> flat list of screens, each
|
|
3
|
+
* with a flat list of NormalisedElement.
|
|
4
|
+
*
|
|
5
|
+
* Walk strategy:
|
|
6
|
+
* - The DOCUMENT root has CANVAS children (one per Figma page).
|
|
7
|
+
* - We pick top-level FRAMEs inside each CANVAS as "screens".
|
|
8
|
+
* (Figma adopters typically design one screen = one FRAME.)
|
|
9
|
+
* - Inside each screen we depth-first walk, mapping each node
|
|
10
|
+
* via heuristics.mapNodeRole. Skipped nodes do not appear in
|
|
11
|
+
* `elements`.
|
|
12
|
+
*
|
|
13
|
+
* Pure: no I/O, no LLM. Easy to feed fixtures.
|
|
14
|
+
*
|
|
15
|
+
* ASCII-only.
|
|
16
|
+
*/
|
|
17
|
+
import { mapNodeRole } from './heuristics.js';
|
|
18
|
+
import { extractDesignTokens } from './tokens.js';
|
|
19
|
+
export function normaliseFigmaFile(file, opts = {}) {
|
|
20
|
+
const screens = [];
|
|
21
|
+
/* file.document is the DOCUMENT node. Its children are CANVASes. */
|
|
22
|
+
for (const canvas of file.document.children ?? []) {
|
|
23
|
+
if (canvas.type !== 'CANVAS')
|
|
24
|
+
continue;
|
|
25
|
+
if (opts.pageName && canvas.name !== opts.pageName)
|
|
26
|
+
continue;
|
|
27
|
+
for (const candidate of canvas.children ?? []) {
|
|
28
|
+
if (candidate.type !== 'FRAME' && candidate.type !== 'COMPONENT')
|
|
29
|
+
continue;
|
|
30
|
+
screens.push(normaliseScreen(candidate));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const tokens = extractDesignTokens(file);
|
|
34
|
+
return {
|
|
35
|
+
source: {
|
|
36
|
+
name: file.name,
|
|
37
|
+
lastModified: file.lastModified,
|
|
38
|
+
version: file.version,
|
|
39
|
+
},
|
|
40
|
+
screens,
|
|
41
|
+
tokens,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function normaliseScreen(root) {
|
|
45
|
+
const elements = [];
|
|
46
|
+
walk(root, undefined, 0, elements);
|
|
47
|
+
return {
|
|
48
|
+
figma_node_id: root.id,
|
|
49
|
+
name: root.name,
|
|
50
|
+
slug: slugify(root.name),
|
|
51
|
+
elements,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function walk(node, parentName, depth, out) {
|
|
55
|
+
if (node.visible === false)
|
|
56
|
+
return;
|
|
57
|
+
const mapped = mapNodeRole(node, parentName);
|
|
58
|
+
if (mapped) {
|
|
59
|
+
const el = {
|
|
60
|
+
figma_node_id: node.id,
|
|
61
|
+
name: node.name,
|
|
62
|
+
role: mapped.role,
|
|
63
|
+
depth,
|
|
64
|
+
source_type: node.type,
|
|
65
|
+
};
|
|
66
|
+
if (mapped.verb_hint)
|
|
67
|
+
el.verb_hint = mapped.verb_hint;
|
|
68
|
+
if (node.characters)
|
|
69
|
+
el.text = node.characters;
|
|
70
|
+
out.push(el);
|
|
71
|
+
}
|
|
72
|
+
for (const child of node.children ?? []) {
|
|
73
|
+
walk(child, node.name, depth + 1, out);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Layer name -> URL slug. Honours explicit path-like names
|
|
78
|
+
* ("/login" stays "/login"); everything else gets lowercased +
|
|
79
|
+
* underscores stripped.
|
|
80
|
+
*
|
|
81
|
+
* Exported for tests + reuse by the CLI when building the React
|
|
82
|
+
* Router scaffold.
|
|
83
|
+
*/
|
|
84
|
+
export function slugify(name) {
|
|
85
|
+
const trimmed = (name || '').trim();
|
|
86
|
+
if (trimmed.startsWith('/')) {
|
|
87
|
+
/* Explicit route. Lowercase but otherwise preserve. */
|
|
88
|
+
return trimmed.toLowerCase();
|
|
89
|
+
}
|
|
90
|
+
const slug = trimmed
|
|
91
|
+
.toLowerCase()
|
|
92
|
+
.replace(/[^a-z0-9]+/g, '_')
|
|
93
|
+
.replace(/^_+|_+$/g, '');
|
|
94
|
+
if (!slug)
|
|
95
|
+
return '/';
|
|
96
|
+
/* Convention: "Home" / "home" -> "/" (the index route). */
|
|
97
|
+
if (slug === 'home' || slug === 'index')
|
|
98
|
+
return '/';
|
|
99
|
+
return '/' + slug;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../src/figma/normalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AASH,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAQlD,MAAM,UAAU,kBAAkB,CAAC,IAAe,EAAE,OAAyB,EAAE;IAC7E,MAAM,OAAO,GAAuB,EAAE,CAAC;IAEvC,oEAAoE;IACpE,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;QAClD,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ;YAAE,SAAS;QACvC,IAAI,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC,QAAQ;YAAE,SAAS;QAC7D,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;YAC9C,IAAI,SAAS,CAAC,IAAI,KAAK,OAAO,IAAI,SAAS,CAAC,IAAI,KAAK,WAAW;gBAAE,SAAS;YAC3E,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAEzC,OAAO;QACL,MAAM,EAAE;YACN,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB;QACD,OAAO;QACP,MAAM;KACP,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,IAAe;IACtC,MAAM,QAAQ,GAAwB,EAAE,CAAC;IACzC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC;IACnC,OAAO;QACL,aAAa,EAAE,IAAI,CAAC,EAAE;QACtB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;QACxB,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,SAAS,IAAI,CAAC,IAAe,EAAE,UAA8B,EAAE,KAAa,EAAE,GAAwB;IACpG,IAAI,IAAI,CAAC,OAAO,KAAK,KAAK;QAAE,OAAO;IAEnC,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAC7C,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,EAAE,GAAsB;YAC5B,aAAa,EAAE,IAAI,CAAC,EAAE;YACtB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,KAAK;YACL,WAAW,EAAE,IAAI,CAAC,IAAI;SACvB,CAAC;QACF,IAAI,MAAM,CAAC,SAAS;YAAE,EAAE,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;QACtD,IAAI,IAAI,CAAC,UAAU;YAAE,EAAE,CAAC,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC;QAC/C,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;QACxC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;IACzC,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,MAAM,OAAO,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5B,uDAAuD;QACvD,OAAO,OAAO,CAAC,WAAW,EAAE,CAAC;IAC/B,CAAC;IACD,MAAM,IAAI,GAAG,OAAO;SACjB,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC3B,IAAI,CAAC,IAAI;QAAE,OAAO,GAAG,CAAC;IACtB,2DAA2D;IAC3D,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,GAAG,CAAC;IACpD,OAAO,GAAG,GAAG,IAAI,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Design-token extractor for Figma files.
|
|
3
|
+
*
|
|
4
|
+
* Walks the file's published styles + the fills/typography on every
|
|
5
|
+
* referenced node and emits a small {colors, fonts, spacing} map
|
|
6
|
+
* the Forge emitter renders as CSS custom properties on :root.
|
|
7
|
+
*
|
|
8
|
+
* Heuristics:
|
|
9
|
+
* - File-level FILL styles whose name starts with "color/"
|
|
10
|
+
* become --color-* entries.
|
|
11
|
+
* - File-level TEXT styles whose name starts with "text/"
|
|
12
|
+
* become --font-* entries (size + family).
|
|
13
|
+
* - Spacing (FRAME paddings, gaps) is not modelled at v0.3.0 --
|
|
14
|
+
* it would require traversing every FRAME's auto-layout config
|
|
15
|
+
* which is large. Marked as TODO in the SPEC's OQ-FIG-4.
|
|
16
|
+
*
|
|
17
|
+
* Pure: no I/O.
|
|
18
|
+
*
|
|
19
|
+
* ASCII-only.
|
|
20
|
+
*/
|
|
21
|
+
import type { FigmaFile, DesignTokens } from './types.js';
|
|
22
|
+
export declare function extractDesignTokens(file: FigmaFile): DesignTokens;
|
|
23
|
+
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/figma/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAc,YAAY,EAAE,MAAM,YAAY,CAAC;AAEtE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,GAAG,YAAY,CA4BjE"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Design-token extractor for Figma files.
|
|
3
|
+
*
|
|
4
|
+
* Walks the file's published styles + the fills/typography on every
|
|
5
|
+
* referenced node and emits a small {colors, fonts, spacing} map
|
|
6
|
+
* the Forge emitter renders as CSS custom properties on :root.
|
|
7
|
+
*
|
|
8
|
+
* Heuristics:
|
|
9
|
+
* - File-level FILL styles whose name starts with "color/"
|
|
10
|
+
* become --color-* entries.
|
|
11
|
+
* - File-level TEXT styles whose name starts with "text/"
|
|
12
|
+
* become --font-* entries (size + family).
|
|
13
|
+
* - Spacing (FRAME paddings, gaps) is not modelled at v0.3.0 --
|
|
14
|
+
* it would require traversing every FRAME's auto-layout config
|
|
15
|
+
* which is large. Marked as TODO in the SPEC's OQ-FIG-4.
|
|
16
|
+
*
|
|
17
|
+
* Pure: no I/O.
|
|
18
|
+
*
|
|
19
|
+
* ASCII-only.
|
|
20
|
+
*/
|
|
21
|
+
export function extractDesignTokens(file) {
|
|
22
|
+
const tokens = { colors: {}, fonts: {}, spacing: {} };
|
|
23
|
+
for (const [, style] of Object.entries(file.styles ?? {})) {
|
|
24
|
+
if (!style?.name)
|
|
25
|
+
continue;
|
|
26
|
+
if (style.styleType === 'FILL' && style.name.startsWith('color/')) {
|
|
27
|
+
const slug = cssVarSlug(style.name.slice('color/'.length));
|
|
28
|
+
if (slug)
|
|
29
|
+
tokens.colors['--color-' + slug] = '#000000';
|
|
30
|
+
/* The Figma REST file API does not embed the resolved color
|
|
31
|
+
hex on the published-style table directly; the value lives
|
|
32
|
+
on the first node that references the style. The emitter
|
|
33
|
+
pass fills this in by walking the document for the style
|
|
34
|
+
id. For the v0.3.0 alpha we leave a placeholder which the
|
|
35
|
+
CLI overwrites once we visit the referencing node. */
|
|
36
|
+
}
|
|
37
|
+
if (style.styleType === 'TEXT' && style.name.startsWith('text/')) {
|
|
38
|
+
const slug = cssVarSlug(style.name.slice('text/'.length));
|
|
39
|
+
if (slug)
|
|
40
|
+
tokens.fonts['--font-' + slug] = 'inherit';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/* Second pass: walk the document and resolve published-style refs
|
|
44
|
+
with the actual paint / typeStyle from the node. We only record
|
|
45
|
+
the FIRST referencing node's value so a style is captured even
|
|
46
|
+
when not used twice. */
|
|
47
|
+
walkAndResolve(file, tokens);
|
|
48
|
+
return tokens;
|
|
49
|
+
}
|
|
50
|
+
function walkAndResolve(file, tokens) {
|
|
51
|
+
const styleMeta = file.styles ?? {};
|
|
52
|
+
const seenColor = new Set();
|
|
53
|
+
const seenFont = new Set();
|
|
54
|
+
function visit(node) {
|
|
55
|
+
if (!node || typeof node !== 'object')
|
|
56
|
+
return;
|
|
57
|
+
/* Resolve a FILL style. */
|
|
58
|
+
const styleRefs = node.styles ?? {};
|
|
59
|
+
if (styleRefs.fill && !seenColor.has(styleRefs.fill)) {
|
|
60
|
+
const meta = styleMeta[styleRefs.fill];
|
|
61
|
+
if (meta && meta.styleType === 'FILL' && meta.name.startsWith('color/')) {
|
|
62
|
+
const slug = cssVarSlug(meta.name.slice('color/'.length));
|
|
63
|
+
const fill = (node.fills || [])[0];
|
|
64
|
+
if (slug && fill && fill.type === 'SOLID' && fill.color) {
|
|
65
|
+
tokens.colors['--color-' + slug] = rgbaToHex(fill.color);
|
|
66
|
+
seenColor.add(styleRefs.fill);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/* Resolve a TEXT style. */
|
|
71
|
+
if (styleRefs.text && !seenFont.has(styleRefs.text)) {
|
|
72
|
+
const meta = styleMeta[styleRefs.text];
|
|
73
|
+
if (meta && meta.styleType === 'TEXT' && meta.name.startsWith('text/')) {
|
|
74
|
+
const slug = cssVarSlug(meta.name.slice('text/'.length));
|
|
75
|
+
if (slug && node.style) {
|
|
76
|
+
const fam = node.style.fontFamily ? '"' + node.style.fontFamily + '"' : 'inherit';
|
|
77
|
+
const size = node.style.fontSize ? node.style.fontSize + 'px' : 'inherit';
|
|
78
|
+
/* We surface two tokens per text style: family + size. */
|
|
79
|
+
tokens.fonts['--font-' + slug + '-family'] = fam;
|
|
80
|
+
tokens.fonts['--font-' + slug + '-size'] = size;
|
|
81
|
+
seenFont.add(styleRefs.text);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
for (const child of node.children ?? [])
|
|
86
|
+
visit(child);
|
|
87
|
+
}
|
|
88
|
+
visit(file.document);
|
|
89
|
+
}
|
|
90
|
+
function rgbaToHex(c) {
|
|
91
|
+
/* Figma normalises rgb to 0..1. */
|
|
92
|
+
const r = clamp255(Math.round(c.r * 255));
|
|
93
|
+
const g = clamp255(Math.round(c.g * 255));
|
|
94
|
+
const b = clamp255(Math.round(c.b * 255));
|
|
95
|
+
const hex = ((r << 16) | (g << 8) | b).toString(16).padStart(6, '0');
|
|
96
|
+
return '#' + hex;
|
|
97
|
+
}
|
|
98
|
+
function clamp255(n) {
|
|
99
|
+
if (n < 0)
|
|
100
|
+
return 0;
|
|
101
|
+
if (n > 255)
|
|
102
|
+
return 255;
|
|
103
|
+
return n;
|
|
104
|
+
}
|
|
105
|
+
function cssVarSlug(s) {
|
|
106
|
+
return s
|
|
107
|
+
.toLowerCase()
|
|
108
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
109
|
+
.replace(/^-+|-+$/g, '');
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=tokens.js.map
|