@muloka/figma-console-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +898 -0
- package/dist/apps/design-system-dashboard/scoring/accessibility.d.ts +14 -0
- package/dist/apps/design-system-dashboard/scoring/accessibility.d.ts.map +1 -0
- package/dist/apps/design-system-dashboard/scoring/accessibility.js +278 -0
- package/dist/apps/design-system-dashboard/scoring/accessibility.js.map +1 -0
- package/dist/apps/design-system-dashboard/scoring/component-metadata.d.ts +29 -0
- package/dist/apps/design-system-dashboard/scoring/component-metadata.d.ts.map +1 -0
- package/dist/apps/design-system-dashboard/scoring/component-metadata.js +358 -0
- package/dist/apps/design-system-dashboard/scoring/component-metadata.js.map +1 -0
- package/dist/apps/design-system-dashboard/scoring/consistency.d.ts +14 -0
- package/dist/apps/design-system-dashboard/scoring/consistency.d.ts.map +1 -0
- package/dist/apps/design-system-dashboard/scoring/consistency.js +342 -0
- package/dist/apps/design-system-dashboard/scoring/consistency.js.map +1 -0
- package/dist/apps/design-system-dashboard/scoring/coverage.d.ts +14 -0
- package/dist/apps/design-system-dashboard/scoring/coverage.d.ts.map +1 -0
- package/dist/apps/design-system-dashboard/scoring/coverage.js +231 -0
- package/dist/apps/design-system-dashboard/scoring/coverage.js.map +1 -0
- package/dist/apps/design-system-dashboard/scoring/engine.d.ts +27 -0
- package/dist/apps/design-system-dashboard/scoring/engine.d.ts.map +1 -0
- package/dist/apps/design-system-dashboard/scoring/engine.js +93 -0
- package/dist/apps/design-system-dashboard/scoring/engine.js.map +1 -0
- package/dist/apps/design-system-dashboard/scoring/naming-semantics.d.ts +14 -0
- package/dist/apps/design-system-dashboard/scoring/naming-semantics.d.ts.map +1 -0
- package/dist/apps/design-system-dashboard/scoring/naming-semantics.js +309 -0
- package/dist/apps/design-system-dashboard/scoring/naming-semantics.js.map +1 -0
- package/dist/apps/design-system-dashboard/scoring/token-architecture.d.ts +14 -0
- package/dist/apps/design-system-dashboard/scoring/token-architecture.d.ts.map +1 -0
- package/dist/apps/design-system-dashboard/scoring/token-architecture.js +350 -0
- package/dist/apps/design-system-dashboard/scoring/token-architecture.js.map +1 -0
- package/dist/apps/design-system-dashboard/scoring/types.d.ts +89 -0
- package/dist/apps/design-system-dashboard/scoring/types.d.ts.map +1 -0
- package/dist/apps/design-system-dashboard/scoring/types.js +41 -0
- package/dist/apps/design-system-dashboard/scoring/types.js.map +1 -0
- package/dist/apps/design-system-dashboard/server.d.ts +24 -0
- package/dist/apps/design-system-dashboard/server.d.ts.map +1 -0
- package/dist/apps/design-system-dashboard/server.js +160 -0
- package/dist/apps/design-system-dashboard/server.js.map +1 -0
- package/dist/apps/token-browser/server.d.ts +26 -0
- package/dist/apps/token-browser/server.d.ts.map +1 -0
- package/dist/apps/token-browser/server.js +137 -0
- package/dist/apps/token-browser/server.js.map +1 -0
- package/dist/browser/base.d.ts +58 -0
- package/dist/browser/base.d.ts.map +1 -0
- package/dist/browser/base.js +6 -0
- package/dist/browser/base.js.map +1 -0
- package/dist/browser/local.d.ts +87 -0
- package/dist/browser/local.d.ts.map +1 -0
- package/dist/browser/local.js +318 -0
- package/dist/browser/local.js.map +1 -0
- package/dist/cloudflare/apps/design-system-dashboard/scoring/accessibility.js +277 -0
- package/dist/cloudflare/apps/design-system-dashboard/scoring/component-metadata.js +357 -0
- package/dist/cloudflare/apps/design-system-dashboard/scoring/consistency.js +341 -0
- package/dist/cloudflare/apps/design-system-dashboard/scoring/coverage.js +230 -0
- package/dist/cloudflare/apps/design-system-dashboard/scoring/engine.js +92 -0
- package/dist/cloudflare/apps/design-system-dashboard/scoring/naming-semantics.js +308 -0
- package/dist/cloudflare/apps/design-system-dashboard/scoring/token-architecture.js +349 -0
- package/dist/cloudflare/apps/design-system-dashboard/scoring/types.js +40 -0
- package/dist/cloudflare/apps/design-system-dashboard/server.js +159 -0
- package/dist/cloudflare/apps/token-browser/server.js +136 -0
- package/dist/cloudflare/browser-manager.js +157 -0
- package/dist/cloudflare/core/accessibility-tools.js +361 -0
- package/dist/cloudflare/core/annotation-tools.js +230 -0
- package/dist/cloudflare/core/cloud-websocket-connector.js +419 -0
- package/dist/cloudflare/core/cloud-websocket-relay.js +198 -0
- package/dist/cloudflare/core/comment-tools.js +292 -0
- package/dist/cloudflare/core/config.js +153 -0
- package/dist/cloudflare/core/console-monitor.js +427 -0
- package/dist/cloudflare/core/deep-component-tools.js +128 -0
- package/dist/cloudflare/core/design-code-tools.js +2793 -0
- package/dist/cloudflare/core/design-system-manifest.js +265 -0
- package/dist/cloudflare/core/design-system-tools.js +882 -0
- package/dist/cloudflare/core/diagnose-tool.js +100 -0
- package/dist/cloudflare/core/diff/changelog-formatter.js +275 -0
- package/dist/cloudflare/core/diff/diff-engine.js +334 -0
- package/dist/cloudflare/core/diff/property-compare.js +36 -0
- package/dist/cloudflare/core/diff/version-cache.js +74 -0
- package/dist/cloudflare/core/enrichment/enrichment-service.js +374 -0
- package/dist/cloudflare/core/enrichment/index.js +7 -0
- package/dist/cloudflare/core/enrichment/relationship-mapper.js +351 -0
- package/dist/cloudflare/core/enrichment/style-resolver.js +346 -0
- package/dist/cloudflare/core/figjam-tools.js +547 -0
- package/dist/cloudflare/core/figma-api.js +509 -0
- package/dist/cloudflare/core/figma-connector.js +7 -0
- package/dist/cloudflare/core/figma-reconstruction-spec.js +402 -0
- package/dist/cloudflare/core/figma-style-extractor.js +311 -0
- package/dist/cloudflare/core/figma-tools.js +3302 -0
- package/dist/cloudflare/core/identity.js +96 -0
- package/dist/cloudflare/core/library-tools.js +580 -0
- package/dist/cloudflare/core/logger.js +53 -0
- package/dist/cloudflare/core/port-discovery.js +935 -0
- package/dist/cloudflare/core/resolve-package-root.js +11 -0
- package/dist/cloudflare/core/slides-tools.js +714 -0
- package/dist/cloudflare/core/slot-tools.js +334 -0
- package/dist/cloudflare/core/snippet-injector.js +96 -0
- package/dist/cloudflare/core/tokens/alias-resolver.js +205 -0
- package/dist/cloudflare/core/tokens/config.js +294 -0
- package/dist/cloudflare/core/tokens/dialect.js +232 -0
- package/dist/cloudflare/core/tokens/figma-converter.js +323 -0
- package/dist/cloudflare/core/tokens/formatters/css-vars.js +338 -0
- package/dist/cloudflare/core/tokens/formatters/dtcg.js +376 -0
- package/dist/cloudflare/core/tokens/formatters/index.js +45 -0
- package/dist/cloudflare/core/tokens/formatters/json.js +205 -0
- package/dist/cloudflare/core/tokens/formatters/less.js +4 -0
- package/dist/cloudflare/core/tokens/formatters/scss.js +258 -0
- package/dist/cloudflare/core/tokens/formatters/stubs.js +13 -0
- package/dist/cloudflare/core/tokens/formatters/style-dictionary-v3.js +213 -0
- package/dist/cloudflare/core/tokens/formatters/tailwind-v3.js +237 -0
- package/dist/cloudflare/core/tokens/formatters/tailwind-v4.js +335 -0
- package/dist/cloudflare/core/tokens/formatters/tokens-studio.js +256 -0
- package/dist/cloudflare/core/tokens/formatters/ts-module.js +198 -0
- package/dist/cloudflare/core/tokens/index.js +16 -0
- package/dist/cloudflare/core/tokens/parsers/css-vars.js +4 -0
- package/dist/cloudflare/core/tokens/parsers/dtcg.js +280 -0
- package/dist/cloudflare/core/tokens/parsers/index.js +138 -0
- package/dist/cloudflare/core/tokens/parsers/json.js +7 -0
- package/dist/cloudflare/core/tokens/parsers/scss.js +4 -0
- package/dist/cloudflare/core/tokens/parsers/stubs.js +20 -0
- package/dist/cloudflare/core/tokens/parsers/style-dictionary-v3.js +4 -0
- package/dist/cloudflare/core/tokens/parsers/tailwind-v3.js +4 -0
- package/dist/cloudflare/core/tokens/parsers/tailwind-v4.js +4 -0
- package/dist/cloudflare/core/tokens/parsers/tokens-studio.js +4 -0
- package/dist/cloudflare/core/tokens/schemas.js +152 -0
- package/dist/cloudflare/core/tokens/transforms/color.js +12 -0
- package/dist/cloudflare/core/tokens/transforms/index.js +29 -0
- package/dist/cloudflare/core/tokens/transforms/size.js +7 -0
- package/dist/cloudflare/core/tokens/types.js +18 -0
- package/dist/cloudflare/core/tokens-tools.js +1788 -0
- package/dist/cloudflare/core/types/design-code.js +4 -0
- package/dist/cloudflare/core/types/enriched.js +5 -0
- package/dist/cloudflare/core/types/index.js +4 -0
- package/dist/cloudflare/core/variable-resolver.js +85 -0
- package/dist/cloudflare/core/version-tools.js +1199 -0
- package/dist/cloudflare/core/websocket-connector.js +436 -0
- package/dist/cloudflare/core/websocket-server.js +956 -0
- package/dist/cloudflare/core/write-tools.js +2506 -0
- package/dist/cloudflare/index.js +3010 -0
- package/dist/core/accessibility-tools.d.ts +21 -0
- package/dist/core/accessibility-tools.d.ts.map +1 -0
- package/dist/core/accessibility-tools.js +362 -0
- package/dist/core/accessibility-tools.js.map +1 -0
- package/dist/core/annotation-tools.d.ts +14 -0
- package/dist/core/annotation-tools.d.ts.map +1 -0
- package/dist/core/annotation-tools.js +231 -0
- package/dist/core/annotation-tools.js.map +1 -0
- package/dist/core/comment-tools.d.ts +11 -0
- package/dist/core/comment-tools.d.ts.map +1 -0
- package/dist/core/comment-tools.js +293 -0
- package/dist/core/comment-tools.js.map +1 -0
- package/dist/core/config.d.ts +17 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +154 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/console-monitor.d.ts +82 -0
- package/dist/core/console-monitor.d.ts.map +1 -0
- package/dist/core/console-monitor.js +428 -0
- package/dist/core/console-monitor.js.map +1 -0
- package/dist/core/deep-component-tools.d.ts +14 -0
- package/dist/core/deep-component-tools.d.ts.map +1 -0
- package/dist/core/deep-component-tools.js +129 -0
- package/dist/core/deep-component-tools.js.map +1 -0
- package/dist/core/design-code-tools.d.ts +116 -0
- package/dist/core/design-code-tools.d.ts.map +1 -0
- package/dist/core/design-code-tools.js +2794 -0
- package/dist/core/design-code-tools.js.map +1 -0
- package/dist/core/design-system-manifest.d.ts +273 -0
- package/dist/core/design-system-manifest.d.ts.map +1 -0
- package/dist/core/design-system-manifest.js +266 -0
- package/dist/core/design-system-manifest.js.map +1 -0
- package/dist/core/design-system-tools.d.ts +67 -0
- package/dist/core/design-system-tools.d.ts.map +1 -0
- package/dist/core/design-system-tools.js +883 -0
- package/dist/core/design-system-tools.js.map +1 -0
- package/dist/core/diagnose-tool.d.ts +41 -0
- package/dist/core/diagnose-tool.d.ts.map +1 -0
- package/dist/core/diagnose-tool.js +101 -0
- package/dist/core/diagnose-tool.js.map +1 -0
- package/dist/core/diff/changelog-formatter.d.ts +35 -0
- package/dist/core/diff/changelog-formatter.d.ts.map +1 -0
- package/dist/core/diff/changelog-formatter.js +276 -0
- package/dist/core/diff/changelog-formatter.js.map +1 -0
- package/dist/core/diff/diff-engine.d.ts +127 -0
- package/dist/core/diff/diff-engine.d.ts.map +1 -0
- package/dist/core/diff/diff-engine.js +335 -0
- package/dist/core/diff/diff-engine.js.map +1 -0
- package/dist/core/diff/property-compare.d.ts +19 -0
- package/dist/core/diff/property-compare.d.ts.map +1 -0
- package/dist/core/diff/property-compare.js +37 -0
- package/dist/core/diff/property-compare.js.map +1 -0
- package/dist/core/diff/version-cache.d.ts +40 -0
- package/dist/core/diff/version-cache.d.ts.map +1 -0
- package/dist/core/diff/version-cache.js +75 -0
- package/dist/core/diff/version-cache.js.map +1 -0
- package/dist/core/enrichment/enrichment-service.d.ts +52 -0
- package/dist/core/enrichment/enrichment-service.d.ts.map +1 -0
- package/dist/core/enrichment/enrichment-service.js +375 -0
- package/dist/core/enrichment/enrichment-service.js.map +1 -0
- package/dist/core/enrichment/index.d.ts +8 -0
- package/dist/core/enrichment/index.d.ts.map +1 -0
- package/dist/core/enrichment/index.js +8 -0
- package/dist/core/enrichment/index.js.map +1 -0
- package/dist/core/enrichment/relationship-mapper.d.ts +106 -0
- package/dist/core/enrichment/relationship-mapper.d.ts.map +1 -0
- package/dist/core/enrichment/relationship-mapper.js +352 -0
- package/dist/core/enrichment/relationship-mapper.js.map +1 -0
- package/dist/core/enrichment/style-resolver.d.ts +85 -0
- package/dist/core/enrichment/style-resolver.d.ts.map +1 -0
- package/dist/core/enrichment/style-resolver.js +347 -0
- package/dist/core/enrichment/style-resolver.js.map +1 -0
- package/dist/core/figjam-tools.d.ts +8 -0
- package/dist/core/figjam-tools.d.ts.map +1 -0
- package/dist/core/figjam-tools.js +548 -0
- package/dist/core/figjam-tools.js.map +1 -0
- package/dist/core/figma-api.d.ts +254 -0
- package/dist/core/figma-api.d.ts.map +1 -0
- package/dist/core/figma-api.js +510 -0
- package/dist/core/figma-api.js.map +1 -0
- package/dist/core/figma-connector.d.ts +214 -0
- package/dist/core/figma-connector.d.ts.map +1 -0
- package/dist/core/figma-connector.js +8 -0
- package/dist/core/figma-connector.js.map +1 -0
- package/dist/core/figma-desktop-connector.d.ts +312 -0
- package/dist/core/figma-desktop-connector.d.ts.map +1 -0
- package/dist/core/figma-desktop-connector.js +1298 -0
- package/dist/core/figma-desktop-connector.js.map +1 -0
- package/dist/core/figma-reconstruction-spec.d.ts +166 -0
- package/dist/core/figma-reconstruction-spec.d.ts.map +1 -0
- package/dist/core/figma-reconstruction-spec.js +403 -0
- package/dist/core/figma-reconstruction-spec.js.map +1 -0
- package/dist/core/figma-style-extractor.d.ts +76 -0
- package/dist/core/figma-style-extractor.d.ts.map +1 -0
- package/dist/core/figma-style-extractor.js +312 -0
- package/dist/core/figma-style-extractor.js.map +1 -0
- package/dist/core/figma-tools.d.ts +22 -0
- package/dist/core/figma-tools.d.ts.map +1 -0
- package/dist/core/figma-tools.js +3303 -0
- package/dist/core/figma-tools.js.map +1 -0
- package/dist/core/identity.d.ts +41 -0
- package/dist/core/identity.d.ts.map +1 -0
- package/dist/core/identity.js +97 -0
- package/dist/core/identity.js.map +1 -0
- package/dist/core/library-tools.d.ts +17 -0
- package/dist/core/library-tools.d.ts.map +1 -0
- package/dist/core/library-tools.js +581 -0
- package/dist/core/library-tools.js.map +1 -0
- package/dist/core/logger.d.ts +22 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +54 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/port-discovery.d.ts +211 -0
- package/dist/core/port-discovery.d.ts.map +1 -0
- package/dist/core/port-discovery.js +936 -0
- package/dist/core/port-discovery.js.map +1 -0
- package/dist/core/resolve-package-root.d.ts +2 -0
- package/dist/core/resolve-package-root.d.ts.map +1 -0
- package/dist/core/resolve-package-root.js +12 -0
- package/dist/core/resolve-package-root.js.map +1 -0
- package/dist/core/slides-tools.d.ts +8 -0
- package/dist/core/slides-tools.d.ts.map +1 -0
- package/dist/core/slides-tools.js +715 -0
- package/dist/core/slides-tools.js.map +1 -0
- package/dist/core/slot-tools.d.ts +7 -0
- package/dist/core/slot-tools.d.ts.map +1 -0
- package/dist/core/slot-tools.js +335 -0
- package/dist/core/slot-tools.js.map +1 -0
- package/dist/core/snippet-injector.d.ts +24 -0
- package/dist/core/snippet-injector.d.ts.map +1 -0
- package/dist/core/snippet-injector.js +97 -0
- package/dist/core/snippet-injector.js.map +1 -0
- package/dist/core/tokens/alias-resolver.d.ts +97 -0
- package/dist/core/tokens/alias-resolver.d.ts.map +1 -0
- package/dist/core/tokens/alias-resolver.js +206 -0
- package/dist/core/tokens/alias-resolver.js.map +1 -0
- package/dist/core/tokens/config.d.ts +380 -0
- package/dist/core/tokens/config.d.ts.map +1 -0
- package/dist/core/tokens/config.js +295 -0
- package/dist/core/tokens/config.js.map +1 -0
- package/dist/core/tokens/dialect.d.ts +107 -0
- package/dist/core/tokens/dialect.d.ts.map +1 -0
- package/dist/core/tokens/dialect.js +233 -0
- package/dist/core/tokens/dialect.js.map +1 -0
- package/dist/core/tokens/figma-converter.d.ts +102 -0
- package/dist/core/tokens/figma-converter.d.ts.map +1 -0
- package/dist/core/tokens/figma-converter.js +324 -0
- package/dist/core/tokens/figma-converter.js.map +1 -0
- package/dist/core/tokens/formatters/css-vars.d.ts +24 -0
- package/dist/core/tokens/formatters/css-vars.d.ts.map +1 -0
- package/dist/core/tokens/formatters/css-vars.js +339 -0
- package/dist/core/tokens/formatters/css-vars.js.map +1 -0
- package/dist/core/tokens/formatters/dtcg.d.ts +28 -0
- package/dist/core/tokens/formatters/dtcg.d.ts.map +1 -0
- package/dist/core/tokens/formatters/dtcg.js +377 -0
- package/dist/core/tokens/formatters/dtcg.js.map +1 -0
- package/dist/core/tokens/formatters/index.d.ts +30 -0
- package/dist/core/tokens/formatters/index.d.ts.map +1 -0
- package/dist/core/tokens/formatters/index.js +46 -0
- package/dist/core/tokens/formatters/index.js.map +1 -0
- package/dist/core/tokens/formatters/json.d.ts +37 -0
- package/dist/core/tokens/formatters/json.d.ts.map +1 -0
- package/dist/core/tokens/formatters/json.js +206 -0
- package/dist/core/tokens/formatters/json.js.map +1 -0
- package/dist/core/tokens/formatters/less.d.ts +4 -0
- package/dist/core/tokens/formatters/less.d.ts.map +1 -0
- package/dist/core/tokens/formatters/less.js +5 -0
- package/dist/core/tokens/formatters/less.js.map +1 -0
- package/dist/core/tokens/formatters/scss.d.ts +26 -0
- package/dist/core/tokens/formatters/scss.d.ts.map +1 -0
- package/dist/core/tokens/formatters/scss.js +259 -0
- package/dist/core/tokens/formatters/scss.js.map +1 -0
- package/dist/core/tokens/formatters/stubs.d.ts +9 -0
- package/dist/core/tokens/formatters/stubs.d.ts.map +1 -0
- package/dist/core/tokens/formatters/stubs.js +14 -0
- package/dist/core/tokens/formatters/stubs.js.map +1 -0
- package/dist/core/tokens/formatters/style-dictionary-v3.d.ts +45 -0
- package/dist/core/tokens/formatters/style-dictionary-v3.d.ts.map +1 -0
- package/dist/core/tokens/formatters/style-dictionary-v3.js +214 -0
- package/dist/core/tokens/formatters/style-dictionary-v3.js.map +1 -0
- package/dist/core/tokens/formatters/tailwind-v3.d.ts +37 -0
- package/dist/core/tokens/formatters/tailwind-v3.d.ts.map +1 -0
- package/dist/core/tokens/formatters/tailwind-v3.js +238 -0
- package/dist/core/tokens/formatters/tailwind-v3.js.map +1 -0
- package/dist/core/tokens/formatters/tailwind-v4.d.ts +41 -0
- package/dist/core/tokens/formatters/tailwind-v4.d.ts.map +1 -0
- package/dist/core/tokens/formatters/tailwind-v4.js +336 -0
- package/dist/core/tokens/formatters/tailwind-v4.js.map +1 -0
- package/dist/core/tokens/formatters/tokens-studio.d.ts +44 -0
- package/dist/core/tokens/formatters/tokens-studio.d.ts.map +1 -0
- package/dist/core/tokens/formatters/tokens-studio.js +257 -0
- package/dist/core/tokens/formatters/tokens-studio.js.map +1 -0
- package/dist/core/tokens/formatters/ts-module.d.ts +35 -0
- package/dist/core/tokens/formatters/ts-module.d.ts.map +1 -0
- package/dist/core/tokens/formatters/ts-module.js +199 -0
- package/dist/core/tokens/formatters/ts-module.js.map +1 -0
- package/dist/core/tokens/index.d.ts +18 -0
- package/dist/core/tokens/index.d.ts.map +1 -0
- package/dist/core/tokens/index.js +17 -0
- package/dist/core/tokens/index.js.map +1 -0
- package/dist/core/tokens/parsers/css-vars.d.ts +3 -0
- package/dist/core/tokens/parsers/css-vars.d.ts.map +1 -0
- package/dist/core/tokens/parsers/css-vars.js +5 -0
- package/dist/core/tokens/parsers/css-vars.js.map +1 -0
- package/dist/core/tokens/parsers/dtcg.d.ts +21 -0
- package/dist/core/tokens/parsers/dtcg.d.ts.map +1 -0
- package/dist/core/tokens/parsers/dtcg.js +281 -0
- package/dist/core/tokens/parsers/dtcg.js.map +1 -0
- package/dist/core/tokens/parsers/index.d.ts +37 -0
- package/dist/core/tokens/parsers/index.d.ts.map +1 -0
- package/dist/core/tokens/parsers/index.js +139 -0
- package/dist/core/tokens/parsers/index.js.map +1 -0
- package/dist/core/tokens/parsers/json.d.ts +4 -0
- package/dist/core/tokens/parsers/json.d.ts.map +1 -0
- package/dist/core/tokens/parsers/json.js +8 -0
- package/dist/core/tokens/parsers/json.js.map +1 -0
- package/dist/core/tokens/parsers/scss.d.ts +3 -0
- package/dist/core/tokens/parsers/scss.d.ts.map +1 -0
- package/dist/core/tokens/parsers/scss.js +5 -0
- package/dist/core/tokens/parsers/scss.js.map +1 -0
- package/dist/core/tokens/parsers/stubs.d.ts +15 -0
- package/dist/core/tokens/parsers/stubs.d.ts.map +1 -0
- package/dist/core/tokens/parsers/stubs.js +21 -0
- package/dist/core/tokens/parsers/stubs.js.map +1 -0
- package/dist/core/tokens/parsers/style-dictionary-v3.d.ts +3 -0
- package/dist/core/tokens/parsers/style-dictionary-v3.d.ts.map +1 -0
- package/dist/core/tokens/parsers/style-dictionary-v3.js +5 -0
- package/dist/core/tokens/parsers/style-dictionary-v3.js.map +1 -0
- package/dist/core/tokens/parsers/tailwind-v3.d.ts +3 -0
- package/dist/core/tokens/parsers/tailwind-v3.d.ts.map +1 -0
- package/dist/core/tokens/parsers/tailwind-v3.js +5 -0
- package/dist/core/tokens/parsers/tailwind-v3.js.map +1 -0
- package/dist/core/tokens/parsers/tailwind-v4.d.ts +3 -0
- package/dist/core/tokens/parsers/tailwind-v4.d.ts.map +1 -0
- package/dist/core/tokens/parsers/tailwind-v4.js +5 -0
- package/dist/core/tokens/parsers/tailwind-v4.js.map +1 -0
- package/dist/core/tokens/parsers/tokens-studio.d.ts +3 -0
- package/dist/core/tokens/parsers/tokens-studio.d.ts.map +1 -0
- package/dist/core/tokens/parsers/tokens-studio.js +5 -0
- package/dist/core/tokens/parsers/tokens-studio.js.map +1 -0
- package/dist/core/tokens/schemas.d.ts +155 -0
- package/dist/core/tokens/schemas.d.ts.map +1 -0
- package/dist/core/tokens/schemas.js +153 -0
- package/dist/core/tokens/schemas.js.map +1 -0
- package/dist/core/tokens/transforms/color.d.ts +9 -0
- package/dist/core/tokens/transforms/color.d.ts.map +1 -0
- package/dist/core/tokens/transforms/color.js +13 -0
- package/dist/core/tokens/transforms/color.js.map +1 -0
- package/dist/core/tokens/transforms/index.d.ts +36 -0
- package/dist/core/tokens/transforms/index.d.ts.map +1 -0
- package/dist/core/tokens/transforms/index.js +30 -0
- package/dist/core/tokens/transforms/index.js.map +1 -0
- package/dist/core/tokens/transforms/size.d.ts +7 -0
- package/dist/core/tokens/transforms/size.d.ts.map +1 -0
- package/dist/core/tokens/transforms/size.js +8 -0
- package/dist/core/tokens/transforms/size.js.map +1 -0
- package/dist/core/tokens/types.d.ts +284 -0
- package/dist/core/tokens/types.d.ts.map +1 -0
- package/dist/core/tokens/types.js +19 -0
- package/dist/core/tokens/types.js.map +1 -0
- package/dist/core/tokens-tools.d.ts +285 -0
- package/dist/core/tokens-tools.d.ts.map +1 -0
- package/dist/core/tokens-tools.js +1789 -0
- package/dist/core/tokens-tools.js.map +1 -0
- package/dist/core/types/design-code.d.ts +271 -0
- package/dist/core/types/design-code.d.ts.map +1 -0
- package/dist/core/types/design-code.js +5 -0
- package/dist/core/types/design-code.js.map +1 -0
- package/dist/core/types/enriched.d.ts +213 -0
- package/dist/core/types/enriched.d.ts.map +1 -0
- package/dist/core/types/enriched.js +6 -0
- package/dist/core/types/enriched.js.map +1 -0
- package/dist/core/types/index.d.ts +104 -0
- package/dist/core/types/index.d.ts.map +1 -0
- package/dist/core/types/index.js +5 -0
- package/dist/core/types/index.js.map +1 -0
- package/dist/core/variable-resolver.d.ts +45 -0
- package/dist/core/variable-resolver.d.ts.map +1 -0
- package/dist/core/variable-resolver.js +86 -0
- package/dist/core/variable-resolver.js.map +1 -0
- package/dist/core/version-tools.d.ts +59 -0
- package/dist/core/version-tools.d.ts.map +1 -0
- package/dist/core/version-tools.js +1200 -0
- package/dist/core/version-tools.js.map +1 -0
- package/dist/core/websocket-connector.d.ts +247 -0
- package/dist/core/websocket-connector.d.ts.map +1 -0
- package/dist/core/websocket-connector.js +437 -0
- package/dist/core/websocket-connector.js.map +1 -0
- package/dist/core/websocket-server.d.ts +290 -0
- package/dist/core/websocket-server.d.ts.map +1 -0
- package/dist/core/websocket-server.js +957 -0
- package/dist/core/websocket-server.js.map +1 -0
- package/dist/core/write-tools.d.ts +7 -0
- package/dist/core/write-tools.d.ts.map +1 -0
- package/dist/core/write-tools.js +2507 -0
- package/dist/core/write-tools.js.map +1 -0
- package/dist/local.d.ts +105 -0
- package/dist/local.d.ts.map +1 -0
- package/dist/local.js +3314 -0
- package/dist/local.js.map +1 -0
- package/figma-desktop-bridge/README.md +365 -0
- package/figma-desktop-bridge/code.js +7359 -0
- package/figma-desktop-bridge/icon.png +0 -0
- package/figma-desktop-bridge/manifest.json +67 -0
- package/figma-desktop-bridge/ui.html +2783 -0
- package/package.json +103 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createChildLogger } from "./logger.js";
|
|
3
|
+
const logger = createChildLogger({ component: "slot-tools" });
|
|
4
|
+
const SLOT_LAYOUT_MODES = ["NONE", "HORIZONTAL", "VERTICAL"];
|
|
5
|
+
const SLOT_CHILD_NODE_TYPES = [
|
|
6
|
+
"FRAME",
|
|
7
|
+
"RECTANGLE",
|
|
8
|
+
"ELLIPSE",
|
|
9
|
+
"TEXT",
|
|
10
|
+
"LINE",
|
|
11
|
+
"POLYGON",
|
|
12
|
+
"STAR",
|
|
13
|
+
"VECTOR",
|
|
14
|
+
];
|
|
15
|
+
const preferredValueSchema = z.object({
|
|
16
|
+
type: z.enum(["COMPONENT", "COMPONENT_SET"]).describe("Type of preferred value"),
|
|
17
|
+
key: z.string().describe("Component or component set key"),
|
|
18
|
+
});
|
|
19
|
+
/**
|
|
20
|
+
* Register MCP tools for Figma Slots (open beta).
|
|
21
|
+
* Requires Desktop Bridge and a Figma Desktop version with SlotNode API support.
|
|
22
|
+
*/
|
|
23
|
+
export function registerSlotTools(server, getDesktopConnector) {
|
|
24
|
+
server.tool("figma_create_slot", `Create a SlotNode inside a component using createSlot(). Automatically creates a linked SLOT component property named after the slot (renaming the slot renames the property). Slots are freeform drop zones for instance content — more flexible than INSTANCE_SWAP. Works on standalone COMPONENTs and variant COMPONENTs inside a COMPONENT_SET (call once per variant). GRID layout is not allowed on slots. Requires Desktop Bridge.`, {
|
|
25
|
+
nodeId: z
|
|
26
|
+
.string()
|
|
27
|
+
.describe("The COMPONENT node ID to add a slot to"),
|
|
28
|
+
name: z
|
|
29
|
+
.string()
|
|
30
|
+
.optional()
|
|
31
|
+
.describe("Slot layer name (e.g. 'Content', 'Footer')"),
|
|
32
|
+
width: z
|
|
33
|
+
.number()
|
|
34
|
+
.optional()
|
|
35
|
+
.describe("Initial slot width in pixels"),
|
|
36
|
+
height: z
|
|
37
|
+
.number()
|
|
38
|
+
.optional()
|
|
39
|
+
.describe("Initial slot height in pixels"),
|
|
40
|
+
layoutMode: z
|
|
41
|
+
.enum(SLOT_LAYOUT_MODES)
|
|
42
|
+
.optional()
|
|
43
|
+
.describe("Auto-layout mode for the slot (GRID is not supported)"),
|
|
44
|
+
}, async ({ nodeId, name, width, height, layoutMode }) => {
|
|
45
|
+
try {
|
|
46
|
+
const connector = await getDesktopConnector();
|
|
47
|
+
const result = await connector.createSlot(nodeId, {
|
|
48
|
+
name,
|
|
49
|
+
width,
|
|
50
|
+
height,
|
|
51
|
+
layoutMode,
|
|
52
|
+
});
|
|
53
|
+
if (!result.success) {
|
|
54
|
+
throw new Error(result.error || "Failed to create slot");
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
content: [
|
|
58
|
+
{
|
|
59
|
+
type: "text",
|
|
60
|
+
text: JSON.stringify({
|
|
61
|
+
success: true,
|
|
62
|
+
slot: result.slot,
|
|
63
|
+
hint: "Use figma_get_slots to inspect slots. Populate instance slots with figma_append_to_slot (not figma_set_instance_properties).",
|
|
64
|
+
}),
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
logger.error({ error }, "figma_create_slot failed");
|
|
71
|
+
return {
|
|
72
|
+
content: [
|
|
73
|
+
{
|
|
74
|
+
type: "text",
|
|
75
|
+
text: JSON.stringify({
|
|
76
|
+
error: error instanceof Error ? error.message : String(error),
|
|
77
|
+
hint: "Target a COMPONENT node (standalone or a variant inside a COMPONENT_SET). Ensure Figma Desktop supports Slots.",
|
|
78
|
+
}),
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
isError: true,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
server.tool("figma_get_slots", `List SlotNode children on a component, component set, or instance. Returns slot IDs, names, property keys, dimensions, and current child nodes. Use on instances before figma_append_to_slot to discover slot names/IDs.`, {
|
|
86
|
+
nodeId: z
|
|
87
|
+
.string()
|
|
88
|
+
.describe("COMPONENT, COMPONENT_SET, or INSTANCE node ID"),
|
|
89
|
+
}, async ({ nodeId }) => {
|
|
90
|
+
try {
|
|
91
|
+
const connector = await getDesktopConnector();
|
|
92
|
+
const result = await connector.getSlots(nodeId);
|
|
93
|
+
if (!result.success) {
|
|
94
|
+
throw new Error(result.error || "Failed to get slots");
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
content: [
|
|
98
|
+
{
|
|
99
|
+
type: "text",
|
|
100
|
+
text: JSON.stringify({ success: true, ...(result.data ?? {}) }),
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
logger.error({ error }, "figma_get_slots failed");
|
|
107
|
+
return {
|
|
108
|
+
content: [
|
|
109
|
+
{
|
|
110
|
+
type: "text",
|
|
111
|
+
text: JSON.stringify({
|
|
112
|
+
error: error instanceof Error ? error.message : String(error),
|
|
113
|
+
hint: "Ensure the node is a component or instance with slots, and Desktop Bridge is connected.",
|
|
114
|
+
}),
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
isError: true,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
server.tool("figma_append_to_slot", `Add content to a slot on a component instance. Clones sourceNodeId into the slot, or creates a new node (nodeType). SLOT content cannot be set via figma_set_instance_properties — use this tool instead. Widgets, stickies, and raw ComponentNodes cannot be appended to slots.`, {
|
|
122
|
+
slotId: z
|
|
123
|
+
.string()
|
|
124
|
+
.optional()
|
|
125
|
+
.describe("Direct SlotNode ID (from figma_get_slots)"),
|
|
126
|
+
instanceId: z
|
|
127
|
+
.string()
|
|
128
|
+
.optional()
|
|
129
|
+
.describe("Instance ID — use with slotName when slotId is unknown"),
|
|
130
|
+
slotName: z
|
|
131
|
+
.string()
|
|
132
|
+
.optional()
|
|
133
|
+
.describe("Slot layer name on the instance (e.g. 'Content')"),
|
|
134
|
+
sourceNodeId: z
|
|
135
|
+
.string()
|
|
136
|
+
.optional()
|
|
137
|
+
.describe("Node to clone into the slot (default: clone=true)"),
|
|
138
|
+
nodeType: z
|
|
139
|
+
.enum(SLOT_CHILD_NODE_TYPES)
|
|
140
|
+
.optional()
|
|
141
|
+
.describe("Create a new node in the slot instead of cloning"),
|
|
142
|
+
properties: z
|
|
143
|
+
.record(z.string(), z.union([z.string(), z.number()]))
|
|
144
|
+
.optional()
|
|
145
|
+
.describe("Properties for created nodes: name, text, width, height"),
|
|
146
|
+
clone: z
|
|
147
|
+
.boolean()
|
|
148
|
+
.optional()
|
|
149
|
+
.default(true)
|
|
150
|
+
.describe("When using sourceNodeId, clone the node (default true). Set false to move."),
|
|
151
|
+
clearExisting: z
|
|
152
|
+
.boolean()
|
|
153
|
+
.optional()
|
|
154
|
+
.default(false)
|
|
155
|
+
.describe("Remove existing slot children before appending"),
|
|
156
|
+
}, async (args) => {
|
|
157
|
+
try {
|
|
158
|
+
if (!args.slotId && !(args.instanceId && args.slotName)) {
|
|
159
|
+
throw new Error("Provide slotId OR (instanceId + slotName)");
|
|
160
|
+
}
|
|
161
|
+
if (!args.sourceNodeId && !args.nodeType) {
|
|
162
|
+
throw new Error("Provide sourceNodeId (clone into slot) or nodeType (create new content)");
|
|
163
|
+
}
|
|
164
|
+
const connector = await getDesktopConnector();
|
|
165
|
+
const result = await connector.appendToSlot(args);
|
|
166
|
+
if (!result.success) {
|
|
167
|
+
throw new Error(result.error || "Failed to append to slot");
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
content: [
|
|
171
|
+
{
|
|
172
|
+
type: "text",
|
|
173
|
+
text: JSON.stringify({
|
|
174
|
+
success: true,
|
|
175
|
+
slot: result.slot,
|
|
176
|
+
appendedNode: result.appendedNode,
|
|
177
|
+
hint: "Use figma_capture_screenshot to verify slot content visually.",
|
|
178
|
+
}),
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
logger.error({ error }, "figma_append_to_slot failed");
|
|
185
|
+
return {
|
|
186
|
+
content: [
|
|
187
|
+
{
|
|
188
|
+
type: "text",
|
|
189
|
+
text: JSON.stringify({
|
|
190
|
+
error: error instanceof Error ? error.message : String(error),
|
|
191
|
+
hints: [
|
|
192
|
+
"Use figma_get_slots on the instance to find slot IDs/names",
|
|
193
|
+
"Clone instances or frames — not raw ComponentNodes",
|
|
194
|
+
"SLOT properties cannot be set via figma_set_instance_properties",
|
|
195
|
+
],
|
|
196
|
+
}),
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
isError: true,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
server.tool("figma_reset_slot", `Reset a slot on a component instance to its default (empty) state from the main component. Uses SlotNode.resetSlot().`, {
|
|
204
|
+
slotId: z
|
|
205
|
+
.string()
|
|
206
|
+
.optional()
|
|
207
|
+
.describe("Direct SlotNode ID"),
|
|
208
|
+
instanceId: z
|
|
209
|
+
.string()
|
|
210
|
+
.optional()
|
|
211
|
+
.describe("Instance ID — use with slotName when slotId is unknown"),
|
|
212
|
+
slotName: z
|
|
213
|
+
.string()
|
|
214
|
+
.optional()
|
|
215
|
+
.describe("Slot layer name on the instance"),
|
|
216
|
+
}, async ({ slotId, instanceId, slotName }) => {
|
|
217
|
+
try {
|
|
218
|
+
if (!slotId && !(instanceId && slotName)) {
|
|
219
|
+
throw new Error("Provide slotId OR (instanceId + slotName)");
|
|
220
|
+
}
|
|
221
|
+
const connector = await getDesktopConnector();
|
|
222
|
+
const result = await connector.resetSlot({ slotId, instanceId, slotName });
|
|
223
|
+
if (!result.success) {
|
|
224
|
+
throw new Error(result.error || "Failed to reset slot");
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
content: [
|
|
228
|
+
{
|
|
229
|
+
type: "text",
|
|
230
|
+
text: JSON.stringify({
|
|
231
|
+
success: true,
|
|
232
|
+
slot: result.slot,
|
|
233
|
+
}),
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
239
|
+
logger.error({ error }, "figma_reset_slot failed");
|
|
240
|
+
return {
|
|
241
|
+
content: [
|
|
242
|
+
{
|
|
243
|
+
type: "text",
|
|
244
|
+
text: JSON.stringify({
|
|
245
|
+
error: error instanceof Error ? error.message : String(error),
|
|
246
|
+
}),
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
isError: true,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
server.tool("figma_add_slot_property", `Manually add a SLOT component property and bind it to an existing frame (alternative to figma_create_slot). The frame must be a direct child of the component, must not use GRID layout, and must not be nested inside another slot. Supports description and preferredValues.`, {
|
|
254
|
+
nodeId: z.string().describe("COMPONENT or COMPONENT_SET node ID"),
|
|
255
|
+
propertyName: z.string().describe("Slot property name (e.g. 'Content')"),
|
|
256
|
+
frameNodeId: z
|
|
257
|
+
.string()
|
|
258
|
+
.describe("Frame node ID to bind as the slot content area"),
|
|
259
|
+
description: z
|
|
260
|
+
.string()
|
|
261
|
+
.optional()
|
|
262
|
+
.describe("Slot property description (SLOT-only)"),
|
|
263
|
+
preferredValues: z
|
|
264
|
+
.array(preferredValueSchema)
|
|
265
|
+
.optional()
|
|
266
|
+
.describe("Preferred components for slot content (SLOT-only)"),
|
|
267
|
+
}, async ({ nodeId, propertyName, frameNodeId, description, preferredValues }) => {
|
|
268
|
+
try {
|
|
269
|
+
const connector = await getDesktopConnector();
|
|
270
|
+
const code = `
|
|
271
|
+
const component = await figma.getNodeByIdAsync(${JSON.stringify(nodeId)});
|
|
272
|
+
if (!component || (component.type !== 'COMPONENT' && component.type !== 'COMPONENT_SET')) {
|
|
273
|
+
throw new Error('Node must be COMPONENT or COMPONENT_SET');
|
|
274
|
+
}
|
|
275
|
+
const frame = await figma.getNodeByIdAsync(${JSON.stringify(frameNodeId)});
|
|
276
|
+
if (!frame || frame.type !== 'FRAME') {
|
|
277
|
+
throw new Error('frameNodeId must be a FRAME node');
|
|
278
|
+
}
|
|
279
|
+
// For a COMPONENT the frame must be its direct child. For a COMPONENT_SET the
|
|
280
|
+
// property lives on the set but the frame lives inside a variant COMPONENT —
|
|
281
|
+
// accept a frame whose parent is a variant of this set.
|
|
282
|
+
const directChild = frame.parent === component;
|
|
283
|
+
const variantChild = component.type === 'COMPONENT_SET' &&
|
|
284
|
+
frame.parent && frame.parent.type === 'COMPONENT' && frame.parent.parent === component;
|
|
285
|
+
if (!directChild && !variantChild) {
|
|
286
|
+
throw new Error(component.type === 'COMPONENT_SET'
|
|
287
|
+
? 'Frame must be a direct child of one of the set\\'s variant components'
|
|
288
|
+
: 'Frame must be a direct child of the component');
|
|
289
|
+
}
|
|
290
|
+
if (frame.layoutMode === 'GRID') {
|
|
291
|
+
throw new Error('GRID layoutMode is not allowed on slot frames');
|
|
292
|
+
}
|
|
293
|
+
const options = {};
|
|
294
|
+
${description ? `options.description = ${JSON.stringify(description)};` : ""}
|
|
295
|
+
${preferredValues ? `options.preferredValues = ${JSON.stringify(preferredValues)};` : ""}
|
|
296
|
+
const propKey = component.addComponentProperty(${JSON.stringify(propertyName)}, 'SLOT', '', Object.keys(options).length ? options : undefined);
|
|
297
|
+
// Merge — assigning a fresh object would wipe the frame's existing bindings
|
|
298
|
+
// (e.g. a BOOLEAN property driving 'visible').
|
|
299
|
+
frame.componentPropertyReferences = Object.assign({}, frame.componentPropertyReferences, { slotContentId: propKey });
|
|
300
|
+
return { propertyKey: propKey, frameId: frame.id, frameName: frame.name };
|
|
301
|
+
`;
|
|
302
|
+
const result = await connector.executeCodeViaUI(code, 10000);
|
|
303
|
+
if (!result.success) {
|
|
304
|
+
throw new Error(result.error || "Failed to add slot property");
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
content: [
|
|
308
|
+
{
|
|
309
|
+
type: "text",
|
|
310
|
+
text: JSON.stringify({
|
|
311
|
+
success: true,
|
|
312
|
+
result: result.result,
|
|
313
|
+
hint: "Prefer figma_create_slot() for new slots — it creates the SlotNode and property automatically.",
|
|
314
|
+
}),
|
|
315
|
+
},
|
|
316
|
+
],
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
logger.error({ error }, "figma_add_slot_property failed");
|
|
321
|
+
return {
|
|
322
|
+
content: [
|
|
323
|
+
{
|
|
324
|
+
type: "text",
|
|
325
|
+
text: JSON.stringify({
|
|
326
|
+
error: error instanceof Error ? error.message : String(error),
|
|
327
|
+
}),
|
|
328
|
+
},
|
|
329
|
+
],
|
|
330
|
+
isError: true,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snippet Injector
|
|
3
|
+
* Generates and manages console-based data extraction snippets for Figma
|
|
4
|
+
*/
|
|
5
|
+
import { createChildLogger } from './logger.js';
|
|
6
|
+
const logger = createChildLogger({ component: 'snippet-injector' });
|
|
7
|
+
export class SnippetInjector {
|
|
8
|
+
/**
|
|
9
|
+
* Generate variables extraction snippet for Figma console
|
|
10
|
+
*/
|
|
11
|
+
generateVariablesSnippet() {
|
|
12
|
+
return `
|
|
13
|
+
(async () => {
|
|
14
|
+
try {
|
|
15
|
+
const vars = await figma.variables.getLocalVariablesAsync();
|
|
16
|
+
const collections = await figma.variables.getLocalVariableCollectionsAsync();
|
|
17
|
+
|
|
18
|
+
const payload = {
|
|
19
|
+
timestamp: Date.now(),
|
|
20
|
+
variables: vars.map(v => ({
|
|
21
|
+
id: v.id,
|
|
22
|
+
name: v.name,
|
|
23
|
+
key: v.key,
|
|
24
|
+
resolvedType: v.resolvedType,
|
|
25
|
+
valuesByMode: v.valuesByMode,
|
|
26
|
+
variableCollectionId: v.variableCollectionId,
|
|
27
|
+
scopes: v.scopes,
|
|
28
|
+
description: v.description,
|
|
29
|
+
hiddenFromPublishing: v.hiddenFromPublishing
|
|
30
|
+
})),
|
|
31
|
+
variableCollections: collections.map(c => ({
|
|
32
|
+
id: c.id,
|
|
33
|
+
name: c.name,
|
|
34
|
+
key: c.key,
|
|
35
|
+
modes: c.modes.map(m => ({ modeId: m.modeId, name: m.name })),
|
|
36
|
+
defaultModeId: c.defaultModeId,
|
|
37
|
+
variableIds: c.variableIds
|
|
38
|
+
}))
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
console.log('[MCP_VARIABLES]', JSON.stringify(payload), '[MCP_VARIABLES_END]');
|
|
42
|
+
console.log('✅ Variables data captured! Run figma_get_variables({ parseFromConsole: true }) in Claude to retrieve.');
|
|
43
|
+
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error('[MCP_VARIABLES_ERROR]', error.message);
|
|
46
|
+
console.log('❌ Make sure you\\'re running this in a Figma file with variables.');
|
|
47
|
+
}
|
|
48
|
+
})();
|
|
49
|
+
`.trim();
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Parse variables from console log entry
|
|
53
|
+
*/
|
|
54
|
+
parseVariablesFromLog(logEntry) {
|
|
55
|
+
try {
|
|
56
|
+
// Check for marker
|
|
57
|
+
if (!logEntry.message.includes('[MCP_VARIABLES]')) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
// Extract JSON from args
|
|
61
|
+
// The snippet logs: console.log('[MCP_VARIABLES]', JSON.stringify(payload), '[MCP_VARIABLES_END]')
|
|
62
|
+
// So args[0] is the marker, args[1] is the JSON string
|
|
63
|
+
const jsonStr = logEntry.args[1] || logEntry.args[0];
|
|
64
|
+
if (!jsonStr) {
|
|
65
|
+
throw new Error('No data found in console log');
|
|
66
|
+
}
|
|
67
|
+
const data = typeof jsonStr === 'string' ? JSON.parse(jsonStr) : jsonStr;
|
|
68
|
+
logger.info({
|
|
69
|
+
variableCount: data.variables?.length || 0,
|
|
70
|
+
collectionCount: data.variableCollections?.length || 0,
|
|
71
|
+
}, 'Successfully parsed variables from console log');
|
|
72
|
+
return {
|
|
73
|
+
variables: data.variables || [],
|
|
74
|
+
variableCollections: data.variableCollections || [],
|
|
75
|
+
timestamp: data.timestamp || Date.now(),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
logger.error({ error }, 'Failed to parse variables from console log');
|
|
80
|
+
throw new Error(`Failed to parse variables from console log: ${error instanceof Error ? error.message : String(error)}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Find the most recent variables log entry
|
|
85
|
+
*/
|
|
86
|
+
findVariablesLog(logs) {
|
|
87
|
+
// Search in reverse (most recent first)
|
|
88
|
+
for (let i = logs.length - 1; i >= 0; i--) {
|
|
89
|
+
const log = logs[i];
|
|
90
|
+
if (log.message.includes('[MCP_VARIABLES]')) {
|
|
91
|
+
return log;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alias reference resolution and validation.
|
|
3
|
+
*
|
|
4
|
+
* DTCG alias references look like `{color.primary}` or `{tier-1.color.blue.500}`.
|
|
5
|
+
* They can chain (an alias can reference another alias), but cycles are an
|
|
6
|
+
* error.
|
|
7
|
+
*
|
|
8
|
+
* This module:
|
|
9
|
+
* - Resolves an alias to its eventual literal value (for formatters that
|
|
10
|
+
* can't natively express references — CSS, SCSS, Tailwind).
|
|
11
|
+
* - Validates that every alias points to an existing token.
|
|
12
|
+
* - Detects cycles.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Slugify a set/collection name into the key used for the top-level set
|
|
16
|
+
* group in DTCG output AND the set-qualifier prefix in alias references
|
|
17
|
+
* (`{<set-slug>.<path.to.token>}`). Must stay in sync with the DTCG
|
|
18
|
+
* formatter's group keys so emitted references resolve inside the file.
|
|
19
|
+
*/
|
|
20
|
+
export function slugifySetName(name) {
|
|
21
|
+
return name
|
|
22
|
+
.trim()
|
|
23
|
+
.toLowerCase()
|
|
24
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
25
|
+
.replace(/^-+|-+$/g, "");
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Build a lookup map for alias resolution. Every token is indexed under its
|
|
29
|
+
* set-qualified key (`<set-slug>.<dot.path>` — the same shape the converter
|
|
30
|
+
* emits in references), and additionally under its bare dot-path
|
|
31
|
+
* (`color.primary`) when that bare path is unambiguous across sets.
|
|
32
|
+
*
|
|
33
|
+
* When two collections both contain the same bare path (e.g. two collections
|
|
34
|
+
* each defining `color/primary`), the bare key is NOT indexed — resolving a
|
|
35
|
+
* bare reference to "whichever set was indexed last" is exactly the
|
|
36
|
+
* cross-collection misresolution bug this guards against. Callers that pass
|
|
37
|
+
* a `warnings` array get one warning per ambiguous bare path.
|
|
38
|
+
*/
|
|
39
|
+
export function buildTokenIndex(doc, warnings) {
|
|
40
|
+
const index = new Map();
|
|
41
|
+
for (const [key, entry] of buildTokenLookup(doc, warnings)) {
|
|
42
|
+
index.set(key, entry.token);
|
|
43
|
+
}
|
|
44
|
+
return index;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Set-aware variant of buildTokenIndex — identical key scheme and ambiguity
|
|
48
|
+
* rules (set-qualified keys always; bare-path fallback only when unambiguous
|
|
49
|
+
* across sets), but each entry carries the owning set's name alongside the
|
|
50
|
+
* token. buildTokenIndex delegates to this so the two can never drift.
|
|
51
|
+
*/
|
|
52
|
+
export function buildTokenLookup(doc, warnings) {
|
|
53
|
+
const index = new Map();
|
|
54
|
+
// barePath → owning entries, used to detect cross-set ambiguity.
|
|
55
|
+
const bareOwners = new Map();
|
|
56
|
+
for (const set of doc.sets) {
|
|
57
|
+
const setKey = slugifySetName(set.name);
|
|
58
|
+
for (const token of set.tokens) {
|
|
59
|
+
const bare = token.path.join(".");
|
|
60
|
+
index.set(`${setKey}.${bare}`, { setName: set.name, token });
|
|
61
|
+
const owners = bareOwners.get(bare) ?? [];
|
|
62
|
+
owners.push({ setName: set.name, token });
|
|
63
|
+
bareOwners.set(bare, owners);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Bare-path fallback: only when a path exists in exactly one set, and only
|
|
67
|
+
// when it doesn't shadow a set-qualified key that's already indexed.
|
|
68
|
+
for (const [bare, owners] of bareOwners) {
|
|
69
|
+
if (owners.length === 1) {
|
|
70
|
+
if (!index.has(bare))
|
|
71
|
+
index.set(bare, owners[0]);
|
|
72
|
+
}
|
|
73
|
+
else if (warnings) {
|
|
74
|
+
warnings.push(`Token path "${bare}" exists in multiple collections (${owners
|
|
75
|
+
.map((o) => `"${o.setName}"`)
|
|
76
|
+
.join(", ")}) — bare alias references to it are ambiguous and will not resolve. Use a set-qualified reference like {${slugifySetName(owners[0].setName)}.${bare}}.`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return index;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Resolve a reference to the path segments of its TARGET token — for
|
|
83
|
+
* formatters that name-ify aliases (CSS `var(--...)`, SCSS `$...`,
|
|
84
|
+
* Tokens Studio / SD v3 `{...}` refs). Set-qualified references
|
|
85
|
+
* (`{set-slug.path.to.token}`) resolve to the target token's own path
|
|
86
|
+
* (WITHOUT the set qualifier), matching how those formatters name the
|
|
87
|
+
* target's declaration. Falls back to the raw reference path when the
|
|
88
|
+
* target isn't in the index (e.g. hand-written bare refs to tokens that
|
|
89
|
+
* weren't exported).
|
|
90
|
+
*/
|
|
91
|
+
export function referenceTargetPath(reference, index) {
|
|
92
|
+
const bare = reference.replace(/^\{|\}$/g, "");
|
|
93
|
+
const target = index.get(bare);
|
|
94
|
+
if (target)
|
|
95
|
+
return target.path;
|
|
96
|
+
return bare.split(".");
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Resolve a single alias reference. Returns the eventual literal value, or
|
|
100
|
+
* throws if the reference is unresolvable or cyclic.
|
|
101
|
+
*/
|
|
102
|
+
export function resolveReference(reference, mode, index, seen = new Set()) {
|
|
103
|
+
// Strip the curly braces if present: "{color.primary}" → "color.primary"
|
|
104
|
+
const path = reference.replace(/^\{|\}$/g, "");
|
|
105
|
+
if (seen.has(path)) {
|
|
106
|
+
throw new Error(`[figma-console-mcp] Alias cycle detected: ${[...seen, path].join(" → ")}`);
|
|
107
|
+
}
|
|
108
|
+
seen.add(path);
|
|
109
|
+
const target = index.get(path);
|
|
110
|
+
if (!target) {
|
|
111
|
+
throw new Error(`[figma-console-mcp] Unresolvable alias reference: {${path}}`);
|
|
112
|
+
}
|
|
113
|
+
// Find the value for the requested mode; fall back to the only mode if
|
|
114
|
+
// there's just one (alias's target may be single-mode while the source
|
|
115
|
+
// is multi-mode, or vice versa).
|
|
116
|
+
const value = target.values[mode] ??
|
|
117
|
+
(Object.keys(target.values).length === 1
|
|
118
|
+
? Object.values(target.values)[0]
|
|
119
|
+
: undefined);
|
|
120
|
+
if (!value) {
|
|
121
|
+
throw new Error(`[figma-console-mcp] Alias {${path}} has no value for mode "${mode}"`);
|
|
122
|
+
}
|
|
123
|
+
// Chain: if the target's value is itself an alias, recurse.
|
|
124
|
+
if (value.reference) {
|
|
125
|
+
return resolveReference(value.reference, mode, index, seen);
|
|
126
|
+
}
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Resolve an alias chain to its final literal value, walking through
|
|
131
|
+
* intermediate alias hops. Returns the final TokenValue (with `literal` set
|
|
132
|
+
* if resolution succeeded) or `null` if the chain ends at a cross-library
|
|
133
|
+
* reference / unresolvable target / cycle.
|
|
134
|
+
*
|
|
135
|
+
* Used by formatters that can't natively express alias references in their
|
|
136
|
+
* output (Tailwind v3, TypeScript modules, plain JSON) — those need literal
|
|
137
|
+
* values at export time.
|
|
138
|
+
*
|
|
139
|
+
* Safer counterpart of `resolveReference` because it swallows errors
|
|
140
|
+
* (unresolvable / cycle) into `null` rather than throwing; formatters can
|
|
141
|
+
* then emit a comment or skip the token instead of failing the whole export.
|
|
142
|
+
*/
|
|
143
|
+
export function resolveAliasChain(value, mode, index) {
|
|
144
|
+
if (!value.reference)
|
|
145
|
+
return value;
|
|
146
|
+
// Cross-library aliases are not resolvable — formatters should skip with a comment.
|
|
147
|
+
const bare = value.reference.replace(/^\{|\}$/g, "");
|
|
148
|
+
if (bare.startsWith("__library:") || bare === "unknown")
|
|
149
|
+
return null;
|
|
150
|
+
try {
|
|
151
|
+
const resolved = resolveReference(value.reference, mode, index);
|
|
152
|
+
// resolveReference throws on cycles / unresolvable, so a returned value
|
|
153
|
+
// is either a literal or another reference. If still a reference,
|
|
154
|
+
// recurse (defensive — resolveReference already chases chains, but the
|
|
155
|
+
// top-level call may return a value with `reference` if mode-fallback
|
|
156
|
+
// routes through an aliased entry).
|
|
157
|
+
if (resolved.reference) {
|
|
158
|
+
return resolveAliasChain(resolved, mode, index);
|
|
159
|
+
}
|
|
160
|
+
return resolved;
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Validate every alias in the document. Returns a list of error messages —
|
|
168
|
+
* empty array means all aliases resolve cleanly.
|
|
169
|
+
*/
|
|
170
|
+
export function validateAliases(doc) {
|
|
171
|
+
const index = buildTokenIndex(doc);
|
|
172
|
+
const errors = [];
|
|
173
|
+
for (const set of doc.sets) {
|
|
174
|
+
for (const token of set.tokens) {
|
|
175
|
+
for (const [mode, value] of Object.entries(token.values)) {
|
|
176
|
+
if (value.reference) {
|
|
177
|
+
try {
|
|
178
|
+
resolveReference(value.reference, mode, index);
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
errors.push(`${token.path.join(".")} (mode "${mode}"): ${err instanceof Error ? err.message : String(err)}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return errors;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Format an alias reference for DTCG output. DTCG uses `{path.to.token}`
|
|
191
|
+
* syntax with curly braces.
|
|
192
|
+
*/
|
|
193
|
+
export function formatDtcgReference(referencePath) {
|
|
194
|
+
return `{${referencePath.join(".")}}`;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Parse a DTCG alias string back into a path array. Returns null if the
|
|
198
|
+
* string isn't an alias reference.
|
|
199
|
+
*/
|
|
200
|
+
export function parseDtcgReference(s) {
|
|
201
|
+
const match = s.match(/^\{([^}]+)\}$/);
|
|
202
|
+
if (!match)
|
|
203
|
+
return null;
|
|
204
|
+
return match[1].split(".");
|
|
205
|
+
}
|