@caupulican/pi-adaptative 0.80.48 → 0.80.49
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 +8 -0
- package/dist/modes/interactive/components/profile-resource-editor.d.ts +3 -0
- package/dist/modes/interactive/components/profile-resource-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/profile-resource-editor.js +78 -1
- package/dist/modes/interactive/components/profile-resource-editor.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +4 -4
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +111 -25
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- 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/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## [0.80.49] - 2026-06-26
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
|
|
5
|
+
- Added Edit-in-$EDITOR action (key `e`) in the Manage Library browser to directly edit the highlighted resource's source file and reload upon return.
|
|
6
|
+
- Added consistent "Profile / Situation" labeling across all settings selectors.
|
|
7
|
+
- Added friendly empty-state messaging in kind tabs with no available resources.
|
|
8
|
+
|
|
1
9
|
## [0.80.48] - 2026-06-26
|
|
2
10
|
|
|
3
11
|
### Added
|
|
@@ -18,10 +18,12 @@ export interface ProfileResourceEditorOptions {
|
|
|
18
18
|
onSave: (resources: ResourceProfileSettings) => void;
|
|
19
19
|
onCancel: () => void;
|
|
20
20
|
onScopeChange?: () => void;
|
|
21
|
+
onEdit?: (id: string, path: string, kind: ResourceProfileKind) => void;
|
|
21
22
|
cwd?: string;
|
|
22
23
|
agentDir?: string;
|
|
23
24
|
externalResourceRoots?: string[];
|
|
24
25
|
}
|
|
26
|
+
export declare function resolveResourceEditPath(_id: string, filePath: string | undefined, kind: ResourceProfileKind): string | undefined;
|
|
25
27
|
export declare function classifyResourceSource(filePath: string | undefined, cwd: string, agentDir: string, externalRoots: string[]): "catalog" | "user" | "project" | "bundled";
|
|
26
28
|
export declare class ProfileResourceEditorComponent extends Container implements Focusable {
|
|
27
29
|
private profileName;
|
|
@@ -49,6 +51,7 @@ export declare class ProfileResourceEditorComponent extends Container implements
|
|
|
49
51
|
private onSave;
|
|
50
52
|
private onCancel;
|
|
51
53
|
private onScopeChange?;
|
|
54
|
+
private onEdit?;
|
|
52
55
|
constructor(options: ProfileResourceEditorOptions);
|
|
53
56
|
private getKindHeaderText;
|
|
54
57
|
private getCurrentKind;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profile-resource-editor.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/profile-resource-editor.ts"],"names":[],"mappings":"AACA,OAAO,EACN,SAAS,EACT,KAAK,SAAS,EAGd,KAAK,EAKL,MAAM,oBAAoB,CAAC;AAO5B,OAAO,KAAK,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AAKtG,MAAM,WAAW,mBAAmB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,yBAAyB;IACzC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,mBAAmB,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,4BAA4B;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,uBAAuB,CAAC;IAC1C,KAAK,EAAE,yBAAyB,EAAE,CAAC;IACnC,MAAM,EAAE,CAAC,SAAS,EAAE,uBAAuB,KAAK,IAAI,CAAC;IACrD,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAwBD,wBAAgB,sBAAsB,CACrC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EAAE,GACrB,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,CA8B5C;AAED,qBAAa,8BAA+B,SAAQ,SAAU,YAAW,SAAS;IACjF,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,KAAK,CAA8B;IAC3C,OAAO,CAAC,aAAa,CAAoD;IACzE,OAAO,CAAC,aAAa,CAAwD;IAC7E,OAAO,CAAC,gBAAgB,CAAoD;IAC5E,OAAO,CAAC,gBAAgB,CAAK;IAE7B,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,qBAAqB,CAAW;IAExC,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,cAAc,CAAO;IAC7B,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,eAAe,CAAO;IAC9B,OAAO,CAAC,UAAU,CAAO;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAK;IAEvB,OAAO,CAAC,QAAQ,CAAS;IAEzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IAED,OAAO,CAAC,MAAM,CAA+C;IAC7D,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,aAAa,CAAC,CAAa;IAEnC,YAAY,OAAO,EAAE,4BAA4B,EA8FhD;IAED,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,UAAU;IAqDlB,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,OAAO;IASf,OAAO,CAAC,UAAU;IAwDlB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CA+F9B;IAED,OAAO,CAAC,cAAc;IAgBtB,cAAc,IAAI,KAAK,CAEtB;CACD","sourcesContent":["import { isAbsolute, resolve } from \"node:path\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tKey,\n\tmatchesKey,\n\tSpacer,\n\tText,\n} from \"@caupulican/pi-tui\";\nimport {\n\tdecodeResourceSelection,\n\tdetectResourceFraming,\n\tencodeResourceSelectionWithFraming,\n\ttype ResourceFraming,\n} from \"../../../core/profile-resource-selection.ts\";\nimport type { ResourceProfileKind, ResourceProfileSettings } from \"../../../core/settings-manager.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\n\nexport interface ProfileResourceItem {\n\tid: string;\n\tpath?: string;\n\tdescription?: string;\n}\n\nexport interface ProfileResourceEditorKind {\n\tkind: ResourceProfileKind; // \"tools\" | \"skills\" | \"extensions\" | \"agents\" | \"prompts\" | \"themes\"\n\tlabel: string; // display label, e.g. \"Tools\"\n\titems: ProfileResourceItem[]; // the available items of this kind\n}\n\nexport interface ProfileResourceEditorOptions {\n\tprofileName: string;\n\tprofileScope: string; // e.g. \"directory\" | \"project\" | \"global\" | \"session\" | \"reusable-file\"\n\tinitialResources: ResourceProfileSettings; // existing profile.resources; may be {}\n\tkinds: ProfileResourceEditorKind[]; // the six kinds, with their universes\n\tonSave: (resources: ResourceProfileSettings) => void; // called on ctrl+s with the encoded result\n\tonCancel: () => void; // called on esc\n\tonScopeChange?: () => void;\n\tcwd?: string;\n\tagentDir?: string;\n\texternalResourceRoots?: string[];\n}\n\ninterface ResourceItem {\n\tid: string;\n\tenabled: boolean;\n\tpath?: string;\n\tdescription?: string;\n\tsourceLabel?: \"catalog\" | \"user\" | \"project\" | \"bundled\";\n\tisMissing?: boolean;\n}\n\nconst TOOL_DESCRIPTIONS: Record<string, string> = {\n\tread: \"Read files from the filesystem.\",\n\tbash: \"Execute arbitrary commands in the shell.\",\n\tedit: \"Edit files surgically.\",\n\twrite: \"Create new files or overwrite existing files.\",\n\tgrep: \"Search for text patterns using ripgrep.\",\n\tfind: \"Locate files matching a search query.\",\n\tls: \"List files and directories.\",\n\tskill_audit: \"Inspect and audit local skills.\",\n\tskillify: \"Convert a set of files/instructions into a skill.\",\n\textensionify: \"Package a tool or script as an extension.\",\n};\n\nexport function classifyResourceSource(\n\tfilePath: string | undefined,\n\tcwd: string,\n\tagentDir: string,\n\texternalRoots: string[],\n): \"catalog\" | \"user\" | \"project\" | \"bundled\" {\n\tif (!filePath) {\n\t\treturn \"bundled\";\n\t}\n\n\tconst absolutePath = isAbsolute(filePath) ? filePath : resolve(cwd, filePath);\n\tconst normPath = resolve(absolutePath);\n\tconst normCwd = resolve(cwd);\n\tconst normAgentDir = agentDir ? resolve(agentDir) : \"\";\n\n\tfor (const extRoot of externalRoots) {\n\t\tconst normExt = resolve(extRoot);\n\t\tif (normPath.startsWith(normExt)) {\n\t\t\treturn \"catalog\";\n\t\t}\n\t}\n\n\tif (normAgentDir && normPath.startsWith(normAgentDir)) {\n\t\treturn \"user\";\n\t}\n\n\tif (normPath.startsWith(normCwd)) {\n\t\treturn \"project\";\n\t}\n\n\tif (normPath.includes(\"node_modules\")) {\n\t\treturn \"bundled\";\n\t}\n\n\treturn \"bundled\";\n}\n\nexport class ProfileResourceEditorComponent extends Container implements Focusable {\n\tprivate profileName: string;\n\tprivate profileScope: string;\n\tprivate kinds: ProfileResourceEditorKind[];\n\tprivate enabledByKind: Map<ResourceProfileKind, Set<string>> = new Map();\n\tprivate framingByKind: Map<ResourceProfileKind, ResourceFraming> = new Map();\n\tprivate missingIdsByKind: Map<ResourceProfileKind, Set<string>> = new Map();\n\tprivate currentKindIndex = 0;\n\n\tprivate cwd: string;\n\tprivate agentDir: string;\n\tprivate externalResourceRoots: string[];\n\n\tprivate filteredItems: ResourceItem[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\tprivate kindHeaderText: Text;\n\tprivate listContainer: Container;\n\tprivate descriptionText: Text;\n\tprivate footerText: Text;\n\tprivate isDirty = false;\n\tprivate maxVisible = 8;\n\n\tprivate _focused = false;\n\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\tprivate onSave: (resources: ResourceProfileSettings) => void;\n\tprivate onCancel: () => void;\n\tprivate onScopeChange?: () => void;\n\n\tconstructor(options: ProfileResourceEditorOptions) {\n\t\tsuper();\n\t\tthis.profileName = options.profileName;\n\t\tthis.profileScope = options.profileScope;\n\t\tthis.kinds = options.kinds;\n\t\tthis.onSave = options.onSave;\n\t\tthis.onCancel = options.onCancel;\n\t\tthis.onScopeChange = options.onScopeChange;\n\t\tthis.cwd = options.cwd || process.cwd();\n\t\tthis.agentDir = options.agentDir || \"\";\n\t\tthis.externalResourceRoots = options.externalResourceRoots || [];\n\n\t\t// Initialize enabled, framing, and missing sets for each kind\n\t\tfor (const kind of this.kinds) {\n\t\t\tconst filter = options.initialResources[kind.kind];\n\t\t\tconst allIds = kind.items.map((item) => item.id);\n\t\t\tconst enabledSet = decodeResourceSelection(filter, allIds);\n\t\t\tthis.enabledByKind.set(kind.kind, enabledSet);\n\t\t\tthis.framingByKind.set(kind.kind, detectResourceFraming(filter));\n\n\t\t\t// Compute missing items\n\t\t\tconst mentionedIds = new Set<string>();\n\t\t\tif (filter) {\n\t\t\t\tif (filter.allow) {\n\t\t\t\t\tfor (const id of filter.allow) {\n\t\t\t\t\t\tmentionedIds.add(id);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (filter.block) {\n\t\t\t\t\tfor (const id of filter.block) {\n\t\t\t\t\t\tif (id !== \"*\") {\n\t\t\t\t\t\t\tmentionedIds.add(id);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst availableIds = new Set(allIds);\n\t\t\tconst missingSet = new Set<string>();\n\t\t\tfor (const id of mentionedIds) {\n\t\t\t\tif (!availableIds.has(id)) {\n\t\t\t\t\tmissingSet.add(id);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.missingIdsByKind.set(kind.kind, missingSet);\n\t\t}\n\n\t\t// Header\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"accent\", theme.bold(`Library — editing \"${this.profileName}\" (${this.profileScope})`)),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\n\t\t\t\t\t\"muted\",\n\t\t\t\t\t`Navigate kinds: ${keyText(\"tui.input.tab\")}. Toggle: ${keyText(\"tui.select.confirm\")}. Mode: ${theme.fg(\"accent\", \"a\")} allow / ${theme.fg(\"accent\", \"b\")} block. Scope: ${theme.fg(\"accent\", \"s\")} change.`,\n\t\t\t\t),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Kind selector header\n\t\tthis.kindHeaderText = new Text(this.getKindHeaderText(), 0, 0);\n\t\tthis.addChild(this.kindHeaderText);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Search input\n\t\tthis.searchInput = new Input();\n\t\tthis.addChild(this.searchInput);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// List container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\t// Description area\n\t\tthis.descriptionText = new Text(\"\", 0, 0);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(this.descriptionText);\n\n\t\t// Footer hint\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.footerText = new Text(this.getFooterText(), 0, 0);\n\t\tthis.addChild(this.footerText);\n\n\t\tthis.addChild(new DynamicBorder());\n\n\t\tthis.refresh();\n\t}\n\n\tprivate getKindHeaderText(): string {\n\t\tconst kind = this.kinds[this.currentKindIndex]!;\n\t\tconst enabledSet = this.enabledByKind.get(kind.kind)!;\n\t\tconst framing = this.framingByKind.get(kind.kind)!;\n\t\tconst framingText = framing === \"allow\" ? \"allow-list\" : \"block-list\";\n\t\tconst countText = `${enabledSet.size}/${kind.items.length} enabled (${framingText})`;\n\t\tconst kindIndicator = this.kinds\n\t\t\t.map((k, i) => {\n\t\t\t\tconst marker = i === this.currentKindIndex ? \"●\" : \"○\";\n\t\t\t\treturn theme.fg(i === this.currentKindIndex ? \"accent\" : \"muted\", `${marker} ${k.label}`);\n\t\t\t})\n\t\t\t.join(\" \");\n\t\treturn `${kindIndicator} ${theme.fg(\"muted\", countText)}`;\n\t}\n\n\tprivate getCurrentKind(): ProfileResourceEditorKind {\n\t\treturn this.kinds[this.currentKindIndex]!;\n\t}\n\n\tprivate getCurrentEnabledSet(): Set<string> {\n\t\tconst kind = this.getCurrentKind();\n\t\treturn this.enabledByKind.get(kind.kind)!;\n\t}\n\n\tprivate buildItems(): ResourceItem[] {\n\t\tconst kind = this.getCurrentKind();\n\t\tconst enabledSet = this.getCurrentEnabledSet();\n\n\t\tconst items: ResourceItem[] = [];\n\n\t\t// Add all available items\n\t\tfor (const availableItem of kind.items) {\n\t\t\tconst isEnabled = enabledSet.has(availableItem.id);\n\t\t\tlet desc = availableItem.description;\n\t\t\tif (kind.kind === \"tools\" && !desc) {\n\t\t\t\tdesc = TOOL_DESCRIPTIONS[availableItem.id];\n\t\t\t}\n\t\t\titems.push({\n\t\t\t\tid: availableItem.id,\n\t\t\t\tenabled: isEnabled,\n\t\t\t\tpath: availableItem.path,\n\t\t\t\tdescription: desc,\n\t\t\t\tsourceLabel: classifyResourceSource(\n\t\t\t\t\tavailableItem.path,\n\t\t\t\t\tthis.cwd,\n\t\t\t\t\tthis.agentDir,\n\t\t\t\t\tthis.externalResourceRoots,\n\t\t\t\t),\n\t\t\t\tisMissing: false,\n\t\t\t});\n\t\t}\n\n\t\t// Add missing items\n\t\tconst missingSet = this.missingIdsByKind.get(kind.kind) || new Set<string>();\n\t\tfor (const missingId of missingSet) {\n\t\t\tconst isEnabled = enabledSet.has(missingId);\n\t\t\titems.push({\n\t\t\t\tid: missingId,\n\t\t\t\tenabled: isEnabled,\n\t\t\t\tisMissing: true,\n\t\t\t\tdescription: \"Referenced in profile but missing from available resources.\",\n\t\t\t});\n\t\t}\n\n\t\t// Sort: enabled first, then disabled\n\t\tconst enabled: ResourceItem[] = [];\n\t\tconst disabled: ResourceItem[] = [];\n\t\tfor (const item of items) {\n\t\t\tif (item.enabled) {\n\t\t\t\tenabled.push(item);\n\t\t\t} else {\n\t\t\t\tdisabled.push(item);\n\t\t\t}\n\t\t}\n\t\treturn [...enabled, ...disabled];\n\t}\n\n\tprivate getFooterText(): string {\n\t\tconst kind = this.getCurrentKind();\n\t\tconst enabledSet = this.getCurrentEnabledSet();\n\t\tconst countText = `${enabledSet.size}/${kind.items.length} enabled`;\n\t\tconst parts = [\n\t\t\t`${keyText(\"tui.select.confirm\")} toggle`,\n\t\t\t`${keyText(\"tui.input.tab\")} kind`,\n\t\t\t`${keyText(\"app.models.save\")} save`,\n\t\t\tcountText,\n\t\t];\n\t\treturn this.isDirty\n\t\t\t? theme.fg(\"dim\", ` ${parts.join(\" · \")} `) + theme.fg(\"warning\", \"(unsaved)\")\n\t\t\t: theme.fg(\"dim\", ` ${parts.join(\" · \")}`);\n\t}\n\n\tprivate refresh(): void {\n\t\tconst query = this.searchInput.getValue();\n\t\tconst items = this.buildItems();\n\t\tthis.filteredItems = query ? fuzzyFilter(items, query, (i) => `${i.id} ${i.description ?? \"\"}`) : items;\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredItems.length - 1));\n\t\tthis.updateList();\n\t\tthis.footerText.setText(this.getFooterText());\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tif (this.filteredItems.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching resources\"), 0, 0));\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", \" No description available\"));\n\t\t\treturn;\n\t\t}\n\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 item = this.filteredItems[i]!;\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst prefix = isSelected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tlet resourceText = isSelected ? theme.fg(\"accent\", item.id) : item.id;\n\n\t\t\tif (item.isMissing) {\n\t\t\t\tresourceText = theme.fg(\"muted\", `${item.id} [missing]`);\n\t\t\t} else if (item.sourceLabel) {\n\t\t\t\tconst labelColor =\n\t\t\t\t\titem.sourceLabel === \"catalog\"\n\t\t\t\t\t\t? \"thinkingText\"\n\t\t\t\t\t\t: item.sourceLabel === \"project\"\n\t\t\t\t\t\t\t? \"success\"\n\t\t\t\t\t\t\t: item.sourceLabel === \"user\"\n\t\t\t\t\t\t\t\t? \"warning\"\n\t\t\t\t\t\t\t\t: \"muted\";\n\t\t\t\tconst labelText = theme.fg(labelColor, ` [${item.sourceLabel}]`);\n\t\t\t\tresourceText = `${resourceText}${labelText}`;\n\t\t\t}\n\n\t\t\tconst status = item.enabled ? theme.fg(\"success\", \" ✓\") : theme.fg(\"dim\", \" ✗\");\n\t\t\tthis.listContainer.addChild(new Text(`${prefix}${resourceText}${status}`, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredItems.length) {\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`), 0, 0),\n\t\t\t);\n\t\t}\n\n\t\t// Update description area for current selection\n\t\tconst selectedItem = this.filteredItems[this.selectedIndex];\n\t\tif (selectedItem?.description) {\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", ` Description: ${selectedItem.description}`));\n\t\t} else {\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", \" No description available\"));\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\n\t\t// Navigation within list\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredItems.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.down\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredItems.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch kind with Tab (cycles forward)\n\t\tif (kb.matches(data, \"tui.input.tab\")) {\n\t\t\tthis.currentKindIndex = (this.currentKindIndex + 1) % this.kinds.length;\n\t\t\tthis.selectedIndex = 0;\n\t\t\tthis.searchInput.setValue(\"\");\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch framing: Allow-list\n\t\tif (matchesKey(data, Key.ctrl(\"a\")) || (this.searchInput.getValue() === \"\" && data === \"a\")) {\n\t\t\tthis.framingByKind.set(this.getCurrentKind().kind, \"allow\");\n\t\t\tthis.isDirty = true;\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch framing: Block-list\n\t\tif (matchesKey(data, Key.ctrl(\"b\")) || (this.searchInput.getValue() === \"\" && data === \"b\")) {\n\t\t\tthis.framingByKind.set(this.getCurrentKind().kind, \"block\");\n\t\t\tthis.isDirty = true;\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch scope: Scope selector\n\t\tif (matchesKey(data, Key.ctrl(\"o\")) || (this.searchInput.getValue() === \"\" && data === \"s\")) {\n\t\t\tif (this.onScopeChange) {\n\t\t\t\tthis.onScopeChange();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Toggle on space/enter\n\t\tif (kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item) {\n\t\t\t\tconst enabledSet = this.getCurrentEnabledSet();\n\t\t\t\tif (enabledSet.has(item.id)) {\n\t\t\t\t\tenabledSet.delete(item.id);\n\t\t\t\t} else {\n\t\t\t\t\tenabledSet.add(item.id);\n\t\t\t\t}\n\t\t\t\tthis.isDirty = true;\n\t\t\t\tthis.refresh();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Save/persist to settings\n\t\tif (kb.matches(data, \"app.models.save\")) {\n\t\t\tthis.persistChanges();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+C - clear search or cancel if empty\n\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\tif (this.searchInput.getValue()) {\n\t\t\t\tthis.searchInput.setValue(\"\");\n\t\t\t\tthis.refresh();\n\t\t\t} else {\n\t\t\t\tthis.onCancel();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Escape - cancel\n\t\tif (matchesKey(data, Key.escape)) {\n\t\t\tthis.onCancel();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass everything else to search input\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.refresh();\n\t}\n\n\tprivate persistChanges(): void {\n\t\tconst resources: ResourceProfileSettings = {};\n\t\tfor (const kind of this.kinds) {\n\t\t\tconst enabledSet = this.enabledByKind.get(kind.kind)!;\n\t\t\tconst framing = this.framingByKind.get(kind.kind)!;\n\t\t\tconst allIds = kind.items.map((item) => item.id);\n\t\t\tconst encoded = encodeResourceSelectionWithFraming(enabledSet, allIds, framing);\n\t\t\tif (encoded !== undefined) {\n\t\t\t\tresources[kind.kind] = encoded;\n\t\t\t}\n\t\t}\n\t\tthis.onSave(resources);\n\t\tthis.isDirty = false;\n\t\tthis.footerText.setText(this.getFooterText());\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"profile-resource-editor.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/profile-resource-editor.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { isAbsolute, resolve } from \"node:path\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tKey,\n\tmatchesKey,\n\tSpacer,\n\tText,\n} from \"@caupulican/pi-tui\";\nimport {\n\tdecodeResourceSelection,\n\tdetectResourceFraming,\n\tencodeResourceSelectionWithFraming,\n\ttype ResourceFraming,\n} from \"../../../core/profile-resource-selection.ts\";\nimport type { ResourceProfileKind, ResourceProfileSettings } from \"../../../core/settings-manager.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\n\nexport interface ProfileResourceItem {\n\tid: string;\n\tpath?: string;\n\tdescription?: string;\n}\n\nexport interface ProfileResourceEditorKind {\n\tkind: ResourceProfileKind; // \"tools\" | \"skills\" | \"extensions\" | \"agents\" | \"prompts\" | \"themes\"\n\tlabel: string; // display label, e.g. \"Tools\"\n\titems: ProfileResourceItem[]; // the available items of this kind\n}\n\nexport interface ProfileResourceEditorOptions {\n\tprofileName: string;\n\tprofileScope: string; // e.g. \"directory\" | \"project\" | \"global\" | \"session\" | \"reusable-file\"\n\tinitialResources: ResourceProfileSettings; // existing profile.resources; may be {}\n\tkinds: ProfileResourceEditorKind[]; // the six kinds, with their universes\n\tonSave: (resources: ResourceProfileSettings) => void; // called on ctrl+s with the encoded result\n\tonCancel: () => void; // called on esc\n\tonScopeChange?: () => void;\n\tonEdit?: (id: string, path: string, kind: ResourceProfileKind) => void;\n\tcwd?: string;\n\tagentDir?: string;\n\texternalResourceRoots?: string[];\n}\n\ninterface ResourceItem {\n\tid: string;\n\tenabled: boolean;\n\tpath?: string;\n\tdescription?: string;\n\tsourceLabel?: \"catalog\" | \"user\" | \"project\" | \"bundled\";\n\tisMissing?: boolean;\n}\n\nconst TOOL_DESCRIPTIONS: Record<string, string> = {\n\tread: \"Read files from the filesystem.\",\n\tbash: \"Execute arbitrary commands in the shell.\",\n\tedit: \"Edit files surgically.\",\n\twrite: \"Create new files or overwrite existing files.\",\n\tgrep: \"Search for text patterns using ripgrep.\",\n\tfind: \"Locate files matching a search query.\",\n\tls: \"List files and directories.\",\n\tskill_audit: \"Inspect and audit local skills.\",\n\tskillify: \"Convert a set of files/instructions into a skill.\",\n\textensionify: \"Package a tool or script as an extension.\",\n};\n\nexport function resolveResourceEditPath(\n\t_id: string,\n\tfilePath: string | undefined,\n\tkind: ResourceProfileKind,\n): string | undefined {\n\tif (kind === \"tools\") {\n\t\treturn undefined;\n\t}\n\n\tif (!filePath) {\n\t\treturn undefined;\n\t}\n\n\tif (kind === \"extensions\") {\n\t\ttry {\n\t\t\tconst stats = fs.statSync(filePath);\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\tconst indexTs = path.join(filePath, \"index.ts\");\n\t\t\t\tif (fs.existsSync(indexTs)) {\n\t\t\t\t\treturn indexTs;\n\t\t\t\t}\n\t\t\t\tconst indexJs = path.join(filePath, \"index.js\");\n\t\t\t\tif (fs.existsSync(indexJs)) {\n\t\t\t\t\treturn indexJs;\n\t\t\t\t}\n\t\t\t\tconst pkgJsonPath = path.join(filePath, \"package.json\");\n\t\t\t\tif (fs.existsSync(pkgJsonPath)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst pkg = JSON.parse(fs.readFileSync(pkgJsonPath, \"utf-8\"));\n\t\t\t\t\t\tif (typeof pkg.main === \"string\") {\n\t\t\t\t\t\t\tconst resolvedMain = path.resolve(filePath, pkg.main);\n\t\t\t\t\t\t\tif (fs.existsSync(resolvedMain)) {\n\t\t\t\t\t\t\t\treturn resolvedMain;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t}\n\t\t\t\treturn indexTs;\n\t\t\t}\n\t\t} catch {}\n\t\treturn filePath;\n\t}\n\n\tif (kind === \"skills\") {\n\t\ttry {\n\t\t\tconst stats = fs.statSync(filePath);\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\tconst skillMd = path.join(filePath, \"SKILL.md\");\n\t\t\t\tif (fs.existsSync(skillMd)) {\n\t\t\t\t\treturn skillMd;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t\treturn filePath;\n\t}\n\n\treturn filePath;\n}\n\nexport function classifyResourceSource(\n\tfilePath: string | undefined,\n\tcwd: string,\n\tagentDir: string,\n\texternalRoots: string[],\n): \"catalog\" | \"user\" | \"project\" | \"bundled\" {\n\tif (!filePath) {\n\t\treturn \"bundled\";\n\t}\n\n\tconst absolutePath = isAbsolute(filePath) ? filePath : resolve(cwd, filePath);\n\tconst normPath = resolve(absolutePath);\n\tconst normCwd = resolve(cwd);\n\tconst normAgentDir = agentDir ? resolve(agentDir) : \"\";\n\n\tfor (const extRoot of externalRoots) {\n\t\tconst normExt = resolve(extRoot);\n\t\tif (normPath.startsWith(normExt)) {\n\t\t\treturn \"catalog\";\n\t\t}\n\t}\n\n\tif (normAgentDir && normPath.startsWith(normAgentDir)) {\n\t\treturn \"user\";\n\t}\n\n\tif (normPath.startsWith(normCwd)) {\n\t\treturn \"project\";\n\t}\n\n\tif (normPath.includes(\"node_modules\")) {\n\t\treturn \"bundled\";\n\t}\n\n\treturn \"bundled\";\n}\n\nexport class ProfileResourceEditorComponent extends Container implements Focusable {\n\tprivate profileName: string;\n\tprivate profileScope: string;\n\tprivate kinds: ProfileResourceEditorKind[];\n\tprivate enabledByKind: Map<ResourceProfileKind, Set<string>> = new Map();\n\tprivate framingByKind: Map<ResourceProfileKind, ResourceFraming> = new Map();\n\tprivate missingIdsByKind: Map<ResourceProfileKind, Set<string>> = new Map();\n\tprivate currentKindIndex = 0;\n\n\tprivate cwd: string;\n\tprivate agentDir: string;\n\tprivate externalResourceRoots: string[];\n\n\tprivate filteredItems: ResourceItem[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\tprivate kindHeaderText: Text;\n\tprivate listContainer: Container;\n\tprivate descriptionText: Text;\n\tprivate footerText: Text;\n\tprivate isDirty = false;\n\tprivate maxVisible = 8;\n\n\tprivate _focused = false;\n\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\tprivate onSave: (resources: ResourceProfileSettings) => void;\n\tprivate onCancel: () => void;\n\tprivate onScopeChange?: () => void;\n\tprivate onEdit?: (id: string, path: string, kind: ResourceProfileKind) => void;\n\n\tconstructor(options: ProfileResourceEditorOptions) {\n\t\tsuper();\n\t\tthis.profileName = options.profileName;\n\t\tthis.profileScope = options.profileScope;\n\t\tthis.kinds = options.kinds;\n\t\tthis.onSave = options.onSave;\n\t\tthis.onCancel = options.onCancel;\n\t\tthis.onScopeChange = options.onScopeChange;\n\t\tthis.onEdit = options.onEdit;\n\t\tthis.cwd = options.cwd || process.cwd();\n\t\tthis.agentDir = options.agentDir || \"\";\n\t\tthis.externalResourceRoots = options.externalResourceRoots || [];\n\n\t\t// Initialize enabled, framing, and missing sets for each kind\n\t\tfor (const kind of this.kinds) {\n\t\t\tconst filter = options.initialResources[kind.kind];\n\t\t\tconst allIds = kind.items.map((item) => item.id);\n\t\t\tconst enabledSet = decodeResourceSelection(filter, allIds);\n\t\t\tthis.enabledByKind.set(kind.kind, enabledSet);\n\t\t\tthis.framingByKind.set(kind.kind, detectResourceFraming(filter));\n\n\t\t\t// Compute missing items\n\t\t\tconst mentionedIds = new Set<string>();\n\t\t\tif (filter) {\n\t\t\t\tif (filter.allow) {\n\t\t\t\t\tfor (const id of filter.allow) {\n\t\t\t\t\t\tmentionedIds.add(id);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (filter.block) {\n\t\t\t\t\tfor (const id of filter.block) {\n\t\t\t\t\t\tif (id !== \"*\") {\n\t\t\t\t\t\t\tmentionedIds.add(id);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst availableIds = new Set(allIds);\n\t\t\tconst missingSet = new Set<string>();\n\t\t\tfor (const id of mentionedIds) {\n\t\t\t\tif (!availableIds.has(id)) {\n\t\t\t\t\tmissingSet.add(id);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.missingIdsByKind.set(kind.kind, missingSet);\n\t\t}\n\n\t\t// Header\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"accent\", theme.bold(`Library — editing \"${this.profileName}\" (${this.profileScope})`)),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\n\t\t\t\t\t\"muted\",\n\t\t\t\t\t`Navigate kinds: ${keyText(\"tui.input.tab\")}. Toggle: ${keyText(\"tui.select.confirm\")}. Mode: ${theme.fg(\"accent\", \"a\")} allow / ${theme.fg(\"accent\", \"b\")} block. Scope: ${theme.fg(\"accent\", \"s\")} change. Edit: ${theme.fg(\"accent\", \"e\")}.`,\n\t\t\t\t),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Kind selector header\n\t\tthis.kindHeaderText = new Text(this.getKindHeaderText(), 0, 0);\n\t\tthis.addChild(this.kindHeaderText);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Search input\n\t\tthis.searchInput = new Input();\n\t\tthis.addChild(this.searchInput);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// List container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\t// Description area\n\t\tthis.descriptionText = new Text(\"\", 0, 0);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(this.descriptionText);\n\n\t\t// Footer hint\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.footerText = new Text(this.getFooterText(), 0, 0);\n\t\tthis.addChild(this.footerText);\n\n\t\tthis.addChild(new DynamicBorder());\n\n\t\tthis.refresh();\n\t}\n\n\tprivate getKindHeaderText(): string {\n\t\tconst kind = this.kinds[this.currentKindIndex]!;\n\t\tconst enabledSet = this.enabledByKind.get(kind.kind)!;\n\t\tconst framing = this.framingByKind.get(kind.kind)!;\n\t\tconst framingText = framing === \"allow\" ? \"allow-list\" : \"block-list\";\n\t\tconst countText = `${enabledSet.size}/${kind.items.length} enabled (${framingText})`;\n\t\tconst kindIndicator = this.kinds\n\t\t\t.map((k, i) => {\n\t\t\t\tconst marker = i === this.currentKindIndex ? \"●\" : \"○\";\n\t\t\t\treturn theme.fg(i === this.currentKindIndex ? \"accent\" : \"muted\", `${marker} ${k.label}`);\n\t\t\t})\n\t\t\t.join(\" \");\n\t\treturn `${kindIndicator} ${theme.fg(\"muted\", countText)}`;\n\t}\n\n\tprivate getCurrentKind(): ProfileResourceEditorKind {\n\t\treturn this.kinds[this.currentKindIndex]!;\n\t}\n\n\tprivate getCurrentEnabledSet(): Set<string> {\n\t\tconst kind = this.getCurrentKind();\n\t\treturn this.enabledByKind.get(kind.kind)!;\n\t}\n\n\tprivate buildItems(): ResourceItem[] {\n\t\tconst kind = this.getCurrentKind();\n\t\tconst enabledSet = this.getCurrentEnabledSet();\n\n\t\tconst items: ResourceItem[] = [];\n\n\t\t// Add all available items\n\t\tfor (const availableItem of kind.items) {\n\t\t\tconst isEnabled = enabledSet.has(availableItem.id);\n\t\t\tlet desc = availableItem.description;\n\t\t\tif (kind.kind === \"tools\" && !desc) {\n\t\t\t\tdesc = TOOL_DESCRIPTIONS[availableItem.id];\n\t\t\t}\n\t\t\titems.push({\n\t\t\t\tid: availableItem.id,\n\t\t\t\tenabled: isEnabled,\n\t\t\t\tpath: availableItem.path,\n\t\t\t\tdescription: desc,\n\t\t\t\tsourceLabel: classifyResourceSource(\n\t\t\t\t\tavailableItem.path,\n\t\t\t\t\tthis.cwd,\n\t\t\t\t\tthis.agentDir,\n\t\t\t\t\tthis.externalResourceRoots,\n\t\t\t\t),\n\t\t\t\tisMissing: false,\n\t\t\t});\n\t\t}\n\n\t\t// Add missing items\n\t\tconst missingSet = this.missingIdsByKind.get(kind.kind) || new Set<string>();\n\t\tfor (const missingId of missingSet) {\n\t\t\tconst isEnabled = enabledSet.has(missingId);\n\t\t\titems.push({\n\t\t\t\tid: missingId,\n\t\t\t\tenabled: isEnabled,\n\t\t\t\tisMissing: true,\n\t\t\t\tdescription: \"Referenced in profile but missing from available resources.\",\n\t\t\t});\n\t\t}\n\n\t\t// Sort: enabled first, then disabled\n\t\tconst enabled: ResourceItem[] = [];\n\t\tconst disabled: ResourceItem[] = [];\n\t\tfor (const item of items) {\n\t\t\tif (item.enabled) {\n\t\t\t\tenabled.push(item);\n\t\t\t} else {\n\t\t\t\tdisabled.push(item);\n\t\t\t}\n\t\t}\n\t\treturn [...enabled, ...disabled];\n\t}\n\n\tprivate getFooterText(): string {\n\t\tconst kind = this.getCurrentKind();\n\t\tconst enabledSet = this.getCurrentEnabledSet();\n\t\tconst countText = `${enabledSet.size}/${kind.items.length} enabled`;\n\t\tconst parts = [\n\t\t\t`${keyText(\"tui.select.confirm\")} toggle`,\n\t\t\t`${keyText(\"tui.input.tab\")} kind`,\n\t\t\t`${keyText(\"app.models.save\")} save`,\n\t\t\t`${theme.fg(\"accent\", \"e\")} edit`,\n\t\t\tcountText,\n\t\t];\n\t\treturn this.isDirty\n\t\t\t? theme.fg(\"dim\", ` ${parts.join(\" · \")} `) + theme.fg(\"warning\", \"(unsaved)\")\n\t\t\t: theme.fg(\"dim\", ` ${parts.join(\" · \")}`);\n\t}\n\n\tprivate refresh(): void {\n\t\tconst query = this.searchInput.getValue();\n\t\tconst items = this.buildItems();\n\t\tthis.filteredItems = query ? fuzzyFilter(items, query, (i) => `${i.id} ${i.description ?? \"\"}`) : items;\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredItems.length - 1));\n\t\tthis.updateList();\n\t\tthis.footerText.setText(this.getFooterText());\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tconst kind = this.getCurrentKind();\n\t\tconst missingSet = this.missingIdsByKind.get(kind.kind) || new Set<string>();\n\t\tconst totalAvailableCount = kind.items.length + missingSet.size;\n\n\t\tif (totalAvailableCount === 0) {\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(\"muted\", \" (none available — add a Source or install resources)\"), 0, 0),\n\t\t\t);\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", \" No description available\"));\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.filteredItems.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching resources\"), 0, 0));\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", \" No description available\"));\n\t\t\treturn;\n\t\t}\n\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 item = this.filteredItems[i]!;\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst prefix = isSelected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tlet resourceText = isSelected ? theme.fg(\"accent\", item.id) : item.id;\n\n\t\t\tif (item.isMissing) {\n\t\t\t\tresourceText = theme.fg(\"muted\", `${item.id} [missing]`);\n\t\t\t} else if (item.sourceLabel) {\n\t\t\t\tconst labelColor =\n\t\t\t\t\titem.sourceLabel === \"catalog\"\n\t\t\t\t\t\t? \"thinkingText\"\n\t\t\t\t\t\t: item.sourceLabel === \"project\"\n\t\t\t\t\t\t\t? \"success\"\n\t\t\t\t\t\t\t: item.sourceLabel === \"user\"\n\t\t\t\t\t\t\t\t? \"warning\"\n\t\t\t\t\t\t\t\t: \"muted\";\n\t\t\t\tconst labelText = theme.fg(labelColor, ` [${item.sourceLabel}]`);\n\t\t\t\tresourceText = `${resourceText}${labelText}`;\n\t\t\t}\n\n\t\t\tconst status = item.enabled ? theme.fg(\"success\", \" ✓\") : theme.fg(\"dim\", \" ✗\");\n\t\t\tthis.listContainer.addChild(new Text(`${prefix}${resourceText}${status}`, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredItems.length) {\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`), 0, 0),\n\t\t\t);\n\t\t}\n\n\t\t// Update description area for current selection\n\t\tconst selectedItem = this.filteredItems[this.selectedIndex];\n\t\tif (selectedItem?.description) {\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", ` Description: ${selectedItem.description}`));\n\t\t} else {\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", \" No description available\"));\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\n\t\t// Navigation within list\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredItems.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.down\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredItems.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch kind with Tab (cycles forward)\n\t\tif (kb.matches(data, \"tui.input.tab\")) {\n\t\t\tthis.currentKindIndex = (this.currentKindIndex + 1) % this.kinds.length;\n\t\t\tthis.selectedIndex = 0;\n\t\t\tthis.searchInput.setValue(\"\");\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch framing: Allow-list\n\t\tif (matchesKey(data, Key.ctrl(\"a\")) || (this.searchInput.getValue() === \"\" && data === \"a\")) {\n\t\t\tthis.framingByKind.set(this.getCurrentKind().kind, \"allow\");\n\t\t\tthis.isDirty = true;\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch framing: Block-list\n\t\tif (matchesKey(data, Key.ctrl(\"b\")) || (this.searchInput.getValue() === \"\" && data === \"b\")) {\n\t\t\tthis.framingByKind.set(this.getCurrentKind().kind, \"block\");\n\t\t\tthis.isDirty = true;\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Edit highlighted resource\n\t\tif (data === \"e\" && this.searchInput.getValue() === \"\") {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item && !item.isMissing && item.path) {\n\t\t\t\tconst kind = this.getCurrentKind().kind;\n\t\t\t\tif (this.onEdit) {\n\t\t\t\t\tthis.onEdit(item.id, item.path, kind);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch scope: Scope selector\n\t\tif (matchesKey(data, Key.ctrl(\"o\")) || (this.searchInput.getValue() === \"\" && data === \"s\")) {\n\t\t\tif (this.onScopeChange) {\n\t\t\t\tthis.onScopeChange();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Toggle on space/enter\n\t\tif (kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item) {\n\t\t\t\tconst enabledSet = this.getCurrentEnabledSet();\n\t\t\t\tif (enabledSet.has(item.id)) {\n\t\t\t\t\tenabledSet.delete(item.id);\n\t\t\t\t} else {\n\t\t\t\t\tenabledSet.add(item.id);\n\t\t\t\t}\n\t\t\t\tthis.isDirty = true;\n\t\t\t\tthis.refresh();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Save/persist to settings\n\t\tif (kb.matches(data, \"app.models.save\")) {\n\t\t\tthis.persistChanges();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+C - clear search or cancel if empty\n\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\tif (this.searchInput.getValue()) {\n\t\t\t\tthis.searchInput.setValue(\"\");\n\t\t\t\tthis.refresh();\n\t\t\t} else {\n\t\t\t\tthis.onCancel();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Escape - cancel\n\t\tif (matchesKey(data, Key.escape)) {\n\t\t\tthis.onCancel();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass everything else to search input\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.refresh();\n\t}\n\n\tprivate persistChanges(): void {\n\t\tconst resources: ResourceProfileSettings = {};\n\t\tfor (const kind of this.kinds) {\n\t\t\tconst enabledSet = this.enabledByKind.get(kind.kind)!;\n\t\t\tconst framing = this.framingByKind.get(kind.kind)!;\n\t\t\tconst allIds = kind.items.map((item) => item.id);\n\t\t\tconst encoded = encodeResourceSelectionWithFraming(enabledSet, allIds, framing);\n\t\t\tif (encoded !== undefined) {\n\t\t\t\tresources[kind.kind] = encoded;\n\t\t\t}\n\t\t}\n\t\tthis.onSave(resources);\n\t\tthis.isDirty = false;\n\t\tthis.footerText.setText(this.getFooterText());\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
1
3
|
import { isAbsolute, resolve } from "node:path";
|
|
2
4
|
import { Container, fuzzyFilter, getKeybindings, Input, Key, matchesKey, Spacer, Text, } from "@caupulican/pi-tui";
|
|
3
5
|
import { decodeResourceSelection, detectResourceFraming, encodeResourceSelectionWithFraming, } from "../../../core/profile-resource-selection.js";
|
|
@@ -16,6 +18,59 @@ const TOOL_DESCRIPTIONS = {
|
|
|
16
18
|
skillify: "Convert a set of files/instructions into a skill.",
|
|
17
19
|
extensionify: "Package a tool or script as an extension.",
|
|
18
20
|
};
|
|
21
|
+
export function resolveResourceEditPath(_id, filePath, kind) {
|
|
22
|
+
if (kind === "tools") {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
if (!filePath) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
if (kind === "extensions") {
|
|
29
|
+
try {
|
|
30
|
+
const stats = fs.statSync(filePath);
|
|
31
|
+
if (stats.isDirectory()) {
|
|
32
|
+
const indexTs = path.join(filePath, "index.ts");
|
|
33
|
+
if (fs.existsSync(indexTs)) {
|
|
34
|
+
return indexTs;
|
|
35
|
+
}
|
|
36
|
+
const indexJs = path.join(filePath, "index.js");
|
|
37
|
+
if (fs.existsSync(indexJs)) {
|
|
38
|
+
return indexJs;
|
|
39
|
+
}
|
|
40
|
+
const pkgJsonPath = path.join(filePath, "package.json");
|
|
41
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
42
|
+
try {
|
|
43
|
+
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
|
|
44
|
+
if (typeof pkg.main === "string") {
|
|
45
|
+
const resolvedMain = path.resolve(filePath, pkg.main);
|
|
46
|
+
if (fs.existsSync(resolvedMain)) {
|
|
47
|
+
return resolvedMain;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch { }
|
|
52
|
+
}
|
|
53
|
+
return indexTs;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch { }
|
|
57
|
+
return filePath;
|
|
58
|
+
}
|
|
59
|
+
if (kind === "skills") {
|
|
60
|
+
try {
|
|
61
|
+
const stats = fs.statSync(filePath);
|
|
62
|
+
if (stats.isDirectory()) {
|
|
63
|
+
const skillMd = path.join(filePath, "SKILL.md");
|
|
64
|
+
if (fs.existsSync(skillMd)) {
|
|
65
|
+
return skillMd;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch { }
|
|
70
|
+
return filePath;
|
|
71
|
+
}
|
|
72
|
+
return filePath;
|
|
73
|
+
}
|
|
19
74
|
export function classifyResourceSource(filePath, cwd, agentDir, externalRoots) {
|
|
20
75
|
if (!filePath) {
|
|
21
76
|
return "bundled";
|
|
@@ -72,6 +127,7 @@ export class ProfileResourceEditorComponent extends Container {
|
|
|
72
127
|
onSave;
|
|
73
128
|
onCancel;
|
|
74
129
|
onScopeChange;
|
|
130
|
+
onEdit;
|
|
75
131
|
constructor(options) {
|
|
76
132
|
super();
|
|
77
133
|
this.profileName = options.profileName;
|
|
@@ -80,6 +136,7 @@ export class ProfileResourceEditorComponent extends Container {
|
|
|
80
136
|
this.onSave = options.onSave;
|
|
81
137
|
this.onCancel = options.onCancel;
|
|
82
138
|
this.onScopeChange = options.onScopeChange;
|
|
139
|
+
this.onEdit = options.onEdit;
|
|
83
140
|
this.cwd = options.cwd || process.cwd();
|
|
84
141
|
this.agentDir = options.agentDir || "";
|
|
85
142
|
this.externalResourceRoots = options.externalResourceRoots || [];
|
|
@@ -118,7 +175,7 @@ export class ProfileResourceEditorComponent extends Container {
|
|
|
118
175
|
// Header
|
|
119
176
|
this.addChild(new DynamicBorder());
|
|
120
177
|
this.addChild(new Text(theme.fg("accent", theme.bold(`Library — editing "${this.profileName}" (${this.profileScope})`)), 0, 0));
|
|
121
|
-
this.addChild(new Text(theme.fg("muted", `Navigate kinds: ${keyText("tui.input.tab")}. Toggle: ${keyText("tui.select.confirm")}. Mode: ${theme.fg("accent", "a")} allow / ${theme.fg("accent", "b")} block. Scope: ${theme.fg("accent", "s")} change.`), 0, 0));
|
|
178
|
+
this.addChild(new Text(theme.fg("muted", `Navigate kinds: ${keyText("tui.input.tab")}. Toggle: ${keyText("tui.select.confirm")}. Mode: ${theme.fg("accent", "a")} allow / ${theme.fg("accent", "b")} block. Scope: ${theme.fg("accent", "s")} change. Edit: ${theme.fg("accent", "e")}.`), 0, 0));
|
|
122
179
|
this.addChild(new Spacer(1));
|
|
123
180
|
// Kind selector header
|
|
124
181
|
this.kindHeaderText = new Text(this.getKindHeaderText(), 0, 0);
|
|
@@ -215,6 +272,7 @@ export class ProfileResourceEditorComponent extends Container {
|
|
|
215
272
|
`${keyText("tui.select.confirm")} toggle`,
|
|
216
273
|
`${keyText("tui.input.tab")} kind`,
|
|
217
274
|
`${keyText("app.models.save")} save`,
|
|
275
|
+
`${theme.fg("accent", "e")} edit`,
|
|
218
276
|
countText,
|
|
219
277
|
];
|
|
220
278
|
return this.isDirty
|
|
@@ -231,6 +289,14 @@ export class ProfileResourceEditorComponent extends Container {
|
|
|
231
289
|
}
|
|
232
290
|
updateList() {
|
|
233
291
|
this.listContainer.clear();
|
|
292
|
+
const kind = this.getCurrentKind();
|
|
293
|
+
const missingSet = this.missingIdsByKind.get(kind.kind) || new Set();
|
|
294
|
+
const totalAvailableCount = kind.items.length + missingSet.size;
|
|
295
|
+
if (totalAvailableCount === 0) {
|
|
296
|
+
this.listContainer.addChild(new Text(theme.fg("muted", " (none available — add a Source or install resources)"), 0, 0));
|
|
297
|
+
this.descriptionText.setText(theme.fg("muted", " No description available"));
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
234
300
|
if (this.filteredItems.length === 0) {
|
|
235
301
|
this.listContainer.addChild(new Text(theme.fg("muted", " No matching resources"), 0, 0));
|
|
236
302
|
this.descriptionText.setText(theme.fg("muted", " No description available"));
|
|
@@ -315,6 +381,17 @@ export class ProfileResourceEditorComponent extends Container {
|
|
|
315
381
|
this.refresh();
|
|
316
382
|
return;
|
|
317
383
|
}
|
|
384
|
+
// Edit highlighted resource
|
|
385
|
+
if (data === "e" && this.searchInput.getValue() === "") {
|
|
386
|
+
const item = this.filteredItems[this.selectedIndex];
|
|
387
|
+
if (item && !item.isMissing && item.path) {
|
|
388
|
+
const kind = this.getCurrentKind().kind;
|
|
389
|
+
if (this.onEdit) {
|
|
390
|
+
this.onEdit(item.id, item.path, kind);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
318
395
|
// Switch scope: Scope selector
|
|
319
396
|
if (matchesKey(data, Key.ctrl("o")) || (this.searchInput.getValue() === "" && data === "s")) {
|
|
320
397
|
if (this.onScopeChange) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profile-resource-editor.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/profile-resource-editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EACN,SAAS,EAET,WAAW,EACX,cAAc,EACd,KAAK,EACL,GAAG,EACH,UAAU,EACV,MAAM,EACN,IAAI,GACJ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACN,uBAAuB,EACvB,qBAAqB,EACrB,kCAAkC,GAElC,MAAM,6CAA6C,CAAC;AAErD,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAoChD,MAAM,iBAAiB,GAA2B;IACjD,IAAI,EAAE,iCAAiC;IACvC,IAAI,EAAE,0CAA0C;IAChD,IAAI,EAAE,wBAAwB;IAC9B,KAAK,EAAE,+CAA+C;IACtD,IAAI,EAAE,yCAAyC;IAC/C,IAAI,EAAE,uCAAuC;IAC7C,EAAE,EAAE,6BAA6B;IACjC,WAAW,EAAE,iCAAiC;IAC9C,QAAQ,EAAE,mDAAmD;IAC7D,YAAY,EAAE,2CAA2C;CACzD,CAAC;AAEF,MAAM,UAAU,sBAAsB,CACrC,QAA4B,EAC5B,GAAW,EACX,QAAgB,EAChB,aAAuB,EACsB;IAC7C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACf,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,MAAM,YAAY,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC9E,MAAM,QAAQ,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7B,MAAM,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEvD,KAAK,MAAM,OAAO,IAAI,aAAa,EAAE,CAAC;QACrC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAClC,OAAO,SAAS,CAAC;QAClB,CAAC;IACF,CAAC;IAED,IAAI,YAAY,IAAI,QAAQ,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACvD,OAAO,MAAM,CAAC;IACf,CAAC;IAED,IAAI,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAClC,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,IAAI,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;QACvC,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,OAAO,SAAS,CAAC;AAAA,CACjB;AAED,MAAM,OAAO,8BAA+B,SAAQ,SAAS;IACpD,WAAW,CAAS;IACpB,YAAY,CAAS;IACrB,KAAK,CAA8B;IACnC,aAAa,GAA0C,IAAI,GAAG,EAAE,CAAC;IACjE,aAAa,GAA8C,IAAI,GAAG,EAAE,CAAC;IACrE,gBAAgB,GAA0C,IAAI,GAAG,EAAE,CAAC;IACpE,gBAAgB,GAAG,CAAC,CAAC;IAErB,GAAG,CAAS;IACZ,QAAQ,CAAS;IACjB,qBAAqB,CAAW;IAEhC,aAAa,GAAmB,EAAE,CAAC;IACnC,aAAa,GAAG,CAAC,CAAC;IAClB,WAAW,CAAQ;IACnB,cAAc,CAAO;IACrB,aAAa,CAAY;IACzB,eAAe,CAAO;IACtB,UAAU,CAAO;IACjB,OAAO,GAAG,KAAK,CAAC;IAChB,UAAU,GAAG,CAAC,CAAC;IAEf,QAAQ,GAAG,KAAK,CAAC;IAEzB,IAAI,OAAO,GAAY;QACtB,OAAO,IAAI,CAAC,QAAQ,CAAC;IAAA,CACrB;IACD,IAAI,OAAO,CAAC,KAAc,EAAE;QAC3B,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACtB,IAAI,CAAC,WAAW,CAAC,OAAO,GAAG,KAAK,CAAC;IAAA,CACjC;IAEO,MAAM,CAA+C;IACrD,QAAQ,CAAa;IACrB,aAAa,CAAc;IAEnC,YAAY,OAAqC,EAAE;QAClD,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QACvC,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;QACzC,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;QAC3C,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QACxC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;QACvC,IAAI,CAAC,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,IAAI,EAAE,CAAC;QAEjE,8DAA8D;QAC9D,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC/B,MAAM,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACjD,MAAM,UAAU,GAAG,uBAAuB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAC3D,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YAC9C,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC;YAEjE,wBAAwB;YACxB,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;YACvC,IAAI,MAAM,EAAE,CAAC;gBACZ,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;oBAClB,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;wBAC/B,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;oBACtB,CAAC;gBACF,CAAC;gBACD,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;oBAClB,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;wBAC/B,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;4BAChB,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;wBACtB,CAAC;oBACF,CAAC;gBACF,CAAC;YACF,CAAC;YACD,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;YACrC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;YACrC,KAAK,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC;gBAC/B,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;oBAC3B,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACpB,CAAC;YACF,CAAC;YACD,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QAClD,CAAC;QAED,SAAS;QACT,IAAI,CAAC,QAAQ,CAAC,IAAI,aAAa,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,QAAQ,CACZ,IAAI,IAAI,CACP,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,wBAAsB,IAAI,CAAC,WAAW,MAAM,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC,EAChG,CAAC,EACD,CAAC,CACD,CACD,CAAC;QACF,IAAI,CAAC,QAAQ,CACZ,IAAI,IAAI,CACP,KAAK,CAAC,EAAE,CACP,OAAO,EACP,mBAAmB,OAAO,CAAC,eAAe,CAAC,aAAa,OAAO,CAAC,oBAAoB,CAAC,WAAW,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC,YAAY,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC,kBAAkB,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC,UAAU,CAC7M,EACD,CAAC,EACD,CAAC,CACD,CACD,CAAC;QACF,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAE7B,uBAAuB;QACvB,IAAI,CAAC,cAAc,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC/D,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACnC,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAE7B,eAAe;QACf,IAAI,CAAC,WAAW,GAAG,IAAI,KAAK,EAAE,CAAC;QAC/B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAChC,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAE7B,iBAAiB;QACjB,IAAI,CAAC,aAAa,GAAG,IAAI,SAAS,EAAE,CAAC;QACrC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAElC,mBAAmB;QACnB,IAAI,CAAC,eAAe,GAAG,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1C,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAEpC,cAAc;QACd,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QACvD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAE/B,IAAI,CAAC,QAAQ,CAAC,IAAI,aAAa,EAAE,CAAC,CAAC;QAEnC,IAAI,CAAC,OAAO,EAAE,CAAC;IAAA,CACf;IAEO,iBAAiB,GAAW;QACnC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAE,CAAC;QAChD,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAE,CAAC;QACtD,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAE,CAAC;QACnD,MAAM,WAAW,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC;QACtE,MAAM,SAAS,GAAG,GAAG,UAAU,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,aAAa,WAAW,GAAG,CAAC;QACrF,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK;aAC9B,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YACd,MAAM,MAAM,GAAG,CAAC,KAAK,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAG,CAAC,CAAC,CAAC,KAAG,CAAC;YACvD,OAAO,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QAAA,CAC1F,CAAC;aACD,IAAI,CAAC,GAAG,CAAC,CAAC;QACZ,OAAO,GAAG,aAAa,KAAK,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;IAAA,CAC3D;IAEO,cAAc,GAA8B;QACnD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAE,CAAC;IAAA,CAC1C;IAEO,oBAAoB,GAAgB;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAE,CAAC;IAAA,CAC1C;IAEO,UAAU,GAAmB;QACpC,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;QACnC,MAAM,UAAU,GAAG,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAE/C,MAAM,KAAK,GAAmB,EAAE,CAAC;QAEjC,0BAA0B;QAC1B,KAAK,MAAM,aAAa,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACxC,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;YACnD,IAAI,IAAI,GAAG,aAAa,CAAC,WAAW,CAAC;YACrC,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;gBACpC,IAAI,GAAG,iBAAiB,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;YAC5C,CAAC;YACD,KAAK,CAAC,IAAI,CAAC;gBACV,EAAE,EAAE,aAAa,CAAC,EAAE;gBACpB,OAAO,EAAE,SAAS;gBAClB,IAAI,EAAE,aAAa,CAAC,IAAI;gBACxB,WAAW,EAAE,IAAI;gBACjB,WAAW,EAAE,sBAAsB,CAClC,aAAa,CAAC,IAAI,EAClB,IAAI,CAAC,GAAG,EACR,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,qBAAqB,CAC1B;gBACD,SAAS,EAAE,KAAK;aAChB,CAAC,CAAC;QACJ,CAAC;QAED,oBAAoB;QACpB,MAAM,UAAU,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;QAC7E,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACpC,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAC5C,KAAK,CAAC,IAAI,CAAC;gBACV,EAAE,EAAE,SAAS;gBACb,OAAO,EAAE,SAAS;gBAClB,SAAS,EAAE,IAAI;gBACf,WAAW,EAAE,6DAA6D;aAC1E,CAAC,CAAC;QACJ,CAAC;QAED,qCAAqC;QACrC,MAAM,OAAO,GAAmB,EAAE,CAAC;QACnC,MAAM,QAAQ,GAAmB,EAAE,CAAC;QACpC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YAC1B,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBAClB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpB,CAAC;iBAAM,CAAC;gBACP,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrB,CAAC;QACF,CAAC;QACD,OAAO,CAAC,GAAG,OAAO,EAAE,GAAG,QAAQ,CAAC,CAAC;IAAA,CACjC;IAEO,aAAa,GAAW;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;QACnC,MAAM,UAAU,GAAG,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC/C,MAAM,SAAS,GAAG,GAAG,UAAU,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,UAAU,CAAC;QACpE,MAAM,KAAK,GAAG;YACb,GAAG,OAAO,CAAC,oBAAoB,CAAC,SAAS;YACzC,GAAG,OAAO,CAAC,eAAe,CAAC,OAAO;YAClC,GAAG,OAAO,CAAC,iBAAiB,CAAC,OAAO;YACpC,SAAS;SACT,CAAC;QACF,OAAO,IAAI,CAAC,OAAO;YAClB,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,CAAC,IAAI,CAAC,MAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,WAAW,CAAC;YAC/E,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,CAAC,IAAI,CAAC,MAAK,CAAC,EAAE,CAAC,CAAC;IAAA,CAC7C;IAEO,OAAO,GAAS;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QAChC,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QACxG,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;QAC9F,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;IAAA,CAC9C;IAEO,UAAU,GAAS;QAC1B,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;QAE3B,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,yBAAyB,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC1F,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,4BAA4B,CAAC,CAAC,CAAC;YAC9E,OAAO;QACR,CAAC;QAED,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAC1B,CAAC,EACD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,CAC3G,CAAC;QACF,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QAEnF,KAAK,IAAI,CAAC,GAAG,UAAU,EAAE,CAAC,GAAG,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAE,CAAC;YACpC,MAAM,UAAU,GAAG,CAAC,KAAK,IAAI,CAAC,aAAa,CAAC;YAC5C,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC5D,IAAI,YAAY,GAAG,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YAEtE,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACpB,YAAY,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,EAAE,YAAY,CAAC,CAAC;YAC1D,CAAC;iBAAM,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBAC7B,MAAM,UAAU,GACf,IAAI,CAAC,WAAW,KAAK,SAAS;oBAC7B,CAAC,CAAC,cAAc;oBAChB,CAAC,CAAC,IAAI,CAAC,WAAW,KAAK,SAAS;wBAC/B,CAAC,CAAC,SAAS;wBACX,CAAC,CAAC,IAAI,CAAC,WAAW,KAAK,MAAM;4BAC5B,CAAC,CAAC,SAAS;4BACX,CAAC,CAAC,OAAO,CAAC;gBACd,MAAM,SAAS,GAAG,KAAK,CAAC,EAAE,CAAC,UAAU,EAAE,KAAK,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;gBACjE,YAAY,GAAG,GAAG,YAAY,GAAG,SAAS,EAAE,CAAC;YAC9C,CAAC;YAED,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,MAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAI,CAAC,CAAC;YAChF,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,GAAG,MAAM,GAAG,YAAY,GAAG,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAClF,CAAC;QAED,iCAAiC;QACjC,IAAI,UAAU,GAAG,CAAC,IAAI,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC;YAC5D,IAAI,CAAC,aAAa,CAAC,QAAQ,CAC1B,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC,aAAa,GAAG,CAAC,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAC/F,CAAC;QACH,CAAC;QAED,gDAAgD;QAChD,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC5D,IAAI,YAAY,EAAE,WAAW,EAAE,CAAC;YAC/B,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,YAAY,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QAC/F,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,4BAA4B,CAAC,CAAC,CAAC;QAC/E,CAAC;IAAA,CACD;IAED,WAAW,CAAC,IAAY,EAAQ;QAC/B,MAAM,EAAE,GAAG,cAAc,EAAE,CAAC;QAE5B,yBAAyB;QACzB,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,eAAe,CAAC,EAAE,CAAC;YACvC,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO;YAC5C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC;YACvG,IAAI,CAAC,UAAU,EAAE,CAAC;YAClB,OAAO;QACR,CAAC;QACD,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,iBAAiB,CAAC,EAAE,CAAC;YACzC,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO;YAC5C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,KAAK,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC;YACvG,IAAI,CAAC,UAAU,EAAE,CAAC;YAClB,OAAO;QACR,CAAC;QAED,wCAAwC;QACxC,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,eAAe,CAAC,EAAE,CAAC;YACvC,IAAI,CAAC,gBAAgB,GAAG,CAAC,IAAI,CAAC,gBAAgB,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;YACxE,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC;YACvB,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YAC9B,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC;YACtD,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,OAAO;QACR,CAAC;QAED,6BAA6B;QAC7B,IAAI,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YAC7F,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC5D,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC;YACtD,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,OAAO;QACR,CAAC;QAED,6BAA6B;QAC7B,IAAI,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YAC7F,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC5D,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC;YACtD,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,OAAO;QACR,CAAC;QAED,+BAA+B;QAC/B,IAAI,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YAC7F,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;gBACxB,IAAI,CAAC,aAAa,EAAE,CAAC;YACtB,CAAC;YACD,OAAO;QACR,CAAC;QAED,wBAAwB;QACxB,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,oBAAoB,CAAC,EAAE,CAAC;YAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACpD,IAAI,IAAI,EAAE,CAAC;gBACV,MAAM,UAAU,GAAG,IAAI,CAAC,oBAAoB,EAAE,CAAC;gBAC/C,IAAI,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;oBAC7B,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAC5B,CAAC;qBAAM,CAAC;oBACP,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACzB,CAAC;gBACD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;gBACpB,IAAI,CAAC,OAAO,EAAE,CAAC;YAChB,CAAC;YACD,OAAO;QACR,CAAC;QAED,2BAA2B;QAC3B,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,iBAAiB,CAAC,EAAE,CAAC;YACzC,IAAI,CAAC,cAAc,EAAE,CAAC;YACtB,OAAO;QACR,CAAC;QAED,2CAA2C;QAC3C,IAAI,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACrC,IAAI,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,EAAE,CAAC;gBACjC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;gBAC9B,IAAI,CAAC,OAAO,EAAE,CAAC;YAChB,CAAC;iBAAM,CAAC;gBACP,IAAI,CAAC,QAAQ,EAAE,CAAC;YACjB,CAAC;YACD,OAAO;QACR,CAAC;QAED,kBAAkB;QAClB,IAAI,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAClC,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChB,OAAO;QACR,CAAC;QAED,uCAAuC;QACvC,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,CAAC,OAAO,EAAE,CAAC;IAAA,CACf;IAEO,cAAc,GAAS;QAC9B,MAAM,SAAS,GAA4B,EAAE,CAAC;QAC9C,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC/B,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAE,CAAC;YACtD,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAE,CAAC;YACnD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACjD,MAAM,OAAO,GAAG,kCAAkC,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;YAChF,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC3B,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC;YAChC,CAAC;QACF,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACvB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;IAAA,CAC9C;IAED,cAAc,GAAU;QACvB,OAAO,IAAI,CAAC,WAAW,CAAC;IAAA,CACxB;CACD","sourcesContent":["import { isAbsolute, resolve } from \"node:path\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tKey,\n\tmatchesKey,\n\tSpacer,\n\tText,\n} from \"@caupulican/pi-tui\";\nimport {\n\tdecodeResourceSelection,\n\tdetectResourceFraming,\n\tencodeResourceSelectionWithFraming,\n\ttype ResourceFraming,\n} from \"../../../core/profile-resource-selection.ts\";\nimport type { ResourceProfileKind, ResourceProfileSettings } from \"../../../core/settings-manager.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\n\nexport interface ProfileResourceItem {\n\tid: string;\n\tpath?: string;\n\tdescription?: string;\n}\n\nexport interface ProfileResourceEditorKind {\n\tkind: ResourceProfileKind; // \"tools\" | \"skills\" | \"extensions\" | \"agents\" | \"prompts\" | \"themes\"\n\tlabel: string; // display label, e.g. \"Tools\"\n\titems: ProfileResourceItem[]; // the available items of this kind\n}\n\nexport interface ProfileResourceEditorOptions {\n\tprofileName: string;\n\tprofileScope: string; // e.g. \"directory\" | \"project\" | \"global\" | \"session\" | \"reusable-file\"\n\tinitialResources: ResourceProfileSettings; // existing profile.resources; may be {}\n\tkinds: ProfileResourceEditorKind[]; // the six kinds, with their universes\n\tonSave: (resources: ResourceProfileSettings) => void; // called on ctrl+s with the encoded result\n\tonCancel: () => void; // called on esc\n\tonScopeChange?: () => void;\n\tcwd?: string;\n\tagentDir?: string;\n\texternalResourceRoots?: string[];\n}\n\ninterface ResourceItem {\n\tid: string;\n\tenabled: boolean;\n\tpath?: string;\n\tdescription?: string;\n\tsourceLabel?: \"catalog\" | \"user\" | \"project\" | \"bundled\";\n\tisMissing?: boolean;\n}\n\nconst TOOL_DESCRIPTIONS: Record<string, string> = {\n\tread: \"Read files from the filesystem.\",\n\tbash: \"Execute arbitrary commands in the shell.\",\n\tedit: \"Edit files surgically.\",\n\twrite: \"Create new files or overwrite existing files.\",\n\tgrep: \"Search for text patterns using ripgrep.\",\n\tfind: \"Locate files matching a search query.\",\n\tls: \"List files and directories.\",\n\tskill_audit: \"Inspect and audit local skills.\",\n\tskillify: \"Convert a set of files/instructions into a skill.\",\n\textensionify: \"Package a tool or script as an extension.\",\n};\n\nexport function classifyResourceSource(\n\tfilePath: string | undefined,\n\tcwd: string,\n\tagentDir: string,\n\texternalRoots: string[],\n): \"catalog\" | \"user\" | \"project\" | \"bundled\" {\n\tif (!filePath) {\n\t\treturn \"bundled\";\n\t}\n\n\tconst absolutePath = isAbsolute(filePath) ? filePath : resolve(cwd, filePath);\n\tconst normPath = resolve(absolutePath);\n\tconst normCwd = resolve(cwd);\n\tconst normAgentDir = agentDir ? resolve(agentDir) : \"\";\n\n\tfor (const extRoot of externalRoots) {\n\t\tconst normExt = resolve(extRoot);\n\t\tif (normPath.startsWith(normExt)) {\n\t\t\treturn \"catalog\";\n\t\t}\n\t}\n\n\tif (normAgentDir && normPath.startsWith(normAgentDir)) {\n\t\treturn \"user\";\n\t}\n\n\tif (normPath.startsWith(normCwd)) {\n\t\treturn \"project\";\n\t}\n\n\tif (normPath.includes(\"node_modules\")) {\n\t\treturn \"bundled\";\n\t}\n\n\treturn \"bundled\";\n}\n\nexport class ProfileResourceEditorComponent extends Container implements Focusable {\n\tprivate profileName: string;\n\tprivate profileScope: string;\n\tprivate kinds: ProfileResourceEditorKind[];\n\tprivate enabledByKind: Map<ResourceProfileKind, Set<string>> = new Map();\n\tprivate framingByKind: Map<ResourceProfileKind, ResourceFraming> = new Map();\n\tprivate missingIdsByKind: Map<ResourceProfileKind, Set<string>> = new Map();\n\tprivate currentKindIndex = 0;\n\n\tprivate cwd: string;\n\tprivate agentDir: string;\n\tprivate externalResourceRoots: string[];\n\n\tprivate filteredItems: ResourceItem[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\tprivate kindHeaderText: Text;\n\tprivate listContainer: Container;\n\tprivate descriptionText: Text;\n\tprivate footerText: Text;\n\tprivate isDirty = false;\n\tprivate maxVisible = 8;\n\n\tprivate _focused = false;\n\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\tprivate onSave: (resources: ResourceProfileSettings) => void;\n\tprivate onCancel: () => void;\n\tprivate onScopeChange?: () => void;\n\n\tconstructor(options: ProfileResourceEditorOptions) {\n\t\tsuper();\n\t\tthis.profileName = options.profileName;\n\t\tthis.profileScope = options.profileScope;\n\t\tthis.kinds = options.kinds;\n\t\tthis.onSave = options.onSave;\n\t\tthis.onCancel = options.onCancel;\n\t\tthis.onScopeChange = options.onScopeChange;\n\t\tthis.cwd = options.cwd || process.cwd();\n\t\tthis.agentDir = options.agentDir || \"\";\n\t\tthis.externalResourceRoots = options.externalResourceRoots || [];\n\n\t\t// Initialize enabled, framing, and missing sets for each kind\n\t\tfor (const kind of this.kinds) {\n\t\t\tconst filter = options.initialResources[kind.kind];\n\t\t\tconst allIds = kind.items.map((item) => item.id);\n\t\t\tconst enabledSet = decodeResourceSelection(filter, allIds);\n\t\t\tthis.enabledByKind.set(kind.kind, enabledSet);\n\t\t\tthis.framingByKind.set(kind.kind, detectResourceFraming(filter));\n\n\t\t\t// Compute missing items\n\t\t\tconst mentionedIds = new Set<string>();\n\t\t\tif (filter) {\n\t\t\t\tif (filter.allow) {\n\t\t\t\t\tfor (const id of filter.allow) {\n\t\t\t\t\t\tmentionedIds.add(id);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (filter.block) {\n\t\t\t\t\tfor (const id of filter.block) {\n\t\t\t\t\t\tif (id !== \"*\") {\n\t\t\t\t\t\t\tmentionedIds.add(id);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst availableIds = new Set(allIds);\n\t\t\tconst missingSet = new Set<string>();\n\t\t\tfor (const id of mentionedIds) {\n\t\t\t\tif (!availableIds.has(id)) {\n\t\t\t\t\tmissingSet.add(id);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.missingIdsByKind.set(kind.kind, missingSet);\n\t\t}\n\n\t\t// Header\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"accent\", theme.bold(`Library — editing \"${this.profileName}\" (${this.profileScope})`)),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\n\t\t\t\t\t\"muted\",\n\t\t\t\t\t`Navigate kinds: ${keyText(\"tui.input.tab\")}. Toggle: ${keyText(\"tui.select.confirm\")}. Mode: ${theme.fg(\"accent\", \"a\")} allow / ${theme.fg(\"accent\", \"b\")} block. Scope: ${theme.fg(\"accent\", \"s\")} change.`,\n\t\t\t\t),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Kind selector header\n\t\tthis.kindHeaderText = new Text(this.getKindHeaderText(), 0, 0);\n\t\tthis.addChild(this.kindHeaderText);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Search input\n\t\tthis.searchInput = new Input();\n\t\tthis.addChild(this.searchInput);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// List container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\t// Description area\n\t\tthis.descriptionText = new Text(\"\", 0, 0);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(this.descriptionText);\n\n\t\t// Footer hint\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.footerText = new Text(this.getFooterText(), 0, 0);\n\t\tthis.addChild(this.footerText);\n\n\t\tthis.addChild(new DynamicBorder());\n\n\t\tthis.refresh();\n\t}\n\n\tprivate getKindHeaderText(): string {\n\t\tconst kind = this.kinds[this.currentKindIndex]!;\n\t\tconst enabledSet = this.enabledByKind.get(kind.kind)!;\n\t\tconst framing = this.framingByKind.get(kind.kind)!;\n\t\tconst framingText = framing === \"allow\" ? \"allow-list\" : \"block-list\";\n\t\tconst countText = `${enabledSet.size}/${kind.items.length} enabled (${framingText})`;\n\t\tconst kindIndicator = this.kinds\n\t\t\t.map((k, i) => {\n\t\t\t\tconst marker = i === this.currentKindIndex ? \"●\" : \"○\";\n\t\t\t\treturn theme.fg(i === this.currentKindIndex ? \"accent\" : \"muted\", `${marker} ${k.label}`);\n\t\t\t})\n\t\t\t.join(\" \");\n\t\treturn `${kindIndicator} ${theme.fg(\"muted\", countText)}`;\n\t}\n\n\tprivate getCurrentKind(): ProfileResourceEditorKind {\n\t\treturn this.kinds[this.currentKindIndex]!;\n\t}\n\n\tprivate getCurrentEnabledSet(): Set<string> {\n\t\tconst kind = this.getCurrentKind();\n\t\treturn this.enabledByKind.get(kind.kind)!;\n\t}\n\n\tprivate buildItems(): ResourceItem[] {\n\t\tconst kind = this.getCurrentKind();\n\t\tconst enabledSet = this.getCurrentEnabledSet();\n\n\t\tconst items: ResourceItem[] = [];\n\n\t\t// Add all available items\n\t\tfor (const availableItem of kind.items) {\n\t\t\tconst isEnabled = enabledSet.has(availableItem.id);\n\t\t\tlet desc = availableItem.description;\n\t\t\tif (kind.kind === \"tools\" && !desc) {\n\t\t\t\tdesc = TOOL_DESCRIPTIONS[availableItem.id];\n\t\t\t}\n\t\t\titems.push({\n\t\t\t\tid: availableItem.id,\n\t\t\t\tenabled: isEnabled,\n\t\t\t\tpath: availableItem.path,\n\t\t\t\tdescription: desc,\n\t\t\t\tsourceLabel: classifyResourceSource(\n\t\t\t\t\tavailableItem.path,\n\t\t\t\t\tthis.cwd,\n\t\t\t\t\tthis.agentDir,\n\t\t\t\t\tthis.externalResourceRoots,\n\t\t\t\t),\n\t\t\t\tisMissing: false,\n\t\t\t});\n\t\t}\n\n\t\t// Add missing items\n\t\tconst missingSet = this.missingIdsByKind.get(kind.kind) || new Set<string>();\n\t\tfor (const missingId of missingSet) {\n\t\t\tconst isEnabled = enabledSet.has(missingId);\n\t\t\titems.push({\n\t\t\t\tid: missingId,\n\t\t\t\tenabled: isEnabled,\n\t\t\t\tisMissing: true,\n\t\t\t\tdescription: \"Referenced in profile but missing from available resources.\",\n\t\t\t});\n\t\t}\n\n\t\t// Sort: enabled first, then disabled\n\t\tconst enabled: ResourceItem[] = [];\n\t\tconst disabled: ResourceItem[] = [];\n\t\tfor (const item of items) {\n\t\t\tif (item.enabled) {\n\t\t\t\tenabled.push(item);\n\t\t\t} else {\n\t\t\t\tdisabled.push(item);\n\t\t\t}\n\t\t}\n\t\treturn [...enabled, ...disabled];\n\t}\n\n\tprivate getFooterText(): string {\n\t\tconst kind = this.getCurrentKind();\n\t\tconst enabledSet = this.getCurrentEnabledSet();\n\t\tconst countText = `${enabledSet.size}/${kind.items.length} enabled`;\n\t\tconst parts = [\n\t\t\t`${keyText(\"tui.select.confirm\")} toggle`,\n\t\t\t`${keyText(\"tui.input.tab\")} kind`,\n\t\t\t`${keyText(\"app.models.save\")} save`,\n\t\t\tcountText,\n\t\t];\n\t\treturn this.isDirty\n\t\t\t? theme.fg(\"dim\", ` ${parts.join(\" · \")} `) + theme.fg(\"warning\", \"(unsaved)\")\n\t\t\t: theme.fg(\"dim\", ` ${parts.join(\" · \")}`);\n\t}\n\n\tprivate refresh(): void {\n\t\tconst query = this.searchInput.getValue();\n\t\tconst items = this.buildItems();\n\t\tthis.filteredItems = query ? fuzzyFilter(items, query, (i) => `${i.id} ${i.description ?? \"\"}`) : items;\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredItems.length - 1));\n\t\tthis.updateList();\n\t\tthis.footerText.setText(this.getFooterText());\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tif (this.filteredItems.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching resources\"), 0, 0));\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", \" No description available\"));\n\t\t\treturn;\n\t\t}\n\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 item = this.filteredItems[i]!;\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst prefix = isSelected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tlet resourceText = isSelected ? theme.fg(\"accent\", item.id) : item.id;\n\n\t\t\tif (item.isMissing) {\n\t\t\t\tresourceText = theme.fg(\"muted\", `${item.id} [missing]`);\n\t\t\t} else if (item.sourceLabel) {\n\t\t\t\tconst labelColor =\n\t\t\t\t\titem.sourceLabel === \"catalog\"\n\t\t\t\t\t\t? \"thinkingText\"\n\t\t\t\t\t\t: item.sourceLabel === \"project\"\n\t\t\t\t\t\t\t? \"success\"\n\t\t\t\t\t\t\t: item.sourceLabel === \"user\"\n\t\t\t\t\t\t\t\t? \"warning\"\n\t\t\t\t\t\t\t\t: \"muted\";\n\t\t\t\tconst labelText = theme.fg(labelColor, ` [${item.sourceLabel}]`);\n\t\t\t\tresourceText = `${resourceText}${labelText}`;\n\t\t\t}\n\n\t\t\tconst status = item.enabled ? theme.fg(\"success\", \" ✓\") : theme.fg(\"dim\", \" ✗\");\n\t\t\tthis.listContainer.addChild(new Text(`${prefix}${resourceText}${status}`, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredItems.length) {\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`), 0, 0),\n\t\t\t);\n\t\t}\n\n\t\t// Update description area for current selection\n\t\tconst selectedItem = this.filteredItems[this.selectedIndex];\n\t\tif (selectedItem?.description) {\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", ` Description: ${selectedItem.description}`));\n\t\t} else {\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", \" No description available\"));\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\n\t\t// Navigation within list\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredItems.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.down\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredItems.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch kind with Tab (cycles forward)\n\t\tif (kb.matches(data, \"tui.input.tab\")) {\n\t\t\tthis.currentKindIndex = (this.currentKindIndex + 1) % this.kinds.length;\n\t\t\tthis.selectedIndex = 0;\n\t\t\tthis.searchInput.setValue(\"\");\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch framing: Allow-list\n\t\tif (matchesKey(data, Key.ctrl(\"a\")) || (this.searchInput.getValue() === \"\" && data === \"a\")) {\n\t\t\tthis.framingByKind.set(this.getCurrentKind().kind, \"allow\");\n\t\t\tthis.isDirty = true;\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch framing: Block-list\n\t\tif (matchesKey(data, Key.ctrl(\"b\")) || (this.searchInput.getValue() === \"\" && data === \"b\")) {\n\t\t\tthis.framingByKind.set(this.getCurrentKind().kind, \"block\");\n\t\t\tthis.isDirty = true;\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch scope: Scope selector\n\t\tif (matchesKey(data, Key.ctrl(\"o\")) || (this.searchInput.getValue() === \"\" && data === \"s\")) {\n\t\t\tif (this.onScopeChange) {\n\t\t\t\tthis.onScopeChange();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Toggle on space/enter\n\t\tif (kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item) {\n\t\t\t\tconst enabledSet = this.getCurrentEnabledSet();\n\t\t\t\tif (enabledSet.has(item.id)) {\n\t\t\t\t\tenabledSet.delete(item.id);\n\t\t\t\t} else {\n\t\t\t\t\tenabledSet.add(item.id);\n\t\t\t\t}\n\t\t\t\tthis.isDirty = true;\n\t\t\t\tthis.refresh();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Save/persist to settings\n\t\tif (kb.matches(data, \"app.models.save\")) {\n\t\t\tthis.persistChanges();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+C - clear search or cancel if empty\n\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\tif (this.searchInput.getValue()) {\n\t\t\t\tthis.searchInput.setValue(\"\");\n\t\t\t\tthis.refresh();\n\t\t\t} else {\n\t\t\t\tthis.onCancel();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Escape - cancel\n\t\tif (matchesKey(data, Key.escape)) {\n\t\t\tthis.onCancel();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass everything else to search input\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.refresh();\n\t}\n\n\tprivate persistChanges(): void {\n\t\tconst resources: ResourceProfileSettings = {};\n\t\tfor (const kind of this.kinds) {\n\t\t\tconst enabledSet = this.enabledByKind.get(kind.kind)!;\n\t\t\tconst framing = this.framingByKind.get(kind.kind)!;\n\t\t\tconst allIds = kind.items.map((item) => item.id);\n\t\t\tconst encoded = encodeResourceSelectionWithFraming(enabledSet, allIds, framing);\n\t\t\tif (encoded !== undefined) {\n\t\t\t\tresources[kind.kind] = encoded;\n\t\t\t}\n\t\t}\n\t\tthis.onSave(resources);\n\t\tthis.isDirty = false;\n\t\tthis.footerText.setText(this.getFooterText());\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"profile-resource-editor.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/profile-resource-editor.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { isAbsolute, resolve } from \"node:path\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tKey,\n\tmatchesKey,\n\tSpacer,\n\tText,\n} from \"@caupulican/pi-tui\";\nimport {\n\tdecodeResourceSelection,\n\tdetectResourceFraming,\n\tencodeResourceSelectionWithFraming,\n\ttype ResourceFraming,\n} from \"../../../core/profile-resource-selection.ts\";\nimport type { ResourceProfileKind, ResourceProfileSettings } from \"../../../core/settings-manager.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\n\nexport interface ProfileResourceItem {\n\tid: string;\n\tpath?: string;\n\tdescription?: string;\n}\n\nexport interface ProfileResourceEditorKind {\n\tkind: ResourceProfileKind; // \"tools\" | \"skills\" | \"extensions\" | \"agents\" | \"prompts\" | \"themes\"\n\tlabel: string; // display label, e.g. \"Tools\"\n\titems: ProfileResourceItem[]; // the available items of this kind\n}\n\nexport interface ProfileResourceEditorOptions {\n\tprofileName: string;\n\tprofileScope: string; // e.g. \"directory\" | \"project\" | \"global\" | \"session\" | \"reusable-file\"\n\tinitialResources: ResourceProfileSettings; // existing profile.resources; may be {}\n\tkinds: ProfileResourceEditorKind[]; // the six kinds, with their universes\n\tonSave: (resources: ResourceProfileSettings) => void; // called on ctrl+s with the encoded result\n\tonCancel: () => void; // called on esc\n\tonScopeChange?: () => void;\n\tonEdit?: (id: string, path: string, kind: ResourceProfileKind) => void;\n\tcwd?: string;\n\tagentDir?: string;\n\texternalResourceRoots?: string[];\n}\n\ninterface ResourceItem {\n\tid: string;\n\tenabled: boolean;\n\tpath?: string;\n\tdescription?: string;\n\tsourceLabel?: \"catalog\" | \"user\" | \"project\" | \"bundled\";\n\tisMissing?: boolean;\n}\n\nconst TOOL_DESCRIPTIONS: Record<string, string> = {\n\tread: \"Read files from the filesystem.\",\n\tbash: \"Execute arbitrary commands in the shell.\",\n\tedit: \"Edit files surgically.\",\n\twrite: \"Create new files or overwrite existing files.\",\n\tgrep: \"Search for text patterns using ripgrep.\",\n\tfind: \"Locate files matching a search query.\",\n\tls: \"List files and directories.\",\n\tskill_audit: \"Inspect and audit local skills.\",\n\tskillify: \"Convert a set of files/instructions into a skill.\",\n\textensionify: \"Package a tool or script as an extension.\",\n};\n\nexport function resolveResourceEditPath(\n\t_id: string,\n\tfilePath: string | undefined,\n\tkind: ResourceProfileKind,\n): string | undefined {\n\tif (kind === \"tools\") {\n\t\treturn undefined;\n\t}\n\n\tif (!filePath) {\n\t\treturn undefined;\n\t}\n\n\tif (kind === \"extensions\") {\n\t\ttry {\n\t\t\tconst stats = fs.statSync(filePath);\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\tconst indexTs = path.join(filePath, \"index.ts\");\n\t\t\t\tif (fs.existsSync(indexTs)) {\n\t\t\t\t\treturn indexTs;\n\t\t\t\t}\n\t\t\t\tconst indexJs = path.join(filePath, \"index.js\");\n\t\t\t\tif (fs.existsSync(indexJs)) {\n\t\t\t\t\treturn indexJs;\n\t\t\t\t}\n\t\t\t\tconst pkgJsonPath = path.join(filePath, \"package.json\");\n\t\t\t\tif (fs.existsSync(pkgJsonPath)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst pkg = JSON.parse(fs.readFileSync(pkgJsonPath, \"utf-8\"));\n\t\t\t\t\t\tif (typeof pkg.main === \"string\") {\n\t\t\t\t\t\t\tconst resolvedMain = path.resolve(filePath, pkg.main);\n\t\t\t\t\t\t\tif (fs.existsSync(resolvedMain)) {\n\t\t\t\t\t\t\t\treturn resolvedMain;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t}\n\t\t\t\treturn indexTs;\n\t\t\t}\n\t\t} catch {}\n\t\treturn filePath;\n\t}\n\n\tif (kind === \"skills\") {\n\t\ttry {\n\t\t\tconst stats = fs.statSync(filePath);\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\tconst skillMd = path.join(filePath, \"SKILL.md\");\n\t\t\t\tif (fs.existsSync(skillMd)) {\n\t\t\t\t\treturn skillMd;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t\treturn filePath;\n\t}\n\n\treturn filePath;\n}\n\nexport function classifyResourceSource(\n\tfilePath: string | undefined,\n\tcwd: string,\n\tagentDir: string,\n\texternalRoots: string[],\n): \"catalog\" | \"user\" | \"project\" | \"bundled\" {\n\tif (!filePath) {\n\t\treturn \"bundled\";\n\t}\n\n\tconst absolutePath = isAbsolute(filePath) ? filePath : resolve(cwd, filePath);\n\tconst normPath = resolve(absolutePath);\n\tconst normCwd = resolve(cwd);\n\tconst normAgentDir = agentDir ? resolve(agentDir) : \"\";\n\n\tfor (const extRoot of externalRoots) {\n\t\tconst normExt = resolve(extRoot);\n\t\tif (normPath.startsWith(normExt)) {\n\t\t\treturn \"catalog\";\n\t\t}\n\t}\n\n\tif (normAgentDir && normPath.startsWith(normAgentDir)) {\n\t\treturn \"user\";\n\t}\n\n\tif (normPath.startsWith(normCwd)) {\n\t\treturn \"project\";\n\t}\n\n\tif (normPath.includes(\"node_modules\")) {\n\t\treturn \"bundled\";\n\t}\n\n\treturn \"bundled\";\n}\n\nexport class ProfileResourceEditorComponent extends Container implements Focusable {\n\tprivate profileName: string;\n\tprivate profileScope: string;\n\tprivate kinds: ProfileResourceEditorKind[];\n\tprivate enabledByKind: Map<ResourceProfileKind, Set<string>> = new Map();\n\tprivate framingByKind: Map<ResourceProfileKind, ResourceFraming> = new Map();\n\tprivate missingIdsByKind: Map<ResourceProfileKind, Set<string>> = new Map();\n\tprivate currentKindIndex = 0;\n\n\tprivate cwd: string;\n\tprivate agentDir: string;\n\tprivate externalResourceRoots: string[];\n\n\tprivate filteredItems: ResourceItem[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\tprivate kindHeaderText: Text;\n\tprivate listContainer: Container;\n\tprivate descriptionText: Text;\n\tprivate footerText: Text;\n\tprivate isDirty = false;\n\tprivate maxVisible = 8;\n\n\tprivate _focused = false;\n\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\tprivate onSave: (resources: ResourceProfileSettings) => void;\n\tprivate onCancel: () => void;\n\tprivate onScopeChange?: () => void;\n\tprivate onEdit?: (id: string, path: string, kind: ResourceProfileKind) => void;\n\n\tconstructor(options: ProfileResourceEditorOptions) {\n\t\tsuper();\n\t\tthis.profileName = options.profileName;\n\t\tthis.profileScope = options.profileScope;\n\t\tthis.kinds = options.kinds;\n\t\tthis.onSave = options.onSave;\n\t\tthis.onCancel = options.onCancel;\n\t\tthis.onScopeChange = options.onScopeChange;\n\t\tthis.onEdit = options.onEdit;\n\t\tthis.cwd = options.cwd || process.cwd();\n\t\tthis.agentDir = options.agentDir || \"\";\n\t\tthis.externalResourceRoots = options.externalResourceRoots || [];\n\n\t\t// Initialize enabled, framing, and missing sets for each kind\n\t\tfor (const kind of this.kinds) {\n\t\t\tconst filter = options.initialResources[kind.kind];\n\t\t\tconst allIds = kind.items.map((item) => item.id);\n\t\t\tconst enabledSet = decodeResourceSelection(filter, allIds);\n\t\t\tthis.enabledByKind.set(kind.kind, enabledSet);\n\t\t\tthis.framingByKind.set(kind.kind, detectResourceFraming(filter));\n\n\t\t\t// Compute missing items\n\t\t\tconst mentionedIds = new Set<string>();\n\t\t\tif (filter) {\n\t\t\t\tif (filter.allow) {\n\t\t\t\t\tfor (const id of filter.allow) {\n\t\t\t\t\t\tmentionedIds.add(id);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (filter.block) {\n\t\t\t\t\tfor (const id of filter.block) {\n\t\t\t\t\t\tif (id !== \"*\") {\n\t\t\t\t\t\t\tmentionedIds.add(id);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst availableIds = new Set(allIds);\n\t\t\tconst missingSet = new Set<string>();\n\t\t\tfor (const id of mentionedIds) {\n\t\t\t\tif (!availableIds.has(id)) {\n\t\t\t\t\tmissingSet.add(id);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.missingIdsByKind.set(kind.kind, missingSet);\n\t\t}\n\n\t\t// Header\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"accent\", theme.bold(`Library — editing \"${this.profileName}\" (${this.profileScope})`)),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\n\t\t\t\t\t\"muted\",\n\t\t\t\t\t`Navigate kinds: ${keyText(\"tui.input.tab\")}. Toggle: ${keyText(\"tui.select.confirm\")}. Mode: ${theme.fg(\"accent\", \"a\")} allow / ${theme.fg(\"accent\", \"b\")} block. Scope: ${theme.fg(\"accent\", \"s\")} change. Edit: ${theme.fg(\"accent\", \"e\")}.`,\n\t\t\t\t),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Kind selector header\n\t\tthis.kindHeaderText = new Text(this.getKindHeaderText(), 0, 0);\n\t\tthis.addChild(this.kindHeaderText);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Search input\n\t\tthis.searchInput = new Input();\n\t\tthis.addChild(this.searchInput);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// List container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\t// Description area\n\t\tthis.descriptionText = new Text(\"\", 0, 0);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(this.descriptionText);\n\n\t\t// Footer hint\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.footerText = new Text(this.getFooterText(), 0, 0);\n\t\tthis.addChild(this.footerText);\n\n\t\tthis.addChild(new DynamicBorder());\n\n\t\tthis.refresh();\n\t}\n\n\tprivate getKindHeaderText(): string {\n\t\tconst kind = this.kinds[this.currentKindIndex]!;\n\t\tconst enabledSet = this.enabledByKind.get(kind.kind)!;\n\t\tconst framing = this.framingByKind.get(kind.kind)!;\n\t\tconst framingText = framing === \"allow\" ? \"allow-list\" : \"block-list\";\n\t\tconst countText = `${enabledSet.size}/${kind.items.length} enabled (${framingText})`;\n\t\tconst kindIndicator = this.kinds\n\t\t\t.map((k, i) => {\n\t\t\t\tconst marker = i === this.currentKindIndex ? \"●\" : \"○\";\n\t\t\t\treturn theme.fg(i === this.currentKindIndex ? \"accent\" : \"muted\", `${marker} ${k.label}`);\n\t\t\t})\n\t\t\t.join(\" \");\n\t\treturn `${kindIndicator} ${theme.fg(\"muted\", countText)}`;\n\t}\n\n\tprivate getCurrentKind(): ProfileResourceEditorKind {\n\t\treturn this.kinds[this.currentKindIndex]!;\n\t}\n\n\tprivate getCurrentEnabledSet(): Set<string> {\n\t\tconst kind = this.getCurrentKind();\n\t\treturn this.enabledByKind.get(kind.kind)!;\n\t}\n\n\tprivate buildItems(): ResourceItem[] {\n\t\tconst kind = this.getCurrentKind();\n\t\tconst enabledSet = this.getCurrentEnabledSet();\n\n\t\tconst items: ResourceItem[] = [];\n\n\t\t// Add all available items\n\t\tfor (const availableItem of kind.items) {\n\t\t\tconst isEnabled = enabledSet.has(availableItem.id);\n\t\t\tlet desc = availableItem.description;\n\t\t\tif (kind.kind === \"tools\" && !desc) {\n\t\t\t\tdesc = TOOL_DESCRIPTIONS[availableItem.id];\n\t\t\t}\n\t\t\titems.push({\n\t\t\t\tid: availableItem.id,\n\t\t\t\tenabled: isEnabled,\n\t\t\t\tpath: availableItem.path,\n\t\t\t\tdescription: desc,\n\t\t\t\tsourceLabel: classifyResourceSource(\n\t\t\t\t\tavailableItem.path,\n\t\t\t\t\tthis.cwd,\n\t\t\t\t\tthis.agentDir,\n\t\t\t\t\tthis.externalResourceRoots,\n\t\t\t\t),\n\t\t\t\tisMissing: false,\n\t\t\t});\n\t\t}\n\n\t\t// Add missing items\n\t\tconst missingSet = this.missingIdsByKind.get(kind.kind) || new Set<string>();\n\t\tfor (const missingId of missingSet) {\n\t\t\tconst isEnabled = enabledSet.has(missingId);\n\t\t\titems.push({\n\t\t\t\tid: missingId,\n\t\t\t\tenabled: isEnabled,\n\t\t\t\tisMissing: true,\n\t\t\t\tdescription: \"Referenced in profile but missing from available resources.\",\n\t\t\t});\n\t\t}\n\n\t\t// Sort: enabled first, then disabled\n\t\tconst enabled: ResourceItem[] = [];\n\t\tconst disabled: ResourceItem[] = [];\n\t\tfor (const item of items) {\n\t\t\tif (item.enabled) {\n\t\t\t\tenabled.push(item);\n\t\t\t} else {\n\t\t\t\tdisabled.push(item);\n\t\t\t}\n\t\t}\n\t\treturn [...enabled, ...disabled];\n\t}\n\n\tprivate getFooterText(): string {\n\t\tconst kind = this.getCurrentKind();\n\t\tconst enabledSet = this.getCurrentEnabledSet();\n\t\tconst countText = `${enabledSet.size}/${kind.items.length} enabled`;\n\t\tconst parts = [\n\t\t\t`${keyText(\"tui.select.confirm\")} toggle`,\n\t\t\t`${keyText(\"tui.input.tab\")} kind`,\n\t\t\t`${keyText(\"app.models.save\")} save`,\n\t\t\t`${theme.fg(\"accent\", \"e\")} edit`,\n\t\t\tcountText,\n\t\t];\n\t\treturn this.isDirty\n\t\t\t? theme.fg(\"dim\", ` ${parts.join(\" · \")} `) + theme.fg(\"warning\", \"(unsaved)\")\n\t\t\t: theme.fg(\"dim\", ` ${parts.join(\" · \")}`);\n\t}\n\n\tprivate refresh(): void {\n\t\tconst query = this.searchInput.getValue();\n\t\tconst items = this.buildItems();\n\t\tthis.filteredItems = query ? fuzzyFilter(items, query, (i) => `${i.id} ${i.description ?? \"\"}`) : items;\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredItems.length - 1));\n\t\tthis.updateList();\n\t\tthis.footerText.setText(this.getFooterText());\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tconst kind = this.getCurrentKind();\n\t\tconst missingSet = this.missingIdsByKind.get(kind.kind) || new Set<string>();\n\t\tconst totalAvailableCount = kind.items.length + missingSet.size;\n\n\t\tif (totalAvailableCount === 0) {\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(\"muted\", \" (none available — add a Source or install resources)\"), 0, 0),\n\t\t\t);\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", \" No description available\"));\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.filteredItems.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching resources\"), 0, 0));\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", \" No description available\"));\n\t\t\treturn;\n\t\t}\n\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 item = this.filteredItems[i]!;\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst prefix = isSelected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tlet resourceText = isSelected ? theme.fg(\"accent\", item.id) : item.id;\n\n\t\t\tif (item.isMissing) {\n\t\t\t\tresourceText = theme.fg(\"muted\", `${item.id} [missing]`);\n\t\t\t} else if (item.sourceLabel) {\n\t\t\t\tconst labelColor =\n\t\t\t\t\titem.sourceLabel === \"catalog\"\n\t\t\t\t\t\t? \"thinkingText\"\n\t\t\t\t\t\t: item.sourceLabel === \"project\"\n\t\t\t\t\t\t\t? \"success\"\n\t\t\t\t\t\t\t: item.sourceLabel === \"user\"\n\t\t\t\t\t\t\t\t? \"warning\"\n\t\t\t\t\t\t\t\t: \"muted\";\n\t\t\t\tconst labelText = theme.fg(labelColor, ` [${item.sourceLabel}]`);\n\t\t\t\tresourceText = `${resourceText}${labelText}`;\n\t\t\t}\n\n\t\t\tconst status = item.enabled ? theme.fg(\"success\", \" ✓\") : theme.fg(\"dim\", \" ✗\");\n\t\t\tthis.listContainer.addChild(new Text(`${prefix}${resourceText}${status}`, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredItems.length) {\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`), 0, 0),\n\t\t\t);\n\t\t}\n\n\t\t// Update description area for current selection\n\t\tconst selectedItem = this.filteredItems[this.selectedIndex];\n\t\tif (selectedItem?.description) {\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", ` Description: ${selectedItem.description}`));\n\t\t} else {\n\t\t\tthis.descriptionText.setText(theme.fg(\"muted\", \" No description available\"));\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\n\t\t// Navigation within list\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredItems.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.down\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredItems.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch kind with Tab (cycles forward)\n\t\tif (kb.matches(data, \"tui.input.tab\")) {\n\t\t\tthis.currentKindIndex = (this.currentKindIndex + 1) % this.kinds.length;\n\t\t\tthis.selectedIndex = 0;\n\t\t\tthis.searchInput.setValue(\"\");\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch framing: Allow-list\n\t\tif (matchesKey(data, Key.ctrl(\"a\")) || (this.searchInput.getValue() === \"\" && data === \"a\")) {\n\t\t\tthis.framingByKind.set(this.getCurrentKind().kind, \"allow\");\n\t\t\tthis.isDirty = true;\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch framing: Block-list\n\t\tif (matchesKey(data, Key.ctrl(\"b\")) || (this.searchInput.getValue() === \"\" && data === \"b\")) {\n\t\t\tthis.framingByKind.set(this.getCurrentKind().kind, \"block\");\n\t\t\tthis.isDirty = true;\n\t\t\tthis.kindHeaderText.setText(this.getKindHeaderText());\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Edit highlighted resource\n\t\tif (data === \"e\" && this.searchInput.getValue() === \"\") {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item && !item.isMissing && item.path) {\n\t\t\t\tconst kind = this.getCurrentKind().kind;\n\t\t\t\tif (this.onEdit) {\n\t\t\t\t\tthis.onEdit(item.id, item.path, kind);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Switch scope: Scope selector\n\t\tif (matchesKey(data, Key.ctrl(\"o\")) || (this.searchInput.getValue() === \"\" && data === \"s\")) {\n\t\t\tif (this.onScopeChange) {\n\t\t\t\tthis.onScopeChange();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Toggle on space/enter\n\t\tif (kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item) {\n\t\t\t\tconst enabledSet = this.getCurrentEnabledSet();\n\t\t\t\tif (enabledSet.has(item.id)) {\n\t\t\t\t\tenabledSet.delete(item.id);\n\t\t\t\t} else {\n\t\t\t\t\tenabledSet.add(item.id);\n\t\t\t\t}\n\t\t\t\tthis.isDirty = true;\n\t\t\t\tthis.refresh();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Save/persist to settings\n\t\tif (kb.matches(data, \"app.models.save\")) {\n\t\t\tthis.persistChanges();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+C - clear search or cancel if empty\n\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\tif (this.searchInput.getValue()) {\n\t\t\t\tthis.searchInput.setValue(\"\");\n\t\t\t\tthis.refresh();\n\t\t\t} else {\n\t\t\t\tthis.onCancel();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Escape - cancel\n\t\tif (matchesKey(data, Key.escape)) {\n\t\t\tthis.onCancel();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass everything else to search input\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.refresh();\n\t}\n\n\tprivate persistChanges(): void {\n\t\tconst resources: ResourceProfileSettings = {};\n\t\tfor (const kind of this.kinds) {\n\t\t\tconst enabledSet = this.enabledByKind.get(kind.kind)!;\n\t\t\tconst framing = this.framingByKind.get(kind.kind)!;\n\t\t\tconst allIds = kind.items.map((item) => item.id);\n\t\t\tconst encoded = encodeResourceSelectionWithFraming(enabledSet, allIds, framing);\n\t\t\tif (encoded !== undefined) {\n\t\t\t\tresources[kind.kind] = encoded;\n\t\t\t}\n\t\t}\n\t\tthis.onSave(resources);\n\t\tthis.isDirty = false;\n\t\tthis.footerText.setText(this.getFooterText());\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
|