@vandeepunk/pi-coding-agent 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +105 -0
- package/README.md +6 -6
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +1 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -2
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +7 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +16 -0
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/export-html/template.css +3 -0
- package/dist/core/export-html/template.js +32 -15
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +17 -2
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +53 -9
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/wrapper.d.ts.map +1 -1
- package/dist/core/extensions/wrapper.js +3 -3
- package/dist/core/extensions/wrapper.js.map +1 -1
- package/dist/core/model-registry.d.ts +3 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +133 -37
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +5 -5
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/package-manager.d.ts +21 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +134 -33
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/prompt-templates.d.ts +3 -3
- package/dist/core/prompt-templates.d.ts.map +1 -1
- package/dist/core/prompt-templates.js +15 -15
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +6 -6
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/settings-manager.d.ts +2 -2
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +4 -4
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +57 -3
- package/dist/core/skills.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/main.d.ts.map +1 -1
- package/dist/main.js +172 -177
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +11 -11
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +9 -4
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +6 -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 +5 -0
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +5 -0
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +49 -34
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +0 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +117 -104
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/utils/git.d.ts +21 -1
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +150 -4
- package/dist/utils/git.js.map +1 -1
- package/docs/extensions.md +5 -0
- package/docs/models.md +40 -1
- package/docs/packages.md +23 -3
- package/docs/prompt-templates.md +6 -6
- package/docs/providers.md +13 -0
- package/docs/rpc.md +1 -1
- package/docs/sdk.md +5 -3
- package/docs/settings.md +2 -2
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
- package/examples/extensions/hello.ts +1 -1
- package/examples/extensions/subagent/README.md +4 -4
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/08-prompt-templates.ts +2 -2
- package/package.json +7 -8
- /package/examples/extensions/subagent/{prompts → commands}/implement-and-review.md +0 -0
- /package/examples/extensions/subagent/{prompts → commands}/implement.md +0 -0
- /package/examples/extensions/subagent/{prompts → commands}/scout-and-plan.md +0 -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 { basename, dirname, join, relative } from \"node:path\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tgetEditorKeybindings,\n\tInput,\n\tmatchesKey,\n\tSpacer,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@mariozechner/pi-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 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\treturn metadata.scope === \"user\" ? \"User (~/.pi/agent/)\" : \"Project (.pi/)\";\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}`;\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\tlines.push(theme.fg(\"dim\", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`));\n\t\t}\n\n\t\treturn lines;\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getEditorKeybindings();\n\n\t\tif (kb.matches(data, \"selectUp\")) {\n\t\t\tthis.selectedIndex = this.findNextItem(this.selectedIndex, -1);\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"selectDown\")) {\n\t\t\tthis.selectedIndex = this.findNextItem(this.selectedIndex, 1);\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"selectPageUp\")) {\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, \"selectPageDown\")) {\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, \"selectCancel\")) {\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, \"selectConfirm\")) {\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 { basename, dirname, join, relative } from \"node:path\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tgetEditorKeybindings,\n\tInput,\n\tmatchesKey,\n\tSpacer,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@mariozechner/pi-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\" | \"commands\" | \"themes\";\n\nconst RESOURCE_TYPE_LABELS: Record<ResourceType, string> = {\n\textensions: \"Extensions\",\n\tskills: \"Skills\",\n\tcommands: \"Commands\",\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 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\treturn metadata.scope === \"user\" ? \"User (~/.pi/agent/)\" : \"Project (.pi/)\";\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}`;\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.commands, \"commands\");\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, commands: 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\tlines.push(theme.fg(\"dim\", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`));\n\t\t}\n\n\t\treturn lines;\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getEditorKeybindings();\n\n\t\tif (kb.matches(data, \"selectUp\")) {\n\t\t\tthis.selectedIndex = this.findNextItem(this.selectedIndex, -1);\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"selectDown\")) {\n\t\t\tthis.selectedIndex = this.findNextItem(this.selectedIndex, 1);\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"selectPageUp\")) {\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, \"selectPageDown\")) {\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, \"selectCancel\")) {\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, \"selectConfirm\")) {\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\" | \"commands\" | \"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 === \"commands\") {\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 === \"commands\") {\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\" | \"commands\" | \"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\", \"commands\", \"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"]}
|
|
@@ -10,7 +10,7 @@ import { rawKeyHint } from "./keybinding-hints.js";
|
|
|
10
10
|
const RESOURCE_TYPE_LABELS = {
|
|
11
11
|
extensions: "Extensions",
|
|
12
12
|
skills: "Skills",
|
|
13
|
-
|
|
13
|
+
commands: "Commands",
|
|
14
14
|
themes: "Themes",
|
|
15
15
|
};
|
|
16
16
|
function getGroupLabel(metadata) {
|
|
@@ -75,7 +75,7 @@ function buildGroups(resolved) {
|
|
|
75
75
|
};
|
|
76
76
|
addToGroup(resolved.extensions, "extensions");
|
|
77
77
|
addToGroup(resolved.skills, "skills");
|
|
78
|
-
addToGroup(resolved.
|
|
78
|
+
addToGroup(resolved.commands, "commands");
|
|
79
79
|
addToGroup(resolved.themes, "themes");
|
|
80
80
|
// Sort groups: packages first, then top-level; user before project
|
|
81
81
|
const groups = Array.from(groupMap.values());
|
|
@@ -89,7 +89,7 @@ function buildGroups(resolved) {
|
|
|
89
89
|
return a.source.localeCompare(b.source);
|
|
90
90
|
});
|
|
91
91
|
// Sort subgroups within each group by type order, and items by name
|
|
92
|
-
const typeOrder = { extensions: 0, skills: 1,
|
|
92
|
+
const typeOrder = { extensions: 0, skills: 1, commands: 2, themes: 3 };
|
|
93
93
|
for (const group of groups) {
|
|
94
94
|
group.subgroups.sort((a, b) => typeOrder[a.type] - typeOrder[b.type]);
|
|
95
95
|
for (const subgroup of group.subgroups) {
|
|
@@ -361,7 +361,7 @@ class ResourceList {
|
|
|
361
361
|
else if (arrayKey === "skills") {
|
|
362
362
|
this.settingsManager.setProjectSkillPaths(updated);
|
|
363
363
|
}
|
|
364
|
-
else if (arrayKey === "
|
|
364
|
+
else if (arrayKey === "commands") {
|
|
365
365
|
this.settingsManager.setProjectPromptTemplatePaths(updated);
|
|
366
366
|
}
|
|
367
367
|
else if (arrayKey === "themes") {
|
|
@@ -375,7 +375,7 @@ class ResourceList {
|
|
|
375
375
|
else if (arrayKey === "skills") {
|
|
376
376
|
this.settingsManager.setSkillPaths(updated);
|
|
377
377
|
}
|
|
378
|
-
else if (arrayKey === "
|
|
378
|
+
else if (arrayKey === "commands") {
|
|
379
379
|
this.settingsManager.setPromptTemplatePaths(updated);
|
|
380
380
|
}
|
|
381
381
|
else if (arrayKey === "themes") {
|
|
@@ -419,7 +419,7 @@ class ResourceList {
|
|
|
419
419
|
}
|
|
420
420
|
pkg[arrayKey] = updated.length > 0 ? updated : undefined;
|
|
421
421
|
// Clean up empty filter object
|
|
422
|
-
const hasFilters = ["extensions", "skills", "
|
|
422
|
+
const hasFilters = ["extensions", "skills", "commands", "themes"].some((k) => pkg[k] !== undefined);
|
|
423
423
|
if (!hasFilters) {
|
|
424
424
|
packages[pkgIndex] = pkg.source;
|
|
425
425
|
}
|
|
@@ -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 { basename, dirname, join, relative } from \"node:path\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tgetEditorKeybindings,\n\tInput,\n\tmatchesKey,\n\tSpacer,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@mariozechner/pi-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 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\treturn metadata.scope === \"user\" ? \"User (~/.pi/agent/)\" : \"Project (.pi/)\";\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}`;\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\tlines.push(theme.fg(\"dim\", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`));\n\t\t}\n\n\t\treturn lines;\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getEditorKeybindings();\n\n\t\tif (kb.matches(data, \"selectUp\")) {\n\t\t\tthis.selectedIndex = this.findNextItem(this.selectedIndex, -1);\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"selectDown\")) {\n\t\t\tthis.selectedIndex = this.findNextItem(this.selectedIndex, 1);\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"selectPageUp\")) {\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, \"selectPageDown\")) {\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, \"selectCancel\")) {\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, \"selectConfirm\")) {\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 { basename, dirname, join, relative } from \"node:path\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tgetEditorKeybindings,\n\tInput,\n\tmatchesKey,\n\tSpacer,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@mariozechner/pi-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\" | \"commands\" | \"themes\";\n\nconst RESOURCE_TYPE_LABELS: Record<ResourceType, string> = {\n\textensions: \"Extensions\",\n\tskills: \"Skills\",\n\tcommands: \"Commands\",\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 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\treturn metadata.scope === \"user\" ? \"User (~/.pi/agent/)\" : \"Project (.pi/)\";\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}`;\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.commands, \"commands\");\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, commands: 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\tlines.push(theme.fg(\"dim\", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`));\n\t\t}\n\n\t\treturn lines;\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getEditorKeybindings();\n\n\t\tif (kb.matches(data, \"selectUp\")) {\n\t\t\tthis.selectedIndex = this.findNextItem(this.selectedIndex, -1);\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"selectDown\")) {\n\t\t\tthis.selectedIndex = this.findNextItem(this.selectedIndex, 1);\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"selectPageUp\")) {\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, \"selectPageDown\")) {\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, \"selectCancel\")) {\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, \"selectConfirm\")) {\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\" | \"commands\" | \"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 === \"commands\") {\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 === \"commands\") {\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\" | \"commands\" | \"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\", \"commands\", \"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 +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,qBAAqB,CAAC;AACjE,OAAO,EACN,SAAS,EACT,KAAK,SAAS,EAGd,KAAK,EAGL,KAAK,GAAG,EACR,MAAM,sBAAsB,CAAC;AAC9B,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,EAAE,MAAM,CAAC;CACtB;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;IA0CxB,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,QAAQ;IAWhB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,UAAU;IAqDlB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAwCjC;IAED,OAAO,CAAC,YAAY;IAMpB,cAAc,IAAI,KAAK,CAEtB;CACD","sourcesContent":["import { type Model, modelsAreEqual } from \"@mariozechner/pi-ai\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetEditorKeybindings,\n\tInput,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@mariozechner/pi-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 with configured API keys (see README for details)\";\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.scopedModelItems = this.sortModels(\n\t\t\tthis.scopedModels.map((scoped) => ({\n\t\t\t\tprovider: scoped.model.provider,\n\t\t\t\tid: scoped.model.id,\n\t\t\t\tmodel: scoped.model,\n\t\t\t})),\n\t\t);\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tthis.filteredModels = this.activeModels;\n\t\tthis.selectedIndex = 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(\"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\tthis.selectedIndex = 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(this.activeModels, query, ({ id, provider }) => `${id} ${provider}`)\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}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getEditorKeybindings();\n\t\tif (kb.matches(keyData, \"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, \"selectUp\")) {\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, \"selectDown\")) {\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, \"selectConfirm\")) {\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, \"selectCancel\")) {\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,qBAAqB,CAAC;AACjE,OAAO,EACN,SAAS,EACT,KAAK,SAAS,EAGd,KAAK,EAGL,KAAK,GAAG,EACR,MAAM,sBAAsB,CAAC;AAC9B,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,EAAE,MAAM,CAAC;CACtB;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;IA0CxB,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,QAAQ;IAWhB,OAAO,CAAC,YAAY;IAQpB,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 \"@mariozechner/pi-ai\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetEditorKeybindings,\n\tInput,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@mariozechner/pi-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 with configured API keys (see README for details)\";\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.scopedModelItems = this.sortModels(\n\t\t\tthis.scopedModels.map((scoped) => ({\n\t\t\t\tprovider: scoped.model.provider,\n\t\t\t\tid: scoped.model.id,\n\t\t\t\tmodel: scoped.model,\n\t\t\t})),\n\t\t);\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tthis.filteredModels = this.activeModels;\n\t\tthis.selectedIndex = 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(\"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\tthis.selectedIndex = 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(this.activeModels, query, ({ id, provider }) => `${id} ${provider}`)\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 = getEditorKeybindings();\n\t\tif (kb.matches(keyData, \"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, \"selectUp\")) {\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, \"selectDown\")) {\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, \"selectConfirm\")) {\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, \"selectCancel\")) {\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"]}
|
|
@@ -210,6 +210,11 @@ export class ModelSelectorComponent extends Container {
|
|
|
210
210
|
else if (this.filteredModels.length === 0) {
|
|
211
211
|
this.listContainer.addChild(new Text(theme.fg("muted", " No matching models"), 0, 0));
|
|
212
212
|
}
|
|
213
|
+
else {
|
|
214
|
+
const selected = this.filteredModels[this.selectedIndex];
|
|
215
|
+
this.listContainer.addChild(new Spacer(1));
|
|
216
|
+
this.listContainer.addChild(new Text(theme.fg("muted", ` Model Name: ${selected.model.name}`), 0, 0));
|
|
217
|
+
}
|
|
213
218
|
}
|
|
214
219
|
handleInput(keyData) {
|
|
215
220
|
const kb = getEditorKeybindings();
|
|
@@ -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 \"@mariozechner/pi-ai\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetEditorKeybindings,\n\tInput,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@mariozechner/pi-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 with configured API keys (see README for details)\";\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.scopedModelItems = this.sortModels(\n\t\t\tthis.scopedModels.map((scoped) => ({\n\t\t\t\tprovider: scoped.model.provider,\n\t\t\t\tid: scoped.model.id,\n\t\t\t\tmodel: scoped.model,\n\t\t\t})),\n\t\t);\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tthis.filteredModels = this.activeModels;\n\t\tthis.selectedIndex = 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(\"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\tthis.selectedIndex = 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(this.activeModels, query, ({ id, provider }) => `${id} ${provider}`)\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}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getEditorKeybindings();\n\t\tif (kb.matches(keyData, \"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, \"selectUp\")) {\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, \"selectDown\")) {\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, \"selectConfirm\")) {\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, \"selectCancel\")) {\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":"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","sourcesContent":["import { type Model, modelsAreEqual } from \"@mariozechner/pi-ai\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetEditorKeybindings,\n\tInput,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@mariozechner/pi-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 with configured API keys (see README for details)\";\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.scopedModelItems = this.sortModels(\n\t\t\tthis.scopedModels.map((scoped) => ({\n\t\t\t\tprovider: scoped.model.provider,\n\t\t\t\tid: scoped.model.id,\n\t\t\t\tmodel: scoped.model,\n\t\t\t})),\n\t\t);\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tthis.filteredModels = this.activeModels;\n\t\tthis.selectedIndex = 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(\"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\tthis.selectedIndex = 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(this.activeModels, query, ({ id, provider }) => `${id} ${provider}`)\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 = getEditorKeybindings();\n\t\tif (kb.matches(keyData, \"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, \"selectUp\")) {\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, \"selectDown\")) {\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, \"selectConfirm\")) {\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, \"selectCancel\")) {\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"]}
|