"use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.ConfigSelectorComponent = void 0; var _nodePath = require("node:path"); var _piTui = require("@mariozechner/pi-tui"); var _config = require("../../../config.js"); var _theme = require("../theme/theme.js"); var _dynamicBorder = require("./dynamic-border.js"); var _keybindingHints = require("./keybinding-hints.js"); /** * TUI component for managing package resources (enable/disable) */const RESOURCE_TYPE_LABELS = { extensions: "Extensions", skills: "Skills", prompts: "Prompts", themes: "Themes" }; function getGroupLabel(metadata) { if (metadata.origin === "package") { return `${metadata.source} (${metadata.scope})`; } // Top-level resources if (metadata.source === "auto") { return metadata.scope === "user" ? "User (~/.pi/agent/)" : "Project (.pi/)"; } return metadata.scope === "user" ? "User settings" : "Project settings"; } function buildGroups(resolved) { const groupMap = new Map(); const addToGroup = (resources, resourceType) => { for (const res of resources) { const { path, enabled, metadata } = res; const groupKey = `${metadata.origin}:${metadata.scope}:${metadata.source}`; if (!groupMap.has(groupKey)) { groupMap.set(groupKey, { key: groupKey, label: getGroupLabel(metadata), scope: metadata.scope, origin: metadata.origin, source: metadata.source, subgroups: [] }); } const group = groupMap.get(groupKey); const subgroupKey = `${groupKey}:${resourceType}`; let subgroup = group.subgroups.find((sg) => sg.type === resourceType); if (!subgroup) { subgroup = { type: resourceType, label: RESOURCE_TYPE_LABELS[resourceType], items: [] }; group.subgroups.push(subgroup); } const fileName = (0, _nodePath.basename)(path); const parentFolder = (0, _nodePath.basename)((0, _nodePath.dirname)(path)); let displayName; if (resourceType === "extensions" && parentFolder !== "extensions") { displayName = `${parentFolder}/${fileName}`; } else if (resourceType === "skills" && fileName === "SKILL.md") { displayName = parentFolder; } else { displayName = fileName; } subgroup.items.push({ path, enabled, metadata, resourceType, displayName, groupKey, subgroupKey }); } }; addToGroup(resolved.extensions, "extensions"); addToGroup(resolved.skills, "skills"); addToGroup(resolved.prompts, "prompts"); addToGroup(resolved.themes, "themes"); // Sort groups: packages first, then top-level; user before project const groups = Array.from(groupMap.values()); groups.sort((a, b) => { if (a.origin !== b.origin) { return a.origin === "package" ? -1 : 1; } if (a.scope !== b.scope) { return a.scope === "user" ? -1 : 1; } return a.source.localeCompare(b.source); }); // Sort subgroups within each group by type order, and items by name const typeOrder = { extensions: 0, skills: 1, prompts: 2, themes: 3 }; for (const group of groups) { group.subgroups.sort((a, b) => typeOrder[a.type] - typeOrder[b.type]); for (const subgroup of group.subgroups) { subgroup.items.sort((a, b) => a.displayName.localeCompare(b.displayName)); } } return groups; } class ConfigSelectorHeader { invalidate() {} render(width) { const title = _theme.theme.bold("Resource Configuration"); const sep = _theme.theme.fg("muted", " ยท "); const hint = (0, _keybindingHints.rawKeyHint)("space", "toggle") + sep + (0, _keybindingHints.rawKeyHint)("esc", "close"); const hintWidth = (0, _piTui.visibleWidth)(hint); const titleWidth = (0, _piTui.visibleWidth)(title); const spacing = Math.max(1, width - titleWidth - hintWidth); return [ (0, _piTui.truncateToWidth)(`${title}${" ".repeat(spacing)}${hint}`, width, ""), _theme.theme.fg("muted", "Type to filter resources")]; } } class ResourceList { groups; flatItems = []; filteredItems = []; selectedIndex = 0; searchInput; maxVisible = 15; settingsManager; cwd; agentDir; onCancel; onExit; onToggle; _focused = false; get focused() { return this._focused; } set focused(value) { this._focused = value; this.searchInput.focused = value; } constructor(groups, settingsManager, cwd, agentDir) { this.groups = groups; this.settingsManager = settingsManager; this.cwd = cwd; this.agentDir = agentDir; this.searchInput = new _piTui.Input(); this.buildFlatList(); this.filteredItems = [...this.flatItems]; } buildFlatList() { this.flatItems = []; for (const group of this.groups) { this.flatItems.push({ type: "group", group }); for (const subgroup of group.subgroups) { this.flatItems.push({ type: "subgroup", subgroup, group }); for (const item of subgroup.items) { this.flatItems.push({ type: "item", item }); } } } // Start selection on first item (not header) this.selectedIndex = this.flatItems.findIndex((e) => e.type === "item"); if (this.selectedIndex < 0) this.selectedIndex = 0; } findNextItem(fromIndex, direction) { let idx = fromIndex + direction; while (idx >= 0 && idx < this.filteredItems.length) { if (this.filteredItems[idx].type === "item") { return idx; } idx += direction; } return fromIndex; // Stay at current if no item found } filterItems(query) { if (!query.trim()) { this.filteredItems = [...this.flatItems]; this.selectFirstItem(); return; } const lowerQuery = query.toLowerCase(); const matchingItems = new Set(); const matchingSubgroups = new Set(); const matchingGroups = new Set(); for (const entry of this.flatItems) { if (entry.type === "item") { const item = entry.item; if (item.displayName.toLowerCase().includes(lowerQuery) || item.resourceType.toLowerCase().includes(lowerQuery) || item.path.toLowerCase().includes(lowerQuery)) { matchingItems.add(item); } } } // Find which subgroups and groups contain matching items for (const group of this.groups) { for (const subgroup of group.subgroups) { for (const item of subgroup.items) { if (matchingItems.has(item)) { matchingSubgroups.add(subgroup); matchingGroups.add(group); } } } } this.filteredItems = []; for (const entry of this.flatItems) { if (entry.type === "group" && matchingGroups.has(entry.group)) { this.filteredItems.push(entry); } else if (entry.type === "subgroup" && matchingSubgroups.has(entry.subgroup)) { this.filteredItems.push(entry); } else if (entry.type === "item" && matchingItems.has(entry.item)) { this.filteredItems.push(entry); } } this.selectFirstItem(); } selectFirstItem() { const firstItemIndex = this.filteredItems.findIndex((e) => e.type === "item"); this.selectedIndex = firstItemIndex >= 0 ? firstItemIndex : 0; } updateItem(item, enabled) { item.enabled = enabled; // Update in groups too for (const group of this.groups) { for (const subgroup of group.subgroups) { const found = subgroup.items.find((i) => i.path === item.path && i.resourceType === item.resourceType); if (found) { found.enabled = enabled; return; } } } } invalidate() {} render(width) { const lines = []; // Search input lines.push(...this.searchInput.render(width)); lines.push(""); if (this.filteredItems.length === 0) { lines.push(_theme.theme.fg("muted", " No resources found")); return lines; } // Calculate visible range const startIndex = Math.max(0, Math.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible)); const endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length); for (let i = startIndex; i < endIndex; i++) { const entry = this.filteredItems[i]; const isSelected = i === this.selectedIndex; if (entry.type === "group") { // Main group header (no cursor) const groupLine = _theme.theme.fg("accent", _theme.theme.bold(entry.group.label)); lines.push((0, _piTui.truncateToWidth)(` ${groupLine}`, width, "")); } else if (entry.type === "subgroup") { // Subgroup header (indented, no cursor) const subgroupLine = _theme.theme.fg("muted", entry.subgroup.label); lines.push((0, _piTui.truncateToWidth)(` ${subgroupLine}`, width, "")); } else { // Resource item (cursor only on items) const item = entry.item; const cursor = isSelected ? "> " : " "; const checkbox = item.enabled ? _theme.theme.fg("success", "[x]") : _theme.theme.fg("dim", "[ ]"); const name = isSelected ? _theme.theme.bold(item.displayName) : item.displayName; lines.push((0, _piTui.truncateToWidth)(`${cursor} ${checkbox} ${name}`, width, "...")); } } // Scroll indicator if (startIndex > 0 || endIndex < this.filteredItems.length) { lines.push(_theme.theme.fg("dim", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`)); } return lines; } handleInput(data) { const kb = (0, _piTui.getEditorKeybindings)(); if (kb.matches(data, "selectUp")) { this.selectedIndex = this.findNextItem(this.selectedIndex, -1); return; } if (kb.matches(data, "selectDown")) { this.selectedIndex = this.findNextItem(this.selectedIndex, 1); return; } if (kb.matches(data, "selectPageUp")) { // Jump up by maxVisible, then find nearest item let target = Math.max(0, this.selectedIndex - this.maxVisible); while (target < this.filteredItems.length && this.filteredItems[target].type !== "item") { target++; } if (target < this.filteredItems.length) { this.selectedIndex = target; } return; } if (kb.matches(data, "selectPageDown")) { // Jump down by maxVisible, then find nearest item let target = Math.min(this.filteredItems.length - 1, this.selectedIndex + this.maxVisible); while (target >= 0 && this.filteredItems[target].type !== "item") { target--; } if (target >= 0) { this.selectedIndex = target; } return; } if (kb.matches(data, "selectCancel")) { this.onCancel?.(); return; } if ((0, _piTui.matchesKey)(data, "ctrl+c")) { this.onExit?.(); return; } if (data === " " || kb.matches(data, "selectConfirm")) { const entry = this.filteredItems[this.selectedIndex]; if (entry?.type === "item") { const newEnabled = !entry.item.enabled; this.toggleResource(entry.item, newEnabled); this.updateItem(entry.item, newEnabled); this.onToggle?.(entry.item, newEnabled); } return; } // Pass to search input this.searchInput.handleInput(data); this.filterItems(this.searchInput.getValue()); } toggleResource(item, enabled) { if (item.metadata.origin === "top-level") { this.toggleTopLevelResource(item, enabled); } else { this.togglePackageResource(item, enabled); } } toggleTopLevelResource(item, enabled) { const scope = item.metadata.scope; const settings = scope === "project" ? this.settingsManager.getProjectSettings() : this.settingsManager.getGlobalSettings(); const arrayKey = item.resourceType; const current = settings[arrayKey] ?? []; // Generate pattern for this resource const pattern = this.getResourcePattern(item); const disablePattern = `-${pattern}`; const enablePattern = `+${pattern}`; // Filter out existing patterns for this resource const updated = current.filter((p) => { const stripped = p.startsWith("!") || p.startsWith("+") || p.startsWith("-") ? p.slice(1) : p; return stripped !== pattern; }); if (enabled) { updated.push(enablePattern); } else { updated.push(disablePattern); } if (scope === "project") { if (arrayKey === "extensions") { this.settingsManager.setProjectExtensionPaths(updated); } else if (arrayKey === "skills") { this.settingsManager.setProjectSkillPaths(updated); } else if (arrayKey === "prompts") { this.settingsManager.setProjectPromptTemplatePaths(updated); } else if (arrayKey === "themes") { this.settingsManager.setProjectThemePaths(updated); } } else { if (arrayKey === "extensions") { this.settingsManager.setExtensionPaths(updated); } else if (arrayKey === "skills") { this.settingsManager.setSkillPaths(updated); } else if (arrayKey === "prompts") { this.settingsManager.setPromptTemplatePaths(updated); } else if (arrayKey === "themes") { this.settingsManager.setThemePaths(updated); } } } togglePackageResource(item, enabled) { const scope = item.metadata.scope; const settings = scope === "project" ? this.settingsManager.getProjectSettings() : this.settingsManager.getGlobalSettings(); const packages = [...(settings.packages ?? [])]; const pkgIndex = packages.findIndex((pkg) => { const source = typeof pkg === "string" ? pkg : pkg.source; return source === item.metadata.source; }); if (pkgIndex === -1) return; let pkg = packages[pkgIndex]; // Convert string to object form if needed if (typeof pkg === "string") { pkg = { source: pkg }; packages[pkgIndex] = pkg; } // Get the resource array for this type const arrayKey = item.resourceType; const current = pkg[arrayKey] ?? []; // Generate pattern relative to package root const pattern = this.getPackageResourcePattern(item); const disablePattern = `-${pattern}`; const enablePattern = `+${pattern}`; // Filter out existing patterns for this resource const updated = current.filter((p) => { const stripped = p.startsWith("!") || p.startsWith("+") || p.startsWith("-") ? p.slice(1) : p; return stripped !== pattern; }); if (enabled) { updated.push(enablePattern); } else { updated.push(disablePattern); } pkg[arrayKey] = updated.length > 0 ? updated : undefined; // Clean up empty filter object const hasFilters = ["extensions", "skills", "prompts", "themes"].some((k) => pkg[k] !== undefined); if (!hasFilters) { packages[pkgIndex] = pkg.source; } if (scope === "project") { this.settingsManager.setProjectPackages(packages); } else { this.settingsManager.setPackages(packages); } } getTopLevelBaseDir(scope) { return scope === "project" ? (0, _nodePath.join)(this.cwd, _config.CONFIG_DIR_NAME) : this.agentDir; } getResourcePattern(item) { const scope = item.metadata.scope; const baseDir = this.getTopLevelBaseDir(scope); return (0, _nodePath.relative)(baseDir, item.path); } getPackageResourcePattern(item) { const baseDir = item.metadata.baseDir ?? (0, _nodePath.dirname)(item.path); return (0, _nodePath.relative)(baseDir, item.path); } } class ConfigSelectorComponent extends _piTui.Container { resourceList; _focused = false; get focused() { return this._focused; } set focused(value) { this._focused = value; this.resourceList.focused = value; } constructor(resolvedPaths, settingsManager, cwd, agentDir, onClose, onExit, requestRender) { super(); const groups = buildGroups(resolvedPaths); // Add header this.addChild(new _piTui.Spacer(1)); this.addChild(new _dynamicBorder.DynamicBorder()); this.addChild(new _piTui.Spacer(1)); this.addChild(new ConfigSelectorHeader()); this.addChild(new _piTui.Spacer(1)); // Resource list this.resourceList = new ResourceList(groups, settingsManager, cwd, agentDir); this.resourceList.onCancel = onClose; this.resourceList.onExit = onExit; this.resourceList.onToggle = () => requestRender(); this.addChild(this.resourceList); // Bottom border this.addChild(new _piTui.Spacer(1)); this.addChild(new _dynamicBorder.DynamicBorder()); } getResourceList() { return this.resourceList; } }exports.ConfigSelectorComponent = ConfigSelectorComponent; /* v9-5346fccd3bc02cd6 */