@fprad0/skill-master-mcp 0.0.12 → 1.0.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/CHANGELOG.md +96 -90
- package/README.md +472 -472
- package/VERSION.md +9 -9
- package/bin/lib/bootstrap-global-core.mjs +34 -0
- package/bin/lib/client-config.mjs +293 -293
- package/bin/lib/doctor-core.mjs +202 -0
- package/bin/lib/menu-core.mjs +1629 -1522
- package/bin/lib/operation-result.mjs +59 -0
- package/bin/lib/register-clients-core.mjs +247 -0
- package/bin/lib/skill-installation.mjs +215 -215
- package/bin/lib/update-cli-core.mjs +117 -0
- package/bin/skill-master-activation.mjs +163 -163
- package/bin/skill-master-bootstrap-global.mjs +61 -49
- package/bin/skill-master-configure-private-registry.mjs +3 -3
- package/bin/skill-master-doctor.mjs +239 -228
- package/bin/skill-master-eval-activation.mjs +32 -32
- package/bin/skill-master-install-global-skills.mjs +59 -59
- package/bin/skill-master-install-project-skills.mjs +97 -97
- package/bin/skill-master-menu.mjs +406 -405
- package/bin/skill-master-register-clients.mjs +232 -153
- package/bin/skill-master-success-skills.mjs +307 -307
- package/bin/skill-master-update.mjs +121 -72
- package/bin/skill-master.mjs +3 -3
- package/dist/activation.d.ts.map +1 -1
- package/dist/activation.js +12 -0
- package/dist/activation.js.map +1 -1
- package/dist/prompt-router.d.ts.map +1 -1
- package/dist/prompt-router.js +19 -0
- package/dist/prompt-router.js.map +1 -1
- package/dist/recommender.d.ts.map +1 -1
- package/dist/recommender.js +4 -1
- package/dist/recommender.js.map +1 -1
- package/docs/architecture/APRENDIZADO_DE_IMPLEMENTACOES_BEM_SUCEDIDAS.md +125 -125
- package/docs/architecture/ARQUITETURA_AUTO_UPDATE.md +9 -9
- package/docs/architecture/PLANO_MASTER_ACIONAMENTO_AUTOMATICO_E_APRENDIZADO.md +341 -341
- package/docs/architecture/REDE_SEGURA_DE_SKILLS.md +148 -148
- package/docs/operations/GUIA_MULTI_COMPUTADOR.md +262 -262
- package/docs/operations/GUIA_NPM_PRIVADO.md +294 -294
- package/docs/operations/GUIA_NPM_PUBLICO.md +147 -147
- package/docs/operations/MENU_VISUAL_EVIDENCE_2026-06-28.md +66 -66
- package/docs/operations/assets/menu-frame-compact.html +75 -75
- package/docs/operations/assets/menu-frame-large.html +83 -83
- package/docs/operations/assets/menu-frame-running.html +79 -79
- package/docs/operations/cross-platform-auth-transfer/ANALISE_COMPATIBILIDADE_MCP_2026-06-28.md +140 -140
- package/docs/operations/cross-platform-auth-transfer/README_TRANSFERENCIA.md +85 -85
- package/docs/operations/reborn-menu-cyberpunk-transfer/ANALISE_MENU_REBORN_CYBERPUNK_2026-06-28.md +174 -174
- package/docs/operations/reborn-menu-cyberpunk-transfer/HANDOFF_IMPLEMENTACAO_REBORN_CYBERPUNK_2026-06-28.md +119 -119
- package/docs/operations/reborn-menu-cyberpunk-transfer/ORDEM_DE_EXECUCAO_MENU_REBORN_CYBERPUNK.md +134 -134
- package/docs/operations/reborn-menu-cyberpunk-transfer/README_TRANSFERENCIA.md +84 -84
- package/docs/operations/reborn-menu-cyberpunk-transfer/README_TRANSFERENCIA_REBORN_PACKAGE.md +56 -56
- package/docs/operations/token-economy-transfer/ANALISE_AVANCADA_ECONOMIA_TOKENS_2026-06-30.md +141 -0
- package/docs/operations/token-economy-transfer/PLANO_DEV_SENIOR_MASTER_TOKEN_ECONOMY_2026-06-30.md +171 -0
- package/docs/operations/token-economy-transfer/README_TRANSFERENCIA_TOKEN_ECONOMY.md +31 -0
- package/docs/planning/MENU_RUNTIME_CORRECTION_PLAN_2026-06-30.md +551 -0
- package/docs/planning/V0_0_9_APROVACAO_CRITICA_MENSAGENS_DE_VENDA.md +85 -85
- package/docs/planning/V0_0_9_FONTES_E_CRITERIOS_DE_AUTORIDADE.md +139 -139
- package/docs/planning/V0_0_9_MATRIZ_SKILLS_MULTIDISCIPLINARES.md +105 -105
- package/docs/planning/V0_0_9_POLITICA_MORAL_CATOLICA_PARA_IA.md +181 -181
- package/docs/planning/V0_0_9_PROMPTS_EXECUCAO.md +59 -59
- package/docs/planning/V0_0_9_ROADMAP_DISCERNIMENTO_E_CONHECIMENTO_AMPLO.md +181 -181
- package/docs/prompt-tasks/PROMPT_TASK_001_BOOTSTRAP_SKILL_MASTER_MCP.md +6 -6
- package/docs/prompt-tasks/PROMPT_TASK_002_AUTO_UPDATE_LAUNCHER.md +6 -6
- package/docs/prompt-tasks/PROMPT_TASK_003_REMOTE_MANIFEST_AND_RELEASES.md +6 -6
- package/docs/prompt-tasks/PROMPT_TASK_004_MULTI_USER_DISTRIBUTION.md +6 -6
- package/docs/prompt-tasks/PROMPT_TASK_005_SECURITY_AND_QUALITY_GATE.md +6 -6
- package/docs/prompt-tasks/PROMPT_TASK_006_MASTER_ACIONAMENTO_APRENDIZADO.md +83 -83
- package/docs/prompt-tasks/PROMPT_TASK_007_PERSONA_ORQUESTRADORA.md +88 -88
- package/docs/prompt-tasks/PROMPT_TASK_008_PROMPT_ROUTER_MODOS_ATIVACAO.md +156 -156
- package/docs/prompt-tasks/PROMPT_TASK_009_PIPELINE_APRENDIZADO_SUCESSO.md +105 -105
- package/docs/prompt-tasks/PROMPT_TASK_010_EVALS_GOVERNANCA_ATIVACAO.md +119 -119
- package/docs/prompt-tasks/PROMPT_TASK_011_MENU_NOTIFICACOES_NOTION.md +120 -120
- package/docs/prompt-tasks/PROMPT_TASK_012_MENU_CYBERPUNK_PIXEL_FRAME.md +123 -123
- package/docs/prompt-tasks/PROMPT_TASK_013_MENU_FLUID_DNA_ANIMATION.md +114 -114
- package/docs/prompt-tasks/PROMPT_TASK_014_MENU_FUNCTIONAL_PARITY_QA.md +157 -157
- package/docs/prompt-tasks/PROMPT_TASK_015_TRANSFER_RELEASE_HANDOFF.md +127 -127
- package/docs/prompt-tasks/PROMPT_TASK_016_CROSS_PLATFORM_MCP_AUTH_REGISTRATION.md +107 -107
- package/docs/prompt-tasks/PROMPT_TASK_018_NPM_PUBLISH_2FA_SETUP.md +80 -80
- package/docs/prompt-tasks/PROMPT_TASK_019_TOKEN_ECONOMY_GLOBAL_SKILLS.md +56 -0
- package/docs/prompt-tasks/PROMPT_TASK_MASTER_EXECUTOR.md +6 -6
- package/docs/skill-candidates/v0.0.10/cli-creator/LICENSE.txt +201 -201
- package/docs/skill-candidates/v0.0.10/cli-creator/SKILL.md +160 -160
- package/docs/skill-candidates/v0.0.10/cli-creator/agents/openai.yaml +4 -4
- package/docs/skill-candidates/v0.0.10/cli-creator/references/agent-cli-patterns.md +154 -154
- package/docs/skill-candidates/v0.0.10/developer-workstation-ops/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.10/figma/LICENSE.txt +1 -1
- package/docs/skill-candidates/v0.0.10/figma/SKILL.md +42 -42
- package/docs/skill-candidates/v0.0.10/figma/agents/openai.yaml +14 -14
- package/docs/skill-candidates/v0.0.10/figma/assets/figma-small.svg +3 -3
- package/docs/skill-candidates/v0.0.10/figma/assets/icon.svg +28 -28
- package/docs/skill-candidates/v0.0.10/figma/references/figma-mcp-config.md +35 -35
- package/docs/skill-candidates/v0.0.10/figma/references/figma-tools-and-prompts.md +34 -34
- package/docs/skill-candidates/v0.0.10/figma-code-connect-components/LICENSE.TXT +1 -1
- package/docs/skill-candidates/v0.0.10/figma-code-connect-components/SKILL.md +349 -349
- package/docs/skill-candidates/v0.0.10/figma-code-connect-components/agents/openai.yaml +14 -14
- package/docs/skill-candidates/v0.0.10/figma-code-connect-components/assets/figma-small.svg +3 -3
- package/docs/skill-candidates/v0.0.10/figma-code-connect-components/assets/icon.svg +28 -28
- package/docs/skill-candidates/v0.0.10/figma-code-connect-components/references/mapping-checklist.md +7 -7
- package/docs/skill-candidates/v0.0.10/figma-code-connect-components/scripts/normalize_node_id.py +25 -25
- package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/LICENSE.TXT +1 -1
- package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/SKILL.md +537 -537
- package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/agents/openai.yaml +14 -14
- package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/assets/figma-small.svg +3 -3
- package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/assets/icon.svg +28 -28
- package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/references/rule-template.md +15 -15
- package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/scripts/check_agents_md.sh +9 -9
- package/docs/skill-candidates/v0.0.10/figma-generate-design/LICENSE.TXT +1 -1
- package/docs/skill-candidates/v0.0.10/figma-generate-design/SKILL.md +341 -341
- package/docs/skill-candidates/v0.0.10/figma-generate-design/agents/openai.yaml +14 -14
- package/docs/skill-candidates/v0.0.10/figma-generate-design/assets/figma-small.svg +3 -3
- package/docs/skill-candidates/v0.0.10/figma-generate-design/assets/icon.svg +28 -28
- package/docs/skill-candidates/v0.0.10/figma-generate-design/maintainers.yml +1 -1
- package/docs/skill-candidates/v0.0.10/figma-generate-library/LICENSE.TXT +1 -1
- package/docs/skill-candidates/v0.0.10/figma-generate-library/SKILL.md +314 -314
- package/docs/skill-candidates/v0.0.10/figma-generate-library/agents/openai.yaml +14 -14
- package/docs/skill-candidates/v0.0.10/figma-generate-library/assets/figma-small.svg +3 -3
- package/docs/skill-candidates/v0.0.10/figma-generate-library/assets/icon.svg +28 -28
- package/docs/skill-candidates/v0.0.10/figma-generate-library/maintainers.yml +3 -3
- package/docs/skill-candidates/v0.0.10/figma-generate-library/references/code-connect-setup.md +260 -260
- package/docs/skill-candidates/v0.0.10/figma-generate-library/references/component-creation.md +1014 -1014
- package/docs/skill-candidates/v0.0.10/figma-generate-library/references/discovery-phase.md +518 -518
- package/docs/skill-candidates/v0.0.10/figma-generate-library/references/documentation-creation.md +834 -834
- package/docs/skill-candidates/v0.0.10/figma-generate-library/references/error-recovery.md +540 -540
- package/docs/skill-candidates/v0.0.10/figma-generate-library/references/naming-conventions.md +527 -527
- package/docs/skill-candidates/v0.0.10/figma-generate-library/references/token-creation.md +962 -962
- package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/bindVariablesToComponent.js +110 -110
- package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/cleanupOrphans.js +127 -127
- package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/createComponentWithVariants.js +148 -148
- package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/createDocumentationPage.js +139 -139
- package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/createSemanticTokens.js +108 -108
- package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/createVariableCollection.js +49 -49
- package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/inspectFileStructure.js +121 -121
- package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/rehydrateState.js +92 -92
- package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/validateCreation.js +83 -83
- package/docs/skill-candidates/v0.0.10/figma-implement-design/LICENSE.txt +1 -1
- package/docs/skill-candidates/v0.0.10/figma-implement-design/SKILL.md +258 -258
- package/docs/skill-candidates/v0.0.10/figma-implement-design/agents/openai.yaml +14 -14
- package/docs/skill-candidates/v0.0.10/figma-implement-design/assets/figma-small.svg +3 -3
- package/docs/skill-candidates/v0.0.10/figma-implement-design/assets/icon.svg +28 -28
- package/docs/skill-candidates/v0.0.10/figma-use/LICENSE.TXT +1 -1
- package/docs/skill-candidates/v0.0.10/figma-use/SKILL.md +233 -233
- package/docs/skill-candidates/v0.0.10/figma-use/agents/openai.yaml +14 -14
- package/docs/skill-candidates/v0.0.10/figma-use/assets/figma-small.svg +3 -3
- package/docs/skill-candidates/v0.0.10/figma-use/assets/icon.svg +28 -28
- package/docs/skill-candidates/v0.0.10/figma-use/maintainers.yml +1 -1
- package/docs/skill-candidates/v0.0.10/figma-use/references/api-reference.md +301 -301
- package/docs/skill-candidates/v0.0.10/figma-use/references/common-patterns.md +512 -512
- package/docs/skill-candidates/v0.0.10/figma-use/references/component-patterns.md +488 -488
- package/docs/skill-candidates/v0.0.10/figma-use/references/effect-style-patterns.md +123 -123
- package/docs/skill-candidates/v0.0.10/figma-use/references/gotchas.md +599 -599
- package/docs/skill-candidates/v0.0.10/figma-use/references/maintainers.yml +12 -12
- package/docs/skill-candidates/v0.0.10/figma-use/references/plugin-api-patterns.md +513 -513
- package/docs/skill-candidates/v0.0.10/figma-use/references/plugin-api-standalone.d.ts +11293 -11293
- package/docs/skill-candidates/v0.0.10/figma-use/references/plugin-api-standalone.index.md +441 -441
- package/docs/skill-candidates/v0.0.10/figma-use/references/text-style-patterns.md +203 -203
- package/docs/skill-candidates/v0.0.10/figma-use/references/validation-and-recovery.md +109 -109
- package/docs/skill-candidates/v0.0.10/figma-use/references/variable-patterns.md +354 -354
- package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/maintainers.yml +9 -9
- package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-components--creating.md +17 -17
- package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-components--using.md +17 -17
- package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-components.md +50 -50
- package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-effect-styles.md +52 -52
- package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-text-styles.md +90 -90
- package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-variables--creating.md +13 -13
- package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-variables--using.md +13 -13
- package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-variables.md +64 -64
- package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds.md +41 -41
- package/docs/skill-candidates/v0.0.10/frontend-design/LICENSE.txt +177 -177
- package/docs/skill-candidates/v0.0.10/frontend-design/SKILL.md +55 -55
- package/docs/skill-candidates/v0.0.10/frontend-ui-ux-systems/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.10/github/SKILL.md +74 -74
- package/docs/skill-candidates/v0.0.10/github/agents/openai.yaml +6 -6
- package/docs/skill-candidates/v0.0.10/github/assets/github-small.svg +3 -3
- package/docs/skill-candidates/v0.0.10/image-graphic-design-rendering/SKILL.md +28 -28
- package/docs/skill-candidates/v0.0.10/language-quality-pt-en-fr-it-ru/SKILL.md +28 -28
- package/docs/skill-candidates/v0.0.10/math-physics-reasoning/SKILL.md +28 -28
- package/docs/skill-candidates/v0.0.10/mcp-builder/LICENSE.txt +201 -201
- package/docs/skill-candidates/v0.0.10/mcp-builder/SKILL.md +236 -236
- package/docs/skill-candidates/v0.0.10/mcp-builder/reference/evaluation.md +601 -601
- package/docs/skill-candidates/v0.0.10/mcp-builder/reference/mcp_best_practices.md +249 -249
- package/docs/skill-candidates/v0.0.10/mcp-builder/reference/node_mcp_server.md +969 -969
- package/docs/skill-candidates/v0.0.10/mcp-builder/reference/python_mcp_server.md +718 -718
- package/docs/skill-candidates/v0.0.10/mcp-builder/scripts/connections.py +151 -151
- package/docs/skill-candidates/v0.0.10/mcp-builder/scripts/evaluation.py +373 -373
- package/docs/skill-candidates/v0.0.10/mcp-builder/scripts/example_evaluation.xml +22 -22
- package/docs/skill-candidates/v0.0.10/mcp-builder/scripts/requirements.txt +2 -2
- package/docs/skill-candidates/v0.0.10/mcp-client-readiness/SKILL.md +31 -31
- package/docs/skill-candidates/v0.0.10/openai-docs/LICENSE.txt +201 -201
- package/docs/skill-candidates/v0.0.10/openai-docs/SKILL.md +161 -161
- package/docs/skill-candidates/v0.0.10/openai-docs/agents/openai.yaml +14 -14
- package/docs/skill-candidates/v0.0.10/openai-docs/assets/openai-small.svg +3 -3
- package/docs/skill-candidates/v0.0.10/openai-docs/references/latest-model.md +37 -37
- package/docs/skill-candidates/v0.0.10/openai-docs/references/prompting-guide.md +244 -244
- package/docs/skill-candidates/v0.0.10/openai-docs/references/upgrade-guide.md +181 -181
- package/docs/skill-candidates/v0.0.10/openai-docs/scripts/fetch-codex-manual.mjs +598 -598
- package/docs/skill-candidates/v0.0.10/openai-docs/scripts/resolve-latest-model-info.js +147 -147
- package/docs/skill-candidates/v0.0.10/playwright/NOTICE.txt +14 -14
- package/docs/skill-candidates/v0.0.10/playwright/SKILL.md +147 -147
- package/docs/skill-candidates/v0.0.10/playwright/agents/openai.yaml +6 -6
- package/docs/skill-candidates/v0.0.10/playwright/assets/playwright-small.svg +3 -3
- package/docs/skill-candidates/v0.0.10/playwright/references/cli.md +116 -116
- package/docs/skill-candidates/v0.0.10/playwright/references/workflows.md +95 -95
- package/docs/skill-candidates/v0.0.10/playwright/scripts/playwright_cli.sh +25 -25
- package/docs/skill-candidates/v0.0.10/polyglot-backend-engineering/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.10/screenshot/LICENSE.txt +201 -201
- package/docs/skill-candidates/v0.0.10/screenshot/SKILL.md +267 -267
- package/docs/skill-candidates/v0.0.10/screenshot/agents/openai.yaml +6 -6
- package/docs/skill-candidates/v0.0.10/screenshot/assets/screenshot-small.svg +5 -5
- package/docs/skill-candidates/v0.0.10/screenshot/scripts/ensure_macos_permissions.sh +54 -54
- package/docs/skill-candidates/v0.0.10/screenshot/scripts/macos_display_info.swift +22 -22
- package/docs/skill-candidates/v0.0.10/screenshot/scripts/macos_permissions.swift +40 -40
- package/docs/skill-candidates/v0.0.10/screenshot/scripts/macos_window_info.swift +126 -126
- package/docs/skill-candidates/v0.0.10/screenshot/scripts/take_screenshot.ps1 +163 -163
- package/docs/skill-candidates/v0.0.10/screenshot/scripts/take_screenshot.py +585 -585
- package/docs/skill-candidates/v0.0.10/skill-master-orchestrator/SKILL.md +62 -62
- package/docs/skill-candidates/v0.0.10/skill-master-orchestrator/agents/openai.yaml +4 -4
- package/docs/skill-candidates/v0.0.10/skill-master-orchestrator/references/activation-policy.md +77 -77
- package/docs/skill-candidates/v0.0.10/skill-master-orchestrator/references/human-approval-policy.md +83 -83
- package/docs/skill-candidates/v0.0.10/skill-master-orchestrator/references/persona-dev-senior-master.md +46 -46
- package/docs/skill-candidates/v0.0.10/terminal-menu-operations/SKILL.md +30 -30
- package/docs/skill-candidates/v0.0.10/terminal-pixel-art-tui/SKILL.md +43 -43
- package/docs/skill-candidates/v0.0.10/webapp-testing/LICENSE.txt +201 -201
- package/docs/skill-candidates/v0.0.10/webapp-testing/SKILL.md +95 -95
- package/docs/skill-candidates/v0.0.10/webapp-testing/examples/console_logging.py +34 -34
- package/docs/skill-candidates/v0.0.10/webapp-testing/examples/element_discovery.py +39 -39
- package/docs/skill-candidates/v0.0.10/webapp-testing/examples/static_html_automation.py +32 -32
- package/docs/skill-candidates/v0.0.10/webapp-testing/scripts/with_server.py +105 -105
- package/docs/skill-candidates/v0.0.10/winui-app/LICENSE.txt +201 -201
- package/docs/skill-candidates/v0.0.10/winui-app/SKILL.md +94 -94
- package/docs/skill-candidates/v0.0.10/winui-app/agents/openai.yaml +5 -5
- package/docs/skill-candidates/v0.0.10/winui-app/config.yaml +50 -50
- package/docs/skill-candidates/v0.0.10/winui-app/references/_sections.md +96 -96
- package/docs/skill-candidates/v0.0.10/winui-app/references/accessibility-input-and-localization.md +51 -51
- package/docs/skill-candidates/v0.0.10/winui-app/references/build-run-and-launch-verification.md +72 -72
- package/docs/skill-candidates/v0.0.10/winui-app/references/community-toolkit-controls-and-helpers.md +57 -57
- package/docs/skill-candidates/v0.0.10/winui-app/references/controls-layout-and-adaptive-ui.md +84 -84
- package/docs/skill-candidates/v0.0.10/winui-app/references/foundation-environment-audit-and-remediation.md +82 -82
- package/docs/skill-candidates/v0.0.10/winui-app/references/foundation-setup-and-project-selection.md +67 -67
- package/docs/skill-candidates/v0.0.10/winui-app/references/foundation-template-first-recovery.md +62 -62
- package/docs/skill-candidates/v0.0.10/winui-app/references/foundation-winui-app-structure.md +62 -62
- package/docs/skill-candidates/v0.0.10/winui-app/references/motion-animations-and-polish.md +45 -45
- package/docs/skill-candidates/v0.0.10/winui-app/references/performance-diagnostics-and-responsiveness.md +46 -46
- package/docs/skill-candidates/v0.0.10/winui-app/references/sample-source-map.md +37 -37
- package/docs/skill-candidates/v0.0.10/winui-app/references/shell-navigation-and-windowing.md +67 -67
- package/docs/skill-candidates/v0.0.10/winui-app/references/styling-theming-materials-and-icons.md +71 -71
- package/docs/skill-candidates/v0.0.10/winui-app/references/testing-debugging-and-review-checklists.md +77 -77
- package/docs/skill-candidates/v0.0.10/winui-app/references/windows-app-sdk-lifecycle-notifications-and-deployment.md +52 -52
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/SKILL.md +398 -398
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/common-patterns.md +330 -330
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/complete-examples.md +871 -871
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/component-patterns.md +501 -501
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/data-fetching.md +766 -766
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/file-organization.md +501 -501
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/loading-and-error-states.md +500 -500
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/performance.md +405 -405
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/routing-guide.md +363 -363
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/styling-guide.md +427 -427
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/typescript-standards.md +417 -417
- package/docs/skill-candidates/v0.0.11/git-version-control-ops/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/go-engineering/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/java-engineering/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/javascript-engineering/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/json-contract-design/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/multi-client-mcp-ops/SKILL.md +36 -36
- package/docs/skill-candidates/v0.0.11/nextjs/SKILL.md +745 -745
- package/docs/skill-candidates/v0.0.11/nextjs/agents/openai.yaml +3 -3
- package/docs/skill-candidates/v0.0.11/nextjs/references/app-router-files.md +94 -94
- package/docs/skill-candidates/v0.0.11/python-engineering/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/ruby-engineering/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/senior-fullstack/SKILL.md +209 -209
- package/docs/skill-candidates/v0.0.11/senior-fullstack/references/architecture_patterns.md +103 -103
- package/docs/skill-candidates/v0.0.11/senior-fullstack/references/development_workflows.md +103 -103
- package/docs/skill-candidates/v0.0.11/senior-fullstack/references/tech_stack_guide.md +103 -103
- package/docs/skill-candidates/v0.0.11/senior-fullstack/scripts/code_quality_analyzer.py +114 -114
- package/docs/skill-candidates/v0.0.11/senior-fullstack/scripts/fullstack_scaffolder.py +114 -114
- package/docs/skill-candidates/v0.0.11/senior-fullstack/scripts/project_scaffolder.py +114 -114
- package/docs/skill-candidates/v0.0.11/shadcn/SKILL.md +573 -573
- package/docs/skill-candidates/v0.0.11/shadcn/agents/openai.yaml +3 -3
- package/docs/skill-candidates/v0.0.11/sql-postgresql-engineering/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/terminal-shell-ops/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/typescript-expert/SKILL.md +429 -429
- package/docs/skill-candidates/v0.0.11/typescript-expert/references/tsconfig-strict.json +91 -91
- package/docs/skill-candidates/v0.0.11/typescript-expert/references/typescript-cheatsheet.md +383 -383
- package/docs/skill-candidates/v0.0.11/typescript-expert/references/utility-types.ts +335 -335
- package/docs/skill-candidates/v0.0.11/typescript-expert/scripts/ts_diagnostic.py +203 -203
- package/docs/skill-candidates/v0.0.11/ui-component-primitives/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/web-mobile-design-systems/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.11/windows-linux-platform-ops/SKILL.md +34 -34
- package/docs/skill-candidates/v0.0.12/context-compression-handoff/SKILL.md +47 -0
- package/docs/skill-candidates/v0.0.12/csharp-senior-master-engineering/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.12/css-senior-master-engineering/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.12/go-senior-master-engineering/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.12/html-senior-master-engineering/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.12/javascript-senior-master-engineering/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.12/json-senior-master-engineering/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.12/prompt-budget-gate/SKILL.md +46 -0
- package/docs/skill-candidates/v0.0.12/python-senior-master-engineering/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.12/react-senior-master-engineering/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.12/ruby-senior-master-engineering/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.12/senior-master-code-optimizer/SKILL.md +48 -48
- package/docs/skill-candidates/v0.0.12/sql-senior-master-engineering/SKILL.md +31 -31
- package/docs/skill-candidates/v0.0.12/token-economy-orchestrator/SKILL.md +38 -0
- package/docs/skill-candidates/v0.0.12/typescript-senior-master-engineering/SKILL.md +35 -35
- package/docs/skill-candidates/v0.0.9/ai-ethics-human-dignity/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.9/broad-domain-router/SKILL.md +41 -41
- package/docs/skill-candidates/v0.0.9/catholic-moral-discernment/SKILL.md +31 -31
- package/docs/skill-candidates/v0.0.9/engineering-systems-master/SKILL.md +31 -31
- package/docs/skill-candidates/v0.0.9/language-quality-pt-en-fr/SKILL.md +28 -28
- package/docs/skill-candidates/v0.0.9/math-science-reasoning/SKILL.md +29 -29
- package/docs/skill-candidates/v0.0.9/philosophy-sociology-discernment/SKILL.md +28 -28
- package/docs/skill-candidates/v0.0.9/professional-boundary-triage/SKILL.md +40 -40
- package/docs/skill-candidates/v0.0.9/release-ethics-gate/SKILL.md +32 -32
- package/docs/skill-candidates/v0.0.9/source-authority-reviewer/SKILL.md +31 -31
- package/examples/client-configs/claude-code.commands.md +21 -21
- package/examples/client-configs/claude-code.project.mcp.json +18 -18
- package/examples/client-configs/claude-desktop.macos.json +18 -18
- package/examples/client-configs/claude-desktop.windows.json +20 -20
- package/examples/client-configs/codex.windows.toml +11 -11
- package/examples/client-configs/gemini-code-assist.intellij.mcp.json +18 -18
- package/examples/client-configs/gemini.linux.settings.json +21 -21
- package/examples/client-configs/gemini.windows.settings.json +23 -23
- package/examples/client-configs/generic-stdio.json +16 -16
- package/manifests/channels/beta.json +26 -26
- package/manifests/channels/stable.json +27 -27
- package/network/approved-skills.json +54 -54
- package/network/unapproved-skill-candidates.json +110 -110
- package/package.json +87 -86
- package/scripts/configure-private-registry.mjs +208 -208
- package/scripts/lib/private-registry.mjs +97 -97
- package/scripts/render-menu-evidence.mjs +130 -130
- package/scripts/verify-menu-actions.mjs +117 -117
- package/sources.json +11 -11
|
@@ -1,488 +1,488 @@
|
|
|
1
|
-
# Component & Variant API Patterns
|
|
2
|
-
|
|
3
|
-
> Part of the [use_figma skill](../SKILL.md). How to correctly use the Plugin API for components, variants, and component properties.
|
|
4
|
-
>
|
|
5
|
-
> For design system context (when to use variants vs properties, code-to-Figma translation, property model), see [wwds-components](working-with-design-systems/wwds-components.md).
|
|
6
|
-
|
|
7
|
-
## Contents
|
|
8
|
-
|
|
9
|
-
- Creating a Component
|
|
10
|
-
- Combining Components into a Component Set (Variants)
|
|
11
|
-
- Laying Out Variants After combineAsVariants (Required)
|
|
12
|
-
- Component Properties: addComponentProperty API
|
|
13
|
-
- Linking Properties to Child Nodes (Required)
|
|
14
|
-
- INSTANCE_SWAP: Avoiding Variant Explosion
|
|
15
|
-
- Discovering Existing Conventions in the File
|
|
16
|
-
- Importing Components by Key
|
|
17
|
-
- Working with Instances (finding variants, setProperties, text overrides, detachInstance)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
## Creating a Component
|
|
21
|
-
|
|
22
|
-
`figma.createComponent()` returns a `ComponentNode`, which behaves like a `FrameNode` but can be published, instanced, and combined into variant sets.
|
|
23
|
-
|
|
24
|
-
```javascript
|
|
25
|
-
const comp = figma.createComponent();
|
|
26
|
-
comp.name = "MyComponent";
|
|
27
|
-
comp.layoutMode = "HORIZONTAL";
|
|
28
|
-
comp.primaryAxisAlignItems = "CENTER";
|
|
29
|
-
comp.counterAxisAlignItems = "CENTER";
|
|
30
|
-
comp.paddingLeft = 12;
|
|
31
|
-
comp.paddingRight = 12;
|
|
32
|
-
comp.layoutSizingHorizontal = "HUG";
|
|
33
|
-
comp.layoutSizingVertical = "HUG";
|
|
34
|
-
comp.fills = [{ type: "SOLID", color: { r: 0.2, g: 0.36, b: 0.96 } }];
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Combining Components into a Component Set (Variants)
|
|
38
|
-
|
|
39
|
-
`figma.combineAsVariants(components, parent)` takes an array of `ComponentNode`s (not frames — frames will throw) and groups them into a `ComponentSetNode`.
|
|
40
|
-
|
|
41
|
-
Variant names use a `Property=Value` format. Every unique combination must exist as a child component — missing ones show as blank gaps in the variant picker.
|
|
42
|
-
|
|
43
|
-
```javascript
|
|
44
|
-
// Each component's name encodes its variant properties
|
|
45
|
-
const comp1 = figma.createComponent();
|
|
46
|
-
comp1.name = "size=md, style=primary";
|
|
47
|
-
const comp2 = figma.createComponent();
|
|
48
|
-
comp2.name = "size=md, style=secondary";
|
|
49
|
-
|
|
50
|
-
const componentSet = figma.combineAsVariants([comp1, comp2], figma.currentPage);
|
|
51
|
-
componentSet.name = "Button";
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
**Before creating variants, inspect the file** for existing naming patterns. Different files use different conventions (`State=Default` vs `state=default` vs `State/Default`). Always match what's already there.
|
|
55
|
-
|
|
56
|
-
## Laying Out Variants After combineAsVariants (Required)
|
|
57
|
-
|
|
58
|
-
After `combineAsVariants`, all children stack at `(0, 0)`. You **must** position them or the component set will appear as a single collapsed element with all variants overlapping.
|
|
59
|
-
|
|
60
|
-
```javascript
|
|
61
|
-
const cs = figma.combineAsVariants(components, figma.currentPage);
|
|
62
|
-
|
|
63
|
-
// Simple row layout
|
|
64
|
-
cs.children.forEach((child, i) => {
|
|
65
|
-
child.x = i * 150;
|
|
66
|
-
child.y = 0;
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
// CRITICAL: resize the component set from actual child bounds
|
|
70
|
-
let maxX = 0, maxY = 0;
|
|
71
|
-
for (const child of cs.children) {
|
|
72
|
-
maxX = Math.max(maxX, child.x + child.width);
|
|
73
|
-
maxY = Math.max(maxY, child.y + child.height);
|
|
74
|
-
}
|
|
75
|
-
cs.resizeWithoutConstraints(maxX + 40, maxY + 40);
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
For multi-axis variants (e.g., size × style × state), parse the child's name to determine grid position:
|
|
79
|
-
|
|
80
|
-
```javascript
|
|
81
|
-
for (const child of cs.children) {
|
|
82
|
-
const props = Object.fromEntries(
|
|
83
|
-
child.name.split(', ').map(p => p.split('='))
|
|
84
|
-
);
|
|
85
|
-
const col = stateValues.indexOf(props.state);
|
|
86
|
-
const row = styleValues.indexOf(props.style);
|
|
87
|
-
child.x = col * colWidth;
|
|
88
|
-
child.y = row * rowHeight;
|
|
89
|
-
}
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
## Component Properties: addComponentProperty API
|
|
93
|
-
|
|
94
|
-
`addComponentProperty` adds a TEXT, BOOLEAN, or INSTANCE_SWAP property to a component. It returns a **string key** (e.g., `"label#4:0"`) — never hardcode or guess this key.
|
|
95
|
-
|
|
96
|
-
```javascript
|
|
97
|
-
// Returns the key as a string — capture it!
|
|
98
|
-
const labelKey = comp.addComponentProperty('Label', 'TEXT', 'Default text');
|
|
99
|
-
const showIconKey = comp.addComponentProperty('Show Icon', 'BOOLEAN', true);
|
|
100
|
-
const iconSlotKey = comp.addComponentProperty('Icon', 'INSTANCE_SWAP', iconComponentId);
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
**Timing**: Add component properties to each variant component **before** calling `combineAsVariants`. After combining, the component set inherits all properties from its children. Do not add properties to the `ComponentSetNode` directly.
|
|
104
|
-
|
|
105
|
-
## Linking Properties to Child Nodes (Required)
|
|
106
|
-
|
|
107
|
-
A property that is added but not linked to a child node does **nothing**. You must set `componentPropertyReferences` on the child:
|
|
108
|
-
|
|
109
|
-
```javascript
|
|
110
|
-
// TEXT property → link to a text node's characters
|
|
111
|
-
const labelKey = comp.addComponentProperty('Label', 'TEXT', 'Button');
|
|
112
|
-
const textNode = figma.createText();
|
|
113
|
-
textNode.characters = "Button";
|
|
114
|
-
comp.appendChild(textNode);
|
|
115
|
-
textNode.componentPropertyReferences = { characters: labelKey };
|
|
116
|
-
|
|
117
|
-
// BOOLEAN + INSTANCE_SWAP → link to an instance node
|
|
118
|
-
const showIconKey = comp.addComponentProperty('Show Icon', 'BOOLEAN', true);
|
|
119
|
-
const iconSlotKey = comp.addComponentProperty('Icon', 'INSTANCE_SWAP', iconComp.id);
|
|
120
|
-
const iconInstance = iconComp.createInstance();
|
|
121
|
-
comp.appendChild(iconInstance);
|
|
122
|
-
iconInstance.componentPropertyReferences = {
|
|
123
|
-
visible: showIconKey, // BOOLEAN controls show/hide
|
|
124
|
-
mainComponent: iconSlotKey // INSTANCE_SWAP controls which component
|
|
125
|
-
};
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
**Valid `componentPropertyReferences` keys:**
|
|
129
|
-
- `characters` — TEXT property on a TextNode
|
|
130
|
-
- `visible` — BOOLEAN property (any node)
|
|
131
|
-
- `mainComponent` — INSTANCE_SWAP property on an InstanceNode
|
|
132
|
-
|
|
133
|
-
## INSTANCE_SWAP: Avoiding Variant Explosion
|
|
134
|
-
|
|
135
|
-
When a component has many possible sub-elements (e.g., 30 different icons), **never** create a variant per sub-element. Use a single INSTANCE_SWAP property instead — the user picks from any compatible component at design time.
|
|
136
|
-
|
|
137
|
-
```javascript
|
|
138
|
-
// Create icon as its own ComponentNode
|
|
139
|
-
const iconComp = figma.createComponent();
|
|
140
|
-
iconComp.name = "Icon/Search";
|
|
141
|
-
iconComp.resize(24, 24);
|
|
142
|
-
const svgNode = figma.createNodeFromSvg('<svg>...</svg>');
|
|
143
|
-
iconComp.appendChild(svgNode);
|
|
144
|
-
|
|
145
|
-
// Use it as the default for INSTANCE_SWAP
|
|
146
|
-
const iconSlotKey = comp.addComponentProperty('Icon', 'INSTANCE_SWAP', iconComp.id);
|
|
147
|
-
const instance = iconComp.createInstance();
|
|
148
|
-
comp.appendChild(instance);
|
|
149
|
-
instance.componentPropertyReferences = { mainComponent: iconSlotKey };
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
This works for icons, avatars, badges, or any swappable nested element.
|
|
153
|
-
|
|
154
|
-
## Discovering Existing Conventions in the File
|
|
155
|
-
|
|
156
|
-
**Always inspect the file before creating components.** Different files have different naming styles, structures, and conventions. Your code should match what's already there.
|
|
157
|
-
|
|
158
|
-
### List all existing components across all pages
|
|
159
|
-
|
|
160
|
-
```javascript
|
|
161
|
-
(async () => {
|
|
162
|
-
try {
|
|
163
|
-
const results = [];
|
|
164
|
-
for (const page of figma.root.children) {
|
|
165
|
-
await figma.setCurrentPageAsync(page);
|
|
166
|
-
page.findAll(n => {
|
|
167
|
-
if (n.type === 'COMPONENT') results.push(`[${page.name}] ${n.name} (COMPONENT) id=${n.id}`);
|
|
168
|
-
if (n.type === 'COMPONENT_SET') results.push(`[${page.name}] ${n.name} (COMPONENT_SET) id=${n.id}`);
|
|
169
|
-
return false;
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
figma.closePlugin(results.join('\n'));
|
|
173
|
-
} catch(e) { figma.closePluginWithFailure(e.toString()); }
|
|
174
|
-
})()
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
### Inspect an existing component set's variant naming pattern
|
|
178
|
-
|
|
179
|
-
```javascript
|
|
180
|
-
(async () => {
|
|
181
|
-
try {
|
|
182
|
-
const cs = await figma.getNodeByIdAsync('COMPONENT_SET_ID');
|
|
183
|
-
const variantNames = cs.children.map(c => c.name);
|
|
184
|
-
const propDefs = cs.componentPropertyDefinitions;
|
|
185
|
-
figma.closePlugin(JSON.stringify({ variantNames, propDefs }));
|
|
186
|
-
} catch(e) { figma.closePluginWithFailure(e.toString()); }
|
|
187
|
-
})()
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
### Find existing components in the file
|
|
191
|
-
|
|
192
|
-
```javascript
|
|
193
|
-
(async () => {
|
|
194
|
-
try {
|
|
195
|
-
const components = [];
|
|
196
|
-
for (const page of figma.root.children) {
|
|
197
|
-
await figma.setCurrentPageAsync(page);
|
|
198
|
-
page.findAll(n => {
|
|
199
|
-
if (n.type === 'COMPONENT') {
|
|
200
|
-
components.push({ name: n.name, id: n.id, page: page.name, w: n.width, h: n.height });
|
|
201
|
-
}
|
|
202
|
-
return false;
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
figma.closePlugin(JSON.stringify(components));
|
|
206
|
-
} catch(e) { figma.closePluginWithFailure(e.toString()); }
|
|
207
|
-
})()
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
## Importing Components by Key (Team Libraries)
|
|
211
|
-
|
|
212
|
-
`importComponentByKeyAsync` and `importComponentSetByKeyAsync` import components from **team libraries** (not the same file you're working in). For components in the current file, use `figma.getNodeByIdAsync()` or `findOne()`/`findAll()` to locate them directly.
|
|
213
|
-
|
|
214
|
-
```javascript
|
|
215
|
-
// Import a component from a team library
|
|
216
|
-
const comp = await figma.importComponentByKeyAsync("COMPONENT_KEY");
|
|
217
|
-
const instance = comp.createInstance();
|
|
218
|
-
|
|
219
|
-
// Import a component set from a team library and pick a variant
|
|
220
|
-
const set = await figma.importComponentSetByKeyAsync("COMPONENT_SET_KEY");
|
|
221
|
-
const variant = set.children.find(c =>
|
|
222
|
-
c.type === "COMPONENT" && c.name.includes("size=md")
|
|
223
|
-
) || set.defaultVariant;
|
|
224
|
-
const variantInstance = variant.createInstance();
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
## Working with Instances
|
|
228
|
-
|
|
229
|
-
### Finding the right variant in a component set
|
|
230
|
-
|
|
231
|
-
Parse variant names to match on multiple properties simultaneously:
|
|
232
|
-
|
|
233
|
-
```javascript
|
|
234
|
-
const compSet = await figma.importComponentSetByKeyAsync("KEY");
|
|
235
|
-
|
|
236
|
-
const variant = compSet.children.find(c => {
|
|
237
|
-
const props = Object.fromEntries(
|
|
238
|
-
c.name.split(', ').map(p => p.split('='))
|
|
239
|
-
);
|
|
240
|
-
return props.variant === "primary" && props.size === "md";
|
|
241
|
-
}) || compSet.defaultVariant;
|
|
242
|
-
|
|
243
|
-
const instance = variant.createInstance();
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
### Setting variant properties on an instance
|
|
247
|
-
|
|
248
|
-
After creating an instance from a component set, you can set variant properties via `setProperties`:
|
|
249
|
-
|
|
250
|
-
```javascript
|
|
251
|
-
const instance = defaultVariant.createInstance();
|
|
252
|
-
instance.setProperties({
|
|
253
|
-
"variant": "primary",
|
|
254
|
-
"size": "medium"
|
|
255
|
-
});
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
### Overriding text in a component instance
|
|
259
|
-
|
|
260
|
-
**Always discover component properties BEFORE writing text overrides.** Components expose text as `TEXT`-type component properties, and `setProperties()` is the correct way to override them. Direct `node.characters` changes on property-managed text may be overridden by the component property system on render.
|
|
261
|
-
|
|
262
|
-
**Step 1: Inspect componentProperties on a sample instance:**
|
|
263
|
-
|
|
264
|
-
```javascript
|
|
265
|
-
const instance = comp.createInstance();
|
|
266
|
-
const propDefs = instance.componentProperties;
|
|
267
|
-
// Returns e.g.: { "Label#2:0": { type: "TEXT", value: "Button" }, "Has Icon#4:64": { type: "BOOLEAN", value: true } }
|
|
268
|
-
figma.closePlugin(JSON.stringify(propDefs));
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
Also check nested instances — a parent component may not expose text properties directly, but its nested child instances might:
|
|
272
|
-
|
|
273
|
-
```javascript
|
|
274
|
-
const nestedInstances = instance.findAll(n => n.type === "INSTANCE");
|
|
275
|
-
const nestedProps = nestedInstances.map(ni => ({
|
|
276
|
-
name: ni.name,
|
|
277
|
-
id: ni.id,
|
|
278
|
-
properties: ni.componentProperties
|
|
279
|
-
}));
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
**Step 2: Use setProperties() for TEXT-type properties:**
|
|
283
|
-
|
|
284
|
-
```javascript
|
|
285
|
-
const instance = comp.createInstance();
|
|
286
|
-
const propDefs = instance.componentProperties;
|
|
287
|
-
for (const [key, def] of Object.entries(propDefs)) {
|
|
288
|
-
if (def.type === "TEXT") {
|
|
289
|
-
instance.setProperties({ [key]: "New text value" });
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
For nested instances that expose their own TEXT properties, call `setProperties()` on the nested instance:
|
|
295
|
-
|
|
296
|
-
```javascript
|
|
297
|
-
const nestedHeading = instance.findOne(n => n.type === "INSTANCE" && n.name === "Text Heading");
|
|
298
|
-
if (nestedHeading) {
|
|
299
|
-
nestedHeading.setProperties({ "Text#2104:5": "Actual heading text" });
|
|
300
|
-
}
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
**Step 3: Only fall back to direct node.characters for unmanaged text.** If text is NOT controlled by any component property, find text nodes directly. **Always load the node's actual font first** — instance text nodes inherit fonts from the source component, so don't assume Inter Regular:
|
|
304
|
-
|
|
305
|
-
```javascript
|
|
306
|
-
const textNodes = instance.findAll(n => n.type === "TEXT");
|
|
307
|
-
for (const t of textNodes) {
|
|
308
|
-
await figma.loadFontAsync(t.fontName);
|
|
309
|
-
t.characters = "Updated text";
|
|
310
|
-
}
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
### detachInstance() invalidates ancestor node IDs
|
|
314
|
-
|
|
315
|
-
**Warning:** When `detachInstance()` is called on a nested instance inside a library component instance, the parent instance may also get implicitly detached (converted from INSTANCE to FRAME with a **new ID**). Subsequent `getNodeByIdAsync(oldParentId)` returns null.
|
|
316
|
-
|
|
317
|
-
```javascript
|
|
318
|
-
// WRONG — cached parent ID becomes invalid after child detach
|
|
319
|
-
const parentId = parentInstance.id;
|
|
320
|
-
nestedChild.detachInstance();
|
|
321
|
-
const parent = await figma.getNodeByIdAsync(parentId); // null!
|
|
322
|
-
|
|
323
|
-
// CORRECT — re-discover nodes by traversal from a stable (non-instance) parent
|
|
324
|
-
const stableFrame = await figma.getNodeByIdAsync(manualFrameId); // a frame YOU created
|
|
325
|
-
nestedChild.detachInstance();
|
|
326
|
-
// Re-find the parent by traversing from the stable frame
|
|
327
|
-
const parent = stableFrame.findOne(n => n.name === "ParentName");
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
If you must detach multiple nested instances across sibling components, do it in a **single** `use_figma` call — discover all targets by traversal at the start before any detachment mutates the tree.
|
|
331
|
-
|
|
332
|
-
## Inspecting Component Metadata (Deep Traversal)
|
|
333
|
-
|
|
334
|
-
These helpers extract the full property schema and descendant structure of a component. Useful for understanding complex components before creating instances or setting properties.
|
|
335
|
-
|
|
336
|
-
```javascript
|
|
337
|
-
/**
|
|
338
|
-
* Imports a component or component set from a library by its published key.
|
|
339
|
-
* Tries COMPONENT first, then falls back to COMPONENT_SET.
|
|
340
|
-
*
|
|
341
|
-
* @param {string} componentKey - The published key of the component or component set.
|
|
342
|
-
* @returns {Promise<ComponentNode|ComponentSetNode>}
|
|
343
|
-
*/
|
|
344
|
-
async function importComponentByKey(componentKey) {
|
|
345
|
-
try {
|
|
346
|
-
return await figma.importComponentByKeyAsync(componentKey);
|
|
347
|
-
} catch {
|
|
348
|
-
try {
|
|
349
|
-
return await figma.importComponentSetByKeyAsync(componentKey);
|
|
350
|
-
} catch {
|
|
351
|
-
throw new Error(`No Component or Component Set available with key '${componentKey}'`);
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
/**
|
|
357
|
-
* Given a main component node, returns the component set parent if one exists,
|
|
358
|
-
* otherwise returns the component itself. Used to get the top-level node that
|
|
359
|
-
* holds `componentPropertyDefinitions`.
|
|
360
|
-
*
|
|
361
|
-
* @param {ComponentNode} mainComponent
|
|
362
|
-
* @returns {ComponentNode|ComponentSetNode}
|
|
363
|
-
*/
|
|
364
|
-
function getRelevantComponentNode(mainComponent) {
|
|
365
|
-
return mainComponent.parent.type === "COMPONENT_SET"
|
|
366
|
-
? mainComponent.parent
|
|
367
|
-
: mainComponent;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
/**
|
|
371
|
-
* Extracts `componentPropertyDefinitions` from a component or component set node
|
|
372
|
-
* into a flat map keyed by property key.
|
|
373
|
-
*
|
|
374
|
-
* @param {ComponentNode|ComponentSetNode} node
|
|
375
|
-
* @returns {Record<string, {name: string, type: string, key: string, variantOptions?: string[]}>}
|
|
376
|
-
*/
|
|
377
|
-
function getComponentProps(node) {
|
|
378
|
-
const result = {};
|
|
379
|
-
for (let key in node.componentPropertyDefinitions) {
|
|
380
|
-
const prop = {
|
|
381
|
-
name: key.replace(/#[^#]+$/, ""),
|
|
382
|
-
type: node.componentPropertyDefinitions[key].type,
|
|
383
|
-
key: key
|
|
384
|
-
};
|
|
385
|
-
if (prop.type === "VARIANT") {
|
|
386
|
-
prop.variantOptions = node.componentPropertyDefinitions[key].variantOptions;
|
|
387
|
-
}
|
|
388
|
-
result[key] = prop;
|
|
389
|
-
}
|
|
390
|
-
return result;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Recursively walks a component tree and collects all INSTANCE and TEXT nodes
|
|
395
|
-
* into `result`, keyed by `TYPE[name]`. Handles variant namespacing and
|
|
396
|
-
* deduplicates nodes with identical names but differing property references.
|
|
397
|
-
*
|
|
398
|
-
* @param {SceneNode} node - The node to traverse.
|
|
399
|
-
* @param {string[]} namespace - Accumulated variant names for the current path.
|
|
400
|
-
* @param {Record<string, object>} result - Accumulator object populated in place.
|
|
401
|
-
*/
|
|
402
|
-
function collectDescendants(node, namespace, result) {
|
|
403
|
-
if (node.type === "INSTANCE" || node.type === "TEXT") {
|
|
404
|
-
const references = node.componentPropertyReferences || {};
|
|
405
|
-
if (!node.visible && !references.visible) return;
|
|
406
|
-
|
|
407
|
-
const object = { type: node.type, name: node.name, references };
|
|
408
|
-
let key = `${node.type}[${node.name}]`;
|
|
409
|
-
|
|
410
|
-
if (result[key] && JSON.stringify(references) !== JSON.stringify(result[key].references)) {
|
|
411
|
-
key += btoa(btoa(unescape(encodeURIComponent(JSON.stringify(references)))));
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
if (node.type === "INSTANCE") {
|
|
415
|
-
const mainComponent = getRelevantComponentNode(node.mainComponent);
|
|
416
|
-
object.properties = getComponentProps(mainComponent);
|
|
417
|
-
object.descendants = {};
|
|
418
|
-
object.mainComponentName = mainComponent.name;
|
|
419
|
-
collectDescendants(mainComponent, [], object.descendants);
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
const start = namespace.length ? { variants: [] } : {};
|
|
423
|
-
result[key] = Object.assign(object, result[key] || start);
|
|
424
|
-
if (namespace.length) result[key].variants.push(namespace[namespace.length - 1]);
|
|
425
|
-
} else if ("children" in node && node.visible) {
|
|
426
|
-
if (node.type === "COMPONENT" && node.parent.type === "COMPONENT_SET") namespace.push(node.name);
|
|
427
|
-
node.children.forEach(child => collectDescendants(child, namespace, result));
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
/**
|
|
432
|
-
* Returns structured metadata for a component or component set defined in the current file.
|
|
433
|
-
*
|
|
434
|
-
* @param {string} componentId - The node ID of a COMPONENT or COMPONENT_SET node.
|
|
435
|
-
* @returns {Promise<{name: string, nodeId: string, properties: object, descendants: object}|undefined>}
|
|
436
|
-
*/
|
|
437
|
-
async function getLocalComponentMetadata(componentId) {
|
|
438
|
-
const node = await figma.getNodeByIdAsync(componentId);
|
|
439
|
-
if (node.type === "COMPONENT_SET" || node.type === "COMPONENT") {
|
|
440
|
-
const result = {
|
|
441
|
-
name: node.name,
|
|
442
|
-
nodeId: node.id,
|
|
443
|
-
properties: {},
|
|
444
|
-
descendants: {}
|
|
445
|
-
};
|
|
446
|
-
result.properties = getComponentProps(node);
|
|
447
|
-
collectDescendants(node, [], result.descendants);
|
|
448
|
-
return result;
|
|
449
|
-
} else {
|
|
450
|
-
throw new Error("Node is not a Component or Component Set");
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
/**
|
|
455
|
-
* Returns structured metadata for a published component or component set loaded by its key.
|
|
456
|
-
*
|
|
457
|
-
* @param {string} componentKey - The published key of the component or component set.
|
|
458
|
-
* @returns {Promise<{name: string, nodeId: string, properties: object, descendants: object}>}
|
|
459
|
-
*/
|
|
460
|
-
async function getPublishedComponentMetadata(componentKey) {
|
|
461
|
-
const node = await importComponentByKey(componentKey);
|
|
462
|
-
const result = {
|
|
463
|
-
name: node.name,
|
|
464
|
-
nodeId: node.id,
|
|
465
|
-
properties: {},
|
|
466
|
-
descendants: {}
|
|
467
|
-
};
|
|
468
|
-
result.properties = getComponentProps(node);
|
|
469
|
-
collectDescendants(node, [], result.descendants);
|
|
470
|
-
return result;
|
|
471
|
-
}
|
|
472
|
-
```
|
|
473
|
-
|
|
474
|
-
### Full metadata extraction script
|
|
475
|
-
|
|
476
|
-
```javascript
|
|
477
|
-
(async () => {
|
|
478
|
-
try {
|
|
479
|
-
// For local components, use getLocalComponentMetadata:
|
|
480
|
-
const result = await getLocalComponentMetadata('COMPONENT_OR_SET_ID');
|
|
481
|
-
figma.closePlugin(JSON.stringify(result));
|
|
482
|
-
|
|
483
|
-
// For published components, use getPublishedComponentMetadata:
|
|
484
|
-
// const result = await getPublishedComponentMetadata('COMPONENT_KEY');
|
|
485
|
-
// figma.closePlugin(JSON.stringify(result));
|
|
486
|
-
} catch(e) { figma.closePluginWithFailure(e.toString()); }
|
|
487
|
-
})()
|
|
488
|
-
```
|
|
1
|
+
# Component & Variant API Patterns
|
|
2
|
+
|
|
3
|
+
> Part of the [use_figma skill](../SKILL.md). How to correctly use the Plugin API for components, variants, and component properties.
|
|
4
|
+
>
|
|
5
|
+
> For design system context (when to use variants vs properties, code-to-Figma translation, property model), see [wwds-components](working-with-design-systems/wwds-components.md).
|
|
6
|
+
|
|
7
|
+
## Contents
|
|
8
|
+
|
|
9
|
+
- Creating a Component
|
|
10
|
+
- Combining Components into a Component Set (Variants)
|
|
11
|
+
- Laying Out Variants After combineAsVariants (Required)
|
|
12
|
+
- Component Properties: addComponentProperty API
|
|
13
|
+
- Linking Properties to Child Nodes (Required)
|
|
14
|
+
- INSTANCE_SWAP: Avoiding Variant Explosion
|
|
15
|
+
- Discovering Existing Conventions in the File
|
|
16
|
+
- Importing Components by Key
|
|
17
|
+
- Working with Instances (finding variants, setProperties, text overrides, detachInstance)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## Creating a Component
|
|
21
|
+
|
|
22
|
+
`figma.createComponent()` returns a `ComponentNode`, which behaves like a `FrameNode` but can be published, instanced, and combined into variant sets.
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
const comp = figma.createComponent();
|
|
26
|
+
comp.name = "MyComponent";
|
|
27
|
+
comp.layoutMode = "HORIZONTAL";
|
|
28
|
+
comp.primaryAxisAlignItems = "CENTER";
|
|
29
|
+
comp.counterAxisAlignItems = "CENTER";
|
|
30
|
+
comp.paddingLeft = 12;
|
|
31
|
+
comp.paddingRight = 12;
|
|
32
|
+
comp.layoutSizingHorizontal = "HUG";
|
|
33
|
+
comp.layoutSizingVertical = "HUG";
|
|
34
|
+
comp.fills = [{ type: "SOLID", color: { r: 0.2, g: 0.36, b: 0.96 } }];
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Combining Components into a Component Set (Variants)
|
|
38
|
+
|
|
39
|
+
`figma.combineAsVariants(components, parent)` takes an array of `ComponentNode`s (not frames — frames will throw) and groups them into a `ComponentSetNode`.
|
|
40
|
+
|
|
41
|
+
Variant names use a `Property=Value` format. Every unique combination must exist as a child component — missing ones show as blank gaps in the variant picker.
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
// Each component's name encodes its variant properties
|
|
45
|
+
const comp1 = figma.createComponent();
|
|
46
|
+
comp1.name = "size=md, style=primary";
|
|
47
|
+
const comp2 = figma.createComponent();
|
|
48
|
+
comp2.name = "size=md, style=secondary";
|
|
49
|
+
|
|
50
|
+
const componentSet = figma.combineAsVariants([comp1, comp2], figma.currentPage);
|
|
51
|
+
componentSet.name = "Button";
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Before creating variants, inspect the file** for existing naming patterns. Different files use different conventions (`State=Default` vs `state=default` vs `State/Default`). Always match what's already there.
|
|
55
|
+
|
|
56
|
+
## Laying Out Variants After combineAsVariants (Required)
|
|
57
|
+
|
|
58
|
+
After `combineAsVariants`, all children stack at `(0, 0)`. You **must** position them or the component set will appear as a single collapsed element with all variants overlapping.
|
|
59
|
+
|
|
60
|
+
```javascript
|
|
61
|
+
const cs = figma.combineAsVariants(components, figma.currentPage);
|
|
62
|
+
|
|
63
|
+
// Simple row layout
|
|
64
|
+
cs.children.forEach((child, i) => {
|
|
65
|
+
child.x = i * 150;
|
|
66
|
+
child.y = 0;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// CRITICAL: resize the component set from actual child bounds
|
|
70
|
+
let maxX = 0, maxY = 0;
|
|
71
|
+
for (const child of cs.children) {
|
|
72
|
+
maxX = Math.max(maxX, child.x + child.width);
|
|
73
|
+
maxY = Math.max(maxY, child.y + child.height);
|
|
74
|
+
}
|
|
75
|
+
cs.resizeWithoutConstraints(maxX + 40, maxY + 40);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
For multi-axis variants (e.g., size × style × state), parse the child's name to determine grid position:
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
for (const child of cs.children) {
|
|
82
|
+
const props = Object.fromEntries(
|
|
83
|
+
child.name.split(', ').map(p => p.split('='))
|
|
84
|
+
);
|
|
85
|
+
const col = stateValues.indexOf(props.state);
|
|
86
|
+
const row = styleValues.indexOf(props.style);
|
|
87
|
+
child.x = col * colWidth;
|
|
88
|
+
child.y = row * rowHeight;
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Component Properties: addComponentProperty API
|
|
93
|
+
|
|
94
|
+
`addComponentProperty` adds a TEXT, BOOLEAN, or INSTANCE_SWAP property to a component. It returns a **string key** (e.g., `"label#4:0"`) — never hardcode or guess this key.
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
// Returns the key as a string — capture it!
|
|
98
|
+
const labelKey = comp.addComponentProperty('Label', 'TEXT', 'Default text');
|
|
99
|
+
const showIconKey = comp.addComponentProperty('Show Icon', 'BOOLEAN', true);
|
|
100
|
+
const iconSlotKey = comp.addComponentProperty('Icon', 'INSTANCE_SWAP', iconComponentId);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Timing**: Add component properties to each variant component **before** calling `combineAsVariants`. After combining, the component set inherits all properties from its children. Do not add properties to the `ComponentSetNode` directly.
|
|
104
|
+
|
|
105
|
+
## Linking Properties to Child Nodes (Required)
|
|
106
|
+
|
|
107
|
+
A property that is added but not linked to a child node does **nothing**. You must set `componentPropertyReferences` on the child:
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
// TEXT property → link to a text node's characters
|
|
111
|
+
const labelKey = comp.addComponentProperty('Label', 'TEXT', 'Button');
|
|
112
|
+
const textNode = figma.createText();
|
|
113
|
+
textNode.characters = "Button";
|
|
114
|
+
comp.appendChild(textNode);
|
|
115
|
+
textNode.componentPropertyReferences = { characters: labelKey };
|
|
116
|
+
|
|
117
|
+
// BOOLEAN + INSTANCE_SWAP → link to an instance node
|
|
118
|
+
const showIconKey = comp.addComponentProperty('Show Icon', 'BOOLEAN', true);
|
|
119
|
+
const iconSlotKey = comp.addComponentProperty('Icon', 'INSTANCE_SWAP', iconComp.id);
|
|
120
|
+
const iconInstance = iconComp.createInstance();
|
|
121
|
+
comp.appendChild(iconInstance);
|
|
122
|
+
iconInstance.componentPropertyReferences = {
|
|
123
|
+
visible: showIconKey, // BOOLEAN controls show/hide
|
|
124
|
+
mainComponent: iconSlotKey // INSTANCE_SWAP controls which component
|
|
125
|
+
};
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Valid `componentPropertyReferences` keys:**
|
|
129
|
+
- `characters` — TEXT property on a TextNode
|
|
130
|
+
- `visible` — BOOLEAN property (any node)
|
|
131
|
+
- `mainComponent` — INSTANCE_SWAP property on an InstanceNode
|
|
132
|
+
|
|
133
|
+
## INSTANCE_SWAP: Avoiding Variant Explosion
|
|
134
|
+
|
|
135
|
+
When a component has many possible sub-elements (e.g., 30 different icons), **never** create a variant per sub-element. Use a single INSTANCE_SWAP property instead — the user picks from any compatible component at design time.
|
|
136
|
+
|
|
137
|
+
```javascript
|
|
138
|
+
// Create icon as its own ComponentNode
|
|
139
|
+
const iconComp = figma.createComponent();
|
|
140
|
+
iconComp.name = "Icon/Search";
|
|
141
|
+
iconComp.resize(24, 24);
|
|
142
|
+
const svgNode = figma.createNodeFromSvg('<svg>...</svg>');
|
|
143
|
+
iconComp.appendChild(svgNode);
|
|
144
|
+
|
|
145
|
+
// Use it as the default for INSTANCE_SWAP
|
|
146
|
+
const iconSlotKey = comp.addComponentProperty('Icon', 'INSTANCE_SWAP', iconComp.id);
|
|
147
|
+
const instance = iconComp.createInstance();
|
|
148
|
+
comp.appendChild(instance);
|
|
149
|
+
instance.componentPropertyReferences = { mainComponent: iconSlotKey };
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
This works for icons, avatars, badges, or any swappable nested element.
|
|
153
|
+
|
|
154
|
+
## Discovering Existing Conventions in the File
|
|
155
|
+
|
|
156
|
+
**Always inspect the file before creating components.** Different files have different naming styles, structures, and conventions. Your code should match what's already there.
|
|
157
|
+
|
|
158
|
+
### List all existing components across all pages
|
|
159
|
+
|
|
160
|
+
```javascript
|
|
161
|
+
(async () => {
|
|
162
|
+
try {
|
|
163
|
+
const results = [];
|
|
164
|
+
for (const page of figma.root.children) {
|
|
165
|
+
await figma.setCurrentPageAsync(page);
|
|
166
|
+
page.findAll(n => {
|
|
167
|
+
if (n.type === 'COMPONENT') results.push(`[${page.name}] ${n.name} (COMPONENT) id=${n.id}`);
|
|
168
|
+
if (n.type === 'COMPONENT_SET') results.push(`[${page.name}] ${n.name} (COMPONENT_SET) id=${n.id}`);
|
|
169
|
+
return false;
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
figma.closePlugin(results.join('\n'));
|
|
173
|
+
} catch(e) { figma.closePluginWithFailure(e.toString()); }
|
|
174
|
+
})()
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Inspect an existing component set's variant naming pattern
|
|
178
|
+
|
|
179
|
+
```javascript
|
|
180
|
+
(async () => {
|
|
181
|
+
try {
|
|
182
|
+
const cs = await figma.getNodeByIdAsync('COMPONENT_SET_ID');
|
|
183
|
+
const variantNames = cs.children.map(c => c.name);
|
|
184
|
+
const propDefs = cs.componentPropertyDefinitions;
|
|
185
|
+
figma.closePlugin(JSON.stringify({ variantNames, propDefs }));
|
|
186
|
+
} catch(e) { figma.closePluginWithFailure(e.toString()); }
|
|
187
|
+
})()
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Find existing components in the file
|
|
191
|
+
|
|
192
|
+
```javascript
|
|
193
|
+
(async () => {
|
|
194
|
+
try {
|
|
195
|
+
const components = [];
|
|
196
|
+
for (const page of figma.root.children) {
|
|
197
|
+
await figma.setCurrentPageAsync(page);
|
|
198
|
+
page.findAll(n => {
|
|
199
|
+
if (n.type === 'COMPONENT') {
|
|
200
|
+
components.push({ name: n.name, id: n.id, page: page.name, w: n.width, h: n.height });
|
|
201
|
+
}
|
|
202
|
+
return false;
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
figma.closePlugin(JSON.stringify(components));
|
|
206
|
+
} catch(e) { figma.closePluginWithFailure(e.toString()); }
|
|
207
|
+
})()
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Importing Components by Key (Team Libraries)
|
|
211
|
+
|
|
212
|
+
`importComponentByKeyAsync` and `importComponentSetByKeyAsync` import components from **team libraries** (not the same file you're working in). For components in the current file, use `figma.getNodeByIdAsync()` or `findOne()`/`findAll()` to locate them directly.
|
|
213
|
+
|
|
214
|
+
```javascript
|
|
215
|
+
// Import a component from a team library
|
|
216
|
+
const comp = await figma.importComponentByKeyAsync("COMPONENT_KEY");
|
|
217
|
+
const instance = comp.createInstance();
|
|
218
|
+
|
|
219
|
+
// Import a component set from a team library and pick a variant
|
|
220
|
+
const set = await figma.importComponentSetByKeyAsync("COMPONENT_SET_KEY");
|
|
221
|
+
const variant = set.children.find(c =>
|
|
222
|
+
c.type === "COMPONENT" && c.name.includes("size=md")
|
|
223
|
+
) || set.defaultVariant;
|
|
224
|
+
const variantInstance = variant.createInstance();
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Working with Instances
|
|
228
|
+
|
|
229
|
+
### Finding the right variant in a component set
|
|
230
|
+
|
|
231
|
+
Parse variant names to match on multiple properties simultaneously:
|
|
232
|
+
|
|
233
|
+
```javascript
|
|
234
|
+
const compSet = await figma.importComponentSetByKeyAsync("KEY");
|
|
235
|
+
|
|
236
|
+
const variant = compSet.children.find(c => {
|
|
237
|
+
const props = Object.fromEntries(
|
|
238
|
+
c.name.split(', ').map(p => p.split('='))
|
|
239
|
+
);
|
|
240
|
+
return props.variant === "primary" && props.size === "md";
|
|
241
|
+
}) || compSet.defaultVariant;
|
|
242
|
+
|
|
243
|
+
const instance = variant.createInstance();
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Setting variant properties on an instance
|
|
247
|
+
|
|
248
|
+
After creating an instance from a component set, you can set variant properties via `setProperties`:
|
|
249
|
+
|
|
250
|
+
```javascript
|
|
251
|
+
const instance = defaultVariant.createInstance();
|
|
252
|
+
instance.setProperties({
|
|
253
|
+
"variant": "primary",
|
|
254
|
+
"size": "medium"
|
|
255
|
+
});
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Overriding text in a component instance
|
|
259
|
+
|
|
260
|
+
**Always discover component properties BEFORE writing text overrides.** Components expose text as `TEXT`-type component properties, and `setProperties()` is the correct way to override them. Direct `node.characters` changes on property-managed text may be overridden by the component property system on render.
|
|
261
|
+
|
|
262
|
+
**Step 1: Inspect componentProperties on a sample instance:**
|
|
263
|
+
|
|
264
|
+
```javascript
|
|
265
|
+
const instance = comp.createInstance();
|
|
266
|
+
const propDefs = instance.componentProperties;
|
|
267
|
+
// Returns e.g.: { "Label#2:0": { type: "TEXT", value: "Button" }, "Has Icon#4:64": { type: "BOOLEAN", value: true } }
|
|
268
|
+
figma.closePlugin(JSON.stringify(propDefs));
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Also check nested instances — a parent component may not expose text properties directly, but its nested child instances might:
|
|
272
|
+
|
|
273
|
+
```javascript
|
|
274
|
+
const nestedInstances = instance.findAll(n => n.type === "INSTANCE");
|
|
275
|
+
const nestedProps = nestedInstances.map(ni => ({
|
|
276
|
+
name: ni.name,
|
|
277
|
+
id: ni.id,
|
|
278
|
+
properties: ni.componentProperties
|
|
279
|
+
}));
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
**Step 2: Use setProperties() for TEXT-type properties:**
|
|
283
|
+
|
|
284
|
+
```javascript
|
|
285
|
+
const instance = comp.createInstance();
|
|
286
|
+
const propDefs = instance.componentProperties;
|
|
287
|
+
for (const [key, def] of Object.entries(propDefs)) {
|
|
288
|
+
if (def.type === "TEXT") {
|
|
289
|
+
instance.setProperties({ [key]: "New text value" });
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
For nested instances that expose their own TEXT properties, call `setProperties()` on the nested instance:
|
|
295
|
+
|
|
296
|
+
```javascript
|
|
297
|
+
const nestedHeading = instance.findOne(n => n.type === "INSTANCE" && n.name === "Text Heading");
|
|
298
|
+
if (nestedHeading) {
|
|
299
|
+
nestedHeading.setProperties({ "Text#2104:5": "Actual heading text" });
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
**Step 3: Only fall back to direct node.characters for unmanaged text.** If text is NOT controlled by any component property, find text nodes directly. **Always load the node's actual font first** — instance text nodes inherit fonts from the source component, so don't assume Inter Regular:
|
|
304
|
+
|
|
305
|
+
```javascript
|
|
306
|
+
const textNodes = instance.findAll(n => n.type === "TEXT");
|
|
307
|
+
for (const t of textNodes) {
|
|
308
|
+
await figma.loadFontAsync(t.fontName);
|
|
309
|
+
t.characters = "Updated text";
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### detachInstance() invalidates ancestor node IDs
|
|
314
|
+
|
|
315
|
+
**Warning:** When `detachInstance()` is called on a nested instance inside a library component instance, the parent instance may also get implicitly detached (converted from INSTANCE to FRAME with a **new ID**). Subsequent `getNodeByIdAsync(oldParentId)` returns null.
|
|
316
|
+
|
|
317
|
+
```javascript
|
|
318
|
+
// WRONG — cached parent ID becomes invalid after child detach
|
|
319
|
+
const parentId = parentInstance.id;
|
|
320
|
+
nestedChild.detachInstance();
|
|
321
|
+
const parent = await figma.getNodeByIdAsync(parentId); // null!
|
|
322
|
+
|
|
323
|
+
// CORRECT — re-discover nodes by traversal from a stable (non-instance) parent
|
|
324
|
+
const stableFrame = await figma.getNodeByIdAsync(manualFrameId); // a frame YOU created
|
|
325
|
+
nestedChild.detachInstance();
|
|
326
|
+
// Re-find the parent by traversing from the stable frame
|
|
327
|
+
const parent = stableFrame.findOne(n => n.name === "ParentName");
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
If you must detach multiple nested instances across sibling components, do it in a **single** `use_figma` call — discover all targets by traversal at the start before any detachment mutates the tree.
|
|
331
|
+
|
|
332
|
+
## Inspecting Component Metadata (Deep Traversal)
|
|
333
|
+
|
|
334
|
+
These helpers extract the full property schema and descendant structure of a component. Useful for understanding complex components before creating instances or setting properties.
|
|
335
|
+
|
|
336
|
+
```javascript
|
|
337
|
+
/**
|
|
338
|
+
* Imports a component or component set from a library by its published key.
|
|
339
|
+
* Tries COMPONENT first, then falls back to COMPONENT_SET.
|
|
340
|
+
*
|
|
341
|
+
* @param {string} componentKey - The published key of the component or component set.
|
|
342
|
+
* @returns {Promise<ComponentNode|ComponentSetNode>}
|
|
343
|
+
*/
|
|
344
|
+
async function importComponentByKey(componentKey) {
|
|
345
|
+
try {
|
|
346
|
+
return await figma.importComponentByKeyAsync(componentKey);
|
|
347
|
+
} catch {
|
|
348
|
+
try {
|
|
349
|
+
return await figma.importComponentSetByKeyAsync(componentKey);
|
|
350
|
+
} catch {
|
|
351
|
+
throw new Error(`No Component or Component Set available with key '${componentKey}'`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Given a main component node, returns the component set parent if one exists,
|
|
358
|
+
* otherwise returns the component itself. Used to get the top-level node that
|
|
359
|
+
* holds `componentPropertyDefinitions`.
|
|
360
|
+
*
|
|
361
|
+
* @param {ComponentNode} mainComponent
|
|
362
|
+
* @returns {ComponentNode|ComponentSetNode}
|
|
363
|
+
*/
|
|
364
|
+
function getRelevantComponentNode(mainComponent) {
|
|
365
|
+
return mainComponent.parent.type === "COMPONENT_SET"
|
|
366
|
+
? mainComponent.parent
|
|
367
|
+
: mainComponent;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Extracts `componentPropertyDefinitions` from a component or component set node
|
|
372
|
+
* into a flat map keyed by property key.
|
|
373
|
+
*
|
|
374
|
+
* @param {ComponentNode|ComponentSetNode} node
|
|
375
|
+
* @returns {Record<string, {name: string, type: string, key: string, variantOptions?: string[]}>}
|
|
376
|
+
*/
|
|
377
|
+
function getComponentProps(node) {
|
|
378
|
+
const result = {};
|
|
379
|
+
for (let key in node.componentPropertyDefinitions) {
|
|
380
|
+
const prop = {
|
|
381
|
+
name: key.replace(/#[^#]+$/, ""),
|
|
382
|
+
type: node.componentPropertyDefinitions[key].type,
|
|
383
|
+
key: key
|
|
384
|
+
};
|
|
385
|
+
if (prop.type === "VARIANT") {
|
|
386
|
+
prop.variantOptions = node.componentPropertyDefinitions[key].variantOptions;
|
|
387
|
+
}
|
|
388
|
+
result[key] = prop;
|
|
389
|
+
}
|
|
390
|
+
return result;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Recursively walks a component tree and collects all INSTANCE and TEXT nodes
|
|
395
|
+
* into `result`, keyed by `TYPE[name]`. Handles variant namespacing and
|
|
396
|
+
* deduplicates nodes with identical names but differing property references.
|
|
397
|
+
*
|
|
398
|
+
* @param {SceneNode} node - The node to traverse.
|
|
399
|
+
* @param {string[]} namespace - Accumulated variant names for the current path.
|
|
400
|
+
* @param {Record<string, object>} result - Accumulator object populated in place.
|
|
401
|
+
*/
|
|
402
|
+
function collectDescendants(node, namespace, result) {
|
|
403
|
+
if (node.type === "INSTANCE" || node.type === "TEXT") {
|
|
404
|
+
const references = node.componentPropertyReferences || {};
|
|
405
|
+
if (!node.visible && !references.visible) return;
|
|
406
|
+
|
|
407
|
+
const object = { type: node.type, name: node.name, references };
|
|
408
|
+
let key = `${node.type}[${node.name}]`;
|
|
409
|
+
|
|
410
|
+
if (result[key] && JSON.stringify(references) !== JSON.stringify(result[key].references)) {
|
|
411
|
+
key += btoa(btoa(unescape(encodeURIComponent(JSON.stringify(references)))));
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (node.type === "INSTANCE") {
|
|
415
|
+
const mainComponent = getRelevantComponentNode(node.mainComponent);
|
|
416
|
+
object.properties = getComponentProps(mainComponent);
|
|
417
|
+
object.descendants = {};
|
|
418
|
+
object.mainComponentName = mainComponent.name;
|
|
419
|
+
collectDescendants(mainComponent, [], object.descendants);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const start = namespace.length ? { variants: [] } : {};
|
|
423
|
+
result[key] = Object.assign(object, result[key] || start);
|
|
424
|
+
if (namespace.length) result[key].variants.push(namespace[namespace.length - 1]);
|
|
425
|
+
} else if ("children" in node && node.visible) {
|
|
426
|
+
if (node.type === "COMPONENT" && node.parent.type === "COMPONENT_SET") namespace.push(node.name);
|
|
427
|
+
node.children.forEach(child => collectDescendants(child, namespace, result));
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Returns structured metadata for a component or component set defined in the current file.
|
|
433
|
+
*
|
|
434
|
+
* @param {string} componentId - The node ID of a COMPONENT or COMPONENT_SET node.
|
|
435
|
+
* @returns {Promise<{name: string, nodeId: string, properties: object, descendants: object}|undefined>}
|
|
436
|
+
*/
|
|
437
|
+
async function getLocalComponentMetadata(componentId) {
|
|
438
|
+
const node = await figma.getNodeByIdAsync(componentId);
|
|
439
|
+
if (node.type === "COMPONENT_SET" || node.type === "COMPONENT") {
|
|
440
|
+
const result = {
|
|
441
|
+
name: node.name,
|
|
442
|
+
nodeId: node.id,
|
|
443
|
+
properties: {},
|
|
444
|
+
descendants: {}
|
|
445
|
+
};
|
|
446
|
+
result.properties = getComponentProps(node);
|
|
447
|
+
collectDescendants(node, [], result.descendants);
|
|
448
|
+
return result;
|
|
449
|
+
} else {
|
|
450
|
+
throw new Error("Node is not a Component or Component Set");
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Returns structured metadata for a published component or component set loaded by its key.
|
|
456
|
+
*
|
|
457
|
+
* @param {string} componentKey - The published key of the component or component set.
|
|
458
|
+
* @returns {Promise<{name: string, nodeId: string, properties: object, descendants: object}>}
|
|
459
|
+
*/
|
|
460
|
+
async function getPublishedComponentMetadata(componentKey) {
|
|
461
|
+
const node = await importComponentByKey(componentKey);
|
|
462
|
+
const result = {
|
|
463
|
+
name: node.name,
|
|
464
|
+
nodeId: node.id,
|
|
465
|
+
properties: {},
|
|
466
|
+
descendants: {}
|
|
467
|
+
};
|
|
468
|
+
result.properties = getComponentProps(node);
|
|
469
|
+
collectDescendants(node, [], result.descendants);
|
|
470
|
+
return result;
|
|
471
|
+
}
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### Full metadata extraction script
|
|
475
|
+
|
|
476
|
+
```javascript
|
|
477
|
+
(async () => {
|
|
478
|
+
try {
|
|
479
|
+
// For local components, use getLocalComponentMetadata:
|
|
480
|
+
const result = await getLocalComponentMetadata('COMPONENT_OR_SET_ID');
|
|
481
|
+
figma.closePlugin(JSON.stringify(result));
|
|
482
|
+
|
|
483
|
+
// For published components, use getPublishedComponentMetadata:
|
|
484
|
+
// const result = await getPublishedComponentMetadata('COMPONENT_KEY');
|
|
485
|
+
// figma.closePlugin(JSON.stringify(result));
|
|
486
|
+
} catch(e) { figma.closePluginWithFailure(e.toString()); }
|
|
487
|
+
})()
|
|
488
|
+
```
|