@claude-collective/cli 0.26.0 → 0.29.4
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 +125 -0
- package/README.md +47 -13
- package/config/stacks.yaml +330 -93
- package/dist/chunk-56ERY7H7.js +29 -0
- package/dist/chunk-56ERY7H7.js.map +1 -0
- package/dist/{chunk-OBXAY23Y.js → chunk-5I6VY2E7.js} +5 -5
- package/dist/chunk-5I6VY2E7.js.map +1 -0
- package/dist/{chunk-ZDREFYD2.js → chunk-5WIHSJRO.js} +234 -59
- package/dist/chunk-5WIHSJRO.js.map +1 -0
- package/dist/{chunk-GGFOD5PK.js → chunk-6F3ZKDVE.js} +122 -66
- package/dist/chunk-6F3ZKDVE.js.map +1 -0
- package/dist/{chunk-DBRUQQUF.js → chunk-7GHTQSWI.js} +5 -1
- package/dist/{chunk-DBRUQQUF.js.map → chunk-7GHTQSWI.js.map} +1 -1
- package/dist/chunk-7ICBJZV2.js +63 -0
- package/dist/chunk-7ICBJZV2.js.map +1 -0
- package/dist/{chunk-3X5D7RM5.js → chunk-7UKQZSWT.js} +15 -4
- package/dist/chunk-7UKQZSWT.js.map +1 -0
- package/dist/{chunk-F4RD5FYM.js → chunk-A4T4YSV4.js} +5 -2
- package/dist/chunk-A4T4YSV4.js.map +1 -0
- package/dist/{chunk-VVYNZZUX.js → chunk-AG5YGYJT.js} +9 -5
- package/dist/chunk-AG5YGYJT.js.map +1 -0
- package/dist/chunk-AJFSCLJ7.js +81 -0
- package/dist/chunk-AJFSCLJ7.js.map +1 -0
- package/dist/{chunk-NQJ47R4N.js → chunk-CQZAKMPJ.js} +66 -14
- package/dist/chunk-CQZAKMPJ.js.map +1 -0
- package/dist/chunk-DIRH4PDF.js +24 -0
- package/dist/chunk-DIRH4PDF.js.map +1 -0
- package/dist/{chunk-R7B63JAP.js → chunk-DUIYVKFK.js} +123 -86
- package/dist/chunk-DUIYVKFK.js.map +1 -0
- package/dist/chunk-EP6J44I4.js +142 -0
- package/dist/chunk-EP6J44I4.js.map +1 -0
- package/dist/{chunk-TDZE4TDG.js → chunk-EUPMWSM3.js} +92 -29
- package/dist/chunk-EUPMWSM3.js.map +1 -0
- package/dist/chunk-FUPBGSRA.js +66 -0
- package/dist/chunk-FUPBGSRA.js.map +1 -0
- package/dist/{chunk-U7HFKR74.js → chunk-FY5D4KIC.js} +5 -2
- package/dist/chunk-FY5D4KIC.js.map +1 -0
- package/dist/chunk-G5WXKKQM.js +233 -0
- package/dist/chunk-G5WXKKQM.js.map +1 -0
- package/dist/{chunk-KWYO3M5Q.js → chunk-GVVEPVR7.js} +25 -24
- package/dist/chunk-GVVEPVR7.js.map +1 -0
- package/dist/chunk-IFODQTCX.js +162 -0
- package/dist/chunk-IFODQTCX.js.map +1 -0
- package/dist/chunk-IQUBOWWU.js +366 -0
- package/dist/chunk-IQUBOWWU.js.map +1 -0
- package/dist/{chunk-ETCVEV3S.js → chunk-IRG52AN5.js} +242 -155
- package/dist/chunk-IRG52AN5.js.map +1 -0
- package/dist/chunk-MQAYAISQ.js +88 -0
- package/dist/chunk-MQAYAISQ.js.map +1 -0
- package/dist/{chunk-4357L7VK.js → chunk-N73GQTCK.js} +37 -104
- package/dist/chunk-N73GQTCK.js.map +1 -0
- package/dist/{chunk-H7SSBSPR.js → chunk-OA5RCL2L.js} +8 -5
- package/dist/chunk-OA5RCL2L.js.map +1 -0
- package/dist/{chunk-I3YYG5IO.js → chunk-PNXFJPXF.js} +3 -3
- package/dist/{chunk-MCTSHLAF.js → chunk-RI5QEK5W.js} +41 -14
- package/dist/chunk-RI5QEK5W.js.map +1 -0
- package/dist/chunk-RXC7AF7N.js +31 -0
- package/dist/chunk-RXC7AF7N.js.map +1 -0
- package/dist/{chunk-ETQ3BPGU.js → chunk-SSHG7MEE.js} +1252 -728
- package/dist/chunk-SSHG7MEE.js.map +1 -0
- package/dist/{chunk-4S4FCAA2.js → chunk-VTUPUKFD.js} +26 -31
- package/dist/chunk-VTUPUKFD.js.map +1 -0
- package/dist/{chunk-XENOESJZ.js → chunk-WLQUQXWO.js} +10 -67
- package/dist/chunk-WLQUQXWO.js.map +1 -0
- package/dist/chunk-WPED6CL3.js +105 -0
- package/dist/chunk-WPED6CL3.js.map +1 -0
- package/dist/{chunk-R5KJVI54.js → chunk-XPMEGGJK.js} +97 -76
- package/dist/chunk-XPMEGGJK.js.map +1 -0
- package/dist/chunk-XZKVOPCR.js +75 -0
- package/dist/chunk-XZKVOPCR.js.map +1 -0
- package/dist/{chunk-ZW2PELOH.js → chunk-ZX5DM4D5.js} +106 -69
- package/dist/chunk-ZX5DM4D5.js.map +1 -0
- package/dist/commands/build/marketplace.js +21 -20
- package/dist/commands/build/marketplace.js.map +1 -1
- package/dist/commands/build/plugins.js +7 -11
- package/dist/commands/build/plugins.js.map +1 -1
- package/dist/commands/build/stack.js +8 -13
- package/dist/commands/build/stack.js.map +1 -1
- package/dist/commands/compile.js +109 -135
- package/dist/commands/compile.js.map +1 -1
- package/dist/commands/config/get.js +4 -5
- package/dist/commands/config/get.js.map +1 -1
- package/dist/commands/config/index.js +5 -6
- package/dist/commands/config/index.js.map +1 -1
- package/dist/commands/config/path.js +4 -5
- package/dist/commands/config/path.js.map +1 -1
- package/dist/commands/config/set-project.js +4 -5
- package/dist/commands/config/set-project.js.map +1 -1
- package/dist/commands/config/show.js +5 -6
- package/dist/commands/config/unset-project.js +4 -5
- package/dist/commands/config/unset-project.js.map +1 -1
- package/dist/commands/diff.js +26 -11
- package/dist/commands/diff.js.map +1 -1
- package/dist/commands/doctor.js +13 -16
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/edit.js +71 -42
- package/dist/commands/edit.js.map +1 -1
- package/dist/commands/eject.js +34 -14
- package/dist/commands/eject.js.map +1 -1
- package/dist/commands/import/skill.js +93 -52
- package/dist/commands/import/skill.js.map +1 -1
- package/dist/commands/info.js +27 -9
- package/dist/commands/info.js.map +1 -1
- package/dist/commands/init.js +98 -48
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/list.js +10 -5
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/new/agent.js +8 -11
- package/dist/commands/new/agent.js.map +1 -1
- package/dist/commands/new/skill.js +17 -18
- package/dist/commands/new/skill.js.map +1 -1
- package/dist/commands/outdated.js +23 -9
- package/dist/commands/outdated.js.map +1 -1
- package/dist/commands/search.js +23 -20
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/uninstall.js +28 -21
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.js +30 -22
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/validate.js +103 -39
- package/dist/commands/validate.js.map +1 -1
- package/dist/commands/version/bump.js +4 -5
- package/dist/commands/version/bump.js.map +1 -1
- package/dist/commands/version/index.js +4 -5
- package/dist/commands/version/index.js.map +1 -1
- package/dist/commands/version/set.js +4 -5
- package/dist/commands/version/set.js.map +1 -1
- package/dist/commands/version/show.js +4 -5
- package/dist/commands/version/show.js.map +1 -1
- package/dist/components/common/confirm.test.js +2 -2
- package/dist/components/common/confirm.test.js.map +1 -1
- package/dist/components/skill-search/skill-search.js +4 -2
- package/dist/components/wizard/category-grid.js +3 -1
- package/dist/components/wizard/category-grid.test.js +63 -64
- package/dist/components/wizard/category-grid.test.js.map +1 -1
- package/dist/components/wizard/domain-selection.js +13 -0
- package/dist/components/wizard/help-modal.js +10 -0
- package/dist/components/wizard/help-modal.js.map +1 -0
- package/dist/components/wizard/menu-item.js +2 -1
- package/dist/components/wizard/search-modal.js +3 -1
- package/dist/components/wizard/search-modal.test.js +4 -2
- package/dist/components/wizard/search-modal.test.js.map +1 -1
- package/dist/components/wizard/section-progress.js +2 -1
- package/dist/components/wizard/section-progress.test.js +2 -1
- package/dist/components/wizard/section-progress.test.js.map +1 -1
- package/dist/components/wizard/source-grid.js +6 -2
- package/dist/components/wizard/source-grid.test.js +49 -45
- package/dist/components/wizard/source-grid.test.js.map +1 -1
- package/dist/components/wizard/stack-selection.js +15 -0
- package/dist/components/wizard/stack-selection.js.map +1 -0
- package/dist/components/wizard/step-approach.js +8 -6
- package/dist/components/wizard/step-approach.test.js +11 -9
- package/dist/components/wizard/step-approach.test.js.map +1 -1
- package/dist/components/wizard/step-build.js +9 -13
- package/dist/components/wizard/step-build.test.js +27 -45
- package/dist/components/wizard/step-build.test.js.map +1 -1
- package/dist/components/wizard/step-confirm.js +2 -1
- package/dist/components/wizard/step-confirm.test.js +6 -5
- package/dist/components/wizard/step-confirm.test.js.map +1 -1
- package/dist/components/wizard/step-refine.js +2 -1
- package/dist/components/wizard/step-refine.test.js +3 -2
- package/dist/components/wizard/step-refine.test.js.map +1 -1
- package/dist/components/wizard/step-settings.js +8 -6
- package/dist/components/wizard/step-settings.test.js +12 -10
- package/dist/components/wizard/step-settings.test.js.map +1 -1
- package/dist/components/wizard/step-sources.js +11 -9
- package/dist/components/wizard/step-sources.test.js +16 -15
- package/dist/components/wizard/step-sources.test.js.map +1 -1
- package/dist/components/wizard/step-stack.js +9 -6
- package/dist/components/wizard/step-stack.test.js +15 -12
- package/dist/components/wizard/step-stack.test.js.map +1 -1
- package/dist/components/wizard/view-title.js +2 -1
- package/dist/components/wizard/wizard-layout.js +7 -9
- package/dist/components/wizard/wizard-tabs.js +2 -1
- package/dist/components/wizard/wizard-tabs.test.js +2 -1
- package/dist/components/wizard/wizard-tabs.test.js.map +1 -1
- package/dist/components/wizard/wizard.js +26 -20
- package/dist/config/stacks.yaml +330 -93
- package/dist/hooks/init.js +3 -4
- package/dist/hooks/init.js.map +1 -1
- package/dist/source-manager-TV2YGPAN.js +15 -0
- package/dist/source-manager-TV2YGPAN.js.map +1 -0
- package/dist/stores/wizard-store.js +4 -3
- package/dist/stores/wizard-store.test.js +272 -25
- package/dist/stores/wizard-store.test.js.map +1 -1
- package/package.json +2 -1
- package/src/schemas/agent-frontmatter.schema.json +84 -0
- package/src/schemas/agent.schema.json +93 -0
- package/src/schemas/hooks.schema.json +47 -0
- package/src/schemas/marketplace.schema.json +119 -0
- package/src/schemas/metadata.schema.json +113 -0
- package/src/schemas/plugin.schema.json +130 -0
- package/src/schemas/project-config.schema.json +125 -0
- package/src/schemas/project-source-config.schema.json +81 -0
- package/src/schemas/skill-frontmatter.schema.json +42 -0
- package/src/schemas/skills-matrix.schema.json +467 -0
- package/src/schemas/stack.schema.json +191 -0
- package/src/schemas/stacks.schema.json +111 -0
- package/dist/chunk-3X5D7RM5.js.map +0 -1
- package/dist/chunk-4357L7VK.js.map +0 -1
- package/dist/chunk-4S4FCAA2.js.map +0 -1
- package/dist/chunk-7UPXT32F.js +0 -197
- package/dist/chunk-7UPXT32F.js.map +0 -1
- package/dist/chunk-ETCVEV3S.js.map +0 -1
- package/dist/chunk-ETQ3BPGU.js.map +0 -1
- package/dist/chunk-F4RD5FYM.js.map +0 -1
- package/dist/chunk-GGFOD5PK.js.map +0 -1
- package/dist/chunk-H7SSBSPR.js.map +0 -1
- package/dist/chunk-HWD32NP7.js +0 -19
- package/dist/chunk-HWD32NP7.js.map +0 -1
- package/dist/chunk-KWYO3M5Q.js.map +0 -1
- package/dist/chunk-MCTSHLAF.js.map +0 -1
- package/dist/chunk-NQJ47R4N.js.map +0 -1
- package/dist/chunk-O6ZTD7ZI.js +0 -70
- package/dist/chunk-O6ZTD7ZI.js.map +0 -1
- package/dist/chunk-OBXAY23Y.js.map +0 -1
- package/dist/chunk-R5KJVI54.js.map +0 -1
- package/dist/chunk-R7B63JAP.js.map +0 -1
- package/dist/chunk-TDZE4TDG.js.map +0 -1
- package/dist/chunk-TMED5DQ2.js +0 -210
- package/dist/chunk-TMED5DQ2.js.map +0 -1
- package/dist/chunk-U7HFKR74.js.map +0 -1
- package/dist/chunk-UEMRJI2K.js +0 -146
- package/dist/chunk-UEMRJI2K.js.map +0 -1
- package/dist/chunk-UNN7523L.js +0 -78
- package/dist/chunk-UNN7523L.js.map +0 -1
- package/dist/chunk-VVYNZZUX.js.map +0 -1
- package/dist/chunk-XENOESJZ.js.map +0 -1
- package/dist/chunk-ZDREFYD2.js.map +0 -1
- package/dist/chunk-ZW2PELOH.js.map +0 -1
- package/dist/source-manager-EYO3F2DV.js +0 -16
- /package/dist/{chunk-I3YYG5IO.js.map → chunk-PNXFJPXF.js.map} +0 -0
- /package/dist/{source-manager-EYO3F2DV.js.map → components/wizard/domain-selection.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/stores/wizard-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { DEFAULT_PRESELECTED_SKILLS } from \"../consts\";\nimport { resolveAlias } from \"../lib/matrix/index.js\";\nimport type {\n BoundSkill,\n Domain,\n DomainSelections,\n MergedSkillsMatrix,\n SkillAlias,\n SkillAssignment,\n SkillId,\n Subcategory,\n SubcategorySelections,\n} from \"../types/index.js\";\nimport { warn } from \"../utils/logger.js\";\nimport { typedEntries, typedKeys } from \"../utils/typed-object.js\";\n\nconst ALL_DOMAINS: Domain[] = [\"web\", \"web-extras\", \"api\", \"cli\", \"mobile\", \"shared\"];\n\nconst DEFAULT_SOURCE_ID = \"public\";\nconst DEFAULT_SOURCE_LABEL = \"Public\";\n\n/** Sort priority: local first, then public, then private/other */\nconst SOURCE_SORT_ORDER: Record<string, number> = {\n local: 0,\n public: 1,\n private: 2,\n};\n\nconst SOURCE_DISPLAY_NAMES: Record<string, string> = {\n public: \"Public\",\n local: \"Local\",\n};\n\nconst DEFAULT_SORT_PRIORITY = 3;\n\nfunction formatSourceLabel(source: {\n name: string;\n version?: string;\n installed?: boolean;\n}): string {\n const displayName = SOURCE_DISPLAY_NAMES[source.name] ?? source.name;\n const prefix = source.installed ? \"\\u2713 \" : \"\";\n const versionSuffix = source.version ? ` \\u00B7 v${source.version}` : \"\";\n return `${prefix}${displayName}${versionSuffix}`;\n}\n\ntype SkillLookupEntry = { category: string; displayName?: string };\n\nfunction resolveSkillForPopulation(\n skillId: SkillId,\n skills: Partial<Record<SkillId, SkillLookupEntry>>,\n categories: Partial<Record<Subcategory, { domain?: Domain }>>,\n): { domain: Domain; subcat: Subcategory; techId: SkillId } | null {\n const skill = skills[skillId];\n if (!skill?.category || !skill.displayName) {\n warn(\n `Installed skill '${skillId}' is missing from the marketplace — it may have been removed or renamed`,\n );\n return null;\n }\n\n // Boundary cast: category is a Subcategory at the data boundary\n const subcat = skill.category as Subcategory;\n const domain = categories[subcat]?.domain;\n if (!domain) {\n warn(`Installed skill '${skillId}' has unknown category '${skill.category}' — skipping`);\n return null;\n }\n\n // Boundary cast: display name resolved to SkillId downstream by resolveAlias\n return { domain, subcat, techId: skill.displayName as SkillId };\n}\n\nfunction buildBoundSkillOptions(\n boundSkills: BoundSkill[],\n alias: SkillAlias,\n selectedSource: string,\n): { id: string; label: string; selected: boolean; installed: boolean }[] {\n return boundSkills\n .filter((b) => b.boundTo === alias)\n .map((bound) => ({\n id: bound.sourceName,\n label: formatSourceLabel({\n name: bound.sourceName,\n installed: false,\n }),\n selected: selectedSource === bound.sourceName,\n installed: false,\n }));\n}\n\n/** Extract the alias from a skill ID or use displayName from the matrix */\nfunction getSkillAlias(skillId: SkillId, matrix: MergedSkillsMatrix): SkillAlias {\n const displayName = matrix.displayNames?.[skillId];\n if (displayName) return displayName;\n // Fallback: use the last segment of the skill ID (e.g., \"web-framework-react\" -> \"react\")\n const segments = skillId.split(\"-\");\n const fallback = segments[segments.length - 1] || skillId;\n warn(`No display name found for skill '${skillId}', using fallback alias '${fallback}'`);\n return fallback;\n}\n\n/**\n * Wizard step identifiers for the multi-step init/edit flow.\n *\n * Progression: approach -> stack -> build -> sources -> confirm\n * Navigation is tracked via the `history` stack for goBack() support.\n */\nexport type WizardStep =\n | \"approach\" // Choose stack or build from scratch\n | \"stack\" // Select stack (if approach=stack) or domains (if approach=scratch)\n | \"build\" // CategoryGrid for technology selection\n | \"sources\" // Choose skill sources (recommended vs custom)\n | \"confirm\"; // Final confirmation\n\n/**\n * Wizard store state and actions.\n *\n * The store uses a composition pattern: small, focused actions that each mutate\n * one or two state fields. Wizard step components compose these actions to build\n * up the full selection state incrementally (domains -> subcategories -> skills -> sources).\n *\n * State flow: approach selection -> stack/domain selection -> per-domain skill\n * selection (build step) -> source customization -> confirmation.\n */\nexport type WizardState = {\n step: WizardStep;\n\n approach: \"stack\" | \"scratch\" | null;\n selectedStackId: string | null;\n stackAction: \"defaults\" | \"customize\" | null;\n\n selectedDomains: Domain[];\n\n currentDomainIndex: number;\n domainSelections: DomainSelections;\n\n showDescriptions: boolean;\n expertMode: boolean;\n\n installMode: \"plugin\" | \"local\";\n\n sourceSelections: Partial<Record<SkillId, string>>;\n customizeSources: boolean;\n\n showSettings: boolean;\n showHelp: boolean;\n enabledSources: Record<string, boolean>;\n\n boundSkills: BoundSkill[];\n\n history: WizardStep[];\n\n /**\n * Navigate to a wizard step, pushing the current step onto history.\n * @param step - Target step to navigate to\n *\n * Side effects: sets `step`, appends previous step to `history`\n */\n setStep: (step: WizardStep) => void;\n /**\n * Set the wizard approach (stack-based or build-from-scratch).\n * @param approach - \"stack\" to use a pre-built template, \"scratch\" to select skills manually\n *\n * Side effects: sets `approach`\n */\n setApproach: (approach: \"stack\" | \"scratch\") => void;\n /**\n * Select a stack by ID, or null to deselect.\n * @param stackId - Stack identifier from suggestedStacks, or null to clear\n *\n * Side effects: sets `selectedStackId`\n */\n selectStack: (stackId: string | null) => void;\n /**\n * Set how to apply the selected stack.\n * @param action - \"defaults\" to use stack as-is, \"customize\" to enter the build step\n *\n * Side effects: sets `stackAction`\n */\n setStackAction: (action: \"defaults\" | \"customize\") => void;\n /**\n * Pre-populate domainSelections from a stack's agent-to-skill mappings.\n *\n * Iterates all agents in the stack, resolving each subcategory's skill assignments\n * to the appropriate domain. Enables all domains and deduplicates skill IDs.\n *\n * @param stack - Stack definition with agent-level skill assignments\n * @param stack.agents - Record of agent name to `{ subcategory: SkillAssignment[] }` mappings\n * @param categories - Category definitions used to resolve subcategory -> domain mapping\n *\n * Side effects: sets `domainSelections`, sets `selectedDomains` to ALL_DOMAINS\n */\n populateFromStack: (\n stack: { agents: Record<string, Partial<Record<Subcategory, SkillAssignment[]>>> },\n categories: Partial<Record<Subcategory, { domain?: Domain }>>,\n ) => void;\n /**\n * Pre-populate domainSelections from a flat list of installed skill IDs.\n *\n * Used by `cc edit` to restore wizard state from existing project config.\n * Looks up each skill's category and domain, warns for unresolvable skills.\n *\n * @param skillIds - Flat array of currently installed skill IDs\n * @param skills - Skill lookup providing category and displayName per skill ID\n * @param categories - Category definitions used to resolve subcategory -> domain mapping\n *\n * Side effects: sets `domainSelections`, sets `selectedDomains` to ALL_DOMAINS\n */\n populateFromSkillIds: (\n skillIds: SkillId[],\n skills: Partial<Record<SkillId, { category: string; displayName?: string }>>,\n categories: Partial<Record<Subcategory, { domain?: Domain }>>,\n ) => void;\n /**\n * Toggle a domain on or off in the selectedDomains list.\n * @param domain - Domain to toggle\n *\n * Side effects: adds or removes from `selectedDomains`\n */\n toggleDomain: (domain: Domain) => void;\n /**\n * Toggle a skill selection within a domain's subcategory.\n *\n * When exclusive is true (radio behavior), selecting a new skill replaces any\n * existing selection in that subcategory. When false (checkbox behavior),\n * the skill is added to or removed from the selection array.\n *\n * @param domain - Domain containing the subcategory\n * @param subcategory - Subcategory within the domain\n * @param technology - Skill ID to toggle\n * @param exclusive - If true, only one skill can be selected per subcategory (radio)\n *\n * Side effects: updates `domainSelections[domain][subcategory]`\n */\n toggleTechnology: (\n domain: Domain,\n subcategory: Subcategory,\n technology: SkillId,\n exclusive: boolean,\n ) => void;\n /**\n * Advance to the next domain in the build step.\n * @returns true if advanced, false if already at the last domain\n *\n * Side effects: increments `currentDomainIndex`\n */\n nextDomain: () => boolean;\n /**\n * Go back to the previous domain in the build step.\n * @returns true if moved back, false if already at the first domain\n *\n * Side effects: decrements `currentDomainIndex`\n */\n prevDomain: () => boolean;\n /** Toggle skill description visibility in the build step grid. */\n toggleShowDescriptions: () => void;\n /** Toggle expert mode (shows advanced/niche skills in the build step). */\n toggleExpertMode: () => void;\n /** Toggle between \"plugin\" and \"local\" install modes. */\n toggleInstallMode: () => void;\n /**\n * Set which source provides a specific skill.\n * @param skillId - Skill to configure the source for\n * @param sourceId - Source identifier (e.g., \"public\", \"local\", marketplace name)\n *\n * Side effects: updates `sourceSelections[skillId]`. No-op with warning if either param is empty.\n */\n setSourceSelection: (skillId: SkillId, sourceId: string) => void;\n /**\n * Enable or disable source customization on the sources step.\n * @param customize - true to show per-skill source pickers\n *\n * Side effects: sets `customizeSources`\n */\n setCustomizeSources: (customize: boolean) => void;\n /** Toggle the settings overlay (source management). */\n toggleSettings: () => void;\n /** Toggle the help overlay (hotkey reference). */\n toggleHelp: () => void;\n /**\n * Replace the full set of enabled/disabled sources.\n * @param sources - Record of source name to enabled boolean. Empty-string keys are filtered out.\n *\n * Side effects: sets `enabledSources`\n */\n setEnabledSources: (sources: Record<string, boolean>) => void;\n /**\n * Add a bound skill from search to the wizard's bound skills list.\n * Duplicates (same id + sourceUrl) are silently skipped with a warning.\n *\n * @param skill - Bound skill to add (foreign skill tied to a subcategory alias)\n *\n * Side effects: appends to `boundSkills`\n */\n bindSkill: (skill: BoundSkill) => void;\n /**\n * Navigate to the previous wizard step using the history stack.\n * Falls back to \"approach\" if history is empty.\n *\n * Side effects: pops from `history`, sets `step` to the popped value\n */\n goBack: () => void;\n /** Reset all wizard state to initial values. */\n reset: () => void;\n\n /**\n * Collect all selected skill IDs across all domains and subcategories.\n * @returns Flat array of every selected SkillId (may contain duplicates if shared across domains)\n */\n getAllSelectedTechnologies: () => SkillId[];\n /**\n * Group selected skill IDs by domain.\n * @returns Partial record mapping each domain with selections to its skill ID array\n */\n getSelectedTechnologiesPerDomain: () => Partial<Record<Domain, SkillId[]>>;\n /**\n * Get the domain currently visible in the build step.\n * @returns The domain at currentDomainIndex, or null if no domains are selected\n */\n getCurrentDomain: () => Domain | null;\n /** Returns the foundational methodology skills that are always preselected (DEFAULT_PRESELECTED_SKILLS). */\n getDefaultMethodologySkills: () => SkillId[];\n /**\n * Count total selected technologies across all domains.\n * @returns Number of selected skill IDs\n */\n getTechnologyCount: () => number;\n /**\n * Compute which wizard steps are completed and which are skipped.\n * Used by WizardTabs to render step progress indicators.\n * @returns Object with completedSteps and skippedSteps string arrays\n */\n getStepProgress: () => { completedSteps: string[]; skippedSteps: string[] };\n /** @returns true if there is a next domain after the current one */\n canGoToNextDomain: () => boolean;\n /** @returns true if there is a previous domain before the current one */\n canGoToPreviousDomain: () => boolean;\n /**\n * Find the parent domain for a sub-domain (e.g., web-extras -> web).\n * @param domain - Domain to look up\n * @param matrix - Merged skills matrix containing category definitions with parent_domain\n * @returns The parent domain, or undefined if the domain has no parent\n */\n getParentDomain: (domain: Domain, matrix: MergedSkillsMatrix) => Domain | undefined;\n /**\n * Get the current selections of a domain's parent domain.\n * Used for framework-first filtering: web-extras inherits web's framework selections.\n *\n * @param domain - Sub-domain to find parent selections for\n * @param matrix - Merged skills matrix containing category definitions\n * @returns The parent domain's SubcategorySelections, or undefined if no parent\n */\n getParentDomainSelections: (\n domain: Domain,\n matrix: MergedSkillsMatrix,\n ) => SubcategorySelections | undefined;\n /**\n * Build the source selection rows for the sources step UI.\n *\n * For each selected technology, resolves the canonical skill ID, looks up available\n * sources from the matrix, merges in any bound skills from search, and determines\n * which source is currently selected. Sources are sorted: local first, then public,\n * then private/other.\n *\n * @param matrix - Merged skills matrix with resolved skills and their available sources\n * @returns Array of row objects, one per selected technology, each containing:\n * - `skillId` - Canonical resolved skill ID\n * - `displayName` - Human-readable skill alias\n * - `alias` - Same as displayName (for backward compatibility)\n * - `options` - Available sources with selection state and install status\n */\n buildSourceRows: (matrix: MergedSkillsMatrix) => {\n skillId: SkillId;\n displayName: SkillAlias;\n alias: SkillAlias;\n options: { id: string; label: string; selected: boolean; installed: boolean }[];\n }[];\n};\n\nconst createInitialState = () => ({\n step: \"approach\" as WizardStep,\n approach: null as \"stack\" | \"scratch\" | null,\n selectedStackId: null as string | null,\n stackAction: null as \"defaults\" | \"customize\" | null,\n selectedDomains: [] as Domain[],\n currentDomainIndex: 0,\n domainSelections: {} as DomainSelections,\n showDescriptions: false,\n expertMode: false,\n installMode: \"local\" as \"plugin\" | \"local\",\n sourceSelections: {} as Partial<Record<SkillId, string>>,\n customizeSources: false,\n showSettings: false,\n showHelp: false,\n enabledSources: {} as Record<string, boolean>,\n boundSkills: [] as BoundSkill[],\n history: [] as WizardStep[],\n});\n\nexport const useWizardStore = create<WizardState>((set, get) => ({\n ...createInitialState(),\n\n setStep: (step) =>\n set((state) => ({\n step,\n history: [...state.history, state.step],\n })),\n\n setApproach: (approach) => set({ approach }),\n\n selectStack: (stackId) => set({ selectedStackId: stackId }),\n\n setStackAction: (action) => set({ stackAction: action }),\n\n populateFromStack: (stack, categories) =>\n set(() => {\n const domainSelections: DomainSelections = {};\n const domains = new Set<Domain>();\n\n for (const agentConfig of Object.values(stack.agents)) {\n for (const [subcat, assignments] of typedEntries<Subcategory, SkillAssignment[]>(\n agentConfig,\n )) {\n const category = categories[subcat];\n const domain = category?.domain;\n\n if (!domain || !assignments) {\n continue;\n }\n\n domains.add(domain);\n\n if (!domainSelections[domain]) {\n domainSelections[domain] = {};\n }\n\n if (!domainSelections[domain][subcat]) {\n domainSelections[domain][subcat] = [];\n }\n\n for (const assignment of assignments) {\n if (!domainSelections[domain][subcat].includes(assignment.id)) {\n domainSelections[domain][subcat].push(assignment.id);\n }\n }\n }\n }\n\n return {\n domainSelections,\n selectedDomains: ALL_DOMAINS,\n };\n }),\n\n populateFromSkillIds: (skillIds, skills, categories) =>\n set(() => {\n const domainSelections: DomainSelections = {};\n let skippedCount = 0;\n\n for (const skillId of skillIds) {\n const resolved = resolveSkillForPopulation(skillId, skills, categories);\n if (!resolved) {\n skippedCount++;\n continue;\n }\n\n const { domain, subcat, techId } = resolved;\n if (!domainSelections[domain]) domainSelections[domain] = {};\n if (!domainSelections[domain][subcat]) domainSelections[domain][subcat] = [];\n\n if (!domainSelections[domain][subcat].includes(techId)) {\n domainSelections[domain][subcat].push(techId);\n }\n }\n\n if (skippedCount > 0) {\n warn(`${skippedCount} installed skill(s) could not be resolved and were skipped`);\n }\n\n return { domainSelections, selectedDomains: ALL_DOMAINS };\n }),\n\n toggleDomain: (domain) =>\n set((state) => {\n const isSelected = state.selectedDomains.includes(domain);\n return {\n selectedDomains: isSelected\n ? state.selectedDomains.filter((d) => d !== domain)\n : [...state.selectedDomains, domain],\n };\n }),\n\n toggleTechnology: (domain, subcategory, technology, exclusive) =>\n set((state) => {\n const currentSelections = state.domainSelections[domain]?.[subcategory] || [];\n const isSelected = currentSelections.includes(technology);\n\n let newSelections: SkillId[];\n if (exclusive) {\n newSelections = isSelected ? [] : [technology];\n } else {\n newSelections = isSelected\n ? currentSelections.filter((t) => t !== technology)\n : [...currentSelections, technology];\n }\n\n return {\n domainSelections: {\n ...state.domainSelections,\n [domain]: {\n ...state.domainSelections[domain],\n [subcategory]: newSelections,\n },\n },\n };\n }),\n\n nextDomain: () => {\n const state = get();\n if (state.currentDomainIndex < state.selectedDomains.length - 1) {\n set({\n currentDomainIndex: state.currentDomainIndex + 1,\n });\n return true;\n }\n return false;\n },\n\n prevDomain: () => {\n const state = get();\n if (state.currentDomainIndex > 0) {\n set({\n currentDomainIndex: state.currentDomainIndex - 1,\n });\n return true;\n }\n return false;\n },\n\n toggleShowDescriptions: () => set((state) => ({ showDescriptions: !state.showDescriptions })),\n\n toggleExpertMode: () => set((state) => ({ expertMode: !state.expertMode })),\n\n toggleInstallMode: () =>\n set((state) => ({\n installMode: state.installMode === \"plugin\" ? \"local\" : \"plugin\",\n })),\n\n setSourceSelection: (skillId, sourceId) =>\n set((state) => {\n if (!skillId) {\n warn(\"Ignoring setSourceSelection call with empty skillId\");\n return state;\n }\n if (!sourceId) {\n warn(`Ignoring setSourceSelection call with empty sourceId for skill '${skillId}'`);\n return state;\n }\n return {\n sourceSelections: { ...state.sourceSelections, [skillId]: sourceId },\n };\n }),\n\n setCustomizeSources: (customize) => set({ customizeSources: customize }),\n\n toggleSettings: () => set((state) => ({ showSettings: !state.showSettings })),\n\n toggleHelp: () => set((state) => ({ showHelp: !state.showHelp })),\n\n setEnabledSources: (sources) => {\n const invalidKeys = Object.keys(sources).filter((key) => !key.trim());\n if (invalidKeys.length > 0) {\n warn(\"Ignoring setEnabledSources call with empty source name(s)\");\n }\n const validSources = Object.fromEntries(Object.entries(sources).filter(([key]) => key.trim()));\n return set({ enabledSources: validSources });\n },\n\n bindSkill: (skill) =>\n set((state) => {\n const exists = state.boundSkills.some(\n (b) => b.id === skill.id && b.sourceUrl === skill.sourceUrl,\n );\n if (exists) {\n warn(`Skill '${skill.id}' from '${skill.sourceUrl}' is already bound — skipping duplicate`);\n return state;\n }\n return { boundSkills: [...state.boundSkills, skill] };\n }),\n\n goBack: () =>\n set((state) => {\n const history = [...state.history];\n const previousStep = history.pop();\n return {\n step: previousStep || \"approach\",\n history,\n };\n }),\n\n reset: () => set(createInitialState()),\n\n getAllSelectedTechnologies: () => {\n const state = get();\n const technologies: SkillId[] = [];\n for (const domain of typedKeys<Domain>(state.domainSelections)) {\n const domainSel = state.domainSelections[domain];\n if (!domainSel) continue;\n for (const subcategory of typedKeys<Subcategory>(domainSel)) {\n const techs = domainSel[subcategory];\n if (techs) technologies.push(...techs);\n }\n }\n return technologies;\n },\n\n getSelectedTechnologiesPerDomain: () => {\n const state = get();\n const result: Partial<Record<Domain, SkillId[]>> = {};\n for (const domain of typedKeys<Domain>(state.domainSelections)) {\n const domainSel = state.domainSelections[domain];\n if (!domainSel) continue;\n const techs: SkillId[] = [];\n for (const subcategory of typedKeys<Subcategory>(domainSel)) {\n const subTechs = domainSel[subcategory];\n if (subTechs) techs.push(...subTechs);\n }\n if (techs.length > 0) {\n result[domain] = techs;\n }\n }\n return result;\n },\n\n getCurrentDomain: () => {\n const state = get();\n return state.selectedDomains[state.currentDomainIndex] || null;\n },\n\n getDefaultMethodologySkills: () => {\n return [...DEFAULT_PRESELECTED_SKILLS];\n },\n\n getTechnologyCount: () => {\n return get().getAllSelectedTechnologies().length;\n },\n\n getStepProgress: () => {\n const state = get();\n const completed: string[] = [];\n const skipped: string[] = [];\n\n if (state.step !== \"approach\") {\n completed.push(\"approach\");\n }\n\n if (state.step !== \"approach\" && state.step !== \"stack\") {\n completed.push(\"stack\");\n }\n\n if (state.approach === \"stack\" && state.selectedStackId && state.stackAction === \"defaults\") {\n skipped.push(\"build\");\n skipped.push(\"sources\");\n } else if (state.step === \"confirm\") {\n completed.push(\"build\");\n completed.push(\"sources\");\n } else if (state.step === \"sources\") {\n completed.push(\"build\");\n }\n\n return { completedSteps: completed, skippedSteps: skipped };\n },\n\n canGoToNextDomain: () => {\n const state = get();\n return state.currentDomainIndex < state.selectedDomains.length - 1;\n },\n\n canGoToPreviousDomain: () => {\n const state = get();\n return state.currentDomainIndex > 0;\n },\n\n getParentDomain: (domain, matrix) => {\n const cat = Object.values(matrix.categories).find(\n (c) => c.domain === domain && c.parent_domain,\n );\n return cat?.parent_domain;\n },\n\n getParentDomainSelections: (domain, matrix) => {\n const state = get();\n const parentDomain = get().getParentDomain(domain, matrix);\n if (!parentDomain) return undefined;\n return state.domainSelections[parentDomain];\n },\n\n buildSourceRows: (matrix) => {\n const state = get();\n const selectedTechnologies = get().getAllSelectedTechnologies();\n const { sourceSelections, boundSkills } = state;\n\n return selectedTechnologies.map((tech) => {\n const skillId = resolveAlias(tech, matrix);\n const skill = matrix.skills[skillId];\n const selectedSource =\n sourceSelections[skillId] || skill?.activeSource?.name || DEFAULT_SOURCE_ID;\n const alias = getSkillAlias(skillId, matrix);\n\n const sortedSources = [...(skill?.availableSources || [])].sort(\n (a, b) =>\n (SOURCE_SORT_ORDER[a.type] ?? DEFAULT_SORT_PRIORITY) -\n (SOURCE_SORT_ORDER[b.type] ?? DEFAULT_SORT_PRIORITY),\n );\n\n const options =\n sortedSources.length > 0\n ? sortedSources.map((source) => ({\n id: source.name,\n label: formatSourceLabel({\n name: source.name,\n version: source.version,\n installed: source.installed,\n }),\n selected: selectedSource === source.name,\n installed: source.installed,\n }))\n : [\n {\n id: DEFAULT_SOURCE_ID,\n label: DEFAULT_SOURCE_LABEL,\n selected: selectedSource === DEFAULT_SOURCE_ID,\n installed: false,\n },\n ];\n\n options.push(...buildBoundSkillOptions(boundSkills, alias, selectedSource));\n\n return { skillId, displayName: alias, alias, options };\n });\n },\n}));\n"],"mappings":"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","names":[]}
|
|
@@ -1,16 +1,158 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
useFocusedListItem
|
|
4
|
+
} from "./chunk-WPED6CL3.js";
|
|
5
|
+
import {
|
|
6
|
+
CLI_COLORS,
|
|
7
|
+
UI_SYMBOLS
|
|
8
|
+
} from "./chunk-IFODQTCX.js";
|
|
2
9
|
import {
|
|
3
10
|
init_esm_shims
|
|
4
11
|
} from "./chunk-AWKZ5BDL.js";
|
|
5
12
|
|
|
6
13
|
// src/cli/components/wizard/category-grid.tsx
|
|
7
14
|
init_esm_shims();
|
|
8
|
-
import { useCallback,
|
|
9
|
-
import { Box, Text
|
|
15
|
+
import { useCallback as useCallback2, useMemo } from "react";
|
|
16
|
+
import { Box, Text } from "ink";
|
|
10
17
|
import { sortBy } from "remeda";
|
|
18
|
+
|
|
19
|
+
// src/cli/components/hooks/use-category-grid-input.ts
|
|
20
|
+
init_esm_shims();
|
|
21
|
+
import { useCallback, useEffect } from "react";
|
|
22
|
+
import { useInput } from "ink";
|
|
23
|
+
var FRAMEWORK_CATEGORY_ID = "framework";
|
|
24
|
+
var isSectionLocked = (categoryId, categories) => {
|
|
25
|
+
if (categoryId === FRAMEWORK_CATEGORY_ID) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
const frameworkCategory = categories.find((cat) => cat.id === FRAMEWORK_CATEGORY_ID);
|
|
29
|
+
if (!frameworkCategory) return false;
|
|
30
|
+
return !frameworkCategory.options.some((opt) => opt.selected);
|
|
31
|
+
};
|
|
32
|
+
var findValidStartColumn = (options) => {
|
|
33
|
+
for (let i = 0; i < options.length; i++) {
|
|
34
|
+
if (options[i] && options[i].state !== "disabled") {
|
|
35
|
+
return i;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return 0;
|
|
39
|
+
};
|
|
40
|
+
var findNextUnlockedIndex = (processed, currentIndex, allCategories) => {
|
|
41
|
+
const length = processed.length;
|
|
42
|
+
if (length === 0) return currentIndex;
|
|
43
|
+
let index = currentIndex;
|
|
44
|
+
let attempts = 0;
|
|
45
|
+
while (attempts < length) {
|
|
46
|
+
index += 1;
|
|
47
|
+
if (index >= length) index = 0;
|
|
48
|
+
const category = processed[index];
|
|
49
|
+
if (category && !isSectionLocked(category.id, allCategories)) {
|
|
50
|
+
return index;
|
|
51
|
+
}
|
|
52
|
+
attempts++;
|
|
53
|
+
}
|
|
54
|
+
return currentIndex;
|
|
55
|
+
};
|
|
56
|
+
function useCategoryGridInput({
|
|
57
|
+
processedCategories,
|
|
58
|
+
categories,
|
|
59
|
+
focusedRow,
|
|
60
|
+
focusedCol,
|
|
61
|
+
setFocused,
|
|
62
|
+
moveFocus,
|
|
63
|
+
onToggle,
|
|
64
|
+
onToggleDescriptions
|
|
65
|
+
}) {
|
|
66
|
+
const currentRow = processedCategories[focusedRow];
|
|
67
|
+
const currentOptions = currentRow?.sortedOptions || [];
|
|
68
|
+
const currentLocked = currentRow ? isSectionLocked(currentRow.id, categories) : false;
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (!currentRow) return;
|
|
71
|
+
const maxCol = currentOptions.length - 1;
|
|
72
|
+
if (focusedCol > maxCol) {
|
|
73
|
+
const newCol = Math.max(0, maxCol);
|
|
74
|
+
setFocused(focusedRow, newCol);
|
|
75
|
+
} else if (currentOptions[focusedCol]?.state === "disabled") {
|
|
76
|
+
const validCol = findValidStartColumn(currentOptions);
|
|
77
|
+
if (validCol !== focusedCol) {
|
|
78
|
+
setFocused(focusedRow, validCol);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}, [focusedRow, currentOptions, focusedCol, setFocused, currentRow]);
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
if (currentRow && currentLocked) {
|
|
84
|
+
const nextUnlocked = findNextUnlockedIndex(processedCategories, focusedRow, categories);
|
|
85
|
+
if (nextUnlocked !== focusedRow) {
|
|
86
|
+
const newRowOptions = processedCategories[nextUnlocked]?.sortedOptions || [];
|
|
87
|
+
const newCol = findValidStartColumn(newRowOptions);
|
|
88
|
+
setFocused(nextUnlocked, newCol);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}, [currentRow, currentLocked, focusedRow, processedCategories, categories, setFocused]);
|
|
92
|
+
useInput(
|
|
93
|
+
useCallback(
|
|
94
|
+
(input, key) => {
|
|
95
|
+
if (key.tab && key.shift) {
|
|
96
|
+
onToggleDescriptions();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (key.tab && !key.shift) {
|
|
100
|
+
const nextSection = findNextUnlockedIndex(processedCategories, focusedRow, categories);
|
|
101
|
+
if (nextSection !== focusedRow) {
|
|
102
|
+
const newRowOptions = processedCategories[nextSection]?.sortedOptions || [];
|
|
103
|
+
const newCol = findValidStartColumn(newRowOptions);
|
|
104
|
+
setFocused(nextSection, newCol);
|
|
105
|
+
}
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (input === "d" || input === "D") {
|
|
109
|
+
onToggleDescriptions();
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (input === " ") {
|
|
113
|
+
if (currentLocked) return;
|
|
114
|
+
const currentOption = currentOptions[focusedCol];
|
|
115
|
+
if (currentOption && currentOption.state !== "disabled") {
|
|
116
|
+
onToggle(currentRow.id, currentOption.id);
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const isLeft = key.leftArrow || input === "h";
|
|
121
|
+
const isRight = key.rightArrow || input === "l";
|
|
122
|
+
const isUp = key.upArrow || input === "k";
|
|
123
|
+
const isDown = key.downArrow || input === "j";
|
|
124
|
+
if (isLeft) {
|
|
125
|
+
if (currentLocked) return;
|
|
126
|
+
moveFocus("left");
|
|
127
|
+
} else if (isRight) {
|
|
128
|
+
if (currentLocked) return;
|
|
129
|
+
moveFocus("right");
|
|
130
|
+
} else if (isUp) {
|
|
131
|
+
moveFocus("up");
|
|
132
|
+
} else if (isDown) {
|
|
133
|
+
moveFocus("down");
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
[
|
|
137
|
+
focusedRow,
|
|
138
|
+
focusedCol,
|
|
139
|
+
currentOptions,
|
|
140
|
+
currentRow,
|
|
141
|
+
currentLocked,
|
|
142
|
+
processedCategories,
|
|
143
|
+
categories,
|
|
144
|
+
onToggle,
|
|
145
|
+
onToggleDescriptions,
|
|
146
|
+
setFocused,
|
|
147
|
+
moveFocus
|
|
148
|
+
]
|
|
149
|
+
)
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// src/cli/components/wizard/category-grid.tsx
|
|
11
154
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
12
155
|
var SYMBOL_REQUIRED = "*";
|
|
13
|
-
var FRAMEWORK_CATEGORY_ID = "framework";
|
|
14
156
|
var sortOptions = (options, expertMode) => {
|
|
15
157
|
if (expertMode) {
|
|
16
158
|
return options;
|
|
@@ -44,70 +186,51 @@ var findNextValidOption = (options, currentIndex, direction, wrap = true) => {
|
|
|
44
186
|
}
|
|
45
187
|
return currentIndex;
|
|
46
188
|
};
|
|
47
|
-
var
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
return 0;
|
|
54
|
-
};
|
|
55
|
-
var isSectionLocked = (categoryId, categories) => {
|
|
56
|
-
if (categoryId === FRAMEWORK_CATEGORY_ID) {
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
const frameworkCategory = categories.find((cat) => cat.id === FRAMEWORK_CATEGORY_ID);
|
|
60
|
-
if (!frameworkCategory) return false;
|
|
61
|
-
return !frameworkCategory.options.some((opt) => opt.selected);
|
|
189
|
+
var getStateSuffix = (state, isLocked) => {
|
|
190
|
+
if (isLocked || state === "disabled") return "(disabled)";
|
|
191
|
+
if (state === "recommended") return "(recommended)";
|
|
192
|
+
if (state === "discouraged") return "(discouraged)";
|
|
193
|
+
return null;
|
|
62
194
|
};
|
|
63
|
-
var
|
|
64
|
-
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
while (attempts < length) {
|
|
69
|
-
index += direction;
|
|
70
|
-
if (index < 0) index = length - 1;
|
|
71
|
-
if (index >= length) index = 0;
|
|
72
|
-
const category = categories[index];
|
|
73
|
-
if (category && !isSectionLocked(category.id, allCategories)) {
|
|
74
|
-
return index;
|
|
75
|
-
}
|
|
76
|
-
attempts++;
|
|
77
|
-
}
|
|
78
|
-
return currentIndex;
|
|
195
|
+
var getStateSymbol = (option, isLocked) => {
|
|
196
|
+
if (isLocked || option.state === "disabled") return UI_SYMBOLS.DISABLED;
|
|
197
|
+
if (option.selected) return UI_SYMBOLS.SELECTED;
|
|
198
|
+
if (option.state === "discouraged") return UI_SYMBOLS.DISCOURAGED;
|
|
199
|
+
return UI_SYMBOLS.UNSELECTED;
|
|
79
200
|
};
|
|
80
201
|
var SkillTag = ({ option, isFocused, isLocked }) => {
|
|
81
202
|
const getColor = () => {
|
|
82
203
|
if (isLocked || option.state === "disabled") {
|
|
83
204
|
return {
|
|
84
|
-
text:
|
|
85
|
-
border:
|
|
205
|
+
text: CLI_COLORS.NEUTRAL,
|
|
206
|
+
border: CLI_COLORS.NEUTRAL
|
|
86
207
|
};
|
|
87
208
|
}
|
|
88
209
|
if (option.selected) {
|
|
89
210
|
return {
|
|
90
|
-
text:
|
|
91
|
-
border:
|
|
211
|
+
text: CLI_COLORS.PRIMARY,
|
|
212
|
+
border: CLI_COLORS.PRIMARY
|
|
92
213
|
};
|
|
93
214
|
}
|
|
94
215
|
if (option.state === "recommended") {
|
|
95
216
|
return {
|
|
96
|
-
text:
|
|
97
|
-
border:
|
|
217
|
+
text: CLI_COLORS.UNFOCUSED,
|
|
218
|
+
border: CLI_COLORS.NEUTRAL
|
|
98
219
|
};
|
|
99
220
|
}
|
|
100
221
|
if (option.state === "discouraged") {
|
|
101
222
|
return {
|
|
102
|
-
text:
|
|
103
|
-
border:
|
|
223
|
+
text: CLI_COLORS.WARNING,
|
|
224
|
+
border: CLI_COLORS.WARNING
|
|
104
225
|
};
|
|
105
226
|
}
|
|
106
227
|
return void 0;
|
|
107
228
|
};
|
|
108
229
|
const isBold = isFocused || option.selected;
|
|
109
230
|
const isDimmed = isLocked || option.state === "disabled";
|
|
110
|
-
const focusBorderColor = option.selected ?
|
|
231
|
+
const focusBorderColor = option.selected ? CLI_COLORS.PRIMARY : CLI_COLORS.UNFOCUSED;
|
|
232
|
+
const stateSuffix = getStateSuffix(option.state, isLocked);
|
|
233
|
+
const stateSymbol = getStateSymbol(option, isLocked);
|
|
111
234
|
return /* @__PURE__ */ jsx(
|
|
112
235
|
Box,
|
|
113
236
|
{
|
|
@@ -118,11 +241,22 @@ var SkillTag = ({ option, isFocused, isLocked }) => {
|
|
|
118
241
|
children: /* @__PURE__ */ jsxs(Text, { color: getColor()?.text, bold: isBold, dimColor: false, children: [
|
|
119
242
|
" ",
|
|
120
243
|
option.local && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
121
|
-
/* @__PURE__ */ jsx(Text, { backgroundColor:
|
|
244
|
+
/* @__PURE__ */ jsx(Text, { backgroundColor: CLI_COLORS.NEUTRAL, children: " L " }),
|
|
245
|
+
" "
|
|
246
|
+
] }),
|
|
247
|
+
option.installed && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
248
|
+
UI_SYMBOLS.SELECTED,
|
|
249
|
+
" "
|
|
250
|
+
] }),
|
|
251
|
+
!option.installed && /* @__PURE__ */ jsxs(Text, { dimColor: isDimmed, children: [
|
|
252
|
+
stateSymbol,
|
|
122
253
|
" "
|
|
123
254
|
] }),
|
|
124
|
-
option.installed && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2713 " }),
|
|
125
255
|
option.label,
|
|
256
|
+
stateSuffix && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
257
|
+
" ",
|
|
258
|
+
stateSuffix
|
|
259
|
+
] }),
|
|
126
260
|
" "
|
|
127
261
|
] })
|
|
128
262
|
}
|
|
@@ -139,7 +273,7 @@ var CategorySection = ({
|
|
|
139
273
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
140
274
|
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
|
|
141
275
|
/* @__PURE__ */ jsx(Text, { dimColor: isLocked, children: category.displayName }),
|
|
142
|
-
category.required && /* @__PURE__ */ jsxs(Text, { color: isLocked ?
|
|
276
|
+
category.required && /* @__PURE__ */ jsxs(Text, { color: isLocked ? CLI_COLORS.NEUTRAL : CLI_COLORS.ERROR, dimColor: isLocked, children: [
|
|
143
277
|
" ",
|
|
144
278
|
SYMBOL_REQUIRED
|
|
145
279
|
] })
|
|
@@ -159,121 +293,74 @@ var CategorySection = ({
|
|
|
159
293
|
};
|
|
160
294
|
var CategoryGrid = ({
|
|
161
295
|
categories,
|
|
162
|
-
focusedRow,
|
|
163
|
-
focusedCol,
|
|
164
296
|
showDescriptions,
|
|
165
297
|
expertMode,
|
|
166
298
|
onToggle,
|
|
167
|
-
|
|
168
|
-
|
|
299
|
+
onToggleDescriptions,
|
|
300
|
+
defaultFocusedRow = 0,
|
|
301
|
+
defaultFocusedCol = 0,
|
|
302
|
+
onFocusChange
|
|
169
303
|
}) => {
|
|
170
|
-
const processedCategories =
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
304
|
+
const processedCategories = useMemo(
|
|
305
|
+
() => categories.map((category) => ({
|
|
306
|
+
...category,
|
|
307
|
+
sortedOptions: sortOptions(category.options, expertMode)
|
|
308
|
+
})),
|
|
309
|
+
[categories, expertMode]
|
|
310
|
+
);
|
|
311
|
+
const getColCount = useCallback2(
|
|
312
|
+
(row) => processedCategories[row]?.sortedOptions.length ?? 0,
|
|
313
|
+
[processedCategories]
|
|
314
|
+
);
|
|
315
|
+
const isRowLocked = useCallback2(
|
|
316
|
+
(row) => {
|
|
317
|
+
const cat = processedCategories[row];
|
|
318
|
+
return cat ? isSectionLocked(cat.id, categories) : false;
|
|
319
|
+
},
|
|
320
|
+
[processedCategories, categories]
|
|
321
|
+
);
|
|
322
|
+
const findValidCol = useCallback2(
|
|
323
|
+
(row, currentCol, direction) => {
|
|
324
|
+
const options = processedCategories[row]?.sortedOptions || [];
|
|
325
|
+
const catId = processedCategories[row]?.id;
|
|
326
|
+
if (catId && isSectionLocked(catId, categories)) return currentCol;
|
|
327
|
+
return findNextValidOption(options, currentCol, direction, true);
|
|
328
|
+
},
|
|
329
|
+
[processedCategories, categories]
|
|
330
|
+
);
|
|
331
|
+
const adjustCol = useCallback2(
|
|
332
|
+
(row, clampedCol) => {
|
|
333
|
+
const options = processedCategories[row]?.sortedOptions || [];
|
|
334
|
+
if (options[clampedCol]?.state === "disabled") {
|
|
335
|
+
return findValidStartColumn(options);
|
|
197
336
|
}
|
|
337
|
+
return clampedCol;
|
|
338
|
+
},
|
|
339
|
+
[processedCategories]
|
|
340
|
+
);
|
|
341
|
+
const { focusedRow, focusedCol, setFocused, moveFocus } = useFocusedListItem(
|
|
342
|
+
processedCategories.length,
|
|
343
|
+
getColCount,
|
|
344
|
+
{
|
|
345
|
+
wrap: true,
|
|
346
|
+
isRowLocked,
|
|
347
|
+
findValidCol,
|
|
348
|
+
adjustCol,
|
|
349
|
+
onChange: onFocusChange,
|
|
350
|
+
initialRow: defaultFocusedRow,
|
|
351
|
+
initialCol: defaultFocusedCol
|
|
198
352
|
}
|
|
199
|
-
}, [currentRow, currentLocked, focusedRow, processedCategories, categories, onFocusChange]);
|
|
200
|
-
useInput(
|
|
201
|
-
useCallback(
|
|
202
|
-
(input, key) => {
|
|
203
|
-
if (key.tab && key.shift) {
|
|
204
|
-
onToggleDescriptions();
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
if (key.tab && !key.shift) {
|
|
208
|
-
const nextSection = findNextUnlockedSection(
|
|
209
|
-
processedCategories,
|
|
210
|
-
focusedRow,
|
|
211
|
-
1,
|
|
212
|
-
categories
|
|
213
|
-
);
|
|
214
|
-
if (nextSection !== focusedRow) {
|
|
215
|
-
const newRowOptions = processedCategories[nextSection]?.sortedOptions || [];
|
|
216
|
-
const newCol = findValidStartColumn(newRowOptions);
|
|
217
|
-
onFocusChange(nextSection, newCol);
|
|
218
|
-
}
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
if (input === "d" || input === "D") {
|
|
222
|
-
onToggleDescriptions();
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
if (input === " ") {
|
|
226
|
-
if (currentLocked) return;
|
|
227
|
-
const currentOption = currentOptions[focusedCol];
|
|
228
|
-
if (currentOption && currentOption.state !== "disabled") {
|
|
229
|
-
onToggle(currentRow.id, currentOption.id);
|
|
230
|
-
}
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
const isLeft = key.leftArrow || input === "h";
|
|
234
|
-
const isRight = key.rightArrow || input === "l";
|
|
235
|
-
const isUp = key.upArrow || input === "k";
|
|
236
|
-
const isDown = key.downArrow || input === "j";
|
|
237
|
-
if (isLeft) {
|
|
238
|
-
if (currentLocked) return;
|
|
239
|
-
const newCol = findNextValidOption(currentOptions, focusedCol, -1, true);
|
|
240
|
-
onFocusChange(focusedRow, newCol);
|
|
241
|
-
} else if (isRight) {
|
|
242
|
-
if (currentLocked) return;
|
|
243
|
-
const newCol = findNextValidOption(currentOptions, focusedCol, 1, true);
|
|
244
|
-
onFocusChange(focusedRow, newCol);
|
|
245
|
-
} else if (isUp) {
|
|
246
|
-
const newRow = findNextUnlockedSection(processedCategories, focusedRow, -1, categories);
|
|
247
|
-
const newRowOptions = processedCategories[newRow]?.sortedOptions || [];
|
|
248
|
-
let newCol = Math.min(focusedCol, newRowOptions.length - 1);
|
|
249
|
-
if (newRowOptions[newCol]?.state === "disabled") {
|
|
250
|
-
newCol = findValidStartColumn(newRowOptions);
|
|
251
|
-
}
|
|
252
|
-
onFocusChange(newRow, newCol);
|
|
253
|
-
} else if (isDown) {
|
|
254
|
-
const newRow = findNextUnlockedSection(processedCategories, focusedRow, 1, categories);
|
|
255
|
-
const newRowOptions = processedCategories[newRow]?.sortedOptions || [];
|
|
256
|
-
let newCol = Math.min(focusedCol, newRowOptions.length - 1);
|
|
257
|
-
if (newRowOptions[newCol]?.state === "disabled") {
|
|
258
|
-
newCol = findValidStartColumn(newRowOptions);
|
|
259
|
-
}
|
|
260
|
-
onFocusChange(newRow, newCol);
|
|
261
|
-
}
|
|
262
|
-
},
|
|
263
|
-
[
|
|
264
|
-
focusedRow,
|
|
265
|
-
focusedCol,
|
|
266
|
-
currentOptions,
|
|
267
|
-
currentRow,
|
|
268
|
-
currentLocked,
|
|
269
|
-
processedCategories,
|
|
270
|
-
categories,
|
|
271
|
-
onToggle,
|
|
272
|
-
onFocusChange,
|
|
273
|
-
onToggleDescriptions
|
|
274
|
-
]
|
|
275
|
-
)
|
|
276
353
|
);
|
|
354
|
+
useCategoryGridInput({
|
|
355
|
+
processedCategories,
|
|
356
|
+
categories,
|
|
357
|
+
focusedRow,
|
|
358
|
+
focusedCol,
|
|
359
|
+
setFocused,
|
|
360
|
+
moveFocus,
|
|
361
|
+
onToggle,
|
|
362
|
+
onToggleDescriptions
|
|
363
|
+
});
|
|
277
364
|
if (categories.length === 0) {
|
|
278
365
|
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No categories to display." }) });
|
|
279
366
|
}
|
|
@@ -297,4 +384,4 @@ var CategoryGrid = ({
|
|
|
297
384
|
export {
|
|
298
385
|
CategoryGrid
|
|
299
386
|
};
|
|
300
|
-
//# sourceMappingURL=chunk-
|
|
387
|
+
//# sourceMappingURL=chunk-IRG52AN5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/components/wizard/category-grid.tsx","../src/cli/components/hooks/use-category-grid-input.ts"],"sourcesContent":["import React, { useCallback, useMemo } from \"react\";\n\nimport { Box, Text } from \"ink\";\nimport { sortBy } from \"remeda\";\n\nimport type { SkillId, Subcategory } from \"../../types/index.js\";\nimport { CLI_COLORS, UI_SYMBOLS } from \"../../consts.js\";\nimport {\n findValidStartColumn,\n isSectionLocked,\n useCategoryGridInput,\n} from \"../hooks/use-category-grid-input.js\";\nimport { useFocusedListItem } from \"../hooks/use-focused-list-item.js\";\n\nexport type OptionState = \"normal\" | \"recommended\" | \"discouraged\" | \"disabled\";\n\nexport type CategoryOption = {\n id: SkillId;\n label: string;\n state: OptionState;\n stateReason?: string;\n selected: boolean;\n local?: boolean;\n installed?: boolean;\n};\n\nexport type CategoryRow = {\n id: Subcategory;\n displayName: string;\n required: boolean;\n exclusive: boolean;\n options: CategoryOption[];\n};\n\nexport type CategoryGridProps = {\n categories: CategoryRow[];\n showDescriptions: boolean;\n expertMode: boolean;\n onToggle: (categoryId: Subcategory, technologyId: SkillId) => void;\n onToggleDescriptions: () => void;\n /** Optional initial focus row (default: 0). Use with React `key` to reset. */\n defaultFocusedRow?: number;\n /** Optional initial focus col (default: 0). Use with React `key` to reset. */\n defaultFocusedCol?: number;\n /** Optional callback fired whenever the focused position changes */\n onFocusChange?: (row: number, col: number) => void;\n};\n\nconst SYMBOL_REQUIRED = \"*\";\n\n// Recommended first, discouraged last. Expert mode preserves original order.\nconst sortOptions = (options: CategoryOption[], expertMode: boolean): CategoryOption[] => {\n if (expertMode) {\n return options;\n }\n\n const stateOrder: Record<OptionState, number> = {\n recommended: 0,\n normal: 1,\n discouraged: 2,\n disabled: 3,\n };\n\n return sortBy([...options], (o) => stateOrder[o.state]);\n};\n\nconst findNextValidOption = (\n options: CategoryOption[],\n currentIndex: number,\n direction: 1 | -1,\n wrap = true,\n): number => {\n const length = options.length;\n if (length === 0) return currentIndex;\n\n let index = currentIndex;\n let attempts = 0;\n\n while (attempts < length) {\n index += direction;\n\n if (wrap) {\n if (index < 0) index = length - 1;\n if (index >= length) index = 0;\n } else {\n if (index < 0) index = 0;\n if (index >= length) index = length - 1;\n }\n\n if (options[index] && options[index].state !== \"disabled\") {\n return index;\n }\n\n attempts++;\n }\n\n return currentIndex;\n};\n\ntype SkillTagProps = {\n option: CategoryOption;\n isFocused: boolean;\n isLocked: boolean;\n};\n\nconst getStateSuffix = (state: OptionState, isLocked: boolean): string | null => {\n if (isLocked || state === \"disabled\") return \"(disabled)\";\n if (state === \"recommended\") return \"(recommended)\";\n if (state === \"discouraged\") return \"(discouraged)\";\n return null;\n};\n\nconst getStateSymbol = (option: CategoryOption, isLocked: boolean): string => {\n if (isLocked || option.state === \"disabled\") return UI_SYMBOLS.DISABLED;\n if (option.selected) return UI_SYMBOLS.SELECTED;\n if (option.state === \"discouraged\") return UI_SYMBOLS.DISCOURAGED;\n return UI_SYMBOLS.UNSELECTED;\n};\n\nconst SkillTag: React.FC<SkillTagProps> = ({ option, isFocused, isLocked }) => {\n const getColor = (): { text: string; border: string } | undefined => {\n if (isLocked || option.state === \"disabled\") {\n return {\n text: CLI_COLORS.NEUTRAL,\n border: CLI_COLORS.NEUTRAL,\n };\n }\n if (option.selected) {\n return {\n text: CLI_COLORS.PRIMARY,\n border: CLI_COLORS.PRIMARY,\n };\n }\n if (option.state === \"recommended\") {\n return {\n text: CLI_COLORS.UNFOCUSED,\n border: CLI_COLORS.NEUTRAL,\n };\n }\n if (option.state === \"discouraged\") {\n return {\n text: CLI_COLORS.WARNING,\n border: CLI_COLORS.WARNING,\n };\n }\n return undefined;\n };\n\n const isBold = isFocused || option.selected;\n const isDimmed = isLocked || option.state === \"disabled\";\n const focusBorderColor = option.selected ? CLI_COLORS.PRIMARY : CLI_COLORS.UNFOCUSED;\n const stateSuffix = getStateSuffix(option.state, isLocked);\n const stateSymbol = getStateSymbol(option, isLocked);\n\n return (\n <Box\n marginRight={1}\n borderColor={isFocused ? focusBorderColor : getColor()?.border}\n borderStyle=\"single\"\n borderDimColor={isDimmed}\n >\n <Text color={getColor()?.text} bold={isBold} dimColor={false}>\n {\" \"}\n {option.local && (\n <>\n <Text backgroundColor={CLI_COLORS.NEUTRAL}> L </Text>{\" \"}\n </>\n )}\n {option.installed && <Text dimColor>{UI_SYMBOLS.SELECTED} </Text>}\n {!option.installed && <Text dimColor={isDimmed}>{stateSymbol} </Text>}\n {option.label}\n {stateSuffix && <Text dimColor> {stateSuffix}</Text>}{\" \"}\n </Text>\n </Box>\n );\n};\n\ntype CategorySectionProps = {\n category: CategoryRow;\n options: CategoryOption[];\n isLocked: boolean;\n isFocused: boolean;\n focusedOptionIndex: number;\n showDescriptions: boolean;\n};\n\nconst CategorySection: React.FC<CategorySectionProps> = ({\n category,\n options,\n isLocked,\n isFocused,\n focusedOptionIndex,\n showDescriptions,\n}) => {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box flexDirection=\"row\">\n <Text dimColor={isLocked}>{category.displayName}</Text>\n {category.required && (\n <Text color={isLocked ? CLI_COLORS.NEUTRAL : CLI_COLORS.ERROR} dimColor={isLocked}>\n {\" \"}\n {SYMBOL_REQUIRED}\n </Text>\n )}\n </Box>\n\n <Box flexDirection=\"row\" flexWrap=\"wrap\" marginTop={0}>\n {options.map((option, index) => (\n <Box key={option.id} flexDirection=\"column\">\n <SkillTag\n option={option}\n isFocused={isFocused && index === focusedOptionIndex && !isLocked}\n isLocked={isLocked}\n />\n {showDescriptions && option.stateReason && !isLocked && (\n <Box marginLeft={1} marginBottom={0}>\n <Text dimColor wrap=\"truncate-end\">\n {option.stateReason}\n </Text>\n </Box>\n )}\n </Box>\n ))}\n </Box>\n </Box>\n );\n};\n\nexport const CategoryGrid: React.FC<CategoryGridProps> = ({\n categories,\n showDescriptions,\n expertMode,\n onToggle,\n onToggleDescriptions,\n defaultFocusedRow = 0,\n defaultFocusedCol = 0,\n onFocusChange,\n}) => {\n const processedCategories = useMemo(\n () =>\n categories.map((category) => ({\n ...category,\n sortedOptions: sortOptions(category.options, expertMode),\n })),\n [categories, expertMode],\n );\n\n const getColCount = useCallback(\n (row: number): number => processedCategories[row]?.sortedOptions.length ?? 0,\n [processedCategories],\n );\n\n const isRowLocked = useCallback(\n (row: number): boolean => {\n const cat = processedCategories[row];\n return cat ? isSectionLocked(cat.id, categories) : false;\n },\n [processedCategories, categories],\n );\n\n const findValidCol = useCallback(\n (row: number, currentCol: number, direction: 1 | -1): number => {\n const options = processedCategories[row]?.sortedOptions || [];\n const catId = processedCategories[row]?.id;\n if (catId && isSectionLocked(catId, categories)) return currentCol;\n return findNextValidOption(options, currentCol, direction, true);\n },\n [processedCategories, categories],\n );\n\n const adjustCol = useCallback(\n (row: number, clampedCol: number): number => {\n const options = processedCategories[row]?.sortedOptions || [];\n if (options[clampedCol]?.state === \"disabled\") {\n return findValidStartColumn(options);\n }\n return clampedCol;\n },\n [processedCategories],\n );\n\n const { focusedRow, focusedCol, setFocused, moveFocus } = useFocusedListItem(\n processedCategories.length,\n getColCount,\n {\n wrap: true,\n isRowLocked,\n findValidCol,\n adjustCol,\n onChange: onFocusChange,\n initialRow: defaultFocusedRow,\n initialCol: defaultFocusedCol,\n },\n );\n\n useCategoryGridInput({\n processedCategories,\n categories,\n focusedRow,\n focusedCol,\n setFocused,\n moveFocus,\n onToggle,\n onToggleDescriptions,\n });\n\n if (categories.length === 0) {\n return (\n <Box flexDirection=\"column\">\n <Text dimColor>No categories to display.</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\">\n {processedCategories.map((category, rowIndex) => {\n const isLocked = isSectionLocked(category.id, categories);\n\n return (\n <CategorySection\n key={category.id}\n category={category}\n options={category.sortedOptions}\n isLocked={isLocked}\n isFocused={rowIndex === focusedRow}\n focusedOptionIndex={focusedCol}\n showDescriptions={showDescriptions}\n />\n );\n })}\n </Box>\n );\n};\n","import { useCallback, useEffect } from \"react\";\nimport { useInput } from \"ink\";\n\nimport type { Subcategory, SkillId } from \"../../types/index.js\";\nimport type { CategoryOption, CategoryRow } from \"../wizard/category-grid.js\";\n\nconst FRAMEWORK_CATEGORY_ID = \"framework\";\n\n// Locked = non-framework section when no framework is selected\nexport const isSectionLocked = (categoryId: Subcategory, categories: CategoryRow[]): boolean => {\n if (categoryId === FRAMEWORK_CATEGORY_ID) {\n return false;\n }\n\n const frameworkCategory = categories.find((cat) => cat.id === FRAMEWORK_CATEGORY_ID);\n if (!frameworkCategory) return false;\n\n return !frameworkCategory.options.some((opt) => opt.selected);\n};\n\nexport const findValidStartColumn = (options: CategoryOption[]): number => {\n for (let i = 0; i < options.length; i++) {\n if (options[i] && options[i].state !== \"disabled\") {\n return i;\n }\n }\n return 0;\n};\n\n/** Find next unlocked section index (wrapping, direction: forward) */\nexport const findNextUnlockedIndex = (\n processed: { id: Subcategory; sortedOptions: CategoryOption[] }[],\n currentIndex: number,\n allCategories: CategoryRow[],\n): number => {\n const length = processed.length;\n if (length === 0) return currentIndex;\n\n let index = currentIndex;\n let attempts = 0;\n\n while (attempts < length) {\n index += 1;\n if (index >= length) index = 0;\n\n const category = processed[index];\n if (category && !isSectionLocked(category.id, allCategories)) {\n return index;\n }\n\n attempts++;\n }\n\n return currentIndex;\n};\n\ntype ProcessedCategory = CategoryRow & { sortedOptions: CategoryOption[] };\n\ntype UseCategoryGridInputOptions = {\n processedCategories: ProcessedCategory[];\n categories: CategoryRow[];\n focusedRow: number;\n focusedCol: number;\n setFocused: (row: number, col: number) => void;\n moveFocus: (direction: \"up\" | \"down\" | \"left\" | \"right\") => void;\n onToggle: (categoryId: Subcategory, technologyId: SkillId) => void;\n onToggleDescriptions: () => void;\n};\n\nexport function useCategoryGridInput({\n processedCategories,\n categories,\n focusedRow,\n focusedCol,\n setFocused,\n moveFocus,\n onToggle,\n onToggleDescriptions,\n}: UseCategoryGridInputOptions): void {\n const currentRow = processedCategories[focusedRow];\n const currentOptions = currentRow?.sortedOptions || [];\n const currentLocked = currentRow ? isSectionLocked(currentRow.id, categories) : false;\n\n // Adjust column when current row's options change externally (e.g. option becomes disabled)\n useEffect(() => {\n if (!currentRow) return;\n\n const maxCol = currentOptions.length - 1;\n if (focusedCol > maxCol) {\n const newCol = Math.max(0, maxCol);\n setFocused(focusedRow, newCol);\n } else if (currentOptions[focusedCol]?.state === \"disabled\") {\n const validCol = findValidStartColumn(currentOptions);\n if (validCol !== focusedCol) {\n setFocused(focusedRow, validCol);\n }\n }\n }, [focusedRow, currentOptions, focusedCol, setFocused, currentRow]);\n\n // Bounce off locked sections when a section becomes locked (e.g. framework deselected)\n useEffect(() => {\n if (currentRow && currentLocked) {\n const nextUnlocked = findNextUnlockedIndex(processedCategories, focusedRow, categories);\n if (nextUnlocked !== focusedRow) {\n const newRowOptions = processedCategories[nextUnlocked]?.sortedOptions || [];\n const newCol = findValidStartColumn(newRowOptions);\n setFocused(nextUnlocked, newCol);\n }\n }\n }, [currentRow, currentLocked, focusedRow, processedCategories, categories, setFocused]);\n\n useInput(\n useCallback(\n (\n input: string,\n key: {\n leftArrow: boolean;\n rightArrow: boolean;\n upArrow: boolean;\n downArrow: boolean;\n tab: boolean;\n shift: boolean;\n },\n ) => {\n if (key.tab && key.shift) {\n onToggleDescriptions();\n return;\n }\n\n if (key.tab && !key.shift) {\n const nextSection = findNextUnlockedIndex(processedCategories, focusedRow, categories);\n if (nextSection !== focusedRow) {\n const newRowOptions = processedCategories[nextSection]?.sortedOptions || [];\n const newCol = findValidStartColumn(newRowOptions);\n setFocused(nextSection, newCol);\n }\n return;\n }\n\n if (input === \"d\" || input === \"D\") {\n onToggleDescriptions();\n return;\n }\n\n if (input === \" \") {\n if (currentLocked) return;\n const currentOption = currentOptions[focusedCol];\n if (currentOption && currentOption.state !== \"disabled\") {\n onToggle(currentRow.id, currentOption.id);\n }\n return;\n }\n\n const isLeft = key.leftArrow || input === \"h\";\n const isRight = key.rightArrow || input === \"l\";\n const isUp = key.upArrow || input === \"k\";\n const isDown = key.downArrow || input === \"j\";\n\n if (isLeft) {\n if (currentLocked) return;\n moveFocus(\"left\");\n } else if (isRight) {\n if (currentLocked) return;\n moveFocus(\"right\");\n } else if (isUp) {\n moveFocus(\"up\");\n } else if (isDown) {\n moveFocus(\"down\");\n }\n },\n [\n focusedRow,\n focusedCol,\n currentOptions,\n currentRow,\n currentLocked,\n processedCategories,\n categories,\n onToggle,\n onToggleDescriptions,\n setFocused,\n moveFocus,\n ],\n ),\n );\n}\n"],"mappings":"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","names":["useCallback","useCallback"]}
|