@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,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code-side accessibility scanning via axe-core + JSDOM.
|
|
3
|
+
*
|
|
4
|
+
* Delegates all rule logic to axe-core (Deque) — the MCP never owns
|
|
5
|
+
* a rule database. JSDOM provides a lightweight DOM for structural checks
|
|
6
|
+
* (~50 rules: ARIA, semantics, alt text, form labels, headings, landmarks).
|
|
7
|
+
*
|
|
8
|
+
* Visual rules (color contrast, focus-visible) are NOT available via JSDOM —
|
|
9
|
+
* those are handled by the design-side figma_lint_design tool.
|
|
10
|
+
*/
|
|
11
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
12
|
+
/**
|
|
13
|
+
* Extract a CodeSpec.accessibility object from HTML + axe-core results.
|
|
14
|
+
* This bridges Phase 3 (code scanning) → Phase 4 (parity comparison).
|
|
15
|
+
*
|
|
16
|
+
* Parses the HTML to extract semantic element, ARIA attributes, and states.
|
|
17
|
+
* Uses axe-core results to infer what the code supports.
|
|
18
|
+
*/
|
|
19
|
+
export declare function axeResultsToCodeSpec(html: string, axeResults: any): Record<string, any>;
|
|
20
|
+
export declare function registerAccessibilityTools(server: McpServer): void;
|
|
21
|
+
//# sourceMappingURL=accessibility-tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessibility-tools.d.ts","sourceRoot":"","sources":["../../src/core/accessibility-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA6FpE;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAoGvF;AAgHD,wBAAgB,0BAA0B,CACzC,MAAM,EAAE,SAAS,GACf,IAAI,CAmFN"}
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code-side accessibility scanning via axe-core + JSDOM.
|
|
3
|
+
*
|
|
4
|
+
* Delegates all rule logic to axe-core (Deque) — the MCP never owns
|
|
5
|
+
* a rule database. JSDOM provides a lightweight DOM for structural checks
|
|
6
|
+
* (~50 rules: ARIA, semantics, alt text, form labels, headings, landmarks).
|
|
7
|
+
*
|
|
8
|
+
* Visual rules (color contrast, focus-visible) are NOT available via JSDOM —
|
|
9
|
+
* those are handled by the design-side figma_lint_design tool.
|
|
10
|
+
*/
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import { logger } from "./logger.js";
|
|
13
|
+
// Lazy-load axe-core and jsdom to keep them optional
|
|
14
|
+
let axeCore = null;
|
|
15
|
+
let JSDOM = null;
|
|
16
|
+
let depsLoaded = false;
|
|
17
|
+
let depsError = null;
|
|
18
|
+
async function loadDeps() {
|
|
19
|
+
if (depsLoaded)
|
|
20
|
+
return;
|
|
21
|
+
try {
|
|
22
|
+
axeCore = await import("axe-core");
|
|
23
|
+
// axe-core's default export structure
|
|
24
|
+
if (axeCore.default)
|
|
25
|
+
axeCore = axeCore.default;
|
|
26
|
+
const jsdomModule = await import("jsdom");
|
|
27
|
+
JSDOM = jsdomModule.JSDOM;
|
|
28
|
+
depsLoaded = true;
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
depsError = `axe-core or jsdom not installed. Run: npm install axe-core jsdom\n${e.message}`;
|
|
32
|
+
throw new Error(depsError);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Run axe-core against an HTML string using JSDOM.
|
|
37
|
+
*
|
|
38
|
+
* JSDOM limitations: no computed styles, no layout, no visual rendering.
|
|
39
|
+
* This means ~50-60 structural rules work, but visual rules
|
|
40
|
+
* (color-contrast, focus-visible, etc.) will report as "incomplete".
|
|
41
|
+
*/
|
|
42
|
+
async function scanHtmlWithAxe(html, options = {}) {
|
|
43
|
+
await loadDeps();
|
|
44
|
+
// Wrap HTML fragment in a full document if needed
|
|
45
|
+
const fullHtml = html.includes("<html") || html.includes("<!DOCTYPE")
|
|
46
|
+
? html
|
|
47
|
+
: `<!DOCTYPE html><html lang="en"><head><title>Scan</title></head><body>${html}</body></html>`;
|
|
48
|
+
const dom = new JSDOM(fullHtml, {
|
|
49
|
+
runScripts: "dangerously",
|
|
50
|
+
pretendToBeVisual: true,
|
|
51
|
+
url: "http://localhost",
|
|
52
|
+
});
|
|
53
|
+
const { document, window } = dom.window;
|
|
54
|
+
// Inject axe-core into the JSDOM window
|
|
55
|
+
const axeSource = axeCore.source;
|
|
56
|
+
const scriptEl = document.createElement("script");
|
|
57
|
+
scriptEl.textContent = axeSource;
|
|
58
|
+
document.head.appendChild(scriptEl);
|
|
59
|
+
// Configure axe run options
|
|
60
|
+
const runOptions = {};
|
|
61
|
+
if (options.tags && options.tags.length > 0) {
|
|
62
|
+
runOptions.runOnly = { type: "tag", values: options.tags };
|
|
63
|
+
}
|
|
64
|
+
// Disable rules that require visual rendering (always fail/incomplete in JSDOM)
|
|
65
|
+
if (options.disableVisualRules !== false) {
|
|
66
|
+
runOptions.rules = {
|
|
67
|
+
"color-contrast": { enabled: false },
|
|
68
|
+
"color-contrast-enhanced": { enabled: false },
|
|
69
|
+
"link-in-text-block": { enabled: false },
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
// Determine scan context
|
|
73
|
+
const context = options.context || document;
|
|
74
|
+
try {
|
|
75
|
+
const results = await window.axe.run(context, runOptions);
|
|
76
|
+
// Clean up
|
|
77
|
+
dom.window.close();
|
|
78
|
+
return results;
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
dom.window.close();
|
|
82
|
+
throw new Error(`axe-core scan failed: ${err.message}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Extract a CodeSpec.accessibility object from HTML + axe-core results.
|
|
87
|
+
* This bridges Phase 3 (code scanning) → Phase 4 (parity comparison).
|
|
88
|
+
*
|
|
89
|
+
* Parses the HTML to extract semantic element, ARIA attributes, and states.
|
|
90
|
+
* Uses axe-core results to infer what the code supports.
|
|
91
|
+
*/
|
|
92
|
+
export function axeResultsToCodeSpec(html, axeResults) {
|
|
93
|
+
const spec = {};
|
|
94
|
+
// Parse HTML to extract attributes (lightweight regex-based, no DOM needed)
|
|
95
|
+
const htmlLower = html.toLowerCase();
|
|
96
|
+
// Semantic element: find the root/first meaningful element
|
|
97
|
+
const rootElementMatch = html.match(/<(button|a|input|select|textarea|details|dialog|nav|main|form|label|fieldset)\b/i);
|
|
98
|
+
if (rootElementMatch) {
|
|
99
|
+
spec.semanticElement = rootElementMatch[1].toLowerCase();
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
const firstElementMatch = html.match(/<(\w+)[\s>]/);
|
|
103
|
+
if (firstElementMatch && !["div", "span", "html", "head", "body", "script", "style", "!doctype"].includes(firstElementMatch[1].toLowerCase())) {
|
|
104
|
+
spec.semanticElement = firstElementMatch[1].toLowerCase();
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
spec.semanticElement = "div";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// ARIA role
|
|
111
|
+
const roleMatch = html.match(/role=["']([^"']+)["']/i);
|
|
112
|
+
if (roleMatch) {
|
|
113
|
+
spec.role = roleMatch[1];
|
|
114
|
+
}
|
|
115
|
+
// ARIA label
|
|
116
|
+
const ariaLabelMatch = html.match(/aria-label=["']([^"']+)["']/i);
|
|
117
|
+
if (ariaLabelMatch) {
|
|
118
|
+
spec.ariaLabel = ariaLabelMatch[1];
|
|
119
|
+
}
|
|
120
|
+
// Focus visible: check for :focus-visible or :focus in inline styles/class names,
|
|
121
|
+
// or infer from element type (native interactive elements have default focus)
|
|
122
|
+
const nativeFocusElements = ["button", "a", "input", "select", "textarea"];
|
|
123
|
+
const hasFocusCSS = /focus-visible|:focus\b|outline.*focus|ring.*focus|focus.*ring/i.test(html);
|
|
124
|
+
spec.focusVisible = hasFocusCSS || nativeFocusElements.includes(spec.semanticElement || "");
|
|
125
|
+
// Disabled support: only assert true when we find positive evidence.
|
|
126
|
+
// Absence of disabled/aria-disabled in a single HTML snapshot does NOT mean
|
|
127
|
+
// the component lacks disabled support — it may be in a non-disabled state.
|
|
128
|
+
if (/\bdisabled\b|aria-disabled/i.test(htmlLower)) {
|
|
129
|
+
spec.supportsDisabled = true;
|
|
130
|
+
}
|
|
131
|
+
// (leave undefined when not found — absence ≠ lack of support)
|
|
132
|
+
// Error support: same principle — only assert true on positive evidence.
|
|
133
|
+
// A default-state HTML snippet won't have aria-invalid; that doesn't mean
|
|
134
|
+
// the component can't enter an error state.
|
|
135
|
+
if (/aria-invalid|aria-errormessage|aria-describedby.*error/i.test(htmlLower)) {
|
|
136
|
+
spec.supportsError = true;
|
|
137
|
+
}
|
|
138
|
+
// (leave undefined when not found — scan a different state to confirm)
|
|
139
|
+
// Required: check for required or aria-required attributes
|
|
140
|
+
if (/aria-required=["']true["']|required(?!=)/i.test(html)) {
|
|
141
|
+
spec.ariaRequired = true;
|
|
142
|
+
}
|
|
143
|
+
else if (/aria-required=["']false["']/i.test(html)) {
|
|
144
|
+
spec.ariaRequired = false;
|
|
145
|
+
}
|
|
146
|
+
// Keyboard interactions: infer from element type
|
|
147
|
+
const keyboardInteractions = [];
|
|
148
|
+
if (spec.semanticElement === "button" || spec.role === "button") {
|
|
149
|
+
keyboardInteractions.push("Enter", "Space");
|
|
150
|
+
}
|
|
151
|
+
else if (spec.semanticElement === "a" || spec.role === "link") {
|
|
152
|
+
keyboardInteractions.push("Enter");
|
|
153
|
+
}
|
|
154
|
+
else if (spec.semanticElement === "input" || spec.semanticElement === "textarea") {
|
|
155
|
+
keyboardInteractions.push("Tab (focus)", "Type (input)");
|
|
156
|
+
}
|
|
157
|
+
else if (spec.semanticElement === "select" || spec.role === "listbox") {
|
|
158
|
+
keyboardInteractions.push("Arrow keys", "Enter", "Space");
|
|
159
|
+
}
|
|
160
|
+
else if (spec.role === "checkbox" || spec.role === "switch") {
|
|
161
|
+
keyboardInteractions.push("Space");
|
|
162
|
+
}
|
|
163
|
+
else if (spec.role === "tab") {
|
|
164
|
+
keyboardInteractions.push("Arrow keys");
|
|
165
|
+
}
|
|
166
|
+
// Check HTML for custom keyboard handlers
|
|
167
|
+
if (/onkeydown|onkeyup|onkeypress|@keydown|@keyup|v-on:keydown/i.test(html)) {
|
|
168
|
+
if (!keyboardInteractions.includes("Custom key handler")) {
|
|
169
|
+
keyboardInteractions.push("Custom key handler");
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (keyboardInteractions.length > 0) {
|
|
173
|
+
spec.keyboardInteractions = keyboardInteractions;
|
|
174
|
+
}
|
|
175
|
+
// Use axe-core results to refine: if certain violations exist, it tells us what's missing
|
|
176
|
+
if (axeResults?.violations) {
|
|
177
|
+
for (const v of axeResults.violations) {
|
|
178
|
+
// If button-name violation exists, the button has no accessible name
|
|
179
|
+
if (v.id === "button-name") {
|
|
180
|
+
spec.ariaLabel = undefined; // Explicitly missing
|
|
181
|
+
}
|
|
182
|
+
// If label violation exists, input lacks a label
|
|
183
|
+
if (v.id === "label") {
|
|
184
|
+
spec.ariaLabel = undefined;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return spec;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Format axe-core results into our standard lint-like output structure.
|
|
192
|
+
*/
|
|
193
|
+
function formatAxeResults(axeResults) {
|
|
194
|
+
const categories = [];
|
|
195
|
+
const severityMap = {
|
|
196
|
+
critical: "critical",
|
|
197
|
+
serious: "critical",
|
|
198
|
+
moderate: "warning",
|
|
199
|
+
minor: "info",
|
|
200
|
+
};
|
|
201
|
+
// Group violations
|
|
202
|
+
for (const violation of axeResults.violations || []) {
|
|
203
|
+
const severity = severityMap[violation.impact] || "warning";
|
|
204
|
+
const nodes = violation.nodes.map((node) => ({
|
|
205
|
+
html: node.html?.substring(0, 200),
|
|
206
|
+
target: node.target,
|
|
207
|
+
failureSummary: node.failureSummary?.substring(0, 300),
|
|
208
|
+
}));
|
|
209
|
+
categories.push({
|
|
210
|
+
rule: violation.id,
|
|
211
|
+
severity,
|
|
212
|
+
count: violation.nodes.length,
|
|
213
|
+
description: violation.help,
|
|
214
|
+
wcagTags: violation.tags.filter((t) => t.startsWith("wcag") || t.startsWith("best-practice")),
|
|
215
|
+
helpUrl: violation.helpUrl,
|
|
216
|
+
nodes: nodes.slice(0, 10), // Cap at 10 per rule
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
// Sort: critical first, then by count
|
|
220
|
+
categories.sort((a, b) => {
|
|
221
|
+
const sevOrder = { critical: 0, warning: 1, info: 2 };
|
|
222
|
+
if (sevOrder[a.severity] !== sevOrder[b.severity]) {
|
|
223
|
+
return sevOrder[a.severity] - sevOrder[b.severity];
|
|
224
|
+
}
|
|
225
|
+
return b.count - a.count;
|
|
226
|
+
});
|
|
227
|
+
// Summary
|
|
228
|
+
const summary = { critical: 0, warning: 0, info: 0, total: 0 };
|
|
229
|
+
for (const cat of categories) {
|
|
230
|
+
summary[cat.severity] += cat.count;
|
|
231
|
+
summary.total += cat.count;
|
|
232
|
+
}
|
|
233
|
+
return {
|
|
234
|
+
engine: "axe-core",
|
|
235
|
+
version: axeResults.testEngine?.version || "unknown",
|
|
236
|
+
mode: "jsdom-structural",
|
|
237
|
+
note: "JSDOM mode: structural/semantic checks only. Visual rules (color contrast, focus visibility) are disabled — use figma_lint_design for visual accessibility checks.",
|
|
238
|
+
categories,
|
|
239
|
+
summary,
|
|
240
|
+
passes: axeResults.passes?.length || 0,
|
|
241
|
+
incomplete: axeResults.incomplete?.length || 0,
|
|
242
|
+
inapplicable: axeResults.inapplicable?.length || 0,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Heuristic WCAG 1.4.12 (Text Spacing) / 1.4.4 (Resize Text) signal for CODE.
|
|
247
|
+
*
|
|
248
|
+
* This is where text-spacing override support actually belongs: 1.4.12 is about whether
|
|
249
|
+
* content survives a USER overriding line/letter/word/paragraph spacing — it cannot be
|
|
250
|
+
* proven from a design's spacing value, only from the code. axe-core can't evaluate it
|
|
251
|
+
* (it needs layout), but the common, checkable risk indicator is fixed-px typography:
|
|
252
|
+
* line-height/font-size locked in px don't respond to spacing overrides or text-only zoom,
|
|
253
|
+
* whereas unitless line-height and rem/em sizing do. Surfaces fixed-px usage as an advisory
|
|
254
|
+
* (info) finding so teams verify reflow — without red-flagging sub-1.5 line heights, which
|
|
255
|
+
* are not failures.
|
|
256
|
+
*/
|
|
257
|
+
function scanTextSpacingSupport(html) {
|
|
258
|
+
// Collect CSS from <style> blocks and inline style="" attributes
|
|
259
|
+
let css = "";
|
|
260
|
+
const styleBlocks = html.match(/<style[^>]*>([\s\S]*?)<\/style>/gi) || [];
|
|
261
|
+
for (const block of styleBlocks)
|
|
262
|
+
css += block.replace(/<\/?style[^>]*>/gi, "") + "\n";
|
|
263
|
+
const inlineStyles = html.match(/style=["']([^"']*)["']/gi) || [];
|
|
264
|
+
for (const s of inlineStyles)
|
|
265
|
+
css += s.replace(/^style=["']|["']$/gi, "") + ";\n";
|
|
266
|
+
if (!css.trim())
|
|
267
|
+
return null;
|
|
268
|
+
// line-height: <n>px → won't grow when a user overrides text spacing (1.4.12 risk)
|
|
269
|
+
const pxLineHeights = css.match(/line-height\s*:\s*[\d.]+px/gi) || [];
|
|
270
|
+
// font-size: <n>px → won't scale on text-only zoom / 200% resize (1.4.4 risk)
|
|
271
|
+
const pxFontSizes = css.match(/font-size\s*:\s*[\d.]+px/gi) || [];
|
|
272
|
+
if (pxLineHeights.length === 0 && pxFontSizes.length === 0)
|
|
273
|
+
return null;
|
|
274
|
+
const samples = [
|
|
275
|
+
...pxLineHeights.slice(0, 5).map((m) => m.trim()),
|
|
276
|
+
...pxFontSizes.slice(0, 5).map((m) => m.trim()),
|
|
277
|
+
];
|
|
278
|
+
return {
|
|
279
|
+
rule: "text-spacing-support",
|
|
280
|
+
severity: "info",
|
|
281
|
+
count: pxLineHeights.length + pxFontSizes.length,
|
|
282
|
+
description: `Typography uses fixed px units (${pxLineHeights.length} px line-height, ${pxFontSizes.length} px font-size). ` +
|
|
283
|
+
"This is where WCAG 1.4.12 (Text Spacing) and 1.4.4 (Resize Text) are actually verified — not on the design side. " +
|
|
284
|
+
"Prefer unitless line-height (e.g. line-height: 1.4) and rem/em font sizes so user spacing overrides and text-only zoom do not clip or overlap content. " +
|
|
285
|
+
"Advisory only: a sub-1.5 line height is NOT a failure — confirm content still reflows when the 1.4.12 text-spacing overrides are applied.",
|
|
286
|
+
wcagTags: ["wcag22aa", "wcag1412", "wcag144"],
|
|
287
|
+
helpUrl: "https://www.w3.org/WAI/WCAG22/Understanding/text-spacing.html",
|
|
288
|
+
samples,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
export function registerAccessibilityTools(server) {
|
|
292
|
+
server.tool("figma_scan_code_accessibility", "Scan HTML code for accessibility violations using axe-core (Deque). " +
|
|
293
|
+
"Runs structural/semantic checks via JSDOM: ARIA attributes, roles, labels, alt text, " +
|
|
294
|
+
"form labels, heading order, landmarks, semantic HTML, tabindex, duplicate IDs, lang attribute, and ~50 more rules. " +
|
|
295
|
+
"Visual checks (color contrast, focus visibility) are disabled in this mode — use figma_lint_design for visual a11y on the design side. " +
|
|
296
|
+
"Also surfaces a WCAG 1.4.12 (Text Spacing) / 1.4.4 (Resize Text) advisory when typography is locked to fixed px units — the code side is where text-spacing override support is actually verified (a sub-1.5 line height in a design is not itself a failure). " +
|
|
297
|
+
"Together, these two tools provide full-spectrum accessibility coverage across design and code. " +
|
|
298
|
+
"Pass component HTML directly or use with figma_check_design_parity for design-to-code a11y comparison. " +
|
|
299
|
+
"No Figma connection required — this is a standalone code analysis tool.", {
|
|
300
|
+
html: z.string().describe("HTML string to scan. Can be a full document or a component fragment (will be wrapped in a valid document)."),
|
|
301
|
+
tags: z.array(z.string()).optional().describe("WCAG tag filter. Examples: ['wcag2a'], ['wcag2aa'], ['wcag21aa'], ['wcag22aa'], ['best-practice']. " +
|
|
302
|
+
"Defaults to all structural rules if omitted."),
|
|
303
|
+
context: z.string().optional().describe("CSS selector to scope the scan to a specific element (e.g., '#my-component', '.card'). Scans entire document if omitted."),
|
|
304
|
+
includePassingRules: z.boolean().optional().describe("If true, includes count of passing and incomplete rules in the response (default: false)."),
|
|
305
|
+
mapToCodeSpec: z.boolean().optional().describe("If true, includes a codeSpec.accessibility object auto-extracted from the HTML + scan results. " +
|
|
306
|
+
"Pass this directly into figma_check_design_parity's codeSpec.accessibility field for automated design-to-code a11y parity checking."),
|
|
307
|
+
}, async ({ html, tags, context, includePassingRules, mapToCodeSpec }) => {
|
|
308
|
+
try {
|
|
309
|
+
const axeResults = await scanHtmlWithAxe(html, {
|
|
310
|
+
tags: tags || undefined,
|
|
311
|
+
context: context || undefined,
|
|
312
|
+
});
|
|
313
|
+
const formatted = formatAxeResults(axeResults);
|
|
314
|
+
// Supplementary WCAG 1.4.12 / 1.4.4 advisory — axe can't see layout/overrides,
|
|
315
|
+
// and this (not the design side) is where text-spacing support is verifiable.
|
|
316
|
+
const textSpacing = scanTextSpacingSupport(html);
|
|
317
|
+
if (textSpacing) {
|
|
318
|
+
formatted.categories.push(textSpacing);
|
|
319
|
+
formatted.summary.info += textSpacing.count;
|
|
320
|
+
formatted.summary.total += textSpacing.count;
|
|
321
|
+
}
|
|
322
|
+
// Optionally strip pass/incomplete counts to save tokens
|
|
323
|
+
if (!includePassingRules) {
|
|
324
|
+
delete formatted.passes;
|
|
325
|
+
delete formatted.incomplete;
|
|
326
|
+
delete formatted.inapplicable;
|
|
327
|
+
}
|
|
328
|
+
// Auto-generate CodeSpec.accessibility from HTML + results
|
|
329
|
+
if (mapToCodeSpec) {
|
|
330
|
+
formatted.codeSpecAccessibility = axeResultsToCodeSpec(html, axeResults);
|
|
331
|
+
formatted.codeSpecAccessibility._usage = "Pass this object as codeSpec.accessibility in figma_check_design_parity for automated a11y parity checking.";
|
|
332
|
+
}
|
|
333
|
+
return {
|
|
334
|
+
content: [
|
|
335
|
+
{
|
|
336
|
+
type: "text",
|
|
337
|
+
text: JSON.stringify(formatted, null, 2),
|
|
338
|
+
},
|
|
339
|
+
],
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
catch (error) {
|
|
343
|
+
const isDepsError = error.message?.includes("not installed");
|
|
344
|
+
logger.error({ error }, "Failed to scan code accessibility");
|
|
345
|
+
return {
|
|
346
|
+
content: [
|
|
347
|
+
{
|
|
348
|
+
type: "text",
|
|
349
|
+
text: JSON.stringify({
|
|
350
|
+
error: error.message,
|
|
351
|
+
hint: isDepsError
|
|
352
|
+
? "Install dependencies: npm install axe-core jsdom"
|
|
353
|
+
: "Check that the HTML is valid. For visual accessibility checks, use figma_lint_design instead.",
|
|
354
|
+
}),
|
|
355
|
+
},
|
|
356
|
+
],
|
|
357
|
+
isError: true,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
//# sourceMappingURL=accessibility-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessibility-tools.js","sourceRoot":"","sources":["../../src/core/accessibility-tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Figma Annotations MCP Tools
|
|
3
|
+
* Tools for reading, writing, and managing design annotations on Figma nodes.
|
|
4
|
+
* Annotations are a Plugin API feature — requires Desktop Bridge plugin connection.
|
|
5
|
+
*
|
|
6
|
+
* Annotations are distinct from comments: they are node-level design specs that
|
|
7
|
+
* can pin specific properties (fills, width, typography, etc.) and support
|
|
8
|
+
* markdown-formatted labels. Designers use them to communicate animation timings,
|
|
9
|
+
* accessibility requirements, interaction specs, and other implementation details
|
|
10
|
+
* that don't fit in the description field.
|
|
11
|
+
*/
|
|
12
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
13
|
+
export declare function registerAnnotationTools(server: McpServer, getDesktopConnector: () => Promise<any>): void;
|
|
14
|
+
//# sourceMappingURL=annotation-tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"annotation-tools.d.ts","sourceRoot":"","sources":["../../src/core/annotation-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA4EzE,wBAAgB,uBAAuB,CACtC,MAAM,EAAE,SAAS,EACjB,mBAAmB,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,GACrC,IAAI,CA0LN"}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Figma Annotations MCP Tools
|
|
3
|
+
* Tools for reading, writing, and managing design annotations on Figma nodes.
|
|
4
|
+
* Annotations are a Plugin API feature — requires Desktop Bridge plugin connection.
|
|
5
|
+
*
|
|
6
|
+
* Annotations are distinct from comments: they are node-level design specs that
|
|
7
|
+
* can pin specific properties (fills, width, typography, etc.) and support
|
|
8
|
+
* markdown-formatted labels. Designers use them to communicate animation timings,
|
|
9
|
+
* accessibility requirements, interaction specs, and other implementation details
|
|
10
|
+
* that don't fit in the description field.
|
|
11
|
+
*/
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
import { createChildLogger } from "./logger.js";
|
|
14
|
+
const logger = createChildLogger({ component: "annotation-tools" });
|
|
15
|
+
// Valid AnnotationPropertyType values from the Figma Plugin API
|
|
16
|
+
// Sourced from @figma/plugin-typings — keep in sync with AnnotationPropertyType union
|
|
17
|
+
// Reference: https://developers.figma.com/docs/plugins/api/AnnotationProperty
|
|
18
|
+
const ANNOTATION_PROPERTY_TYPES = [
|
|
19
|
+
"width",
|
|
20
|
+
"height",
|
|
21
|
+
"maxWidth",
|
|
22
|
+
"minWidth",
|
|
23
|
+
"maxHeight",
|
|
24
|
+
"minHeight",
|
|
25
|
+
"fills",
|
|
26
|
+
"strokes",
|
|
27
|
+
"effects",
|
|
28
|
+
"strokeWeight",
|
|
29
|
+
"cornerRadius",
|
|
30
|
+
"textStyleId",
|
|
31
|
+
"textAlignHorizontal",
|
|
32
|
+
"fontFamily",
|
|
33
|
+
"fontStyle",
|
|
34
|
+
"fontSize",
|
|
35
|
+
"fontWeight",
|
|
36
|
+
"lineHeight",
|
|
37
|
+
"letterSpacing",
|
|
38
|
+
"itemSpacing",
|
|
39
|
+
"padding",
|
|
40
|
+
"layoutMode",
|
|
41
|
+
"alignItems",
|
|
42
|
+
"opacity",
|
|
43
|
+
"mainComponent",
|
|
44
|
+
"gridRowGap",
|
|
45
|
+
"gridColumnGap",
|
|
46
|
+
"gridRowCount",
|
|
47
|
+
"gridColumnCount",
|
|
48
|
+
"gridRowAnchorIndex",
|
|
49
|
+
"gridColumnAnchorIndex",
|
|
50
|
+
"gridRowSpan",
|
|
51
|
+
"gridColumnSpan",
|
|
52
|
+
];
|
|
53
|
+
// Zod schema for annotation property
|
|
54
|
+
const annotationPropertySchema = z.object({
|
|
55
|
+
type: z
|
|
56
|
+
.enum(ANNOTATION_PROPERTY_TYPES)
|
|
57
|
+
.describe("Design property to pin (e.g., 'fills', 'width', 'fontSize')"),
|
|
58
|
+
});
|
|
59
|
+
// Zod schema for a single annotation
|
|
60
|
+
const annotationSchema = z.object({
|
|
61
|
+
label: z
|
|
62
|
+
.string()
|
|
63
|
+
.optional()
|
|
64
|
+
.describe("Plain text annotation label"),
|
|
65
|
+
labelMarkdown: z
|
|
66
|
+
.string()
|
|
67
|
+
.optional()
|
|
68
|
+
.describe("Rich text annotation label with markdown formatting. Supports bold, italic, links, lists, code, and headers."),
|
|
69
|
+
properties: z
|
|
70
|
+
.array(annotationPropertySchema)
|
|
71
|
+
.optional()
|
|
72
|
+
.describe("Design properties to pin to this annotation (e.g., fills, width, fontSize)"),
|
|
73
|
+
categoryId: z
|
|
74
|
+
.string()
|
|
75
|
+
.optional()
|
|
76
|
+
.describe("Annotation category ID. Use figma_get_annotation_categories to list available categories."),
|
|
77
|
+
});
|
|
78
|
+
// ============================================================================
|
|
79
|
+
// Tool Registration
|
|
80
|
+
// ============================================================================
|
|
81
|
+
export function registerAnnotationTools(server, getDesktopConnector) {
|
|
82
|
+
// -----------------------------------------------------------------------
|
|
83
|
+
// Tool: figma_get_annotations
|
|
84
|
+
// -----------------------------------------------------------------------
|
|
85
|
+
server.tool("figma_get_annotations", "Read annotations from a Figma node. Annotations are designer-authored specs attached to nodes — they can include notes (plain text or markdown), pinned design properties (fills, width, fontSize, etc.), and category labels. Use this to discover animation timings, interaction specs, accessibility requirements, and other implementation details that designers annotate directly on the design. Set include_children=true to get annotations from child nodes too (useful for full component documentation). Requires Desktop Bridge plugin.", {
|
|
86
|
+
nodeId: z
|
|
87
|
+
.string()
|
|
88
|
+
.describe("Node ID to read annotations from (e.g., '695:313')"),
|
|
89
|
+
include_children: z.preprocess((v) => (typeof v === "string" ? v === "true" : v), z.boolean().optional().default(false)).describe("Also read annotations from child nodes. Useful for getting all annotations within a component tree."),
|
|
90
|
+
depth: z.preprocess((v) => (typeof v === "string" ? Number(v) : v), z.number().optional().default(1)).describe("How many levels deep to traverse when include_children is true (default: 1, max recommended: 5)"),
|
|
91
|
+
}, async ({ nodeId, include_children = false, depth = 1 }) => {
|
|
92
|
+
try {
|
|
93
|
+
logger.info({ nodeId, include_children, depth }, "Getting annotations");
|
|
94
|
+
const connector = await getDesktopConnector();
|
|
95
|
+
const result = await connector.getAnnotations(nodeId, include_children, Math.min(depth, 10));
|
|
96
|
+
if (!result || (result.success === false)) {
|
|
97
|
+
throw new Error(result?.error || "Failed to get annotations");
|
|
98
|
+
}
|
|
99
|
+
// The result may come back as { success, data } (WebSocket) or directly as data
|
|
100
|
+
const data = result.data || result;
|
|
101
|
+
return {
|
|
102
|
+
content: [
|
|
103
|
+
{
|
|
104
|
+
type: "text",
|
|
105
|
+
text: JSON.stringify(data),
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
112
|
+
logger.error({ error }, "Failed to get annotations");
|
|
113
|
+
return {
|
|
114
|
+
content: [
|
|
115
|
+
{
|
|
116
|
+
type: "text",
|
|
117
|
+
text: JSON.stringify({
|
|
118
|
+
error: "get_annotations_failed",
|
|
119
|
+
message: `Cannot get annotations. ${message}`,
|
|
120
|
+
hint: "Annotations require the Desktop Bridge plugin to be running in Figma.",
|
|
121
|
+
}),
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
isError: true,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
// -----------------------------------------------------------------------
|
|
129
|
+
// Tool: figma_set_annotations
|
|
130
|
+
// -----------------------------------------------------------------------
|
|
131
|
+
server.tool("figma_set_annotations", "Write or clear annotations on a Figma node. Annotations communicate design specs to developers — use them to document animation timings, easing curves, interaction behaviors, accessibility requirements, and implementation notes. Supports plain text labels, rich markdown labels, pinned design properties, and annotation categories. Pass an empty array to clear all annotations. Use mode='append' to add to existing annotations, or mode='replace' (default) to overwrite. Requires Desktop Bridge plugin. This operation is undoable in Figma (Cmd+Z).", {
|
|
132
|
+
nodeId: z
|
|
133
|
+
.string()
|
|
134
|
+
.describe("Node ID to write annotations to (e.g., '695:313')"),
|
|
135
|
+
annotations: z.preprocess((v) => {
|
|
136
|
+
if (typeof v === "string") {
|
|
137
|
+
try {
|
|
138
|
+
return JSON.parse(v);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return v;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return v;
|
|
145
|
+
}, z.array(annotationSchema)).describe("Array of annotations to set. Each annotation can have a label (plain or markdown), pinned properties, and a category. Pass an empty array [] to clear all annotations."),
|
|
146
|
+
mode: z
|
|
147
|
+
.enum(["replace", "append"])
|
|
148
|
+
.optional()
|
|
149
|
+
.default("replace")
|
|
150
|
+
.describe("'replace' (default) overwrites all existing annotations. 'append' adds new annotations while keeping existing ones."),
|
|
151
|
+
}, async ({ nodeId, annotations, mode = "replace" }) => {
|
|
152
|
+
try {
|
|
153
|
+
logger.info({ nodeId, count: annotations.length, mode }, "Setting annotations");
|
|
154
|
+
const connector = await getDesktopConnector();
|
|
155
|
+
const result = await connector.setAnnotations(nodeId, annotations, mode);
|
|
156
|
+
if (!result || (result.success === false)) {
|
|
157
|
+
throw new Error(result?.error || "Failed to set annotations");
|
|
158
|
+
}
|
|
159
|
+
const data = result.data || result;
|
|
160
|
+
return {
|
|
161
|
+
content: [
|
|
162
|
+
{
|
|
163
|
+
type: "text",
|
|
164
|
+
text: JSON.stringify({
|
|
165
|
+
success: true,
|
|
166
|
+
...data,
|
|
167
|
+
note: "Annotations set successfully. This operation is undoable in Figma (Cmd+Z).",
|
|
168
|
+
}),
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
175
|
+
logger.error({ error }, "Failed to set annotations");
|
|
176
|
+
return {
|
|
177
|
+
content: [
|
|
178
|
+
{
|
|
179
|
+
type: "text",
|
|
180
|
+
text: JSON.stringify({
|
|
181
|
+
error: "set_annotations_failed",
|
|
182
|
+
message: `Cannot set annotations. ${message}`,
|
|
183
|
+
hint: "Annotations require the Desktop Bridge plugin to be running in Figma.",
|
|
184
|
+
}),
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
isError: true,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
// -----------------------------------------------------------------------
|
|
192
|
+
// Tool: figma_get_annotation_categories
|
|
193
|
+
// -----------------------------------------------------------------------
|
|
194
|
+
server.tool("figma_get_annotation_categories", "List available annotation categories in the current Figma file. Categories group annotations by purpose (e.g., interactions, accessibility, development notes). Use the returned category IDs when creating annotations with figma_set_annotations. Requires Desktop Bridge plugin.", {}, async () => {
|
|
195
|
+
try {
|
|
196
|
+
logger.info("Getting annotation categories");
|
|
197
|
+
const connector = await getDesktopConnector();
|
|
198
|
+
const result = await connector.getAnnotationCategories();
|
|
199
|
+
if (!result || (result.success === false)) {
|
|
200
|
+
throw new Error(result?.error || "Failed to get annotation categories");
|
|
201
|
+
}
|
|
202
|
+
const data = result.data || result;
|
|
203
|
+
return {
|
|
204
|
+
content: [
|
|
205
|
+
{
|
|
206
|
+
type: "text",
|
|
207
|
+
text: JSON.stringify(data),
|
|
208
|
+
},
|
|
209
|
+
],
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
214
|
+
logger.error({ error }, "Failed to get annotation categories");
|
|
215
|
+
return {
|
|
216
|
+
content: [
|
|
217
|
+
{
|
|
218
|
+
type: "text",
|
|
219
|
+
text: JSON.stringify({
|
|
220
|
+
error: "get_annotation_categories_failed",
|
|
221
|
+
message: `Cannot get annotation categories. ${message}`,
|
|
222
|
+
hint: "Annotation categories require the Desktop Bridge plugin to be running in Figma.",
|
|
223
|
+
}),
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
isError: true,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=annotation-tools.js.map
|