@kolisachint/hoocode-agent 0.4.9 → 0.4.11
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 +12 -0
- package/dist/core/dispatch-evaluator.d.ts +31 -0
- package/dist/core/dispatch-evaluator.d.ts.map +1 -0
- package/dist/core/dispatch-evaluator.js +300 -0
- package/dist/core/dispatch-evaluator.js.map +1 -0
- package/dist/core/lifeguard.d.ts +1 -0
- package/dist/core/lifeguard.d.ts.map +1 -1
- package/dist/core/lifeguard.js +6 -0
- package/dist/core/lifeguard.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/subagent-pool.d.ts +35 -0
- package/dist/core/subagent-pool.d.ts.map +1 -1
- package/dist/core/subagent-pool.js +152 -11
- package/dist/core/subagent-pool.js.map +1 -1
- package/dist/core/subagent.d.ts +9 -1
- package/dist/core/subagent.d.ts.map +1 -1
- package/dist/core/subagent.js +23 -2
- package/dist/core/subagent.js.map +1 -1
- package/dist/core/task-store.d.ts +12 -3
- package/dist/core/task-store.d.ts.map +1 -1
- package/dist/core/task-store.js +5 -2
- package/dist/core/task-store.js.map +1 -1
- package/dist/core/tools/subagent.d.ts +5 -1
- package/dist/core/tools/subagent.d.ts.map +1 -1
- package/dist/core/tools/subagent.js +64 -13
- package/dist/core/tools/subagent.js.map +1 -1
- package/dist/init-templates.generated.d.ts +1 -0
- package/dist/init-templates.generated.d.ts.map +1 -1
- package/dist/init-templates.generated.js +8 -0
- package/dist/init-templates.generated.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +2 -2
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +9 -14
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +1 -6
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +1 -5
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/task-panel.d.ts +5 -2
- package/dist/modes/interactive/components/task-panel.d.ts.map +1 -1
- package/dist/modes/interactive/components/task-panel.js +103 -9
- package/dist/modes/interactive/components/task-panel.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +12 -10
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +64 -3
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/dark.json +5 -5
- package/dist/modes/interactive/theme/light.json +5 -5
- package/docs/routing.md +57 -0
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
- package/templates/agents/doc.md +35 -0
- package/templates/agents/edit.md +37 -0
- package/templates/agents/explore.md +35 -0
- package/templates/agents/review.md +36 -0
- package/templates/agents/test.md +35 -0
- package/templates/subagent/doc.md +16 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/config-selector.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * TUI component for managing package resources (enable/disable)\n */\n\nimport { homedir } from \"node:os\";\nimport { basename, dirname, join, relative } from \"node:path\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tgetKeybindings,\n\tInput,\n\tmatchesKey,\n\tSpacer,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@kolisachint/hoocode-tui\";\nimport { CONFIG_DIR_NAME } from \"../../../config.js\";\nimport type { PathMetadata, ResolvedPaths, ResolvedResource } from \"../../../core/package-manager.js\";\nimport type { PackageSource, SettingsManager } from \"../../../core/settings-manager.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { rawKeyHint } from \"./keybinding-hints.js\";\n\ntype ResourceType = \"extensions\" | \"skills\" | \"prompts\" | \"themes\";\n\nconst RESOURCE_TYPE_LABELS: Record<ResourceType, string> = {\n\textensions: \"Extensions\",\n\tskills: \"Skills\",\n\tprompts: \"Prompts\",\n\tthemes: \"Themes\",\n};\n\ninterface ResourceItem {\n\tpath: string;\n\tenabled: boolean;\n\tmetadata: PathMetadata;\n\tresourceType: ResourceType;\n\tdisplayName: string;\n\tgroupKey: string;\n\tsubgroupKey: string;\n}\n\ninterface ResourceSubgroup {\n\ttype: ResourceType;\n\tlabel: string;\n\titems: ResourceItem[];\n}\n\ninterface ResourceGroup {\n\tkey: string;\n\tlabel: string;\n\tscope: \"user\" | \"project\" | \"temporary\";\n\torigin: \"package\" | \"top-level\";\n\tsource: string;\n\tsubgroups: ResourceSubgroup[];\n}\n\nfunction formatBaseDir(baseDir: string): string {\n\tconst homeDir = homedir();\n\tlet displayPath: string;\n\n\tif (baseDir === homeDir) {\n\t\tdisplayPath = \"~\";\n\t} else if (baseDir.startsWith(homeDir)) {\n\t\t// Replace home prefix with ~, normalize separators for display\n\t\tconst rest = baseDir.slice(homeDir.length);\n\t\tdisplayPath = `~${rest.replace(/\\\\/g, \"/\")}`;\n\t} else {\n\t\tdisplayPath = baseDir.replace(/\\\\/g, \"/\");\n\t}\n\n\treturn displayPath.endsWith(\"/\") ? displayPath : `${displayPath}/`;\n}\n\nfunction getGroupLabel(metadata: PathMetadata): string {\n\tif (metadata.origin === \"package\") {\n\t\treturn `${metadata.source} (${metadata.scope})`;\n\t}\n\t// Top-level resources\n\tif (metadata.source === \"auto\") {\n\t\tif (metadata.baseDir) {\n\t\t\treturn metadata.scope === \"user\"\n\t\t\t\t? `User (${formatBaseDir(metadata.baseDir)})`\n\t\t\t\t: `Project (${formatBaseDir(metadata.baseDir)})`;\n\t\t}\n\t\treturn metadata.scope === \"user\" ? \"User (~/.hoocode/agent/)\" : \"Project (.hoocode/)\";\n\t}\n\treturn metadata.scope === \"user\" ? \"User settings\" : \"Project settings\";\n}\n\nfunction buildGroups(resolved: ResolvedPaths): ResourceGroup[] {\n\tconst groupMap = new Map<string, ResourceGroup>();\n\n\tconst addToGroup = (resources: ResolvedResource[], resourceType: ResourceType) => {\n\t\tfor (const res of resources) {\n\t\t\tconst { path, enabled, metadata } = res;\n\t\t\tconst groupKey = `${metadata.origin}:${metadata.scope}:${metadata.source}:${metadata.baseDir ?? \"\"}`;\n\n\t\t\tif (!groupMap.has(groupKey)) {\n\t\t\t\tgroupMap.set(groupKey, {\n\t\t\t\t\tkey: groupKey,\n\t\t\t\t\tlabel: getGroupLabel(metadata),\n\t\t\t\t\tscope: metadata.scope,\n\t\t\t\t\torigin: metadata.origin,\n\t\t\t\t\tsource: metadata.source,\n\t\t\t\t\tsubgroups: [],\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst group = groupMap.get(groupKey)!;\n\t\t\tconst subgroupKey = `${groupKey}:${resourceType}`;\n\n\t\t\tlet subgroup = group.subgroups.find((sg) => sg.type === resourceType);\n\t\t\tif (!subgroup) {\n\t\t\t\tsubgroup = {\n\t\t\t\t\ttype: resourceType,\n\t\t\t\t\tlabel: RESOURCE_TYPE_LABELS[resourceType],\n\t\t\t\t\titems: [],\n\t\t\t\t};\n\t\t\t\tgroup.subgroups.push(subgroup);\n\t\t\t}\n\n\t\t\tconst fileName = basename(path);\n\t\t\tconst parentFolder = basename(dirname(path));\n\t\t\tlet displayName: string;\n\t\t\tif (resourceType === \"extensions\" && parentFolder !== \"extensions\") {\n\t\t\t\tdisplayName = `${parentFolder}/${fileName}`;\n\t\t\t} else if (resourceType === \"skills\" && fileName === \"SKILL.md\") {\n\t\t\t\tdisplayName = parentFolder;\n\t\t\t} else {\n\t\t\t\tdisplayName = fileName;\n\t\t\t}\n\t\t\tsubgroup.items.push({\n\t\t\t\tpath,\n\t\t\t\tenabled,\n\t\t\t\tmetadata,\n\t\t\t\tresourceType,\n\t\t\t\tdisplayName,\n\t\t\t\tgroupKey,\n\t\t\t\tsubgroupKey,\n\t\t\t});\n\t\t}\n\t};\n\n\taddToGroup(resolved.extensions, \"extensions\");\n\taddToGroup(resolved.skills, \"skills\");\n\taddToGroup(resolved.prompts, \"prompts\");\n\taddToGroup(resolved.themes, \"themes\");\n\n\t// Sort groups: packages first, then top-level; user before project\n\tconst groups = Array.from(groupMap.values());\n\tgroups.sort((a, b) => {\n\t\tif (a.origin !== b.origin) {\n\t\t\treturn a.origin === \"package\" ? -1 : 1;\n\t\t}\n\t\tif (a.scope !== b.scope) {\n\t\t\treturn a.scope === \"user\" ? -1 : 1;\n\t\t}\n\t\treturn a.source.localeCompare(b.source);\n\t});\n\n\t// Sort subgroups within each group by type order, and items by name\n\tconst typeOrder: Record<ResourceType, number> = { extensions: 0, skills: 1, prompts: 2, themes: 3 };\n\tfor (const group of groups) {\n\t\tgroup.subgroups.sort((a, b) => typeOrder[a.type] - typeOrder[b.type]);\n\t\tfor (const subgroup of group.subgroups) {\n\t\t\tsubgroup.items.sort((a, b) => a.displayName.localeCompare(b.displayName));\n\t\t}\n\t}\n\n\treturn groups;\n}\n\ntype FlatEntry =\n\t| { type: \"group\"; group: ResourceGroup }\n\t| { type: \"subgroup\"; subgroup: ResourceSubgroup; group: ResourceGroup }\n\t| { type: \"item\"; item: ResourceItem };\n\nclass ConfigSelectorHeader implements Component {\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst title = theme.bold(\"Resource Configuration\");\n\t\tconst sep = theme.fg(\"muted\", \" · \");\n\t\tconst hint = rawKeyHint(\"space\", \"toggle\") + sep + rawKeyHint(\"esc\", \"close\");\n\t\tconst hintWidth = visibleWidth(hint);\n\t\tconst titleWidth = visibleWidth(title);\n\t\tconst spacing = Math.max(1, width - titleWidth - hintWidth);\n\n\t\treturn [\n\t\t\ttruncateToWidth(`${title}${\" \".repeat(spacing)}${hint}`, width, \"\"),\n\t\t\ttheme.fg(\"muted\", \"Type to filter resources\"),\n\t\t];\n\t}\n}\n\nclass ResourceList implements Component, Focusable {\n\tprivate groups: ResourceGroup[];\n\tprivate flatItems: FlatEntry[] = [];\n\tprivate filteredItems: FlatEntry[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\tprivate maxVisible = 15;\n\tprivate settingsManager: SettingsManager;\n\tprivate cwd: string;\n\tprivate agentDir: string;\n\n\tpublic onCancel?: () => void;\n\tpublic onExit?: () => void;\n\tpublic onToggle?: (item: ResourceItem, newEnabled: boolean) => void;\n\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\n\tconstructor(groups: ResourceGroup[], settingsManager: SettingsManager, cwd: string, agentDir: string) {\n\t\tthis.groups = groups;\n\t\tthis.settingsManager = settingsManager;\n\t\tthis.cwd = cwd;\n\t\tthis.agentDir = agentDir;\n\t\tthis.searchInput = new Input();\n\t\tthis.buildFlatList();\n\t\tthis.filteredItems = [...this.flatItems];\n\t}\n\n\tprivate buildFlatList(): void {\n\t\tthis.flatItems = [];\n\t\tfor (const group of this.groups) {\n\t\t\tthis.flatItems.push({ type: \"group\", group });\n\t\t\tfor (const subgroup of group.subgroups) {\n\t\t\t\tthis.flatItems.push({ type: \"subgroup\", subgroup, group });\n\t\t\t\tfor (const item of subgroup.items) {\n\t\t\t\t\tthis.flatItems.push({ type: \"item\", item });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Start selection on first item (not header)\n\t\tthis.selectedIndex = this.flatItems.findIndex((e) => e.type === \"item\");\n\t\tif (this.selectedIndex < 0) this.selectedIndex = 0;\n\t}\n\n\tprivate findNextItem(fromIndex: number, direction: 1 | -1): number {\n\t\tlet idx = fromIndex + direction;\n\t\twhile (idx >= 0 && idx < this.filteredItems.length) {\n\t\t\tif (this.filteredItems[idx].type === \"item\") {\n\t\t\t\treturn idx;\n\t\t\t}\n\t\t\tidx += direction;\n\t\t}\n\t\treturn fromIndex; // Stay at current if no item found\n\t}\n\n\tprivate filterItems(query: string): void {\n\t\tif (!query.trim()) {\n\t\t\tthis.filteredItems = [...this.flatItems];\n\t\t\tthis.selectFirstItem();\n\t\t\treturn;\n\t\t}\n\n\t\tconst lowerQuery = query.toLowerCase();\n\t\tconst matchingItems = new Set<ResourceItem>();\n\t\tconst matchingSubgroups = new Set<ResourceSubgroup>();\n\t\tconst matchingGroups = new Set<ResourceGroup>();\n\n\t\tfor (const entry of this.flatItems) {\n\t\t\tif (entry.type === \"item\") {\n\t\t\t\tconst item = entry.item;\n\t\t\t\tif (\n\t\t\t\t\titem.displayName.toLowerCase().includes(lowerQuery) ||\n\t\t\t\t\titem.resourceType.toLowerCase().includes(lowerQuery) ||\n\t\t\t\t\titem.path.toLowerCase().includes(lowerQuery)\n\t\t\t\t) {\n\t\t\t\t\tmatchingItems.add(item);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Find which subgroups and groups contain matching items\n\t\tfor (const group of this.groups) {\n\t\t\tfor (const subgroup of group.subgroups) {\n\t\t\t\tfor (const item of subgroup.items) {\n\t\t\t\t\tif (matchingItems.has(item)) {\n\t\t\t\t\t\tmatchingSubgroups.add(subgroup);\n\t\t\t\t\t\tmatchingGroups.add(group);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis.filteredItems = [];\n\t\tfor (const entry of this.flatItems) {\n\t\t\tif (entry.type === \"group\" && matchingGroups.has(entry.group)) {\n\t\t\t\tthis.filteredItems.push(entry);\n\t\t\t} else if (entry.type === \"subgroup\" && matchingSubgroups.has(entry.subgroup)) {\n\t\t\t\tthis.filteredItems.push(entry);\n\t\t\t} else if (entry.type === \"item\" && matchingItems.has(entry.item)) {\n\t\t\t\tthis.filteredItems.push(entry);\n\t\t\t}\n\t\t}\n\n\t\tthis.selectFirstItem();\n\t}\n\n\tprivate selectFirstItem(): void {\n\t\tconst firstItemIndex = this.filteredItems.findIndex((e) => e.type === \"item\");\n\t\tthis.selectedIndex = firstItemIndex >= 0 ? firstItemIndex : 0;\n\t}\n\n\tupdateItem(item: ResourceItem, enabled: boolean): void {\n\t\titem.enabled = enabled;\n\t\t// Update in groups too\n\t\tfor (const group of this.groups) {\n\t\t\tfor (const subgroup of group.subgroups) {\n\t\t\t\tconst found = subgroup.items.find((i) => i.path === item.path && i.resourceType === item.resourceType);\n\t\t\t\tif (found) {\n\t\t\t\t\tfound.enabled = enabled;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\n\t\t// Search input\n\t\tlines.push(...this.searchInput.render(width));\n\t\tlines.push(\"\");\n\n\t\tif (this.filteredItems.length === 0) {\n\t\t\tlines.push(theme.fg(\"muted\", \" No resources found\"));\n\t\t\treturn lines;\n\t\t}\n\n\t\t// Calculate visible range\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);\n\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst entry = this.filteredItems[i];\n\t\t\tconst isSelected = i === this.selectedIndex;\n\n\t\t\tif (entry.type === \"group\") {\n\t\t\t\t// Main group header (no cursor)\n\t\t\t\tconst groupLine = theme.fg(\"accent\", theme.bold(entry.group.label));\n\t\t\t\tlines.push(truncateToWidth(` ${groupLine}`, width, \"\"));\n\t\t\t} else if (entry.type === \"subgroup\") {\n\t\t\t\t// Subgroup header (indented, no cursor)\n\t\t\t\tconst subgroupLine = theme.fg(\"muted\", entry.subgroup.label);\n\t\t\t\tlines.push(truncateToWidth(` ${subgroupLine}`, width, \"\"));\n\t\t\t} else {\n\t\t\t\t// Resource item (cursor only on items)\n\t\t\t\tconst item = entry.item;\n\t\t\t\tconst cursor = isSelected ? \"> \" : \" \";\n\t\t\t\tconst checkbox = item.enabled ? theme.fg(\"success\", \"[x]\") : theme.fg(\"dim\", \"[ ]\");\n\t\t\t\tconst name = isSelected ? theme.bold(item.displayName) : item.displayName;\n\t\t\t\tlines.push(truncateToWidth(`${cursor} ${checkbox} ${name}`, width, \"...\"));\n\t\t\t}\n\t\t}\n\n\t\t// Scroll indicator\n\t\tif (startIndex > 0 || endIndex < this.filteredItems.length) {\n\t\t\tconst itemCount = this.filteredItems.filter((e) => e.type === \"item\").length;\n\t\t\tconst currentItemIndex =\n\t\t\t\tthis.filteredItems.slice(0, this.selectedIndex).filter((e) => e.type === \"item\").length + 1;\n\t\t\tlines.push(theme.fg(\"dim\", ` (${currentItemIndex}/${itemCount})`));\n\t\t}\n\n\t\treturn lines;\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tthis.selectedIndex = this.findNextItem(this.selectedIndex, -1);\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.down\")) {\n\t\t\tthis.selectedIndex = this.findNextItem(this.selectedIndex, 1);\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.pageUp\")) {\n\t\t\t// Jump up by maxVisible, then find nearest item\n\t\t\tlet target = Math.max(0, this.selectedIndex - this.maxVisible);\n\t\t\twhile (target < this.filteredItems.length && this.filteredItems[target].type !== \"item\") {\n\t\t\t\ttarget++;\n\t\t\t}\n\t\t\tif (target < this.filteredItems.length) {\n\t\t\t\tthis.selectedIndex = target;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.pageDown\")) {\n\t\t\t// Jump down by maxVisible, then find nearest item\n\t\t\tlet target = Math.min(this.filteredItems.length - 1, this.selectedIndex + this.maxVisible);\n\t\t\twhile (target >= 0 && this.filteredItems[target].type !== \"item\") {\n\t\t\t\ttarget--;\n\t\t\t}\n\t\t\tif (target >= 0) {\n\t\t\t\tthis.selectedIndex = target;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onCancel?.();\n\t\t\treturn;\n\t\t}\n\t\tif (matchesKey(data, \"ctrl+c\")) {\n\t\t\tthis.onExit?.();\n\t\t\treturn;\n\t\t}\n\t\tif (data === \" \" || kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tconst entry = this.filteredItems[this.selectedIndex];\n\t\t\tif (entry?.type === \"item\") {\n\t\t\t\tconst newEnabled = !entry.item.enabled;\n\t\t\t\tthis.toggleResource(entry.item, newEnabled);\n\t\t\t\tthis.updateItem(entry.item, newEnabled);\n\t\t\t\tthis.onToggle?.(entry.item, newEnabled);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass to search input\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.filterItems(this.searchInput.getValue());\n\t}\n\n\tprivate toggleResource(item: ResourceItem, enabled: boolean): void {\n\t\tif (item.metadata.origin === \"top-level\") {\n\t\t\tthis.toggleTopLevelResource(item, enabled);\n\t\t} else {\n\t\t\tthis.togglePackageResource(item, enabled);\n\t\t}\n\t}\n\n\tprivate toggleTopLevelResource(item: ResourceItem, enabled: boolean): void {\n\t\tconst scope = item.metadata.scope as \"user\" | \"project\";\n\t\tconst settings =\n\t\t\tscope === \"project\" ? this.settingsManager.getProjectSettings() : this.settingsManager.getGlobalSettings();\n\n\t\tconst arrayKey = item.resourceType as \"extensions\" | \"skills\" | \"prompts\" | \"themes\";\n\t\tconst current = (settings[arrayKey] ?? []) as string[];\n\n\t\t// Generate pattern for this resource\n\t\tconst pattern = this.getResourcePattern(item);\n\t\tconst disablePattern = `-${pattern}`;\n\t\tconst enablePattern = `+${pattern}`;\n\n\t\t// Filter out existing patterns for this resource\n\t\tconst updated = current.filter((p) => {\n\t\t\tconst stripped = p.startsWith(\"!\") || p.startsWith(\"+\") || p.startsWith(\"-\") ? p.slice(1) : p;\n\t\t\treturn stripped !== pattern;\n\t\t});\n\n\t\tif (enabled) {\n\t\t\tupdated.push(enablePattern);\n\t\t} else {\n\t\t\tupdated.push(disablePattern);\n\t\t}\n\n\t\tif (scope === \"project\") {\n\t\t\tif (arrayKey === \"extensions\") {\n\t\t\t\tthis.settingsManager.setProjectExtensionPaths(updated);\n\t\t\t} else if (arrayKey === \"skills\") {\n\t\t\t\tthis.settingsManager.setProjectSkillPaths(updated);\n\t\t\t} else if (arrayKey === \"prompts\") {\n\t\t\t\tthis.settingsManager.setProjectPromptTemplatePaths(updated);\n\t\t\t} else if (arrayKey === \"themes\") {\n\t\t\t\tthis.settingsManager.setProjectThemePaths(updated);\n\t\t\t}\n\t\t} else {\n\t\t\tif (arrayKey === \"extensions\") {\n\t\t\t\tthis.settingsManager.setExtensionPaths(updated);\n\t\t\t} else if (arrayKey === \"skills\") {\n\t\t\t\tthis.settingsManager.setSkillPaths(updated);\n\t\t\t} else if (arrayKey === \"prompts\") {\n\t\t\t\tthis.settingsManager.setPromptTemplatePaths(updated);\n\t\t\t} else if (arrayKey === \"themes\") {\n\t\t\t\tthis.settingsManager.setThemePaths(updated);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate togglePackageResource(item: ResourceItem, enabled: boolean): void {\n\t\tconst scope = item.metadata.scope as \"user\" | \"project\";\n\t\tconst settings =\n\t\t\tscope === \"project\" ? this.settingsManager.getProjectSettings() : this.settingsManager.getGlobalSettings();\n\n\t\tconst packages = [...(settings.packages ?? [])] as PackageSource[];\n\t\tconst pkgIndex = packages.findIndex((pkg) => {\n\t\t\tconst source = typeof pkg === \"string\" ? pkg : pkg.source;\n\t\t\treturn source === item.metadata.source;\n\t\t});\n\n\t\tif (pkgIndex === -1) return;\n\n\t\tlet pkg = packages[pkgIndex];\n\n\t\t// Convert string to object form if needed\n\t\tif (typeof pkg === \"string\") {\n\t\t\tpkg = { source: pkg };\n\t\t\tpackages[pkgIndex] = pkg;\n\t\t}\n\n\t\t// Get the resource array for this type\n\t\tconst arrayKey = item.resourceType as \"extensions\" | \"skills\" | \"prompts\" | \"themes\";\n\t\tconst current = (pkg[arrayKey] ?? []) as string[];\n\n\t\t// Generate pattern relative to package root\n\t\tconst pattern = this.getPackageResourcePattern(item);\n\t\tconst disablePattern = `-${pattern}`;\n\t\tconst enablePattern = `+${pattern}`;\n\n\t\t// Filter out existing patterns for this resource\n\t\tconst updated = current.filter((p) => {\n\t\t\tconst stripped = p.startsWith(\"!\") || p.startsWith(\"+\") || p.startsWith(\"-\") ? p.slice(1) : p;\n\t\t\treturn stripped !== pattern;\n\t\t});\n\n\t\tif (enabled) {\n\t\t\tupdated.push(enablePattern);\n\t\t} else {\n\t\t\tupdated.push(disablePattern);\n\t\t}\n\n\t\t(pkg as Record<string, unknown>)[arrayKey] = updated.length > 0 ? updated : undefined;\n\n\t\t// Clean up empty filter object\n\t\tconst hasFilters = [\"extensions\", \"skills\", \"prompts\", \"themes\"].some(\n\t\t\t(k) => (pkg as Record<string, unknown>)[k] !== undefined,\n\t\t);\n\t\tif (!hasFilters) {\n\t\t\tpackages[pkgIndex] = (pkg as { source: string }).source;\n\t\t}\n\n\t\tif (scope === \"project\") {\n\t\t\tthis.settingsManager.setProjectPackages(packages);\n\t\t} else {\n\t\t\tthis.settingsManager.setPackages(packages);\n\t\t}\n\t}\n\n\tprivate getTopLevelBaseDir(scope: \"user\" | \"project\"): string {\n\t\treturn scope === \"project\" ? join(this.cwd, CONFIG_DIR_NAME) : this.agentDir;\n\t}\n\n\tprivate getResourcePattern(item: ResourceItem): string {\n\t\tconst scope = item.metadata.scope as \"user\" | \"project\";\n\t\tconst baseDir = this.getTopLevelBaseDir(scope);\n\t\treturn relative(baseDir, item.path);\n\t}\n\n\tprivate getPackageResourcePattern(item: ResourceItem): string {\n\t\tconst baseDir = item.metadata.baseDir ?? dirname(item.path);\n\t\treturn relative(baseDir, item.path);\n\t}\n}\n\nexport class ConfigSelectorComponent extends Container implements Focusable {\n\tprivate resourceList: ResourceList;\n\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.resourceList.focused = value;\n\t}\n\n\tconstructor(\n\t\tresolvedPaths: ResolvedPaths,\n\t\tsettingsManager: SettingsManager,\n\t\tcwd: string,\n\t\tagentDir: string,\n\t\tonClose: () => void,\n\t\tonExit: () => void,\n\t\trequestRender: () => void,\n\t) {\n\t\tsuper();\n\n\t\tconst groups = buildGroups(resolvedPaths);\n\n\t\t// Add header\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new ConfigSelectorHeader());\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Resource list\n\t\tthis.resourceList = new ResourceList(groups, settingsManager, cwd, agentDir);\n\t\tthis.resourceList.onCancel = onClose;\n\t\tthis.resourceList.onExit = onExit;\n\t\tthis.resourceList.onToggle = () => requestRender();\n\t\tthis.addChild(this.resourceList);\n\n\t\t// Bottom border\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\tgetResourceList(): ResourceList {\n\t\treturn this.resourceList;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"config-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/config-selector.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * TUI component for managing package resources (enable/disable)\n */\n\nimport { homedir } from \"node:os\";\nimport { basename, dirname, join, relative } from \"node:path\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tgetKeybindings,\n\tInput,\n\tmatchesKey,\n\tSpacer,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@kolisachint/hoocode-tui\";\nimport { CONFIG_DIR_NAME } from \"../../../config.js\";\nimport type { PathMetadata, ResolvedPaths, ResolvedResource } from \"../../../core/package-manager.js\";\nimport type { PackageSource, SettingsManager } from \"../../../core/settings-manager.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { rawKeyHint } from \"./keybinding-hints.js\";\n\ntype ResourceType = \"extensions\" | \"skills\" | \"prompts\" | \"themes\";\n\nconst RESOURCE_TYPE_LABELS: Record<ResourceType, string> = {\n\textensions: \"Extensions\",\n\tskills: \"Skills\",\n\tprompts: \"Prompts\",\n\tthemes: \"Themes\",\n};\n\ninterface ResourceItem {\n\tpath: string;\n\tenabled: boolean;\n\tmetadata: PathMetadata;\n\tresourceType: ResourceType;\n\tdisplayName: string;\n\tgroupKey: string;\n\tsubgroupKey: string;\n}\n\ninterface ResourceSubgroup {\n\ttype: ResourceType;\n\tlabel: string;\n\titems: ResourceItem[];\n}\n\ninterface ResourceGroup {\n\tkey: string;\n\tlabel: string;\n\tscope: \"user\" | \"project\" | \"temporary\";\n\torigin: \"package\" | \"top-level\";\n\tsource: string;\n\tsubgroups: ResourceSubgroup[];\n}\n\nfunction formatBaseDir(baseDir: string): string {\n\tconst homeDir = homedir();\n\tlet displayPath: string;\n\n\tif (baseDir === homeDir) {\n\t\tdisplayPath = \"~\";\n\t} else if (baseDir.startsWith(homeDir)) {\n\t\t// Replace home prefix with ~, normalize separators for display\n\t\tconst rest = baseDir.slice(homeDir.length);\n\t\tdisplayPath = `~${rest.replace(/\\\\/g, \"/\")}`;\n\t} else {\n\t\tdisplayPath = baseDir.replace(/\\\\/g, \"/\");\n\t}\n\n\treturn displayPath.endsWith(\"/\") ? displayPath : `${displayPath}/`;\n}\n\nfunction getGroupLabel(metadata: PathMetadata): string {\n\tif (metadata.origin === \"package\") {\n\t\treturn `${metadata.source} (${metadata.scope})`;\n\t}\n\t// Top-level resources\n\tif (metadata.source === \"auto\") {\n\t\tif (metadata.baseDir) {\n\t\t\treturn metadata.scope === \"user\"\n\t\t\t\t? `User (${formatBaseDir(metadata.baseDir)})`\n\t\t\t\t: `Project (${formatBaseDir(metadata.baseDir)})`;\n\t\t}\n\t\treturn metadata.scope === \"user\" ? \"User (~/.hoocode/agent/)\" : \"Project (.hoocode/)\";\n\t}\n\treturn metadata.scope === \"user\" ? \"User settings\" : \"Project settings\";\n}\n\nfunction buildGroups(resolved: ResolvedPaths): ResourceGroup[] {\n\tconst groupMap = new Map<string, ResourceGroup>();\n\n\tconst addToGroup = (resources: ResolvedResource[], resourceType: ResourceType) => {\n\t\tfor (const res of resources) {\n\t\t\tconst { path, enabled, metadata } = res;\n\t\t\tconst groupKey = `${metadata.origin}:${metadata.scope}:${metadata.source}:${metadata.baseDir ?? \"\"}`;\n\n\t\t\tif (!groupMap.has(groupKey)) {\n\t\t\t\tgroupMap.set(groupKey, {\n\t\t\t\t\tkey: groupKey,\n\t\t\t\t\tlabel: getGroupLabel(metadata),\n\t\t\t\t\tscope: metadata.scope,\n\t\t\t\t\torigin: metadata.origin,\n\t\t\t\t\tsource: metadata.source,\n\t\t\t\t\tsubgroups: [],\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst group = groupMap.get(groupKey)!;\n\t\t\tconst subgroupKey = `${groupKey}:${resourceType}`;\n\n\t\t\tlet subgroup = group.subgroups.find((sg) => sg.type === resourceType);\n\t\t\tif (!subgroup) {\n\t\t\t\tsubgroup = {\n\t\t\t\t\ttype: resourceType,\n\t\t\t\t\tlabel: RESOURCE_TYPE_LABELS[resourceType],\n\t\t\t\t\titems: [],\n\t\t\t\t};\n\t\t\t\tgroup.subgroups.push(subgroup);\n\t\t\t}\n\n\t\t\tconst fileName = basename(path);\n\t\t\tconst parentFolder = basename(dirname(path));\n\t\t\tlet displayName: string;\n\t\t\tif (resourceType === \"extensions\" && parentFolder !== \"extensions\") {\n\t\t\t\tdisplayName = `${parentFolder}/${fileName}`;\n\t\t\t} else if (resourceType === \"skills\" && fileName === \"SKILL.md\") {\n\t\t\t\tdisplayName = parentFolder;\n\t\t\t} else {\n\t\t\t\tdisplayName = fileName;\n\t\t\t}\n\t\t\tsubgroup.items.push({\n\t\t\t\tpath,\n\t\t\t\tenabled,\n\t\t\t\tmetadata,\n\t\t\t\tresourceType,\n\t\t\t\tdisplayName,\n\t\t\t\tgroupKey,\n\t\t\t\tsubgroupKey,\n\t\t\t});\n\t\t}\n\t};\n\n\taddToGroup(resolved.extensions, \"extensions\");\n\taddToGroup(resolved.skills, \"skills\");\n\taddToGroup(resolved.prompts, \"prompts\");\n\taddToGroup(resolved.themes, \"themes\");\n\n\t// Sort groups: packages first, then top-level; user before project\n\tconst groups = Array.from(groupMap.values());\n\tgroups.sort((a, b) => {\n\t\tif (a.origin !== b.origin) {\n\t\t\treturn a.origin === \"package\" ? -1 : 1;\n\t\t}\n\t\tif (a.scope !== b.scope) {\n\t\t\treturn a.scope === \"user\" ? -1 : 1;\n\t\t}\n\t\treturn a.source.localeCompare(b.source);\n\t});\n\n\t// Sort subgroups within each group by type order, and items by name\n\tconst typeOrder: Record<ResourceType, number> = { extensions: 0, skills: 1, prompts: 2, themes: 3 };\n\tfor (const group of groups) {\n\t\tgroup.subgroups.sort((a, b) => typeOrder[a.type] - typeOrder[b.type]);\n\t\tfor (const subgroup of group.subgroups) {\n\t\t\tsubgroup.items.sort((a, b) => a.displayName.localeCompare(b.displayName));\n\t\t}\n\t}\n\n\treturn groups;\n}\n\ntype FlatEntry =\n\t| { type: \"group\"; group: ResourceGroup }\n\t| { type: \"subgroup\"; subgroup: ResourceSubgroup; group: ResourceGroup }\n\t| { type: \"item\"; item: ResourceItem };\n\nclass ConfigSelectorHeader implements Component {\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst title = theme.bold(\"Resource Configuration\");\n\t\tconst sep = theme.fg(\"muted\", \" · \");\n\t\tconst hint = rawKeyHint(\"space\", \"toggle\") + sep + rawKeyHint(\"esc\", \"close\");\n\t\tconst hintWidth = visibleWidth(hint);\n\t\tconst titleWidth = visibleWidth(title);\n\t\tconst spacing = Math.max(1, width - titleWidth - hintWidth);\n\n\t\treturn [\n\t\t\ttruncateToWidth(`${title}${\" \".repeat(spacing)}${hint}`, width, \"\"),\n\t\t\ttheme.fg(\"muted\", \"Type to filter resources\"),\n\t\t];\n\t}\n}\n\nclass ResourceList implements Component, Focusable {\n\tprivate groups: ResourceGroup[];\n\tprivate flatItems: FlatEntry[] = [];\n\tprivate filteredItems: FlatEntry[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\tprivate maxVisible = 15;\n\tprivate settingsManager: SettingsManager;\n\tprivate cwd: string;\n\tprivate agentDir: string;\n\n\tpublic onCancel?: () => void;\n\tpublic onExit?: () => void;\n\tpublic onToggle?: (item: ResourceItem, newEnabled: boolean) => void;\n\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\n\tconstructor(groups: ResourceGroup[], settingsManager: SettingsManager, cwd: string, agentDir: string) {\n\t\tthis.groups = groups;\n\t\tthis.settingsManager = settingsManager;\n\t\tthis.cwd = cwd;\n\t\tthis.agentDir = agentDir;\n\t\tthis.searchInput = new Input();\n\t\tthis.buildFlatList();\n\t\tthis.filteredItems = [...this.flatItems];\n\t}\n\n\tprivate buildFlatList(): void {\n\t\tthis.flatItems = [];\n\t\tfor (const group of this.groups) {\n\t\t\tthis.flatItems.push({ type: \"group\", group });\n\t\t\tfor (const subgroup of group.subgroups) {\n\t\t\t\tthis.flatItems.push({ type: \"subgroup\", subgroup, group });\n\t\t\t\tfor (const item of subgroup.items) {\n\t\t\t\t\tthis.flatItems.push({ type: \"item\", item });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Start selection on first item (not header)\n\t\tthis.selectedIndex = this.flatItems.findIndex((e) => e.type === \"item\");\n\t\tif (this.selectedIndex < 0) this.selectedIndex = 0;\n\t}\n\n\tprivate findNextItem(fromIndex: number, direction: 1 | -1): number {\n\t\tlet idx = fromIndex + direction;\n\t\twhile (idx >= 0 && idx < this.filteredItems.length) {\n\t\t\tif (this.filteredItems[idx].type === \"item\") {\n\t\t\t\treturn idx;\n\t\t\t}\n\t\t\tidx += direction;\n\t\t}\n\t\treturn fromIndex; // Stay at current if no item found\n\t}\n\n\tprivate filterItems(query: string): void {\n\t\tif (!query.trim()) {\n\t\t\tthis.filteredItems = [...this.flatItems];\n\t\t\tthis.selectFirstItem();\n\t\t\treturn;\n\t\t}\n\n\t\tconst lowerQuery = query.toLowerCase();\n\t\tconst matchingItems = new Set<ResourceItem>();\n\t\tconst matchingSubgroups = new Set<ResourceSubgroup>();\n\t\tconst matchingGroups = new Set<ResourceGroup>();\n\n\t\tfor (const entry of this.flatItems) {\n\t\t\tif (entry.type === \"item\") {\n\t\t\t\tconst item = entry.item;\n\t\t\t\tif (\n\t\t\t\t\titem.displayName.toLowerCase().includes(lowerQuery) ||\n\t\t\t\t\titem.resourceType.toLowerCase().includes(lowerQuery) ||\n\t\t\t\t\titem.path.toLowerCase().includes(lowerQuery)\n\t\t\t\t) {\n\t\t\t\t\tmatchingItems.add(item);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Find which subgroups and groups contain matching items\n\t\tfor (const group of this.groups) {\n\t\t\tfor (const subgroup of group.subgroups) {\n\t\t\t\tfor (const item of subgroup.items) {\n\t\t\t\t\tif (matchingItems.has(item)) {\n\t\t\t\t\t\tmatchingSubgroups.add(subgroup);\n\t\t\t\t\t\tmatchingGroups.add(group);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis.filteredItems = [];\n\t\tfor (const entry of this.flatItems) {\n\t\t\tif (entry.type === \"group\" && matchingGroups.has(entry.group)) {\n\t\t\t\tthis.filteredItems.push(entry);\n\t\t\t} else if (entry.type === \"subgroup\" && matchingSubgroups.has(entry.subgroup)) {\n\t\t\t\tthis.filteredItems.push(entry);\n\t\t\t} else if (entry.type === \"item\" && matchingItems.has(entry.item)) {\n\t\t\t\tthis.filteredItems.push(entry);\n\t\t\t}\n\t\t}\n\n\t\tthis.selectFirstItem();\n\t}\n\n\tprivate selectFirstItem(): void {\n\t\tconst firstItemIndex = this.filteredItems.findIndex((e) => e.type === \"item\");\n\t\tthis.selectedIndex = firstItemIndex >= 0 ? firstItemIndex : 0;\n\t}\n\n\tupdateItem(item: ResourceItem, enabled: boolean): void {\n\t\titem.enabled = enabled;\n\t\t// Update in groups too\n\t\tfor (const group of this.groups) {\n\t\t\tfor (const subgroup of group.subgroups) {\n\t\t\t\tconst found = subgroup.items.find((i) => i.path === item.path && i.resourceType === item.resourceType);\n\t\t\t\tif (found) {\n\t\t\t\t\tfound.enabled = enabled;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\n\t\t// Search input\n\t\tlines.push(...this.searchInput.render(width));\n\t\tlines.push(\"\");\n\n\t\tif (this.filteredItems.length === 0) {\n\t\t\tlines.push(theme.fg(\"muted\", \" No resources found\"));\n\t\t\treturn lines;\n\t\t}\n\n\t\t// Calculate visible range\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);\n\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst entry = this.filteredItems[i];\n\t\t\tconst isSelected = i === this.selectedIndex;\n\n\t\t\tif (entry.type === \"group\") {\n\t\t\t\t// Main group header (no cursor)\n\t\t\t\tconst groupLine = theme.fg(\"accent\", theme.bold(entry.group.label));\n\t\t\t\tlines.push(truncateToWidth(` ${groupLine}`, width, \"\"));\n\t\t\t} else if (entry.type === \"subgroup\") {\n\t\t\t\t// Subgroup header (indented, no cursor)\n\t\t\t\tconst subgroupLine = theme.fg(\"muted\", entry.subgroup.label);\n\t\t\t\tlines.push(truncateToWidth(` ${subgroupLine}`, width, \"\"));\n\t\t\t} else {\n\t\t\t\t// Resource item (cursor only on items)\n\t\t\t\tconst item = entry.item;\n\t\t\t\tconst cursor = isSelected ? \"> \" : \" \";\n\t\t\t\tconst checkbox = item.enabled ? theme.fg(\"success\", \"[x]\") : theme.fg(\"dim\", \"[ ]\");\n\t\t\t\tconst name = isSelected ? theme.bold(item.displayName) : item.displayName;\n\t\t\t\tlines.push(truncateToWidth(`${cursor} ${checkbox} ${name}`, width, \"...\"));\n\t\t\t}\n\t\t}\n\n\t\t// Scroll indicator\n\t\tif (startIndex > 0 || endIndex < this.filteredItems.length) {\n\t\t\tconst itemCount = this.filteredItems.filter((e) => e.type === \"item\").length;\n\t\t\tconst currentItemIndex =\n\t\t\t\tthis.filteredItems.slice(0, this.selectedIndex).filter((e) => e.type === \"item\").length + 1;\n\t\t\tlines.push(theme.fg(\"dim\", ` (${currentItemIndex}/${itemCount})`));\n\t\t}\n\n\t\treturn lines;\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tthis.selectedIndex = this.findNextItem(this.selectedIndex, -1);\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.down\")) {\n\t\t\tthis.selectedIndex = this.findNextItem(this.selectedIndex, 1);\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.pageUp\")) {\n\t\t\t// Jump up by maxVisible, then find nearest item\n\t\t\tlet target = Math.max(0, this.selectedIndex - this.maxVisible);\n\t\t\twhile (target < this.filteredItems.length && this.filteredItems[target].type !== \"item\") {\n\t\t\t\ttarget++;\n\t\t\t}\n\t\t\tif (target < this.filteredItems.length) {\n\t\t\t\tthis.selectedIndex = target;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.pageDown\")) {\n\t\t\t// Jump down by maxVisible, then find nearest item\n\t\t\tlet target = Math.min(this.filteredItems.length - 1, this.selectedIndex + this.maxVisible);\n\t\t\twhile (target >= 0 && this.filteredItems[target].type !== \"item\") {\n\t\t\t\ttarget--;\n\t\t\t}\n\t\t\tif (target >= 0) {\n\t\t\t\tthis.selectedIndex = target;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onCancel?.();\n\t\t\treturn;\n\t\t}\n\t\tif (matchesKey(data, \"ctrl+c\")) {\n\t\t\tthis.onExit?.();\n\t\t\treturn;\n\t\t}\n\t\tif (data === \" \" || kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tconst entry = this.filteredItems[this.selectedIndex];\n\t\t\tif (entry?.type === \"item\") {\n\t\t\t\tconst newEnabled = !entry.item.enabled;\n\t\t\t\tthis.toggleResource(entry.item, newEnabled);\n\t\t\t\tthis.updateItem(entry.item, newEnabled);\n\t\t\t\tthis.onToggle?.(entry.item, newEnabled);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass to search input\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.filterItems(this.searchInput.getValue());\n\t}\n\n\tprivate toggleResource(item: ResourceItem, enabled: boolean): void {\n\t\tif (item.metadata.origin === \"top-level\") {\n\t\t\tthis.toggleTopLevelResource(item, enabled);\n\t\t} else {\n\t\t\tthis.togglePackageResource(item, enabled);\n\t\t}\n\t}\n\n\tprivate toggleTopLevelResource(item: ResourceItem, enabled: boolean): void {\n\t\tconst scope = item.metadata.scope as \"user\" | \"project\";\n\t\tconst settings =\n\t\t\tscope === \"project\" ? this.settingsManager.getProjectSettings() : this.settingsManager.getGlobalSettings();\n\n\t\tconst arrayKey = item.resourceType as \"extensions\" | \"skills\" | \"prompts\" | \"themes\";\n\t\tconst current = (settings[arrayKey] ?? []) as string[];\n\n\t\t// Generate pattern for this resource\n\t\tconst pattern = this.getResourcePattern(item);\n\t\tconst disablePattern = `-${pattern}`;\n\t\tconst enablePattern = `+${pattern}`;\n\n\t\t// Filter out existing patterns for this resource\n\t\tconst updated = current.filter((p) => {\n\t\t\tconst stripped = p.startsWith(\"!\") || p.startsWith(\"+\") || p.startsWith(\"-\") ? p.slice(1) : p;\n\t\t\treturn stripped !== pattern;\n\t\t});\n\n\t\tif (enabled) {\n\t\t\tupdated.push(enablePattern);\n\t\t} else {\n\t\t\tupdated.push(disablePattern);\n\t\t}\n\n\t\tif (scope === \"project\") {\n\t\t\tif (arrayKey === \"extensions\") {\n\t\t\t\tthis.settingsManager.setProjectExtensionPaths(updated);\n\t\t\t} else if (arrayKey === \"skills\") {\n\t\t\t\tthis.settingsManager.setProjectSkillPaths(updated);\n\t\t\t} else if (arrayKey === \"prompts\") {\n\t\t\t\tthis.settingsManager.setProjectPromptTemplatePaths(updated);\n\t\t\t} else if (arrayKey === \"themes\") {\n\t\t\t\tthis.settingsManager.setProjectThemePaths(updated);\n\t\t\t}\n\t\t} else {\n\t\t\tif (arrayKey === \"extensions\") {\n\t\t\t\tthis.settingsManager.setExtensionPaths(updated);\n\t\t\t} else if (arrayKey === \"skills\") {\n\t\t\t\tthis.settingsManager.setSkillPaths(updated);\n\t\t\t} else if (arrayKey === \"prompts\") {\n\t\t\t\tthis.settingsManager.setPromptTemplatePaths(updated);\n\t\t\t} else if (arrayKey === \"themes\") {\n\t\t\t\tthis.settingsManager.setThemePaths(updated);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate togglePackageResource(item: ResourceItem, enabled: boolean): void {\n\t\tconst scope = item.metadata.scope as \"user\" | \"project\";\n\t\tconst settings =\n\t\t\tscope === \"project\" ? this.settingsManager.getProjectSettings() : this.settingsManager.getGlobalSettings();\n\n\t\tconst packages = [...(settings.packages ?? [])] as PackageSource[];\n\t\tconst pkgIndex = packages.findIndex((pkg) => {\n\t\t\tconst source = typeof pkg === \"string\" ? pkg : pkg.source;\n\t\t\treturn source === item.metadata.source;\n\t\t});\n\n\t\tif (pkgIndex === -1) return;\n\n\t\tlet pkg = packages[pkgIndex];\n\n\t\t// Convert string to object form if needed\n\t\tif (typeof pkg === \"string\") {\n\t\t\tpkg = { source: pkg };\n\t\t\tpackages[pkgIndex] = pkg;\n\t\t}\n\n\t\t// Get the resource array for this type\n\t\tconst arrayKey = item.resourceType as \"extensions\" | \"skills\" | \"prompts\" | \"themes\";\n\t\tconst current = (pkg[arrayKey] ?? []) as string[];\n\n\t\t// Generate pattern relative to package root\n\t\tconst pattern = this.getPackageResourcePattern(item);\n\t\tconst disablePattern = `-${pattern}`;\n\t\tconst enablePattern = `+${pattern}`;\n\n\t\t// Filter out existing patterns for this resource\n\t\tconst updated = current.filter((p) => {\n\t\t\tconst stripped = p.startsWith(\"!\") || p.startsWith(\"+\") || p.startsWith(\"-\") ? p.slice(1) : p;\n\t\t\treturn stripped !== pattern;\n\t\t});\n\n\t\tif (enabled) {\n\t\t\tupdated.push(enablePattern);\n\t\t} else {\n\t\t\tupdated.push(disablePattern);\n\t\t}\n\n\t\t(pkg as Record<string, unknown>)[arrayKey] = updated.length > 0 ? updated : undefined;\n\n\t\t// Clean up empty filter object\n\t\tconst hasFilters = [\"extensions\", \"skills\", \"prompts\", \"themes\"].some(\n\t\t\t(k) => (pkg as Record<string, unknown>)[k] !== undefined,\n\t\t);\n\t\tif (!hasFilters) {\n\t\t\tpackages[pkgIndex] = (pkg as { source: string }).source;\n\t\t}\n\n\t\tif (scope === \"project\") {\n\t\t\tthis.settingsManager.setProjectPackages(packages);\n\t\t} else {\n\t\t\tthis.settingsManager.setPackages(packages);\n\t\t}\n\t}\n\n\tprivate getTopLevelBaseDir(scope: \"user\" | \"project\"): string {\n\t\treturn scope === \"project\" ? join(this.cwd, CONFIG_DIR_NAME) : this.agentDir;\n\t}\n\n\tprivate getResourcePattern(item: ResourceItem): string {\n\t\tconst scope = item.metadata.scope as \"user\" | \"project\";\n\t\tconst baseDir = this.getTopLevelBaseDir(scope);\n\t\treturn relative(baseDir, item.path);\n\t}\n\n\tprivate getPackageResourcePattern(item: ResourceItem): string {\n\t\tconst baseDir = item.metadata.baseDir ?? dirname(item.path);\n\t\treturn relative(baseDir, item.path);\n\t}\n}\n\nexport class ConfigSelectorComponent extends Container implements Focusable {\n\tprivate resourceList: ResourceList;\n\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.resourceList.focused = value;\n\t}\n\n\tconstructor(\n\t\tresolvedPaths: ResolvedPaths,\n\t\tsettingsManager: SettingsManager,\n\t\tcwd: string,\n\t\tagentDir: string,\n\t\tonClose: () => void,\n\t\tonExit: () => void,\n\t\trequestRender: () => void,\n\t) {\n\t\tsuper();\n\n\t\tconst groups = buildGroups(resolvedPaths);\n\n\t\t// Minimal chrome — no filled borders.\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new ConfigSelectorHeader());\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Resource list\n\t\tthis.resourceList = new ResourceList(groups, settingsManager, cwd, agentDir);\n\t\tthis.resourceList.onCancel = onClose;\n\t\tthis.resourceList.onExit = onExit;\n\t\tthis.resourceList.onToggle = () => requestRender();\n\t\tthis.addChild(this.resourceList);\n\n\t\tthis.addChild(new Spacer(1));\n\t}\n\n\tgetResourceList(): ResourceList {\n\t\treturn this.resourceList;\n\t}\n}\n"]}
|
|
@@ -6,7 +6,6 @@ import { basename, dirname, join, relative } from "node:path";
|
|
|
6
6
|
import { Container, getKeybindings, Input, matchesKey, Spacer, truncateToWidth, visibleWidth, } from "@kolisachint/hoocode-tui";
|
|
7
7
|
import { CONFIG_DIR_NAME } from "../../../config.js";
|
|
8
8
|
import { theme } from "../theme/theme.js";
|
|
9
|
-
import { DynamicBorder } from "./dynamic-border.js";
|
|
10
9
|
import { rawKeyHint } from "./keybinding-hints.js";
|
|
11
10
|
const RESOURCE_TYPE_LABELS = {
|
|
12
11
|
extensions: "Extensions",
|
|
@@ -480,9 +479,7 @@ export class ConfigSelectorComponent extends Container {
|
|
|
480
479
|
constructor(resolvedPaths, settingsManager, cwd, agentDir, onClose, onExit, requestRender) {
|
|
481
480
|
super();
|
|
482
481
|
const groups = buildGroups(resolvedPaths);
|
|
483
|
-
//
|
|
484
|
-
this.addChild(new Spacer(1));
|
|
485
|
-
this.addChild(new DynamicBorder());
|
|
482
|
+
// Minimal chrome — no filled borders.
|
|
486
483
|
this.addChild(new Spacer(1));
|
|
487
484
|
this.addChild(new ConfigSelectorHeader());
|
|
488
485
|
this.addChild(new Spacer(1));
|
|
@@ -492,9 +489,7 @@ export class ConfigSelectorComponent extends Container {
|
|
|
492
489
|
this.resourceList.onExit = onExit;
|
|
493
490
|
this.resourceList.onToggle = () => requestRender();
|
|
494
491
|
this.addChild(this.resourceList);
|
|
495
|
-
// Bottom border
|
|
496
492
|
this.addChild(new Spacer(1));
|
|
497
|
-
this.addChild(new DynamicBorder());
|
|
498
493
|
}
|
|
499
494
|
getResourceList() {
|
|
500
495
|
return this.resourceList;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/config-selector.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * TUI component for managing package resources (enable/disable)\n */\n\nimport { homedir } from \"node:os\";\nimport { basename, dirname, join, relative } from \"node:path\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tgetKeybindings,\n\tInput,\n\tmatchesKey,\n\tSpacer,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@kolisachint/hoocode-tui\";\nimport { CONFIG_DIR_NAME } from \"../../../config.js\";\nimport type { PathMetadata, ResolvedPaths, ResolvedResource } from \"../../../core/package-manager.js\";\nimport type { PackageSource, SettingsManager } from \"../../../core/settings-manager.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { rawKeyHint } from \"./keybinding-hints.js\";\n\ntype ResourceType = \"extensions\" | \"skills\" | \"prompts\" | \"themes\";\n\nconst RESOURCE_TYPE_LABELS: Record<ResourceType, string> = {\n\textensions: \"Extensions\",\n\tskills: \"Skills\",\n\tprompts: \"Prompts\",\n\tthemes: \"Themes\",\n};\n\ninterface ResourceItem {\n\tpath: string;\n\tenabled: boolean;\n\tmetadata: PathMetadata;\n\tresourceType: ResourceType;\n\tdisplayName: string;\n\tgroupKey: string;\n\tsubgroupKey: string;\n}\n\ninterface ResourceSubgroup {\n\ttype: ResourceType;\n\tlabel: string;\n\titems: ResourceItem[];\n}\n\ninterface ResourceGroup {\n\tkey: string;\n\tlabel: string;\n\tscope: \"user\" | \"project\" | \"temporary\";\n\torigin: \"package\" | \"top-level\";\n\tsource: string;\n\tsubgroups: ResourceSubgroup[];\n}\n\nfunction formatBaseDir(baseDir: string): string {\n\tconst homeDir = homedir();\n\tlet displayPath: string;\n\n\tif (baseDir === homeDir) {\n\t\tdisplayPath = \"~\";\n\t} else if (baseDir.startsWith(homeDir)) {\n\t\t// Replace home prefix with ~, normalize separators for display\n\t\tconst rest = baseDir.slice(homeDir.length);\n\t\tdisplayPath = `~${rest.replace(/\\\\/g, \"/\")}`;\n\t} else {\n\t\tdisplayPath = baseDir.replace(/\\\\/g, \"/\");\n\t}\n\n\treturn displayPath.endsWith(\"/\") ? displayPath : `${displayPath}/`;\n}\n\nfunction getGroupLabel(metadata: PathMetadata): string {\n\tif (metadata.origin === \"package\") {\n\t\treturn `${metadata.source} (${metadata.scope})`;\n\t}\n\t// Top-level resources\n\tif (metadata.source === \"auto\") {\n\t\tif (metadata.baseDir) {\n\t\t\treturn metadata.scope === \"user\"\n\t\t\t\t? `User (${formatBaseDir(metadata.baseDir)})`\n\t\t\t\t: `Project (${formatBaseDir(metadata.baseDir)})`;\n\t\t}\n\t\treturn metadata.scope === \"user\" ? \"User (~/.hoocode/agent/)\" : \"Project (.hoocode/)\";\n\t}\n\treturn metadata.scope === \"user\" ? \"User settings\" : \"Project settings\";\n}\n\nfunction buildGroups(resolved: ResolvedPaths): ResourceGroup[] {\n\tconst groupMap = new Map<string, ResourceGroup>();\n\n\tconst addToGroup = (resources: ResolvedResource[], resourceType: ResourceType) => {\n\t\tfor (const res of resources) {\n\t\t\tconst { path, enabled, metadata } = res;\n\t\t\tconst groupKey = `${metadata.origin}:${metadata.scope}:${metadata.source}:${metadata.baseDir ?? \"\"}`;\n\n\t\t\tif (!groupMap.has(groupKey)) {\n\t\t\t\tgroupMap.set(groupKey, {\n\t\t\t\t\tkey: groupKey,\n\t\t\t\t\tlabel: getGroupLabel(metadata),\n\t\t\t\t\tscope: metadata.scope,\n\t\t\t\t\torigin: metadata.origin,\n\t\t\t\t\tsource: metadata.source,\n\t\t\t\t\tsubgroups: [],\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst group = groupMap.get(groupKey)!;\n\t\t\tconst subgroupKey = `${groupKey}:${resourceType}`;\n\n\t\t\tlet subgroup = group.subgroups.find((sg) => sg.type === resourceType);\n\t\t\tif (!subgroup) {\n\t\t\t\tsubgroup = {\n\t\t\t\t\ttype: resourceType,\n\t\t\t\t\tlabel: RESOURCE_TYPE_LABELS[resourceType],\n\t\t\t\t\titems: [],\n\t\t\t\t};\n\t\t\t\tgroup.subgroups.push(subgroup);\n\t\t\t}\n\n\t\t\tconst fileName = basename(path);\n\t\t\tconst parentFolder = basename(dirname(path));\n\t\t\tlet displayName: string;\n\t\t\tif (resourceType === \"extensions\" && parentFolder !== \"extensions\") {\n\t\t\t\tdisplayName = `${parentFolder}/${fileName}`;\n\t\t\t} else if (resourceType === \"skills\" && fileName === \"SKILL.md\") {\n\t\t\t\tdisplayName = parentFolder;\n\t\t\t} else {\n\t\t\t\tdisplayName = fileName;\n\t\t\t}\n\t\t\tsubgroup.items.push({\n\t\t\t\tpath,\n\t\t\t\tenabled,\n\t\t\t\tmetadata,\n\t\t\t\tresourceType,\n\t\t\t\tdisplayName,\n\t\t\t\tgroupKey,\n\t\t\t\tsubgroupKey,\n\t\t\t});\n\t\t}\n\t};\n\n\taddToGroup(resolved.extensions, \"extensions\");\n\taddToGroup(resolved.skills, \"skills\");\n\taddToGroup(resolved.prompts, \"prompts\");\n\taddToGroup(resolved.themes, \"themes\");\n\n\t// Sort groups: packages first, then top-level; user before project\n\tconst groups = Array.from(groupMap.values());\n\tgroups.sort((a, b) => {\n\t\tif (a.origin !== b.origin) {\n\t\t\treturn a.origin === \"package\" ? -1 : 1;\n\t\t}\n\t\tif (a.scope !== b.scope) {\n\t\t\treturn a.scope === \"user\" ? -1 : 1;\n\t\t}\n\t\treturn a.source.localeCompare(b.source);\n\t});\n\n\t// Sort subgroups within each group by type order, and items by name\n\tconst typeOrder: Record<ResourceType, number> = { extensions: 0, skills: 1, prompts: 2, themes: 3 };\n\tfor (const group of groups) {\n\t\tgroup.subgroups.sort((a, b) => typeOrder[a.type] - typeOrder[b.type]);\n\t\tfor (const subgroup of group.subgroups) {\n\t\t\tsubgroup.items.sort((a, b) => a.displayName.localeCompare(b.displayName));\n\t\t}\n\t}\n\n\treturn groups;\n}\n\ntype FlatEntry =\n\t| { type: \"group\"; group: ResourceGroup }\n\t| { type: \"subgroup\"; subgroup: ResourceSubgroup; group: ResourceGroup }\n\t| { type: \"item\"; item: ResourceItem };\n\nclass ConfigSelectorHeader implements Component {\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst title = theme.bold(\"Resource Configuration\");\n\t\tconst sep = theme.fg(\"muted\", \" · \");\n\t\tconst hint = rawKeyHint(\"space\", \"toggle\") + sep + rawKeyHint(\"esc\", \"close\");\n\t\tconst hintWidth = visibleWidth(hint);\n\t\tconst titleWidth = visibleWidth(title);\n\t\tconst spacing = Math.max(1, width - titleWidth - hintWidth);\n\n\t\treturn [\n\t\t\ttruncateToWidth(`${title}${\" \".repeat(spacing)}${hint}`, width, \"\"),\n\t\t\ttheme.fg(\"muted\", \"Type to filter resources\"),\n\t\t];\n\t}\n}\n\nclass ResourceList implements Component, Focusable {\n\tprivate groups: ResourceGroup[];\n\tprivate flatItems: FlatEntry[] = [];\n\tprivate filteredItems: FlatEntry[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\tprivate maxVisible = 15;\n\tprivate settingsManager: SettingsManager;\n\tprivate cwd: string;\n\tprivate agentDir: string;\n\n\tpublic onCancel?: () => void;\n\tpublic onExit?: () => void;\n\tpublic onToggle?: (item: ResourceItem, newEnabled: boolean) => void;\n\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\n\tconstructor(groups: ResourceGroup[], settingsManager: SettingsManager, cwd: string, agentDir: string) {\n\t\tthis.groups = groups;\n\t\tthis.settingsManager = settingsManager;\n\t\tthis.cwd = cwd;\n\t\tthis.agentDir = agentDir;\n\t\tthis.searchInput = new Input();\n\t\tthis.buildFlatList();\n\t\tthis.filteredItems = [...this.flatItems];\n\t}\n\n\tprivate buildFlatList(): void {\n\t\tthis.flatItems = [];\n\t\tfor (const group of this.groups) {\n\t\t\tthis.flatItems.push({ type: \"group\", group });\n\t\t\tfor (const subgroup of group.subgroups) {\n\t\t\t\tthis.flatItems.push({ type: \"subgroup\", subgroup, group });\n\t\t\t\tfor (const item of subgroup.items) {\n\t\t\t\t\tthis.flatItems.push({ type: \"item\", item });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Start selection on first item (not header)\n\t\tthis.selectedIndex = this.flatItems.findIndex((e) => e.type === \"item\");\n\t\tif (this.selectedIndex < 0) this.selectedIndex = 0;\n\t}\n\n\tprivate findNextItem(fromIndex: number, direction: 1 | -1): number {\n\t\tlet idx = fromIndex + direction;\n\t\twhile (idx >= 0 && idx < this.filteredItems.length) {\n\t\t\tif (this.filteredItems[idx].type === \"item\") {\n\t\t\t\treturn idx;\n\t\t\t}\n\t\t\tidx += direction;\n\t\t}\n\t\treturn fromIndex; // Stay at current if no item found\n\t}\n\n\tprivate filterItems(query: string): void {\n\t\tif (!query.trim()) {\n\t\t\tthis.filteredItems = [...this.flatItems];\n\t\t\tthis.selectFirstItem();\n\t\t\treturn;\n\t\t}\n\n\t\tconst lowerQuery = query.toLowerCase();\n\t\tconst matchingItems = new Set<ResourceItem>();\n\t\tconst matchingSubgroups = new Set<ResourceSubgroup>();\n\t\tconst matchingGroups = new Set<ResourceGroup>();\n\n\t\tfor (const entry of this.flatItems) {\n\t\t\tif (entry.type === \"item\") {\n\t\t\t\tconst item = entry.item;\n\t\t\t\tif (\n\t\t\t\t\titem.displayName.toLowerCase().includes(lowerQuery) ||\n\t\t\t\t\titem.resourceType.toLowerCase().includes(lowerQuery) ||\n\t\t\t\t\titem.path.toLowerCase().includes(lowerQuery)\n\t\t\t\t) {\n\t\t\t\t\tmatchingItems.add(item);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Find which subgroups and groups contain matching items\n\t\tfor (const group of this.groups) {\n\t\t\tfor (const subgroup of group.subgroups) {\n\t\t\t\tfor (const item of subgroup.items) {\n\t\t\t\t\tif (matchingItems.has(item)) {\n\t\t\t\t\t\tmatchingSubgroups.add(subgroup);\n\t\t\t\t\t\tmatchingGroups.add(group);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis.filteredItems = [];\n\t\tfor (const entry of this.flatItems) {\n\t\t\tif (entry.type === \"group\" && matchingGroups.has(entry.group)) {\n\t\t\t\tthis.filteredItems.push(entry);\n\t\t\t} else if (entry.type === \"subgroup\" && matchingSubgroups.has(entry.subgroup)) {\n\t\t\t\tthis.filteredItems.push(entry);\n\t\t\t} else if (entry.type === \"item\" && matchingItems.has(entry.item)) {\n\t\t\t\tthis.filteredItems.push(entry);\n\t\t\t}\n\t\t}\n\n\t\tthis.selectFirstItem();\n\t}\n\n\tprivate selectFirstItem(): void {\n\t\tconst firstItemIndex = this.filteredItems.findIndex((e) => e.type === \"item\");\n\t\tthis.selectedIndex = firstItemIndex >= 0 ? firstItemIndex : 0;\n\t}\n\n\tupdateItem(item: ResourceItem, enabled: boolean): void {\n\t\titem.enabled = enabled;\n\t\t// Update in groups too\n\t\tfor (const group of this.groups) {\n\t\t\tfor (const subgroup of group.subgroups) {\n\t\t\t\tconst found = subgroup.items.find((i) => i.path === item.path && i.resourceType === item.resourceType);\n\t\t\t\tif (found) {\n\t\t\t\t\tfound.enabled = enabled;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\n\t\t// Search input\n\t\tlines.push(...this.searchInput.render(width));\n\t\tlines.push(\"\");\n\n\t\tif (this.filteredItems.length === 0) {\n\t\t\tlines.push(theme.fg(\"muted\", \" No resources found\"));\n\t\t\treturn lines;\n\t\t}\n\n\t\t// Calculate visible range\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);\n\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst entry = this.filteredItems[i];\n\t\t\tconst isSelected = i === this.selectedIndex;\n\n\t\t\tif (entry.type === \"group\") {\n\t\t\t\t// Main group header (no cursor)\n\t\t\t\tconst groupLine = theme.fg(\"accent\", theme.bold(entry.group.label));\n\t\t\t\tlines.push(truncateToWidth(` ${groupLine}`, width, \"\"));\n\t\t\t} else if (entry.type === \"subgroup\") {\n\t\t\t\t// Subgroup header (indented, no cursor)\n\t\t\t\tconst subgroupLine = theme.fg(\"muted\", entry.subgroup.label);\n\t\t\t\tlines.push(truncateToWidth(` ${subgroupLine}`, width, \"\"));\n\t\t\t} else {\n\t\t\t\t// Resource item (cursor only on items)\n\t\t\t\tconst item = entry.item;\n\t\t\t\tconst cursor = isSelected ? \"> \" : \" \";\n\t\t\t\tconst checkbox = item.enabled ? theme.fg(\"success\", \"[x]\") : theme.fg(\"dim\", \"[ ]\");\n\t\t\t\tconst name = isSelected ? theme.bold(item.displayName) : item.displayName;\n\t\t\t\tlines.push(truncateToWidth(`${cursor} ${checkbox} ${name}`, width, \"...\"));\n\t\t\t}\n\t\t}\n\n\t\t// Scroll indicator\n\t\tif (startIndex > 0 || endIndex < this.filteredItems.length) {\n\t\t\tconst itemCount = this.filteredItems.filter((e) => e.type === \"item\").length;\n\t\t\tconst currentItemIndex =\n\t\t\t\tthis.filteredItems.slice(0, this.selectedIndex).filter((e) => e.type === \"item\").length + 1;\n\t\t\tlines.push(theme.fg(\"dim\", ` (${currentItemIndex}/${itemCount})`));\n\t\t}\n\n\t\treturn lines;\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tthis.selectedIndex = this.findNextItem(this.selectedIndex, -1);\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.down\")) {\n\t\t\tthis.selectedIndex = this.findNextItem(this.selectedIndex, 1);\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.pageUp\")) {\n\t\t\t// Jump up by maxVisible, then find nearest item\n\t\t\tlet target = Math.max(0, this.selectedIndex - this.maxVisible);\n\t\t\twhile (target < this.filteredItems.length && this.filteredItems[target].type !== \"item\") {\n\t\t\t\ttarget++;\n\t\t\t}\n\t\t\tif (target < this.filteredItems.length) {\n\t\t\t\tthis.selectedIndex = target;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.pageDown\")) {\n\t\t\t// Jump down by maxVisible, then find nearest item\n\t\t\tlet target = Math.min(this.filteredItems.length - 1, this.selectedIndex + this.maxVisible);\n\t\t\twhile (target >= 0 && this.filteredItems[target].type !== \"item\") {\n\t\t\t\ttarget--;\n\t\t\t}\n\t\t\tif (target >= 0) {\n\t\t\t\tthis.selectedIndex = target;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onCancel?.();\n\t\t\treturn;\n\t\t}\n\t\tif (matchesKey(data, \"ctrl+c\")) {\n\t\t\tthis.onExit?.();\n\t\t\treturn;\n\t\t}\n\t\tif (data === \" \" || kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tconst entry = this.filteredItems[this.selectedIndex];\n\t\t\tif (entry?.type === \"item\") {\n\t\t\t\tconst newEnabled = !entry.item.enabled;\n\t\t\t\tthis.toggleResource(entry.item, newEnabled);\n\t\t\t\tthis.updateItem(entry.item, newEnabled);\n\t\t\t\tthis.onToggle?.(entry.item, newEnabled);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass to search input\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.filterItems(this.searchInput.getValue());\n\t}\n\n\tprivate toggleResource(item: ResourceItem, enabled: boolean): void {\n\t\tif (item.metadata.origin === \"top-level\") {\n\t\t\tthis.toggleTopLevelResource(item, enabled);\n\t\t} else {\n\t\t\tthis.togglePackageResource(item, enabled);\n\t\t}\n\t}\n\n\tprivate toggleTopLevelResource(item: ResourceItem, enabled: boolean): void {\n\t\tconst scope = item.metadata.scope as \"user\" | \"project\";\n\t\tconst settings =\n\t\t\tscope === \"project\" ? this.settingsManager.getProjectSettings() : this.settingsManager.getGlobalSettings();\n\n\t\tconst arrayKey = item.resourceType as \"extensions\" | \"skills\" | \"prompts\" | \"themes\";\n\t\tconst current = (settings[arrayKey] ?? []) as string[];\n\n\t\t// Generate pattern for this resource\n\t\tconst pattern = this.getResourcePattern(item);\n\t\tconst disablePattern = `-${pattern}`;\n\t\tconst enablePattern = `+${pattern}`;\n\n\t\t// Filter out existing patterns for this resource\n\t\tconst updated = current.filter((p) => {\n\t\t\tconst stripped = p.startsWith(\"!\") || p.startsWith(\"+\") || p.startsWith(\"-\") ? p.slice(1) : p;\n\t\t\treturn stripped !== pattern;\n\t\t});\n\n\t\tif (enabled) {\n\t\t\tupdated.push(enablePattern);\n\t\t} else {\n\t\t\tupdated.push(disablePattern);\n\t\t}\n\n\t\tif (scope === \"project\") {\n\t\t\tif (arrayKey === \"extensions\") {\n\t\t\t\tthis.settingsManager.setProjectExtensionPaths(updated);\n\t\t\t} else if (arrayKey === \"skills\") {\n\t\t\t\tthis.settingsManager.setProjectSkillPaths(updated);\n\t\t\t} else if (arrayKey === \"prompts\") {\n\t\t\t\tthis.settingsManager.setProjectPromptTemplatePaths(updated);\n\t\t\t} else if (arrayKey === \"themes\") {\n\t\t\t\tthis.settingsManager.setProjectThemePaths(updated);\n\t\t\t}\n\t\t} else {\n\t\t\tif (arrayKey === \"extensions\") {\n\t\t\t\tthis.settingsManager.setExtensionPaths(updated);\n\t\t\t} else if (arrayKey === \"skills\") {\n\t\t\t\tthis.settingsManager.setSkillPaths(updated);\n\t\t\t} else if (arrayKey === \"prompts\") {\n\t\t\t\tthis.settingsManager.setPromptTemplatePaths(updated);\n\t\t\t} else if (arrayKey === \"themes\") {\n\t\t\t\tthis.settingsManager.setThemePaths(updated);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate togglePackageResource(item: ResourceItem, enabled: boolean): void {\n\t\tconst scope = item.metadata.scope as \"user\" | \"project\";\n\t\tconst settings =\n\t\t\tscope === \"project\" ? this.settingsManager.getProjectSettings() : this.settingsManager.getGlobalSettings();\n\n\t\tconst packages = [...(settings.packages ?? [])] as PackageSource[];\n\t\tconst pkgIndex = packages.findIndex((pkg) => {\n\t\t\tconst source = typeof pkg === \"string\" ? pkg : pkg.source;\n\t\t\treturn source === item.metadata.source;\n\t\t});\n\n\t\tif (pkgIndex === -1) return;\n\n\t\tlet pkg = packages[pkgIndex];\n\n\t\t// Convert string to object form if needed\n\t\tif (typeof pkg === \"string\") {\n\t\t\tpkg = { source: pkg };\n\t\t\tpackages[pkgIndex] = pkg;\n\t\t}\n\n\t\t// Get the resource array for this type\n\t\tconst arrayKey = item.resourceType as \"extensions\" | \"skills\" | \"prompts\" | \"themes\";\n\t\tconst current = (pkg[arrayKey] ?? []) as string[];\n\n\t\t// Generate pattern relative to package root\n\t\tconst pattern = this.getPackageResourcePattern(item);\n\t\tconst disablePattern = `-${pattern}`;\n\t\tconst enablePattern = `+${pattern}`;\n\n\t\t// Filter out existing patterns for this resource\n\t\tconst updated = current.filter((p) => {\n\t\t\tconst stripped = p.startsWith(\"!\") || p.startsWith(\"+\") || p.startsWith(\"-\") ? p.slice(1) : p;\n\t\t\treturn stripped !== pattern;\n\t\t});\n\n\t\tif (enabled) {\n\t\t\tupdated.push(enablePattern);\n\t\t} else {\n\t\t\tupdated.push(disablePattern);\n\t\t}\n\n\t\t(pkg as Record<string, unknown>)[arrayKey] = updated.length > 0 ? updated : undefined;\n\n\t\t// Clean up empty filter object\n\t\tconst hasFilters = [\"extensions\", \"skills\", \"prompts\", \"themes\"].some(\n\t\t\t(k) => (pkg as Record<string, unknown>)[k] !== undefined,\n\t\t);\n\t\tif (!hasFilters) {\n\t\t\tpackages[pkgIndex] = (pkg as { source: string }).source;\n\t\t}\n\n\t\tif (scope === \"project\") {\n\t\t\tthis.settingsManager.setProjectPackages(packages);\n\t\t} else {\n\t\t\tthis.settingsManager.setPackages(packages);\n\t\t}\n\t}\n\n\tprivate getTopLevelBaseDir(scope: \"user\" | \"project\"): string {\n\t\treturn scope === \"project\" ? join(this.cwd, CONFIG_DIR_NAME) : this.agentDir;\n\t}\n\n\tprivate getResourcePattern(item: ResourceItem): string {\n\t\tconst scope = item.metadata.scope as \"user\" | \"project\";\n\t\tconst baseDir = this.getTopLevelBaseDir(scope);\n\t\treturn relative(baseDir, item.path);\n\t}\n\n\tprivate getPackageResourcePattern(item: ResourceItem): string {\n\t\tconst baseDir = item.metadata.baseDir ?? dirname(item.path);\n\t\treturn relative(baseDir, item.path);\n\t}\n}\n\nexport class ConfigSelectorComponent extends Container implements Focusable {\n\tprivate resourceList: ResourceList;\n\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.resourceList.focused = value;\n\t}\n\n\tconstructor(\n\t\tresolvedPaths: ResolvedPaths,\n\t\tsettingsManager: SettingsManager,\n\t\tcwd: string,\n\t\tagentDir: string,\n\t\tonClose: () => void,\n\t\tonExit: () => void,\n\t\trequestRender: () => void,\n\t) {\n\t\tsuper();\n\n\t\tconst groups = buildGroups(resolvedPaths);\n\n\t\t// Add header\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new ConfigSelectorHeader());\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Resource list\n\t\tthis.resourceList = new ResourceList(groups, settingsManager, cwd, agentDir);\n\t\tthis.resourceList.onCancel = onClose;\n\t\tthis.resourceList.onExit = onExit;\n\t\tthis.resourceList.onToggle = () => requestRender();\n\t\tthis.addChild(this.resourceList);\n\n\t\t// Bottom border\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\tgetResourceList(): ResourceList {\n\t\treturn this.resourceList;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"config-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/config-selector.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * TUI component for managing package resources (enable/disable)\n */\n\nimport { homedir } from \"node:os\";\nimport { basename, dirname, join, relative } from \"node:path\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tgetKeybindings,\n\tInput,\n\tmatchesKey,\n\tSpacer,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@kolisachint/hoocode-tui\";\nimport { CONFIG_DIR_NAME } from \"../../../config.js\";\nimport type { PathMetadata, ResolvedPaths, ResolvedResource } from \"../../../core/package-manager.js\";\nimport type { PackageSource, SettingsManager } from \"../../../core/settings-manager.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { rawKeyHint } from \"./keybinding-hints.js\";\n\ntype ResourceType = \"extensions\" | \"skills\" | \"prompts\" | \"themes\";\n\nconst RESOURCE_TYPE_LABELS: Record<ResourceType, string> = {\n\textensions: \"Extensions\",\n\tskills: \"Skills\",\n\tprompts: \"Prompts\",\n\tthemes: \"Themes\",\n};\n\ninterface ResourceItem {\n\tpath: string;\n\tenabled: boolean;\n\tmetadata: PathMetadata;\n\tresourceType: ResourceType;\n\tdisplayName: string;\n\tgroupKey: string;\n\tsubgroupKey: string;\n}\n\ninterface ResourceSubgroup {\n\ttype: ResourceType;\n\tlabel: string;\n\titems: ResourceItem[];\n}\n\ninterface ResourceGroup {\n\tkey: string;\n\tlabel: string;\n\tscope: \"user\" | \"project\" | \"temporary\";\n\torigin: \"package\" | \"top-level\";\n\tsource: string;\n\tsubgroups: ResourceSubgroup[];\n}\n\nfunction formatBaseDir(baseDir: string): string {\n\tconst homeDir = homedir();\n\tlet displayPath: string;\n\n\tif (baseDir === homeDir) {\n\t\tdisplayPath = \"~\";\n\t} else if (baseDir.startsWith(homeDir)) {\n\t\t// Replace home prefix with ~, normalize separators for display\n\t\tconst rest = baseDir.slice(homeDir.length);\n\t\tdisplayPath = `~${rest.replace(/\\\\/g, \"/\")}`;\n\t} else {\n\t\tdisplayPath = baseDir.replace(/\\\\/g, \"/\");\n\t}\n\n\treturn displayPath.endsWith(\"/\") ? displayPath : `${displayPath}/`;\n}\n\nfunction getGroupLabel(metadata: PathMetadata): string {\n\tif (metadata.origin === \"package\") {\n\t\treturn `${metadata.source} (${metadata.scope})`;\n\t}\n\t// Top-level resources\n\tif (metadata.source === \"auto\") {\n\t\tif (metadata.baseDir) {\n\t\t\treturn metadata.scope === \"user\"\n\t\t\t\t? `User (${formatBaseDir(metadata.baseDir)})`\n\t\t\t\t: `Project (${formatBaseDir(metadata.baseDir)})`;\n\t\t}\n\t\treturn metadata.scope === \"user\" ? \"User (~/.hoocode/agent/)\" : \"Project (.hoocode/)\";\n\t}\n\treturn metadata.scope === \"user\" ? \"User settings\" : \"Project settings\";\n}\n\nfunction buildGroups(resolved: ResolvedPaths): ResourceGroup[] {\n\tconst groupMap = new Map<string, ResourceGroup>();\n\n\tconst addToGroup = (resources: ResolvedResource[], resourceType: ResourceType) => {\n\t\tfor (const res of resources) {\n\t\t\tconst { path, enabled, metadata } = res;\n\t\t\tconst groupKey = `${metadata.origin}:${metadata.scope}:${metadata.source}:${metadata.baseDir ?? \"\"}`;\n\n\t\t\tif (!groupMap.has(groupKey)) {\n\t\t\t\tgroupMap.set(groupKey, {\n\t\t\t\t\tkey: groupKey,\n\t\t\t\t\tlabel: getGroupLabel(metadata),\n\t\t\t\t\tscope: metadata.scope,\n\t\t\t\t\torigin: metadata.origin,\n\t\t\t\t\tsource: metadata.source,\n\t\t\t\t\tsubgroups: [],\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst group = groupMap.get(groupKey)!;\n\t\t\tconst subgroupKey = `${groupKey}:${resourceType}`;\n\n\t\t\tlet subgroup = group.subgroups.find((sg) => sg.type === resourceType);\n\t\t\tif (!subgroup) {\n\t\t\t\tsubgroup = {\n\t\t\t\t\ttype: resourceType,\n\t\t\t\t\tlabel: RESOURCE_TYPE_LABELS[resourceType],\n\t\t\t\t\titems: [],\n\t\t\t\t};\n\t\t\t\tgroup.subgroups.push(subgroup);\n\t\t\t}\n\n\t\t\tconst fileName = basename(path);\n\t\t\tconst parentFolder = basename(dirname(path));\n\t\t\tlet displayName: string;\n\t\t\tif (resourceType === \"extensions\" && parentFolder !== \"extensions\") {\n\t\t\t\tdisplayName = `${parentFolder}/${fileName}`;\n\t\t\t} else if (resourceType === \"skills\" && fileName === \"SKILL.md\") {\n\t\t\t\tdisplayName = parentFolder;\n\t\t\t} else {\n\t\t\t\tdisplayName = fileName;\n\t\t\t}\n\t\t\tsubgroup.items.push({\n\t\t\t\tpath,\n\t\t\t\tenabled,\n\t\t\t\tmetadata,\n\t\t\t\tresourceType,\n\t\t\t\tdisplayName,\n\t\t\t\tgroupKey,\n\t\t\t\tsubgroupKey,\n\t\t\t});\n\t\t}\n\t};\n\n\taddToGroup(resolved.extensions, \"extensions\");\n\taddToGroup(resolved.skills, \"skills\");\n\taddToGroup(resolved.prompts, \"prompts\");\n\taddToGroup(resolved.themes, \"themes\");\n\n\t// Sort groups: packages first, then top-level; user before project\n\tconst groups = Array.from(groupMap.values());\n\tgroups.sort((a, b) => {\n\t\tif (a.origin !== b.origin) {\n\t\t\treturn a.origin === \"package\" ? -1 : 1;\n\t\t}\n\t\tif (a.scope !== b.scope) {\n\t\t\treturn a.scope === \"user\" ? -1 : 1;\n\t\t}\n\t\treturn a.source.localeCompare(b.source);\n\t});\n\n\t// Sort subgroups within each group by type order, and items by name\n\tconst typeOrder: Record<ResourceType, number> = { extensions: 0, skills: 1, prompts: 2, themes: 3 };\n\tfor (const group of groups) {\n\t\tgroup.subgroups.sort((a, b) => typeOrder[a.type] - typeOrder[b.type]);\n\t\tfor (const subgroup of group.subgroups) {\n\t\t\tsubgroup.items.sort((a, b) => a.displayName.localeCompare(b.displayName));\n\t\t}\n\t}\n\n\treturn groups;\n}\n\ntype FlatEntry =\n\t| { type: \"group\"; group: ResourceGroup }\n\t| { type: \"subgroup\"; subgroup: ResourceSubgroup; group: ResourceGroup }\n\t| { type: \"item\"; item: ResourceItem };\n\nclass ConfigSelectorHeader implements Component {\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst title = theme.bold(\"Resource Configuration\");\n\t\tconst sep = theme.fg(\"muted\", \" · \");\n\t\tconst hint = rawKeyHint(\"space\", \"toggle\") + sep + rawKeyHint(\"esc\", \"close\");\n\t\tconst hintWidth = visibleWidth(hint);\n\t\tconst titleWidth = visibleWidth(title);\n\t\tconst spacing = Math.max(1, width - titleWidth - hintWidth);\n\n\t\treturn [\n\t\t\ttruncateToWidth(`${title}${\" \".repeat(spacing)}${hint}`, width, \"\"),\n\t\t\ttheme.fg(\"muted\", \"Type to filter resources\"),\n\t\t];\n\t}\n}\n\nclass ResourceList implements Component, Focusable {\n\tprivate groups: ResourceGroup[];\n\tprivate flatItems: FlatEntry[] = [];\n\tprivate filteredItems: FlatEntry[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\tprivate maxVisible = 15;\n\tprivate settingsManager: SettingsManager;\n\tprivate cwd: string;\n\tprivate agentDir: string;\n\n\tpublic onCancel?: () => void;\n\tpublic onExit?: () => void;\n\tpublic onToggle?: (item: ResourceItem, newEnabled: boolean) => void;\n\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\n\tconstructor(groups: ResourceGroup[], settingsManager: SettingsManager, cwd: string, agentDir: string) {\n\t\tthis.groups = groups;\n\t\tthis.settingsManager = settingsManager;\n\t\tthis.cwd = cwd;\n\t\tthis.agentDir = agentDir;\n\t\tthis.searchInput = new Input();\n\t\tthis.buildFlatList();\n\t\tthis.filteredItems = [...this.flatItems];\n\t}\n\n\tprivate buildFlatList(): void {\n\t\tthis.flatItems = [];\n\t\tfor (const group of this.groups) {\n\t\t\tthis.flatItems.push({ type: \"group\", group });\n\t\t\tfor (const subgroup of group.subgroups) {\n\t\t\t\tthis.flatItems.push({ type: \"subgroup\", subgroup, group });\n\t\t\t\tfor (const item of subgroup.items) {\n\t\t\t\t\tthis.flatItems.push({ type: \"item\", item });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Start selection on first item (not header)\n\t\tthis.selectedIndex = this.flatItems.findIndex((e) => e.type === \"item\");\n\t\tif (this.selectedIndex < 0) this.selectedIndex = 0;\n\t}\n\n\tprivate findNextItem(fromIndex: number, direction: 1 | -1): number {\n\t\tlet idx = fromIndex + direction;\n\t\twhile (idx >= 0 && idx < this.filteredItems.length) {\n\t\t\tif (this.filteredItems[idx].type === \"item\") {\n\t\t\t\treturn idx;\n\t\t\t}\n\t\t\tidx += direction;\n\t\t}\n\t\treturn fromIndex; // Stay at current if no item found\n\t}\n\n\tprivate filterItems(query: string): void {\n\t\tif (!query.trim()) {\n\t\t\tthis.filteredItems = [...this.flatItems];\n\t\t\tthis.selectFirstItem();\n\t\t\treturn;\n\t\t}\n\n\t\tconst lowerQuery = query.toLowerCase();\n\t\tconst matchingItems = new Set<ResourceItem>();\n\t\tconst matchingSubgroups = new Set<ResourceSubgroup>();\n\t\tconst matchingGroups = new Set<ResourceGroup>();\n\n\t\tfor (const entry of this.flatItems) {\n\t\t\tif (entry.type === \"item\") {\n\t\t\t\tconst item = entry.item;\n\t\t\t\tif (\n\t\t\t\t\titem.displayName.toLowerCase().includes(lowerQuery) ||\n\t\t\t\t\titem.resourceType.toLowerCase().includes(lowerQuery) ||\n\t\t\t\t\titem.path.toLowerCase().includes(lowerQuery)\n\t\t\t\t) {\n\t\t\t\t\tmatchingItems.add(item);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Find which subgroups and groups contain matching items\n\t\tfor (const group of this.groups) {\n\t\t\tfor (const subgroup of group.subgroups) {\n\t\t\t\tfor (const item of subgroup.items) {\n\t\t\t\t\tif (matchingItems.has(item)) {\n\t\t\t\t\t\tmatchingSubgroups.add(subgroup);\n\t\t\t\t\t\tmatchingGroups.add(group);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis.filteredItems = [];\n\t\tfor (const entry of this.flatItems) {\n\t\t\tif (entry.type === \"group\" && matchingGroups.has(entry.group)) {\n\t\t\t\tthis.filteredItems.push(entry);\n\t\t\t} else if (entry.type === \"subgroup\" && matchingSubgroups.has(entry.subgroup)) {\n\t\t\t\tthis.filteredItems.push(entry);\n\t\t\t} else if (entry.type === \"item\" && matchingItems.has(entry.item)) {\n\t\t\t\tthis.filteredItems.push(entry);\n\t\t\t}\n\t\t}\n\n\t\tthis.selectFirstItem();\n\t}\n\n\tprivate selectFirstItem(): void {\n\t\tconst firstItemIndex = this.filteredItems.findIndex((e) => e.type === \"item\");\n\t\tthis.selectedIndex = firstItemIndex >= 0 ? firstItemIndex : 0;\n\t}\n\n\tupdateItem(item: ResourceItem, enabled: boolean): void {\n\t\titem.enabled = enabled;\n\t\t// Update in groups too\n\t\tfor (const group of this.groups) {\n\t\t\tfor (const subgroup of group.subgroups) {\n\t\t\t\tconst found = subgroup.items.find((i) => i.path === item.path && i.resourceType === item.resourceType);\n\t\t\t\tif (found) {\n\t\t\t\t\tfound.enabled = enabled;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\n\t\t// Search input\n\t\tlines.push(...this.searchInput.render(width));\n\t\tlines.push(\"\");\n\n\t\tif (this.filteredItems.length === 0) {\n\t\t\tlines.push(theme.fg(\"muted\", \" No resources found\"));\n\t\t\treturn lines;\n\t\t}\n\n\t\t// Calculate visible range\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);\n\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst entry = this.filteredItems[i];\n\t\t\tconst isSelected = i === this.selectedIndex;\n\n\t\t\tif (entry.type === \"group\") {\n\t\t\t\t// Main group header (no cursor)\n\t\t\t\tconst groupLine = theme.fg(\"accent\", theme.bold(entry.group.label));\n\t\t\t\tlines.push(truncateToWidth(` ${groupLine}`, width, \"\"));\n\t\t\t} else if (entry.type === \"subgroup\") {\n\t\t\t\t// Subgroup header (indented, no cursor)\n\t\t\t\tconst subgroupLine = theme.fg(\"muted\", entry.subgroup.label);\n\t\t\t\tlines.push(truncateToWidth(` ${subgroupLine}`, width, \"\"));\n\t\t\t} else {\n\t\t\t\t// Resource item (cursor only on items)\n\t\t\t\tconst item = entry.item;\n\t\t\t\tconst cursor = isSelected ? \"> \" : \" \";\n\t\t\t\tconst checkbox = item.enabled ? theme.fg(\"success\", \"[x]\") : theme.fg(\"dim\", \"[ ]\");\n\t\t\t\tconst name = isSelected ? theme.bold(item.displayName) : item.displayName;\n\t\t\t\tlines.push(truncateToWidth(`${cursor} ${checkbox} ${name}`, width, \"...\"));\n\t\t\t}\n\t\t}\n\n\t\t// Scroll indicator\n\t\tif (startIndex > 0 || endIndex < this.filteredItems.length) {\n\t\t\tconst itemCount = this.filteredItems.filter((e) => e.type === \"item\").length;\n\t\t\tconst currentItemIndex =\n\t\t\t\tthis.filteredItems.slice(0, this.selectedIndex).filter((e) => e.type === \"item\").length + 1;\n\t\t\tlines.push(theme.fg(\"dim\", ` (${currentItemIndex}/${itemCount})`));\n\t\t}\n\n\t\treturn lines;\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tthis.selectedIndex = this.findNextItem(this.selectedIndex, -1);\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.down\")) {\n\t\t\tthis.selectedIndex = this.findNextItem(this.selectedIndex, 1);\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.pageUp\")) {\n\t\t\t// Jump up by maxVisible, then find nearest item\n\t\t\tlet target = Math.max(0, this.selectedIndex - this.maxVisible);\n\t\t\twhile (target < this.filteredItems.length && this.filteredItems[target].type !== \"item\") {\n\t\t\t\ttarget++;\n\t\t\t}\n\t\t\tif (target < this.filteredItems.length) {\n\t\t\t\tthis.selectedIndex = target;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.pageDown\")) {\n\t\t\t// Jump down by maxVisible, then find nearest item\n\t\t\tlet target = Math.min(this.filteredItems.length - 1, this.selectedIndex + this.maxVisible);\n\t\t\twhile (target >= 0 && this.filteredItems[target].type !== \"item\") {\n\t\t\t\ttarget--;\n\t\t\t}\n\t\t\tif (target >= 0) {\n\t\t\t\tthis.selectedIndex = target;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onCancel?.();\n\t\t\treturn;\n\t\t}\n\t\tif (matchesKey(data, \"ctrl+c\")) {\n\t\t\tthis.onExit?.();\n\t\t\treturn;\n\t\t}\n\t\tif (data === \" \" || kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tconst entry = this.filteredItems[this.selectedIndex];\n\t\t\tif (entry?.type === \"item\") {\n\t\t\t\tconst newEnabled = !entry.item.enabled;\n\t\t\t\tthis.toggleResource(entry.item, newEnabled);\n\t\t\t\tthis.updateItem(entry.item, newEnabled);\n\t\t\t\tthis.onToggle?.(entry.item, newEnabled);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass to search input\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.filterItems(this.searchInput.getValue());\n\t}\n\n\tprivate toggleResource(item: ResourceItem, enabled: boolean): void {\n\t\tif (item.metadata.origin === \"top-level\") {\n\t\t\tthis.toggleTopLevelResource(item, enabled);\n\t\t} else {\n\t\t\tthis.togglePackageResource(item, enabled);\n\t\t}\n\t}\n\n\tprivate toggleTopLevelResource(item: ResourceItem, enabled: boolean): void {\n\t\tconst scope = item.metadata.scope as \"user\" | \"project\";\n\t\tconst settings =\n\t\t\tscope === \"project\" ? this.settingsManager.getProjectSettings() : this.settingsManager.getGlobalSettings();\n\n\t\tconst arrayKey = item.resourceType as \"extensions\" | \"skills\" | \"prompts\" | \"themes\";\n\t\tconst current = (settings[arrayKey] ?? []) as string[];\n\n\t\t// Generate pattern for this resource\n\t\tconst pattern = this.getResourcePattern(item);\n\t\tconst disablePattern = `-${pattern}`;\n\t\tconst enablePattern = `+${pattern}`;\n\n\t\t// Filter out existing patterns for this resource\n\t\tconst updated = current.filter((p) => {\n\t\t\tconst stripped = p.startsWith(\"!\") || p.startsWith(\"+\") || p.startsWith(\"-\") ? p.slice(1) : p;\n\t\t\treturn stripped !== pattern;\n\t\t});\n\n\t\tif (enabled) {\n\t\t\tupdated.push(enablePattern);\n\t\t} else {\n\t\t\tupdated.push(disablePattern);\n\t\t}\n\n\t\tif (scope === \"project\") {\n\t\t\tif (arrayKey === \"extensions\") {\n\t\t\t\tthis.settingsManager.setProjectExtensionPaths(updated);\n\t\t\t} else if (arrayKey === \"skills\") {\n\t\t\t\tthis.settingsManager.setProjectSkillPaths(updated);\n\t\t\t} else if (arrayKey === \"prompts\") {\n\t\t\t\tthis.settingsManager.setProjectPromptTemplatePaths(updated);\n\t\t\t} else if (arrayKey === \"themes\") {\n\t\t\t\tthis.settingsManager.setProjectThemePaths(updated);\n\t\t\t}\n\t\t} else {\n\t\t\tif (arrayKey === \"extensions\") {\n\t\t\t\tthis.settingsManager.setExtensionPaths(updated);\n\t\t\t} else if (arrayKey === \"skills\") {\n\t\t\t\tthis.settingsManager.setSkillPaths(updated);\n\t\t\t} else if (arrayKey === \"prompts\") {\n\t\t\t\tthis.settingsManager.setPromptTemplatePaths(updated);\n\t\t\t} else if (arrayKey === \"themes\") {\n\t\t\t\tthis.settingsManager.setThemePaths(updated);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate togglePackageResource(item: ResourceItem, enabled: boolean): void {\n\t\tconst scope = item.metadata.scope as \"user\" | \"project\";\n\t\tconst settings =\n\t\t\tscope === \"project\" ? this.settingsManager.getProjectSettings() : this.settingsManager.getGlobalSettings();\n\n\t\tconst packages = [...(settings.packages ?? [])] as PackageSource[];\n\t\tconst pkgIndex = packages.findIndex((pkg) => {\n\t\t\tconst source = typeof pkg === \"string\" ? pkg : pkg.source;\n\t\t\treturn source === item.metadata.source;\n\t\t});\n\n\t\tif (pkgIndex === -1) return;\n\n\t\tlet pkg = packages[pkgIndex];\n\n\t\t// Convert string to object form if needed\n\t\tif (typeof pkg === \"string\") {\n\t\t\tpkg = { source: pkg };\n\t\t\tpackages[pkgIndex] = pkg;\n\t\t}\n\n\t\t// Get the resource array for this type\n\t\tconst arrayKey = item.resourceType as \"extensions\" | \"skills\" | \"prompts\" | \"themes\";\n\t\tconst current = (pkg[arrayKey] ?? []) as string[];\n\n\t\t// Generate pattern relative to package root\n\t\tconst pattern = this.getPackageResourcePattern(item);\n\t\tconst disablePattern = `-${pattern}`;\n\t\tconst enablePattern = `+${pattern}`;\n\n\t\t// Filter out existing patterns for this resource\n\t\tconst updated = current.filter((p) => {\n\t\t\tconst stripped = p.startsWith(\"!\") || p.startsWith(\"+\") || p.startsWith(\"-\") ? p.slice(1) : p;\n\t\t\treturn stripped !== pattern;\n\t\t});\n\n\t\tif (enabled) {\n\t\t\tupdated.push(enablePattern);\n\t\t} else {\n\t\t\tupdated.push(disablePattern);\n\t\t}\n\n\t\t(pkg as Record<string, unknown>)[arrayKey] = updated.length > 0 ? updated : undefined;\n\n\t\t// Clean up empty filter object\n\t\tconst hasFilters = [\"extensions\", \"skills\", \"prompts\", \"themes\"].some(\n\t\t\t(k) => (pkg as Record<string, unknown>)[k] !== undefined,\n\t\t);\n\t\tif (!hasFilters) {\n\t\t\tpackages[pkgIndex] = (pkg as { source: string }).source;\n\t\t}\n\n\t\tif (scope === \"project\") {\n\t\t\tthis.settingsManager.setProjectPackages(packages);\n\t\t} else {\n\t\t\tthis.settingsManager.setPackages(packages);\n\t\t}\n\t}\n\n\tprivate getTopLevelBaseDir(scope: \"user\" | \"project\"): string {\n\t\treturn scope === \"project\" ? join(this.cwd, CONFIG_DIR_NAME) : this.agentDir;\n\t}\n\n\tprivate getResourcePattern(item: ResourceItem): string {\n\t\tconst scope = item.metadata.scope as \"user\" | \"project\";\n\t\tconst baseDir = this.getTopLevelBaseDir(scope);\n\t\treturn relative(baseDir, item.path);\n\t}\n\n\tprivate getPackageResourcePattern(item: ResourceItem): string {\n\t\tconst baseDir = item.metadata.baseDir ?? dirname(item.path);\n\t\treturn relative(baseDir, item.path);\n\t}\n}\n\nexport class ConfigSelectorComponent extends Container implements Focusable {\n\tprivate resourceList: ResourceList;\n\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.resourceList.focused = value;\n\t}\n\n\tconstructor(\n\t\tresolvedPaths: ResolvedPaths,\n\t\tsettingsManager: SettingsManager,\n\t\tcwd: string,\n\t\tagentDir: string,\n\t\tonClose: () => void,\n\t\tonExit: () => void,\n\t\trequestRender: () => void,\n\t) {\n\t\tsuper();\n\n\t\tconst groups = buildGroups(resolvedPaths);\n\n\t\t// Minimal chrome — no filled borders.\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new ConfigSelectorHeader());\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Resource list\n\t\tthis.resourceList = new ResourceList(groups, settingsManager, cwd, agentDir);\n\t\tthis.resourceList.onCancel = onClose;\n\t\tthis.resourceList.onExit = onExit;\n\t\tthis.resourceList.onToggle = () => requestRender();\n\t\tthis.addChild(this.resourceList);\n\n\t\tthis.addChild(new Spacer(1));\n\t}\n\n\tgetResourceList(): ResourceList {\n\t\treturn this.resourceList;\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/model-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAkB,MAAM,yBAAyB,CAAC;AACrE,OAAO,EACN,SAAS,EACT,KAAK,SAAS,EAGd,KAAK,EAGL,KAAK,GAAG,EACR,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAWzE,UAAU,eAAe;IACxB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAID;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,SAAU,YAAW,SAAS;IACzE,OAAO,CAAC,WAAW,CAAQ;IAG3B,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IACD,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,cAAc,CAAmB;IACzC,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,YAAY,CAAC,CAAa;IAClC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,YAAY,CAAiC;IACrD,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,SAAS,CAAC,CAAO;IACzB,OAAO,CAAC,aAAa,CAAC,CAAO;IAE7B,YACC,GAAG,EAAE,GAAG,EACR,YAAY,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,EACpC,eAAe,EAAE,eAAe,EAChC,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,aAAa,CAAC,eAAe,CAAC,EAC5C,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,EACrC,QAAQ,EAAE,MAAM,IAAI,EACpB,kBAAkB,CAAC,EAAE,MAAM,EA+D3B;YAEa,UAAU;IA8CxB,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,QAAQ;IAYhB,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,UAAU;IAyDlB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAwCjC;IAED,OAAO,CAAC,YAAY;IAMpB,cAAc,IAAI,KAAK,CAEtB;CACD","sourcesContent":["import { type Model, modelsAreEqual } from \"@kolisachint/hoocode-ai\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@kolisachint/hoocode-tui\";\nimport type { ModelRegistry } from \"../../../core/model-registry.js\";\nimport type { SettingsManager } from \"../../../core/settings-manager.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { keyHint } from \"./keybinding-hints.js\";\n\ninterface ModelItem {\n\tprovider: string;\n\tid: string;\n\tmodel: Model<any>;\n}\n\ninterface ScopedModelItem {\n\tmodel: Model<any>;\n\tthinkingLevel?: string;\n}\n\ntype ModelScope = \"all\" | \"scoped\";\n\n/**\n * Component that renders a model selector with search\n */\nexport class ModelSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\tprivate listContainer: Container;\n\tprivate allModels: ModelItem[] = [];\n\tprivate scopedModelItems: ModelItem[] = [];\n\tprivate activeModels: ModelItem[] = [];\n\tprivate filteredModels: ModelItem[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate currentModel?: Model<any>;\n\tprivate settingsManager: SettingsManager;\n\tprivate modelRegistry: ModelRegistry;\n\tprivate onSelectCallback: (model: Model<any>) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate errorMessage?: string;\n\tprivate tui: TUI;\n\tprivate scopedModels: ReadonlyArray<ScopedModelItem>;\n\tprivate scope: ModelScope = \"all\";\n\tprivate scopeText?: Text;\n\tprivate scopeHintText?: Text;\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tcurrentModel: Model<any> | undefined,\n\t\tsettingsManager: SettingsManager,\n\t\tmodelRegistry: ModelRegistry,\n\t\tscopedModels: ReadonlyArray<ScopedModelItem>,\n\t\tonSelect: (model: Model<any>) => void,\n\t\tonCancel: () => void,\n\t\tinitialSearchInput?: string,\n\t) {\n\t\tsuper();\n\n\t\tthis.tui = tui;\n\t\tthis.currentModel = currentModel;\n\t\tthis.settingsManager = settingsManager;\n\t\tthis.modelRegistry = modelRegistry;\n\t\tthis.scopedModels = scopedModels;\n\t\tthis.scope = scopedModels.length > 0 ? \"scoped\" : \"all\";\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onCancelCallback = onCancel;\n\n\t\t// Add top border\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add hint about model filtering\n\t\tif (scopedModels.length > 0) {\n\t\t\tthis.scopeText = new Text(this.getScopeText(), 0, 0);\n\t\t\tthis.addChild(this.scopeText);\n\t\t\tthis.scopeHintText = new Text(this.getScopeHintText(), 0, 0);\n\t\t\tthis.addChild(this.scopeHintText);\n\t\t} else {\n\t\t\tconst hintText = \"Only showing models from configured providers. Use /login to add providers.\";\n\t\t\tthis.addChild(new Text(theme.fg(\"warning\", hintText), 0, 0));\n\t\t}\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create search input\n\t\tthis.searchInput = new Input();\n\t\tif (initialSearchInput) {\n\t\t\tthis.searchInput.setValue(initialSearchInput);\n\t\t}\n\t\tthis.searchInput.onSubmit = () => {\n\t\t\t// Enter on search input selects the first filtered item\n\t\t\tif (this.filteredModels[this.selectedIndex]) {\n\t\t\t\tthis.handleSelect(this.filteredModels[this.selectedIndex].model);\n\t\t\t}\n\t\t};\n\t\tthis.addChild(this.searchInput);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Load models and do initial render\n\t\tthis.loadModels().then(() => {\n\t\t\tif (initialSearchInput) {\n\t\t\t\tthis.filterModels(initialSearchInput);\n\t\t\t} else {\n\t\t\t\tthis.updateList();\n\t\t\t}\n\t\t\t// Request re-render after models are loaded\n\t\t\tthis.tui.requestRender();\n\t\t});\n\t}\n\n\tprivate async loadModels(): Promise<void> {\n\t\tlet models: ModelItem[];\n\n\t\t// Refresh to pick up any changes to models.json\n\t\tthis.modelRegistry.refresh();\n\n\t\t// Check for models.json errors\n\t\tconst loadError = this.modelRegistry.getError();\n\t\tif (loadError) {\n\t\t\tthis.errorMessage = loadError;\n\t\t}\n\n\t\t// Load available models (built-in models still work even if models.json failed)\n\t\ttry {\n\t\t\tconst availableModels = await this.modelRegistry.getAvailable();\n\t\t\tmodels = availableModels.map((model: Model<any>) => ({\n\t\t\t\tprovider: model.provider,\n\t\t\t\tid: model.id,\n\t\t\t\tmodel,\n\t\t\t}));\n\t\t} catch (error) {\n\t\t\tthis.allModels = [];\n\t\t\tthis.scopedModelItems = [];\n\t\t\tthis.activeModels = [];\n\t\t\tthis.filteredModels = [];\n\t\t\tthis.errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.allModels = this.sortModels(models);\n\t\tthis.scopedModels = this.scopedModels.map((scoped) => {\n\t\t\tconst refreshed = this.modelRegistry.find(scoped.model.provider, scoped.model.id);\n\t\t\treturn refreshed ? { ...scoped, model: refreshed } : scoped;\n\t\t});\n\t\tthis.scopedModelItems = this.scopedModels.map((scoped) => ({\n\t\t\tprovider: scoped.model.provider,\n\t\t\tid: scoped.model.id,\n\t\t\tmodel: scoped.model,\n\t\t}));\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tthis.filteredModels = this.activeModels;\n\t\tconst currentIndex = this.filteredModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex =\n\t\t\tcurrentIndex >= 0 ? currentIndex : Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t}\n\n\tprivate sortModels(models: ModelItem[]): ModelItem[] {\n\t\tconst sorted = [...models];\n\t\t// Sort: current model first, then by provider\n\t\tsorted.sort((a, b) => {\n\t\t\tconst aIsCurrent = modelsAreEqual(this.currentModel, a.model);\n\t\t\tconst bIsCurrent = modelsAreEqual(this.currentModel, b.model);\n\t\t\tif (aIsCurrent && !bIsCurrent) return -1;\n\t\t\tif (!aIsCurrent && bIsCurrent) return 1;\n\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t});\n\t\treturn sorted;\n\t}\n\n\tprivate getScopeText(): string {\n\t\tconst allText = this.scope === \"all\" ? theme.fg(\"accent\", \"all\") : theme.fg(\"muted\", \"all\");\n\t\tconst scopedText = this.scope === \"scoped\" ? theme.fg(\"accent\", \"scoped\") : theme.fg(\"muted\", \"scoped\");\n\t\treturn `${theme.fg(\"muted\", \"Scope: \")}${allText}${theme.fg(\"muted\", \" | \")}${scopedText}`;\n\t}\n\n\tprivate getScopeHintText(): string {\n\t\treturn keyHint(\"tui.input.tab\", \"scope\") + theme.fg(\"muted\", \" (all/scoped)\");\n\t}\n\n\tprivate setScope(scope: ModelScope): void {\n\t\tif (this.scope === scope) return;\n\t\tthis.scope = scope;\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tconst currentIndex = this.activeModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex = currentIndex >= 0 ? currentIndex : 0;\n\t\tthis.filterModels(this.searchInput.getValue());\n\t\tif (this.scopeText) {\n\t\t\tthis.scopeText.setText(this.getScopeText());\n\t\t}\n\t}\n\n\tprivate filterModels(query: string): void {\n\t\tthis.filteredModels = query\n\t\t\t? fuzzyFilter(\n\t\t\t\t\tthis.activeModels,\n\t\t\t\t\tquery,\n\t\t\t\t\t({ id, provider }) => `${id} ${provider} ${provider}/${id} ${provider} ${id}`,\n\t\t\t\t)\n\t\t\t: this.activeModels;\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tconst maxVisible = 10;\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(maxVisible / 2), this.filteredModels.length - maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + maxVisible, this.filteredModels.length);\n\n\t\t// Show visible slice of filtered models\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredModels[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst isCurrent = modelsAreEqual(this.currentModel, item.model);\n\n\t\t\tlet line = \"\";\n\t\t\tif (isSelected) {\n\t\t\t\tconst prefix = theme.fg(\"accent\", \"→ \");\n\t\t\t\tconst modelText = `${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${prefix + theme.fg(\"accent\", modelText)} ${providerBadge}${checkmark}`;\n\t\t\t} else {\n\t\t\t\tconst modelText = ` ${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${modelText} ${providerBadge}${checkmark}`;\n\t\t\t}\n\n\t\t\tthis.listContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredModels.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredModels.length})`);\n\t\t\tthis.listContainer.addChild(new Text(scrollInfo, 0, 0));\n\t\t}\n\n\t\t// Show error message or \"no results\" if empty\n\t\tif (this.errorMessage) {\n\t\t\t// Show error in red\n\t\t\tconst errorLines = this.errorMessage.split(\"\\n\");\n\t\t\tfor (const line of errorLines) {\n\t\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"error\", line), 0, 0));\n\t\t\t}\n\t\t} else if (this.filteredModels.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching models\"), 0, 0));\n\t\t} else {\n\t\t\tconst selected = this.filteredModels[this.selectedIndex];\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", ` Model Name: ${selected.model.name}`), 0, 0));\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.input.tab\")) {\n\t\t\tif (this.scopedModelItems.length > 0) {\n\t\t\t\tconst nextScope: ModelScope = this.scope === \"all\" ? \"scoped\" : \"all\";\n\t\t\t\tthis.setScope(nextScope);\n\t\t\t\tif (this.scopeHintText) {\n\t\t\t\t\tthis.scopeHintText.setText(this.getScopeHintText());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// Up arrow - wrap to bottom when at top\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredModels.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Down arrow - wrap to top when at bottom\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredModels.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tconst selectedModel = this.filteredModels[this.selectedIndex];\n\t\t\tif (selectedModel) {\n\t\t\t\tthis.handleSelect(selectedModel.model);\n\t\t\t}\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterModels(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate handleSelect(model: Model<any>): void {\n\t\t// Save as new default\n\t\tthis.settingsManager.setDefaultModelAndProvider(model.provider, model.id);\n\t\tthis.onSelectCallback(model);\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"model-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/model-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAkB,MAAM,yBAAyB,CAAC;AACrE,OAAO,EACN,SAAS,EACT,KAAK,SAAS,EAGd,KAAK,EAGL,KAAK,GAAG,EACR,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAUzE,UAAU,eAAe;IACxB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAID;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,SAAU,YAAW,SAAS;IACzE,OAAO,CAAC,WAAW,CAAQ;IAG3B,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IACD,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,cAAc,CAAmB;IACzC,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,YAAY,CAAC,CAAa;IAClC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,YAAY,CAAiC;IACrD,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,SAAS,CAAC,CAAO;IACzB,OAAO,CAAC,aAAa,CAAC,CAAO;IAE7B,YACC,GAAG,EAAE,GAAG,EACR,YAAY,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,EACpC,eAAe,EAAE,eAAe,EAChC,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,aAAa,CAAC,eAAe,CAAC,EAC5C,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,EACrC,QAAQ,EAAE,MAAM,IAAI,EACpB,kBAAkB,CAAC,EAAE,MAAM,EA2D3B;YAEa,UAAU;IA8CxB,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,QAAQ;IAYhB,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,UAAU;IAyDlB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAwCjC;IAED,OAAO,CAAC,YAAY;IAMpB,cAAc,IAAI,KAAK,CAEtB;CACD","sourcesContent":["import { type Model, modelsAreEqual } from \"@kolisachint/hoocode-ai\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@kolisachint/hoocode-tui\";\nimport type { ModelRegistry } from \"../../../core/model-registry.js\";\nimport type { SettingsManager } from \"../../../core/settings-manager.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { keyHint } from \"./keybinding-hints.js\";\n\ninterface ModelItem {\n\tprovider: string;\n\tid: string;\n\tmodel: Model<any>;\n}\n\ninterface ScopedModelItem {\n\tmodel: Model<any>;\n\tthinkingLevel?: string;\n}\n\ntype ModelScope = \"all\" | \"scoped\";\n\n/**\n * Component that renders a model selector with search\n */\nexport class ModelSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\tprivate listContainer: Container;\n\tprivate allModels: ModelItem[] = [];\n\tprivate scopedModelItems: ModelItem[] = [];\n\tprivate activeModels: ModelItem[] = [];\n\tprivate filteredModels: ModelItem[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate currentModel?: Model<any>;\n\tprivate settingsManager: SettingsManager;\n\tprivate modelRegistry: ModelRegistry;\n\tprivate onSelectCallback: (model: Model<any>) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate errorMessage?: string;\n\tprivate tui: TUI;\n\tprivate scopedModels: ReadonlyArray<ScopedModelItem>;\n\tprivate scope: ModelScope = \"all\";\n\tprivate scopeText?: Text;\n\tprivate scopeHintText?: Text;\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tcurrentModel: Model<any> | undefined,\n\t\tsettingsManager: SettingsManager,\n\t\tmodelRegistry: ModelRegistry,\n\t\tscopedModels: ReadonlyArray<ScopedModelItem>,\n\t\tonSelect: (model: Model<any>) => void,\n\t\tonCancel: () => void,\n\t\tinitialSearchInput?: string,\n\t) {\n\t\tsuper();\n\n\t\tthis.tui = tui;\n\t\tthis.currentModel = currentModel;\n\t\tthis.settingsManager = settingsManager;\n\t\tthis.modelRegistry = modelRegistry;\n\t\tthis.scopedModels = scopedModels;\n\t\tthis.scope = scopedModels.length > 0 ? \"scoped\" : \"all\";\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onCancelCallback = onCancel;\n\n\t\t// Minimal chrome — no filled border, just a clean overlay.\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add hint about model filtering\n\t\tif (scopedModels.length > 0) {\n\t\t\tthis.scopeText = new Text(this.getScopeText(), 0, 0);\n\t\t\tthis.addChild(this.scopeText);\n\t\t\tthis.scopeHintText = new Text(this.getScopeHintText(), 0, 0);\n\t\t\tthis.addChild(this.scopeHintText);\n\t\t} else {\n\t\t\tconst hintText = \"Only showing models from configured providers. Use /login to add providers.\";\n\t\t\tthis.addChild(new Text(theme.fg(\"warning\", hintText), 0, 0));\n\t\t}\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create search input\n\t\tthis.searchInput = new Input();\n\t\tif (initialSearchInput) {\n\t\t\tthis.searchInput.setValue(initialSearchInput);\n\t\t}\n\t\tthis.searchInput.onSubmit = () => {\n\t\t\t// Enter on search input selects the first filtered item\n\t\t\tif (this.filteredModels[this.selectedIndex]) {\n\t\t\t\tthis.handleSelect(this.filteredModels[this.selectedIndex].model);\n\t\t\t}\n\t\t};\n\t\tthis.addChild(this.searchInput);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Load models and do initial render\n\t\tthis.loadModels().then(() => {\n\t\t\tif (initialSearchInput) {\n\t\t\t\tthis.filterModels(initialSearchInput);\n\t\t\t} else {\n\t\t\t\tthis.updateList();\n\t\t\t}\n\t\t\t// Request re-render after models are loaded\n\t\t\tthis.tui.requestRender();\n\t\t});\n\t}\n\n\tprivate async loadModels(): Promise<void> {\n\t\tlet models: ModelItem[];\n\n\t\t// Refresh to pick up any changes to models.json\n\t\tthis.modelRegistry.refresh();\n\n\t\t// Check for models.json errors\n\t\tconst loadError = this.modelRegistry.getError();\n\t\tif (loadError) {\n\t\t\tthis.errorMessage = loadError;\n\t\t}\n\n\t\t// Load available models (built-in models still work even if models.json failed)\n\t\ttry {\n\t\t\tconst availableModels = await this.modelRegistry.getAvailable();\n\t\t\tmodels = availableModels.map((model: Model<any>) => ({\n\t\t\t\tprovider: model.provider,\n\t\t\t\tid: model.id,\n\t\t\t\tmodel,\n\t\t\t}));\n\t\t} catch (error) {\n\t\t\tthis.allModels = [];\n\t\t\tthis.scopedModelItems = [];\n\t\t\tthis.activeModels = [];\n\t\t\tthis.filteredModels = [];\n\t\t\tthis.errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.allModels = this.sortModels(models);\n\t\tthis.scopedModels = this.scopedModels.map((scoped) => {\n\t\t\tconst refreshed = this.modelRegistry.find(scoped.model.provider, scoped.model.id);\n\t\t\treturn refreshed ? { ...scoped, model: refreshed } : scoped;\n\t\t});\n\t\tthis.scopedModelItems = this.scopedModels.map((scoped) => ({\n\t\t\tprovider: scoped.model.provider,\n\t\t\tid: scoped.model.id,\n\t\t\tmodel: scoped.model,\n\t\t}));\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tthis.filteredModels = this.activeModels;\n\t\tconst currentIndex = this.filteredModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex =\n\t\t\tcurrentIndex >= 0 ? currentIndex : Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t}\n\n\tprivate sortModels(models: ModelItem[]): ModelItem[] {\n\t\tconst sorted = [...models];\n\t\t// Sort: current model first, then by provider\n\t\tsorted.sort((a, b) => {\n\t\t\tconst aIsCurrent = modelsAreEqual(this.currentModel, a.model);\n\t\t\tconst bIsCurrent = modelsAreEqual(this.currentModel, b.model);\n\t\t\tif (aIsCurrent && !bIsCurrent) return -1;\n\t\t\tif (!aIsCurrent && bIsCurrent) return 1;\n\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t});\n\t\treturn sorted;\n\t}\n\n\tprivate getScopeText(): string {\n\t\tconst allText = this.scope === \"all\" ? theme.fg(\"accent\", \"all\") : theme.fg(\"muted\", \"all\");\n\t\tconst scopedText = this.scope === \"scoped\" ? theme.fg(\"accent\", \"scoped\") : theme.fg(\"muted\", \"scoped\");\n\t\treturn `${theme.fg(\"muted\", \"Scope: \")}${allText}${theme.fg(\"muted\", \" | \")}${scopedText}`;\n\t}\n\n\tprivate getScopeHintText(): string {\n\t\treturn keyHint(\"tui.input.tab\", \"scope\") + theme.fg(\"muted\", \" (all/scoped)\");\n\t}\n\n\tprivate setScope(scope: ModelScope): void {\n\t\tif (this.scope === scope) return;\n\t\tthis.scope = scope;\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tconst currentIndex = this.activeModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex = currentIndex >= 0 ? currentIndex : 0;\n\t\tthis.filterModels(this.searchInput.getValue());\n\t\tif (this.scopeText) {\n\t\t\tthis.scopeText.setText(this.getScopeText());\n\t\t}\n\t}\n\n\tprivate filterModels(query: string): void {\n\t\tthis.filteredModels = query\n\t\t\t? fuzzyFilter(\n\t\t\t\t\tthis.activeModels,\n\t\t\t\t\tquery,\n\t\t\t\t\t({ id, provider }) => `${id} ${provider} ${provider}/${id} ${provider} ${id}`,\n\t\t\t\t)\n\t\t\t: this.activeModels;\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tconst maxVisible = 10;\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(maxVisible / 2), this.filteredModels.length - maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + maxVisible, this.filteredModels.length);\n\n\t\t// Show visible slice of filtered models\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredModels[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst isCurrent = modelsAreEqual(this.currentModel, item.model);\n\n\t\t\tlet line = \"\";\n\t\t\tif (isSelected) {\n\t\t\t\tconst prefix = theme.fg(\"accent\", \"→ \");\n\t\t\t\tconst modelText = `${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${prefix + theme.fg(\"accent\", modelText)} ${providerBadge}${checkmark}`;\n\t\t\t} else {\n\t\t\t\tconst modelText = ` ${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${modelText} ${providerBadge}${checkmark}`;\n\t\t\t}\n\n\t\t\tthis.listContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredModels.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredModels.length})`);\n\t\t\tthis.listContainer.addChild(new Text(scrollInfo, 0, 0));\n\t\t}\n\n\t\t// Show error message or \"no results\" if empty\n\t\tif (this.errorMessage) {\n\t\t\t// Show error in red\n\t\t\tconst errorLines = this.errorMessage.split(\"\\n\");\n\t\t\tfor (const line of errorLines) {\n\t\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"error\", line), 0, 0));\n\t\t\t}\n\t\t} else if (this.filteredModels.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching models\"), 0, 0));\n\t\t} else {\n\t\t\tconst selected = this.filteredModels[this.selectedIndex];\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", ` Model Name: ${selected.model.name}`), 0, 0));\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.input.tab\")) {\n\t\t\tif (this.scopedModelItems.length > 0) {\n\t\t\t\tconst nextScope: ModelScope = this.scope === \"all\" ? \"scoped\" : \"all\";\n\t\t\t\tthis.setScope(nextScope);\n\t\t\t\tif (this.scopeHintText) {\n\t\t\t\t\tthis.scopeHintText.setText(this.getScopeHintText());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// Up arrow - wrap to bottom when at top\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredModels.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Down arrow - wrap to top when at bottom\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredModels.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tconst selectedModel = this.filteredModels[this.selectedIndex];\n\t\t\tif (selectedModel) {\n\t\t\t\tthis.handleSelect(selectedModel.model);\n\t\t\t}\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterModels(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate handleSelect(model: Model<any>): void {\n\t\t// Save as new default\n\t\tthis.settingsManager.setDefaultModelAndProvider(model.provider, model.id);\n\t\tthis.onSelectCallback(model);\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { modelsAreEqual } from "@kolisachint/hoocode-ai";
|
|
2
2
|
import { Container, fuzzyFilter, getKeybindings, Input, Spacer, Text, } from "@kolisachint/hoocode-tui";
|
|
3
3
|
import { theme } from "../theme/theme.js";
|
|
4
|
-
import { DynamicBorder } from "./dynamic-border.js";
|
|
5
4
|
import { keyHint } from "./keybinding-hints.js";
|
|
6
5
|
/**
|
|
7
6
|
* Component that renders a model selector with search
|
|
@@ -44,8 +43,7 @@ export class ModelSelectorComponent extends Container {
|
|
|
44
43
|
this.scope = scopedModels.length > 0 ? "scoped" : "all";
|
|
45
44
|
this.onSelectCallback = onSelect;
|
|
46
45
|
this.onCancelCallback = onCancel;
|
|
47
|
-
//
|
|
48
|
-
this.addChild(new DynamicBorder());
|
|
46
|
+
// Minimal chrome — no filled border, just a clean overlay.
|
|
49
47
|
this.addChild(new Spacer(1));
|
|
50
48
|
// Add hint about model filtering
|
|
51
49
|
if (scopedModels.length > 0) {
|
|
@@ -76,8 +74,6 @@ export class ModelSelectorComponent extends Container {
|
|
|
76
74
|
this.listContainer = new Container();
|
|
77
75
|
this.addChild(this.listContainer);
|
|
78
76
|
this.addChild(new Spacer(1));
|
|
79
|
-
// Add bottom border
|
|
80
|
-
this.addChild(new DynamicBorder());
|
|
81
77
|
// Load models and do initial render
|
|
82
78
|
this.loadModels().then(() => {
|
|
83
79
|
if (initialSearchInput) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/model-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Model, modelsAreEqual } from \"@kolisachint/hoocode-ai\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@kolisachint/hoocode-tui\";\nimport type { ModelRegistry } from \"../../../core/model-registry.js\";\nimport type { SettingsManager } from \"../../../core/settings-manager.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { keyHint } from \"./keybinding-hints.js\";\n\ninterface ModelItem {\n\tprovider: string;\n\tid: string;\n\tmodel: Model<any>;\n}\n\ninterface ScopedModelItem {\n\tmodel: Model<any>;\n\tthinkingLevel?: string;\n}\n\ntype ModelScope = \"all\" | \"scoped\";\n\n/**\n * Component that renders a model selector with search\n */\nexport class ModelSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\tprivate listContainer: Container;\n\tprivate allModels: ModelItem[] = [];\n\tprivate scopedModelItems: ModelItem[] = [];\n\tprivate activeModels: ModelItem[] = [];\n\tprivate filteredModels: ModelItem[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate currentModel?: Model<any>;\n\tprivate settingsManager: SettingsManager;\n\tprivate modelRegistry: ModelRegistry;\n\tprivate onSelectCallback: (model: Model<any>) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate errorMessage?: string;\n\tprivate tui: TUI;\n\tprivate scopedModels: ReadonlyArray<ScopedModelItem>;\n\tprivate scope: ModelScope = \"all\";\n\tprivate scopeText?: Text;\n\tprivate scopeHintText?: Text;\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tcurrentModel: Model<any> | undefined,\n\t\tsettingsManager: SettingsManager,\n\t\tmodelRegistry: ModelRegistry,\n\t\tscopedModels: ReadonlyArray<ScopedModelItem>,\n\t\tonSelect: (model: Model<any>) => void,\n\t\tonCancel: () => void,\n\t\tinitialSearchInput?: string,\n\t) {\n\t\tsuper();\n\n\t\tthis.tui = tui;\n\t\tthis.currentModel = currentModel;\n\t\tthis.settingsManager = settingsManager;\n\t\tthis.modelRegistry = modelRegistry;\n\t\tthis.scopedModels = scopedModels;\n\t\tthis.scope = scopedModels.length > 0 ? \"scoped\" : \"all\";\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onCancelCallback = onCancel;\n\n\t\t// Add top border\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add hint about model filtering\n\t\tif (scopedModels.length > 0) {\n\t\t\tthis.scopeText = new Text(this.getScopeText(), 0, 0);\n\t\t\tthis.addChild(this.scopeText);\n\t\t\tthis.scopeHintText = new Text(this.getScopeHintText(), 0, 0);\n\t\t\tthis.addChild(this.scopeHintText);\n\t\t} else {\n\t\t\tconst hintText = \"Only showing models from configured providers. Use /login to add providers.\";\n\t\t\tthis.addChild(new Text(theme.fg(\"warning\", hintText), 0, 0));\n\t\t}\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create search input\n\t\tthis.searchInput = new Input();\n\t\tif (initialSearchInput) {\n\t\t\tthis.searchInput.setValue(initialSearchInput);\n\t\t}\n\t\tthis.searchInput.onSubmit = () => {\n\t\t\t// Enter on search input selects the first filtered item\n\t\t\tif (this.filteredModels[this.selectedIndex]) {\n\t\t\t\tthis.handleSelect(this.filteredModels[this.selectedIndex].model);\n\t\t\t}\n\t\t};\n\t\tthis.addChild(this.searchInput);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Load models and do initial render\n\t\tthis.loadModels().then(() => {\n\t\t\tif (initialSearchInput) {\n\t\t\t\tthis.filterModels(initialSearchInput);\n\t\t\t} else {\n\t\t\t\tthis.updateList();\n\t\t\t}\n\t\t\t// Request re-render after models are loaded\n\t\t\tthis.tui.requestRender();\n\t\t});\n\t}\n\n\tprivate async loadModels(): Promise<void> {\n\t\tlet models: ModelItem[];\n\n\t\t// Refresh to pick up any changes to models.json\n\t\tthis.modelRegistry.refresh();\n\n\t\t// Check for models.json errors\n\t\tconst loadError = this.modelRegistry.getError();\n\t\tif (loadError) {\n\t\t\tthis.errorMessage = loadError;\n\t\t}\n\n\t\t// Load available models (built-in models still work even if models.json failed)\n\t\ttry {\n\t\t\tconst availableModels = await this.modelRegistry.getAvailable();\n\t\t\tmodels = availableModels.map((model: Model<any>) => ({\n\t\t\t\tprovider: model.provider,\n\t\t\t\tid: model.id,\n\t\t\t\tmodel,\n\t\t\t}));\n\t\t} catch (error) {\n\t\t\tthis.allModels = [];\n\t\t\tthis.scopedModelItems = [];\n\t\t\tthis.activeModels = [];\n\t\t\tthis.filteredModels = [];\n\t\t\tthis.errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.allModels = this.sortModels(models);\n\t\tthis.scopedModels = this.scopedModels.map((scoped) => {\n\t\t\tconst refreshed = this.modelRegistry.find(scoped.model.provider, scoped.model.id);\n\t\t\treturn refreshed ? { ...scoped, model: refreshed } : scoped;\n\t\t});\n\t\tthis.scopedModelItems = this.scopedModels.map((scoped) => ({\n\t\t\tprovider: scoped.model.provider,\n\t\t\tid: scoped.model.id,\n\t\t\tmodel: scoped.model,\n\t\t}));\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tthis.filteredModels = this.activeModels;\n\t\tconst currentIndex = this.filteredModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex =\n\t\t\tcurrentIndex >= 0 ? currentIndex : Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t}\n\n\tprivate sortModels(models: ModelItem[]): ModelItem[] {\n\t\tconst sorted = [...models];\n\t\t// Sort: current model first, then by provider\n\t\tsorted.sort((a, b) => {\n\t\t\tconst aIsCurrent = modelsAreEqual(this.currentModel, a.model);\n\t\t\tconst bIsCurrent = modelsAreEqual(this.currentModel, b.model);\n\t\t\tif (aIsCurrent && !bIsCurrent) return -1;\n\t\t\tif (!aIsCurrent && bIsCurrent) return 1;\n\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t});\n\t\treturn sorted;\n\t}\n\n\tprivate getScopeText(): string {\n\t\tconst allText = this.scope === \"all\" ? theme.fg(\"accent\", \"all\") : theme.fg(\"muted\", \"all\");\n\t\tconst scopedText = this.scope === \"scoped\" ? theme.fg(\"accent\", \"scoped\") : theme.fg(\"muted\", \"scoped\");\n\t\treturn `${theme.fg(\"muted\", \"Scope: \")}${allText}${theme.fg(\"muted\", \" | \")}${scopedText}`;\n\t}\n\n\tprivate getScopeHintText(): string {\n\t\treturn keyHint(\"tui.input.tab\", \"scope\") + theme.fg(\"muted\", \" (all/scoped)\");\n\t}\n\n\tprivate setScope(scope: ModelScope): void {\n\t\tif (this.scope === scope) return;\n\t\tthis.scope = scope;\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tconst currentIndex = this.activeModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex = currentIndex >= 0 ? currentIndex : 0;\n\t\tthis.filterModels(this.searchInput.getValue());\n\t\tif (this.scopeText) {\n\t\t\tthis.scopeText.setText(this.getScopeText());\n\t\t}\n\t}\n\n\tprivate filterModels(query: string): void {\n\t\tthis.filteredModels = query\n\t\t\t? fuzzyFilter(\n\t\t\t\t\tthis.activeModels,\n\t\t\t\t\tquery,\n\t\t\t\t\t({ id, provider }) => `${id} ${provider} ${provider}/${id} ${provider} ${id}`,\n\t\t\t\t)\n\t\t\t: this.activeModels;\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tconst maxVisible = 10;\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(maxVisible / 2), this.filteredModels.length - maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + maxVisible, this.filteredModels.length);\n\n\t\t// Show visible slice of filtered models\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredModels[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst isCurrent = modelsAreEqual(this.currentModel, item.model);\n\n\t\t\tlet line = \"\";\n\t\t\tif (isSelected) {\n\t\t\t\tconst prefix = theme.fg(\"accent\", \"→ \");\n\t\t\t\tconst modelText = `${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${prefix + theme.fg(\"accent\", modelText)} ${providerBadge}${checkmark}`;\n\t\t\t} else {\n\t\t\t\tconst modelText = ` ${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${modelText} ${providerBadge}${checkmark}`;\n\t\t\t}\n\n\t\t\tthis.listContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredModels.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredModels.length})`);\n\t\t\tthis.listContainer.addChild(new Text(scrollInfo, 0, 0));\n\t\t}\n\n\t\t// Show error message or \"no results\" if empty\n\t\tif (this.errorMessage) {\n\t\t\t// Show error in red\n\t\t\tconst errorLines = this.errorMessage.split(\"\\n\");\n\t\t\tfor (const line of errorLines) {\n\t\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"error\", line), 0, 0));\n\t\t\t}\n\t\t} else if (this.filteredModels.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching models\"), 0, 0));\n\t\t} else {\n\t\t\tconst selected = this.filteredModels[this.selectedIndex];\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", ` Model Name: ${selected.model.name}`), 0, 0));\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.input.tab\")) {\n\t\t\tif (this.scopedModelItems.length > 0) {\n\t\t\t\tconst nextScope: ModelScope = this.scope === \"all\" ? \"scoped\" : \"all\";\n\t\t\t\tthis.setScope(nextScope);\n\t\t\t\tif (this.scopeHintText) {\n\t\t\t\t\tthis.scopeHintText.setText(this.getScopeHintText());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// Up arrow - wrap to bottom when at top\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredModels.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Down arrow - wrap to top when at bottom\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredModels.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tconst selectedModel = this.filteredModels[this.selectedIndex];\n\t\t\tif (selectedModel) {\n\t\t\t\tthis.handleSelect(selectedModel.model);\n\t\t\t}\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterModels(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate handleSelect(model: Model<any>): void {\n\t\t// Save as new default\n\t\tthis.settingsManager.setDefaultModelAndProvider(model.provider, model.id);\n\t\tthis.onSelectCallback(model);\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"model-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/model-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EACN,SAAS,EAET,WAAW,EACX,cAAc,EACd,KAAK,EACL,MAAM,EACN,IAAI,GAEJ,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAehD;;GAEG;AACH,MAAM,OAAO,sBAAuB,SAAQ,SAAS;IAC5C,WAAW,CAAQ;IAE3B,iFAAiF;IACzE,QAAQ,GAAG,KAAK,CAAC;IACzB,IAAI,OAAO,GAAY;QACtB,OAAO,IAAI,CAAC,QAAQ,CAAC;IAAA,CACrB;IACD,IAAI,OAAO,CAAC,KAAc,EAAE;QAC3B,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACtB,IAAI,CAAC,WAAW,CAAC,OAAO,GAAG,KAAK,CAAC;IAAA,CACjC;IACO,aAAa,CAAY;IACzB,SAAS,GAAgB,EAAE,CAAC;IAC5B,gBAAgB,GAAgB,EAAE,CAAC;IACnC,YAAY,GAAgB,EAAE,CAAC;IAC/B,cAAc,GAAgB,EAAE,CAAC;IACjC,aAAa,GAAW,CAAC,CAAC;IAC1B,YAAY,CAAc;IAC1B,eAAe,CAAkB;IACjC,aAAa,CAAgB;IAC7B,gBAAgB,CAA8B;IAC9C,gBAAgB,CAAa;IAC7B,YAAY,CAAU;IACtB,GAAG,CAAM;IACT,YAAY,CAAiC;IAC7C,KAAK,GAAe,KAAK,CAAC;IAC1B,SAAS,CAAQ;IACjB,aAAa,CAAQ;IAE7B,YACC,GAAQ,EACR,YAAoC,EACpC,eAAgC,EAChC,aAA4B,EAC5B,YAA4C,EAC5C,QAAqC,EACrC,QAAoB,EACpB,kBAA2B,EAC1B;QACD,KAAK,EAAE,CAAC;QAER,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;QACvC,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,KAAK,GAAG,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;QACxD,IAAI,CAAC,gBAAgB,GAAG,QAAQ,CAAC;QACjC,IAAI,CAAC,gBAAgB,GAAG,QAAQ,CAAC;QAEjC,6DAA2D;QAC3D,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAE7B,iCAAiC;QACjC,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YACrD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC9B,IAAI,CAAC,aAAa,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YAC7D,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACnC,CAAC;aAAM,CAAC;YACP,MAAM,QAAQ,GAAG,6EAA6E,CAAC;YAC/F,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC9D,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAE7B,sBAAsB;QACtB,IAAI,CAAC,WAAW,GAAG,IAAI,KAAK,EAAE,CAAC;QAC/B,IAAI,kBAAkB,EAAE,CAAC;YACxB,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;QAC/C,CAAC;QACD,IAAI,CAAC,WAAW,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAC;YACjC,wDAAwD;YACxD,IAAI,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;gBAC7C,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,KAAK,CAAC,CAAC;YAClE,CAAC;QAAA,CACD,CAAC;QACF,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAEhC,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAE7B,wBAAwB;QACxB,IAAI,CAAC,aAAa,GAAG,IAAI,SAAS,EAAE,CAAC;QACrC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAElC,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAE7B,oCAAoC;QACpC,IAAI,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YAC5B,IAAI,kBAAkB,EAAE,CAAC;gBACxB,IAAI,CAAC,YAAY,CAAC,kBAAkB,CAAC,CAAC;YACvC,CAAC;iBAAM,CAAC;gBACP,IAAI,CAAC,UAAU,EAAE,CAAC;YACnB,CAAC;YACD,4CAA4C;YAC5C,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAAA,CACzB,CAAC,CAAC;IAAA,CACH;IAEO,KAAK,CAAC,UAAU,GAAkB;QACzC,IAAI,MAAmB,CAAC;QAExB,gDAAgD;QAChD,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,CAAC;QAE7B,+BAA+B;QAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC;QAChD,IAAI,SAAS,EAAE,CAAC;YACf,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;QAC/B,CAAC;QAED,gFAAgF;QAChF,IAAI,CAAC;YACJ,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,YAAY,EAAE,CAAC;YAChE,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,KAAiB,EAAE,EAAE,CAAC,CAAC;gBACpD,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,EAAE,EAAE,KAAK,CAAC,EAAE;gBACZ,KAAK;aACL,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;YACpB,IAAI,CAAC,gBAAgB,GAAG,EAAE,CAAC;YAC3B,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;YACvB,IAAI,CAAC,cAAc,GAAG,EAAE,CAAC;YACzB,IAAI,CAAC,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC3E,OAAO;QACR,CAAC;QAED,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACzC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;YACrD,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAClF,OAAO,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;QAAA,CAC5D,CAAC,CAAC;QACH,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YAC1D,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ;YAC/B,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE;YACnB,KAAK,EAAE,MAAM,CAAC,KAAK;SACnB,CAAC,CAAC,CAAC;QACJ,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;QACrF,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,YAAY,CAAC;QACxC,MAAM,YAAY,GAAG,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAC5G,IAAI,CAAC,aAAa;YACjB,YAAY,IAAI,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;IAAA,CAC9G;IAEO,UAAU,CAAC,MAAmB,EAAe;QACpD,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;QAC3B,8CAA8C;QAC9C,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YACrB,MAAM,UAAU,GAAG,cAAc,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;YAC9D,MAAM,UAAU,GAAG,cAAc,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;YAC9D,IAAI,UAAU,IAAI,CAAC,UAAU;gBAAE,OAAO,CAAC,CAAC,CAAC;YACzC,IAAI,CAAC,UAAU,IAAI,UAAU;gBAAE,OAAO,CAAC,CAAC;YACxC,OAAO,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAAA,CAC5C,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAAA,CACd;IAEO,YAAY,GAAW;QAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC5F,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACxG,OAAO,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC,GAAG,OAAO,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,UAAU,EAAE,CAAC;IAAA,CAC3F;IAEO,gBAAgB,GAAW;QAClC,OAAO,OAAO,CAAC,eAAe,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;IAAA,CAC9E;IAEO,QAAQ,CAAC,KAAiB,EAAQ;QACzC,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK;YAAE,OAAO;QACjC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;QACrF,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAC1G,IAAI,CAAC,aAAa,GAAG,YAAY,IAAI,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1D,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC/C,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;QAC7C,CAAC;IAAA,CACD;IAEO,YAAY,CAAC,KAAa,EAAQ;QACzC,IAAI,CAAC,cAAc,GAAG,KAAK;YAC1B,CAAC,CAAC,WAAW,CACX,IAAI,CAAC,YAAY,EACjB,KAAK,EACL,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,IAAI,QAAQ,IAAI,QAAQ,IAAI,EAAE,IAAI,QAAQ,IAAI,EAAE,EAAE,CAC7E;YACF,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC;QACrB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;QAC/F,IAAI,CAAC,UAAU,EAAE,CAAC;IAAA,CAClB;IAEO,UAAU,GAAS;QAC1B,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;QAE3B,MAAM,UAAU,GAAG,EAAE,CAAC;QACtB,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAC1B,CAAC,EACD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,MAAM,GAAG,UAAU,CAAC,CAClG,CAAC;QACF,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,GAAG,UAAU,EAAE,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAE/E,wCAAwC;QACxC,KAAK,IAAI,CAAC,GAAG,UAAU,EAAE,CAAC,GAAG,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;YACpC,IAAI,CAAC,IAAI;gBAAE,SAAS;YAEpB,MAAM,UAAU,GAAG,CAAC,KAAK,IAAI,CAAC,aAAa,CAAC;YAC5C,MAAM,SAAS,GAAG,cAAc,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;YAEhE,IAAI,IAAI,GAAG,EAAE,CAAC;YACd,IAAI,UAAU,EAAE,CAAC;gBAChB,MAAM,MAAM,GAAG,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAI,CAAC,CAAC;gBACxC,MAAM,SAAS,GAAG,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;gBAC/B,MAAM,aAAa,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;gBAC9D,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,MAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC7D,IAAI,GAAG,GAAG,MAAM,GAAG,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,aAAa,GAAG,SAAS,EAAE,CAAC;YACjF,CAAC;iBAAM,CAAC;gBACP,MAAM,SAAS,GAAG,KAAK,IAAI,CAAC,EAAE,EAAE,CAAC;gBACjC,MAAM,aAAa,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;gBAC9D,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,MAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC7D,IAAI,GAAG,GAAG,SAAS,IAAI,aAAa,GAAG,SAAS,EAAE,CAAC;YACpD,CAAC;YAED,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACnD,CAAC;QAED,iCAAiC;QACjC,IAAI,UAAU,GAAG,CAAC,IAAI,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC;YAC7D,MAAM,UAAU,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC,aAAa,GAAG,CAAC,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC;YACpG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACzD,CAAC;QAED,8CAA8C;QAC9C,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,oBAAoB;YACpB,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACjD,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;gBAC/B,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACtE,CAAC;QACF,CAAC;aAAM,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7C,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,sBAAsB,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACxF,CAAC;aAAM,CAAC;YACP,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACzD,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3C,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACxG,CAAC;IAAA,CACD;IAED,WAAW,CAAC,OAAe,EAAQ;QAClC,MAAM,EAAE,GAAG,cAAc,EAAE,CAAC;QAC5B,IAAI,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,eAAe,CAAC,EAAE,CAAC;YAC1C,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACtC,MAAM,SAAS,GAAe,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;gBACtE,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;gBACzB,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;oBACxB,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAC;gBACrD,CAAC;YACF,CAAC;YACD,OAAO;QACR,CAAC;QACD,wCAAwC;QACxC,IAAI,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,eAAe,CAAC,EAAE,CAAC;YAC1C,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO;YAC7C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC;YACxG,IAAI,CAAC,UAAU,EAAE,CAAC;QACnB,CAAC;QACD,0CAA0C;aACrC,IAAI,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,iBAAiB,CAAC,EAAE,CAAC;YACjD,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO;YAC7C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,KAAK,IAAI,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC;YACxG,IAAI,CAAC,UAAU,EAAE,CAAC;QACnB,CAAC;QACD,QAAQ;aACH,IAAI,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,oBAAoB,CAAC,EAAE,CAAC;YACpD,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAC9D,IAAI,aAAa,EAAE,CAAC;gBACnB,IAAI,CAAC,YAAY,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YACxC,CAAC;QACF,CAAC;QACD,mBAAmB;aACd,IAAI,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,mBAAmB,CAAC,EAAE,CAAC;YACnD,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACzB,CAAC;QACD,uCAAuC;aAClC,CAAC;YACL,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;YACtC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC;QAChD,CAAC;IAAA,CACD;IAEO,YAAY,CAAC,KAAiB,EAAQ;QAC7C,sBAAsB;QACtB,IAAI,CAAC,eAAe,CAAC,0BAA0B,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;QAC1E,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAAA,CAC7B;IAED,cAAc,GAAU;QACvB,OAAO,IAAI,CAAC,WAAW,CAAC;IAAA,CACxB;CACD","sourcesContent":["import { type Model, modelsAreEqual } from \"@kolisachint/hoocode-ai\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@kolisachint/hoocode-tui\";\nimport type { ModelRegistry } from \"../../../core/model-registry.js\";\nimport type { SettingsManager } from \"../../../core/settings-manager.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { keyHint } from \"./keybinding-hints.js\";\n\ninterface ModelItem {\n\tprovider: string;\n\tid: string;\n\tmodel: Model<any>;\n}\n\ninterface ScopedModelItem {\n\tmodel: Model<any>;\n\tthinkingLevel?: string;\n}\n\ntype ModelScope = \"all\" | \"scoped\";\n\n/**\n * Component that renders a model selector with search\n */\nexport class ModelSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\tprivate listContainer: Container;\n\tprivate allModels: ModelItem[] = [];\n\tprivate scopedModelItems: ModelItem[] = [];\n\tprivate activeModels: ModelItem[] = [];\n\tprivate filteredModels: ModelItem[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate currentModel?: Model<any>;\n\tprivate settingsManager: SettingsManager;\n\tprivate modelRegistry: ModelRegistry;\n\tprivate onSelectCallback: (model: Model<any>) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate errorMessage?: string;\n\tprivate tui: TUI;\n\tprivate scopedModels: ReadonlyArray<ScopedModelItem>;\n\tprivate scope: ModelScope = \"all\";\n\tprivate scopeText?: Text;\n\tprivate scopeHintText?: Text;\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tcurrentModel: Model<any> | undefined,\n\t\tsettingsManager: SettingsManager,\n\t\tmodelRegistry: ModelRegistry,\n\t\tscopedModels: ReadonlyArray<ScopedModelItem>,\n\t\tonSelect: (model: Model<any>) => void,\n\t\tonCancel: () => void,\n\t\tinitialSearchInput?: string,\n\t) {\n\t\tsuper();\n\n\t\tthis.tui = tui;\n\t\tthis.currentModel = currentModel;\n\t\tthis.settingsManager = settingsManager;\n\t\tthis.modelRegistry = modelRegistry;\n\t\tthis.scopedModels = scopedModels;\n\t\tthis.scope = scopedModels.length > 0 ? \"scoped\" : \"all\";\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onCancelCallback = onCancel;\n\n\t\t// Minimal chrome — no filled border, just a clean overlay.\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add hint about model filtering\n\t\tif (scopedModels.length > 0) {\n\t\t\tthis.scopeText = new Text(this.getScopeText(), 0, 0);\n\t\t\tthis.addChild(this.scopeText);\n\t\t\tthis.scopeHintText = new Text(this.getScopeHintText(), 0, 0);\n\t\t\tthis.addChild(this.scopeHintText);\n\t\t} else {\n\t\t\tconst hintText = \"Only showing models from configured providers. Use /login to add providers.\";\n\t\t\tthis.addChild(new Text(theme.fg(\"warning\", hintText), 0, 0));\n\t\t}\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create search input\n\t\tthis.searchInput = new Input();\n\t\tif (initialSearchInput) {\n\t\t\tthis.searchInput.setValue(initialSearchInput);\n\t\t}\n\t\tthis.searchInput.onSubmit = () => {\n\t\t\t// Enter on search input selects the first filtered item\n\t\t\tif (this.filteredModels[this.selectedIndex]) {\n\t\t\t\tthis.handleSelect(this.filteredModels[this.selectedIndex].model);\n\t\t\t}\n\t\t};\n\t\tthis.addChild(this.searchInput);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Load models and do initial render\n\t\tthis.loadModels().then(() => {\n\t\t\tif (initialSearchInput) {\n\t\t\t\tthis.filterModels(initialSearchInput);\n\t\t\t} else {\n\t\t\t\tthis.updateList();\n\t\t\t}\n\t\t\t// Request re-render after models are loaded\n\t\t\tthis.tui.requestRender();\n\t\t});\n\t}\n\n\tprivate async loadModels(): Promise<void> {\n\t\tlet models: ModelItem[];\n\n\t\t// Refresh to pick up any changes to models.json\n\t\tthis.modelRegistry.refresh();\n\n\t\t// Check for models.json errors\n\t\tconst loadError = this.modelRegistry.getError();\n\t\tif (loadError) {\n\t\t\tthis.errorMessage = loadError;\n\t\t}\n\n\t\t// Load available models (built-in models still work even if models.json failed)\n\t\ttry {\n\t\t\tconst availableModels = await this.modelRegistry.getAvailable();\n\t\t\tmodels = availableModels.map((model: Model<any>) => ({\n\t\t\t\tprovider: model.provider,\n\t\t\t\tid: model.id,\n\t\t\t\tmodel,\n\t\t\t}));\n\t\t} catch (error) {\n\t\t\tthis.allModels = [];\n\t\t\tthis.scopedModelItems = [];\n\t\t\tthis.activeModels = [];\n\t\t\tthis.filteredModels = [];\n\t\t\tthis.errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.allModels = this.sortModels(models);\n\t\tthis.scopedModels = this.scopedModels.map((scoped) => {\n\t\t\tconst refreshed = this.modelRegistry.find(scoped.model.provider, scoped.model.id);\n\t\t\treturn refreshed ? { ...scoped, model: refreshed } : scoped;\n\t\t});\n\t\tthis.scopedModelItems = this.scopedModels.map((scoped) => ({\n\t\t\tprovider: scoped.model.provider,\n\t\t\tid: scoped.model.id,\n\t\t\tmodel: scoped.model,\n\t\t}));\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tthis.filteredModels = this.activeModels;\n\t\tconst currentIndex = this.filteredModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex =\n\t\t\tcurrentIndex >= 0 ? currentIndex : Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t}\n\n\tprivate sortModels(models: ModelItem[]): ModelItem[] {\n\t\tconst sorted = [...models];\n\t\t// Sort: current model first, then by provider\n\t\tsorted.sort((a, b) => {\n\t\t\tconst aIsCurrent = modelsAreEqual(this.currentModel, a.model);\n\t\t\tconst bIsCurrent = modelsAreEqual(this.currentModel, b.model);\n\t\t\tif (aIsCurrent && !bIsCurrent) return -1;\n\t\t\tif (!aIsCurrent && bIsCurrent) return 1;\n\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t});\n\t\treturn sorted;\n\t}\n\n\tprivate getScopeText(): string {\n\t\tconst allText = this.scope === \"all\" ? theme.fg(\"accent\", \"all\") : theme.fg(\"muted\", \"all\");\n\t\tconst scopedText = this.scope === \"scoped\" ? theme.fg(\"accent\", \"scoped\") : theme.fg(\"muted\", \"scoped\");\n\t\treturn `${theme.fg(\"muted\", \"Scope: \")}${allText}${theme.fg(\"muted\", \" | \")}${scopedText}`;\n\t}\n\n\tprivate getScopeHintText(): string {\n\t\treturn keyHint(\"tui.input.tab\", \"scope\") + theme.fg(\"muted\", \" (all/scoped)\");\n\t}\n\n\tprivate setScope(scope: ModelScope): void {\n\t\tif (this.scope === scope) return;\n\t\tthis.scope = scope;\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tconst currentIndex = this.activeModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex = currentIndex >= 0 ? currentIndex : 0;\n\t\tthis.filterModels(this.searchInput.getValue());\n\t\tif (this.scopeText) {\n\t\t\tthis.scopeText.setText(this.getScopeText());\n\t\t}\n\t}\n\n\tprivate filterModels(query: string): void {\n\t\tthis.filteredModels = query\n\t\t\t? fuzzyFilter(\n\t\t\t\t\tthis.activeModels,\n\t\t\t\t\tquery,\n\t\t\t\t\t({ id, provider }) => `${id} ${provider} ${provider}/${id} ${provider} ${id}`,\n\t\t\t\t)\n\t\t\t: this.activeModels;\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tconst maxVisible = 10;\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(maxVisible / 2), this.filteredModels.length - maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + maxVisible, this.filteredModels.length);\n\n\t\t// Show visible slice of filtered models\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredModels[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst isCurrent = modelsAreEqual(this.currentModel, item.model);\n\n\t\t\tlet line = \"\";\n\t\t\tif (isSelected) {\n\t\t\t\tconst prefix = theme.fg(\"accent\", \"→ \");\n\t\t\t\tconst modelText = `${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${prefix + theme.fg(\"accent\", modelText)} ${providerBadge}${checkmark}`;\n\t\t\t} else {\n\t\t\t\tconst modelText = ` ${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${modelText} ${providerBadge}${checkmark}`;\n\t\t\t}\n\n\t\t\tthis.listContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredModels.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredModels.length})`);\n\t\t\tthis.listContainer.addChild(new Text(scrollInfo, 0, 0));\n\t\t}\n\n\t\t// Show error message or \"no results\" if empty\n\t\tif (this.errorMessage) {\n\t\t\t// Show error in red\n\t\t\tconst errorLines = this.errorMessage.split(\"\\n\");\n\t\t\tfor (const line of errorLines) {\n\t\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"error\", line), 0, 0));\n\t\t\t}\n\t\t} else if (this.filteredModels.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching models\"), 0, 0));\n\t\t} else {\n\t\t\tconst selected = this.filteredModels[this.selectedIndex];\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", ` Model Name: ${selected.model.name}`), 0, 0));\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.input.tab\")) {\n\t\t\tif (this.scopedModelItems.length > 0) {\n\t\t\t\tconst nextScope: ModelScope = this.scope === \"all\" ? \"scoped\" : \"all\";\n\t\t\t\tthis.setScope(nextScope);\n\t\t\t\tif (this.scopeHintText) {\n\t\t\t\t\tthis.scopeHintText.setText(this.getScopeHintText());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// Up arrow - wrap to bottom when at top\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredModels.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Down arrow - wrap to top when at bottom\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredModels.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tconst selectedModel = this.filteredModels[this.selectedIndex];\n\t\t\tif (selectedModel) {\n\t\t\t\tthis.handleSelect(selectedModel.model);\n\t\t\t}\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterModels(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate handleSelect(model: Model<any>): void {\n\t\t// Save as new default\n\t\tthis.settingsManager.setDefaultModelAndProvider(model.provider, model.id);\n\t\tthis.onSelectCallback(model);\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
|
|
@@ -2,10 +2,13 @@ import type { Component } from "@kolisachint/hoocode-tui";
|
|
|
2
2
|
/**
|
|
3
3
|
* Task panel rendered just above the editor prompt.
|
|
4
4
|
*
|
|
5
|
+
* - A ledger header (watched/reviewed stamp + done/total count) tops the list.
|
|
5
6
|
* - Shows all tasks with all statuses (pending / in_progress / done / failed).
|
|
7
|
+
* - Subagent mode is intentionally NOT shown here (e.g. no "[explore]" tag) — the
|
|
8
|
+
* task title is the meaningful label; the mode adds noise in the pane.
|
|
6
9
|
* - LIFO within the window: newest tasks appear at the bottom (closest to the prompt).
|
|
7
|
-
* - Finished tasks
|
|
8
|
-
*
|
|
10
|
+
* - Finished tasks carry their wall-clock cost and stay visible until the next
|
|
11
|
+
* user message arrives (see taskStore.retireFinished()), not the moment they finish.
|
|
9
12
|
* - Collapses to zero lines when there are no tasks.
|
|
10
13
|
*/
|
|
11
14
|
export declare class TaskPanelComponent implements Component {
|