@mariozechner/pi-coding-agent 0.50.8 → 0.51.0
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 +62 -0
- package/README.md +4 -3
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +2 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js +3 -1
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -1
- package/dist/core/extensions/index.d.ts +2 -2
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/types.d.ts +67 -5
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +4 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/extensions/wrapper.d.ts.map +1 -1
- package/dist/core/extensions/wrapper.js +1 -1
- package/dist/core/extensions/wrapper.js.map +1 -1
- package/dist/core/keybindings.d.ts +1 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +2 -0
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +19 -17
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +11 -9
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +1 -0
- package/dist/core/skills.js.map +1 -1
- package/dist/core/tools/bash.d.ts +11 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +18 -3
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit.d.ts +2 -0
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts +2 -0
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts +2 -0
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +7 -7
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +5 -5
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts +2 -0
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/read.d.ts +2 -0
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/write.d.ts +2 -0
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.d.ts +3 -1
- package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.js +13 -4
- package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts +11 -2
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +58 -12
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts +6 -0
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +43 -16
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +6 -15
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +6 -3
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/clipboard-native.d.ts +7 -0
- package/dist/utils/clipboard-native.d.ts.map +1 -0
- package/dist/utils/clipboard-native.js +14 -0
- package/dist/utils/clipboard-native.js.map +1 -0
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +10 -1
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +14 -0
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/extensions.md +57 -9
- package/docs/keybindings.md +1 -0
- package/docs/models.md +43 -14
- package/docs/rpc.md +188 -1
- package/docs/termux.md +127 -0
- package/examples/extensions/README.md +2 -0
- package/examples/extensions/antigravity-image-gen.ts +1 -1
- package/examples/extensions/bash-spawn-hook.ts +30 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
- package/examples/extensions/hello.ts +1 -1
- package/examples/extensions/question.ts +1 -1
- package/examples/extensions/questionnaire.ts +1 -1
- package/examples/extensions/rpc-demo.ts +124 -0
- package/examples/extensions/sandbox/index.ts +1 -1
- package/examples/extensions/shutdown-command.ts +2 -2
- package/examples/extensions/ssh.ts +4 -4
- package/examples/extensions/subagent/index.ts +1 -1
- package/examples/extensions/titlebar-spinner.ts +58 -0
- package/examples/extensions/todo.ts +1 -1
- package/examples/extensions/tool-override.ts +1 -1
- package/examples/extensions/truncated-tool.ts +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/rpc-extension-ui.ts +632 -0
- package/examples/sdk/06-extensions.ts +1 -1
- package/package.json +7 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/session-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { unlink } from \"node:fs/promises\";\nimport * as os from \"node:os\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tgetEditorKeybindings,\n\tInput,\n\tmatchesKey,\n\tSpacer,\n\tText,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@mariozechner/pi-tui\";\nimport type { SessionInfo, SessionListProgress } from \"../../../core/session-manager.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { keyHint } from \"./keybinding-hints.js\";\nimport { filterAndSortSessions, type SortMode } from \"./session-selector-search.js\";\n\ntype SessionScope = \"current\" | \"all\";\n\nfunction shortenPath(path: string): string {\n\tconst home = os.homedir();\n\tif (!path) return path;\n\tif (path.startsWith(home)) {\n\t\treturn `~${path.slice(home.length)}`;\n\t}\n\treturn path;\n}\n\nfunction formatSessionDate(date: Date): string {\n\tconst now = new Date();\n\tconst diffMs = now.getTime() - date.getTime();\n\tconst diffMins = Math.floor(diffMs / 60000);\n\tconst diffHours = Math.floor(diffMs / 3600000);\n\tconst diffDays = Math.floor(diffMs / 86400000);\n\n\tif (diffMins < 1) return \"now\";\n\tif (diffMins < 60) return `${diffMins}m`;\n\tif (diffHours < 24) return `${diffHours}h`;\n\tif (diffDays < 7) return `${diffDays}d`;\n\tif (diffDays < 30) return `${Math.floor(diffDays / 7)}w`;\n\tif (diffDays < 365) return `${Math.floor(diffDays / 30)}mo`;\n\treturn `${Math.floor(diffDays / 365)}y`;\n}\n\nclass SessionSelectorHeader implements Component {\n\tprivate scope: SessionScope;\n\tprivate sortMode: SortMode;\n\tprivate requestRender: () => void;\n\tprivate loading = false;\n\tprivate loadProgress: { loaded: number; total: number } | null = null;\n\tprivate showPath = false;\n\tprivate confirmingDeletePath: string | null = null;\n\tprivate statusMessage: { type: \"info\" | \"error\"; message: string } | null = null;\n\tprivate statusTimeout: ReturnType<typeof setTimeout> | null = null;\n\tprivate showRenameHint = false;\n\n\tconstructor(scope: SessionScope, sortMode: SortMode, requestRender: () => void) {\n\t\tthis.scope = scope;\n\t\tthis.sortMode = sortMode;\n\t\tthis.requestRender = requestRender;\n\t}\n\n\tsetScope(scope: SessionScope): void {\n\t\tthis.scope = scope;\n\t}\n\n\tsetSortMode(sortMode: SortMode): void {\n\t\tthis.sortMode = sortMode;\n\t}\n\n\tsetLoading(loading: boolean): void {\n\t\tthis.loading = loading;\n\t\t// Progress is scoped to the current load; clear whenever the loading state is set\n\t\tthis.loadProgress = null;\n\t}\n\n\tsetProgress(loaded: number, total: number): void {\n\t\tthis.loadProgress = { loaded, total };\n\t}\n\n\tsetShowPath(showPath: boolean): void {\n\t\tthis.showPath = showPath;\n\t}\n\n\tsetShowRenameHint(show: boolean): void {\n\t\tthis.showRenameHint = show;\n\t}\n\n\tsetConfirmingDeletePath(path: string | null): void {\n\t\tthis.confirmingDeletePath = path;\n\t}\n\n\tprivate clearStatusTimeout(): void {\n\t\tif (!this.statusTimeout) return;\n\t\tclearTimeout(this.statusTimeout);\n\t\tthis.statusTimeout = null;\n\t}\n\n\tsetStatusMessage(msg: { type: \"info\" | \"error\"; message: string } | null, autoHideMs?: number): void {\n\t\tthis.clearStatusTimeout();\n\t\tthis.statusMessage = msg;\n\t\tif (!msg || !autoHideMs) return;\n\n\t\tthis.statusTimeout = setTimeout(() => {\n\t\t\tthis.statusMessage = null;\n\t\t\tthis.statusTimeout = null;\n\t\t\tthis.requestRender();\n\t\t}, autoHideMs);\n\t}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst title = this.scope === \"current\" ? \"Resume Session (Current Folder)\" : \"Resume Session (All)\";\n\t\tconst leftText = theme.bold(title);\n\n\t\tconst sortLabel = this.sortMode === \"threaded\" ? \"Threaded\" : this.sortMode === \"recent\" ? \"Recent\" : \"Fuzzy\";\n\t\tconst sortText = theme.fg(\"muted\", \"Sort: \") + theme.fg(\"accent\", sortLabel);\n\n\t\tlet scopeText: string;\n\t\tif (this.loading) {\n\t\t\tconst progressText = this.loadProgress ? `${this.loadProgress.loaded}/${this.loadProgress.total}` : \"...\";\n\t\t\tscopeText = `${theme.fg(\"muted\", \"○ Current Folder | \")}${theme.fg(\"accent\", `Loading ${progressText}`)}`;\n\t\t} else if (this.scope === \"current\") {\n\t\t\tscopeText = `${theme.fg(\"accent\", \"◉ Current Folder\")}${theme.fg(\"muted\", \" | ○ All\")}`;\n\t\t} else {\n\t\t\tscopeText = `${theme.fg(\"muted\", \"○ Current Folder | \")}${theme.fg(\"accent\", \"◉ All\")}`;\n\t\t}\n\n\t\tconst rightText = truncateToWidth(`${scopeText} ${sortText}`, width, \"\");\n\t\tconst availableLeft = Math.max(0, width - visibleWidth(rightText) - 1);\n\t\tconst left = truncateToWidth(leftText, availableLeft, \"\");\n\t\tconst spacing = Math.max(0, width - visibleWidth(left) - visibleWidth(rightText));\n\n\t\t// Build hint lines - changes based on state (all branches truncate to width)\n\t\tlet hintLine1: string;\n\t\tlet hintLine2: string;\n\t\tif (this.confirmingDeletePath !== null) {\n\t\t\tconst confirmHint = \"Delete session? [Enter] confirm · [Esc/Ctrl+C] cancel\";\n\t\t\thintLine1 = theme.fg(\"error\", truncateToWidth(confirmHint, width, \"…\"));\n\t\t\thintLine2 = \"\";\n\t\t} else if (this.statusMessage) {\n\t\t\tconst color = this.statusMessage.type === \"error\" ? \"error\" : \"accent\";\n\t\t\thintLine1 = theme.fg(color, truncateToWidth(this.statusMessage.message, width, \"…\"));\n\t\t\thintLine2 = \"\";\n\t\t} else {\n\t\t\tconst pathState = this.showPath ? \"(on)\" : \"(off)\";\n\t\t\tconst sep = theme.fg(\"muted\", \" · \");\n\t\t\tconst hint1 = keyHint(\"tab\", \"scope\") + sep + theme.fg(\"muted\", 're:<pattern> regex · \"phrase\" exact');\n\t\t\tconst hint2Parts = [\n\t\t\t\tkeyHint(\"toggleSessionSort\", \"sort\"),\n\t\t\t\tkeyHint(\"deleteSession\", \"delete\"),\n\t\t\t\tkeyHint(\"toggleSessionPath\", `path ${pathState}`),\n\t\t\t];\n\t\t\tif (this.showRenameHint) {\n\t\t\t\thint2Parts.push(keyHint(\"renameSession\", \"rename\"));\n\t\t\t}\n\t\t\tconst hint2 = hint2Parts.join(sep);\n\t\t\thintLine1 = truncateToWidth(hint1, width, \"…\");\n\t\t\thintLine2 = truncateToWidth(hint2, width, \"…\");\n\t\t}\n\n\t\treturn [`${left}${\" \".repeat(spacing)}${rightText}`, hintLine1, hintLine2];\n\t}\n}\n\n/** A session tree node for hierarchical display */\ninterface SessionTreeNode {\n\tsession: SessionInfo;\n\tchildren: SessionTreeNode[];\n}\n\n/** Flattened node for display with tree structure info */\ninterface FlatSessionNode {\n\tsession: SessionInfo;\n\tdepth: number;\n\tisLast: boolean;\n\t/** For each ancestor level, whether there are more siblings after it */\n\tancestorContinues: boolean[];\n}\n\n/**\n * Build a tree structure from sessions based on parentSessionPath.\n * Returns root nodes sorted by modified date (descending).\n */\nfunction buildSessionTree(sessions: SessionInfo[]): SessionTreeNode[] {\n\tconst byPath = new Map<string, SessionTreeNode>();\n\n\tfor (const session of sessions) {\n\t\tbyPath.set(session.path, { session, children: [] });\n\t}\n\n\tconst roots: SessionTreeNode[] = [];\n\n\tfor (const session of sessions) {\n\t\tconst node = byPath.get(session.path)!;\n\t\tconst parentPath = session.parentSessionPath;\n\n\t\tif (parentPath && byPath.has(parentPath)) {\n\t\t\tbyPath.get(parentPath)!.children.push(node);\n\t\t} else {\n\t\t\troots.push(node);\n\t\t}\n\t}\n\n\t// Sort children and roots by modified date (descending)\n\tconst sortNodes = (nodes: SessionTreeNode[]): void => {\n\t\tnodes.sort((a, b) => b.session.modified.getTime() - a.session.modified.getTime());\n\t\tfor (const node of nodes) {\n\t\t\tsortNodes(node.children);\n\t\t}\n\t};\n\tsortNodes(roots);\n\n\treturn roots;\n}\n\n/**\n * Flatten tree into display list with tree structure metadata.\n */\nfunction flattenSessionTree(roots: SessionTreeNode[]): FlatSessionNode[] {\n\tconst result: FlatSessionNode[] = [];\n\n\tconst walk = (node: SessionTreeNode, depth: number, ancestorContinues: boolean[], isLast: boolean): void => {\n\t\tresult.push({ session: node.session, depth, isLast, ancestorContinues });\n\n\t\tfor (let i = 0; i < node.children.length; i++) {\n\t\t\tconst childIsLast = i === node.children.length - 1;\n\t\t\t// Only show continuation line for non-root ancestors\n\t\t\tconst continues = depth > 0 ? !isLast : false;\n\t\t\twalk(node.children[i]!, depth + 1, [...ancestorContinues, continues], childIsLast);\n\t\t}\n\t};\n\n\tfor (let i = 0; i < roots.length; i++) {\n\t\twalk(roots[i]!, 0, [], i === roots.length - 1);\n\t}\n\n\treturn result;\n}\n\n/**\n * Custom session list component with multi-line items and search\n */\nclass SessionList implements Component, Focusable {\n\tpublic getSelectedSessionPath(): string | undefined {\n\t\tconst selected = this.filteredSessions[this.selectedIndex];\n\t\treturn selected?.session.path;\n\t}\n\tprivate allSessions: SessionInfo[] = [];\n\tprivate filteredSessions: FlatSessionNode[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate searchInput: Input;\n\tprivate showCwd = false;\n\tprivate sortMode: SortMode = \"threaded\";\n\tprivate showPath = false;\n\tprivate confirmingDeletePath: string | null = null;\n\tprivate currentSessionFilePath?: string;\n\tpublic onSelect?: (sessionPath: string) => void;\n\tpublic onCancel?: () => void;\n\tpublic onExit: () => void = () => {};\n\tpublic onToggleScope?: () => void;\n\tpublic onToggleSort?: () => void;\n\tpublic onTogglePath?: (showPath: boolean) => void;\n\tpublic onDeleteConfirmationChange?: (path: string | null) => void;\n\tpublic onDeleteSession?: (sessionPath: string) => Promise<void>;\n\tpublic onRenameSession?: (sessionPath: string) => void;\n\tpublic onError?: (message: string) => void;\n\tprivate maxVisible: number = 10; // Max sessions visible (one line each)\n\n\t// Focusable implementation - propagate to searchInput for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\n\tconstructor(sessions: SessionInfo[], showCwd: boolean, sortMode: SortMode, currentSessionFilePath?: string) {\n\t\tthis.allSessions = sessions;\n\t\tthis.filteredSessions = [];\n\t\tthis.searchInput = new Input();\n\t\tthis.showCwd = showCwd;\n\t\tthis.sortMode = sortMode;\n\t\tthis.currentSessionFilePath = currentSessionFilePath;\n\t\tthis.filterSessions(\"\");\n\n\t\t// Handle Enter in search input - select current item\n\t\tthis.searchInput.onSubmit = () => {\n\t\t\tif (this.filteredSessions[this.selectedIndex]) {\n\t\t\t\tconst selected = this.filteredSessions[this.selectedIndex];\n\t\t\t\tif (this.onSelect) {\n\t\t\t\t\tthis.onSelect(selected.session.path);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n\n\tsetSortMode(sortMode: SortMode): void {\n\t\tthis.sortMode = sortMode;\n\t\tthis.filterSessions(this.searchInput.getValue());\n\t}\n\n\tsetSessions(sessions: SessionInfo[], showCwd: boolean): void {\n\t\tthis.allSessions = sessions;\n\t\tthis.showCwd = showCwd;\n\t\tthis.filterSessions(this.searchInput.getValue());\n\t}\n\n\tprivate filterSessions(query: string): void {\n\t\tconst trimmed = query.trim();\n\n\t\tif (this.sortMode === \"threaded\" && !trimmed) {\n\t\t\t// Threaded mode without search: show tree structure\n\t\t\tconst roots = buildSessionTree(this.allSessions);\n\t\t\tthis.filteredSessions = flattenSessionTree(roots);\n\t\t} else {\n\t\t\t// Other modes or with search: flat list\n\t\t\tconst filtered = trimmed ? filterAndSortSessions(this.allSessions, query, this.sortMode) : this.allSessions;\n\t\t\tthis.filteredSessions = filtered.map((session) => ({\n\t\t\t\tsession,\n\t\t\t\tdepth: 0,\n\t\t\t\tisLast: true,\n\t\t\t\tancestorContinues: [],\n\t\t\t}));\n\t\t}\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredSessions.length - 1));\n\t}\n\n\tprivate setConfirmingDeletePath(path: string | null): void {\n\t\tthis.confirmingDeletePath = path;\n\t\tthis.onDeleteConfirmationChange?.(path);\n\t}\n\n\tprivate startDeleteConfirmationForSelectedSession(): void {\n\t\tconst selected = this.filteredSessions[this.selectedIndex];\n\t\tif (!selected) return;\n\n\t\t// Prevent deleting current session\n\t\tif (this.currentSessionFilePath && selected.session.path === this.currentSessionFilePath) {\n\t\t\tthis.onError?.(\"Cannot delete the currently active session\");\n\t\t\treturn;\n\t\t}\n\n\t\tthis.setConfirmingDeletePath(selected.session.path);\n\t}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\n\t\t// Render search input\n\t\tlines.push(...this.searchInput.render(width));\n\t\tlines.push(\"\"); // Blank line after search\n\n\t\tif (this.filteredSessions.length === 0) {\n\t\t\tif (this.showCwd) {\n\t\t\t\t// \"All\" scope - no sessions anywhere that match filter\n\t\t\t\tlines.push(theme.fg(\"muted\", truncateToWidth(\" No sessions found\", width, \"…\")));\n\t\t\t} else {\n\t\t\t\t// \"Current folder\" scope - hint to try \"all\"\n\t\t\t\tlines.push(\n\t\t\t\t\ttheme.fg(\n\t\t\t\t\t\t\"muted\",\n\t\t\t\t\t\ttruncateToWidth(\" No sessions in current folder. Press Tab to view all.\", width, \"…\"),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn lines;\n\t\t}\n\n\t\t// Calculate visible range with scrolling\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredSessions.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.filteredSessions.length);\n\n\t\t// Render visible sessions (one line each with tree structure)\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst node = this.filteredSessions[i]!;\n\t\t\tconst session = node.session;\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst isConfirmingDelete = session.path === this.confirmingDeletePath;\n\t\t\tconst isCurrent = this.currentSessionFilePath === session.path;\n\n\t\t\t// Build tree prefix\n\t\t\tconst prefix = this.buildTreePrefix(node);\n\n\t\t\t// Session display text (name or first message)\n\t\t\tconst hasName = !!session.name;\n\t\t\tconst displayText = session.name ?? session.firstMessage;\n\t\t\tconst normalizedMessage = displayText.replace(/\\n/g, \" \").trim();\n\n\t\t\t// Right side: message count and age\n\t\t\tconst age = formatSessionDate(session.modified);\n\t\t\tconst msgCount = String(session.messageCount);\n\t\t\tlet rightPart = `${msgCount} ${age}`;\n\t\t\tif (this.showCwd && session.cwd) {\n\t\t\t\trightPart = `${shortenPath(session.cwd)} ${rightPart}`;\n\t\t\t}\n\t\t\tif (this.showPath) {\n\t\t\t\trightPart = `${shortenPath(session.path)} ${rightPart}`;\n\t\t\t}\n\n\t\t\t// Cursor\n\t\t\tconst cursor = isSelected ? theme.fg(\"accent\", \"› \") : \" \";\n\n\t\t\t// Calculate available width for message\n\t\t\tconst prefixWidth = visibleWidth(prefix);\n\t\t\tconst rightWidth = visibleWidth(rightPart) + 2; // +2 for spacing\n\t\t\tconst availableForMsg = width - 2 - prefixWidth - rightWidth; // -2 for cursor\n\n\t\t\tconst truncatedMsg = truncateToWidth(normalizedMessage, Math.max(10, availableForMsg), \"…\");\n\n\t\t\t// Style message\n\t\t\tlet messageColor: \"error\" | \"warning\" | \"accent\" | null = null;\n\t\t\tif (isConfirmingDelete) {\n\t\t\t\tmessageColor = \"error\";\n\t\t\t} else if (isCurrent) {\n\t\t\t\tmessageColor = \"accent\";\n\t\t\t} else if (hasName) {\n\t\t\t\tmessageColor = \"warning\";\n\t\t\t}\n\t\t\tlet styledMsg = messageColor ? theme.fg(messageColor, truncatedMsg) : truncatedMsg;\n\t\t\tif (isSelected) {\n\t\t\t\tstyledMsg = theme.bold(styledMsg);\n\t\t\t}\n\n\t\t\t// Build line\n\t\t\tconst leftPart = cursor + theme.fg(\"dim\", prefix) + styledMsg;\n\t\t\tconst leftWidth = visibleWidth(leftPart);\n\t\t\tconst spacing = Math.max(1, width - leftWidth - visibleWidth(rightPart));\n\t\t\tconst styledRight = theme.fg(isConfirmingDelete ? \"error\" : \"dim\", rightPart);\n\n\t\t\tlet line = leftPart + \" \".repeat(spacing) + styledRight;\n\t\t\tif (isSelected) {\n\t\t\t\tline = theme.bg(\"selectedBg\", line);\n\t\t\t}\n\t\t\tlines.push(truncateToWidth(line, width));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredSessions.length) {\n\t\t\tconst scrollText = ` (${this.selectedIndex + 1}/${this.filteredSessions.length})`;\n\t\t\tconst scrollInfo = theme.fg(\"muted\", truncateToWidth(scrollText, width, \"\"));\n\t\t\tlines.push(scrollInfo);\n\t\t}\n\n\t\treturn lines;\n\t}\n\n\tprivate buildTreePrefix(node: FlatSessionNode): string {\n\t\tif (node.depth === 0) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tconst parts = node.ancestorContinues.map((continues) => (continues ? \"│ \" : \" \"));\n\t\tconst branch = node.isLast ? \"└─ \" : \"├─ \";\n\t\treturn parts.join(\"\") + branch;\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getEditorKeybindings();\n\n\t\t// Handle delete confirmation state first - intercept all keys\n\t\tif (this.confirmingDeletePath !== null) {\n\t\t\tif (kb.matches(keyData, \"selectConfirm\")) {\n\t\t\t\tconst pathToDelete = this.confirmingDeletePath;\n\t\t\t\tthis.setConfirmingDeletePath(null);\n\t\t\t\tvoid this.onDeleteSession?.(pathToDelete);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Allow both Escape and Ctrl+C to cancel (consistent with pi UX)\n\t\t\tif (kb.matches(keyData, \"selectCancel\") || matchesKey(keyData, \"ctrl+c\")) {\n\t\t\t\tthis.setConfirmingDeletePath(null);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Ignore all other keys while confirming\n\t\t\treturn;\n\t\t}\n\n\t\tif (kb.matches(keyData, \"tab\")) {\n\t\t\tif (this.onToggleScope) {\n\t\t\t\tthis.onToggleScope();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (kb.matches(keyData, \"toggleSessionSort\")) {\n\t\t\tthis.onToggleSort?.();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+P: toggle path display\n\t\tif (kb.matches(keyData, \"toggleSessionPath\")) {\n\t\t\tthis.showPath = !this.showPath;\n\t\t\tthis.onTogglePath?.(this.showPath);\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+D: initiate delete confirmation (useful on terminals that don't distinguish Ctrl+Backspace from Backspace)\n\t\tif (kb.matches(keyData, \"deleteSession\")) {\n\t\t\tthis.startDeleteConfirmationForSelectedSession();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+R: rename selected session\n\t\tif (matchesKey(keyData, \"ctrl+r\")) {\n\t\t\tconst selected = this.filteredSessions[this.selectedIndex];\n\t\t\tif (selected) {\n\t\t\t\tthis.onRenameSession?.(selected.session.path);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+Backspace: non-invasive convenience alias for delete\n\t\t// Only triggers deletion when the query is empty; otherwise it is forwarded to the input\n\t\tif (kb.matches(keyData, \"deleteSessionNoninvasive\")) {\n\t\t\tif (this.searchInput.getValue().length > 0) {\n\t\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\t\tthis.filterSessions(this.searchInput.getValue());\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.startDeleteConfirmationForSelectedSession();\n\t\t\treturn;\n\t\t}\n\n\t\t// Up arrow\n\t\tif (kb.matches(keyData, \"selectUp\")) {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - 1);\n\t\t}\n\t\t// Down arrow\n\t\telse if (kb.matches(keyData, \"selectDown\")) {\n\t\t\tthis.selectedIndex = Math.min(this.filteredSessions.length - 1, this.selectedIndex + 1);\n\t\t}\n\t\t// Page up - jump up by maxVisible items\n\t\telse if (kb.matches(keyData, \"selectPageUp\")) {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - this.maxVisible);\n\t\t}\n\t\t// Page down - jump down by maxVisible items\n\t\telse if (kb.matches(keyData, \"selectPageDown\")) {\n\t\t\tthis.selectedIndex = Math.min(this.filteredSessions.length - 1, this.selectedIndex + this.maxVisible);\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"selectConfirm\")) {\n\t\t\tconst selected = this.filteredSessions[this.selectedIndex];\n\t\t\tif (selected && this.onSelect) {\n\t\t\t\tthis.onSelect(selected.session.path);\n\t\t\t}\n\t\t}\n\t\t// Escape - cancel\n\t\telse if (kb.matches(keyData, \"selectCancel\")) {\n\t\t\tif (this.onCancel) {\n\t\t\t\tthis.onCancel();\n\t\t\t}\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterSessions(this.searchInput.getValue());\n\t\t}\n\t}\n}\n\ntype SessionsLoader = (onProgress?: SessionListProgress) => Promise<SessionInfo[]>;\n\n/**\n * Delete a session file, trying the `trash` CLI first, then falling back to unlink\n */\nasync function deleteSessionFile(\n\tsessionPath: string,\n): Promise<{ ok: boolean; method: \"trash\" | \"unlink\"; error?: string }> {\n\t// Try `trash` first (if installed)\n\tconst trashArgs = sessionPath.startsWith(\"-\") ? [\"--\", sessionPath] : [sessionPath];\n\tconst trashResult = spawnSync(\"trash\", trashArgs, { encoding: \"utf-8\" });\n\n\tconst getTrashErrorHint = (): string | null => {\n\t\tconst parts: string[] = [];\n\t\tif (trashResult.error) {\n\t\t\tparts.push(trashResult.error.message);\n\t\t}\n\t\tconst stderr = trashResult.stderr?.trim();\n\t\tif (stderr) {\n\t\t\tparts.push(stderr.split(\"\\n\")[0] ?? stderr);\n\t\t}\n\t\tif (parts.length === 0) return null;\n\t\treturn `trash: ${parts.join(\" · \").slice(0, 200)}`;\n\t};\n\n\t// If trash reports success, or the file is gone afterwards, treat it as successful\n\tif (trashResult.status === 0 || !existsSync(sessionPath)) {\n\t\treturn { ok: true, method: \"trash\" };\n\t}\n\n\t// Fallback to permanent deletion\n\ttry {\n\t\tawait unlink(sessionPath);\n\t\treturn { ok: true, method: \"unlink\" };\n\t} catch (err) {\n\t\tconst unlinkError = err instanceof Error ? err.message : String(err);\n\t\tconst trashErrorHint = getTrashErrorHint();\n\t\tconst error = trashErrorHint ? `${unlinkError} (${trashErrorHint})` : unlinkError;\n\t\treturn { ok: false, method: \"unlink\", error };\n\t}\n}\n\n/**\n * Component that renders a session selector\n */\nexport class SessionSelectorComponent extends Container implements Focusable {\n\thandleInput(data: string): void {\n\t\tif (this.mode === \"rename\") {\n\t\t\tconst kb = getEditorKeybindings();\n\t\t\tif (kb.matches(data, \"selectCancel\") || matchesKey(data, \"ctrl+c\")) {\n\t\t\t\tthis.exitRenameMode();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.renameInput.handleInput(data);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.sessionList.handleInput(data);\n\t}\n\n\tprivate canRename = true;\n\tprivate sessionList: SessionList;\n\tprivate header: SessionSelectorHeader;\n\tprivate scope: SessionScope = \"current\";\n\tprivate sortMode: SortMode = \"threaded\";\n\tprivate currentSessions: SessionInfo[] | null = null;\n\tprivate allSessions: SessionInfo[] | null = null;\n\tprivate currentSessionsLoader: SessionsLoader;\n\tprivate allSessionsLoader: SessionsLoader;\n\tprivate onCancel: () => void;\n\tprivate requestRender: () => void;\n\tprivate renameSession?: (sessionPath: string, currentName: string | undefined) => Promise<void>;\n\tprivate currentLoading = false;\n\tprivate allLoading = false;\n\tprivate allLoadSeq = 0;\n\n\tprivate mode: \"list\" | \"rename\" = \"list\";\n\tprivate renameInput = new Input();\n\tprivate renameTargetPath: string | null = null;\n\n\t// Focusable implementation - propagate to sessionList for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.sessionList.focused = value;\n\t\tthis.renameInput.focused = value;\n\t\tif (value && this.mode === \"rename\") {\n\t\t\tthis.renameInput.focused = true;\n\t\t}\n\t}\n\n\tprivate buildBaseLayout(content: Component, options?: { showHeader?: boolean }): void {\n\t\tthis.clear();\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder((s) => theme.fg(\"accent\", s)));\n\t\tthis.addChild(new Spacer(1));\n\t\tif (options?.showHeader ?? true) {\n\t\t\tthis.addChild(this.header);\n\t\t\tthis.addChild(new Spacer(1));\n\t\t}\n\t\tthis.addChild(content);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder((s) => theme.fg(\"accent\", s)));\n\t}\n\n\tconstructor(\n\t\tcurrentSessionsLoader: SessionsLoader,\n\t\tallSessionsLoader: SessionsLoader,\n\t\tonSelect: (sessionPath: string) => void,\n\t\tonCancel: () => void,\n\t\tonExit: () => void,\n\t\trequestRender: () => void,\n\t\toptions?: {\n\t\t\trenameSession?: (sessionPath: string, currentName: string | undefined) => Promise<void>;\n\t\t\tshowRenameHint?: boolean;\n\t\t},\n\t\tcurrentSessionFilePath?: string,\n\t) {\n\t\tsuper();\n\t\tthis.currentSessionsLoader = currentSessionsLoader;\n\t\tthis.allSessionsLoader = allSessionsLoader;\n\t\tthis.onCancel = onCancel;\n\t\tthis.requestRender = requestRender;\n\t\tthis.header = new SessionSelectorHeader(this.scope, this.sortMode, this.requestRender);\n\t\tconst renameSession = options?.renameSession;\n\t\tthis.renameSession = renameSession;\n\t\tthis.canRename = !!renameSession;\n\t\tthis.header.setShowRenameHint(options?.showRenameHint ?? this.canRename);\n\n\t\t// Create session list (starts empty, will be populated after load)\n\t\tthis.sessionList = new SessionList([], false, this.sortMode, currentSessionFilePath);\n\n\t\tthis.buildBaseLayout(this.sessionList);\n\n\t\tthis.renameInput.onSubmit = (value) => {\n\t\t\tvoid this.confirmRename(value);\n\t\t};\n\n\t\t// Ensure header status timeouts are cleared when leaving the selector\n\t\tconst clearStatusMessage = () => this.header.setStatusMessage(null);\n\t\tthis.sessionList.onSelect = (sessionPath) => {\n\t\t\tclearStatusMessage();\n\t\t\tonSelect(sessionPath);\n\t\t};\n\t\tthis.sessionList.onCancel = () => {\n\t\t\tclearStatusMessage();\n\t\t\tonCancel();\n\t\t};\n\t\tthis.sessionList.onExit = () => {\n\t\t\tclearStatusMessage();\n\t\t\tonExit();\n\t\t};\n\t\tthis.sessionList.onToggleScope = () => this.toggleScope();\n\t\tthis.sessionList.onToggleSort = () => this.toggleSortMode();\n\t\tthis.sessionList.onRenameSession = (sessionPath) => {\n\t\t\tif (!renameSession) return;\n\t\t\tif (this.scope === \"current\" && this.currentLoading) return;\n\t\t\tif (this.scope === \"all\" && this.allLoading) return;\n\n\t\t\tconst sessions = this.scope === \"all\" ? (this.allSessions ?? []) : (this.currentSessions ?? []);\n\t\t\tconst session = sessions.find((s) => s.path === sessionPath);\n\t\t\tthis.enterRenameMode(sessionPath, session?.name);\n\t\t};\n\n\t\t// Sync list events to header\n\t\tthis.sessionList.onTogglePath = (showPath) => {\n\t\t\tthis.header.setShowPath(showPath);\n\t\t\tthis.requestRender();\n\t\t};\n\t\tthis.sessionList.onDeleteConfirmationChange = (path) => {\n\t\t\tthis.header.setConfirmingDeletePath(path);\n\t\t\tthis.requestRender();\n\t\t};\n\t\tthis.sessionList.onError = (msg) => {\n\t\t\tthis.header.setStatusMessage({ type: \"error\", message: msg }, 3000);\n\t\t\tthis.requestRender();\n\t\t};\n\n\t\t// Handle session deletion\n\t\tthis.sessionList.onDeleteSession = async (sessionPath: string) => {\n\t\t\tconst result = await deleteSessionFile(sessionPath);\n\n\t\t\tif (result.ok) {\n\t\t\t\tif (this.currentSessions) {\n\t\t\t\t\tthis.currentSessions = this.currentSessions.filter((s) => s.path !== sessionPath);\n\t\t\t\t}\n\t\t\t\tif (this.allSessions) {\n\t\t\t\t\tthis.allSessions = this.allSessions.filter((s) => s.path !== sessionPath);\n\t\t\t\t}\n\n\t\t\t\tconst sessions = this.scope === \"all\" ? (this.allSessions ?? []) : (this.currentSessions ?? []);\n\t\t\t\tconst showCwd = this.scope === \"all\";\n\t\t\t\tthis.sessionList.setSessions(sessions, showCwd);\n\n\t\t\t\tconst msg = result.method === \"trash\" ? \"Session moved to trash\" : \"Session deleted\";\n\t\t\t\tthis.header.setStatusMessage({ type: \"info\", message: msg }, 2000);\n\t\t\t\tawait this.refreshSessionsAfterMutation();\n\t\t\t} else {\n\t\t\t\tconst errorMessage = result.error ?? \"Unknown error\";\n\t\t\t\tthis.header.setStatusMessage({ type: \"error\", message: `Failed to delete: ${errorMessage}` }, 3000);\n\t\t\t}\n\n\t\t\tthis.requestRender();\n\t\t};\n\n\t\t// Start loading current sessions immediately\n\t\tthis.loadCurrentSessions();\n\t}\n\n\tprivate loadCurrentSessions(): void {\n\t\tvoid this.loadScope(\"current\", \"initial\");\n\t}\n\n\tprivate enterRenameMode(sessionPath: string, currentName: string | undefined): void {\n\t\tthis.mode = \"rename\";\n\t\tthis.renameTargetPath = sessionPath;\n\t\tthis.renameInput.setValue(currentName ?? \"\");\n\t\tthis.renameInput.focused = true;\n\n\t\tconst panel = new Container();\n\t\tpanel.addChild(new Text(theme.bold(\"Rename Session\"), 1, 0));\n\t\tpanel.addChild(new Spacer(1));\n\t\tpanel.addChild(this.renameInput);\n\t\tpanel.addChild(new Spacer(1));\n\t\tpanel.addChild(new Text(theme.fg(\"muted\", \"Enter to save · Esc/Ctrl+C to cancel\"), 1, 0));\n\n\t\tthis.buildBaseLayout(panel, { showHeader: false });\n\t\tthis.requestRender();\n\t}\n\n\tprivate exitRenameMode(): void {\n\t\tthis.mode = \"list\";\n\t\tthis.renameTargetPath = null;\n\n\t\tthis.buildBaseLayout(this.sessionList);\n\n\t\tthis.requestRender();\n\t}\n\n\tprivate async confirmRename(value: string): Promise<void> {\n\t\tconst next = value.trim();\n\t\tif (!next) return;\n\t\tconst target = this.renameTargetPath;\n\t\tif (!target) {\n\t\t\tthis.exitRenameMode();\n\t\t\treturn;\n\t\t}\n\n\t\t// Find current name for callback\n\t\tconst renameSession = this.renameSession;\n\t\tif (!renameSession) {\n\t\t\tthis.exitRenameMode();\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tawait renameSession(target, next);\n\t\t\tawait this.refreshSessionsAfterMutation();\n\t\t} finally {\n\t\t\tthis.exitRenameMode();\n\t\t}\n\t}\n\n\tprivate async loadScope(scope: SessionScope, reason: \"initial\" | \"refresh\" | \"toggle\"): Promise<void> {\n\t\tconst showCwd = scope === \"all\";\n\n\t\t// Mark loading\n\t\tif (scope === \"current\") {\n\t\t\tthis.currentLoading = true;\n\t\t} else {\n\t\t\tthis.allLoading = true;\n\t\t}\n\n\t\tconst seq = scope === \"all\" ? ++this.allLoadSeq : undefined;\n\t\tthis.header.setScope(scope);\n\t\tthis.header.setLoading(true);\n\t\tthis.requestRender();\n\n\t\tconst onProgress = (loaded: number, total: number) => {\n\t\t\tif (scope !== this.scope) return;\n\t\t\tif (seq !== undefined && seq !== this.allLoadSeq) return;\n\t\t\tthis.header.setProgress(loaded, total);\n\t\t\tthis.requestRender();\n\t\t};\n\n\t\ttry {\n\t\t\tconst sessions = await (scope === \"current\"\n\t\t\t\t? this.currentSessionsLoader(onProgress)\n\t\t\t\t: this.allSessionsLoader(onProgress));\n\n\t\t\tif (scope === \"current\") {\n\t\t\t\tthis.currentSessions = sessions;\n\t\t\t\tthis.currentLoading = false;\n\t\t\t} else {\n\t\t\t\tthis.allSessions = sessions;\n\t\t\t\tthis.allLoading = false;\n\t\t\t}\n\n\t\t\tif (scope !== this.scope) return;\n\t\t\tif (seq !== undefined && seq !== this.allLoadSeq) return;\n\n\t\t\tthis.header.setLoading(false);\n\t\t\tthis.sessionList.setSessions(sessions, showCwd);\n\t\t\tthis.requestRender();\n\n\t\t\tif (scope === \"all\" && sessions.length === 0 && (this.currentSessions?.length ?? 0) === 0) {\n\t\t\t\tthis.onCancel();\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tif (scope === \"current\") {\n\t\t\t\tthis.currentLoading = false;\n\t\t\t} else {\n\t\t\t\tthis.allLoading = false;\n\t\t\t}\n\n\t\t\tif (scope !== this.scope) return;\n\t\t\tif (seq !== undefined && seq !== this.allLoadSeq) return;\n\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\tthis.header.setLoading(false);\n\t\t\tthis.header.setStatusMessage({ type: \"error\", message: `Failed to load sessions: ${message}` }, 4000);\n\n\t\t\tif (reason === \"initial\") {\n\t\t\t\tthis.sessionList.setSessions([], showCwd);\n\t\t\t}\n\t\t\tthis.requestRender();\n\t\t}\n\t}\n\n\tprivate toggleSortMode(): void {\n\t\t// Cycle: threaded -> recent -> relevance -> threaded\n\t\tthis.sortMode = this.sortMode === \"threaded\" ? \"recent\" : this.sortMode === \"recent\" ? \"relevance\" : \"threaded\";\n\t\tthis.header.setSortMode(this.sortMode);\n\t\tthis.sessionList.setSortMode(this.sortMode);\n\t\tthis.requestRender();\n\t}\n\n\tprivate async refreshSessionsAfterMutation(): Promise<void> {\n\t\tawait this.loadScope(this.scope, \"refresh\");\n\t}\n\n\tprivate toggleScope(): void {\n\t\tif (this.scope === \"current\") {\n\t\t\tthis.scope = \"all\";\n\t\t\tthis.header.setScope(this.scope);\n\n\t\t\tif (this.allSessions !== null) {\n\t\t\t\tthis.header.setLoading(false);\n\t\t\t\tthis.sessionList.setSessions(this.allSessions, true);\n\t\t\t\tthis.requestRender();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!this.allLoading) {\n\t\t\t\tvoid this.loadScope(\"all\", \"toggle\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tthis.scope = \"current\";\n\t\tthis.header.setScope(this.scope);\n\t\tthis.header.setLoading(this.currentLoading);\n\t\tthis.sessionList.setSessions(this.currentSessions ?? [], false);\n\t\tthis.requestRender();\n\t}\n\n\tgetSessionList(): SessionList {\n\t\treturn this.sessionList;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"session-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/session-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { unlink } from \"node:fs/promises\";\nimport * as os from \"node:os\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tgetEditorKeybindings,\n\tInput,\n\tmatchesKey,\n\tSpacer,\n\tText,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@mariozechner/pi-tui\";\nimport { KeybindingsManager } from \"../../../core/keybindings.js\";\nimport type { SessionInfo, SessionListProgress } from \"../../../core/session-manager.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { appKey, appKeyHint, keyHint } from \"./keybinding-hints.js\";\nimport { filterAndSortSessions, hasSessionName, type NameFilter, type SortMode } from \"./session-selector-search.js\";\n\ntype SessionScope = \"current\" | \"all\";\n\nfunction shortenPath(path: string): string {\n\tconst home = os.homedir();\n\tif (!path) return path;\n\tif (path.startsWith(home)) {\n\t\treturn `~${path.slice(home.length)}`;\n\t}\n\treturn path;\n}\n\nfunction formatSessionDate(date: Date): string {\n\tconst now = new Date();\n\tconst diffMs = now.getTime() - date.getTime();\n\tconst diffMins = Math.floor(diffMs / 60000);\n\tconst diffHours = Math.floor(diffMs / 3600000);\n\tconst diffDays = Math.floor(diffMs / 86400000);\n\n\tif (diffMins < 1) return \"now\";\n\tif (diffMins < 60) return `${diffMins}m`;\n\tif (diffHours < 24) return `${diffHours}h`;\n\tif (diffDays < 7) return `${diffDays}d`;\n\tif (diffDays < 30) return `${Math.floor(diffDays / 7)}w`;\n\tif (diffDays < 365) return `${Math.floor(diffDays / 30)}mo`;\n\treturn `${Math.floor(diffDays / 365)}y`;\n}\n\nclass SessionSelectorHeader implements Component {\n\tprivate scope: SessionScope;\n\tprivate sortMode: SortMode;\n\tprivate nameFilter: NameFilter;\n\tprivate keybindings: KeybindingsManager;\n\tprivate requestRender: () => void;\n\tprivate loading = false;\n\tprivate loadProgress: { loaded: number; total: number } | null = null;\n\tprivate showPath = false;\n\tprivate confirmingDeletePath: string | null = null;\n\tprivate statusMessage: { type: \"info\" | \"error\"; message: string } | null = null;\n\tprivate statusTimeout: ReturnType<typeof setTimeout> | null = null;\n\tprivate showRenameHint = false;\n\n\tconstructor(\n\t\tscope: SessionScope,\n\t\tsortMode: SortMode,\n\t\tnameFilter: NameFilter,\n\t\tkeybindings: KeybindingsManager,\n\t\trequestRender: () => void,\n\t) {\n\t\tthis.scope = scope;\n\t\tthis.sortMode = sortMode;\n\t\tthis.nameFilter = nameFilter;\n\t\tthis.keybindings = keybindings;\n\t\tthis.requestRender = requestRender;\n\t}\n\n\tsetScope(scope: SessionScope): void {\n\t\tthis.scope = scope;\n\t}\n\n\tsetSortMode(sortMode: SortMode): void {\n\t\tthis.sortMode = sortMode;\n\t}\n\n\tsetNameFilter(nameFilter: NameFilter): void {\n\t\tthis.nameFilter = nameFilter;\n\t}\n\n\tsetLoading(loading: boolean): void {\n\t\tthis.loading = loading;\n\t\t// Progress is scoped to the current load; clear whenever the loading state is set\n\t\tthis.loadProgress = null;\n\t}\n\n\tsetProgress(loaded: number, total: number): void {\n\t\tthis.loadProgress = { loaded, total };\n\t}\n\n\tsetShowPath(showPath: boolean): void {\n\t\tthis.showPath = showPath;\n\t}\n\n\tsetShowRenameHint(show: boolean): void {\n\t\tthis.showRenameHint = show;\n\t}\n\n\tsetConfirmingDeletePath(path: string | null): void {\n\t\tthis.confirmingDeletePath = path;\n\t}\n\n\tprivate clearStatusTimeout(): void {\n\t\tif (!this.statusTimeout) return;\n\t\tclearTimeout(this.statusTimeout);\n\t\tthis.statusTimeout = null;\n\t}\n\n\tsetStatusMessage(msg: { type: \"info\" | \"error\"; message: string } | null, autoHideMs?: number): void {\n\t\tthis.clearStatusTimeout();\n\t\tthis.statusMessage = msg;\n\t\tif (!msg || !autoHideMs) return;\n\n\t\tthis.statusTimeout = setTimeout(() => {\n\t\t\tthis.statusMessage = null;\n\t\t\tthis.statusTimeout = null;\n\t\t\tthis.requestRender();\n\t\t}, autoHideMs);\n\t}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst title = this.scope === \"current\" ? \"Resume Session (Current Folder)\" : \"Resume Session (All)\";\n\t\tconst leftText = theme.bold(title);\n\n\t\tconst sortLabel = this.sortMode === \"threaded\" ? \"Threaded\" : this.sortMode === \"recent\" ? \"Recent\" : \"Fuzzy\";\n\t\tconst sortText = theme.fg(\"muted\", \"Sort: \") + theme.fg(\"accent\", sortLabel);\n\n\t\tconst nameLabel = this.nameFilter === \"all\" ? \"All\" : \"Named\";\n\t\tconst nameText = theme.fg(\"muted\", \"Name: \") + theme.fg(\"accent\", nameLabel);\n\n\t\tlet scopeText: string;\n\t\tif (this.loading) {\n\t\t\tconst progressText = this.loadProgress ? `${this.loadProgress.loaded}/${this.loadProgress.total}` : \"...\";\n\t\t\tscopeText = `${theme.fg(\"muted\", \"○ Current Folder | \")}${theme.fg(\"accent\", `Loading ${progressText}`)}`;\n\t\t} else if (this.scope === \"current\") {\n\t\t\tscopeText = `${theme.fg(\"accent\", \"◉ Current Folder\")}${theme.fg(\"muted\", \" | ○ All\")}`;\n\t\t} else {\n\t\t\tscopeText = `${theme.fg(\"muted\", \"○ Current Folder | \")}${theme.fg(\"accent\", \"◉ All\")}`;\n\t\t}\n\n\t\tconst rightText = truncateToWidth(`${scopeText} ${nameText} ${sortText}`, width, \"\");\n\t\tconst availableLeft = Math.max(0, width - visibleWidth(rightText) - 1);\n\t\tconst left = truncateToWidth(leftText, availableLeft, \"\");\n\t\tconst spacing = Math.max(0, width - visibleWidth(left) - visibleWidth(rightText));\n\n\t\t// Build hint lines - changes based on state (all branches truncate to width)\n\t\tlet hintLine1: string;\n\t\tlet hintLine2: string;\n\t\tif (this.confirmingDeletePath !== null) {\n\t\t\tconst confirmHint = \"Delete session? [Enter] confirm · [Esc/Ctrl+C] cancel\";\n\t\t\thintLine1 = theme.fg(\"error\", truncateToWidth(confirmHint, width, \"…\"));\n\t\t\thintLine2 = \"\";\n\t\t} else if (this.statusMessage) {\n\t\t\tconst color = this.statusMessage.type === \"error\" ? \"error\" : \"accent\";\n\t\t\thintLine1 = theme.fg(color, truncateToWidth(this.statusMessage.message, width, \"…\"));\n\t\t\thintLine2 = \"\";\n\t\t} else {\n\t\t\tconst pathState = this.showPath ? \"(on)\" : \"(off)\";\n\t\t\tconst sep = theme.fg(\"muted\", \" · \");\n\t\t\tconst hint1 = keyHint(\"tab\", \"scope\") + sep + theme.fg(\"muted\", 're:<pattern> regex · \"phrase\" exact');\n\t\t\tconst hint2Parts = [\n\t\t\t\tkeyHint(\"toggleSessionSort\", \"sort\"),\n\t\t\t\tappKeyHint(this.keybindings, \"toggleSessionNamedFilter\", \"named\"),\n\t\t\t\tkeyHint(\"deleteSession\", \"delete\"),\n\t\t\t\tkeyHint(\"toggleSessionPath\", `path ${pathState}`),\n\t\t\t];\n\t\t\tif (this.showRenameHint) {\n\t\t\t\thint2Parts.push(keyHint(\"renameSession\", \"rename\"));\n\t\t\t}\n\t\t\tconst hint2 = hint2Parts.join(sep);\n\t\t\thintLine1 = truncateToWidth(hint1, width, \"…\");\n\t\t\thintLine2 = truncateToWidth(hint2, width, \"…\");\n\t\t}\n\n\t\treturn [`${left}${\" \".repeat(spacing)}${rightText}`, hintLine1, hintLine2];\n\t}\n}\n\n/** A session tree node for hierarchical display */\ninterface SessionTreeNode {\n\tsession: SessionInfo;\n\tchildren: SessionTreeNode[];\n}\n\n/** Flattened node for display with tree structure info */\ninterface FlatSessionNode {\n\tsession: SessionInfo;\n\tdepth: number;\n\tisLast: boolean;\n\t/** For each ancestor level, whether there are more siblings after it */\n\tancestorContinues: boolean[];\n}\n\n/**\n * Build a tree structure from sessions based on parentSessionPath.\n * Returns root nodes sorted by modified date (descending).\n */\nfunction buildSessionTree(sessions: SessionInfo[]): SessionTreeNode[] {\n\tconst byPath = new Map<string, SessionTreeNode>();\n\n\tfor (const session of sessions) {\n\t\tbyPath.set(session.path, { session, children: [] });\n\t}\n\n\tconst roots: SessionTreeNode[] = [];\n\n\tfor (const session of sessions) {\n\t\tconst node = byPath.get(session.path)!;\n\t\tconst parentPath = session.parentSessionPath;\n\n\t\tif (parentPath && byPath.has(parentPath)) {\n\t\t\tbyPath.get(parentPath)!.children.push(node);\n\t\t} else {\n\t\t\troots.push(node);\n\t\t}\n\t}\n\n\t// Sort children and roots by modified date (descending)\n\tconst sortNodes = (nodes: SessionTreeNode[]): void => {\n\t\tnodes.sort((a, b) => b.session.modified.getTime() - a.session.modified.getTime());\n\t\tfor (const node of nodes) {\n\t\t\tsortNodes(node.children);\n\t\t}\n\t};\n\tsortNodes(roots);\n\n\treturn roots;\n}\n\n/**\n * Flatten tree into display list with tree structure metadata.\n */\nfunction flattenSessionTree(roots: SessionTreeNode[]): FlatSessionNode[] {\n\tconst result: FlatSessionNode[] = [];\n\n\tconst walk = (node: SessionTreeNode, depth: number, ancestorContinues: boolean[], isLast: boolean): void => {\n\t\tresult.push({ session: node.session, depth, isLast, ancestorContinues });\n\n\t\tfor (let i = 0; i < node.children.length; i++) {\n\t\t\tconst childIsLast = i === node.children.length - 1;\n\t\t\t// Only show continuation line for non-root ancestors\n\t\t\tconst continues = depth > 0 ? !isLast : false;\n\t\t\twalk(node.children[i]!, depth + 1, [...ancestorContinues, continues], childIsLast);\n\t\t}\n\t};\n\n\tfor (let i = 0; i < roots.length; i++) {\n\t\twalk(roots[i]!, 0, [], i === roots.length - 1);\n\t}\n\n\treturn result;\n}\n\n/**\n * Custom session list component with multi-line items and search\n */\nclass SessionList implements Component, Focusable {\n\tpublic getSelectedSessionPath(): string | undefined {\n\t\tconst selected = this.filteredSessions[this.selectedIndex];\n\t\treturn selected?.session.path;\n\t}\n\tprivate allSessions: SessionInfo[] = [];\n\tprivate filteredSessions: FlatSessionNode[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate searchInput: Input;\n\tprivate showCwd = false;\n\tprivate sortMode: SortMode = \"threaded\";\n\tprivate nameFilter: NameFilter = \"all\";\n\tprivate keybindings: KeybindingsManager;\n\tprivate showPath = false;\n\tprivate confirmingDeletePath: string | null = null;\n\tprivate currentSessionFilePath?: string;\n\tpublic onSelect?: (sessionPath: string) => void;\n\tpublic onCancel?: () => void;\n\tpublic onExit: () => void = () => {};\n\tpublic onToggleScope?: () => void;\n\tpublic onToggleSort?: () => void;\n\tpublic onToggleNameFilter?: () => void;\n\tpublic onTogglePath?: (showPath: boolean) => void;\n\tpublic onDeleteConfirmationChange?: (path: string | null) => void;\n\tpublic onDeleteSession?: (sessionPath: string) => Promise<void>;\n\tpublic onRenameSession?: (sessionPath: string) => void;\n\tpublic onError?: (message: string) => void;\n\tprivate maxVisible: number = 10; // Max sessions visible (one line each)\n\n\t// Focusable implementation - propagate to searchInput for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\n\tconstructor(\n\t\tsessions: SessionInfo[],\n\t\tshowCwd: boolean,\n\t\tsortMode: SortMode,\n\t\tnameFilter: NameFilter,\n\t\tkeybindings: KeybindingsManager,\n\t\tcurrentSessionFilePath?: string,\n\t) {\n\t\tthis.allSessions = sessions;\n\t\tthis.filteredSessions = [];\n\t\tthis.searchInput = new Input();\n\t\tthis.showCwd = showCwd;\n\t\tthis.sortMode = sortMode;\n\t\tthis.nameFilter = nameFilter;\n\t\tthis.keybindings = keybindings;\n\t\tthis.currentSessionFilePath = currentSessionFilePath;\n\t\tthis.filterSessions(\"\");\n\n\t\t// Handle Enter in search input - select current item\n\t\tthis.searchInput.onSubmit = () => {\n\t\t\tif (this.filteredSessions[this.selectedIndex]) {\n\t\t\t\tconst selected = this.filteredSessions[this.selectedIndex];\n\t\t\t\tif (this.onSelect) {\n\t\t\t\t\tthis.onSelect(selected.session.path);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n\n\tsetSortMode(sortMode: SortMode): void {\n\t\tthis.sortMode = sortMode;\n\t\tthis.filterSessions(this.searchInput.getValue());\n\t}\n\n\tsetNameFilter(nameFilter: NameFilter): void {\n\t\tthis.nameFilter = nameFilter;\n\t\tthis.filterSessions(this.searchInput.getValue());\n\t}\n\n\tsetSessions(sessions: SessionInfo[], showCwd: boolean): void {\n\t\tthis.allSessions = sessions;\n\t\tthis.showCwd = showCwd;\n\t\tthis.filterSessions(this.searchInput.getValue());\n\t}\n\n\tprivate filterSessions(query: string): void {\n\t\tconst trimmed = query.trim();\n\t\tconst nameFiltered =\n\t\t\tthis.nameFilter === \"all\" ? this.allSessions : this.allSessions.filter((session) => hasSessionName(session));\n\n\t\tif (this.sortMode === \"threaded\" && !trimmed) {\n\t\t\t// Threaded mode without search: show tree structure\n\t\t\tconst roots = buildSessionTree(nameFiltered);\n\t\t\tthis.filteredSessions = flattenSessionTree(roots);\n\t\t} else {\n\t\t\t// Other modes or with search: flat list\n\t\t\tconst filtered = filterAndSortSessions(nameFiltered, query, this.sortMode, \"all\");\n\t\t\tthis.filteredSessions = filtered.map((session) => ({\n\t\t\t\tsession,\n\t\t\t\tdepth: 0,\n\t\t\t\tisLast: true,\n\t\t\t\tancestorContinues: [],\n\t\t\t}));\n\t\t}\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredSessions.length - 1));\n\t}\n\n\tprivate setConfirmingDeletePath(path: string | null): void {\n\t\tthis.confirmingDeletePath = path;\n\t\tthis.onDeleteConfirmationChange?.(path);\n\t}\n\n\tprivate startDeleteConfirmationForSelectedSession(): void {\n\t\tconst selected = this.filteredSessions[this.selectedIndex];\n\t\tif (!selected) return;\n\n\t\t// Prevent deleting current session\n\t\tif (this.currentSessionFilePath && selected.session.path === this.currentSessionFilePath) {\n\t\t\tthis.onError?.(\"Cannot delete the currently active session\");\n\t\t\treturn;\n\t\t}\n\n\t\tthis.setConfirmingDeletePath(selected.session.path);\n\t}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\n\t\t// Render search input\n\t\tlines.push(...this.searchInput.render(width));\n\t\tlines.push(\"\"); // Blank line after search\n\n\t\tif (this.filteredSessions.length === 0) {\n\t\t\tlet emptyMessage: string;\n\t\t\tif (this.nameFilter === \"named\") {\n\t\t\t\tconst toggleKey = appKey(this.keybindings, \"toggleSessionNamedFilter\");\n\t\t\t\tif (this.showCwd) {\n\t\t\t\t\temptyMessage = ` No named sessions found. Press ${toggleKey} to show all.`;\n\t\t\t\t} else {\n\t\t\t\t\temptyMessage = ` No named sessions in current folder. Press ${toggleKey} to show all, or Tab to view all.`;\n\t\t\t\t}\n\t\t\t} else if (this.showCwd) {\n\t\t\t\t// \"All\" scope - no sessions anywhere that match filter\n\t\t\t\temptyMessage = \" No sessions found\";\n\t\t\t} else {\n\t\t\t\t// \"Current folder\" scope - hint to try \"all\"\n\t\t\t\temptyMessage = \" No sessions in current folder. Press Tab to view all.\";\n\t\t\t}\n\t\t\tlines.push(theme.fg(\"muted\", truncateToWidth(emptyMessage, width, \"…\")));\n\t\t\treturn lines;\n\t\t}\n\n\t\t// Calculate visible range with scrolling\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredSessions.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.filteredSessions.length);\n\n\t\t// Render visible sessions (one line each with tree structure)\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst node = this.filteredSessions[i]!;\n\t\t\tconst session = node.session;\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst isConfirmingDelete = session.path === this.confirmingDeletePath;\n\t\t\tconst isCurrent = this.currentSessionFilePath === session.path;\n\n\t\t\t// Build tree prefix\n\t\t\tconst prefix = this.buildTreePrefix(node);\n\n\t\t\t// Session display text (name or first message)\n\t\t\tconst hasName = !!session.name;\n\t\t\tconst displayText = session.name ?? session.firstMessage;\n\t\t\tconst normalizedMessage = displayText.replace(/\\n/g, \" \").trim();\n\n\t\t\t// Right side: message count and age\n\t\t\tconst age = formatSessionDate(session.modified);\n\t\t\tconst msgCount = String(session.messageCount);\n\t\t\tlet rightPart = `${msgCount} ${age}`;\n\t\t\tif (this.showCwd && session.cwd) {\n\t\t\t\trightPart = `${shortenPath(session.cwd)} ${rightPart}`;\n\t\t\t}\n\t\t\tif (this.showPath) {\n\t\t\t\trightPart = `${shortenPath(session.path)} ${rightPart}`;\n\t\t\t}\n\n\t\t\t// Cursor\n\t\t\tconst cursor = isSelected ? theme.fg(\"accent\", \"› \") : \" \";\n\n\t\t\t// Calculate available width for message\n\t\t\tconst prefixWidth = visibleWidth(prefix);\n\t\t\tconst rightWidth = visibleWidth(rightPart) + 2; // +2 for spacing\n\t\t\tconst availableForMsg = width - 2 - prefixWidth - rightWidth; // -2 for cursor\n\n\t\t\tconst truncatedMsg = truncateToWidth(normalizedMessage, Math.max(10, availableForMsg), \"…\");\n\n\t\t\t// Style message\n\t\t\tlet messageColor: \"error\" | \"warning\" | \"accent\" | null = null;\n\t\t\tif (isConfirmingDelete) {\n\t\t\t\tmessageColor = \"error\";\n\t\t\t} else if (isCurrent) {\n\t\t\t\tmessageColor = \"accent\";\n\t\t\t} else if (hasName) {\n\t\t\t\tmessageColor = \"warning\";\n\t\t\t}\n\t\t\tlet styledMsg = messageColor ? theme.fg(messageColor, truncatedMsg) : truncatedMsg;\n\t\t\tif (isSelected) {\n\t\t\t\tstyledMsg = theme.bold(styledMsg);\n\t\t\t}\n\n\t\t\t// Build line\n\t\t\tconst leftPart = cursor + theme.fg(\"dim\", prefix) + styledMsg;\n\t\t\tconst leftWidth = visibleWidth(leftPart);\n\t\t\tconst spacing = Math.max(1, width - leftWidth - visibleWidth(rightPart));\n\t\t\tconst styledRight = theme.fg(isConfirmingDelete ? \"error\" : \"dim\", rightPart);\n\n\t\t\tlet line = leftPart + \" \".repeat(spacing) + styledRight;\n\t\t\tif (isSelected) {\n\t\t\t\tline = theme.bg(\"selectedBg\", line);\n\t\t\t}\n\t\t\tlines.push(truncateToWidth(line, width));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredSessions.length) {\n\t\t\tconst scrollText = ` (${this.selectedIndex + 1}/${this.filteredSessions.length})`;\n\t\t\tconst scrollInfo = theme.fg(\"muted\", truncateToWidth(scrollText, width, \"\"));\n\t\t\tlines.push(scrollInfo);\n\t\t}\n\n\t\treturn lines;\n\t}\n\n\tprivate buildTreePrefix(node: FlatSessionNode): string {\n\t\tif (node.depth === 0) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tconst parts = node.ancestorContinues.map((continues) => (continues ? \"│ \" : \" \"));\n\t\tconst branch = node.isLast ? \"└─ \" : \"├─ \";\n\t\treturn parts.join(\"\") + branch;\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getEditorKeybindings();\n\n\t\t// Handle delete confirmation state first - intercept all keys\n\t\tif (this.confirmingDeletePath !== null) {\n\t\t\tif (kb.matches(keyData, \"selectConfirm\")) {\n\t\t\t\tconst pathToDelete = this.confirmingDeletePath;\n\t\t\t\tthis.setConfirmingDeletePath(null);\n\t\t\t\tvoid this.onDeleteSession?.(pathToDelete);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Allow both Escape and Ctrl+C to cancel (consistent with pi UX)\n\t\t\tif (kb.matches(keyData, \"selectCancel\") || matchesKey(keyData, \"ctrl+c\")) {\n\t\t\t\tthis.setConfirmingDeletePath(null);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Ignore all other keys while confirming\n\t\t\treturn;\n\t\t}\n\n\t\tif (kb.matches(keyData, \"tab\")) {\n\t\t\tif (this.onToggleScope) {\n\t\t\t\tthis.onToggleScope();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (kb.matches(keyData, \"toggleSessionSort\")) {\n\t\t\tthis.onToggleSort?.();\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.keybindings.matches(keyData, \"toggleSessionNamedFilter\")) {\n\t\t\tthis.onToggleNameFilter?.();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+P: toggle path display\n\t\tif (kb.matches(keyData, \"toggleSessionPath\")) {\n\t\t\tthis.showPath = !this.showPath;\n\t\t\tthis.onTogglePath?.(this.showPath);\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+D: initiate delete confirmation (useful on terminals that don't distinguish Ctrl+Backspace from Backspace)\n\t\tif (kb.matches(keyData, \"deleteSession\")) {\n\t\t\tthis.startDeleteConfirmationForSelectedSession();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+R: rename selected session\n\t\tif (matchesKey(keyData, \"ctrl+r\")) {\n\t\t\tconst selected = this.filteredSessions[this.selectedIndex];\n\t\t\tif (selected) {\n\t\t\t\tthis.onRenameSession?.(selected.session.path);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+Backspace: non-invasive convenience alias for delete\n\t\t// Only triggers deletion when the query is empty; otherwise it is forwarded to the input\n\t\tif (kb.matches(keyData, \"deleteSessionNoninvasive\")) {\n\t\t\tif (this.searchInput.getValue().length > 0) {\n\t\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\t\tthis.filterSessions(this.searchInput.getValue());\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.startDeleteConfirmationForSelectedSession();\n\t\t\treturn;\n\t\t}\n\n\t\t// Up arrow\n\t\tif (kb.matches(keyData, \"selectUp\")) {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - 1);\n\t\t}\n\t\t// Down arrow\n\t\telse if (kb.matches(keyData, \"selectDown\")) {\n\t\t\tthis.selectedIndex = Math.min(this.filteredSessions.length - 1, this.selectedIndex + 1);\n\t\t}\n\t\t// Page up - jump up by maxVisible items\n\t\telse if (kb.matches(keyData, \"selectPageUp\")) {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - this.maxVisible);\n\t\t}\n\t\t// Page down - jump down by maxVisible items\n\t\telse if (kb.matches(keyData, \"selectPageDown\")) {\n\t\t\tthis.selectedIndex = Math.min(this.filteredSessions.length - 1, this.selectedIndex + this.maxVisible);\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"selectConfirm\")) {\n\t\t\tconst selected = this.filteredSessions[this.selectedIndex];\n\t\t\tif (selected && this.onSelect) {\n\t\t\t\tthis.onSelect(selected.session.path);\n\t\t\t}\n\t\t}\n\t\t// Escape - cancel\n\t\telse if (kb.matches(keyData, \"selectCancel\")) {\n\t\t\tif (this.onCancel) {\n\t\t\t\tthis.onCancel();\n\t\t\t}\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterSessions(this.searchInput.getValue());\n\t\t}\n\t}\n}\n\ntype SessionsLoader = (onProgress?: SessionListProgress) => Promise<SessionInfo[]>;\n\n/**\n * Delete a session file, trying the `trash` CLI first, then falling back to unlink\n */\nasync function deleteSessionFile(\n\tsessionPath: string,\n): Promise<{ ok: boolean; method: \"trash\" | \"unlink\"; error?: string }> {\n\t// Try `trash` first (if installed)\n\tconst trashArgs = sessionPath.startsWith(\"-\") ? [\"--\", sessionPath] : [sessionPath];\n\tconst trashResult = spawnSync(\"trash\", trashArgs, { encoding: \"utf-8\" });\n\n\tconst getTrashErrorHint = (): string | null => {\n\t\tconst parts: string[] = [];\n\t\tif (trashResult.error) {\n\t\t\tparts.push(trashResult.error.message);\n\t\t}\n\t\tconst stderr = trashResult.stderr?.trim();\n\t\tif (stderr) {\n\t\t\tparts.push(stderr.split(\"\\n\")[0] ?? stderr);\n\t\t}\n\t\tif (parts.length === 0) return null;\n\t\treturn `trash: ${parts.join(\" · \").slice(0, 200)}`;\n\t};\n\n\t// If trash reports success, or the file is gone afterwards, treat it as successful\n\tif (trashResult.status === 0 || !existsSync(sessionPath)) {\n\t\treturn { ok: true, method: \"trash\" };\n\t}\n\n\t// Fallback to permanent deletion\n\ttry {\n\t\tawait unlink(sessionPath);\n\t\treturn { ok: true, method: \"unlink\" };\n\t} catch (err) {\n\t\tconst unlinkError = err instanceof Error ? err.message : String(err);\n\t\tconst trashErrorHint = getTrashErrorHint();\n\t\tconst error = trashErrorHint ? `${unlinkError} (${trashErrorHint})` : unlinkError;\n\t\treturn { ok: false, method: \"unlink\", error };\n\t}\n}\n\n/**\n * Component that renders a session selector\n */\nexport class SessionSelectorComponent extends Container implements Focusable {\n\thandleInput(data: string): void {\n\t\tif (this.mode === \"rename\") {\n\t\t\tconst kb = getEditorKeybindings();\n\t\t\tif (kb.matches(data, \"selectCancel\") || matchesKey(data, \"ctrl+c\")) {\n\t\t\t\tthis.exitRenameMode();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.renameInput.handleInput(data);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.sessionList.handleInput(data);\n\t}\n\n\tprivate canRename = true;\n\tprivate sessionList: SessionList;\n\tprivate header: SessionSelectorHeader;\n\tprivate keybindings: KeybindingsManager;\n\tprivate scope: SessionScope = \"current\";\n\tprivate sortMode: SortMode = \"threaded\";\n\tprivate nameFilter: NameFilter = \"all\";\n\tprivate currentSessions: SessionInfo[] | null = null;\n\tprivate allSessions: SessionInfo[] | null = null;\n\tprivate currentSessionsLoader: SessionsLoader;\n\tprivate allSessionsLoader: SessionsLoader;\n\tprivate onCancel: () => void;\n\tprivate requestRender: () => void;\n\tprivate renameSession?: (sessionPath: string, currentName: string | undefined) => Promise<void>;\n\tprivate currentLoading = false;\n\tprivate allLoading = false;\n\tprivate allLoadSeq = 0;\n\n\tprivate mode: \"list\" | \"rename\" = \"list\";\n\tprivate renameInput = new Input();\n\tprivate renameTargetPath: string | null = null;\n\n\t// Focusable implementation - propagate to sessionList for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.sessionList.focused = value;\n\t\tthis.renameInput.focused = value;\n\t\tif (value && this.mode === \"rename\") {\n\t\t\tthis.renameInput.focused = true;\n\t\t}\n\t}\n\n\tprivate buildBaseLayout(content: Component, options?: { showHeader?: boolean }): void {\n\t\tthis.clear();\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder((s) => theme.fg(\"accent\", s)));\n\t\tthis.addChild(new Spacer(1));\n\t\tif (options?.showHeader ?? true) {\n\t\t\tthis.addChild(this.header);\n\t\t\tthis.addChild(new Spacer(1));\n\t\t}\n\t\tthis.addChild(content);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder((s) => theme.fg(\"accent\", s)));\n\t}\n\n\tconstructor(\n\t\tcurrentSessionsLoader: SessionsLoader,\n\t\tallSessionsLoader: SessionsLoader,\n\t\tonSelect: (sessionPath: string) => void,\n\t\tonCancel: () => void,\n\t\tonExit: () => void,\n\t\trequestRender: () => void,\n\t\toptions?: {\n\t\t\trenameSession?: (sessionPath: string, currentName: string | undefined) => Promise<void>;\n\t\t\tshowRenameHint?: boolean;\n\t\t\tkeybindings?: KeybindingsManager;\n\t\t},\n\t\tcurrentSessionFilePath?: string,\n\t) {\n\t\tsuper();\n\t\tthis.keybindings = options?.keybindings ?? KeybindingsManager.create();\n\t\tthis.currentSessionsLoader = currentSessionsLoader;\n\t\tthis.allSessionsLoader = allSessionsLoader;\n\t\tthis.onCancel = onCancel;\n\t\tthis.requestRender = requestRender;\n\t\tthis.header = new SessionSelectorHeader(\n\t\t\tthis.scope,\n\t\t\tthis.sortMode,\n\t\t\tthis.nameFilter,\n\t\t\tthis.keybindings,\n\t\t\tthis.requestRender,\n\t\t);\n\t\tconst renameSession = options?.renameSession;\n\t\tthis.renameSession = renameSession;\n\t\tthis.canRename = !!renameSession;\n\t\tthis.header.setShowRenameHint(options?.showRenameHint ?? this.canRename);\n\n\t\t// Create session list (starts empty, will be populated after load)\n\t\tthis.sessionList = new SessionList(\n\t\t\t[],\n\t\t\tfalse,\n\t\t\tthis.sortMode,\n\t\t\tthis.nameFilter,\n\t\t\tthis.keybindings,\n\t\t\tcurrentSessionFilePath,\n\t\t);\n\n\t\tthis.buildBaseLayout(this.sessionList);\n\n\t\tthis.renameInput.onSubmit = (value) => {\n\t\t\tvoid this.confirmRename(value);\n\t\t};\n\n\t\t// Ensure header status timeouts are cleared when leaving the selector\n\t\tconst clearStatusMessage = () => this.header.setStatusMessage(null);\n\t\tthis.sessionList.onSelect = (sessionPath) => {\n\t\t\tclearStatusMessage();\n\t\t\tonSelect(sessionPath);\n\t\t};\n\t\tthis.sessionList.onCancel = () => {\n\t\t\tclearStatusMessage();\n\t\t\tonCancel();\n\t\t};\n\t\tthis.sessionList.onExit = () => {\n\t\t\tclearStatusMessage();\n\t\t\tonExit();\n\t\t};\n\t\tthis.sessionList.onToggleScope = () => this.toggleScope();\n\t\tthis.sessionList.onToggleSort = () => this.toggleSortMode();\n\t\tthis.sessionList.onToggleNameFilter = () => this.toggleNameFilter();\n\t\tthis.sessionList.onRenameSession = (sessionPath) => {\n\t\t\tif (!renameSession) return;\n\t\t\tif (this.scope === \"current\" && this.currentLoading) return;\n\t\t\tif (this.scope === \"all\" && this.allLoading) return;\n\n\t\t\tconst sessions = this.scope === \"all\" ? (this.allSessions ?? []) : (this.currentSessions ?? []);\n\t\t\tconst session = sessions.find((s) => s.path === sessionPath);\n\t\t\tthis.enterRenameMode(sessionPath, session?.name);\n\t\t};\n\n\t\t// Sync list events to header\n\t\tthis.sessionList.onTogglePath = (showPath) => {\n\t\t\tthis.header.setShowPath(showPath);\n\t\t\tthis.requestRender();\n\t\t};\n\t\tthis.sessionList.onDeleteConfirmationChange = (path) => {\n\t\t\tthis.header.setConfirmingDeletePath(path);\n\t\t\tthis.requestRender();\n\t\t};\n\t\tthis.sessionList.onError = (msg) => {\n\t\t\tthis.header.setStatusMessage({ type: \"error\", message: msg }, 3000);\n\t\t\tthis.requestRender();\n\t\t};\n\n\t\t// Handle session deletion\n\t\tthis.sessionList.onDeleteSession = async (sessionPath: string) => {\n\t\t\tconst result = await deleteSessionFile(sessionPath);\n\n\t\t\tif (result.ok) {\n\t\t\t\tif (this.currentSessions) {\n\t\t\t\t\tthis.currentSessions = this.currentSessions.filter((s) => s.path !== sessionPath);\n\t\t\t\t}\n\t\t\t\tif (this.allSessions) {\n\t\t\t\t\tthis.allSessions = this.allSessions.filter((s) => s.path !== sessionPath);\n\t\t\t\t}\n\n\t\t\t\tconst sessions = this.scope === \"all\" ? (this.allSessions ?? []) : (this.currentSessions ?? []);\n\t\t\t\tconst showCwd = this.scope === \"all\";\n\t\t\t\tthis.sessionList.setSessions(sessions, showCwd);\n\n\t\t\t\tconst msg = result.method === \"trash\" ? \"Session moved to trash\" : \"Session deleted\";\n\t\t\t\tthis.header.setStatusMessage({ type: \"info\", message: msg }, 2000);\n\t\t\t\tawait this.refreshSessionsAfterMutation();\n\t\t\t} else {\n\t\t\t\tconst errorMessage = result.error ?? \"Unknown error\";\n\t\t\t\tthis.header.setStatusMessage({ type: \"error\", message: `Failed to delete: ${errorMessage}` }, 3000);\n\t\t\t}\n\n\t\t\tthis.requestRender();\n\t\t};\n\n\t\t// Start loading current sessions immediately\n\t\tthis.loadCurrentSessions();\n\t}\n\n\tprivate loadCurrentSessions(): void {\n\t\tvoid this.loadScope(\"current\", \"initial\");\n\t}\n\n\tprivate enterRenameMode(sessionPath: string, currentName: string | undefined): void {\n\t\tthis.mode = \"rename\";\n\t\tthis.renameTargetPath = sessionPath;\n\t\tthis.renameInput.setValue(currentName ?? \"\");\n\t\tthis.renameInput.focused = true;\n\n\t\tconst panel = new Container();\n\t\tpanel.addChild(new Text(theme.bold(\"Rename Session\"), 1, 0));\n\t\tpanel.addChild(new Spacer(1));\n\t\tpanel.addChild(this.renameInput);\n\t\tpanel.addChild(new Spacer(1));\n\t\tpanel.addChild(new Text(theme.fg(\"muted\", \"Enter to save · Esc/Ctrl+C to cancel\"), 1, 0));\n\n\t\tthis.buildBaseLayout(panel, { showHeader: false });\n\t\tthis.requestRender();\n\t}\n\n\tprivate exitRenameMode(): void {\n\t\tthis.mode = \"list\";\n\t\tthis.renameTargetPath = null;\n\n\t\tthis.buildBaseLayout(this.sessionList);\n\n\t\tthis.requestRender();\n\t}\n\n\tprivate async confirmRename(value: string): Promise<void> {\n\t\tconst next = value.trim();\n\t\tif (!next) return;\n\t\tconst target = this.renameTargetPath;\n\t\tif (!target) {\n\t\t\tthis.exitRenameMode();\n\t\t\treturn;\n\t\t}\n\n\t\t// Find current name for callback\n\t\tconst renameSession = this.renameSession;\n\t\tif (!renameSession) {\n\t\t\tthis.exitRenameMode();\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tawait renameSession(target, next);\n\t\t\tawait this.refreshSessionsAfterMutation();\n\t\t} finally {\n\t\t\tthis.exitRenameMode();\n\t\t}\n\t}\n\n\tprivate async loadScope(scope: SessionScope, reason: \"initial\" | \"refresh\" | \"toggle\"): Promise<void> {\n\t\tconst showCwd = scope === \"all\";\n\n\t\t// Mark loading\n\t\tif (scope === \"current\") {\n\t\t\tthis.currentLoading = true;\n\t\t} else {\n\t\t\tthis.allLoading = true;\n\t\t}\n\n\t\tconst seq = scope === \"all\" ? ++this.allLoadSeq : undefined;\n\t\tthis.header.setScope(scope);\n\t\tthis.header.setLoading(true);\n\t\tthis.requestRender();\n\n\t\tconst onProgress = (loaded: number, total: number) => {\n\t\t\tif (scope !== this.scope) return;\n\t\t\tif (seq !== undefined && seq !== this.allLoadSeq) return;\n\t\t\tthis.header.setProgress(loaded, total);\n\t\t\tthis.requestRender();\n\t\t};\n\n\t\ttry {\n\t\t\tconst sessions = await (scope === \"current\"\n\t\t\t\t? this.currentSessionsLoader(onProgress)\n\t\t\t\t: this.allSessionsLoader(onProgress));\n\n\t\t\tif (scope === \"current\") {\n\t\t\t\tthis.currentSessions = sessions;\n\t\t\t\tthis.currentLoading = false;\n\t\t\t} else {\n\t\t\t\tthis.allSessions = sessions;\n\t\t\t\tthis.allLoading = false;\n\t\t\t}\n\n\t\t\tif (scope !== this.scope) return;\n\t\t\tif (seq !== undefined && seq !== this.allLoadSeq) return;\n\n\t\t\tthis.header.setLoading(false);\n\t\t\tthis.sessionList.setSessions(sessions, showCwd);\n\t\t\tthis.requestRender();\n\n\t\t\tif (scope === \"all\" && sessions.length === 0 && (this.currentSessions?.length ?? 0) === 0) {\n\t\t\t\tthis.onCancel();\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tif (scope === \"current\") {\n\t\t\t\tthis.currentLoading = false;\n\t\t\t} else {\n\t\t\t\tthis.allLoading = false;\n\t\t\t}\n\n\t\t\tif (scope !== this.scope) return;\n\t\t\tif (seq !== undefined && seq !== this.allLoadSeq) return;\n\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\tthis.header.setLoading(false);\n\t\t\tthis.header.setStatusMessage({ type: \"error\", message: `Failed to load sessions: ${message}` }, 4000);\n\n\t\t\tif (reason === \"initial\") {\n\t\t\t\tthis.sessionList.setSessions([], showCwd);\n\t\t\t}\n\t\t\tthis.requestRender();\n\t\t}\n\t}\n\n\tprivate toggleSortMode(): void {\n\t\t// Cycle: threaded -> recent -> relevance -> threaded\n\t\tthis.sortMode = this.sortMode === \"threaded\" ? \"recent\" : this.sortMode === \"recent\" ? \"relevance\" : \"threaded\";\n\t\tthis.header.setSortMode(this.sortMode);\n\t\tthis.sessionList.setSortMode(this.sortMode);\n\t\tthis.requestRender();\n\t}\n\n\tprivate toggleNameFilter(): void {\n\t\tthis.nameFilter = this.nameFilter === \"all\" ? \"named\" : \"all\";\n\t\tthis.header.setNameFilter(this.nameFilter);\n\t\tthis.sessionList.setNameFilter(this.nameFilter);\n\t\tthis.requestRender();\n\t}\n\n\tprivate async refreshSessionsAfterMutation(): Promise<void> {\n\t\tawait this.loadScope(this.scope, \"refresh\");\n\t}\n\n\tprivate toggleScope(): void {\n\t\tif (this.scope === \"current\") {\n\t\t\tthis.scope = \"all\";\n\t\t\tthis.header.setScope(this.scope);\n\n\t\t\tif (this.allSessions !== null) {\n\t\t\t\tthis.header.setLoading(false);\n\t\t\t\tthis.sessionList.setSessions(this.allSessions, true);\n\t\t\t\tthis.requestRender();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!this.allLoading) {\n\t\t\t\tvoid this.loadScope(\"all\", \"toggle\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tthis.scope = \"current\";\n\t\tthis.header.setScope(this.scope);\n\t\tthis.header.setLoading(this.currentLoading);\n\t\tthis.sessionList.setSessions(this.currentSessions ?? [], false);\n\t\tthis.requestRender();\n\t}\n\n\tgetSessionList(): SessionList {\n\t\treturn this.sessionList;\n\t}\n}\n"]}
|
|
@@ -11,10 +11,16 @@ declare class TreeList implements Component {
|
|
|
11
11
|
private toolCallMap;
|
|
12
12
|
private multipleRoots;
|
|
13
13
|
private activePathIds;
|
|
14
|
+
private lastSelectedId;
|
|
14
15
|
onSelect?: (entryId: string) => void;
|
|
15
16
|
onCancel?: () => void;
|
|
16
17
|
onLabelEdit?: (entryId: string, currentLabel: string | undefined) => void;
|
|
17
18
|
constructor(tree: SessionTreeNode[], currentLeafId: string | null, maxVisibleLines: number, initialSelectedId?: string);
|
|
19
|
+
/**
|
|
20
|
+
* Find the index of the nearest visible entry, walking up the parent chain if needed.
|
|
21
|
+
* Returns the index in filteredNodes, or the last index as fallback.
|
|
22
|
+
*/
|
|
23
|
+
private findNearestVisibleIndex;
|
|
18
24
|
/** Build the set of entry IDs on the path from root to current leaf */
|
|
19
25
|
private buildActivePath;
|
|
20
26
|
private flattenTree;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/tree-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,SAAS,EACd,SAAS,EACT,KAAK,SAAS,EAQd,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAsCxE,cAAM,QAAS,YAAW,SAAS;IAClC,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,aAAa,CAAkB;IACvC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,UAAU,CAAyB;IAC3C,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,WAAW,CAAwC;IAC3D,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,aAAa,CAA0B;IAExC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IAEjF,YACC,IAAI,EAAE,eAAe,EAAE,EACvB,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,eAAe,EAAE,MAAM,EACvB,iBAAiB,CAAC,EAAE,MAAM,EAiB1B;IAED,uEAAuE;IACvE,OAAO,CAAC,eAAe;IAoBvB,OAAO,CAAC,WAAW;IAkInB,OAAO,CAAC,WAAW;IAmFnB;;;;;OAKG;IACH,OAAO,CAAC,0BAA0B;IAyHlC,8CAA8C;IAC9C,OAAO,CAAC,iBAAiB;IAqDzB,UAAU,IAAI,IAAI,CAAG;IAErB,cAAc,IAAI,MAAM,CAEvB;IAED,eAAe,IAAI,eAAe,GAAG,SAAS,CAE7C;IAED,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAOhE;IAED,OAAO,CAAC,cAAc;IAetB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAuF9B;IAED,OAAO,CAAC,mBAAmB;IAiF3B,OAAO,CAAC,cAAc;IAgBtB,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,cAAc;IA0DtB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CA4EjC;CACD;AAuED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,SAAU,YAAW,SAAS;IACxE,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,mBAAmB,CAAY;IACvC,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,qBAAqB,CAAC,CAAuD;IAGrF,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAMzB;IAED,YACC,IAAI,EAAE,eAAe,EAAE,EACvB,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,EACnC,QAAQ,EAAE,MAAM,IAAI,EACpB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,EACpE,iBAAiB,CAAC,EAAE,MAAM,EAuC1B;IAED,OAAO,CAAC,cAAc;IAiBtB,OAAO,CAAC,cAAc;IAOtB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAMjC;IAED,WAAW,IAAI,QAAQ,CAEtB;CACD","sourcesContent":["import {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tgetEditorKeybindings,\n\tInput,\n\tmatchesKey,\n\tSpacer,\n\tText,\n\tTruncatedText,\n\ttruncateToWidth,\n} from \"@mariozechner/pi-tui\";\nimport type { SessionTreeNode } from \"../../../core/session-manager.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { keyHint } from \"./keybinding-hints.js\";\n\n/** Gutter info: position (displayIndent where connector was) and whether to show │ */\ninterface GutterInfo {\n\tposition: number; // displayIndent level where the connector was shown\n\tshow: boolean; // true = show │, false = show spaces\n}\n\n/** Flattened tree node for navigation */\ninterface FlatNode {\n\tnode: SessionTreeNode;\n\t/** Indentation level (each level = 3 chars) */\n\tindent: number;\n\t/** Whether to show connector (├─ or └─) - true if parent has multiple children */\n\tshowConnector: boolean;\n\t/** If showConnector, true = last sibling (└─), false = not last (├─) */\n\tisLast: boolean;\n\t/** Gutter info for each ancestor branch point */\n\tgutters: GutterInfo[];\n\t/** True if this node is a root under a virtual branching root (multiple roots) */\n\tisVirtualRootChild: boolean;\n}\n\n/** Filter mode for tree display */\ntype FilterMode = \"default\" | \"no-tools\" | \"user-only\" | \"labeled-only\" | \"all\";\n\n/**\n * Tree list component with selection and ASCII art visualization\n */\n/** Tool call info for lookup */\ninterface ToolCallInfo {\n\tname: string;\n\targuments: Record<string, unknown>;\n}\n\nclass TreeList implements Component {\n\tprivate flatNodes: FlatNode[] = [];\n\tprivate filteredNodes: FlatNode[] = [];\n\tprivate selectedIndex = 0;\n\tprivate currentLeafId: string | null;\n\tprivate maxVisibleLines: number;\n\tprivate filterMode: FilterMode = \"default\";\n\tprivate searchQuery = \"\";\n\tprivate toolCallMap: Map<string, ToolCallInfo> = new Map();\n\tprivate multipleRoots = false;\n\tprivate activePathIds: Set<string> = new Set();\n\n\tpublic onSelect?: (entryId: string) => void;\n\tpublic onCancel?: () => void;\n\tpublic onLabelEdit?: (entryId: string, currentLabel: string | undefined) => void;\n\n\tconstructor(\n\t\ttree: SessionTreeNode[],\n\t\tcurrentLeafId: string | null,\n\t\tmaxVisibleLines: number,\n\t\tinitialSelectedId?: string,\n\t) {\n\t\tthis.currentLeafId = currentLeafId;\n\t\tthis.maxVisibleLines = maxVisibleLines;\n\t\tthis.multipleRoots = tree.length > 1;\n\t\tthis.flatNodes = this.flattenTree(tree);\n\t\tthis.buildActivePath();\n\t\tthis.applyFilter();\n\n\t\t// Start with initialSelectedId if provided, otherwise current leaf\n\t\tconst targetId = initialSelectedId ?? currentLeafId;\n\t\tconst targetIndex = this.filteredNodes.findIndex((n) => n.node.entry.id === targetId);\n\t\tif (targetIndex !== -1) {\n\t\t\tthis.selectedIndex = targetIndex;\n\t\t} else {\n\t\t\tthis.selectedIndex = Math.max(0, this.filteredNodes.length - 1);\n\t\t}\n\t}\n\n\t/** Build the set of entry IDs on the path from root to current leaf */\n\tprivate buildActivePath(): void {\n\t\tthis.activePathIds.clear();\n\t\tif (!this.currentLeafId) return;\n\n\t\t// Build a map of id -> entry for parent lookup\n\t\tconst entryMap = new Map<string, FlatNode>();\n\t\tfor (const flatNode of this.flatNodes) {\n\t\t\tentryMap.set(flatNode.node.entry.id, flatNode);\n\t\t}\n\n\t\t// Walk from leaf to root\n\t\tlet currentId: string | null = this.currentLeafId;\n\t\twhile (currentId) {\n\t\t\tthis.activePathIds.add(currentId);\n\t\t\tconst node = entryMap.get(currentId);\n\t\t\tif (!node) break;\n\t\t\tcurrentId = node.node.entry.parentId ?? null;\n\t\t}\n\t}\n\n\tprivate flattenTree(roots: SessionTreeNode[]): FlatNode[] {\n\t\tconst result: FlatNode[] = [];\n\t\tthis.toolCallMap.clear();\n\n\t\t// Indentation rules:\n\t\t// - At indent 0: stay at 0 unless parent has >1 children (then +1)\n\t\t// - At indent 1: children always go to indent 2 (visual grouping of subtree)\n\t\t// - At indent 2+: stay flat for single-child chains, +1 only if parent branches\n\n\t\t// Stack items: [node, indent, justBranched, showConnector, isLast, gutters, isVirtualRootChild]\n\t\ttype StackItem = [SessionTreeNode, number, boolean, boolean, boolean, GutterInfo[], boolean];\n\t\tconst stack: StackItem[] = [];\n\n\t\t// Determine which subtrees contain the active leaf (to sort current branch first)\n\t\t// Use iterative post-order traversal to avoid stack overflow\n\t\tconst containsActive = new Map<SessionTreeNode, boolean>();\n\t\tconst leafId = this.currentLeafId;\n\t\t{\n\t\t\t// Build list in pre-order, then process in reverse for post-order effect\n\t\t\tconst allNodes: SessionTreeNode[] = [];\n\t\t\tconst preOrderStack: SessionTreeNode[] = [...roots];\n\t\t\twhile (preOrderStack.length > 0) {\n\t\t\t\tconst node = preOrderStack.pop()!;\n\t\t\t\tallNodes.push(node);\n\t\t\t\t// Push children in reverse so they're processed left-to-right\n\t\t\t\tfor (let i = node.children.length - 1; i >= 0; i--) {\n\t\t\t\t\tpreOrderStack.push(node.children[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Process in reverse (post-order): children before parents\n\t\t\tfor (let i = allNodes.length - 1; i >= 0; i--) {\n\t\t\t\tconst node = allNodes[i];\n\t\t\t\tlet has = leafId !== null && node.entry.id === leafId;\n\t\t\t\tfor (const child of node.children) {\n\t\t\t\t\tif (containsActive.get(child)) {\n\t\t\t\t\t\thas = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcontainsActive.set(node, has);\n\t\t\t}\n\t\t}\n\n\t\t// Add roots in reverse order, prioritizing the one containing the active leaf\n\t\t// If multiple roots, treat them as children of a virtual root that branches\n\t\tconst multipleRoots = roots.length > 1;\n\t\tconst orderedRoots = [...roots].sort((a, b) => Number(containsActive.get(b)) - Number(containsActive.get(a)));\n\t\tfor (let i = orderedRoots.length - 1; i >= 0; i--) {\n\t\t\tconst isLast = i === orderedRoots.length - 1;\n\t\t\tstack.push([orderedRoots[i], multipleRoots ? 1 : 0, multipleRoots, multipleRoots, isLast, [], multipleRoots]);\n\t\t}\n\n\t\twhile (stack.length > 0) {\n\t\t\tconst [node, indent, justBranched, showConnector, isLast, gutters, isVirtualRootChild] = stack.pop()!;\n\n\t\t\t// Extract tool calls from assistant messages for later lookup\n\t\t\tconst entry = node.entry;\n\t\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\t\tconst content = (entry.message as { content?: unknown }).content;\n\t\t\t\tif (Array.isArray(content)) {\n\t\t\t\t\tfor (const block of content) {\n\t\t\t\t\t\tif (typeof block === \"object\" && block !== null && \"type\" in block && block.type === \"toolCall\") {\n\t\t\t\t\t\t\tconst tc = block as { id: string; name: string; arguments: Record<string, unknown> };\n\t\t\t\t\t\t\tthis.toolCallMap.set(tc.id, { name: tc.name, arguments: tc.arguments });\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tresult.push({ node, indent, showConnector, isLast, gutters, isVirtualRootChild });\n\n\t\t\tconst children = node.children;\n\t\t\tconst multipleChildren = children.length > 1;\n\n\t\t\t// Order children so the branch containing the active leaf comes first\n\t\t\tconst orderedChildren = (() => {\n\t\t\t\tconst prioritized: SessionTreeNode[] = [];\n\t\t\t\tconst rest: SessionTreeNode[] = [];\n\t\t\t\tfor (const child of children) {\n\t\t\t\t\tif (containsActive.get(child)) {\n\t\t\t\t\t\tprioritized.push(child);\n\t\t\t\t\t} else {\n\t\t\t\t\t\trest.push(child);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn [...prioritized, ...rest];\n\t\t\t})();\n\n\t\t\t// Calculate child indent\n\t\t\tlet childIndent: number;\n\t\t\tif (multipleChildren) {\n\t\t\t\t// Parent branches: children get +1\n\t\t\t\tchildIndent = indent + 1;\n\t\t\t} else if (justBranched && indent > 0) {\n\t\t\t\t// First generation after a branch: +1 for visual grouping\n\t\t\t\tchildIndent = indent + 1;\n\t\t\t} else {\n\t\t\t\t// Single-child chain: stay flat\n\t\t\t\tchildIndent = indent;\n\t\t\t}\n\n\t\t\t// Build gutters for children\n\t\t\t// If this node showed a connector, add a gutter entry for descendants\n\t\t\t// Only add gutter if connector is actually displayed (not suppressed for virtual root children)\n\t\t\tconst connectorDisplayed = showConnector && !isVirtualRootChild;\n\t\t\t// When connector is displayed, add a gutter entry at the connector's position\n\t\t\t// Connector is at position (displayIndent - 1), so gutter should be there too\n\t\t\tconst currentDisplayIndent = this.multipleRoots ? Math.max(0, indent - 1) : indent;\n\t\t\tconst connectorPosition = Math.max(0, currentDisplayIndent - 1);\n\t\t\tconst childGutters: GutterInfo[] = connectorDisplayed\n\t\t\t\t? [...gutters, { position: connectorPosition, show: !isLast }]\n\t\t\t\t: gutters;\n\n\t\t\t// Add children in reverse order\n\t\t\tfor (let i = orderedChildren.length - 1; i >= 0; i--) {\n\t\t\t\tconst childIsLast = i === orderedChildren.length - 1;\n\t\t\t\tstack.push([\n\t\t\t\t\torderedChildren[i],\n\t\t\t\t\tchildIndent,\n\t\t\t\t\tmultipleChildren,\n\t\t\t\t\tmultipleChildren,\n\t\t\t\t\tchildIsLast,\n\t\t\t\t\tchildGutters,\n\t\t\t\t\tfalse,\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate applyFilter(): void {\n\t\t// Remember currently selected node to preserve cursor position\n\t\tconst previouslySelectedId = this.filteredNodes[this.selectedIndex]?.node.entry.id;\n\n\t\tconst searchTokens = this.searchQuery.toLowerCase().split(/\\s+/).filter(Boolean);\n\n\t\tthis.filteredNodes = this.flatNodes.filter((flatNode) => {\n\t\t\tconst entry = flatNode.node.entry;\n\t\t\tconst isCurrentLeaf = entry.id === this.currentLeafId;\n\n\t\t\t// Skip assistant messages with only tool calls (no text) unless error/aborted\n\t\t\t// Always show current leaf so active position is visible\n\t\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\" && !isCurrentLeaf) {\n\t\t\t\tconst msg = entry.message as { stopReason?: string; content?: unknown };\n\t\t\t\tconst hasText = this.hasTextContent(msg.content);\n\t\t\t\tconst isErrorOrAborted = msg.stopReason && msg.stopReason !== \"stop\" && msg.stopReason !== \"toolUse\";\n\t\t\t\t// Only hide if no text AND not an error/aborted message\n\t\t\t\tif (!hasText && !isErrorOrAborted) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Apply filter mode\n\t\t\tlet passesFilter = true;\n\t\t\t// Entry types hidden in default view (settings/bookkeeping)\n\t\t\tconst isSettingsEntry =\n\t\t\t\tentry.type === \"label\" ||\n\t\t\t\tentry.type === \"custom\" ||\n\t\t\t\tentry.type === \"model_change\" ||\n\t\t\t\tentry.type === \"thinking_level_change\";\n\n\t\t\tswitch (this.filterMode) {\n\t\t\t\tcase \"user-only\":\n\t\t\t\t\t// Just user messages\n\t\t\t\t\tpassesFilter = entry.type === \"message\" && entry.message.role === \"user\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"no-tools\":\n\t\t\t\t\t// Default minus tool results\n\t\t\t\t\tpassesFilter = !isSettingsEntry && !(entry.type === \"message\" && entry.message.role === \"toolResult\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"labeled-only\":\n\t\t\t\t\t// Just labeled entries\n\t\t\t\t\tpassesFilter = flatNode.node.label !== undefined;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"all\":\n\t\t\t\t\t// Show everything\n\t\t\t\t\tpassesFilter = true;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t// Default mode: hide settings/bookkeeping entries\n\t\t\t\t\tpassesFilter = !isSettingsEntry;\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (!passesFilter) return false;\n\n\t\t\t// Apply search filter\n\t\t\tif (searchTokens.length > 0) {\n\t\t\t\tconst nodeText = this.getSearchableText(flatNode.node).toLowerCase();\n\t\t\t\treturn searchTokens.every((token) => nodeText.includes(token));\n\t\t\t}\n\n\t\t\treturn true;\n\t\t});\n\n\t\t// Recalculate visual structure (indent, connectors, gutters) based on visible tree\n\t\tthis.recalculateVisualStructure();\n\n\t\t// Try to preserve cursor on the same node after filtering\n\t\tif (previouslySelectedId) {\n\t\t\tconst newIndex = this.filteredNodes.findIndex((n) => n.node.entry.id === previouslySelectedId);\n\t\t\tif (newIndex !== -1) {\n\t\t\t\tthis.selectedIndex = newIndex;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Fall back: clamp index if out of bounds\n\t\tif (this.selectedIndex >= this.filteredNodes.length) {\n\t\t\tthis.selectedIndex = Math.max(0, this.filteredNodes.length - 1);\n\t\t}\n\t}\n\n\t/**\n\t * Recompute indentation/connectors for the filtered view\n\t *\n\t * Filtering can hide intermediate entries; descendants attach to the nearest visible ancestor.\n\t * Keep indentation semantics aligned with flattenTree() so single-child chains don't drift right.\n\t */\n\tprivate recalculateVisualStructure(): void {\n\t\tif (this.filteredNodes.length === 0) return;\n\n\t\tconst visibleIds = new Set(this.filteredNodes.map((n) => n.node.entry.id));\n\n\t\t// Build entry map for efficient parent lookup (using full tree)\n\t\tconst entryMap = new Map<string, FlatNode>();\n\t\tfor (const flatNode of this.flatNodes) {\n\t\t\tentryMap.set(flatNode.node.entry.id, flatNode);\n\t\t}\n\n\t\t// Find nearest visible ancestor for a node\n\t\tconst findVisibleAncestor = (nodeId: string): string | null => {\n\t\t\tlet currentId = entryMap.get(nodeId)?.node.entry.parentId ?? null;\n\t\t\twhile (currentId !== null) {\n\t\t\t\tif (visibleIds.has(currentId)) {\n\t\t\t\t\treturn currentId;\n\t\t\t\t}\n\t\t\t\tcurrentId = entryMap.get(currentId)?.node.entry.parentId ?? null;\n\t\t\t}\n\t\t\treturn null;\n\t\t};\n\n\t\t// Build visible tree structure:\n\t\t// - visibleParent: nodeId → nearest visible ancestor (or null for roots)\n\t\t// - visibleChildren: parentId → list of visible children (in filteredNodes order)\n\t\tconst visibleParent = new Map<string, string | null>();\n\t\tconst visibleChildren = new Map<string | null, string[]>();\n\t\tvisibleChildren.set(null, []); // root-level nodes\n\n\t\tfor (const flatNode of this.filteredNodes) {\n\t\t\tconst nodeId = flatNode.node.entry.id;\n\t\t\tconst ancestorId = findVisibleAncestor(nodeId);\n\t\t\tvisibleParent.set(nodeId, ancestorId);\n\n\t\t\tif (!visibleChildren.has(ancestorId)) {\n\t\t\t\tvisibleChildren.set(ancestorId, []);\n\t\t\t}\n\t\t\tvisibleChildren.get(ancestorId)!.push(nodeId);\n\t\t}\n\n\t\t// Update multipleRoots based on visible roots\n\t\tconst visibleRootIds = visibleChildren.get(null)!;\n\t\tthis.multipleRoots = visibleRootIds.length > 1;\n\n\t\t// Build a map for quick lookup: nodeId → FlatNode\n\t\tconst filteredNodeMap = new Map<string, FlatNode>();\n\t\tfor (const flatNode of this.filteredNodes) {\n\t\t\tfilteredNodeMap.set(flatNode.node.entry.id, flatNode);\n\t\t}\n\n\t\t// DFS over the visible tree using flattenTree() indentation semantics\n\t\t// Stack items: [nodeId, indent, justBranched, showConnector, isLast, gutters, isVirtualRootChild]\n\t\ttype StackItem = [string, number, boolean, boolean, boolean, GutterInfo[], boolean];\n\t\tconst stack: StackItem[] = [];\n\n\t\t// Add visible roots in reverse order (to process in forward order via stack)\n\t\tfor (let i = visibleRootIds.length - 1; i >= 0; i--) {\n\t\t\tconst isLast = i === visibleRootIds.length - 1;\n\t\t\tstack.push([\n\t\t\t\tvisibleRootIds[i],\n\t\t\t\tthis.multipleRoots ? 1 : 0,\n\t\t\t\tthis.multipleRoots,\n\t\t\t\tthis.multipleRoots,\n\t\t\t\tisLast,\n\t\t\t\t[],\n\t\t\t\tthis.multipleRoots,\n\t\t\t]);\n\t\t}\n\n\t\twhile (stack.length > 0) {\n\t\t\tconst [nodeId, indent, justBranched, showConnector, isLast, gutters, isVirtualRootChild] = stack.pop()!;\n\n\t\t\tconst flatNode = filteredNodeMap.get(nodeId);\n\t\t\tif (!flatNode) continue;\n\n\t\t\t// Update this node's visual properties\n\t\t\tflatNode.indent = indent;\n\t\t\tflatNode.showConnector = showConnector;\n\t\t\tflatNode.isLast = isLast;\n\t\t\tflatNode.gutters = gutters;\n\t\t\tflatNode.isVirtualRootChild = isVirtualRootChild;\n\n\t\t\t// Get visible children of this node\n\t\t\tconst children = visibleChildren.get(nodeId) || [];\n\t\t\tconst multipleChildren = children.length > 1;\n\n\t\t\t// Child indent follows flattenTree(): branch points (and first generation after a branch) shift +1\n\t\t\tlet childIndent: number;\n\t\t\tif (multipleChildren) {\n\t\t\t\tchildIndent = indent + 1;\n\t\t\t} else if (justBranched && indent > 0) {\n\t\t\t\tchildIndent = indent + 1;\n\t\t\t} else {\n\t\t\t\tchildIndent = indent;\n\t\t\t}\n\n\t\t\t// Child gutters follow flattenTree() connector/gutter rules\n\t\t\tconst connectorDisplayed = showConnector && !isVirtualRootChild;\n\t\t\tconst currentDisplayIndent = this.multipleRoots ? Math.max(0, indent - 1) : indent;\n\t\t\tconst connectorPosition = Math.max(0, currentDisplayIndent - 1);\n\t\t\tconst childGutters: GutterInfo[] = connectorDisplayed\n\t\t\t\t? [...gutters, { position: connectorPosition, show: !isLast }]\n\t\t\t\t: gutters;\n\n\t\t\t// Add children in reverse order (to process in forward order via stack)\n\t\t\tfor (let i = children.length - 1; i >= 0; i--) {\n\t\t\t\tconst childIsLast = i === children.length - 1;\n\t\t\t\tstack.push([\n\t\t\t\t\tchildren[i],\n\t\t\t\t\tchildIndent,\n\t\t\t\t\tmultipleChildren,\n\t\t\t\t\tmultipleChildren,\n\t\t\t\t\tchildIsLast,\n\t\t\t\t\tchildGutters,\n\t\t\t\t\tfalse,\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Get searchable text content from a node */\n\tprivate getSearchableText(node: SessionTreeNode): string {\n\t\tconst entry = node.entry;\n\t\tconst parts: string[] = [];\n\n\t\tif (node.label) {\n\t\t\tparts.push(node.label);\n\t\t}\n\n\t\tswitch (entry.type) {\n\t\t\tcase \"message\": {\n\t\t\t\tconst msg = entry.message;\n\t\t\t\tparts.push(msg.role);\n\t\t\t\tif (\"content\" in msg && msg.content) {\n\t\t\t\t\tparts.push(this.extractContent(msg.content));\n\t\t\t\t}\n\t\t\t\tif (msg.role === \"bashExecution\") {\n\t\t\t\t\tconst bashMsg = msg as { command?: string };\n\t\t\t\t\tif (bashMsg.command) parts.push(bashMsg.command);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"custom_message\": {\n\t\t\t\tparts.push(entry.customType);\n\t\t\t\tif (typeof entry.content === \"string\") {\n\t\t\t\t\tparts.push(entry.content);\n\t\t\t\t} else {\n\t\t\t\t\tparts.push(this.extractContent(entry.content));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"compaction\":\n\t\t\t\tparts.push(\"compaction\");\n\t\t\t\tbreak;\n\t\t\tcase \"branch_summary\":\n\t\t\t\tparts.push(\"branch summary\", entry.summary);\n\t\t\t\tbreak;\n\t\t\tcase \"model_change\":\n\t\t\t\tparts.push(\"model\", entry.modelId);\n\t\t\t\tbreak;\n\t\t\tcase \"thinking_level_change\":\n\t\t\t\tparts.push(\"thinking\", entry.thinkingLevel);\n\t\t\t\tbreak;\n\t\t\tcase \"custom\":\n\t\t\t\tparts.push(\"custom\", entry.customType);\n\t\t\t\tbreak;\n\t\t\tcase \"label\":\n\t\t\t\tparts.push(\"label\", entry.label ?? \"\");\n\t\t\t\tbreak;\n\t\t}\n\n\t\treturn parts.join(\" \");\n\t}\n\n\tinvalidate(): void {}\n\n\tgetSearchQuery(): string {\n\t\treturn this.searchQuery;\n\t}\n\n\tgetSelectedNode(): SessionTreeNode | undefined {\n\t\treturn this.filteredNodes[this.selectedIndex]?.node;\n\t}\n\n\tupdateNodeLabel(entryId: string, label: string | undefined): void {\n\t\tfor (const flatNode of this.flatNodes) {\n\t\t\tif (flatNode.node.entry.id === entryId) {\n\t\t\t\tflatNode.node.label = label;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate getFilterLabel(): string {\n\t\tswitch (this.filterMode) {\n\t\t\tcase \"no-tools\":\n\t\t\t\treturn \" [no-tools]\";\n\t\t\tcase \"user-only\":\n\t\t\t\treturn \" [user]\";\n\t\t\tcase \"labeled-only\":\n\t\t\t\treturn \" [labeled]\";\n\t\t\tcase \"all\":\n\t\t\t\treturn \" [all]\";\n\t\t\tdefault:\n\t\t\t\treturn \"\";\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\n\t\tif (this.filteredNodes.length === 0) {\n\t\t\tlines.push(truncateToWidth(theme.fg(\"muted\", \" No entries found\"), width));\n\t\t\tlines.push(truncateToWidth(theme.fg(\"muted\", ` (0/0)${this.getFilterLabel()}`), width));\n\t\t\treturn lines;\n\t\t}\n\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(\n\t\t\t\tthis.selectedIndex - Math.floor(this.maxVisibleLines / 2),\n\t\t\t\tthis.filteredNodes.length - this.maxVisibleLines,\n\t\t\t),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisibleLines, this.filteredNodes.length);\n\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst flatNode = this.filteredNodes[i];\n\t\t\tconst entry = flatNode.node.entry;\n\t\t\tconst isSelected = i === this.selectedIndex;\n\n\t\t\t// Build line: cursor + prefix + path marker + label + content\n\t\t\tconst cursor = isSelected ? theme.fg(\"accent\", \"› \") : \" \";\n\n\t\t\t// If multiple roots, shift display (roots at 0, not 1)\n\t\t\tconst displayIndent = this.multipleRoots ? Math.max(0, flatNode.indent - 1) : flatNode.indent;\n\n\t\t\t// Build prefix with gutters at their correct positions\n\t\t\t// Each gutter has a position (displayIndent where its connector was shown)\n\t\t\tconst connector =\n\t\t\t\tflatNode.showConnector && !flatNode.isVirtualRootChild ? (flatNode.isLast ? \"└─ \" : \"├─ \") : \"\";\n\t\t\tconst connectorPosition = connector ? displayIndent - 1 : -1;\n\n\t\t\t// Build prefix char by char, placing gutters and connector at their positions\n\t\t\tconst totalChars = displayIndent * 3;\n\t\t\tconst prefixChars: string[] = [];\n\t\t\tfor (let i = 0; i < totalChars; i++) {\n\t\t\t\tconst level = Math.floor(i / 3);\n\t\t\t\tconst posInLevel = i % 3;\n\n\t\t\t\t// Check if there's a gutter at this level\n\t\t\t\tconst gutter = flatNode.gutters.find((g) => g.position === level);\n\t\t\t\tif (gutter) {\n\t\t\t\t\tif (posInLevel === 0) {\n\t\t\t\t\t\tprefixChars.push(gutter.show ? \"│\" : \" \");\n\t\t\t\t\t} else {\n\t\t\t\t\t\tprefixChars.push(\" \");\n\t\t\t\t\t}\n\t\t\t\t} else if (connector && level === connectorPosition) {\n\t\t\t\t\t// Connector at this level\n\t\t\t\t\tif (posInLevel === 0) {\n\t\t\t\t\t\tprefixChars.push(flatNode.isLast ? \"└\" : \"├\");\n\t\t\t\t\t} else if (posInLevel === 1) {\n\t\t\t\t\t\tprefixChars.push(\"─\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\tprefixChars.push(\" \");\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tprefixChars.push(\" \");\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst prefix = prefixChars.join(\"\");\n\n\t\t\t// Active path marker - shown right before the entry text\n\t\t\tconst isOnActivePath = this.activePathIds.has(entry.id);\n\t\t\tconst pathMarker = isOnActivePath ? theme.fg(\"accent\", \"• \") : \"\";\n\n\t\t\tconst label = flatNode.node.label ? theme.fg(\"warning\", `[${flatNode.node.label}] `) : \"\";\n\t\t\tconst content = this.getEntryDisplayText(flatNode.node, isSelected);\n\n\t\t\tlet line = cursor + theme.fg(\"dim\", prefix) + pathMarker + label + content;\n\t\t\tif (isSelected) {\n\t\t\t\tline = theme.bg(\"selectedBg\", line);\n\t\t\t}\n\t\t\tlines.push(truncateToWidth(line, width));\n\t\t}\n\n\t\tlines.push(\n\t\t\ttruncateToWidth(\n\t\t\t\ttheme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredNodes.length})${this.getFilterLabel()}`),\n\t\t\t\twidth,\n\t\t\t),\n\t\t);\n\n\t\treturn lines;\n\t}\n\n\tprivate getEntryDisplayText(node: SessionTreeNode, isSelected: boolean): string {\n\t\tconst entry = node.entry;\n\t\tlet result: string;\n\n\t\tconst normalize = (s: string) => s.replace(/[\\n\\t]/g, \" \").trim();\n\n\t\tswitch (entry.type) {\n\t\t\tcase \"message\": {\n\t\t\t\tconst msg = entry.message;\n\t\t\t\tconst role = msg.role;\n\t\t\t\tif (role === \"user\") {\n\t\t\t\t\tconst msgWithContent = msg as { content?: unknown };\n\t\t\t\t\tconst content = normalize(this.extractContent(msgWithContent.content));\n\t\t\t\t\tresult = theme.fg(\"accent\", \"user: \") + content;\n\t\t\t\t} else if (role === \"assistant\") {\n\t\t\t\t\tconst msgWithContent = msg as { content?: unknown; stopReason?: string; errorMessage?: string };\n\t\t\t\t\tconst textContent = normalize(this.extractContent(msgWithContent.content));\n\t\t\t\t\tif (textContent) {\n\t\t\t\t\t\tresult = theme.fg(\"success\", \"assistant: \") + textContent;\n\t\t\t\t\t} else if (msgWithContent.stopReason === \"aborted\") {\n\t\t\t\t\t\tresult = theme.fg(\"success\", \"assistant: \") + theme.fg(\"muted\", \"(aborted)\");\n\t\t\t\t\t} else if (msgWithContent.errorMessage) {\n\t\t\t\t\t\tconst errMsg = normalize(msgWithContent.errorMessage).slice(0, 80);\n\t\t\t\t\t\tresult = theme.fg(\"success\", \"assistant: \") + theme.fg(\"error\", errMsg);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult = theme.fg(\"success\", \"assistant: \") + theme.fg(\"muted\", \"(no content)\");\n\t\t\t\t\t}\n\t\t\t\t} else if (role === \"toolResult\") {\n\t\t\t\t\tconst toolMsg = msg as { toolCallId?: string; toolName?: string };\n\t\t\t\t\tconst toolCall = toolMsg.toolCallId ? this.toolCallMap.get(toolMsg.toolCallId) : undefined;\n\t\t\t\t\tif (toolCall) {\n\t\t\t\t\t\tresult = theme.fg(\"muted\", this.formatToolCall(toolCall.name, toolCall.arguments));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult = theme.fg(\"muted\", `[${toolMsg.toolName ?? \"tool\"}]`);\n\t\t\t\t\t}\n\t\t\t\t} else if (role === \"bashExecution\") {\n\t\t\t\t\tconst bashMsg = msg as { command?: string };\n\t\t\t\t\tresult = theme.fg(\"dim\", `[bash]: ${normalize(bashMsg.command ?? \"\")}`);\n\t\t\t\t} else {\n\t\t\t\t\tresult = theme.fg(\"dim\", `[${role}]`);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"custom_message\": {\n\t\t\t\tconst content =\n\t\t\t\t\ttypeof entry.content === \"string\"\n\t\t\t\t\t\t? entry.content\n\t\t\t\t\t\t: entry.content\n\t\t\t\t\t\t\t\t.filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n\t\t\t\t\t\t\t\t.map((c) => c.text)\n\t\t\t\t\t\t\t\t.join(\"\");\n\t\t\t\tresult = theme.fg(\"customMessageLabel\", `[${entry.customType}]: `) + normalize(content);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"compaction\": {\n\t\t\t\tconst tokens = Math.round(entry.tokensBefore / 1000);\n\t\t\t\tresult = theme.fg(\"borderAccent\", `[compaction: ${tokens}k tokens]`);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"branch_summary\":\n\t\t\t\tresult = theme.fg(\"warning\", `[branch summary]: `) + normalize(entry.summary);\n\t\t\t\tbreak;\n\t\t\tcase \"model_change\":\n\t\t\t\tresult = theme.fg(\"dim\", `[model: ${entry.modelId}]`);\n\t\t\t\tbreak;\n\t\t\tcase \"thinking_level_change\":\n\t\t\t\tresult = theme.fg(\"dim\", `[thinking: ${entry.thinkingLevel}]`);\n\t\t\t\tbreak;\n\t\t\tcase \"custom\":\n\t\t\t\tresult = theme.fg(\"dim\", `[custom: ${entry.customType}]`);\n\t\t\t\tbreak;\n\t\t\tcase \"label\":\n\t\t\t\tresult = theme.fg(\"dim\", `[label: ${entry.label ?? \"(cleared)\"}]`);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tresult = \"\";\n\t\t}\n\n\t\treturn isSelected ? theme.bold(result) : result;\n\t}\n\n\tprivate extractContent(content: unknown): string {\n\t\tconst maxLen = 200;\n\t\tif (typeof content === \"string\") return content.slice(0, maxLen);\n\t\tif (Array.isArray(content)) {\n\t\t\tlet result = \"\";\n\t\t\tfor (const c of content) {\n\t\t\t\tif (typeof c === \"object\" && c !== null && \"type\" in c && c.type === \"text\") {\n\t\t\t\t\tresult += (c as { text: string }).text;\n\t\t\t\t\tif (result.length >= maxLen) return result.slice(0, maxLen);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\t\treturn \"\";\n\t}\n\n\tprivate hasTextContent(content: unknown): boolean {\n\t\tif (typeof content === \"string\") return content.trim().length > 0;\n\t\tif (Array.isArray(content)) {\n\t\t\tfor (const c of content) {\n\t\t\t\tif (typeof c === \"object\" && c !== null && \"type\" in c && c.type === \"text\") {\n\t\t\t\t\tconst text = (c as { text?: string }).text;\n\t\t\t\t\tif (text && text.trim().length > 0) return true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate formatToolCall(name: string, args: Record<string, unknown>): string {\n\t\tconst shortenPath = (p: string): string => {\n\t\t\tconst home = process.env.HOME || process.env.USERPROFILE || \"\";\n\t\t\tif (home && p.startsWith(home)) return `~${p.slice(home.length)}`;\n\t\t\treturn p;\n\t\t};\n\n\t\tswitch (name) {\n\t\t\tcase \"read\": {\n\t\t\t\tconst path = shortenPath(String(args.path || args.file_path || \"\"));\n\t\t\t\tconst offset = args.offset as number | undefined;\n\t\t\t\tconst limit = args.limit as number | undefined;\n\t\t\t\tlet display = path;\n\t\t\t\tif (offset !== undefined || limit !== undefined) {\n\t\t\t\t\tconst start = offset ?? 1;\n\t\t\t\t\tconst end = limit !== undefined ? start + limit - 1 : \"\";\n\t\t\t\t\tdisplay += `:${start}${end ? `-${end}` : \"\"}`;\n\t\t\t\t}\n\t\t\t\treturn `[read: ${display}]`;\n\t\t\t}\n\t\t\tcase \"write\": {\n\t\t\t\tconst path = shortenPath(String(args.path || args.file_path || \"\"));\n\t\t\t\treturn `[write: ${path}]`;\n\t\t\t}\n\t\t\tcase \"edit\": {\n\t\t\t\tconst path = shortenPath(String(args.path || args.file_path || \"\"));\n\t\t\t\treturn `[edit: ${path}]`;\n\t\t\t}\n\t\t\tcase \"bash\": {\n\t\t\t\tconst rawCmd = String(args.command || \"\");\n\t\t\t\tconst cmd = rawCmd\n\t\t\t\t\t.replace(/[\\n\\t]/g, \" \")\n\t\t\t\t\t.trim()\n\t\t\t\t\t.slice(0, 50);\n\t\t\t\treturn `[bash: ${cmd}${rawCmd.length > 50 ? \"...\" : \"\"}]`;\n\t\t\t}\n\t\t\tcase \"grep\": {\n\t\t\t\tconst pattern = String(args.pattern || \"\");\n\t\t\t\tconst path = shortenPath(String(args.path || \".\"));\n\t\t\t\treturn `[grep: /${pattern}/ in ${path}]`;\n\t\t\t}\n\t\t\tcase \"find\": {\n\t\t\t\tconst pattern = String(args.pattern || \"\");\n\t\t\t\tconst path = shortenPath(String(args.path || \".\"));\n\t\t\t\treturn `[find: ${pattern} in ${path}]`;\n\t\t\t}\n\t\t\tcase \"ls\": {\n\t\t\t\tconst path = shortenPath(String(args.path || \".\"));\n\t\t\t\treturn `[ls: ${path}]`;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\t// Custom tool - show name and truncated JSON args\n\t\t\t\tconst argsStr = JSON.stringify(args).slice(0, 40);\n\t\t\t\treturn `[${name}: ${argsStr}${JSON.stringify(args).length > 40 ? \"...\" : \"\"}]`;\n\t\t\t}\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getEditorKeybindings();\n\t\tif (kb.matches(keyData, \"selectUp\")) {\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredNodes.length - 1 : this.selectedIndex - 1;\n\t\t} else if (kb.matches(keyData, \"selectDown\")) {\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredNodes.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t} else if (kb.matches(keyData, \"cursorLeft\")) {\n\t\t\t// Page up\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - this.maxVisibleLines);\n\t\t} else if (kb.matches(keyData, \"cursorRight\")) {\n\t\t\t// Page down\n\t\t\tthis.selectedIndex = Math.min(this.filteredNodes.length - 1, this.selectedIndex + this.maxVisibleLines);\n\t\t} else if (kb.matches(keyData, \"selectConfirm\")) {\n\t\t\tconst selected = this.filteredNodes[this.selectedIndex];\n\t\t\tif (selected && this.onSelect) {\n\t\t\t\tthis.onSelect(selected.node.entry.id);\n\t\t\t}\n\t\t} else if (kb.matches(keyData, \"selectCancel\")) {\n\t\t\tif (this.searchQuery) {\n\t\t\t\tthis.searchQuery = \"\";\n\t\t\t\tthis.applyFilter();\n\t\t\t} else {\n\t\t\t\tthis.onCancel?.();\n\t\t\t}\n\t\t} else if (matchesKey(keyData, \"ctrl+d\")) {\n\t\t\t// Direct filter: default\n\t\t\tthis.filterMode = \"default\";\n\t\t\tthis.applyFilter();\n\t\t} else if (matchesKey(keyData, \"ctrl+t\")) {\n\t\t\t// Toggle filter: no-tools ↔ default\n\t\t\tthis.filterMode = this.filterMode === \"no-tools\" ? \"default\" : \"no-tools\";\n\t\t\tthis.applyFilter();\n\t\t} else if (matchesKey(keyData, \"ctrl+u\")) {\n\t\t\t// Toggle filter: user-only ↔ default\n\t\t\tthis.filterMode = this.filterMode === \"user-only\" ? \"default\" : \"user-only\";\n\t\t\tthis.applyFilter();\n\t\t} else if (matchesKey(keyData, \"ctrl+l\")) {\n\t\t\t// Toggle filter: labeled-only ↔ default\n\t\t\tthis.filterMode = this.filterMode === \"labeled-only\" ? \"default\" : \"labeled-only\";\n\t\t\tthis.applyFilter();\n\t\t} else if (matchesKey(keyData, \"ctrl+a\")) {\n\t\t\t// Toggle filter: all ↔ default\n\t\t\tthis.filterMode = this.filterMode === \"all\" ? \"default\" : \"all\";\n\t\t\tthis.applyFilter();\n\t\t} else if (matchesKey(keyData, \"shift+ctrl+o\")) {\n\t\t\t// Cycle filter backwards\n\t\t\tconst modes: FilterMode[] = [\"default\", \"no-tools\", \"user-only\", \"labeled-only\", \"all\"];\n\t\t\tconst currentIndex = modes.indexOf(this.filterMode);\n\t\t\tthis.filterMode = modes[(currentIndex - 1 + modes.length) % modes.length];\n\t\t\tthis.applyFilter();\n\t\t} else if (matchesKey(keyData, \"ctrl+o\")) {\n\t\t\t// Cycle filter forwards: default → no-tools → user-only → labeled-only → all → default\n\t\t\tconst modes: FilterMode[] = [\"default\", \"no-tools\", \"user-only\", \"labeled-only\", \"all\"];\n\t\t\tconst currentIndex = modes.indexOf(this.filterMode);\n\t\t\tthis.filterMode = modes[(currentIndex + 1) % modes.length];\n\t\t\tthis.applyFilter();\n\t\t} else if (kb.matches(keyData, \"deleteCharBackward\")) {\n\t\t\tif (this.searchQuery.length > 0) {\n\t\t\t\tthis.searchQuery = this.searchQuery.slice(0, -1);\n\t\t\t\tthis.applyFilter();\n\t\t\t}\n\t\t} else if (matchesKey(keyData, \"shift+l\")) {\n\t\t\tconst selected = this.filteredNodes[this.selectedIndex];\n\t\t\tif (selected && this.onLabelEdit) {\n\t\t\t\tthis.onLabelEdit(selected.node.entry.id, selected.node.label);\n\t\t\t}\n\t\t} else {\n\t\t\tconst hasControlChars = [...keyData].some((ch) => {\n\t\t\t\tconst code = ch.charCodeAt(0);\n\t\t\t\treturn code < 32 || code === 0x7f || (code >= 0x80 && code <= 0x9f);\n\t\t\t});\n\t\t\tif (!hasControlChars && keyData.length > 0) {\n\t\t\t\tthis.searchQuery += keyData;\n\t\t\t\tthis.applyFilter();\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Component that displays the current search query */\nclass SearchLine implements Component {\n\tconstructor(private treeList: TreeList) {}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst query = this.treeList.getSearchQuery();\n\t\tif (query) {\n\t\t\treturn [truncateToWidth(` ${theme.fg(\"muted\", \"Type to search:\")} ${theme.fg(\"accent\", query)}`, width)];\n\t\t}\n\t\treturn [truncateToWidth(` ${theme.fg(\"muted\", \"Type to search:\")}`, width)];\n\t}\n\n\thandleInput(_keyData: string): void {}\n}\n\n/** Label input component shown when editing a label */\nclass LabelInput implements Component, Focusable {\n\tprivate input: Input;\n\tprivate entryId: string;\n\tpublic onSubmit?: (entryId: string, label: string | undefined) => void;\n\tpublic onCancel?: () => void;\n\n\t// Focusable implementation - propagate to input for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.input.focused = value;\n\t}\n\n\tconstructor(entryId: string, currentLabel: string | undefined) {\n\t\tthis.entryId = entryId;\n\t\tthis.input = new Input();\n\t\tif (currentLabel) {\n\t\t\tthis.input.setValue(currentLabel);\n\t\t}\n\t}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\t\tconst indent = \" \";\n\t\tconst availableWidth = width - indent.length;\n\t\tlines.push(truncateToWidth(`${indent}${theme.fg(\"muted\", \"Label (empty to remove):\")}`, width));\n\t\tlines.push(...this.input.render(availableWidth).map((line) => truncateToWidth(`${indent}${line}`, width)));\n\t\tlines.push(\n\t\t\ttruncateToWidth(`${indent}${keyHint(\"selectConfirm\", \"save\")} ${keyHint(\"selectCancel\", \"cancel\")}`, width),\n\t\t);\n\t\treturn lines;\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getEditorKeybindings();\n\t\tif (kb.matches(keyData, \"selectConfirm\")) {\n\t\t\tconst value = this.input.getValue().trim();\n\t\t\tthis.onSubmit?.(this.entryId, value || undefined);\n\t\t} else if (kb.matches(keyData, \"selectCancel\")) {\n\t\t\tthis.onCancel?.();\n\t\t} else {\n\t\t\tthis.input.handleInput(keyData);\n\t\t}\n\t}\n}\n\n/**\n * Component that renders a session tree selector for navigation\n */\nexport class TreeSelectorComponent extends Container implements Focusable {\n\tprivate treeList: TreeList;\n\tprivate labelInput: LabelInput | null = null;\n\tprivate labelInputContainer: Container;\n\tprivate treeContainer: Container;\n\tprivate onLabelChangeCallback?: (entryId: string, label: string | undefined) => void;\n\n\t// Focusable implementation - propagate to labelInput when active for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\t// Propagate to labelInput when it's active\n\t\tif (this.labelInput) {\n\t\t\tthis.labelInput.focused = value;\n\t\t}\n\t}\n\n\tconstructor(\n\t\ttree: SessionTreeNode[],\n\t\tcurrentLeafId: string | null,\n\t\tterminalHeight: number,\n\t\tonSelect: (entryId: string) => void,\n\t\tonCancel: () => void,\n\t\tonLabelChange?: (entryId: string, label: string | undefined) => void,\n\t\tinitialSelectedId?: string,\n\t) {\n\t\tsuper();\n\n\t\tthis.onLabelChangeCallback = onLabelChange;\n\t\tconst maxVisibleLines = Math.max(5, Math.floor(terminalHeight / 2));\n\n\t\tthis.treeList = new TreeList(tree, currentLeafId, maxVisibleLines, initialSelectedId);\n\t\tthis.treeList.onSelect = onSelect;\n\t\tthis.treeList.onCancel = onCancel;\n\t\tthis.treeList.onLabelEdit = (entryId, currentLabel) => this.showLabelInput(entryId, currentLabel);\n\n\t\tthis.treeContainer = new Container();\n\t\tthis.treeContainer.addChild(this.treeList);\n\n\t\tthis.labelInputContainer = new Container();\n\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Text(theme.bold(\" Session Tree\"), 1, 0));\n\t\tthis.addChild(\n\t\t\tnew TruncatedText(\n\t\t\t\ttheme.fg(\"muted\", \" ↑/↓: move. ←/→: page. Shift+L: label. \") +\n\t\t\t\t\ttheme.fg(\"muted\", \"^D/^T/^U/^L/^A: filters (^O/⇧^O cycle)\"),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new SearchLine(this.treeList));\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(this.treeContainer);\n\t\tthis.addChild(this.labelInputContainer);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\n\t\tif (tree.length === 0) {\n\t\t\tsetTimeout(() => onCancel(), 100);\n\t\t}\n\t}\n\n\tprivate showLabelInput(entryId: string, currentLabel: string | undefined): void {\n\t\tthis.labelInput = new LabelInput(entryId, currentLabel);\n\t\tthis.labelInput.onSubmit = (id, label) => {\n\t\t\tthis.treeList.updateNodeLabel(id, label);\n\t\t\tthis.onLabelChangeCallback?.(id, label);\n\t\t\tthis.hideLabelInput();\n\t\t};\n\t\tthis.labelInput.onCancel = () => this.hideLabelInput();\n\n\t\t// Propagate current focused state to the new labelInput\n\t\tthis.labelInput.focused = this._focused;\n\n\t\tthis.treeContainer.clear();\n\t\tthis.labelInputContainer.clear();\n\t\tthis.labelInputContainer.addChild(this.labelInput);\n\t}\n\n\tprivate hideLabelInput(): void {\n\t\tthis.labelInput = null;\n\t\tthis.labelInputContainer.clear();\n\t\tthis.treeContainer.clear();\n\t\tthis.treeContainer.addChild(this.treeList);\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tif (this.labelInput) {\n\t\t\tthis.labelInput.handleInput(keyData);\n\t\t} else {\n\t\t\tthis.treeList.handleInput(keyData);\n\t\t}\n\t}\n\n\tgetTreeList(): TreeList {\n\t\treturn this.treeList;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tree-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/tree-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,SAAS,EACd,SAAS,EACT,KAAK,SAAS,EAQd,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAsCxE,cAAM,QAAS,YAAW,SAAS;IAClC,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,aAAa,CAAkB;IACvC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,UAAU,CAAyB;IAC3C,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,WAAW,CAAwC;IAC3D,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,aAAa,CAA0B;IAC/C,OAAO,CAAC,cAAc,CAAuB;IAEtC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IAEjF,YACC,IAAI,EAAE,eAAe,EAAE,EACvB,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,eAAe,EAAE,MAAM,EACvB,iBAAiB,CAAC,EAAE,MAAM,EAa1B;IAED;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IA0B/B,uEAAuE;IACvE,OAAO,CAAC,eAAe;IAoBvB,OAAO,CAAC,WAAW;IAkInB,OAAO,CAAC,WAAW;IAqFnB;;;;;OAKG;IACH,OAAO,CAAC,0BAA0B;IAyHlC,8CAA8C;IAC9C,OAAO,CAAC,iBAAiB;IAqDzB,UAAU,IAAI,IAAI,CAAG;IAErB,cAAc,IAAI,MAAM,CAEvB;IAED,eAAe,IAAI,eAAe,GAAG,SAAS,CAE7C;IAED,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAOhE;IAED,OAAO,CAAC,cAAc;IAetB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAuF9B;IAED,OAAO,CAAC,mBAAmB;IAiF3B,OAAO,CAAC,cAAc;IAgBtB,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,cAAc;IA0DtB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CA4EjC;CACD;AAuED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,SAAU,YAAW,SAAS;IACxE,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,mBAAmB,CAAY;IACvC,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,qBAAqB,CAAC,CAAuD;IAGrF,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAMzB;IAED,YACC,IAAI,EAAE,eAAe,EAAE,EACvB,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,EACnC,QAAQ,EAAE,MAAM,IAAI,EACpB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,EACpE,iBAAiB,CAAC,EAAE,MAAM,EAuC1B;IAED,OAAO,CAAC,cAAc;IAiBtB,OAAO,CAAC,cAAc;IAOtB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAMjC;IAED,WAAW,IAAI,QAAQ,CAEtB;CACD","sourcesContent":["import {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tgetEditorKeybindings,\n\tInput,\n\tmatchesKey,\n\tSpacer,\n\tText,\n\tTruncatedText,\n\ttruncateToWidth,\n} from \"@mariozechner/pi-tui\";\nimport type { SessionTreeNode } from \"../../../core/session-manager.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { keyHint } from \"./keybinding-hints.js\";\n\n/** Gutter info: position (displayIndent where connector was) and whether to show │ */\ninterface GutterInfo {\n\tposition: number; // displayIndent level where the connector was shown\n\tshow: boolean; // true = show │, false = show spaces\n}\n\n/** Flattened tree node for navigation */\ninterface FlatNode {\n\tnode: SessionTreeNode;\n\t/** Indentation level (each level = 3 chars) */\n\tindent: number;\n\t/** Whether to show connector (├─ or └─) - true if parent has multiple children */\n\tshowConnector: boolean;\n\t/** If showConnector, true = last sibling (└─), false = not last (├─) */\n\tisLast: boolean;\n\t/** Gutter info for each ancestor branch point */\n\tgutters: GutterInfo[];\n\t/** True if this node is a root under a virtual branching root (multiple roots) */\n\tisVirtualRootChild: boolean;\n}\n\n/** Filter mode for tree display */\ntype FilterMode = \"default\" | \"no-tools\" | \"user-only\" | \"labeled-only\" | \"all\";\n\n/**\n * Tree list component with selection and ASCII art visualization\n */\n/** Tool call info for lookup */\ninterface ToolCallInfo {\n\tname: string;\n\targuments: Record<string, unknown>;\n}\n\nclass TreeList implements Component {\n\tprivate flatNodes: FlatNode[] = [];\n\tprivate filteredNodes: FlatNode[] = [];\n\tprivate selectedIndex = 0;\n\tprivate currentLeafId: string | null;\n\tprivate maxVisibleLines: number;\n\tprivate filterMode: FilterMode = \"default\";\n\tprivate searchQuery = \"\";\n\tprivate toolCallMap: Map<string, ToolCallInfo> = new Map();\n\tprivate multipleRoots = false;\n\tprivate activePathIds: Set<string> = new Set();\n\tprivate lastSelectedId: string | null = null;\n\n\tpublic onSelect?: (entryId: string) => void;\n\tpublic onCancel?: () => void;\n\tpublic onLabelEdit?: (entryId: string, currentLabel: string | undefined) => void;\n\n\tconstructor(\n\t\ttree: SessionTreeNode[],\n\t\tcurrentLeafId: string | null,\n\t\tmaxVisibleLines: number,\n\t\tinitialSelectedId?: string,\n\t) {\n\t\tthis.currentLeafId = currentLeafId;\n\t\tthis.maxVisibleLines = maxVisibleLines;\n\t\tthis.multipleRoots = tree.length > 1;\n\t\tthis.flatNodes = this.flattenTree(tree);\n\t\tthis.buildActivePath();\n\t\tthis.applyFilter();\n\n\t\t// Start with initialSelectedId if provided, otherwise current leaf\n\t\tconst targetId = initialSelectedId ?? currentLeafId;\n\t\tthis.selectedIndex = this.findNearestVisibleIndex(targetId);\n\t\tthis.lastSelectedId = this.filteredNodes[this.selectedIndex]?.node.entry.id ?? null;\n\t}\n\n\t/**\n\t * Find the index of the nearest visible entry, walking up the parent chain if needed.\n\t * Returns the index in filteredNodes, or the last index as fallback.\n\t */\n\tprivate findNearestVisibleIndex(entryId: string | null): number {\n\t\tif (this.filteredNodes.length === 0) return 0;\n\n\t\t// Build a map for parent lookup\n\t\tconst entryMap = new Map<string, FlatNode>();\n\t\tfor (const flatNode of this.flatNodes) {\n\t\t\tentryMap.set(flatNode.node.entry.id, flatNode);\n\t\t}\n\n\t\t// Build a map of visible entry IDs to their indices in filteredNodes\n\t\tconst visibleIdToIndex = new Map<string, number>(this.filteredNodes.map((node, i) => [node.node.entry.id, i]));\n\n\t\t// Walk from entryId up to root, looking for a visible entry\n\t\tlet currentId = entryId;\n\t\twhile (currentId !== null) {\n\t\t\tconst index = visibleIdToIndex.get(currentId);\n\t\t\tif (index !== undefined) return index;\n\t\t\tconst node = entryMap.get(currentId);\n\t\t\tif (!node) break;\n\t\t\tcurrentId = node.node.entry.parentId ?? null;\n\t\t}\n\n\t\t// Fallback: last visible entry\n\t\treturn this.filteredNodes.length - 1;\n\t}\n\n\t/** Build the set of entry IDs on the path from root to current leaf */\n\tprivate buildActivePath(): void {\n\t\tthis.activePathIds.clear();\n\t\tif (!this.currentLeafId) return;\n\n\t\t// Build a map of id -> entry for parent lookup\n\t\tconst entryMap = new Map<string, FlatNode>();\n\t\tfor (const flatNode of this.flatNodes) {\n\t\t\tentryMap.set(flatNode.node.entry.id, flatNode);\n\t\t}\n\n\t\t// Walk from leaf to root\n\t\tlet currentId: string | null = this.currentLeafId;\n\t\twhile (currentId) {\n\t\t\tthis.activePathIds.add(currentId);\n\t\t\tconst node = entryMap.get(currentId);\n\t\t\tif (!node) break;\n\t\t\tcurrentId = node.node.entry.parentId ?? null;\n\t\t}\n\t}\n\n\tprivate flattenTree(roots: SessionTreeNode[]): FlatNode[] {\n\t\tconst result: FlatNode[] = [];\n\t\tthis.toolCallMap.clear();\n\n\t\t// Indentation rules:\n\t\t// - At indent 0: stay at 0 unless parent has >1 children (then +1)\n\t\t// - At indent 1: children always go to indent 2 (visual grouping of subtree)\n\t\t// - At indent 2+: stay flat for single-child chains, +1 only if parent branches\n\n\t\t// Stack items: [node, indent, justBranched, showConnector, isLast, gutters, isVirtualRootChild]\n\t\ttype StackItem = [SessionTreeNode, number, boolean, boolean, boolean, GutterInfo[], boolean];\n\t\tconst stack: StackItem[] = [];\n\n\t\t// Determine which subtrees contain the active leaf (to sort current branch first)\n\t\t// Use iterative post-order traversal to avoid stack overflow\n\t\tconst containsActive = new Map<SessionTreeNode, boolean>();\n\t\tconst leafId = this.currentLeafId;\n\t\t{\n\t\t\t// Build list in pre-order, then process in reverse for post-order effect\n\t\t\tconst allNodes: SessionTreeNode[] = [];\n\t\t\tconst preOrderStack: SessionTreeNode[] = [...roots];\n\t\t\twhile (preOrderStack.length > 0) {\n\t\t\t\tconst node = preOrderStack.pop()!;\n\t\t\t\tallNodes.push(node);\n\t\t\t\t// Push children in reverse so they're processed left-to-right\n\t\t\t\tfor (let i = node.children.length - 1; i >= 0; i--) {\n\t\t\t\t\tpreOrderStack.push(node.children[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Process in reverse (post-order): children before parents\n\t\t\tfor (let i = allNodes.length - 1; i >= 0; i--) {\n\t\t\t\tconst node = allNodes[i];\n\t\t\t\tlet has = leafId !== null && node.entry.id === leafId;\n\t\t\t\tfor (const child of node.children) {\n\t\t\t\t\tif (containsActive.get(child)) {\n\t\t\t\t\t\thas = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcontainsActive.set(node, has);\n\t\t\t}\n\t\t}\n\n\t\t// Add roots in reverse order, prioritizing the one containing the active leaf\n\t\t// If multiple roots, treat them as children of a virtual root that branches\n\t\tconst multipleRoots = roots.length > 1;\n\t\tconst orderedRoots = [...roots].sort((a, b) => Number(containsActive.get(b)) - Number(containsActive.get(a)));\n\t\tfor (let i = orderedRoots.length - 1; i >= 0; i--) {\n\t\t\tconst isLast = i === orderedRoots.length - 1;\n\t\t\tstack.push([orderedRoots[i], multipleRoots ? 1 : 0, multipleRoots, multipleRoots, isLast, [], multipleRoots]);\n\t\t}\n\n\t\twhile (stack.length > 0) {\n\t\t\tconst [node, indent, justBranched, showConnector, isLast, gutters, isVirtualRootChild] = stack.pop()!;\n\n\t\t\t// Extract tool calls from assistant messages for later lookup\n\t\t\tconst entry = node.entry;\n\t\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\t\tconst content = (entry.message as { content?: unknown }).content;\n\t\t\t\tif (Array.isArray(content)) {\n\t\t\t\t\tfor (const block of content) {\n\t\t\t\t\t\tif (typeof block === \"object\" && block !== null && \"type\" in block && block.type === \"toolCall\") {\n\t\t\t\t\t\t\tconst tc = block as { id: string; name: string; arguments: Record<string, unknown> };\n\t\t\t\t\t\t\tthis.toolCallMap.set(tc.id, { name: tc.name, arguments: tc.arguments });\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tresult.push({ node, indent, showConnector, isLast, gutters, isVirtualRootChild });\n\n\t\t\tconst children = node.children;\n\t\t\tconst multipleChildren = children.length > 1;\n\n\t\t\t// Order children so the branch containing the active leaf comes first\n\t\t\tconst orderedChildren = (() => {\n\t\t\t\tconst prioritized: SessionTreeNode[] = [];\n\t\t\t\tconst rest: SessionTreeNode[] = [];\n\t\t\t\tfor (const child of children) {\n\t\t\t\t\tif (containsActive.get(child)) {\n\t\t\t\t\t\tprioritized.push(child);\n\t\t\t\t\t} else {\n\t\t\t\t\t\trest.push(child);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn [...prioritized, ...rest];\n\t\t\t})();\n\n\t\t\t// Calculate child indent\n\t\t\tlet childIndent: number;\n\t\t\tif (multipleChildren) {\n\t\t\t\t// Parent branches: children get +1\n\t\t\t\tchildIndent = indent + 1;\n\t\t\t} else if (justBranched && indent > 0) {\n\t\t\t\t// First generation after a branch: +1 for visual grouping\n\t\t\t\tchildIndent = indent + 1;\n\t\t\t} else {\n\t\t\t\t// Single-child chain: stay flat\n\t\t\t\tchildIndent = indent;\n\t\t\t}\n\n\t\t\t// Build gutters for children\n\t\t\t// If this node showed a connector, add a gutter entry for descendants\n\t\t\t// Only add gutter if connector is actually displayed (not suppressed for virtual root children)\n\t\t\tconst connectorDisplayed = showConnector && !isVirtualRootChild;\n\t\t\t// When connector is displayed, add a gutter entry at the connector's position\n\t\t\t// Connector is at position (displayIndent - 1), so gutter should be there too\n\t\t\tconst currentDisplayIndent = this.multipleRoots ? Math.max(0, indent - 1) : indent;\n\t\t\tconst connectorPosition = Math.max(0, currentDisplayIndent - 1);\n\t\t\tconst childGutters: GutterInfo[] = connectorDisplayed\n\t\t\t\t? [...gutters, { position: connectorPosition, show: !isLast }]\n\t\t\t\t: gutters;\n\n\t\t\t// Add children in reverse order\n\t\t\tfor (let i = orderedChildren.length - 1; i >= 0; i--) {\n\t\t\t\tconst childIsLast = i === orderedChildren.length - 1;\n\t\t\t\tstack.push([\n\t\t\t\t\torderedChildren[i],\n\t\t\t\t\tchildIndent,\n\t\t\t\t\tmultipleChildren,\n\t\t\t\t\tmultipleChildren,\n\t\t\t\t\tchildIsLast,\n\t\t\t\t\tchildGutters,\n\t\t\t\t\tfalse,\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate applyFilter(): void {\n\t\t// Update lastSelectedId only when we have a valid selection (non-empty list)\n\t\t// This preserves the selection when switching through empty filter results\n\t\tif (this.filteredNodes.length > 0) {\n\t\t\tthis.lastSelectedId = this.filteredNodes[this.selectedIndex]?.node.entry.id ?? this.lastSelectedId;\n\t\t}\n\n\t\tconst searchTokens = this.searchQuery.toLowerCase().split(/\\s+/).filter(Boolean);\n\n\t\tthis.filteredNodes = this.flatNodes.filter((flatNode) => {\n\t\t\tconst entry = flatNode.node.entry;\n\t\t\tconst isCurrentLeaf = entry.id === this.currentLeafId;\n\n\t\t\t// Skip assistant messages with only tool calls (no text) unless error/aborted\n\t\t\t// Always show current leaf so active position is visible\n\t\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\" && !isCurrentLeaf) {\n\t\t\t\tconst msg = entry.message as { stopReason?: string; content?: unknown };\n\t\t\t\tconst hasText = this.hasTextContent(msg.content);\n\t\t\t\tconst isErrorOrAborted = msg.stopReason && msg.stopReason !== \"stop\" && msg.stopReason !== \"toolUse\";\n\t\t\t\t// Only hide if no text AND not an error/aborted message\n\t\t\t\tif (!hasText && !isErrorOrAborted) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Apply filter mode\n\t\t\tlet passesFilter = true;\n\t\t\t// Entry types hidden in default view (settings/bookkeeping)\n\t\t\tconst isSettingsEntry =\n\t\t\t\tentry.type === \"label\" ||\n\t\t\t\tentry.type === \"custom\" ||\n\t\t\t\tentry.type === \"model_change\" ||\n\t\t\t\tentry.type === \"thinking_level_change\";\n\n\t\t\tswitch (this.filterMode) {\n\t\t\t\tcase \"user-only\":\n\t\t\t\t\t// Just user messages\n\t\t\t\t\tpassesFilter = entry.type === \"message\" && entry.message.role === \"user\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"no-tools\":\n\t\t\t\t\t// Default minus tool results\n\t\t\t\t\tpassesFilter = !isSettingsEntry && !(entry.type === \"message\" && entry.message.role === \"toolResult\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"labeled-only\":\n\t\t\t\t\t// Just labeled entries\n\t\t\t\t\tpassesFilter = flatNode.node.label !== undefined;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"all\":\n\t\t\t\t\t// Show everything\n\t\t\t\t\tpassesFilter = true;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t// Default mode: hide settings/bookkeeping entries\n\t\t\t\t\tpassesFilter = !isSettingsEntry;\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (!passesFilter) return false;\n\n\t\t\t// Apply search filter\n\t\t\tif (searchTokens.length > 0) {\n\t\t\t\tconst nodeText = this.getSearchableText(flatNode.node).toLowerCase();\n\t\t\t\treturn searchTokens.every((token) => nodeText.includes(token));\n\t\t\t}\n\n\t\t\treturn true;\n\t\t});\n\n\t\t// Recalculate visual structure (indent, connectors, gutters) based on visible tree\n\t\tthis.recalculateVisualStructure();\n\n\t\t// Try to preserve cursor on the same node, or find nearest visible ancestor\n\t\tif (this.lastSelectedId) {\n\t\t\tthis.selectedIndex = this.findNearestVisibleIndex(this.lastSelectedId);\n\t\t} else if (this.selectedIndex >= this.filteredNodes.length) {\n\t\t\t// Clamp index if out of bounds\n\t\t\tthis.selectedIndex = Math.max(0, this.filteredNodes.length - 1);\n\t\t}\n\n\t\t// Update lastSelectedId to the actual selection (may have changed due to parent walk)\n\t\tif (this.filteredNodes.length > 0) {\n\t\t\tthis.lastSelectedId = this.filteredNodes[this.selectedIndex]?.node.entry.id ?? this.lastSelectedId;\n\t\t}\n\t}\n\n\t/**\n\t * Recompute indentation/connectors for the filtered view\n\t *\n\t * Filtering can hide intermediate entries; descendants attach to the nearest visible ancestor.\n\t * Keep indentation semantics aligned with flattenTree() so single-child chains don't drift right.\n\t */\n\tprivate recalculateVisualStructure(): void {\n\t\tif (this.filteredNodes.length === 0) return;\n\n\t\tconst visibleIds = new Set(this.filteredNodes.map((n) => n.node.entry.id));\n\n\t\t// Build entry map for efficient parent lookup (using full tree)\n\t\tconst entryMap = new Map<string, FlatNode>();\n\t\tfor (const flatNode of this.flatNodes) {\n\t\t\tentryMap.set(flatNode.node.entry.id, flatNode);\n\t\t}\n\n\t\t// Find nearest visible ancestor for a node\n\t\tconst findVisibleAncestor = (nodeId: string): string | null => {\n\t\t\tlet currentId = entryMap.get(nodeId)?.node.entry.parentId ?? null;\n\t\t\twhile (currentId !== null) {\n\t\t\t\tif (visibleIds.has(currentId)) {\n\t\t\t\t\treturn currentId;\n\t\t\t\t}\n\t\t\t\tcurrentId = entryMap.get(currentId)?.node.entry.parentId ?? null;\n\t\t\t}\n\t\t\treturn null;\n\t\t};\n\n\t\t// Build visible tree structure:\n\t\t// - visibleParent: nodeId → nearest visible ancestor (or null for roots)\n\t\t// - visibleChildren: parentId → list of visible children (in filteredNodes order)\n\t\tconst visibleParent = new Map<string, string | null>();\n\t\tconst visibleChildren = new Map<string | null, string[]>();\n\t\tvisibleChildren.set(null, []); // root-level nodes\n\n\t\tfor (const flatNode of this.filteredNodes) {\n\t\t\tconst nodeId = flatNode.node.entry.id;\n\t\t\tconst ancestorId = findVisibleAncestor(nodeId);\n\t\t\tvisibleParent.set(nodeId, ancestorId);\n\n\t\t\tif (!visibleChildren.has(ancestorId)) {\n\t\t\t\tvisibleChildren.set(ancestorId, []);\n\t\t\t}\n\t\t\tvisibleChildren.get(ancestorId)!.push(nodeId);\n\t\t}\n\n\t\t// Update multipleRoots based on visible roots\n\t\tconst visibleRootIds = visibleChildren.get(null)!;\n\t\tthis.multipleRoots = visibleRootIds.length > 1;\n\n\t\t// Build a map for quick lookup: nodeId → FlatNode\n\t\tconst filteredNodeMap = new Map<string, FlatNode>();\n\t\tfor (const flatNode of this.filteredNodes) {\n\t\t\tfilteredNodeMap.set(flatNode.node.entry.id, flatNode);\n\t\t}\n\n\t\t// DFS over the visible tree using flattenTree() indentation semantics\n\t\t// Stack items: [nodeId, indent, justBranched, showConnector, isLast, gutters, isVirtualRootChild]\n\t\ttype StackItem = [string, number, boolean, boolean, boolean, GutterInfo[], boolean];\n\t\tconst stack: StackItem[] = [];\n\n\t\t// Add visible roots in reverse order (to process in forward order via stack)\n\t\tfor (let i = visibleRootIds.length - 1; i >= 0; i--) {\n\t\t\tconst isLast = i === visibleRootIds.length - 1;\n\t\t\tstack.push([\n\t\t\t\tvisibleRootIds[i],\n\t\t\t\tthis.multipleRoots ? 1 : 0,\n\t\t\t\tthis.multipleRoots,\n\t\t\t\tthis.multipleRoots,\n\t\t\t\tisLast,\n\t\t\t\t[],\n\t\t\t\tthis.multipleRoots,\n\t\t\t]);\n\t\t}\n\n\t\twhile (stack.length > 0) {\n\t\t\tconst [nodeId, indent, justBranched, showConnector, isLast, gutters, isVirtualRootChild] = stack.pop()!;\n\n\t\t\tconst flatNode = filteredNodeMap.get(nodeId);\n\t\t\tif (!flatNode) continue;\n\n\t\t\t// Update this node's visual properties\n\t\t\tflatNode.indent = indent;\n\t\t\tflatNode.showConnector = showConnector;\n\t\t\tflatNode.isLast = isLast;\n\t\t\tflatNode.gutters = gutters;\n\t\t\tflatNode.isVirtualRootChild = isVirtualRootChild;\n\n\t\t\t// Get visible children of this node\n\t\t\tconst children = visibleChildren.get(nodeId) || [];\n\t\t\tconst multipleChildren = children.length > 1;\n\n\t\t\t// Child indent follows flattenTree(): branch points (and first generation after a branch) shift +1\n\t\t\tlet childIndent: number;\n\t\t\tif (multipleChildren) {\n\t\t\t\tchildIndent = indent + 1;\n\t\t\t} else if (justBranched && indent > 0) {\n\t\t\t\tchildIndent = indent + 1;\n\t\t\t} else {\n\t\t\t\tchildIndent = indent;\n\t\t\t}\n\n\t\t\t// Child gutters follow flattenTree() connector/gutter rules\n\t\t\tconst connectorDisplayed = showConnector && !isVirtualRootChild;\n\t\t\tconst currentDisplayIndent = this.multipleRoots ? Math.max(0, indent - 1) : indent;\n\t\t\tconst connectorPosition = Math.max(0, currentDisplayIndent - 1);\n\t\t\tconst childGutters: GutterInfo[] = connectorDisplayed\n\t\t\t\t? [...gutters, { position: connectorPosition, show: !isLast }]\n\t\t\t\t: gutters;\n\n\t\t\t// Add children in reverse order (to process in forward order via stack)\n\t\t\tfor (let i = children.length - 1; i >= 0; i--) {\n\t\t\t\tconst childIsLast = i === children.length - 1;\n\t\t\t\tstack.push([\n\t\t\t\t\tchildren[i],\n\t\t\t\t\tchildIndent,\n\t\t\t\t\tmultipleChildren,\n\t\t\t\t\tmultipleChildren,\n\t\t\t\t\tchildIsLast,\n\t\t\t\t\tchildGutters,\n\t\t\t\t\tfalse,\n\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Get searchable text content from a node */\n\tprivate getSearchableText(node: SessionTreeNode): string {\n\t\tconst entry = node.entry;\n\t\tconst parts: string[] = [];\n\n\t\tif (node.label) {\n\t\t\tparts.push(node.label);\n\t\t}\n\n\t\tswitch (entry.type) {\n\t\t\tcase \"message\": {\n\t\t\t\tconst msg = entry.message;\n\t\t\t\tparts.push(msg.role);\n\t\t\t\tif (\"content\" in msg && msg.content) {\n\t\t\t\t\tparts.push(this.extractContent(msg.content));\n\t\t\t\t}\n\t\t\t\tif (msg.role === \"bashExecution\") {\n\t\t\t\t\tconst bashMsg = msg as { command?: string };\n\t\t\t\t\tif (bashMsg.command) parts.push(bashMsg.command);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"custom_message\": {\n\t\t\t\tparts.push(entry.customType);\n\t\t\t\tif (typeof entry.content === \"string\") {\n\t\t\t\t\tparts.push(entry.content);\n\t\t\t\t} else {\n\t\t\t\t\tparts.push(this.extractContent(entry.content));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"compaction\":\n\t\t\t\tparts.push(\"compaction\");\n\t\t\t\tbreak;\n\t\t\tcase \"branch_summary\":\n\t\t\t\tparts.push(\"branch summary\", entry.summary);\n\t\t\t\tbreak;\n\t\t\tcase \"model_change\":\n\t\t\t\tparts.push(\"model\", entry.modelId);\n\t\t\t\tbreak;\n\t\t\tcase \"thinking_level_change\":\n\t\t\t\tparts.push(\"thinking\", entry.thinkingLevel);\n\t\t\t\tbreak;\n\t\t\tcase \"custom\":\n\t\t\t\tparts.push(\"custom\", entry.customType);\n\t\t\t\tbreak;\n\t\t\tcase \"label\":\n\t\t\t\tparts.push(\"label\", entry.label ?? \"\");\n\t\t\t\tbreak;\n\t\t}\n\n\t\treturn parts.join(\" \");\n\t}\n\n\tinvalidate(): void {}\n\n\tgetSearchQuery(): string {\n\t\treturn this.searchQuery;\n\t}\n\n\tgetSelectedNode(): SessionTreeNode | undefined {\n\t\treturn this.filteredNodes[this.selectedIndex]?.node;\n\t}\n\n\tupdateNodeLabel(entryId: string, label: string | undefined): void {\n\t\tfor (const flatNode of this.flatNodes) {\n\t\t\tif (flatNode.node.entry.id === entryId) {\n\t\t\t\tflatNode.node.label = label;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate getFilterLabel(): string {\n\t\tswitch (this.filterMode) {\n\t\t\tcase \"no-tools\":\n\t\t\t\treturn \" [no-tools]\";\n\t\t\tcase \"user-only\":\n\t\t\t\treturn \" [user]\";\n\t\t\tcase \"labeled-only\":\n\t\t\t\treturn \" [labeled]\";\n\t\t\tcase \"all\":\n\t\t\t\treturn \" [all]\";\n\t\t\tdefault:\n\t\t\t\treturn \"\";\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\n\t\tif (this.filteredNodes.length === 0) {\n\t\t\tlines.push(truncateToWidth(theme.fg(\"muted\", \" No entries found\"), width));\n\t\t\tlines.push(truncateToWidth(theme.fg(\"muted\", ` (0/0)${this.getFilterLabel()}`), width));\n\t\t\treturn lines;\n\t\t}\n\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(\n\t\t\t\tthis.selectedIndex - Math.floor(this.maxVisibleLines / 2),\n\t\t\t\tthis.filteredNodes.length - this.maxVisibleLines,\n\t\t\t),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisibleLines, this.filteredNodes.length);\n\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst flatNode = this.filteredNodes[i];\n\t\t\tconst entry = flatNode.node.entry;\n\t\t\tconst isSelected = i === this.selectedIndex;\n\n\t\t\t// Build line: cursor + prefix + path marker + label + content\n\t\t\tconst cursor = isSelected ? theme.fg(\"accent\", \"› \") : \" \";\n\n\t\t\t// If multiple roots, shift display (roots at 0, not 1)\n\t\t\tconst displayIndent = this.multipleRoots ? Math.max(0, flatNode.indent - 1) : flatNode.indent;\n\n\t\t\t// Build prefix with gutters at their correct positions\n\t\t\t// Each gutter has a position (displayIndent where its connector was shown)\n\t\t\tconst connector =\n\t\t\t\tflatNode.showConnector && !flatNode.isVirtualRootChild ? (flatNode.isLast ? \"└─ \" : \"├─ \") : \"\";\n\t\t\tconst connectorPosition = connector ? displayIndent - 1 : -1;\n\n\t\t\t// Build prefix char by char, placing gutters and connector at their positions\n\t\t\tconst totalChars = displayIndent * 3;\n\t\t\tconst prefixChars: string[] = [];\n\t\t\tfor (let i = 0; i < totalChars; i++) {\n\t\t\t\tconst level = Math.floor(i / 3);\n\t\t\t\tconst posInLevel = i % 3;\n\n\t\t\t\t// Check if there's a gutter at this level\n\t\t\t\tconst gutter = flatNode.gutters.find((g) => g.position === level);\n\t\t\t\tif (gutter) {\n\t\t\t\t\tif (posInLevel === 0) {\n\t\t\t\t\t\tprefixChars.push(gutter.show ? \"│\" : \" \");\n\t\t\t\t\t} else {\n\t\t\t\t\t\tprefixChars.push(\" \");\n\t\t\t\t\t}\n\t\t\t\t} else if (connector && level === connectorPosition) {\n\t\t\t\t\t// Connector at this level\n\t\t\t\t\tif (posInLevel === 0) {\n\t\t\t\t\t\tprefixChars.push(flatNode.isLast ? \"└\" : \"├\");\n\t\t\t\t\t} else if (posInLevel === 1) {\n\t\t\t\t\t\tprefixChars.push(\"─\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\tprefixChars.push(\" \");\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tprefixChars.push(\" \");\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst prefix = prefixChars.join(\"\");\n\n\t\t\t// Active path marker - shown right before the entry text\n\t\t\tconst isOnActivePath = this.activePathIds.has(entry.id);\n\t\t\tconst pathMarker = isOnActivePath ? theme.fg(\"accent\", \"• \") : \"\";\n\n\t\t\tconst label = flatNode.node.label ? theme.fg(\"warning\", `[${flatNode.node.label}] `) : \"\";\n\t\t\tconst content = this.getEntryDisplayText(flatNode.node, isSelected);\n\n\t\t\tlet line = cursor + theme.fg(\"dim\", prefix) + pathMarker + label + content;\n\t\t\tif (isSelected) {\n\t\t\t\tline = theme.bg(\"selectedBg\", line);\n\t\t\t}\n\t\t\tlines.push(truncateToWidth(line, width));\n\t\t}\n\n\t\tlines.push(\n\t\t\ttruncateToWidth(\n\t\t\t\ttheme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredNodes.length})${this.getFilterLabel()}`),\n\t\t\t\twidth,\n\t\t\t),\n\t\t);\n\n\t\treturn lines;\n\t}\n\n\tprivate getEntryDisplayText(node: SessionTreeNode, isSelected: boolean): string {\n\t\tconst entry = node.entry;\n\t\tlet result: string;\n\n\t\tconst normalize = (s: string) => s.replace(/[\\n\\t]/g, \" \").trim();\n\n\t\tswitch (entry.type) {\n\t\t\tcase \"message\": {\n\t\t\t\tconst msg = entry.message;\n\t\t\t\tconst role = msg.role;\n\t\t\t\tif (role === \"user\") {\n\t\t\t\t\tconst msgWithContent = msg as { content?: unknown };\n\t\t\t\t\tconst content = normalize(this.extractContent(msgWithContent.content));\n\t\t\t\t\tresult = theme.fg(\"accent\", \"user: \") + content;\n\t\t\t\t} else if (role === \"assistant\") {\n\t\t\t\t\tconst msgWithContent = msg as { content?: unknown; stopReason?: string; errorMessage?: string };\n\t\t\t\t\tconst textContent = normalize(this.extractContent(msgWithContent.content));\n\t\t\t\t\tif (textContent) {\n\t\t\t\t\t\tresult = theme.fg(\"success\", \"assistant: \") + textContent;\n\t\t\t\t\t} else if (msgWithContent.stopReason === \"aborted\") {\n\t\t\t\t\t\tresult = theme.fg(\"success\", \"assistant: \") + theme.fg(\"muted\", \"(aborted)\");\n\t\t\t\t\t} else if (msgWithContent.errorMessage) {\n\t\t\t\t\t\tconst errMsg = normalize(msgWithContent.errorMessage).slice(0, 80);\n\t\t\t\t\t\tresult = theme.fg(\"success\", \"assistant: \") + theme.fg(\"error\", errMsg);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult = theme.fg(\"success\", \"assistant: \") + theme.fg(\"muted\", \"(no content)\");\n\t\t\t\t\t}\n\t\t\t\t} else if (role === \"toolResult\") {\n\t\t\t\t\tconst toolMsg = msg as { toolCallId?: string; toolName?: string };\n\t\t\t\t\tconst toolCall = toolMsg.toolCallId ? this.toolCallMap.get(toolMsg.toolCallId) : undefined;\n\t\t\t\t\tif (toolCall) {\n\t\t\t\t\t\tresult = theme.fg(\"muted\", this.formatToolCall(toolCall.name, toolCall.arguments));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult = theme.fg(\"muted\", `[${toolMsg.toolName ?? \"tool\"}]`);\n\t\t\t\t\t}\n\t\t\t\t} else if (role === \"bashExecution\") {\n\t\t\t\t\tconst bashMsg = msg as { command?: string };\n\t\t\t\t\tresult = theme.fg(\"dim\", `[bash]: ${normalize(bashMsg.command ?? \"\")}`);\n\t\t\t\t} else {\n\t\t\t\t\tresult = theme.fg(\"dim\", `[${role}]`);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"custom_message\": {\n\t\t\t\tconst content =\n\t\t\t\t\ttypeof entry.content === \"string\"\n\t\t\t\t\t\t? entry.content\n\t\t\t\t\t\t: entry.content\n\t\t\t\t\t\t\t\t.filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n\t\t\t\t\t\t\t\t.map((c) => c.text)\n\t\t\t\t\t\t\t\t.join(\"\");\n\t\t\t\tresult = theme.fg(\"customMessageLabel\", `[${entry.customType}]: `) + normalize(content);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"compaction\": {\n\t\t\t\tconst tokens = Math.round(entry.tokensBefore / 1000);\n\t\t\t\tresult = theme.fg(\"borderAccent\", `[compaction: ${tokens}k tokens]`);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"branch_summary\":\n\t\t\t\tresult = theme.fg(\"warning\", `[branch summary]: `) + normalize(entry.summary);\n\t\t\t\tbreak;\n\t\t\tcase \"model_change\":\n\t\t\t\tresult = theme.fg(\"dim\", `[model: ${entry.modelId}]`);\n\t\t\t\tbreak;\n\t\t\tcase \"thinking_level_change\":\n\t\t\t\tresult = theme.fg(\"dim\", `[thinking: ${entry.thinkingLevel}]`);\n\t\t\t\tbreak;\n\t\t\tcase \"custom\":\n\t\t\t\tresult = theme.fg(\"dim\", `[custom: ${entry.customType}]`);\n\t\t\t\tbreak;\n\t\t\tcase \"label\":\n\t\t\t\tresult = theme.fg(\"dim\", `[label: ${entry.label ?? \"(cleared)\"}]`);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tresult = \"\";\n\t\t}\n\n\t\treturn isSelected ? theme.bold(result) : result;\n\t}\n\n\tprivate extractContent(content: unknown): string {\n\t\tconst maxLen = 200;\n\t\tif (typeof content === \"string\") return content.slice(0, maxLen);\n\t\tif (Array.isArray(content)) {\n\t\t\tlet result = \"\";\n\t\t\tfor (const c of content) {\n\t\t\t\tif (typeof c === \"object\" && c !== null && \"type\" in c && c.type === \"text\") {\n\t\t\t\t\tresult += (c as { text: string }).text;\n\t\t\t\t\tif (result.length >= maxLen) return result.slice(0, maxLen);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\t\treturn \"\";\n\t}\n\n\tprivate hasTextContent(content: unknown): boolean {\n\t\tif (typeof content === \"string\") return content.trim().length > 0;\n\t\tif (Array.isArray(content)) {\n\t\t\tfor (const c of content) {\n\t\t\t\tif (typeof c === \"object\" && c !== null && \"type\" in c && c.type === \"text\") {\n\t\t\t\t\tconst text = (c as { text?: string }).text;\n\t\t\t\t\tif (text && text.trim().length > 0) return true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate formatToolCall(name: string, args: Record<string, unknown>): string {\n\t\tconst shortenPath = (p: string): string => {\n\t\t\tconst home = process.env.HOME || process.env.USERPROFILE || \"\";\n\t\t\tif (home && p.startsWith(home)) return `~${p.slice(home.length)}`;\n\t\t\treturn p;\n\t\t};\n\n\t\tswitch (name) {\n\t\t\tcase \"read\": {\n\t\t\t\tconst path = shortenPath(String(args.path || args.file_path || \"\"));\n\t\t\t\tconst offset = args.offset as number | undefined;\n\t\t\t\tconst limit = args.limit as number | undefined;\n\t\t\t\tlet display = path;\n\t\t\t\tif (offset !== undefined || limit !== undefined) {\n\t\t\t\t\tconst start = offset ?? 1;\n\t\t\t\t\tconst end = limit !== undefined ? start + limit - 1 : \"\";\n\t\t\t\t\tdisplay += `:${start}${end ? `-${end}` : \"\"}`;\n\t\t\t\t}\n\t\t\t\treturn `[read: ${display}]`;\n\t\t\t}\n\t\t\tcase \"write\": {\n\t\t\t\tconst path = shortenPath(String(args.path || args.file_path || \"\"));\n\t\t\t\treturn `[write: ${path}]`;\n\t\t\t}\n\t\t\tcase \"edit\": {\n\t\t\t\tconst path = shortenPath(String(args.path || args.file_path || \"\"));\n\t\t\t\treturn `[edit: ${path}]`;\n\t\t\t}\n\t\t\tcase \"bash\": {\n\t\t\t\tconst rawCmd = String(args.command || \"\");\n\t\t\t\tconst cmd = rawCmd\n\t\t\t\t\t.replace(/[\\n\\t]/g, \" \")\n\t\t\t\t\t.trim()\n\t\t\t\t\t.slice(0, 50);\n\t\t\t\treturn `[bash: ${cmd}${rawCmd.length > 50 ? \"...\" : \"\"}]`;\n\t\t\t}\n\t\t\tcase \"grep\": {\n\t\t\t\tconst pattern = String(args.pattern || \"\");\n\t\t\t\tconst path = shortenPath(String(args.path || \".\"));\n\t\t\t\treturn `[grep: /${pattern}/ in ${path}]`;\n\t\t\t}\n\t\t\tcase \"find\": {\n\t\t\t\tconst pattern = String(args.pattern || \"\");\n\t\t\t\tconst path = shortenPath(String(args.path || \".\"));\n\t\t\t\treturn `[find: ${pattern} in ${path}]`;\n\t\t\t}\n\t\t\tcase \"ls\": {\n\t\t\t\tconst path = shortenPath(String(args.path || \".\"));\n\t\t\t\treturn `[ls: ${path}]`;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\t// Custom tool - show name and truncated JSON args\n\t\t\t\tconst argsStr = JSON.stringify(args).slice(0, 40);\n\t\t\t\treturn `[${name}: ${argsStr}${JSON.stringify(args).length > 40 ? \"...\" : \"\"}]`;\n\t\t\t}\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getEditorKeybindings();\n\t\tif (kb.matches(keyData, \"selectUp\")) {\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredNodes.length - 1 : this.selectedIndex - 1;\n\t\t} else if (kb.matches(keyData, \"selectDown\")) {\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredNodes.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t} else if (kb.matches(keyData, \"cursorLeft\")) {\n\t\t\t// Page up\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - this.maxVisibleLines);\n\t\t} else if (kb.matches(keyData, \"cursorRight\")) {\n\t\t\t// Page down\n\t\t\tthis.selectedIndex = Math.min(this.filteredNodes.length - 1, this.selectedIndex + this.maxVisibleLines);\n\t\t} else if (kb.matches(keyData, \"selectConfirm\")) {\n\t\t\tconst selected = this.filteredNodes[this.selectedIndex];\n\t\t\tif (selected && this.onSelect) {\n\t\t\t\tthis.onSelect(selected.node.entry.id);\n\t\t\t}\n\t\t} else if (kb.matches(keyData, \"selectCancel\")) {\n\t\t\tif (this.searchQuery) {\n\t\t\t\tthis.searchQuery = \"\";\n\t\t\t\tthis.applyFilter();\n\t\t\t} else {\n\t\t\t\tthis.onCancel?.();\n\t\t\t}\n\t\t} else if (matchesKey(keyData, \"ctrl+d\")) {\n\t\t\t// Direct filter: default\n\t\t\tthis.filterMode = \"default\";\n\t\t\tthis.applyFilter();\n\t\t} else if (matchesKey(keyData, \"ctrl+t\")) {\n\t\t\t// Toggle filter: no-tools ↔ default\n\t\t\tthis.filterMode = this.filterMode === \"no-tools\" ? \"default\" : \"no-tools\";\n\t\t\tthis.applyFilter();\n\t\t} else if (matchesKey(keyData, \"ctrl+u\")) {\n\t\t\t// Toggle filter: user-only ↔ default\n\t\t\tthis.filterMode = this.filterMode === \"user-only\" ? \"default\" : \"user-only\";\n\t\t\tthis.applyFilter();\n\t\t} else if (matchesKey(keyData, \"ctrl+l\")) {\n\t\t\t// Toggle filter: labeled-only ↔ default\n\t\t\tthis.filterMode = this.filterMode === \"labeled-only\" ? \"default\" : \"labeled-only\";\n\t\t\tthis.applyFilter();\n\t\t} else if (matchesKey(keyData, \"ctrl+a\")) {\n\t\t\t// Toggle filter: all ↔ default\n\t\t\tthis.filterMode = this.filterMode === \"all\" ? \"default\" : \"all\";\n\t\t\tthis.applyFilter();\n\t\t} else if (matchesKey(keyData, \"shift+ctrl+o\")) {\n\t\t\t// Cycle filter backwards\n\t\t\tconst modes: FilterMode[] = [\"default\", \"no-tools\", \"user-only\", \"labeled-only\", \"all\"];\n\t\t\tconst currentIndex = modes.indexOf(this.filterMode);\n\t\t\tthis.filterMode = modes[(currentIndex - 1 + modes.length) % modes.length];\n\t\t\tthis.applyFilter();\n\t\t} else if (matchesKey(keyData, \"ctrl+o\")) {\n\t\t\t// Cycle filter forwards: default → no-tools → user-only → labeled-only → all → default\n\t\t\tconst modes: FilterMode[] = [\"default\", \"no-tools\", \"user-only\", \"labeled-only\", \"all\"];\n\t\t\tconst currentIndex = modes.indexOf(this.filterMode);\n\t\t\tthis.filterMode = modes[(currentIndex + 1) % modes.length];\n\t\t\tthis.applyFilter();\n\t\t} else if (kb.matches(keyData, \"deleteCharBackward\")) {\n\t\t\tif (this.searchQuery.length > 0) {\n\t\t\t\tthis.searchQuery = this.searchQuery.slice(0, -1);\n\t\t\t\tthis.applyFilter();\n\t\t\t}\n\t\t} else if (matchesKey(keyData, \"shift+l\")) {\n\t\t\tconst selected = this.filteredNodes[this.selectedIndex];\n\t\t\tif (selected && this.onLabelEdit) {\n\t\t\t\tthis.onLabelEdit(selected.node.entry.id, selected.node.label);\n\t\t\t}\n\t\t} else {\n\t\t\tconst hasControlChars = [...keyData].some((ch) => {\n\t\t\t\tconst code = ch.charCodeAt(0);\n\t\t\t\treturn code < 32 || code === 0x7f || (code >= 0x80 && code <= 0x9f);\n\t\t\t});\n\t\t\tif (!hasControlChars && keyData.length > 0) {\n\t\t\t\tthis.searchQuery += keyData;\n\t\t\t\tthis.applyFilter();\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Component that displays the current search query */\nclass SearchLine implements Component {\n\tconstructor(private treeList: TreeList) {}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst query = this.treeList.getSearchQuery();\n\t\tif (query) {\n\t\t\treturn [truncateToWidth(` ${theme.fg(\"muted\", \"Type to search:\")} ${theme.fg(\"accent\", query)}`, width)];\n\t\t}\n\t\treturn [truncateToWidth(` ${theme.fg(\"muted\", \"Type to search:\")}`, width)];\n\t}\n\n\thandleInput(_keyData: string): void {}\n}\n\n/** Label input component shown when editing a label */\nclass LabelInput implements Component, Focusable {\n\tprivate input: Input;\n\tprivate entryId: string;\n\tpublic onSubmit?: (entryId: string, label: string | undefined) => void;\n\tpublic onCancel?: () => void;\n\n\t// Focusable implementation - propagate to input for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.input.focused = value;\n\t}\n\n\tconstructor(entryId: string, currentLabel: string | undefined) {\n\t\tthis.entryId = entryId;\n\t\tthis.input = new Input();\n\t\tif (currentLabel) {\n\t\t\tthis.input.setValue(currentLabel);\n\t\t}\n\t}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\t\tconst indent = \" \";\n\t\tconst availableWidth = width - indent.length;\n\t\tlines.push(truncateToWidth(`${indent}${theme.fg(\"muted\", \"Label (empty to remove):\")}`, width));\n\t\tlines.push(...this.input.render(availableWidth).map((line) => truncateToWidth(`${indent}${line}`, width)));\n\t\tlines.push(\n\t\t\ttruncateToWidth(`${indent}${keyHint(\"selectConfirm\", \"save\")} ${keyHint(\"selectCancel\", \"cancel\")}`, width),\n\t\t);\n\t\treturn lines;\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getEditorKeybindings();\n\t\tif (kb.matches(keyData, \"selectConfirm\")) {\n\t\t\tconst value = this.input.getValue().trim();\n\t\t\tthis.onSubmit?.(this.entryId, value || undefined);\n\t\t} else if (kb.matches(keyData, \"selectCancel\")) {\n\t\t\tthis.onCancel?.();\n\t\t} else {\n\t\t\tthis.input.handleInput(keyData);\n\t\t}\n\t}\n}\n\n/**\n * Component that renders a session tree selector for navigation\n */\nexport class TreeSelectorComponent extends Container implements Focusable {\n\tprivate treeList: TreeList;\n\tprivate labelInput: LabelInput | null = null;\n\tprivate labelInputContainer: Container;\n\tprivate treeContainer: Container;\n\tprivate onLabelChangeCallback?: (entryId: string, label: string | undefined) => void;\n\n\t// Focusable implementation - propagate to labelInput when active for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\t// Propagate to labelInput when it's active\n\t\tif (this.labelInput) {\n\t\t\tthis.labelInput.focused = value;\n\t\t}\n\t}\n\n\tconstructor(\n\t\ttree: SessionTreeNode[],\n\t\tcurrentLeafId: string | null,\n\t\tterminalHeight: number,\n\t\tonSelect: (entryId: string) => void,\n\t\tonCancel: () => void,\n\t\tonLabelChange?: (entryId: string, label: string | undefined) => void,\n\t\tinitialSelectedId?: string,\n\t) {\n\t\tsuper();\n\n\t\tthis.onLabelChangeCallback = onLabelChange;\n\t\tconst maxVisibleLines = Math.max(5, Math.floor(terminalHeight / 2));\n\n\t\tthis.treeList = new TreeList(tree, currentLeafId, maxVisibleLines, initialSelectedId);\n\t\tthis.treeList.onSelect = onSelect;\n\t\tthis.treeList.onCancel = onCancel;\n\t\tthis.treeList.onLabelEdit = (entryId, currentLabel) => this.showLabelInput(entryId, currentLabel);\n\n\t\tthis.treeContainer = new Container();\n\t\tthis.treeContainer.addChild(this.treeList);\n\n\t\tthis.labelInputContainer = new Container();\n\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Text(theme.bold(\" Session Tree\"), 1, 0));\n\t\tthis.addChild(\n\t\t\tnew TruncatedText(\n\t\t\t\ttheme.fg(\"muted\", \" ↑/↓: move. ←/→: page. Shift+L: label. \") +\n\t\t\t\t\ttheme.fg(\"muted\", \"^D/^T/^U/^L/^A: filters (^O/⇧^O cycle)\"),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new SearchLine(this.treeList));\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(this.treeContainer);\n\t\tthis.addChild(this.labelInputContainer);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\n\t\tif (tree.length === 0) {\n\t\t\tsetTimeout(() => onCancel(), 100);\n\t\t}\n\t}\n\n\tprivate showLabelInput(entryId: string, currentLabel: string | undefined): void {\n\t\tthis.labelInput = new LabelInput(entryId, currentLabel);\n\t\tthis.labelInput.onSubmit = (id, label) => {\n\t\t\tthis.treeList.updateNodeLabel(id, label);\n\t\t\tthis.onLabelChangeCallback?.(id, label);\n\t\t\tthis.hideLabelInput();\n\t\t};\n\t\tthis.labelInput.onCancel = () => this.hideLabelInput();\n\n\t\t// Propagate current focused state to the new labelInput\n\t\tthis.labelInput.focused = this._focused;\n\n\t\tthis.treeContainer.clear();\n\t\tthis.labelInputContainer.clear();\n\t\tthis.labelInputContainer.addChild(this.labelInput);\n\t}\n\n\tprivate hideLabelInput(): void {\n\t\tthis.labelInput = null;\n\t\tthis.labelInputContainer.clear();\n\t\tthis.treeContainer.clear();\n\t\tthis.treeContainer.addChild(this.treeList);\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tif (this.labelInput) {\n\t\t\tthis.labelInput.handleInput(keyData);\n\t\t} else {\n\t\t\tthis.treeList.handleInput(keyData);\n\t\t}\n\t}\n\n\tgetTreeList(): TreeList {\n\t\treturn this.treeList;\n\t}\n}\n"]}
|