@vaclav-synacek/pi-coding-agent-termux 0.50.7-1 → 0.51.1-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.
Files changed (168) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/dist/cli/args.d.ts.map +1 -1
  3. package/dist/cli/args.js +2 -0
  4. package/dist/cli/args.js.map +1 -1
  5. package/dist/cli/session-picker.d.ts.map +1 -1
  6. package/dist/cli/session-picker.js +3 -1
  7. package/dist/cli/session-picker.js.map +1 -1
  8. package/dist/config.d.ts.map +1 -1
  9. package/dist/config.js +9 -0
  10. package/dist/config.js.map +1 -1
  11. package/dist/core/agent-session.d.ts +5 -1
  12. package/dist/core/agent-session.d.ts.map +1 -1
  13. package/dist/core/agent-session.js +76 -15
  14. package/dist/core/agent-session.js.map +1 -1
  15. package/dist/core/extensions/index.d.ts +3 -3
  16. package/dist/core/extensions/index.d.ts.map +1 -1
  17. package/dist/core/extensions/index.js +1 -1
  18. package/dist/core/extensions/index.js.map +1 -1
  19. package/dist/core/extensions/runner.d.ts +19 -1
  20. package/dist/core/extensions/runner.d.ts.map +1 -1
  21. package/dist/core/extensions/runner.js +42 -0
  22. package/dist/core/extensions/runner.js.map +1 -1
  23. package/dist/core/extensions/types.d.ts +88 -6
  24. package/dist/core/extensions/types.d.ts.map +1 -1
  25. package/dist/core/extensions/types.js +4 -1
  26. package/dist/core/extensions/types.js.map +1 -1
  27. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  28. package/dist/core/extensions/wrapper.js +1 -1
  29. package/dist/core/extensions/wrapper.js.map +1 -1
  30. package/dist/core/keybindings.d.ts +1 -1
  31. package/dist/core/keybindings.d.ts.map +1 -1
  32. package/dist/core/keybindings.js +8 -0
  33. package/dist/core/keybindings.js.map +1 -1
  34. package/dist/core/model-registry.d.ts.map +1 -1
  35. package/dist/core/model-registry.js +27 -18
  36. package/dist/core/model-registry.js.map +1 -1
  37. package/dist/core/package-manager.d.ts.map +1 -1
  38. package/dist/core/package-manager.js +11 -9
  39. package/dist/core/package-manager.js.map +1 -1
  40. package/dist/core/resource-loader.d.ts +24 -0
  41. package/dist/core/resource-loader.d.ts.map +1 -1
  42. package/dist/core/resource-loader.js +88 -19
  43. package/dist/core/resource-loader.js.map +1 -1
  44. package/dist/core/sdk.d.ts.map +1 -1
  45. package/dist/core/sdk.js +1 -0
  46. package/dist/core/sdk.js.map +1 -1
  47. package/dist/core/session-manager.d.ts +2 -0
  48. package/dist/core/session-manager.d.ts.map +1 -1
  49. package/dist/core/session-manager.js +2 -0
  50. package/dist/core/session-manager.js.map +1 -1
  51. package/dist/core/settings-manager.d.ts +5 -0
  52. package/dist/core/settings-manager.d.ts.map +1 -1
  53. package/dist/core/settings-manager.js +16 -0
  54. package/dist/core/settings-manager.js.map +1 -1
  55. package/dist/core/skills.d.ts.map +1 -1
  56. package/dist/core/skills.js +1 -0
  57. package/dist/core/skills.js.map +1 -1
  58. package/dist/core/tools/bash.d.ts +11 -0
  59. package/dist/core/tools/bash.d.ts.map +1 -1
  60. package/dist/core/tools/bash.js +18 -3
  61. package/dist/core/tools/bash.js.map +1 -1
  62. package/dist/core/tools/edit.d.ts +2 -0
  63. package/dist/core/tools/edit.d.ts.map +1 -1
  64. package/dist/core/tools/edit.js.map +1 -1
  65. package/dist/core/tools/find.d.ts +2 -0
  66. package/dist/core/tools/find.d.ts.map +1 -1
  67. package/dist/core/tools/find.js.map +1 -1
  68. package/dist/core/tools/grep.d.ts +2 -0
  69. package/dist/core/tools/grep.d.ts.map +1 -1
  70. package/dist/core/tools/grep.js.map +1 -1
  71. package/dist/core/tools/index.d.ts +7 -7
  72. package/dist/core/tools/index.d.ts.map +1 -1
  73. package/dist/core/tools/index.js +5 -5
  74. package/dist/core/tools/index.js.map +1 -1
  75. package/dist/core/tools/ls.d.ts +2 -0
  76. package/dist/core/tools/ls.d.ts.map +1 -1
  77. package/dist/core/tools/ls.js.map +1 -1
  78. package/dist/core/tools/read.d.ts +2 -0
  79. package/dist/core/tools/read.d.ts.map +1 -1
  80. package/dist/core/tools/read.js.map +1 -1
  81. package/dist/core/tools/write.d.ts +2 -0
  82. package/dist/core/tools/write.d.ts.map +1 -1
  83. package/dist/core/tools/write.js.map +1 -1
  84. package/dist/index.d.ts +3 -3
  85. package/dist/index.d.ts.map +1 -1
  86. package/dist/index.js +1 -1
  87. package/dist/index.js.map +1 -1
  88. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  89. package/dist/modes/interactive/components/custom-message.js +0 -7
  90. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  91. package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
  92. package/dist/modes/interactive/components/daxnuts.js +1 -1
  93. package/dist/modes/interactive/components/daxnuts.js.map +1 -1
  94. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  95. package/dist/modes/interactive/components/scoped-models-selector.js +4 -1
  96. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  97. package/dist/modes/interactive/components/session-selector-search.d.ts +4 -2
  98. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
  99. package/dist/modes/interactive/components/session-selector-search.js +13 -4
  100. package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
  101. package/dist/modes/interactive/components/session-selector.d.ts +12 -2
  102. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  103. package/dist/modes/interactive/components/session-selector.js +188 -57
  104. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  105. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  106. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  107. package/dist/modes/interactive/components/settings-selector.js +12 -0
  108. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  109. package/dist/modes/interactive/components/tree-selector.d.ts +6 -0
  110. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  111. package/dist/modes/interactive/components/tree-selector.js +43 -16
  112. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  113. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  114. package/dist/modes/interactive/interactive-mode.js +24 -15
  115. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  116. package/dist/modes/print-mode.d.ts.map +1 -1
  117. package/dist/modes/print-mode.js +4 -0
  118. package/dist/modes/print-mode.js.map +1 -1
  119. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  120. package/dist/modes/rpc/rpc-mode.js +4 -0
  121. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  122. package/dist/utils/clipboard-image.d.ts.map +1 -1
  123. package/dist/utils/clipboard-image.js +52 -10
  124. package/dist/utils/clipboard-image.js.map +1 -1
  125. package/dist/utils/clipboard-native.d.ts +7 -0
  126. package/dist/utils/clipboard-native.d.ts.map +1 -0
  127. package/dist/utils/clipboard-native.js +14 -0
  128. package/dist/utils/clipboard-native.js.map +1 -0
  129. package/dist/utils/tools-manager.d.ts.map +1 -1
  130. package/dist/utils/tools-manager.js +14 -0
  131. package/dist/utils/tools-manager.js.map +1 -1
  132. package/docs/custom-provider.md +2 -1
  133. package/docs/extensions.md +57 -9
  134. package/docs/keybindings.md +9 -0
  135. package/docs/models.md +43 -14
  136. package/docs/rpc.md +188 -1
  137. package/docs/settings.md +5 -1
  138. package/docs/termux.md +127 -0
  139. package/examples/extensions/README.md +9 -0
  140. package/examples/extensions/antigravity-image-gen.ts +1 -1
  141. package/examples/extensions/bash-spawn-hook.ts +30 -0
  142. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  143. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  144. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  145. package/examples/extensions/custom-provider-qwen-cli/index.ts +345 -0
  146. package/examples/extensions/custom-provider-qwen-cli/package.json +16 -0
  147. package/examples/extensions/dynamic-resources/SKILL.md +8 -0
  148. package/examples/extensions/dynamic-resources/dynamic.json +79 -0
  149. package/examples/extensions/dynamic-resources/dynamic.md +5 -0
  150. package/examples/extensions/dynamic-resources/index.ts +15 -0
  151. package/examples/extensions/hello.ts +1 -1
  152. package/examples/extensions/question.ts +1 -1
  153. package/examples/extensions/questionnaire.ts +1 -1
  154. package/examples/extensions/rpc-demo.ts +124 -0
  155. package/examples/extensions/sandbox/index.ts +1 -1
  156. package/examples/extensions/shutdown-command.ts +2 -2
  157. package/examples/extensions/ssh.ts +4 -4
  158. package/examples/extensions/subagent/index.ts +1 -1
  159. package/examples/extensions/titlebar-spinner.ts +58 -0
  160. package/examples/extensions/todo.ts +1 -1
  161. package/examples/extensions/tool-override.ts +1 -1
  162. package/examples/extensions/truncated-tool.ts +1 -1
  163. package/examples/extensions/with-deps/package-lock.json +2 -2
  164. package/examples/extensions/with-deps/package.json +1 -1
  165. package/examples/rpc-extension-ui.ts +632 -0
  166. package/examples/sdk/06-extensions.ts +1 -1
  167. package/examples/sdk/12-full-control.ts +1 -0
  168. package/package.json +6 -5
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// Core session management\n\n// Config paths\nexport { getAgentDir, VERSION } from \"./config.js\";\nexport {\n\tAgentSession,\n\ttype AgentSessionConfig,\n\ttype AgentSessionEvent,\n\ttype AgentSessionEventListener,\n\ttype ModelCycleResult,\n\ttype ParsedSkillBlock,\n\ttype PromptOptions,\n\tparseSkillBlock,\n\ttype SessionStats,\n} from \"./core/agent-session.js\";\n// Auth and model registry\nexport { type ApiKeyCredential, type AuthCredential, AuthStorage, type OAuthCredential } from \"./core/auth-storage.js\";\n// Compaction\nexport {\n\ttype BranchPreparation,\n\ttype BranchSummaryResult,\n\ttype CollectEntriesResult,\n\ttype CompactionResult,\n\ttype CutPointResult,\n\tcalculateContextTokens,\n\tcollectEntriesForBranchSummary,\n\tcompact,\n\tDEFAULT_COMPACTION_SETTINGS,\n\testimateTokens,\n\ttype FileOperations,\n\tfindCutPoint,\n\tfindTurnStartIndex,\n\ttype GenerateBranchSummaryOptions,\n\tgenerateBranchSummary,\n\tgenerateSummary,\n\tgetLastAssistantUsage,\n\tprepareBranchEntries,\n\tserializeConversation,\n\tshouldCompact,\n} from \"./core/compaction/index.js\";\nexport { createEventBus, type EventBus, type EventBusController } from \"./core/event-bus.js\";\n// Extension system\nexport type {\n\tAgentEndEvent,\n\tAgentStartEvent,\n\tAgentToolResult,\n\tAgentToolUpdateCallback,\n\tAppAction,\n\tBeforeAgentStartEvent,\n\tCompactOptions,\n\tContextEvent,\n\tContextUsage,\n\tExecOptions,\n\tExecResult,\n\tExtension,\n\tExtensionActions,\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionCommandContextActions,\n\tExtensionContext,\n\tExtensionContextActions,\n\tExtensionError,\n\tExtensionEvent,\n\tExtensionFactory,\n\tExtensionFlag,\n\tExtensionHandler,\n\tExtensionRuntime,\n\tExtensionShortcut,\n\tExtensionUIContext,\n\tExtensionUIDialogOptions,\n\tExtensionWidgetOptions,\n\tInputEvent,\n\tInputEventResult,\n\tInputSource,\n\tKeybindingsManager,\n\tLoadExtensionsResult,\n\tMessageRenderer,\n\tMessageRenderOptions,\n\tProviderConfig,\n\tProviderModelConfig,\n\tRegisteredCommand,\n\tRegisteredTool,\n\tSessionBeforeCompactEvent,\n\tSessionBeforeForkEvent,\n\tSessionBeforeSwitchEvent,\n\tSessionBeforeTreeEvent,\n\tSessionCompactEvent,\n\tSessionForkEvent,\n\tSessionShutdownEvent,\n\tSessionStartEvent,\n\tSessionSwitchEvent,\n\tSessionTreeEvent,\n\tToolCallEvent,\n\tToolDefinition,\n\tToolInfo,\n\tToolRenderResultOptions,\n\tToolResultEvent,\n\tTurnEndEvent,\n\tTurnStartEvent,\n\tUserBashEvent,\n\tUserBashEventResult,\n\tWidgetPlacement,\n} from \"./core/extensions/index.js\";\nexport {\n\tcreateExtensionRuntime,\n\tExtensionRunner,\n\tisBashToolResult,\n\tisEditToolResult,\n\tisFindToolResult,\n\tisGrepToolResult,\n\tisLsToolResult,\n\tisReadToolResult,\n\tisWriteToolResult,\n\twrapRegisteredTool,\n\twrapRegisteredTools,\n\twrapToolsWithExtensions,\n\twrapToolWithExtensions,\n} from \"./core/extensions/index.js\";\n// Footer data provider (git branch + extension statuses - data not otherwise available to extensions)\nexport type { ReadonlyFooterDataProvider } from \"./core/footer-data-provider.js\";\nexport { convertToLlm } from \"./core/messages.js\";\nexport { ModelRegistry } from \"./core/model-registry.js\";\nexport type {\n\tPackageManager,\n\tPathMetadata,\n\tProgressCallback,\n\tProgressEvent,\n\tResolvedPaths,\n\tResolvedResource,\n} from \"./core/package-manager.js\";\nexport { DefaultPackageManager } from \"./core/package-manager.js\";\nexport type { ResourceCollision, ResourceDiagnostic, ResourceLoader } from \"./core/resource-loader.js\";\nexport { DefaultResourceLoader } from \"./core/resource-loader.js\";\n// SDK for programmatic usage\nexport {\n\ttype CreateAgentSessionOptions,\n\ttype CreateAgentSessionResult,\n\t// Factory\n\tcreateAgentSession,\n\tcreateBashTool,\n\t// Tool factories (for custom cwd)\n\tcreateCodingTools,\n\tcreateEditTool,\n\tcreateFindTool,\n\tcreateGrepTool,\n\tcreateLsTool,\n\tcreateReadOnlyTools,\n\tcreateReadTool,\n\tcreateWriteTool,\n\ttype PromptTemplate,\n\t// Pre-built tools (use process.cwd())\n\treadOnlyTools,\n} from \"./core/sdk.js\";\nexport {\n\ttype BranchSummaryEntry,\n\tbuildSessionContext,\n\ttype CompactionEntry,\n\tCURRENT_SESSION_VERSION,\n\ttype CustomEntry,\n\ttype CustomMessageEntry,\n\ttype FileEntry,\n\tgetLatestCompactionEntry,\n\ttype ModelChangeEntry,\n\tmigrateSessionEntries,\n\ttype NewSessionOptions,\n\tparseSessionEntries,\n\ttype SessionContext,\n\ttype SessionEntry,\n\ttype SessionEntryBase,\n\ttype SessionHeader,\n\ttype SessionInfo,\n\ttype SessionInfoEntry,\n\tSessionManager,\n\ttype SessionMessageEntry,\n\ttype ThinkingLevelChangeEntry,\n} from \"./core/session-manager.js\";\nexport {\n\ttype CompactionSettings,\n\ttype ImageSettings,\n\ttype PackageSource,\n\ttype RetrySettings,\n\tSettingsManager,\n} from \"./core/settings-manager.js\";\n// Skills\nexport {\n\tformatSkillsForPrompt,\n\ttype LoadSkillsFromDirOptions,\n\ttype LoadSkillsResult,\n\tloadSkills,\n\tloadSkillsFromDir,\n\ttype Skill,\n\ttype SkillFrontmatter,\n} from \"./core/skills.js\";\n// Tools\nexport {\n\ttype BashOperations,\n\ttype BashToolDetails,\n\ttype BashToolOptions,\n\tbashTool,\n\tcodingTools,\n\tDEFAULT_MAX_BYTES,\n\tDEFAULT_MAX_LINES,\n\ttype EditOperations,\n\ttype EditToolDetails,\n\ttype EditToolOptions,\n\teditTool,\n\ttype FindOperations,\n\ttype FindToolDetails,\n\ttype FindToolOptions,\n\tfindTool,\n\tformatSize,\n\ttype GrepOperations,\n\ttype GrepToolDetails,\n\ttype GrepToolOptions,\n\tgrepTool,\n\ttype LsOperations,\n\ttype LsToolDetails,\n\ttype LsToolOptions,\n\tlsTool,\n\ttype ReadOperations,\n\ttype ReadToolDetails,\n\ttype ReadToolOptions,\n\treadTool,\n\ttype ToolsOptions,\n\ttype TruncationOptions,\n\ttype TruncationResult,\n\ttruncateHead,\n\ttruncateLine,\n\ttruncateTail,\n\ttype WriteOperations,\n\ttype WriteToolOptions,\n\twriteTool,\n} from \"./core/tools/index.js\";\n// Main entry point\nexport { main } from \"./main.js\";\n// Run modes for programmatic SDK usage\nexport {\n\tInteractiveMode,\n\ttype InteractiveModeOptions,\n\ttype PrintModeOptions,\n\trunPrintMode,\n\trunRpcMode,\n} from \"./modes/index.js\";\n// UI components for extensions\nexport {\n\tArminComponent,\n\tAssistantMessageComponent,\n\tappKey,\n\tappKeyHint,\n\tBashExecutionComponent,\n\tBorderedLoader,\n\tBranchSummaryMessageComponent,\n\tCompactionSummaryMessageComponent,\n\tCustomEditor,\n\tCustomMessageComponent,\n\tDynamicBorder,\n\tExtensionEditorComponent,\n\tExtensionInputComponent,\n\tExtensionSelectorComponent,\n\teditorKey,\n\tFooterComponent,\n\tkeyHint,\n\tLoginDialogComponent,\n\tModelSelectorComponent,\n\tOAuthSelectorComponent,\n\ttype RenderDiffOptions,\n\trawKeyHint,\n\trenderDiff,\n\tSessionSelectorComponent,\n\ttype SettingsCallbacks,\n\ttype SettingsConfig,\n\tSettingsSelectorComponent,\n\tShowImagesSelectorComponent,\n\tSkillInvocationMessageComponent,\n\tThemeSelectorComponent,\n\tThinkingSelectorComponent,\n\tToolExecutionComponent,\n\ttype ToolExecutionOptions,\n\tTreeSelectorComponent,\n\ttruncateToVisualLines,\n\tUserMessageComponent,\n\tUserMessageSelectorComponent,\n\ttype VisualTruncateResult,\n} from \"./modes/interactive/components/index.js\";\n// Theme utilities for custom tools and extensions\nexport {\n\tgetLanguageFromPath,\n\tgetMarkdownTheme,\n\tgetSelectListTheme,\n\tgetSettingsListTheme,\n\thighlightCode,\n\tinitTheme,\n\tTheme,\n\ttype ThemeColor,\n} from \"./modes/interactive/theme/theme.js\";\n// Clipboard utilities\nexport { copyToClipboard } from \"./utils/clipboard.js\";\nexport { parseFrontmatter, stripFrontmatter } from \"./utils/frontmatter.js\";\n// Shell utilities\nexport { getShellConfig } from \"./utils/shell.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// Core session management\n\n// Config paths\nexport { getAgentDir, VERSION } from \"./config.js\";\nexport {\n\tAgentSession,\n\ttype AgentSessionConfig,\n\ttype AgentSessionEvent,\n\ttype AgentSessionEventListener,\n\ttype ModelCycleResult,\n\ttype ParsedSkillBlock,\n\ttype PromptOptions,\n\tparseSkillBlock,\n\ttype SessionStats,\n} from \"./core/agent-session.js\";\n// Auth and model registry\nexport { type ApiKeyCredential, type AuthCredential, AuthStorage, type OAuthCredential } from \"./core/auth-storage.js\";\n// Compaction\nexport {\n\ttype BranchPreparation,\n\ttype BranchSummaryResult,\n\ttype CollectEntriesResult,\n\ttype CompactionResult,\n\ttype CutPointResult,\n\tcalculateContextTokens,\n\tcollectEntriesForBranchSummary,\n\tcompact,\n\tDEFAULT_COMPACTION_SETTINGS,\n\testimateTokens,\n\ttype FileOperations,\n\tfindCutPoint,\n\tfindTurnStartIndex,\n\ttype GenerateBranchSummaryOptions,\n\tgenerateBranchSummary,\n\tgenerateSummary,\n\tgetLastAssistantUsage,\n\tprepareBranchEntries,\n\tserializeConversation,\n\tshouldCompact,\n} from \"./core/compaction/index.js\";\nexport { createEventBus, type EventBus, type EventBusController } from \"./core/event-bus.js\";\n// Extension system\nexport type {\n\tAgentEndEvent,\n\tAgentStartEvent,\n\tAgentToolResult,\n\tAgentToolUpdateCallback,\n\tAppAction,\n\tBashToolCallEvent,\n\tBeforeAgentStartEvent,\n\tCompactOptions,\n\tContextEvent,\n\tContextUsage,\n\tCustomToolCallEvent,\n\tEditToolCallEvent,\n\tExecOptions,\n\tExecResult,\n\tExtension,\n\tExtensionActions,\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionCommandContextActions,\n\tExtensionContext,\n\tExtensionContextActions,\n\tExtensionError,\n\tExtensionEvent,\n\tExtensionFactory,\n\tExtensionFlag,\n\tExtensionHandler,\n\tExtensionRuntime,\n\tExtensionShortcut,\n\tExtensionUIContext,\n\tExtensionUIDialogOptions,\n\tExtensionWidgetOptions,\n\tFindToolCallEvent,\n\tGrepToolCallEvent,\n\tInputEvent,\n\tInputEventResult,\n\tInputSource,\n\tKeybindingsManager,\n\tLoadExtensionsResult,\n\tLsToolCallEvent,\n\tMessageRenderer,\n\tMessageRenderOptions,\n\tProviderConfig,\n\tProviderModelConfig,\n\tReadToolCallEvent,\n\tRegisteredCommand,\n\tRegisteredTool,\n\tSessionBeforeCompactEvent,\n\tSessionBeforeForkEvent,\n\tSessionBeforeSwitchEvent,\n\tSessionBeforeTreeEvent,\n\tSessionCompactEvent,\n\tSessionForkEvent,\n\tSessionShutdownEvent,\n\tSessionStartEvent,\n\tSessionSwitchEvent,\n\tSessionTreeEvent,\n\tToolCallEvent,\n\tToolDefinition,\n\tToolInfo,\n\tToolRenderResultOptions,\n\tToolResultEvent,\n\tTurnEndEvent,\n\tTurnStartEvent,\n\tUserBashEvent,\n\tUserBashEventResult,\n\tWidgetPlacement,\n\tWriteToolCallEvent,\n} from \"./core/extensions/index.js\";\nexport {\n\tcreateExtensionRuntime,\n\tdiscoverAndLoadExtensions,\n\tExtensionRunner,\n\tisBashToolResult,\n\tisEditToolResult,\n\tisFindToolResult,\n\tisGrepToolResult,\n\tisLsToolResult,\n\tisReadToolResult,\n\tisToolCallEventType,\n\tisWriteToolResult,\n\twrapRegisteredTool,\n\twrapRegisteredTools,\n\twrapToolsWithExtensions,\n\twrapToolWithExtensions,\n} from \"./core/extensions/index.js\";\n// Footer data provider (git branch + extension statuses - data not otherwise available to extensions)\nexport type { ReadonlyFooterDataProvider } from \"./core/footer-data-provider.js\";\nexport { convertToLlm } from \"./core/messages.js\";\nexport { ModelRegistry } from \"./core/model-registry.js\";\nexport type {\n\tPackageManager,\n\tPathMetadata,\n\tProgressCallback,\n\tProgressEvent,\n\tResolvedPaths,\n\tResolvedResource,\n} from \"./core/package-manager.js\";\nexport { DefaultPackageManager } from \"./core/package-manager.js\";\nexport type { ResourceCollision, ResourceDiagnostic, ResourceLoader } from \"./core/resource-loader.js\";\nexport { DefaultResourceLoader } from \"./core/resource-loader.js\";\n// SDK for programmatic usage\nexport {\n\ttype CreateAgentSessionOptions,\n\ttype CreateAgentSessionResult,\n\t// Factory\n\tcreateAgentSession,\n\tcreateBashTool,\n\t// Tool factories (for custom cwd)\n\tcreateCodingTools,\n\tcreateEditTool,\n\tcreateFindTool,\n\tcreateGrepTool,\n\tcreateLsTool,\n\tcreateReadOnlyTools,\n\tcreateReadTool,\n\tcreateWriteTool,\n\ttype PromptTemplate,\n\t// Pre-built tools (use process.cwd())\n\treadOnlyTools,\n} from \"./core/sdk.js\";\nexport {\n\ttype BranchSummaryEntry,\n\tbuildSessionContext,\n\ttype CompactionEntry,\n\tCURRENT_SESSION_VERSION,\n\ttype CustomEntry,\n\ttype CustomMessageEntry,\n\ttype FileEntry,\n\tgetLatestCompactionEntry,\n\ttype ModelChangeEntry,\n\tmigrateSessionEntries,\n\ttype NewSessionOptions,\n\tparseSessionEntries,\n\ttype SessionContext,\n\ttype SessionEntry,\n\ttype SessionEntryBase,\n\ttype SessionHeader,\n\ttype SessionInfo,\n\ttype SessionInfoEntry,\n\tSessionManager,\n\ttype SessionMessageEntry,\n\ttype ThinkingLevelChangeEntry,\n} from \"./core/session-manager.js\";\nexport {\n\ttype CompactionSettings,\n\ttype ImageSettings,\n\ttype PackageSource,\n\ttype RetrySettings,\n\tSettingsManager,\n} from \"./core/settings-manager.js\";\n// Skills\nexport {\n\tformatSkillsForPrompt,\n\ttype LoadSkillsFromDirOptions,\n\ttype LoadSkillsResult,\n\tloadSkills,\n\tloadSkillsFromDir,\n\ttype Skill,\n\ttype SkillFrontmatter,\n} from \"./core/skills.js\";\n// Tools\nexport {\n\ttype BashOperations,\n\ttype BashSpawnContext,\n\ttype BashSpawnHook,\n\ttype BashToolDetails,\n\ttype BashToolInput,\n\ttype BashToolOptions,\n\tbashTool,\n\tcodingTools,\n\tDEFAULT_MAX_BYTES,\n\tDEFAULT_MAX_LINES,\n\ttype EditOperations,\n\ttype EditToolDetails,\n\ttype EditToolInput,\n\ttype EditToolOptions,\n\teditTool,\n\ttype FindOperations,\n\ttype FindToolDetails,\n\ttype FindToolInput,\n\ttype FindToolOptions,\n\tfindTool,\n\tformatSize,\n\ttype GrepOperations,\n\ttype GrepToolDetails,\n\ttype GrepToolInput,\n\ttype GrepToolOptions,\n\tgrepTool,\n\ttype LsOperations,\n\ttype LsToolDetails,\n\ttype LsToolInput,\n\ttype LsToolOptions,\n\tlsTool,\n\ttype ReadOperations,\n\ttype ReadToolDetails,\n\ttype ReadToolInput,\n\ttype ReadToolOptions,\n\treadTool,\n\ttype ToolsOptions,\n\ttype TruncationOptions,\n\ttype TruncationResult,\n\ttruncateHead,\n\ttruncateLine,\n\ttruncateTail,\n\ttype WriteOperations,\n\ttype WriteToolInput,\n\ttype WriteToolOptions,\n\twriteTool,\n} from \"./core/tools/index.js\";\n// Main entry point\nexport { main } from \"./main.js\";\n// Run modes for programmatic SDK usage\nexport {\n\tInteractiveMode,\n\ttype InteractiveModeOptions,\n\ttype PrintModeOptions,\n\trunPrintMode,\n\trunRpcMode,\n} from \"./modes/index.js\";\n// UI components for extensions\nexport {\n\tArminComponent,\n\tAssistantMessageComponent,\n\tappKey,\n\tappKeyHint,\n\tBashExecutionComponent,\n\tBorderedLoader,\n\tBranchSummaryMessageComponent,\n\tCompactionSummaryMessageComponent,\n\tCustomEditor,\n\tCustomMessageComponent,\n\tDynamicBorder,\n\tExtensionEditorComponent,\n\tExtensionInputComponent,\n\tExtensionSelectorComponent,\n\teditorKey,\n\tFooterComponent,\n\tkeyHint,\n\tLoginDialogComponent,\n\tModelSelectorComponent,\n\tOAuthSelectorComponent,\n\ttype RenderDiffOptions,\n\trawKeyHint,\n\trenderDiff,\n\tSessionSelectorComponent,\n\ttype SettingsCallbacks,\n\ttype SettingsConfig,\n\tSettingsSelectorComponent,\n\tShowImagesSelectorComponent,\n\tSkillInvocationMessageComponent,\n\tThemeSelectorComponent,\n\tThinkingSelectorComponent,\n\tToolExecutionComponent,\n\ttype ToolExecutionOptions,\n\tTreeSelectorComponent,\n\ttruncateToVisualLines,\n\tUserMessageComponent,\n\tUserMessageSelectorComponent,\n\ttype VisualTruncateResult,\n} from \"./modes/interactive/components/index.js\";\n// Theme utilities for custom tools and extensions\nexport {\n\tgetLanguageFromPath,\n\tgetMarkdownTheme,\n\tgetSelectListTheme,\n\tgetSettingsListTheme,\n\thighlightCode,\n\tinitTheme,\n\tTheme,\n\ttype ThemeColor,\n} from \"./modes/interactive/theme/theme.js\";\n// Clipboard utilities\nexport { copyToClipboard } from \"./utils/clipboard.js\";\nexport { parseFrontmatter, stripFrontmatter } from \"./utils/frontmatter.js\";\n// Shell utilities\nexport { getShellConfig } from \"./utils/shell.js\";\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"custom-message.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/custom-message.ts"],"names":[],"mappings":"AAEA,OAAO,EAAO,SAAS,EAAY,KAAK,aAAa,EAAgB,MAAM,sBAAsB,CAAC;AAClG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAG/D;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,SAAS;IACpD,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,cAAc,CAAC,CAAkB;IACzC,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,eAAe,CAAC,CAAY;IACpC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,SAAS,CAAS;IAE1B,YACC,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,EAC/B,cAAc,CAAC,EAAE,eAAe,EAChC,aAAa,GAAE,aAAkC,EAajD;IAED,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAKnC;IAEQ,UAAU,IAAI,IAAI,CAG1B;IAED,OAAO,CAAC,OAAO;CAyDf","sourcesContent":["import type { TextContent } from \"@mariozechner/pi-ai\";\nimport type { Component } from \"@mariozechner/pi-tui\";\nimport { Box, Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@mariozechner/pi-tui\";\nimport type { MessageRenderer } from \"../../../core/extensions/types.js\";\nimport type { CustomMessage } from \"../../../core/messages.js\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.js\";\n\n/**\n * Component that renders a custom message entry from extensions.\n * Uses distinct styling to differentiate from user messages.\n */\nexport class CustomMessageComponent extends Container {\n\tprivate message: CustomMessage<unknown>;\n\tprivate customRenderer?: MessageRenderer;\n\tprivate box: Box;\n\tprivate customComponent?: Component;\n\tprivate markdownTheme: MarkdownTheme;\n\tprivate _expanded = false;\n\n\tconstructor(\n\t\tmessage: CustomMessage<unknown>,\n\t\tcustomRenderer?: MessageRenderer,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t) {\n\t\tsuper();\n\t\tthis.message = message;\n\t\tthis.customRenderer = customRenderer;\n\t\tthis.markdownTheme = markdownTheme;\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create box with purple background (used for default rendering)\n\t\tthis.box = new Box(1, 1, (t) => theme.bg(\"customMessageBg\", t));\n\n\t\tthis.rebuild();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tif (this._expanded !== expanded) {\n\t\t\tthis._expanded = expanded;\n\t\t\tthis.rebuild();\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.rebuild();\n\t}\n\n\tprivate rebuild(): void {\n\t\t// Remove previous content component\n\t\tif (this.customComponent) {\n\t\t\tthis.removeChild(this.customComponent);\n\t\t\tthis.customComponent = undefined;\n\t\t}\n\t\tthis.removeChild(this.box);\n\n\t\t// Try custom renderer first - it handles its own styling\n\t\tif (this.customRenderer) {\n\t\t\ttry {\n\t\t\t\tconst component = this.customRenderer(this.message, { expanded: this._expanded }, theme);\n\t\t\t\tif (component) {\n\t\t\t\t\t// Custom renderer provides its own styled component\n\t\t\t\t\tthis.customComponent = component;\n\t\t\t\t\tthis.addChild(component);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Fall through to default rendering\n\t\t\t}\n\t\t}\n\n\t\t// Default rendering uses our box\n\t\tthis.addChild(this.box);\n\t\tthis.box.clear();\n\n\t\t// Default rendering: label + content\n\t\tconst label = theme.fg(\"customMessageLabel\", `\\x1b[1m[${this.message.customType}]\\x1b[22m`);\n\t\tthis.box.addChild(new Text(label, 0, 0));\n\t\tthis.box.addChild(new Spacer(1));\n\n\t\t// Extract text content\n\t\tlet text: string;\n\t\tif (typeof this.message.content === \"string\") {\n\t\t\ttext = this.message.content;\n\t\t} else {\n\t\t\ttext = this.message.content\n\t\t\t\t.filter((c): c is TextContent => c.type === \"text\")\n\t\t\t\t.map((c) => c.text)\n\t\t\t\t.join(\"\\n\");\n\t\t}\n\n\t\t// Limit lines when collapsed\n\t\tif (!this._expanded) {\n\t\t\tconst lines = text.split(\"\\n\");\n\t\t\tif (lines.length > 5) {\n\t\t\t\ttext = `${lines.slice(0, 5).join(\"\\n\")}\\n...`;\n\t\t\t}\n\t\t}\n\n\t\tthis.box.addChild(\n\t\t\tnew Markdown(text, 0, 0, this.markdownTheme, {\n\t\t\t\tcolor: (text: string) => theme.fg(\"customMessageText\", text),\n\t\t\t}),\n\t\t);\n\t}\n}\n"]}
1
+ {"version":3,"file":"custom-message.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/custom-message.ts"],"names":[],"mappings":"AAEA,OAAO,EAAO,SAAS,EAAY,KAAK,aAAa,EAAgB,MAAM,sBAAsB,CAAC;AAClG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAG/D;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,SAAS;IACpD,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,cAAc,CAAC,CAAkB;IACzC,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,eAAe,CAAC,CAAY;IACpC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,SAAS,CAAS;IAE1B,YACC,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,EAC/B,cAAc,CAAC,EAAE,eAAe,EAChC,aAAa,GAAE,aAAkC,EAajD;IAED,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAKnC;IAEQ,UAAU,IAAI,IAAI,CAG1B;IAED,OAAO,CAAC,OAAO;CAiDf","sourcesContent":["import type { TextContent } from \"@mariozechner/pi-ai\";\nimport type { Component } from \"@mariozechner/pi-tui\";\nimport { Box, Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@mariozechner/pi-tui\";\nimport type { MessageRenderer } from \"../../../core/extensions/types.js\";\nimport type { CustomMessage } from \"../../../core/messages.js\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.js\";\n\n/**\n * Component that renders a custom message entry from extensions.\n * Uses distinct styling to differentiate from user messages.\n */\nexport class CustomMessageComponent extends Container {\n\tprivate message: CustomMessage<unknown>;\n\tprivate customRenderer?: MessageRenderer;\n\tprivate box: Box;\n\tprivate customComponent?: Component;\n\tprivate markdownTheme: MarkdownTheme;\n\tprivate _expanded = false;\n\n\tconstructor(\n\t\tmessage: CustomMessage<unknown>,\n\t\tcustomRenderer?: MessageRenderer,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t) {\n\t\tsuper();\n\t\tthis.message = message;\n\t\tthis.customRenderer = customRenderer;\n\t\tthis.markdownTheme = markdownTheme;\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create box with purple background (used for default rendering)\n\t\tthis.box = new Box(1, 1, (t) => theme.bg(\"customMessageBg\", t));\n\n\t\tthis.rebuild();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tif (this._expanded !== expanded) {\n\t\t\tthis._expanded = expanded;\n\t\t\tthis.rebuild();\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.rebuild();\n\t}\n\n\tprivate rebuild(): void {\n\t\t// Remove previous content component\n\t\tif (this.customComponent) {\n\t\t\tthis.removeChild(this.customComponent);\n\t\t\tthis.customComponent = undefined;\n\t\t}\n\t\tthis.removeChild(this.box);\n\n\t\t// Try custom renderer first - it handles its own styling\n\t\tif (this.customRenderer) {\n\t\t\ttry {\n\t\t\t\tconst component = this.customRenderer(this.message, { expanded: this._expanded }, theme);\n\t\t\t\tif (component) {\n\t\t\t\t\t// Custom renderer provides its own styled component\n\t\t\t\t\tthis.customComponent = component;\n\t\t\t\t\tthis.addChild(component);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Fall through to default rendering\n\t\t\t}\n\t\t}\n\n\t\t// Default rendering uses our box\n\t\tthis.addChild(this.box);\n\t\tthis.box.clear();\n\n\t\t// Default rendering: label + content\n\t\tconst label = theme.fg(\"customMessageLabel\", `\\x1b[1m[${this.message.customType}]\\x1b[22m`);\n\t\tthis.box.addChild(new Text(label, 0, 0));\n\t\tthis.box.addChild(new Spacer(1));\n\n\t\t// Extract text content\n\t\tlet text: string;\n\t\tif (typeof this.message.content === \"string\") {\n\t\t\ttext = this.message.content;\n\t\t} else {\n\t\t\ttext = this.message.content\n\t\t\t\t.filter((c): c is TextContent => c.type === \"text\")\n\t\t\t\t.map((c) => c.text)\n\t\t\t\t.join(\"\\n\");\n\t\t}\n\n\t\tthis.box.addChild(\n\t\t\tnew Markdown(text, 0, 0, this.markdownTheme, {\n\t\t\t\tcolor: (text: string) => theme.fg(\"customMessageText\", text),\n\t\t\t}),\n\t\t);\n\t}\n}\n"]}
@@ -71,13 +71,6 @@ export class CustomMessageComponent extends Container {
71
71
  .map((c) => c.text)
72
72
  .join("\n");
73
73
  }
74
- // Limit lines when collapsed
75
- if (!this._expanded) {
76
- const lines = text.split("\n");
77
- if (lines.length > 5) {
78
- text = `${lines.slice(0, 5).join("\n")}\n...`;
79
- }
80
- }
81
74
  this.box.addChild(new Markdown(text, 0, 0, this.markdownTheme, {
82
75
  color: (text) => theme.fg("customMessageText", text),
83
76
  }));
@@ -1 +1 @@
1
- {"version":3,"file":"custom-message.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/custom-message.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TextContent } from \"@mariozechner/pi-ai\";\nimport type { Component } from \"@mariozechner/pi-tui\";\nimport { Box, Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@mariozechner/pi-tui\";\nimport type { MessageRenderer } from \"../../../core/extensions/types.js\";\nimport type { CustomMessage } from \"../../../core/messages.js\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.js\";\n\n/**\n * Component that renders a custom message entry from extensions.\n * Uses distinct styling to differentiate from user messages.\n */\nexport class CustomMessageComponent extends Container {\n\tprivate message: CustomMessage<unknown>;\n\tprivate customRenderer?: MessageRenderer;\n\tprivate box: Box;\n\tprivate customComponent?: Component;\n\tprivate markdownTheme: MarkdownTheme;\n\tprivate _expanded = false;\n\n\tconstructor(\n\t\tmessage: CustomMessage<unknown>,\n\t\tcustomRenderer?: MessageRenderer,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t) {\n\t\tsuper();\n\t\tthis.message = message;\n\t\tthis.customRenderer = customRenderer;\n\t\tthis.markdownTheme = markdownTheme;\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create box with purple background (used for default rendering)\n\t\tthis.box = new Box(1, 1, (t) => theme.bg(\"customMessageBg\", t));\n\n\t\tthis.rebuild();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tif (this._expanded !== expanded) {\n\t\t\tthis._expanded = expanded;\n\t\t\tthis.rebuild();\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.rebuild();\n\t}\n\n\tprivate rebuild(): void {\n\t\t// Remove previous content component\n\t\tif (this.customComponent) {\n\t\t\tthis.removeChild(this.customComponent);\n\t\t\tthis.customComponent = undefined;\n\t\t}\n\t\tthis.removeChild(this.box);\n\n\t\t// Try custom renderer first - it handles its own styling\n\t\tif (this.customRenderer) {\n\t\t\ttry {\n\t\t\t\tconst component = this.customRenderer(this.message, { expanded: this._expanded }, theme);\n\t\t\t\tif (component) {\n\t\t\t\t\t// Custom renderer provides its own styled component\n\t\t\t\t\tthis.customComponent = component;\n\t\t\t\t\tthis.addChild(component);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Fall through to default rendering\n\t\t\t}\n\t\t}\n\n\t\t// Default rendering uses our box\n\t\tthis.addChild(this.box);\n\t\tthis.box.clear();\n\n\t\t// Default rendering: label + content\n\t\tconst label = theme.fg(\"customMessageLabel\", `\\x1b[1m[${this.message.customType}]\\x1b[22m`);\n\t\tthis.box.addChild(new Text(label, 0, 0));\n\t\tthis.box.addChild(new Spacer(1));\n\n\t\t// Extract text content\n\t\tlet text: string;\n\t\tif (typeof this.message.content === \"string\") {\n\t\t\ttext = this.message.content;\n\t\t} else {\n\t\t\ttext = this.message.content\n\t\t\t\t.filter((c): c is TextContent => c.type === \"text\")\n\t\t\t\t.map((c) => c.text)\n\t\t\t\t.join(\"\\n\");\n\t\t}\n\n\t\t// Limit lines when collapsed\n\t\tif (!this._expanded) {\n\t\t\tconst lines = text.split(\"\\n\");\n\t\t\tif (lines.length > 5) {\n\t\t\t\ttext = `${lines.slice(0, 5).join(\"\\n\")}\\n...`;\n\t\t\t}\n\t\t}\n\n\t\tthis.box.addChild(\n\t\t\tnew Markdown(text, 0, 0, this.markdownTheme, {\n\t\t\t\tcolor: (text: string) => theme.fg(\"customMessageText\", text),\n\t\t\t}),\n\t\t);\n\t}\n}\n"]}
1
+ {"version":3,"file":"custom-message.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/custom-message.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAsB,MAAM,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAGlG,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE5D;;;GAGG;AACH,MAAM,OAAO,sBAAuB,SAAQ,SAAS;IAC5C,OAAO,CAAyB;IAChC,cAAc,CAAmB;IACjC,GAAG,CAAM;IACT,eAAe,CAAa;IAC5B,aAAa,CAAgB;IAC7B,SAAS,GAAG,KAAK,CAAC;IAE1B,YACC,OAA+B,EAC/B,cAAgC,EAChC,aAAa,GAAkB,gBAAgB,EAAE,EAChD;QACD,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QAEnC,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAE7B,iEAAiE;QACjE,IAAI,CAAC,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,CAAC;QAEhE,IAAI,CAAC,OAAO,EAAE,CAAC;IAAA,CACf;IAED,WAAW,CAAC,QAAiB,EAAQ;QACpC,IAAI,IAAI,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;YACjC,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;YAC1B,IAAI,CAAC,OAAO,EAAE,CAAC;QAChB,CAAC;IAAA,CACD;IAEQ,UAAU,GAAS;QAC3B,KAAK,CAAC,UAAU,EAAE,CAAC;QACnB,IAAI,CAAC,OAAO,EAAE,CAAC;IAAA,CACf;IAEO,OAAO,GAAS;QACvB,oCAAoC;QACpC,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YAC1B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YACvC,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;QAClC,CAAC;QACD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAE3B,yDAAyD;QACzD,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACzB,IAAI,CAAC;gBACJ,MAAM,SAAS,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,KAAK,CAAC,CAAC;gBACzF,IAAI,SAAS,EAAE,CAAC;oBACf,oDAAoD;oBACpD,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;oBACjC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;oBACzB,OAAO;gBACR,CAAC;YACF,CAAC;YAAC,MAAM,CAAC;gBACR,oCAAoC;YACrC,CAAC;QACF,CAAC;QAED,iCAAiC;QACjC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACxB,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;QAEjB,qCAAqC;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,oBAAoB,EAAE,WAAW,IAAI,CAAC,OAAO,CAAC,UAAU,WAAW,CAAC,CAAC;QAC5F,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACzC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAEjC,uBAAuB;QACvB,IAAI,IAAY,CAAC;QACjB,IAAI,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC9C,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;QAC7B,CAAC;aAAM,CAAC;YACP,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO;iBACzB,MAAM,CAAC,CAAC,CAAC,EAAoB,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;iBAClD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;iBAClB,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,QAAQ,CAChB,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE;YAC5C,KAAK,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,mBAAmB,EAAE,IAAI,CAAC;SAC5D,CAAC,CACF,CAAC;IAAA,CACF;CACD","sourcesContent":["import type { TextContent } from \"@mariozechner/pi-ai\";\nimport type { Component } from \"@mariozechner/pi-tui\";\nimport { Box, Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@mariozechner/pi-tui\";\nimport type { MessageRenderer } from \"../../../core/extensions/types.js\";\nimport type { CustomMessage } from \"../../../core/messages.js\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.js\";\n\n/**\n * Component that renders a custom message entry from extensions.\n * Uses distinct styling to differentiate from user messages.\n */\nexport class CustomMessageComponent extends Container {\n\tprivate message: CustomMessage<unknown>;\n\tprivate customRenderer?: MessageRenderer;\n\tprivate box: Box;\n\tprivate customComponent?: Component;\n\tprivate markdownTheme: MarkdownTheme;\n\tprivate _expanded = false;\n\n\tconstructor(\n\t\tmessage: CustomMessage<unknown>,\n\t\tcustomRenderer?: MessageRenderer,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t) {\n\t\tsuper();\n\t\tthis.message = message;\n\t\tthis.customRenderer = customRenderer;\n\t\tthis.markdownTheme = markdownTheme;\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create box with purple background (used for default rendering)\n\t\tthis.box = new Box(1, 1, (t) => theme.bg(\"customMessageBg\", t));\n\n\t\tthis.rebuild();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tif (this._expanded !== expanded) {\n\t\t\tthis._expanded = expanded;\n\t\t\tthis.rebuild();\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.rebuild();\n\t}\n\n\tprivate rebuild(): void {\n\t\t// Remove previous content component\n\t\tif (this.customComponent) {\n\t\t\tthis.removeChild(this.customComponent);\n\t\t\tthis.customComponent = undefined;\n\t\t}\n\t\tthis.removeChild(this.box);\n\n\t\t// Try custom renderer first - it handles its own styling\n\t\tif (this.customRenderer) {\n\t\t\ttry {\n\t\t\t\tconst component = this.customRenderer(this.message, { expanded: this._expanded }, theme);\n\t\t\t\tif (component) {\n\t\t\t\t\t// Custom renderer provides its own styled component\n\t\t\t\t\tthis.customComponent = component;\n\t\t\t\t\tthis.addChild(component);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Fall through to default rendering\n\t\t\t}\n\t\t}\n\n\t\t// Default rendering uses our box\n\t\tthis.addChild(this.box);\n\t\tthis.box.clear();\n\n\t\t// Default rendering: label + content\n\t\tconst label = theme.fg(\"customMessageLabel\", `\\x1b[1m[${this.message.customType}]\\x1b[22m`);\n\t\tthis.box.addChild(new Text(label, 0, 0));\n\t\tthis.box.addChild(new Spacer(1));\n\n\t\t// Extract text content\n\t\tlet text: string;\n\t\tif (typeof this.message.content === \"string\") {\n\t\t\ttext = this.message.content;\n\t\t} else {\n\t\t\ttext = this.message.content\n\t\t\t\t.filter((c): c is TextContent => c.type === \"text\")\n\t\t\t\t.map((c) => c.text)\n\t\t\t\t.join(\"\\n\");\n\t\t}\n\n\t\tthis.box.addChild(\n\t\t\tnew Markdown(text, 0, 0, this.markdownTheme, {\n\t\t\t\tcolor: (text: string) => theme.fg(\"customMessageText\", text),\n\t\t\t}),\n\t\t);\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"daxnuts.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/daxnuts.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAkD3D,qBAAa,gBAAiB,YAAW,SAAS;IACjD,OAAO,CAAC,EAAE,CAAM;IAChB,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,QAAQ,CAA+C;IAC/D,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,UAAU,CAAM;IAExB,YAAY,EAAE,EAAE,GAAG,EAIlB;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,aAAa;IAOrB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAgE9B;IAED,OAAO,IAAI,IAAI,CAEd;CACD","sourcesContent":["/**\n * POWERED BY DAXNUTS - Easter egg for OpenCode + Kimi K2.5\n *\n * A heartfelt tribute to dax (@thdxr) for providing free Kimi K2.5 access via OpenCode.\n */\n\nimport type { Component, TUI } from \"@mariozechner/pi-tui\";\nimport { theme } from \"../theme/theme.js\";\n\n// 32x32 RGB image of dax, hex encoded (3 bytes per pixel)\nconst DAX_HEX =\n\t\"bbbab8b9b9b6b9b8b5bcbbb8b8b7b4b7b5b2b6b5b2b8b7b4b7b6b3b6b4b1bdbcb8bab8b6bbb8b5b8b5b1bbb8b4c2bebbc1bebac0bdbabfbcb9c1bebabfbebbc0bfbcc0bdbabbb8b5c1bfbcbfbcb8bbb9b6bfbcb8c2bfbcc1bfbcbfbbb8bdb9b6b8b7b5b9b8b5b8b8b5b5b5b2b6b5b2b8b7b4b9b8b5b9b8b5b6b5b3bab8b5bcbab7bbb9b6bbb8b5bfb9b5bdb2abbcb0a8beb2aabeb5afbfbab6bebab7c0bfbcbebdbabebbb8c0bdbabfbebbc2bebbbdbab7c3c0bdc3c0bdc1bebbc2bebabfbcb8bab9b6b7b6b3b2b1aeb6b5b2b5b4b1b5b4b2b6b5b2b7b6b4b9b8b6b7b6b3bbbab7b2afaba5988fb49e90b09481b79a88b39683b09583b7a395bfb6b0c0bdbabdbbb8bebcb9c1bfbcc0bebbbdbab7bebbb8c2bfbcc0bdbac0bcb9bdb9b6c0bcb8b5b4b2b4b3b0bab9b6b9b9b6b5b4b1b5b4b1b6b5b3b9b8b5b9b8b6b9b8b6b2aeaa968174a6836eaa856eab846eaf8973ac8973b08f79b18f7ab39786b7a89dbbb3aebfbab6c2c0bdbebcb9bfbdbac3c1bdc2bebbc0bcb9bdb9b6c1bdbabfbbb8b4b3b0b9b8b5b8b7b5b4b3b1b5b4b1b8b7b4b8b7b5bab9b6bbbab7b1afad8c7a719d735ca47860a87d65a98069ae8972ae8c75af8d77aa826ba98067aa8974b39e90b6a79dbbb2adc0bdbac1bfbdbfbbb8c1bdb9bebab6c0bdb9bfbbb8c1bdbab4b2b0b7b6b4b7b6b3b4b2b0bab9b7b6b5b2b6b5b2bab9b6bab9b6958c87977663aa836bac8772b08f7aad8c77b2917db0917db0907cac8971a77d64a87f67ac8972b29887b8a89dbfbab5bfbdbac1bebac0bcb9c0bcb9c0bcb9c1bebabebab7b8b7b4b7b6b4b5b4b1b5b4b2b7b6b3b5b4b2bab9b7bab9b6b4b1ada88f7fad8973ae8d78b19684b19685b29786b69a89b29582b1917daa856ea87e66a97e66ad866ea9826baf9280b8ada6bdbbb8bebab7bfbbb8c1bdbabfbbb8bcb8b4bcb8b5b6b4b2b7b5b3b6b5b2b8b7b4b3b2afb8b7b4b6b5b2b3b2b0b3a59aab856fad8d78b0917eb19886b49b8bb49a89b39785b0917eaf8f7cab866fa77d65a77a61a87d64a9816ab08f79b5a296c1bcb8c3bfbcc2bebbbebab7bfbbb7bdbab6c2bebab8b7b4b7b6b4b6b5b3b7b6b3b6b5b2b9b8b6b4b3b1b6b1acac8f7ca9826bae8f7aaf9583b49c8cb49c8bb79d8cb59987b19380ad8e79ae8c77af8e78ac8771a3775faa826bae8972b39888bbb6b2bebbb8bfbbb8bfbbb8c0bdb9bebbb7c0bdb9b6b5b2b9b8b5b4b3b1b8b7b5b4b3b0b7b6b4b6b5b3b1a7a0aa8772a77d65a88570b49887b19b8d9c887c907a6d987f71aa907faf917daf8e7aad8c78ac8b77a8836ca9836cac8770b49b8abdb6b2c0bcb9c0bdb9bfbbb8bebab7bfbcb9bebab7b9b8b6b5b4b2b9b8b5b8b7b5b8b7b4b7b6b4b5b4b2b3a9a2ad8973a1755da9856fb398858c776a65544b776358725d526e594d9c7f6eb1907ba68672ad8e7aab8771ac856db18f79b3a092beb9b5c1bdbabdb9b5bebab7bfbbb7bebab7bcb9b6b7b6b4b6b6b3b8b7b4b5b4b2b8b6b4b7b6b3b4b3b0b4aba4a6826ba3775fb08e79b19584a88e7daa8e7db29481ad8f7c997e6da38674ac8d79ac8e7aae917f9a7c6a896a599a7c6ab3a398c1bdbabdb9b6bcb8b5bebab6bebab7bdb9b5bdb9b6b5b4b1b7b5b3b5b4b2b7b6b3b7b6b4b3b3b0b3b2b0b4aca5a7846fa97f68ae8f7bae9383b59c8bb2937fae8e79ac8b76af927eaf927eb29683b39885b2988891786a72594c6e594d978d86bdbab7bab7b3c0bcb9c0bcb9bebab7bebbb7bdb9b6b3b2b0b4b3b0b5b4b2b4b4b1b4b3b1b4b3b1b4b3b0b6ada5aa8670a57a62ad8e7ab29b8cb69d8dab856fa9826aa88069ab8771af907db49987b19684b29886b59987b39480b09787b5a9a1bcb8b5bebab7bdb9b5bebab7bfbbb8bfbbb7bbb7b4b3b2afb8b7b5b8b7b5b3b2b0b5b4b2b6b5b3b6b4b1afa299a98975a9826baf907cb39988b49a89af8e7aac8973aa856eaf8c74b1917dae907dac907db39988b29785b49785b7a090b9aca3bfbab7bcb8b5bdb9b6bcb8b4bcb8b5bdb9b5bcb8b4b5b4b2b6b5b3b4b3b0b4b3b0b9b8b5b8b6b4908b88887467aa8f7ea78976ad8973b08b74b59885b69e8eb29888b1917cb1917db1937fae907cb19686b39a8ab29886b59b8ab8a192b6aaa3b7b2afbcb8b4bcb8b5bbb7b4c0bcb9bebab7c0bcb9b6b5b2b6b5b3b4b3b0bab9b7b7b6b4b1b0ae7b716ba083709b806f716158967764b08870b29481b69b8ab69f8fb39a89b69f90b49d8db39a89b29988b49c8cb6a090b8a496baa49593867f8f8986bfbbb7bdb9b5bcb7b4bab6b3b9b5b2bab6b2b4b3b1b3b3b0b6b5b3b8b7b5b4b2b0a7a5a38f837dae917ea084725a504c63544da28370b39784b59e8db2a093a698909b918b998e8790857e95877dad998bb39c8cb5a091b9a2938d827c95908dbebab6bbb7b3bdbab7bbb7b4bdb9b6bbb7b4b4b3b0b5b4b1b8b7b5b6b5b3b8b8b5b4b2af968f8ab29a8bab9485544b483a323073655d96887f70655f61595547403e453e3c453f3d57504f655e5b90847db39c8db7a090b6a09189807aaba6a3bdb9b6c0bcb9bebab7bcb7b4bebab7bbb7b4b3b2b0b6b5b3b2b1afb7b6b4b8b7b4b5b4b1aeaba8b5a89fac998d4d44412d25244d46444e4744322b293a3230423937433a37352d2a59504c534b48524a48988a81b59f8fb19c8d827974b2afacbdb9b5bcb8b4bdb9b5bcb8b5bdb9b6bab6b2b8b7b5b5b4b2b6b6b3b9b8b5b7b6b3b6b5b2b8b6b3b9b4b1b2a9a26c64612d25242d2625312a28352d2c453d3a78675c8d7a6ea09792aea6a0615854332b29524a479f8e82b09d90a49b96c1bdb9bebab7bfbbb8bbb8b4b9b5b1b8b4b0b9b4b0b7b6b4b8b7b5b8b7b4b6b5b3b8b6b3bab9b6b9b8b5b4b3b0b7b5b2a5a29f453d3b261e1d261f1e2e2625413936857268977865b19482b5a69caca5a07c7572453d3b746963a0948cc5bfbbc0bbb8beb9b6bbb7b3bbb6b3b7b3afb8b4b0b9b5b1b7b6b3b6b5b3b5b4b2b5b4b2b7b6b3b7b6b3b8b6b3b4b2afb7b6b3b3b1ae6d6765251f1e1e18172a22212d2523443b3971625ab19888b09482a89182877e792c25243e3634766d6abeb9b5bfbbb7bebab6bcb7b3bbb6b3b9b5b1b7b3afb8b4b0b4b3b0b5b4b1b5b4b1b4b3b1b5b4b2b8b6b4b5b3b0b9b6b4b5b4b1b6b4b27f79762a2322221c1b2d2524221b1a443e3c47413f6f676281766f867971675e5a3e37352a222166605dbab7b3bdb9b5beb9b5bcb7b3bcb7b3b9b4b0bab6b2bab6b2b5b3b0b6b4b2b3b2afb7b6b3b4b4b1b4b3b0b6b4b1b5b4b1b4b3b0b9b6b29a8c8252474230292828201f181212322c2c231e1d1c16162c26252923222d26252d2523332b2a8e8885bcb8b5bcb7b3bbb6b2bcb7b3b9b4b1b9b5b1b7b2afb7b2ae7a838e9b9b9caeadacb3b2b0b3b2afb7b7b4b6b5b3b6b6b3b7b6b3b9ada4a991808e7b6f50453f2b24231a14142923221f19181d17161f18182620201d17162a22215d5654b7b3b0bbb7b3bbb6b2b8b4b0bab5b1bbb6b2bab5b1b8b4b0bab6b22c496b4c5d735f68766e727a828285929090adaba8b7b2aeb6a59ab39682a28470a387748e76674e403a1a14141d1716181211221c1c1f1918221c1b2f2827342d2c8d8884bab6b3b9b5b2bab5b1bab5b1b9b4b0bab6b2b8b4b0b9b4b0b7b2ae325e8b365f8a3a5d833f5b7a545f70646469706b6aa08f84b08e78b18e769f7e689e7f6b9e816d907766584940362d2a1c1615201b1a1a1413201a1a251e1d393331a39e9bbab5b1bcb7b3bab6b2b8b3afb8b4b0b9b4b0b9b4b1bab5b2b5b0ac3d6c9843729d44719c426e98415f805a64716f6a699d8677b1927eb3947faa89749d7a649f7f6ba487749e837186716454463f2c25231e181837302e3a33317a7471beb9b6bcb8b4bbb6b2b6b2aebab5b1b9b5b1b8b3afbab6b2b6b1adb5aeaa4877a14c7aa44e7ba345719a3a5d80586b7f767475927b6eb1927faf8e79b08e78a78169a07861a17f6aa58570a688749b83738270666f66618a8480a49e99b7b2aebab6b2bcb8b4b9b5b1b7b2aebab5b1b9b4b0b6b1aeb6b1adb2aca8b2aca84876a04a78a2517fa74771973a5d80405c7a6161677c695fac8a75b08d77b4917aaf8971ad876fa5816aa6846ea78670a98a76ac9484ab9f96b2aca8bdb8b4bcb7b3bcb8b4bcb8b4b8b3afb7b2aeb9b4b0b8b3afb8b2aeb6afabb3aeaab2aeaa4878a14b7aa34c7ba44a759b3d63873b5f825b67766f5f569c7e6caf8c77b18f79b28f78b5927caf8e78a98872aa8a76a98a76ac917fada199b7b0acb9b3afbfb9b5c1bab6bdb6b2b8b3afbab5b1b9b4b0b6afabb7b1adb3ada9b3aeaab0aba8\";\n\nconst WIDTH = 32;\nconst HEIGHT = 32;\n\nfunction parseImage(): number[][][] {\n\tconst pixels: number[][][] = [];\n\tfor (let y = 0; y < HEIGHT; y++) {\n\t\tconst row: number[][] = [];\n\t\tfor (let x = 0; x < WIDTH; x++) {\n\t\t\tconst idx = (y * WIDTH + x) * 6;\n\t\t\tconst r = parseInt(DAX_HEX.slice(idx, idx + 2), 16);\n\t\t\tconst g = parseInt(DAX_HEX.slice(idx + 2, idx + 4), 16);\n\t\t\tconst b = parseInt(DAX_HEX.slice(idx + 4, idx + 6), 16);\n\t\t\trow.push([r, g, b]);\n\t\t}\n\t\tpixels.push(row);\n\t}\n\treturn pixels;\n}\n\nfunction rgb(r: number, g: number, b: number, bg = false): string {\n\treturn `\\x1b[${bg ? 48 : 38};2;${r};${g};${b}m`;\n}\n\nconst RESET = \"\\x1b[0m\";\n\nfunction buildImage(): string[] {\n\tconst pixels = parseImage();\n\tconst lines: string[] = [];\n\n\t// Use half-block chars: ▄ with bg=top pixel, fg=bottom pixel\n\tfor (let row = 0; row < HEIGHT; row += 2) {\n\t\tlet line = \"\";\n\t\tfor (let x = 0; x < WIDTH; x++) {\n\t\t\tconst top = pixels[row][x];\n\t\t\tconst bottom = pixels[row + 1]?.[x] ?? top;\n\t\t\tline += rgb(bottom[0], bottom[1], bottom[2]) + rgb(top[0], top[1], top[2], true) + \"▄\";\n\t\t}\n\t\tline += RESET;\n\t\tlines.push(line);\n\t}\n\treturn lines;\n}\n\nexport class DaxnutsComponent implements Component {\n\tprivate ui: TUI;\n\tprivate image: string[];\n\tprivate interval: ReturnType<typeof setInterval> | null = null;\n\tprivate tick = 0;\n\tprivate maxTicks = 25; // ~2 seconds at 80ms\n\tprivate cachedLines: string[] = [];\n\tprivate cachedWidth = 0;\n\tprivate cachedTick = -1;\n\n\tconstructor(ui: TUI) {\n\t\tthis.ui = ui;\n\t\tthis.image = buildImage();\n\t\tthis.startAnimation();\n\t}\n\n\tinvalidate(): void {\n\t\tthis.cachedWidth = 0;\n\t}\n\n\tprivate startAnimation(): void {\n\t\tthis.interval = setInterval(() => {\n\t\t\tthis.tick++;\n\t\t\tif (this.tick >= this.maxTicks) {\n\t\t\t\tthis.stopAnimation();\n\t\t\t}\n\t\t\tthis.cachedWidth = 0;\n\t\t\tthis.ui.requestRender();\n\t\t}, 80);\n\t}\n\n\tprivate stopAnimation(): void {\n\t\tif (this.interval) {\n\t\t\tclearInterval(this.interval);\n\t\t\tthis.interval = null;\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\tif (width === this.cachedWidth && this.cachedTick === this.tick) {\n\t\t\treturn this.cachedLines;\n\t\t}\n\n\t\tconst t = theme;\n\t\tconst lines: string[] = [];\n\n\t\tconst center = (s: string) => {\n\t\t\tconst visible = s.replace(/\\x1b\\[[0-9;]*m/g, \"\").length;\n\t\t\tconst left = Math.max(0, Math.floor((width - visible) / 2));\n\t\t\treturn \" \".repeat(left) + s;\n\t\t};\n\n\t\tlines.push(\"\");\n\n\t\t// Scanline reveal effect: show rows progressively\n\t\tconst revealedRows = Math.min(\n\t\t\tthis.image.length,\n\t\t\tMath.floor((this.tick / this.maxTicks) * (this.image.length + 3)),\n\t\t);\n\n\t\tfor (let i = 0; i < this.image.length; i++) {\n\t\t\tif (i < revealedRows) {\n\t\t\t\tlines.push(center(this.image[i]));\n\t\t\t} else {\n\t\t\t\t// Show scan line\n\t\t\t\tif (i === revealedRows) {\n\t\t\t\t\tconst scanline = \"▓\".repeat(WIDTH);\n\t\t\t\t\tlines.push(center(rgb(100, 200, 255) + scanline + RESET));\n\t\t\t\t} else {\n\t\t\t\t\tlines.push(center(\" \".repeat(WIDTH)));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlines.push(\"\");\n\n\t\t// Fade in text after image is revealed\n\t\tconst textPhase = Math.max(0, this.tick - this.maxTicks * 0.6);\n\t\tif (textPhase > 0 || this.tick >= this.maxTicks) {\n\t\t\tlines.push(center(t.fg(\"accent\", \"Free Kimi K2.5 via OpenCode Zen\")));\n\t\t\tlines.push(center(t.fg(\"success\", '\"Powered by daxnuts\"')));\n\t\t\tlines.push(center(t.fg(\"muted\", \"— @thdxr\")));\n\t\t} else {\n\t\t\tlines.push(\"\");\n\t\t\tlines.push(\"\");\n\t\t\tlines.push(\"\");\n\t\t}\n\n\t\tlines.push(\"\");\n\t\tif (textPhase > 2 || this.tick >= this.maxTicks) {\n\t\t\tlines.push(center(t.fg(\"dim\", \"Try OpenCode\")));\n\t\t\tlines.push(center(t.fg(\"mdLink\", \"https://mistral.ai/news/mistral-vibe-2-0\")));\n\t\t} else {\n\t\t\tlines.push(\"\");\n\t\t\tlines.push(\"\");\n\t\t}\n\t\tlines.push(\"\");\n\n\t\tthis.cachedLines = lines;\n\t\tthis.cachedWidth = width;\n\t\tthis.cachedTick = this.tick;\n\t\treturn lines;\n\t}\n\n\tdispose(): void {\n\t\tthis.stopAnimation();\n\t}\n}\n"]}
1
+ {"version":3,"file":"daxnuts.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/daxnuts.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAkD3D,qBAAa,gBAAiB,YAAW,SAAS;IACjD,OAAO,CAAC,EAAE,CAAM;IAChB,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,QAAQ,CAA+C;IAC/D,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,UAAU,CAAM;IAExB,YAAY,EAAE,EAAE,GAAG,EAIlB;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,aAAa;IAOrB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAgE9B;IAED,OAAO,IAAI,IAAI,CAEd;CACD","sourcesContent":["/**\n * POWERED BY DAXNUTS - Easter egg for OpenCode + Kimi K2.5\n *\n * A heartfelt tribute to dax (@thdxr) for providing free Kimi K2.5 access via OpenCode.\n */\n\nimport type { Component, TUI } from \"@mariozechner/pi-tui\";\nimport { theme } from \"../theme/theme.js\";\n\n// 32x32 RGB image of dax, hex encoded (3 bytes per pixel)\nconst DAX_HEX =\n\t\"bbbab8b9b9b6b9b8b5bcbbb8b8b7b4b7b5b2b6b5b2b8b7b4b7b6b3b6b4b1bdbcb8bab8b6bbb8b5b8b5b1bbb8b4c2bebbc1bebac0bdbabfbcb9c1bebabfbebbc0bfbcc0bdbabbb8b5c1bfbcbfbcb8bbb9b6bfbcb8c2bfbcc1bfbcbfbbb8bdb9b6b8b7b5b9b8b5b8b8b5b5b5b2b6b5b2b8b7b4b9b8b5b9b8b5b6b5b3bab8b5bcbab7bbb9b6bbb8b5bfb9b5bdb2abbcb0a8beb2aabeb5afbfbab6bebab7c0bfbcbebdbabebbb8c0bdbabfbebbc2bebbbdbab7c3c0bdc3c0bdc1bebbc2bebabfbcb8bab9b6b7b6b3b2b1aeb6b5b2b5b4b1b5b4b2b6b5b2b7b6b4b9b8b6b7b6b3bbbab7b2afaba5988fb49e90b09481b79a88b39683b09583b7a395bfb6b0c0bdbabdbbb8bebcb9c1bfbcc0bebbbdbab7bebbb8c2bfbcc0bdbac0bcb9bdb9b6c0bcb8b5b4b2b4b3b0bab9b6b9b9b6b5b4b1b5b4b1b6b5b3b9b8b5b9b8b6b9b8b6b2aeaa968174a6836eaa856eab846eaf8973ac8973b08f79b18f7ab39786b7a89dbbb3aebfbab6c2c0bdbebcb9bfbdbac3c1bdc2bebbc0bcb9bdb9b6c1bdbabfbbb8b4b3b0b9b8b5b8b7b5b4b3b1b5b4b1b8b7b4b8b7b5bab9b6bbbab7b1afad8c7a719d735ca47860a87d65a98069ae8972ae8c75af8d77aa826ba98067aa8974b39e90b6a79dbbb2adc0bdbac1bfbdbfbbb8c1bdb9bebab6c0bdb9bfbbb8c1bdbab4b2b0b7b6b4b7b6b3b4b2b0bab9b7b6b5b2b6b5b2bab9b6bab9b6958c87977663aa836bac8772b08f7aad8c77b2917db0917db0907cac8971a77d64a87f67ac8972b29887b8a89dbfbab5bfbdbac1bebac0bcb9c0bcb9c0bcb9c1bebabebab7b8b7b4b7b6b4b5b4b1b5b4b2b7b6b3b5b4b2bab9b7bab9b6b4b1ada88f7fad8973ae8d78b19684b19685b29786b69a89b29582b1917daa856ea87e66a97e66ad866ea9826baf9280b8ada6bdbbb8bebab7bfbbb8c1bdbabfbbb8bcb8b4bcb8b5b6b4b2b7b5b3b6b5b2b8b7b4b3b2afb8b7b4b6b5b2b3b2b0b3a59aab856fad8d78b0917eb19886b49b8bb49a89b39785b0917eaf8f7cab866fa77d65a77a61a87d64a9816ab08f79b5a296c1bcb8c3bfbcc2bebbbebab7bfbbb7bdbab6c2bebab8b7b4b7b6b4b6b5b3b7b6b3b6b5b2b9b8b6b4b3b1b6b1acac8f7ca9826bae8f7aaf9583b49c8cb49c8bb79d8cb59987b19380ad8e79ae8c77af8e78ac8771a3775faa826bae8972b39888bbb6b2bebbb8bfbbb8bfbbb8c0bdb9bebbb7c0bdb9b6b5b2b9b8b5b4b3b1b8b7b5b4b3b0b7b6b4b6b5b3b1a7a0aa8772a77d65a88570b49887b19b8d9c887c907a6d987f71aa907faf917daf8e7aad8c78ac8b77a8836ca9836cac8770b49b8abdb6b2c0bcb9c0bdb9bfbbb8bebab7bfbcb9bebab7b9b8b6b5b4b2b9b8b5b8b7b5b8b7b4b7b6b4b5b4b2b3a9a2ad8973a1755da9856fb398858c776a65544b776358725d526e594d9c7f6eb1907ba68672ad8e7aab8771ac856db18f79b3a092beb9b5c1bdbabdb9b5bebab7bfbbb7bebab7bcb9b6b7b6b4b6b6b3b8b7b4b5b4b2b8b6b4b7b6b3b4b3b0b4aba4a6826ba3775fb08e79b19584a88e7daa8e7db29481ad8f7c997e6da38674ac8d79ac8e7aae917f9a7c6a896a599a7c6ab3a398c1bdbabdb9b6bcb8b5bebab6bebab7bdb9b5bdb9b6b5b4b1b7b5b3b5b4b2b7b6b3b7b6b4b3b3b0b3b2b0b4aca5a7846fa97f68ae8f7bae9383b59c8bb2937fae8e79ac8b76af927eaf927eb29683b39885b2988891786a72594c6e594d978d86bdbab7bab7b3c0bcb9c0bcb9bebab7bebbb7bdb9b6b3b2b0b4b3b0b5b4b2b4b4b1b4b3b1b4b3b1b4b3b0b6ada5aa8670a57a62ad8e7ab29b8cb69d8dab856fa9826aa88069ab8771af907db49987b19684b29886b59987b39480b09787b5a9a1bcb8b5bebab7bdb9b5bebab7bfbbb8bfbbb7bbb7b4b3b2afb8b7b5b8b7b5b3b2b0b5b4b2b6b5b3b6b4b1afa299a98975a9826baf907cb39988b49a89af8e7aac8973aa856eaf8c74b1917dae907dac907db39988b29785b49785b7a090b9aca3bfbab7bcb8b5bdb9b6bcb8b4bcb8b5bdb9b5bcb8b4b5b4b2b6b5b3b4b3b0b4b3b0b9b8b5b8b6b4908b88887467aa8f7ea78976ad8973b08b74b59885b69e8eb29888b1917cb1917db1937fae907cb19686b39a8ab29886b59b8ab8a192b6aaa3b7b2afbcb8b4bcb8b5bbb7b4c0bcb9bebab7c0bcb9b6b5b2b6b5b3b4b3b0bab9b7b7b6b4b1b0ae7b716ba083709b806f716158967764b08870b29481b69b8ab69f8fb39a89b69f90b49d8db39a89b29988b49c8cb6a090b8a496baa49593867f8f8986bfbbb7bdb9b5bcb7b4bab6b3b9b5b2bab6b2b4b3b1b3b3b0b6b5b3b8b7b5b4b2b0a7a5a38f837dae917ea084725a504c63544da28370b39784b59e8db2a093a698909b918b998e8790857e95877dad998bb39c8cb5a091b9a2938d827c95908dbebab6bbb7b3bdbab7bbb7b4bdb9b6bbb7b4b4b3b0b5b4b1b8b7b5b6b5b3b8b8b5b4b2af968f8ab29a8bab9485544b483a323073655d96887f70655f61595547403e453e3c453f3d57504f655e5b90847db39c8db7a090b6a09189807aaba6a3bdb9b6c0bcb9bebab7bcb7b4bebab7bbb7b4b3b2b0b6b5b3b2b1afb7b6b4b8b7b4b5b4b1aeaba8b5a89fac998d4d44412d25244d46444e4744322b293a3230423937433a37352d2a59504c534b48524a48988a81b59f8fb19c8d827974b2afacbdb9b5bcb8b4bdb9b5bcb8b5bdb9b6bab6b2b8b7b5b5b4b2b6b6b3b9b8b5b7b6b3b6b5b2b8b6b3b9b4b1b2a9a26c64612d25242d2625312a28352d2c453d3a78675c8d7a6ea09792aea6a0615854332b29524a479f8e82b09d90a49b96c1bdb9bebab7bfbbb8bbb8b4b9b5b1b8b4b0b9b4b0b7b6b4b8b7b5b8b7b4b6b5b3b8b6b3bab9b6b9b8b5b4b3b0b7b5b2a5a29f453d3b261e1d261f1e2e2625413936857268977865b19482b5a69caca5a07c7572453d3b746963a0948cc5bfbbc0bbb8beb9b6bbb7b3bbb6b3b7b3afb8b4b0b9b5b1b7b6b3b6b5b3b5b4b2b5b4b2b7b6b3b7b6b3b8b6b3b4b2afb7b6b3b3b1ae6d6765251f1e1e18172a22212d2523443b3971625ab19888b09482a89182877e792c25243e3634766d6abeb9b5bfbbb7bebab6bcb7b3bbb6b3b9b5b1b7b3afb8b4b0b4b3b0b5b4b1b5b4b1b4b3b1b5b4b2b8b6b4b5b3b0b9b6b4b5b4b1b6b4b27f79762a2322221c1b2d2524221b1a443e3c47413f6f676281766f867971675e5a3e37352a222166605dbab7b3bdb9b5beb9b5bcb7b3bcb7b3b9b4b0bab6b2bab6b2b5b3b0b6b4b2b3b2afb7b6b3b4b4b1b4b3b0b6b4b1b5b4b1b4b3b0b9b6b29a8c8252474230292828201f181212322c2c231e1d1c16162c26252923222d26252d2523332b2a8e8885bcb8b5bcb7b3bbb6b2bcb7b3b9b4b1b9b5b1b7b2afb7b2ae7a838e9b9b9caeadacb3b2b0b3b2afb7b7b4b6b5b3b6b6b3b7b6b3b9ada4a991808e7b6f50453f2b24231a14142923221f19181d17161f18182620201d17162a22215d5654b7b3b0bbb7b3bbb6b2b8b4b0bab5b1bbb6b2bab5b1b8b4b0bab6b22c496b4c5d735f68766e727a828285929090adaba8b7b2aeb6a59ab39682a28470a387748e76674e403a1a14141d1716181211221c1c1f1918221c1b2f2827342d2c8d8884bab6b3b9b5b2bab5b1bab5b1b9b4b0bab6b2b8b4b0b9b4b0b7b2ae325e8b365f8a3a5d833f5b7a545f70646469706b6aa08f84b08e78b18e769f7e689e7f6b9e816d907766584940362d2a1c1615201b1a1a1413201a1a251e1d393331a39e9bbab5b1bcb7b3bab6b2b8b3afb8b4b0b9b4b0b9b4b1bab5b2b5b0ac3d6c9843729d44719c426e98415f805a64716f6a699d8677b1927eb3947faa89749d7a649f7f6ba487749e837186716454463f2c25231e181837302e3a33317a7471beb9b6bcb8b4bbb6b2b6b2aebab5b1b9b5b1b8b3afbab6b2b6b1adb5aeaa4877a14c7aa44e7ba345719a3a5d80586b7f767475927b6eb1927faf8e79b08e78a78169a07861a17f6aa58570a688749b83738270666f66618a8480a49e99b7b2aebab6b2bcb8b4b9b5b1b7b2aebab5b1b9b4b0b6b1aeb6b1adb2aca8b2aca84876a04a78a2517fa74771973a5d80405c7a6161677c695fac8a75b08d77b4917aaf8971ad876fa5816aa6846ea78670a98a76ac9484ab9f96b2aca8bdb8b4bcb7b3bcb8b4bcb8b4b8b3afb7b2aeb9b4b0b8b3afb8b2aeb6afabb3aeaab2aeaa4878a14b7aa34c7ba44a759b3d63873b5f825b67766f5f569c7e6caf8c77b18f79b28f78b5927caf8e78a98872aa8a76a98a76ac917fada199b7b0acb9b3afbfb9b5c1bab6bdb6b2b8b3afbab5b1b9b4b0b6afabb7b1adb3ada9b3aeaab0aba8\";\n\nconst WIDTH = 32;\nconst HEIGHT = 32;\n\nfunction parseImage(): number[][][] {\n\tconst pixels: number[][][] = [];\n\tfor (let y = 0; y < HEIGHT; y++) {\n\t\tconst row: number[][] = [];\n\t\tfor (let x = 0; x < WIDTH; x++) {\n\t\t\tconst idx = (y * WIDTH + x) * 6;\n\t\t\tconst r = parseInt(DAX_HEX.slice(idx, idx + 2), 16);\n\t\t\tconst g = parseInt(DAX_HEX.slice(idx + 2, idx + 4), 16);\n\t\t\tconst b = parseInt(DAX_HEX.slice(idx + 4, idx + 6), 16);\n\t\t\trow.push([r, g, b]);\n\t\t}\n\t\tpixels.push(row);\n\t}\n\treturn pixels;\n}\n\nfunction rgb(r: number, g: number, b: number, bg = false): string {\n\treturn `\\x1b[${bg ? 48 : 38};2;${r};${g};${b}m`;\n}\n\nconst RESET = \"\\x1b[0m\";\n\nfunction buildImage(): string[] {\n\tconst pixels = parseImage();\n\tconst lines: string[] = [];\n\n\t// Use half-block chars: ▄ with bg=top pixel, fg=bottom pixel\n\tfor (let row = 0; row < HEIGHT; row += 2) {\n\t\tlet line = \"\";\n\t\tfor (let x = 0; x < WIDTH; x++) {\n\t\t\tconst top = pixels[row][x];\n\t\t\tconst bottom = pixels[row + 1]?.[x] ?? top;\n\t\t\tline += `${rgb(bottom[0], bottom[1], bottom[2])}${rgb(top[0], top[1], top[2], true)}▄`;\n\t\t}\n\t\tline += RESET;\n\t\tlines.push(line);\n\t}\n\treturn lines;\n}\n\nexport class DaxnutsComponent implements Component {\n\tprivate ui: TUI;\n\tprivate image: string[];\n\tprivate interval: ReturnType<typeof setInterval> | null = null;\n\tprivate tick = 0;\n\tprivate maxTicks = 25; // ~2 seconds at 80ms\n\tprivate cachedLines: string[] = [];\n\tprivate cachedWidth = 0;\n\tprivate cachedTick = -1;\n\n\tconstructor(ui: TUI) {\n\t\tthis.ui = ui;\n\t\tthis.image = buildImage();\n\t\tthis.startAnimation();\n\t}\n\n\tinvalidate(): void {\n\t\tthis.cachedWidth = 0;\n\t}\n\n\tprivate startAnimation(): void {\n\t\tthis.interval = setInterval(() => {\n\t\t\tthis.tick++;\n\t\t\tif (this.tick >= this.maxTicks) {\n\t\t\t\tthis.stopAnimation();\n\t\t\t}\n\t\t\tthis.cachedWidth = 0;\n\t\t\tthis.ui.requestRender();\n\t\t}, 80);\n\t}\n\n\tprivate stopAnimation(): void {\n\t\tif (this.interval) {\n\t\t\tclearInterval(this.interval);\n\t\t\tthis.interval = null;\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\tif (width === this.cachedWidth && this.cachedTick === this.tick) {\n\t\t\treturn this.cachedLines;\n\t\t}\n\n\t\tconst t = theme;\n\t\tconst lines: string[] = [];\n\n\t\tconst center = (s: string) => {\n\t\t\tconst visible = s.replace(/\\x1b\\[[0-9;]*m/g, \"\").length;\n\t\t\tconst left = Math.max(0, Math.floor((width - visible) / 2));\n\t\t\treturn \" \".repeat(left) + s;\n\t\t};\n\n\t\tlines.push(\"\");\n\n\t\t// Scanline reveal effect: show rows progressively\n\t\tconst revealedRows = Math.min(\n\t\t\tthis.image.length,\n\t\t\tMath.floor((this.tick / this.maxTicks) * (this.image.length + 3)),\n\t\t);\n\n\t\tfor (let i = 0; i < this.image.length; i++) {\n\t\t\tif (i < revealedRows) {\n\t\t\t\tlines.push(center(this.image[i]));\n\t\t\t} else {\n\t\t\t\t// Show scan line\n\t\t\t\tif (i === revealedRows) {\n\t\t\t\t\tconst scanline = \"▓\".repeat(WIDTH);\n\t\t\t\t\tlines.push(center(rgb(100, 200, 255) + scanline + RESET));\n\t\t\t\t} else {\n\t\t\t\t\tlines.push(center(\" \".repeat(WIDTH)));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlines.push(\"\");\n\n\t\t// Fade in text after image is revealed\n\t\tconst textPhase = Math.max(0, this.tick - this.maxTicks * 0.6);\n\t\tif (textPhase > 0 || this.tick >= this.maxTicks) {\n\t\t\tlines.push(center(t.fg(\"accent\", \"Free Kimi K2.5 via OpenCode Zen\")));\n\t\t\tlines.push(center(t.fg(\"success\", '\"Powered by daxnuts\"')));\n\t\t\tlines.push(center(t.fg(\"muted\", \"— @thdxr\")));\n\t\t} else {\n\t\t\tlines.push(\"\");\n\t\t\tlines.push(\"\");\n\t\t\tlines.push(\"\");\n\t\t}\n\n\t\tlines.push(\"\");\n\t\tif (textPhase > 2 || this.tick >= this.maxTicks) {\n\t\t\tlines.push(center(t.fg(\"dim\", \"Try OpenCode\")));\n\t\t\tlines.push(center(t.fg(\"mdLink\", \"https://mistral.ai/news/mistral-vibe-2-0\")));\n\t\t} else {\n\t\t\tlines.push(\"\");\n\t\t\tlines.push(\"\");\n\t\t}\n\t\tlines.push(\"\");\n\n\t\tthis.cachedLines = lines;\n\t\tthis.cachedWidth = width;\n\t\tthis.cachedTick = this.tick;\n\t\treturn lines;\n\t}\n\n\tdispose(): void {\n\t\tthis.stopAnimation();\n\t}\n}\n"]}
@@ -36,7 +36,7 @@ function buildImage() {
36
36
  for (let x = 0; x < WIDTH; x++) {
37
37
  const top = pixels[row][x];
38
38
  const bottom = pixels[row + 1]?.[x] ?? top;
39
- line += rgb(bottom[0], bottom[1], bottom[2]) + rgb(top[0], top[1], top[2], true) + "▄";
39
+ line += `${rgb(bottom[0], bottom[1], bottom[2])}${rgb(top[0], top[1], top[2], true)}▄`;
40
40
  }
41
41
  line += RESET;
42
42
  lines.push(line);
@@ -1 +1 @@
1
- {"version":3,"file":"daxnuts.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/daxnuts.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * POWERED BY DAXNUTS - Easter egg for OpenCode + Kimi K2.5\n *\n * A heartfelt tribute to dax (@thdxr) for providing free Kimi K2.5 access via OpenCode.\n */\n\nimport type { Component, TUI } from \"@mariozechner/pi-tui\";\nimport { theme } from \"../theme/theme.js\";\n\n// 32x32 RGB image of dax, hex encoded (3 bytes per pixel)\nconst DAX_HEX =\n\t\"bbbab8b9b9b6b9b8b5bcbbb8b8b7b4b7b5b2b6b5b2b8b7b4b7b6b3b6b4b1bdbcb8bab8b6bbb8b5b8b5b1bbb8b4c2bebbc1bebac0bdbabfbcb9c1bebabfbebbc0bfbcc0bdbabbb8b5c1bfbcbfbcb8bbb9b6bfbcb8c2bfbcc1bfbcbfbbb8bdb9b6b8b7b5b9b8b5b8b8b5b5b5b2b6b5b2b8b7b4b9b8b5b9b8b5b6b5b3bab8b5bcbab7bbb9b6bbb8b5bfb9b5bdb2abbcb0a8beb2aabeb5afbfbab6bebab7c0bfbcbebdbabebbb8c0bdbabfbebbc2bebbbdbab7c3c0bdc3c0bdc1bebbc2bebabfbcb8bab9b6b7b6b3b2b1aeb6b5b2b5b4b1b5b4b2b6b5b2b7b6b4b9b8b6b7b6b3bbbab7b2afaba5988fb49e90b09481b79a88b39683b09583b7a395bfb6b0c0bdbabdbbb8bebcb9c1bfbcc0bebbbdbab7bebbb8c2bfbcc0bdbac0bcb9bdb9b6c0bcb8b5b4b2b4b3b0bab9b6b9b9b6b5b4b1b5b4b1b6b5b3b9b8b5b9b8b6b9b8b6b2aeaa968174a6836eaa856eab846eaf8973ac8973b08f79b18f7ab39786b7a89dbbb3aebfbab6c2c0bdbebcb9bfbdbac3c1bdc2bebbc0bcb9bdb9b6c1bdbabfbbb8b4b3b0b9b8b5b8b7b5b4b3b1b5b4b1b8b7b4b8b7b5bab9b6bbbab7b1afad8c7a719d735ca47860a87d65a98069ae8972ae8c75af8d77aa826ba98067aa8974b39e90b6a79dbbb2adc0bdbac1bfbdbfbbb8c1bdb9bebab6c0bdb9bfbbb8c1bdbab4b2b0b7b6b4b7b6b3b4b2b0bab9b7b6b5b2b6b5b2bab9b6bab9b6958c87977663aa836bac8772b08f7aad8c77b2917db0917db0907cac8971a77d64a87f67ac8972b29887b8a89dbfbab5bfbdbac1bebac0bcb9c0bcb9c0bcb9c1bebabebab7b8b7b4b7b6b4b5b4b1b5b4b2b7b6b3b5b4b2bab9b7bab9b6b4b1ada88f7fad8973ae8d78b19684b19685b29786b69a89b29582b1917daa856ea87e66a97e66ad866ea9826baf9280b8ada6bdbbb8bebab7bfbbb8c1bdbabfbbb8bcb8b4bcb8b5b6b4b2b7b5b3b6b5b2b8b7b4b3b2afb8b7b4b6b5b2b3b2b0b3a59aab856fad8d78b0917eb19886b49b8bb49a89b39785b0917eaf8f7cab866fa77d65a77a61a87d64a9816ab08f79b5a296c1bcb8c3bfbcc2bebbbebab7bfbbb7bdbab6c2bebab8b7b4b7b6b4b6b5b3b7b6b3b6b5b2b9b8b6b4b3b1b6b1acac8f7ca9826bae8f7aaf9583b49c8cb49c8bb79d8cb59987b19380ad8e79ae8c77af8e78ac8771a3775faa826bae8972b39888bbb6b2bebbb8bfbbb8bfbbb8c0bdb9bebbb7c0bdb9b6b5b2b9b8b5b4b3b1b8b7b5b4b3b0b7b6b4b6b5b3b1a7a0aa8772a77d65a88570b49887b19b8d9c887c907a6d987f71aa907faf917daf8e7aad8c78ac8b77a8836ca9836cac8770b49b8abdb6b2c0bcb9c0bdb9bfbbb8bebab7bfbcb9bebab7b9b8b6b5b4b2b9b8b5b8b7b5b8b7b4b7b6b4b5b4b2b3a9a2ad8973a1755da9856fb398858c776a65544b776358725d526e594d9c7f6eb1907ba68672ad8e7aab8771ac856db18f79b3a092beb9b5c1bdbabdb9b5bebab7bfbbb7bebab7bcb9b6b7b6b4b6b6b3b8b7b4b5b4b2b8b6b4b7b6b3b4b3b0b4aba4a6826ba3775fb08e79b19584a88e7daa8e7db29481ad8f7c997e6da38674ac8d79ac8e7aae917f9a7c6a896a599a7c6ab3a398c1bdbabdb9b6bcb8b5bebab6bebab7bdb9b5bdb9b6b5b4b1b7b5b3b5b4b2b7b6b3b7b6b4b3b3b0b3b2b0b4aca5a7846fa97f68ae8f7bae9383b59c8bb2937fae8e79ac8b76af927eaf927eb29683b39885b2988891786a72594c6e594d978d86bdbab7bab7b3c0bcb9c0bcb9bebab7bebbb7bdb9b6b3b2b0b4b3b0b5b4b2b4b4b1b4b3b1b4b3b1b4b3b0b6ada5aa8670a57a62ad8e7ab29b8cb69d8dab856fa9826aa88069ab8771af907db49987b19684b29886b59987b39480b09787b5a9a1bcb8b5bebab7bdb9b5bebab7bfbbb8bfbbb7bbb7b4b3b2afb8b7b5b8b7b5b3b2b0b5b4b2b6b5b3b6b4b1afa299a98975a9826baf907cb39988b49a89af8e7aac8973aa856eaf8c74b1917dae907dac907db39988b29785b49785b7a090b9aca3bfbab7bcb8b5bdb9b6bcb8b4bcb8b5bdb9b5bcb8b4b5b4b2b6b5b3b4b3b0b4b3b0b9b8b5b8b6b4908b88887467aa8f7ea78976ad8973b08b74b59885b69e8eb29888b1917cb1917db1937fae907cb19686b39a8ab29886b59b8ab8a192b6aaa3b7b2afbcb8b4bcb8b5bbb7b4c0bcb9bebab7c0bcb9b6b5b2b6b5b3b4b3b0bab9b7b7b6b4b1b0ae7b716ba083709b806f716158967764b08870b29481b69b8ab69f8fb39a89b69f90b49d8db39a89b29988b49c8cb6a090b8a496baa49593867f8f8986bfbbb7bdb9b5bcb7b4bab6b3b9b5b2bab6b2b4b3b1b3b3b0b6b5b3b8b7b5b4b2b0a7a5a38f837dae917ea084725a504c63544da28370b39784b59e8db2a093a698909b918b998e8790857e95877dad998bb39c8cb5a091b9a2938d827c95908dbebab6bbb7b3bdbab7bbb7b4bdb9b6bbb7b4b4b3b0b5b4b1b8b7b5b6b5b3b8b8b5b4b2af968f8ab29a8bab9485544b483a323073655d96887f70655f61595547403e453e3c453f3d57504f655e5b90847db39c8db7a090b6a09189807aaba6a3bdb9b6c0bcb9bebab7bcb7b4bebab7bbb7b4b3b2b0b6b5b3b2b1afb7b6b4b8b7b4b5b4b1aeaba8b5a89fac998d4d44412d25244d46444e4744322b293a3230423937433a37352d2a59504c534b48524a48988a81b59f8fb19c8d827974b2afacbdb9b5bcb8b4bdb9b5bcb8b5bdb9b6bab6b2b8b7b5b5b4b2b6b6b3b9b8b5b7b6b3b6b5b2b8b6b3b9b4b1b2a9a26c64612d25242d2625312a28352d2c453d3a78675c8d7a6ea09792aea6a0615854332b29524a479f8e82b09d90a49b96c1bdb9bebab7bfbbb8bbb8b4b9b5b1b8b4b0b9b4b0b7b6b4b8b7b5b8b7b4b6b5b3b8b6b3bab9b6b9b8b5b4b3b0b7b5b2a5a29f453d3b261e1d261f1e2e2625413936857268977865b19482b5a69caca5a07c7572453d3b746963a0948cc5bfbbc0bbb8beb9b6bbb7b3bbb6b3b7b3afb8b4b0b9b5b1b7b6b3b6b5b3b5b4b2b5b4b2b7b6b3b7b6b3b8b6b3b4b2afb7b6b3b3b1ae6d6765251f1e1e18172a22212d2523443b3971625ab19888b09482a89182877e792c25243e3634766d6abeb9b5bfbbb7bebab6bcb7b3bbb6b3b9b5b1b7b3afb8b4b0b4b3b0b5b4b1b5b4b1b4b3b1b5b4b2b8b6b4b5b3b0b9b6b4b5b4b1b6b4b27f79762a2322221c1b2d2524221b1a443e3c47413f6f676281766f867971675e5a3e37352a222166605dbab7b3bdb9b5beb9b5bcb7b3bcb7b3b9b4b0bab6b2bab6b2b5b3b0b6b4b2b3b2afb7b6b3b4b4b1b4b3b0b6b4b1b5b4b1b4b3b0b9b6b29a8c8252474230292828201f181212322c2c231e1d1c16162c26252923222d26252d2523332b2a8e8885bcb8b5bcb7b3bbb6b2bcb7b3b9b4b1b9b5b1b7b2afb7b2ae7a838e9b9b9caeadacb3b2b0b3b2afb7b7b4b6b5b3b6b6b3b7b6b3b9ada4a991808e7b6f50453f2b24231a14142923221f19181d17161f18182620201d17162a22215d5654b7b3b0bbb7b3bbb6b2b8b4b0bab5b1bbb6b2bab5b1b8b4b0bab6b22c496b4c5d735f68766e727a828285929090adaba8b7b2aeb6a59ab39682a28470a387748e76674e403a1a14141d1716181211221c1c1f1918221c1b2f2827342d2c8d8884bab6b3b9b5b2bab5b1bab5b1b9b4b0bab6b2b8b4b0b9b4b0b7b2ae325e8b365f8a3a5d833f5b7a545f70646469706b6aa08f84b08e78b18e769f7e689e7f6b9e816d907766584940362d2a1c1615201b1a1a1413201a1a251e1d393331a39e9bbab5b1bcb7b3bab6b2b8b3afb8b4b0b9b4b0b9b4b1bab5b2b5b0ac3d6c9843729d44719c426e98415f805a64716f6a699d8677b1927eb3947faa89749d7a649f7f6ba487749e837186716454463f2c25231e181837302e3a33317a7471beb9b6bcb8b4bbb6b2b6b2aebab5b1b9b5b1b8b3afbab6b2b6b1adb5aeaa4877a14c7aa44e7ba345719a3a5d80586b7f767475927b6eb1927faf8e79b08e78a78169a07861a17f6aa58570a688749b83738270666f66618a8480a49e99b7b2aebab6b2bcb8b4b9b5b1b7b2aebab5b1b9b4b0b6b1aeb6b1adb2aca8b2aca84876a04a78a2517fa74771973a5d80405c7a6161677c695fac8a75b08d77b4917aaf8971ad876fa5816aa6846ea78670a98a76ac9484ab9f96b2aca8bdb8b4bcb7b3bcb8b4bcb8b4b8b3afb7b2aeb9b4b0b8b3afb8b2aeb6afabb3aeaab2aeaa4878a14b7aa34c7ba44a759b3d63873b5f825b67766f5f569c7e6caf8c77b18f79b28f78b5927caf8e78a98872aa8a76a98a76ac917fada199b7b0acb9b3afbfb9b5c1bab6bdb6b2b8b3afbab5b1b9b4b0b6afabb7b1adb3ada9b3aeaab0aba8\";\n\nconst WIDTH = 32;\nconst HEIGHT = 32;\n\nfunction parseImage(): number[][][] {\n\tconst pixels: number[][][] = [];\n\tfor (let y = 0; y < HEIGHT; y++) {\n\t\tconst row: number[][] = [];\n\t\tfor (let x = 0; x < WIDTH; x++) {\n\t\t\tconst idx = (y * WIDTH + x) * 6;\n\t\t\tconst r = parseInt(DAX_HEX.slice(idx, idx + 2), 16);\n\t\t\tconst g = parseInt(DAX_HEX.slice(idx + 2, idx + 4), 16);\n\t\t\tconst b = parseInt(DAX_HEX.slice(idx + 4, idx + 6), 16);\n\t\t\trow.push([r, g, b]);\n\t\t}\n\t\tpixels.push(row);\n\t}\n\treturn pixels;\n}\n\nfunction rgb(r: number, g: number, b: number, bg = false): string {\n\treturn `\\x1b[${bg ? 48 : 38};2;${r};${g};${b}m`;\n}\n\nconst RESET = \"\\x1b[0m\";\n\nfunction buildImage(): string[] {\n\tconst pixels = parseImage();\n\tconst lines: string[] = [];\n\n\t// Use half-block chars: ▄ with bg=top pixel, fg=bottom pixel\n\tfor (let row = 0; row < HEIGHT; row += 2) {\n\t\tlet line = \"\";\n\t\tfor (let x = 0; x < WIDTH; x++) {\n\t\t\tconst top = pixels[row][x];\n\t\t\tconst bottom = pixels[row + 1]?.[x] ?? top;\n\t\t\tline += rgb(bottom[0], bottom[1], bottom[2]) + rgb(top[0], top[1], top[2], true) + \"▄\";\n\t\t}\n\t\tline += RESET;\n\t\tlines.push(line);\n\t}\n\treturn lines;\n}\n\nexport class DaxnutsComponent implements Component {\n\tprivate ui: TUI;\n\tprivate image: string[];\n\tprivate interval: ReturnType<typeof setInterval> | null = null;\n\tprivate tick = 0;\n\tprivate maxTicks = 25; // ~2 seconds at 80ms\n\tprivate cachedLines: string[] = [];\n\tprivate cachedWidth = 0;\n\tprivate cachedTick = -1;\n\n\tconstructor(ui: TUI) {\n\t\tthis.ui = ui;\n\t\tthis.image = buildImage();\n\t\tthis.startAnimation();\n\t}\n\n\tinvalidate(): void {\n\t\tthis.cachedWidth = 0;\n\t}\n\n\tprivate startAnimation(): void {\n\t\tthis.interval = setInterval(() => {\n\t\t\tthis.tick++;\n\t\t\tif (this.tick >= this.maxTicks) {\n\t\t\t\tthis.stopAnimation();\n\t\t\t}\n\t\t\tthis.cachedWidth = 0;\n\t\t\tthis.ui.requestRender();\n\t\t}, 80);\n\t}\n\n\tprivate stopAnimation(): void {\n\t\tif (this.interval) {\n\t\t\tclearInterval(this.interval);\n\t\t\tthis.interval = null;\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\tif (width === this.cachedWidth && this.cachedTick === this.tick) {\n\t\t\treturn this.cachedLines;\n\t\t}\n\n\t\tconst t = theme;\n\t\tconst lines: string[] = [];\n\n\t\tconst center = (s: string) => {\n\t\t\tconst visible = s.replace(/\\x1b\\[[0-9;]*m/g, \"\").length;\n\t\t\tconst left = Math.max(0, Math.floor((width - visible) / 2));\n\t\t\treturn \" \".repeat(left) + s;\n\t\t};\n\n\t\tlines.push(\"\");\n\n\t\t// Scanline reveal effect: show rows progressively\n\t\tconst revealedRows = Math.min(\n\t\t\tthis.image.length,\n\t\t\tMath.floor((this.tick / this.maxTicks) * (this.image.length + 3)),\n\t\t);\n\n\t\tfor (let i = 0; i < this.image.length; i++) {\n\t\t\tif (i < revealedRows) {\n\t\t\t\tlines.push(center(this.image[i]));\n\t\t\t} else {\n\t\t\t\t// Show scan line\n\t\t\t\tif (i === revealedRows) {\n\t\t\t\t\tconst scanline = \"▓\".repeat(WIDTH);\n\t\t\t\t\tlines.push(center(rgb(100, 200, 255) + scanline + RESET));\n\t\t\t\t} else {\n\t\t\t\t\tlines.push(center(\" \".repeat(WIDTH)));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlines.push(\"\");\n\n\t\t// Fade in text after image is revealed\n\t\tconst textPhase = Math.max(0, this.tick - this.maxTicks * 0.6);\n\t\tif (textPhase > 0 || this.tick >= this.maxTicks) {\n\t\t\tlines.push(center(t.fg(\"accent\", \"Free Kimi K2.5 via OpenCode Zen\")));\n\t\t\tlines.push(center(t.fg(\"success\", '\"Powered by daxnuts\"')));\n\t\t\tlines.push(center(t.fg(\"muted\", \"— @thdxr\")));\n\t\t} else {\n\t\t\tlines.push(\"\");\n\t\t\tlines.push(\"\");\n\t\t\tlines.push(\"\");\n\t\t}\n\n\t\tlines.push(\"\");\n\t\tif (textPhase > 2 || this.tick >= this.maxTicks) {\n\t\t\tlines.push(center(t.fg(\"dim\", \"Try OpenCode\")));\n\t\t\tlines.push(center(t.fg(\"mdLink\", \"https://mistral.ai/news/mistral-vibe-2-0\")));\n\t\t} else {\n\t\t\tlines.push(\"\");\n\t\t\tlines.push(\"\");\n\t\t}\n\t\tlines.push(\"\");\n\n\t\tthis.cachedLines = lines;\n\t\tthis.cachedWidth = width;\n\t\tthis.cachedTick = this.tick;\n\t\treturn lines;\n\t}\n\n\tdispose(): void {\n\t\tthis.stopAnimation();\n\t}\n}\n"]}
1
+ {"version":3,"file":"daxnuts.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/daxnuts.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * POWERED BY DAXNUTS - Easter egg for OpenCode + Kimi K2.5\n *\n * A heartfelt tribute to dax (@thdxr) for providing free Kimi K2.5 access via OpenCode.\n */\n\nimport type { Component, TUI } from \"@mariozechner/pi-tui\";\nimport { theme } from \"../theme/theme.js\";\n\n// 32x32 RGB image of dax, hex encoded (3 bytes per pixel)\nconst DAX_HEX =\n\t\"bbbab8b9b9b6b9b8b5bcbbb8b8b7b4b7b5b2b6b5b2b8b7b4b7b6b3b6b4b1bdbcb8bab8b6bbb8b5b8b5b1bbb8b4c2bebbc1bebac0bdbabfbcb9c1bebabfbebbc0bfbcc0bdbabbb8b5c1bfbcbfbcb8bbb9b6bfbcb8c2bfbcc1bfbcbfbbb8bdb9b6b8b7b5b9b8b5b8b8b5b5b5b2b6b5b2b8b7b4b9b8b5b9b8b5b6b5b3bab8b5bcbab7bbb9b6bbb8b5bfb9b5bdb2abbcb0a8beb2aabeb5afbfbab6bebab7c0bfbcbebdbabebbb8c0bdbabfbebbc2bebbbdbab7c3c0bdc3c0bdc1bebbc2bebabfbcb8bab9b6b7b6b3b2b1aeb6b5b2b5b4b1b5b4b2b6b5b2b7b6b4b9b8b6b7b6b3bbbab7b2afaba5988fb49e90b09481b79a88b39683b09583b7a395bfb6b0c0bdbabdbbb8bebcb9c1bfbcc0bebbbdbab7bebbb8c2bfbcc0bdbac0bcb9bdb9b6c0bcb8b5b4b2b4b3b0bab9b6b9b9b6b5b4b1b5b4b1b6b5b3b9b8b5b9b8b6b9b8b6b2aeaa968174a6836eaa856eab846eaf8973ac8973b08f79b18f7ab39786b7a89dbbb3aebfbab6c2c0bdbebcb9bfbdbac3c1bdc2bebbc0bcb9bdb9b6c1bdbabfbbb8b4b3b0b9b8b5b8b7b5b4b3b1b5b4b1b8b7b4b8b7b5bab9b6bbbab7b1afad8c7a719d735ca47860a87d65a98069ae8972ae8c75af8d77aa826ba98067aa8974b39e90b6a79dbbb2adc0bdbac1bfbdbfbbb8c1bdb9bebab6c0bdb9bfbbb8c1bdbab4b2b0b7b6b4b7b6b3b4b2b0bab9b7b6b5b2b6b5b2bab9b6bab9b6958c87977663aa836bac8772b08f7aad8c77b2917db0917db0907cac8971a77d64a87f67ac8972b29887b8a89dbfbab5bfbdbac1bebac0bcb9c0bcb9c0bcb9c1bebabebab7b8b7b4b7b6b4b5b4b1b5b4b2b7b6b3b5b4b2bab9b7bab9b6b4b1ada88f7fad8973ae8d78b19684b19685b29786b69a89b29582b1917daa856ea87e66a97e66ad866ea9826baf9280b8ada6bdbbb8bebab7bfbbb8c1bdbabfbbb8bcb8b4bcb8b5b6b4b2b7b5b3b6b5b2b8b7b4b3b2afb8b7b4b6b5b2b3b2b0b3a59aab856fad8d78b0917eb19886b49b8bb49a89b39785b0917eaf8f7cab866fa77d65a77a61a87d64a9816ab08f79b5a296c1bcb8c3bfbcc2bebbbebab7bfbbb7bdbab6c2bebab8b7b4b7b6b4b6b5b3b7b6b3b6b5b2b9b8b6b4b3b1b6b1acac8f7ca9826bae8f7aaf9583b49c8cb49c8bb79d8cb59987b19380ad8e79ae8c77af8e78ac8771a3775faa826bae8972b39888bbb6b2bebbb8bfbbb8bfbbb8c0bdb9bebbb7c0bdb9b6b5b2b9b8b5b4b3b1b8b7b5b4b3b0b7b6b4b6b5b3b1a7a0aa8772a77d65a88570b49887b19b8d9c887c907a6d987f71aa907faf917daf8e7aad8c78ac8b77a8836ca9836cac8770b49b8abdb6b2c0bcb9c0bdb9bfbbb8bebab7bfbcb9bebab7b9b8b6b5b4b2b9b8b5b8b7b5b8b7b4b7b6b4b5b4b2b3a9a2ad8973a1755da9856fb398858c776a65544b776358725d526e594d9c7f6eb1907ba68672ad8e7aab8771ac856db18f79b3a092beb9b5c1bdbabdb9b5bebab7bfbbb7bebab7bcb9b6b7b6b4b6b6b3b8b7b4b5b4b2b8b6b4b7b6b3b4b3b0b4aba4a6826ba3775fb08e79b19584a88e7daa8e7db29481ad8f7c997e6da38674ac8d79ac8e7aae917f9a7c6a896a599a7c6ab3a398c1bdbabdb9b6bcb8b5bebab6bebab7bdb9b5bdb9b6b5b4b1b7b5b3b5b4b2b7b6b3b7b6b4b3b3b0b3b2b0b4aca5a7846fa97f68ae8f7bae9383b59c8bb2937fae8e79ac8b76af927eaf927eb29683b39885b2988891786a72594c6e594d978d86bdbab7bab7b3c0bcb9c0bcb9bebab7bebbb7bdb9b6b3b2b0b4b3b0b5b4b2b4b4b1b4b3b1b4b3b1b4b3b0b6ada5aa8670a57a62ad8e7ab29b8cb69d8dab856fa9826aa88069ab8771af907db49987b19684b29886b59987b39480b09787b5a9a1bcb8b5bebab7bdb9b5bebab7bfbbb8bfbbb7bbb7b4b3b2afb8b7b5b8b7b5b3b2b0b5b4b2b6b5b3b6b4b1afa299a98975a9826baf907cb39988b49a89af8e7aac8973aa856eaf8c74b1917dae907dac907db39988b29785b49785b7a090b9aca3bfbab7bcb8b5bdb9b6bcb8b4bcb8b5bdb9b5bcb8b4b5b4b2b6b5b3b4b3b0b4b3b0b9b8b5b8b6b4908b88887467aa8f7ea78976ad8973b08b74b59885b69e8eb29888b1917cb1917db1937fae907cb19686b39a8ab29886b59b8ab8a192b6aaa3b7b2afbcb8b4bcb8b5bbb7b4c0bcb9bebab7c0bcb9b6b5b2b6b5b3b4b3b0bab9b7b7b6b4b1b0ae7b716ba083709b806f716158967764b08870b29481b69b8ab69f8fb39a89b69f90b49d8db39a89b29988b49c8cb6a090b8a496baa49593867f8f8986bfbbb7bdb9b5bcb7b4bab6b3b9b5b2bab6b2b4b3b1b3b3b0b6b5b3b8b7b5b4b2b0a7a5a38f837dae917ea084725a504c63544da28370b39784b59e8db2a093a698909b918b998e8790857e95877dad998bb39c8cb5a091b9a2938d827c95908dbebab6bbb7b3bdbab7bbb7b4bdb9b6bbb7b4b4b3b0b5b4b1b8b7b5b6b5b3b8b8b5b4b2af968f8ab29a8bab9485544b483a323073655d96887f70655f61595547403e453e3c453f3d57504f655e5b90847db39c8db7a090b6a09189807aaba6a3bdb9b6c0bcb9bebab7bcb7b4bebab7bbb7b4b3b2b0b6b5b3b2b1afb7b6b4b8b7b4b5b4b1aeaba8b5a89fac998d4d44412d25244d46444e4744322b293a3230423937433a37352d2a59504c534b48524a48988a81b59f8fb19c8d827974b2afacbdb9b5bcb8b4bdb9b5bcb8b5bdb9b6bab6b2b8b7b5b5b4b2b6b6b3b9b8b5b7b6b3b6b5b2b8b6b3b9b4b1b2a9a26c64612d25242d2625312a28352d2c453d3a78675c8d7a6ea09792aea6a0615854332b29524a479f8e82b09d90a49b96c1bdb9bebab7bfbbb8bbb8b4b9b5b1b8b4b0b9b4b0b7b6b4b8b7b5b8b7b4b6b5b3b8b6b3bab9b6b9b8b5b4b3b0b7b5b2a5a29f453d3b261e1d261f1e2e2625413936857268977865b19482b5a69caca5a07c7572453d3b746963a0948cc5bfbbc0bbb8beb9b6bbb7b3bbb6b3b7b3afb8b4b0b9b5b1b7b6b3b6b5b3b5b4b2b5b4b2b7b6b3b7b6b3b8b6b3b4b2afb7b6b3b3b1ae6d6765251f1e1e18172a22212d2523443b3971625ab19888b09482a89182877e792c25243e3634766d6abeb9b5bfbbb7bebab6bcb7b3bbb6b3b9b5b1b7b3afb8b4b0b4b3b0b5b4b1b5b4b1b4b3b1b5b4b2b8b6b4b5b3b0b9b6b4b5b4b1b6b4b27f79762a2322221c1b2d2524221b1a443e3c47413f6f676281766f867971675e5a3e37352a222166605dbab7b3bdb9b5beb9b5bcb7b3bcb7b3b9b4b0bab6b2bab6b2b5b3b0b6b4b2b3b2afb7b6b3b4b4b1b4b3b0b6b4b1b5b4b1b4b3b0b9b6b29a8c8252474230292828201f181212322c2c231e1d1c16162c26252923222d26252d2523332b2a8e8885bcb8b5bcb7b3bbb6b2bcb7b3b9b4b1b9b5b1b7b2afb7b2ae7a838e9b9b9caeadacb3b2b0b3b2afb7b7b4b6b5b3b6b6b3b7b6b3b9ada4a991808e7b6f50453f2b24231a14142923221f19181d17161f18182620201d17162a22215d5654b7b3b0bbb7b3bbb6b2b8b4b0bab5b1bbb6b2bab5b1b8b4b0bab6b22c496b4c5d735f68766e727a828285929090adaba8b7b2aeb6a59ab39682a28470a387748e76674e403a1a14141d1716181211221c1c1f1918221c1b2f2827342d2c8d8884bab6b3b9b5b2bab5b1bab5b1b9b4b0bab6b2b8b4b0b9b4b0b7b2ae325e8b365f8a3a5d833f5b7a545f70646469706b6aa08f84b08e78b18e769f7e689e7f6b9e816d907766584940362d2a1c1615201b1a1a1413201a1a251e1d393331a39e9bbab5b1bcb7b3bab6b2b8b3afb8b4b0b9b4b0b9b4b1bab5b2b5b0ac3d6c9843729d44719c426e98415f805a64716f6a699d8677b1927eb3947faa89749d7a649f7f6ba487749e837186716454463f2c25231e181837302e3a33317a7471beb9b6bcb8b4bbb6b2b6b2aebab5b1b9b5b1b8b3afbab6b2b6b1adb5aeaa4877a14c7aa44e7ba345719a3a5d80586b7f767475927b6eb1927faf8e79b08e78a78169a07861a17f6aa58570a688749b83738270666f66618a8480a49e99b7b2aebab6b2bcb8b4b9b5b1b7b2aebab5b1b9b4b0b6b1aeb6b1adb2aca8b2aca84876a04a78a2517fa74771973a5d80405c7a6161677c695fac8a75b08d77b4917aaf8971ad876fa5816aa6846ea78670a98a76ac9484ab9f96b2aca8bdb8b4bcb7b3bcb8b4bcb8b4b8b3afb7b2aeb9b4b0b8b3afb8b2aeb6afabb3aeaab2aeaa4878a14b7aa34c7ba44a759b3d63873b5f825b67766f5f569c7e6caf8c77b18f79b28f78b5927caf8e78a98872aa8a76a98a76ac917fada199b7b0acb9b3afbfb9b5c1bab6bdb6b2b8b3afbab5b1b9b4b0b6afabb7b1adb3ada9b3aeaab0aba8\";\n\nconst WIDTH = 32;\nconst HEIGHT = 32;\n\nfunction parseImage(): number[][][] {\n\tconst pixels: number[][][] = [];\n\tfor (let y = 0; y < HEIGHT; y++) {\n\t\tconst row: number[][] = [];\n\t\tfor (let x = 0; x < WIDTH; x++) {\n\t\t\tconst idx = (y * WIDTH + x) * 6;\n\t\t\tconst r = parseInt(DAX_HEX.slice(idx, idx + 2), 16);\n\t\t\tconst g = parseInt(DAX_HEX.slice(idx + 2, idx + 4), 16);\n\t\t\tconst b = parseInt(DAX_HEX.slice(idx + 4, idx + 6), 16);\n\t\t\trow.push([r, g, b]);\n\t\t}\n\t\tpixels.push(row);\n\t}\n\treturn pixels;\n}\n\nfunction rgb(r: number, g: number, b: number, bg = false): string {\n\treturn `\\x1b[${bg ? 48 : 38};2;${r};${g};${b}m`;\n}\n\nconst RESET = \"\\x1b[0m\";\n\nfunction buildImage(): string[] {\n\tconst pixels = parseImage();\n\tconst lines: string[] = [];\n\n\t// Use half-block chars: ▄ with bg=top pixel, fg=bottom pixel\n\tfor (let row = 0; row < HEIGHT; row += 2) {\n\t\tlet line = \"\";\n\t\tfor (let x = 0; x < WIDTH; x++) {\n\t\t\tconst top = pixels[row][x];\n\t\t\tconst bottom = pixels[row + 1]?.[x] ?? top;\n\t\t\tline += `${rgb(bottom[0], bottom[1], bottom[2])}${rgb(top[0], top[1], top[2], true)}▄`;\n\t\t}\n\t\tline += RESET;\n\t\tlines.push(line);\n\t}\n\treturn lines;\n}\n\nexport class DaxnutsComponent implements Component {\n\tprivate ui: TUI;\n\tprivate image: string[];\n\tprivate interval: ReturnType<typeof setInterval> | null = null;\n\tprivate tick = 0;\n\tprivate maxTicks = 25; // ~2 seconds at 80ms\n\tprivate cachedLines: string[] = [];\n\tprivate cachedWidth = 0;\n\tprivate cachedTick = -1;\n\n\tconstructor(ui: TUI) {\n\t\tthis.ui = ui;\n\t\tthis.image = buildImage();\n\t\tthis.startAnimation();\n\t}\n\n\tinvalidate(): void {\n\t\tthis.cachedWidth = 0;\n\t}\n\n\tprivate startAnimation(): void {\n\t\tthis.interval = setInterval(() => {\n\t\t\tthis.tick++;\n\t\t\tif (this.tick >= this.maxTicks) {\n\t\t\t\tthis.stopAnimation();\n\t\t\t}\n\t\t\tthis.cachedWidth = 0;\n\t\t\tthis.ui.requestRender();\n\t\t}, 80);\n\t}\n\n\tprivate stopAnimation(): void {\n\t\tif (this.interval) {\n\t\t\tclearInterval(this.interval);\n\t\t\tthis.interval = null;\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\tif (width === this.cachedWidth && this.cachedTick === this.tick) {\n\t\t\treturn this.cachedLines;\n\t\t}\n\n\t\tconst t = theme;\n\t\tconst lines: string[] = [];\n\n\t\tconst center = (s: string) => {\n\t\t\tconst visible = s.replace(/\\x1b\\[[0-9;]*m/g, \"\").length;\n\t\t\tconst left = Math.max(0, Math.floor((width - visible) / 2));\n\t\t\treturn \" \".repeat(left) + s;\n\t\t};\n\n\t\tlines.push(\"\");\n\n\t\t// Scanline reveal effect: show rows progressively\n\t\tconst revealedRows = Math.min(\n\t\t\tthis.image.length,\n\t\t\tMath.floor((this.tick / this.maxTicks) * (this.image.length + 3)),\n\t\t);\n\n\t\tfor (let i = 0; i < this.image.length; i++) {\n\t\t\tif (i < revealedRows) {\n\t\t\t\tlines.push(center(this.image[i]));\n\t\t\t} else {\n\t\t\t\t// Show scan line\n\t\t\t\tif (i === revealedRows) {\n\t\t\t\t\tconst scanline = \"▓\".repeat(WIDTH);\n\t\t\t\t\tlines.push(center(rgb(100, 200, 255) + scanline + RESET));\n\t\t\t\t} else {\n\t\t\t\t\tlines.push(center(\" \".repeat(WIDTH)));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlines.push(\"\");\n\n\t\t// Fade in text after image is revealed\n\t\tconst textPhase = Math.max(0, this.tick - this.maxTicks * 0.6);\n\t\tif (textPhase > 0 || this.tick >= this.maxTicks) {\n\t\t\tlines.push(center(t.fg(\"accent\", \"Free Kimi K2.5 via OpenCode Zen\")));\n\t\t\tlines.push(center(t.fg(\"success\", '\"Powered by daxnuts\"')));\n\t\t\tlines.push(center(t.fg(\"muted\", \"— @thdxr\")));\n\t\t} else {\n\t\t\tlines.push(\"\");\n\t\t\tlines.push(\"\");\n\t\t\tlines.push(\"\");\n\t\t}\n\n\t\tlines.push(\"\");\n\t\tif (textPhase > 2 || this.tick >= this.maxTicks) {\n\t\t\tlines.push(center(t.fg(\"dim\", \"Try OpenCode\")));\n\t\t\tlines.push(center(t.fg(\"mdLink\", \"https://mistral.ai/news/mistral-vibe-2-0\")));\n\t\t} else {\n\t\t\tlines.push(\"\");\n\t\t\tlines.push(\"\");\n\t\t}\n\t\tlines.push(\"\");\n\n\t\tthis.cachedLines = lines;\n\t\tthis.cachedWidth = width;\n\t\tthis.cachedTick = this.tick;\n\t\treturn lines;\n\t}\n\n\tdispose(): void {\n\t\tthis.stopAnimation();\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"scoped-models-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/scoped-models-selector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EACN,SAAS,EACT,KAAK,SAAS,EAGd,KAAK,EAKL,MAAM,sBAAsB,CAAC;AA2D9B,MAAM,WAAW,YAAY;IAC5B,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IACxB,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B,qEAAqE;IACrE,sBAAsB,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC/B,gEAAgE;IAChE,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3D,sEAAsE;IACtE,SAAS,EAAE,CAAC,eAAe,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC/C,0EAA0E;IAC1E,WAAW,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC7C,yCAAyC;IACzC,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,sFAAsF;IACtF,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACnF,QAAQ,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,qBAAa,6BAA8B,SAAQ,SAAU,YAAW,SAAS;IAChF,OAAO,CAAC,UAAU,CAAsC;IACxD,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,UAAU,CAAoB;IACtC,OAAO,CAAC,aAAa,CAAmB;IACxC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,WAAW,CAAQ;IAG3B,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IACD,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,UAAU,CAAO;IACzB,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,OAAO,CAAS;IAExB,YAAY,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,eAAe,EAoC3D;IAED,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,OAAO;IASf,OAAO,CAAC,UAAU;IAiClB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAmH9B;IAED,cAAc,IAAI,KAAK,CAEtB;CACD","sourcesContent":["import type { Model } from \"@mariozechner/pi-ai\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetEditorKeybindings,\n\tInput,\n\tKey,\n\tmatchesKey,\n\tSpacer,\n\tText,\n} from \"@mariozechner/pi-tui\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\n\n// EnabledIds: null = all enabled (no filter), string[] = explicit ordered list\ntype EnabledIds = string[] | null;\n\nfunction isEnabled(enabledIds: EnabledIds, id: string): boolean {\n\treturn enabledIds === null || enabledIds.includes(id);\n}\n\nfunction toggle(enabledIds: EnabledIds, id: string): EnabledIds {\n\tif (enabledIds === null) return [id]; // First toggle: start with only this one\n\tconst index = enabledIds.indexOf(id);\n\tif (index >= 0) return [...enabledIds.slice(0, index), ...enabledIds.slice(index + 1)];\n\treturn [...enabledIds, id];\n}\n\nfunction enableAll(enabledIds: EnabledIds, allIds: string[], targetIds?: string[]): EnabledIds {\n\tif (enabledIds === null) return null; // Already all enabled\n\tconst targets = targetIds ?? allIds;\n\tconst result = [...enabledIds];\n\tfor (const id of targets) {\n\t\tif (!result.includes(id)) result.push(id);\n\t}\n\treturn result.length === allIds.length ? null : result;\n}\n\nfunction clearAll(enabledIds: EnabledIds, allIds: string[], targetIds?: string[]): EnabledIds {\n\tif (enabledIds === null) {\n\t\treturn targetIds ? allIds.filter((id) => !targetIds.includes(id)) : [];\n\t}\n\tconst targets = new Set(targetIds ?? enabledIds);\n\treturn enabledIds.filter((id) => !targets.has(id));\n}\n\nfunction move(enabledIds: EnabledIds, allIds: string[], id: string, delta: number): EnabledIds {\n\tconst list = enabledIds ?? [...allIds];\n\tconst index = list.indexOf(id);\n\tif (index < 0) return list;\n\tconst newIndex = index + delta;\n\tif (newIndex < 0 || newIndex >= list.length) return list;\n\tconst result = [...list];\n\t[result[index], result[newIndex]] = [result[newIndex], result[index]];\n\treturn result;\n}\n\nfunction getSortedIds(enabledIds: EnabledIds, allIds: string[]): string[] {\n\tif (enabledIds === null) return allIds;\n\tconst enabledSet = new Set(enabledIds);\n\treturn [...enabledIds, ...allIds.filter((id) => !enabledSet.has(id))];\n}\n\ninterface ModelItem {\n\tfullId: string;\n\tmodel: Model<any>;\n\tenabled: boolean;\n}\n\nexport interface ModelsConfig {\n\tallModels: Model<any>[];\n\tenabledModelIds: Set<string>;\n\t/** true if enabledModels setting is defined (empty = all enabled) */\n\thasEnabledModelsFilter: boolean;\n}\n\nexport interface ModelsCallbacks {\n\t/** Called when a model is toggled (session-only, no persist) */\n\tonModelToggle: (modelId: string, enabled: boolean) => void;\n\t/** Called when user wants to persist current selection to settings */\n\tonPersist: (enabledModelIds: string[]) => void;\n\t/** Called when user enables all models. Returns list of all model IDs. */\n\tonEnableAll: (allModelIds: string[]) => void;\n\t/** Called when user clears all models */\n\tonClearAll: () => void;\n\t/** Called when user toggles all models for a provider. Returns affected model IDs. */\n\tonToggleProvider: (provider: string, modelIds: string[], enabled: boolean) => void;\n\tonCancel: () => void;\n}\n\n/**\n * Component for enabling/disabling models for Ctrl+P cycling.\n * Changes are session-only until explicitly persisted with Ctrl+S.\n */\nexport class ScopedModelsSelectorComponent extends Container implements Focusable {\n\tprivate modelsById: Map<string, Model<any>> = new Map();\n\tprivate allIds: string[] = [];\n\tprivate enabledIds: EnabledIds = null;\n\tprivate filteredItems: ModelItem[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\tprivate listContainer: Container;\n\tprivate footerText: Text;\n\tprivate callbacks: ModelsCallbacks;\n\tprivate maxVisible = 15;\n\tprivate isDirty = false;\n\n\tconstructor(config: ModelsConfig, callbacks: ModelsCallbacks) {\n\t\tsuper();\n\t\tthis.callbacks = callbacks;\n\n\t\tfor (const model of config.allModels) {\n\t\t\tconst fullId = `${model.provider}/${model.id}`;\n\t\t\tthis.modelsById.set(fullId, model);\n\t\t\tthis.allIds.push(fullId);\n\t\t}\n\n\t\tthis.enabledIds = config.hasEnabledModelsFilter ? [...config.enabledModelIds] : null;\n\t\tthis.filteredItems = this.buildItems();\n\n\t\t// Header\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(theme.fg(\"accent\", theme.bold(\"Model Configuration\")), 0, 0));\n\t\tthis.addChild(new Text(theme.fg(\"muted\", \"Session-only. Ctrl+S to save to settings.\"), 0, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Search input\n\t\tthis.searchInput = new Input();\n\t\tthis.addChild(this.searchInput);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// List container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\t// Footer hint\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.footerText = new Text(this.getFooterText(), 0, 0);\n\t\tthis.addChild(this.footerText);\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.updateList();\n\t}\n\n\tprivate buildItems(): ModelItem[] {\n\t\treturn getSortedIds(this.enabledIds, this.allIds).map((id) => ({\n\t\t\tfullId: id,\n\t\t\tmodel: this.modelsById.get(id)!,\n\t\t\tenabled: isEnabled(this.enabledIds, id),\n\t\t}));\n\t}\n\n\tprivate getFooterText(): string {\n\t\tconst enabledCount = this.enabledIds?.length ?? this.allIds.length;\n\t\tconst allEnabled = this.enabledIds === null;\n\t\tconst countText = allEnabled ? \"all enabled\" : `${enabledCount}/${this.allIds.length} enabled`;\n\t\tconst parts = [\"Enter toggle\", \"^A all\", \"^X clear\", \"^P provider\", \"Alt+↑↓ reorder\", \"^S save\", countText];\n\t\treturn this.isDirty\n\t\t\t? theme.fg(\"dim\", ` ${parts.join(\" · \")} `) + theme.fg(\"warning\", \"(unsaved)\")\n\t\t\t: theme.fg(\"dim\", ` ${parts.join(\" · \")}`);\n\t}\n\n\tprivate refresh(): void {\n\t\tconst query = this.searchInput.getValue();\n\t\tconst items = this.buildItems();\n\t\tthis.filteredItems = query ? fuzzyFilter(items, query, (i) => `${i.model.id} ${i.model.provider}`) : items;\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredItems.length - 1));\n\t\tthis.updateList();\n\t\tthis.footerText.setText(this.getFooterText());\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tif (this.filteredItems.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching models\"), 0, 0));\n\t\t\treturn;\n\t\t}\n\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);\n\t\tconst allEnabled = this.enabledIds === null;\n\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredItems[i]!;\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst prefix = isSelected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tconst modelText = isSelected ? theme.fg(\"accent\", item.model.id) : item.model.id;\n\t\t\tconst providerBadge = theme.fg(\"muted\", ` [${item.model.provider}]`);\n\t\t\tconst status = allEnabled ? \"\" : item.enabled ? theme.fg(\"success\", \" ✓\") : theme.fg(\"dim\", \" ✗\");\n\t\t\tthis.listContainer.addChild(new Text(`${prefix}${modelText}${providerBadge}${status}`, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredItems.length) {\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`), 0, 0),\n\t\t\t);\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getEditorKeybindings();\n\n\t\t// Navigation\n\t\tif (kb.matches(data, \"selectUp\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredItems.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"selectDown\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredItems.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\n\t\t// Alt+Up/Down - Reorder enabled models\n\t\tif (matchesKey(data, Key.alt(\"up\")) || matchesKey(data, Key.alt(\"down\"))) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item && isEnabled(this.enabledIds, item.fullId)) {\n\t\t\t\tconst delta = matchesKey(data, Key.alt(\"up\")) ? -1 : 1;\n\t\t\t\tconst enabledList = this.enabledIds ?? this.allIds;\n\t\t\t\tconst currentIndex = enabledList.indexOf(item.fullId);\n\t\t\t\tconst newIndex = currentIndex + delta;\n\t\t\t\t// Only move if within bounds\n\t\t\t\tif (newIndex >= 0 && newIndex < enabledList.length) {\n\t\t\t\t\tthis.enabledIds = move(this.enabledIds, this.allIds, item.fullId, delta);\n\t\t\t\t\tthis.isDirty = true;\n\t\t\t\t\tthis.selectedIndex += delta;\n\t\t\t\t\tthis.refresh();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Toggle on Enter\n\t\tif (matchesKey(data, Key.enter)) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item) {\n\t\t\t\tconst wasAllEnabled = this.enabledIds === null;\n\t\t\t\tthis.enabledIds = toggle(this.enabledIds, item.fullId);\n\t\t\t\tthis.isDirty = true;\n\t\t\t\tif (wasAllEnabled) this.callbacks.onClearAll();\n\t\t\t\tthis.callbacks.onModelToggle(item.fullId, isEnabled(this.enabledIds, item.fullId));\n\t\t\t\tthis.refresh();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+A - Enable all (filtered if search active, otherwise all)\n\t\tif (matchesKey(data, Key.ctrl(\"a\"))) {\n\t\t\tconst targetIds = this.searchInput.getValue() ? this.filteredItems.map((i) => i.fullId) : undefined;\n\t\t\tthis.enabledIds = enableAll(this.enabledIds, this.allIds, targetIds);\n\t\t\tthis.isDirty = true;\n\t\t\tthis.callbacks.onEnableAll(targetIds ?? this.allIds);\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+X - Clear all (filtered if search active, otherwise all)\n\t\tif (matchesKey(data, Key.ctrl(\"x\"))) {\n\t\t\tconst targetIds = this.searchInput.getValue() ? this.filteredItems.map((i) => i.fullId) : undefined;\n\t\t\tthis.enabledIds = clearAll(this.enabledIds, this.allIds, targetIds);\n\t\t\tthis.isDirty = true;\n\t\t\tthis.callbacks.onClearAll();\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+P - Toggle provider of current item\n\t\tif (matchesKey(data, Key.ctrl(\"p\"))) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item) {\n\t\t\t\tconst provider = item.model.provider;\n\t\t\t\tconst providerIds = this.allIds.filter((id) => this.modelsById.get(id)!.provider === provider);\n\t\t\t\tconst allEnabled = providerIds.every((id) => isEnabled(this.enabledIds, id));\n\t\t\t\tthis.enabledIds = allEnabled\n\t\t\t\t\t? clearAll(this.enabledIds, this.allIds, providerIds)\n\t\t\t\t\t: enableAll(this.enabledIds, this.allIds, providerIds);\n\t\t\t\tthis.isDirty = true;\n\t\t\t\tthis.callbacks.onToggleProvider(provider, providerIds, !allEnabled);\n\t\t\t\tthis.refresh();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+S - Save/persist to settings\n\t\tif (matchesKey(data, Key.ctrl(\"s\"))) {\n\t\t\tthis.callbacks.onPersist(this.enabledIds ?? [...this.allIds]);\n\t\t\tthis.isDirty = false;\n\t\t\tthis.footerText.setText(this.getFooterText());\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+C - clear search or cancel if empty\n\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\tif (this.searchInput.getValue()) {\n\t\t\t\tthis.searchInput.setValue(\"\");\n\t\t\t\tthis.refresh();\n\t\t\t} else {\n\t\t\t\tthis.callbacks.onCancel();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Escape - cancel\n\t\tif (matchesKey(data, Key.escape)) {\n\t\t\tthis.callbacks.onCancel();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass everything else to search input\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.refresh();\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
1
+ {"version":3,"file":"scoped-models-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/scoped-models-selector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EACN,SAAS,EACT,KAAK,SAAS,EAGd,KAAK,EAKL,MAAM,sBAAsB,CAAC;AA2D9B,MAAM,WAAW,YAAY;IAC5B,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IACxB,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B,qEAAqE;IACrE,sBAAsB,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC/B,gEAAgE;IAChE,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3D,sEAAsE;IACtE,SAAS,EAAE,CAAC,eAAe,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC/C,0EAA0E;IAC1E,WAAW,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC7C,yCAAyC;IACzC,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,sFAAsF;IACtF,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACnF,QAAQ,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,qBAAa,6BAA8B,SAAQ,SAAU,YAAW,SAAS;IAChF,OAAO,CAAC,UAAU,CAAsC;IACxD,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,UAAU,CAAoB;IACtC,OAAO,CAAC,aAAa,CAAmB;IACxC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,WAAW,CAAQ;IAG3B,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IACD,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,UAAU,CAAO;IACzB,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,OAAO,CAAS;IAExB,YAAY,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,eAAe,EAoC3D;IAED,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,OAAO;IASf,OAAO,CAAC,UAAU;IAiClB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAmH9B;IAED,cAAc,IAAI,KAAK,CAEtB;CACD","sourcesContent":["import type { Model } from \"@mariozechner/pi-ai\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetEditorKeybindings,\n\tInput,\n\tKey,\n\tmatchesKey,\n\tSpacer,\n\tText,\n} from \"@mariozechner/pi-tui\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\n\n// EnabledIds: null = all enabled (no filter), string[] = explicit ordered list\ntype EnabledIds = string[] | null;\n\nfunction isEnabled(enabledIds: EnabledIds, id: string): boolean {\n\treturn enabledIds === null || enabledIds.includes(id);\n}\n\nfunction toggle(enabledIds: EnabledIds, id: string): EnabledIds {\n\tif (enabledIds === null) return [id]; // First toggle: start with only this one\n\tconst index = enabledIds.indexOf(id);\n\tif (index >= 0) return [...enabledIds.slice(0, index), ...enabledIds.slice(index + 1)];\n\treturn [...enabledIds, id];\n}\n\nfunction enableAll(enabledIds: EnabledIds, allIds: string[], targetIds?: string[]): EnabledIds {\n\tif (enabledIds === null) return null; // Already all enabled\n\tconst targets = targetIds ?? allIds;\n\tconst result = [...enabledIds];\n\tfor (const id of targets) {\n\t\tif (!result.includes(id)) result.push(id);\n\t}\n\treturn result.length === allIds.length ? null : result;\n}\n\nfunction clearAll(enabledIds: EnabledIds, allIds: string[], targetIds?: string[]): EnabledIds {\n\tif (enabledIds === null) {\n\t\treturn targetIds ? allIds.filter((id) => !targetIds.includes(id)) : [];\n\t}\n\tconst targets = new Set(targetIds ?? enabledIds);\n\treturn enabledIds.filter((id) => !targets.has(id));\n}\n\nfunction move(enabledIds: EnabledIds, allIds: string[], id: string, delta: number): EnabledIds {\n\tconst list = enabledIds ?? [...allIds];\n\tconst index = list.indexOf(id);\n\tif (index < 0) return list;\n\tconst newIndex = index + delta;\n\tif (newIndex < 0 || newIndex >= list.length) return list;\n\tconst result = [...list];\n\t[result[index], result[newIndex]] = [result[newIndex], result[index]];\n\treturn result;\n}\n\nfunction getSortedIds(enabledIds: EnabledIds, allIds: string[]): string[] {\n\tif (enabledIds === null) return allIds;\n\tconst enabledSet = new Set(enabledIds);\n\treturn [...enabledIds, ...allIds.filter((id) => !enabledSet.has(id))];\n}\n\ninterface ModelItem {\n\tfullId: string;\n\tmodel: Model<any>;\n\tenabled: boolean;\n}\n\nexport interface ModelsConfig {\n\tallModels: Model<any>[];\n\tenabledModelIds: Set<string>;\n\t/** true if enabledModels setting is defined (empty = all enabled) */\n\thasEnabledModelsFilter: boolean;\n}\n\nexport interface ModelsCallbacks {\n\t/** Called when a model is toggled (session-only, no persist) */\n\tonModelToggle: (modelId: string, enabled: boolean) => void;\n\t/** Called when user wants to persist current selection to settings */\n\tonPersist: (enabledModelIds: string[]) => void;\n\t/** Called when user enables all models. Returns list of all model IDs. */\n\tonEnableAll: (allModelIds: string[]) => void;\n\t/** Called when user clears all models */\n\tonClearAll: () => void;\n\t/** Called when user toggles all models for a provider. Returns affected model IDs. */\n\tonToggleProvider: (provider: string, modelIds: string[], enabled: boolean) => void;\n\tonCancel: () => void;\n}\n\n/**\n * Component for enabling/disabling models for Ctrl+P cycling.\n * Changes are session-only until explicitly persisted with Ctrl+S.\n */\nexport class ScopedModelsSelectorComponent extends Container implements Focusable {\n\tprivate modelsById: Map<string, Model<any>> = new Map();\n\tprivate allIds: string[] = [];\n\tprivate enabledIds: EnabledIds = null;\n\tprivate filteredItems: ModelItem[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\tprivate listContainer: Container;\n\tprivate footerText: Text;\n\tprivate callbacks: ModelsCallbacks;\n\tprivate maxVisible = 15;\n\tprivate isDirty = false;\n\n\tconstructor(config: ModelsConfig, callbacks: ModelsCallbacks) {\n\t\tsuper();\n\t\tthis.callbacks = callbacks;\n\n\t\tfor (const model of config.allModels) {\n\t\t\tconst fullId = `${model.provider}/${model.id}`;\n\t\t\tthis.modelsById.set(fullId, model);\n\t\t\tthis.allIds.push(fullId);\n\t\t}\n\n\t\tthis.enabledIds = config.hasEnabledModelsFilter ? [...config.enabledModelIds] : null;\n\t\tthis.filteredItems = this.buildItems();\n\n\t\t// Header\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(theme.fg(\"accent\", theme.bold(\"Model Configuration\")), 0, 0));\n\t\tthis.addChild(new Text(theme.fg(\"muted\", \"Session-only. Ctrl+S to save to settings.\"), 0, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Search input\n\t\tthis.searchInput = new Input();\n\t\tthis.addChild(this.searchInput);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// List container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\t// Footer hint\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.footerText = new Text(this.getFooterText(), 0, 0);\n\t\tthis.addChild(this.footerText);\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.updateList();\n\t}\n\n\tprivate buildItems(): ModelItem[] {\n\t\t// Filter out IDs that no longer have a corresponding model (e.g., after logout)\n\t\treturn getSortedIds(this.enabledIds, this.allIds)\n\t\t\t.filter((id) => this.modelsById.has(id))\n\t\t\t.map((id) => ({\n\t\t\t\tfullId: id,\n\t\t\t\tmodel: this.modelsById.get(id)!,\n\t\t\t\tenabled: isEnabled(this.enabledIds, id),\n\t\t\t}));\n\t}\n\n\tprivate getFooterText(): string {\n\t\tconst enabledCount = this.enabledIds?.length ?? this.allIds.length;\n\t\tconst allEnabled = this.enabledIds === null;\n\t\tconst countText = allEnabled ? \"all enabled\" : `${enabledCount}/${this.allIds.length} enabled`;\n\t\tconst parts = [\"Enter toggle\", \"^A all\", \"^X clear\", \"^P provider\", \"Alt+↑↓ reorder\", \"^S save\", countText];\n\t\treturn this.isDirty\n\t\t\t? theme.fg(\"dim\", ` ${parts.join(\" · \")} `) + theme.fg(\"warning\", \"(unsaved)\")\n\t\t\t: theme.fg(\"dim\", ` ${parts.join(\" · \")}`);\n\t}\n\n\tprivate refresh(): void {\n\t\tconst query = this.searchInput.getValue();\n\t\tconst items = this.buildItems();\n\t\tthis.filteredItems = query ? fuzzyFilter(items, query, (i) => `${i.model.id} ${i.model.provider}`) : items;\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredItems.length - 1));\n\t\tthis.updateList();\n\t\tthis.footerText.setText(this.getFooterText());\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tif (this.filteredItems.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching models\"), 0, 0));\n\t\t\treturn;\n\t\t}\n\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);\n\t\tconst allEnabled = this.enabledIds === null;\n\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredItems[i]!;\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst prefix = isSelected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tconst modelText = isSelected ? theme.fg(\"accent\", item.model.id) : item.model.id;\n\t\t\tconst providerBadge = theme.fg(\"muted\", ` [${item.model.provider}]`);\n\t\t\tconst status = allEnabled ? \"\" : item.enabled ? theme.fg(\"success\", \" ✓\") : theme.fg(\"dim\", \" ✗\");\n\t\t\tthis.listContainer.addChild(new Text(`${prefix}${modelText}${providerBadge}${status}`, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredItems.length) {\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`), 0, 0),\n\t\t\t);\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getEditorKeybindings();\n\n\t\t// Navigation\n\t\tif (kb.matches(data, \"selectUp\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredItems.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"selectDown\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredItems.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\n\t\t// Alt+Up/Down - Reorder enabled models\n\t\tif (matchesKey(data, Key.alt(\"up\")) || matchesKey(data, Key.alt(\"down\"))) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item && isEnabled(this.enabledIds, item.fullId)) {\n\t\t\t\tconst delta = matchesKey(data, Key.alt(\"up\")) ? -1 : 1;\n\t\t\t\tconst enabledList = this.enabledIds ?? this.allIds;\n\t\t\t\tconst currentIndex = enabledList.indexOf(item.fullId);\n\t\t\t\tconst newIndex = currentIndex + delta;\n\t\t\t\t// Only move if within bounds\n\t\t\t\tif (newIndex >= 0 && newIndex < enabledList.length) {\n\t\t\t\t\tthis.enabledIds = move(this.enabledIds, this.allIds, item.fullId, delta);\n\t\t\t\t\tthis.isDirty = true;\n\t\t\t\t\tthis.selectedIndex += delta;\n\t\t\t\t\tthis.refresh();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Toggle on Enter\n\t\tif (matchesKey(data, Key.enter)) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item) {\n\t\t\t\tconst wasAllEnabled = this.enabledIds === null;\n\t\t\t\tthis.enabledIds = toggle(this.enabledIds, item.fullId);\n\t\t\t\tthis.isDirty = true;\n\t\t\t\tif (wasAllEnabled) this.callbacks.onClearAll();\n\t\t\t\tthis.callbacks.onModelToggle(item.fullId, isEnabled(this.enabledIds, item.fullId));\n\t\t\t\tthis.refresh();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+A - Enable all (filtered if search active, otherwise all)\n\t\tif (matchesKey(data, Key.ctrl(\"a\"))) {\n\t\t\tconst targetIds = this.searchInput.getValue() ? this.filteredItems.map((i) => i.fullId) : undefined;\n\t\t\tthis.enabledIds = enableAll(this.enabledIds, this.allIds, targetIds);\n\t\t\tthis.isDirty = true;\n\t\t\tthis.callbacks.onEnableAll(targetIds ?? this.allIds);\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+X - Clear all (filtered if search active, otherwise all)\n\t\tif (matchesKey(data, Key.ctrl(\"x\"))) {\n\t\t\tconst targetIds = this.searchInput.getValue() ? this.filteredItems.map((i) => i.fullId) : undefined;\n\t\t\tthis.enabledIds = clearAll(this.enabledIds, this.allIds, targetIds);\n\t\t\tthis.isDirty = true;\n\t\t\tthis.callbacks.onClearAll();\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+P - Toggle provider of current item\n\t\tif (matchesKey(data, Key.ctrl(\"p\"))) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item) {\n\t\t\t\tconst provider = item.model.provider;\n\t\t\t\tconst providerIds = this.allIds.filter((id) => this.modelsById.get(id)!.provider === provider);\n\t\t\t\tconst allEnabled = providerIds.every((id) => isEnabled(this.enabledIds, id));\n\t\t\t\tthis.enabledIds = allEnabled\n\t\t\t\t\t? clearAll(this.enabledIds, this.allIds, providerIds)\n\t\t\t\t\t: enableAll(this.enabledIds, this.allIds, providerIds);\n\t\t\t\tthis.isDirty = true;\n\t\t\t\tthis.callbacks.onToggleProvider(provider, providerIds, !allEnabled);\n\t\t\t\tthis.refresh();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+S - Save/persist to settings\n\t\tif (matchesKey(data, Key.ctrl(\"s\"))) {\n\t\t\tthis.callbacks.onPersist(this.enabledIds ?? [...this.allIds]);\n\t\t\tthis.isDirty = false;\n\t\t\tthis.footerText.setText(this.getFooterText());\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+C - clear search or cancel if empty\n\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\tif (this.searchInput.getValue()) {\n\t\t\t\tthis.searchInput.setValue(\"\");\n\t\t\t\tthis.refresh();\n\t\t\t} else {\n\t\t\t\tthis.callbacks.onCancel();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Escape - cancel\n\t\tif (matchesKey(data, Key.escape)) {\n\t\t\tthis.callbacks.onCancel();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass everything else to search input\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.refresh();\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
@@ -104,7 +104,10 @@ export class ScopedModelsSelectorComponent extends Container {
104
104
  this.updateList();
105
105
  }
106
106
  buildItems() {
107
- return getSortedIds(this.enabledIds, this.allIds).map((id) => ({
107
+ // Filter out IDs that no longer have a corresponding model (e.g., after logout)
108
+ return getSortedIds(this.enabledIds, this.allIds)
109
+ .filter((id) => this.modelsById.has(id))
110
+ .map((id) => ({
108
111
  fullId: id,
109
112
  model: this.modelsById.get(id),
110
113
  enabled: isEnabled(this.enabledIds, id),
@@ -1 +1 @@
1
- {"version":3,"file":"scoped-models-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/scoped-models-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Model } from \"@mariozechner/pi-ai\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetEditorKeybindings,\n\tInput,\n\tKey,\n\tmatchesKey,\n\tSpacer,\n\tText,\n} from \"@mariozechner/pi-tui\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\n\n// EnabledIds: null = all enabled (no filter), string[] = explicit ordered list\ntype EnabledIds = string[] | null;\n\nfunction isEnabled(enabledIds: EnabledIds, id: string): boolean {\n\treturn enabledIds === null || enabledIds.includes(id);\n}\n\nfunction toggle(enabledIds: EnabledIds, id: string): EnabledIds {\n\tif (enabledIds === null) return [id]; // First toggle: start with only this one\n\tconst index = enabledIds.indexOf(id);\n\tif (index >= 0) return [...enabledIds.slice(0, index), ...enabledIds.slice(index + 1)];\n\treturn [...enabledIds, id];\n}\n\nfunction enableAll(enabledIds: EnabledIds, allIds: string[], targetIds?: string[]): EnabledIds {\n\tif (enabledIds === null) return null; // Already all enabled\n\tconst targets = targetIds ?? allIds;\n\tconst result = [...enabledIds];\n\tfor (const id of targets) {\n\t\tif (!result.includes(id)) result.push(id);\n\t}\n\treturn result.length === allIds.length ? null : result;\n}\n\nfunction clearAll(enabledIds: EnabledIds, allIds: string[], targetIds?: string[]): EnabledIds {\n\tif (enabledIds === null) {\n\t\treturn targetIds ? allIds.filter((id) => !targetIds.includes(id)) : [];\n\t}\n\tconst targets = new Set(targetIds ?? enabledIds);\n\treturn enabledIds.filter((id) => !targets.has(id));\n}\n\nfunction move(enabledIds: EnabledIds, allIds: string[], id: string, delta: number): EnabledIds {\n\tconst list = enabledIds ?? [...allIds];\n\tconst index = list.indexOf(id);\n\tif (index < 0) return list;\n\tconst newIndex = index + delta;\n\tif (newIndex < 0 || newIndex >= list.length) return list;\n\tconst result = [...list];\n\t[result[index], result[newIndex]] = [result[newIndex], result[index]];\n\treturn result;\n}\n\nfunction getSortedIds(enabledIds: EnabledIds, allIds: string[]): string[] {\n\tif (enabledIds === null) return allIds;\n\tconst enabledSet = new Set(enabledIds);\n\treturn [...enabledIds, ...allIds.filter((id) => !enabledSet.has(id))];\n}\n\ninterface ModelItem {\n\tfullId: string;\n\tmodel: Model<any>;\n\tenabled: boolean;\n}\n\nexport interface ModelsConfig {\n\tallModels: Model<any>[];\n\tenabledModelIds: Set<string>;\n\t/** true if enabledModels setting is defined (empty = all enabled) */\n\thasEnabledModelsFilter: boolean;\n}\n\nexport interface ModelsCallbacks {\n\t/** Called when a model is toggled (session-only, no persist) */\n\tonModelToggle: (modelId: string, enabled: boolean) => void;\n\t/** Called when user wants to persist current selection to settings */\n\tonPersist: (enabledModelIds: string[]) => void;\n\t/** Called when user enables all models. Returns list of all model IDs. */\n\tonEnableAll: (allModelIds: string[]) => void;\n\t/** Called when user clears all models */\n\tonClearAll: () => void;\n\t/** Called when user toggles all models for a provider. Returns affected model IDs. */\n\tonToggleProvider: (provider: string, modelIds: string[], enabled: boolean) => void;\n\tonCancel: () => void;\n}\n\n/**\n * Component for enabling/disabling models for Ctrl+P cycling.\n * Changes are session-only until explicitly persisted with Ctrl+S.\n */\nexport class ScopedModelsSelectorComponent extends Container implements Focusable {\n\tprivate modelsById: Map<string, Model<any>> = new Map();\n\tprivate allIds: string[] = [];\n\tprivate enabledIds: EnabledIds = null;\n\tprivate filteredItems: ModelItem[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\tprivate listContainer: Container;\n\tprivate footerText: Text;\n\tprivate callbacks: ModelsCallbacks;\n\tprivate maxVisible = 15;\n\tprivate isDirty = false;\n\n\tconstructor(config: ModelsConfig, callbacks: ModelsCallbacks) {\n\t\tsuper();\n\t\tthis.callbacks = callbacks;\n\n\t\tfor (const model of config.allModels) {\n\t\t\tconst fullId = `${model.provider}/${model.id}`;\n\t\t\tthis.modelsById.set(fullId, model);\n\t\t\tthis.allIds.push(fullId);\n\t\t}\n\n\t\tthis.enabledIds = config.hasEnabledModelsFilter ? [...config.enabledModelIds] : null;\n\t\tthis.filteredItems = this.buildItems();\n\n\t\t// Header\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(theme.fg(\"accent\", theme.bold(\"Model Configuration\")), 0, 0));\n\t\tthis.addChild(new Text(theme.fg(\"muted\", \"Session-only. Ctrl+S to save to settings.\"), 0, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Search input\n\t\tthis.searchInput = new Input();\n\t\tthis.addChild(this.searchInput);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// List container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\t// Footer hint\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.footerText = new Text(this.getFooterText(), 0, 0);\n\t\tthis.addChild(this.footerText);\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.updateList();\n\t}\n\n\tprivate buildItems(): ModelItem[] {\n\t\treturn getSortedIds(this.enabledIds, this.allIds).map((id) => ({\n\t\t\tfullId: id,\n\t\t\tmodel: this.modelsById.get(id)!,\n\t\t\tenabled: isEnabled(this.enabledIds, id),\n\t\t}));\n\t}\n\n\tprivate getFooterText(): string {\n\t\tconst enabledCount = this.enabledIds?.length ?? this.allIds.length;\n\t\tconst allEnabled = this.enabledIds === null;\n\t\tconst countText = allEnabled ? \"all enabled\" : `${enabledCount}/${this.allIds.length} enabled`;\n\t\tconst parts = [\"Enter toggle\", \"^A all\", \"^X clear\", \"^P provider\", \"Alt+↑↓ reorder\", \"^S save\", countText];\n\t\treturn this.isDirty\n\t\t\t? theme.fg(\"dim\", ` ${parts.join(\" · \")} `) + theme.fg(\"warning\", \"(unsaved)\")\n\t\t\t: theme.fg(\"dim\", ` ${parts.join(\" · \")}`);\n\t}\n\n\tprivate refresh(): void {\n\t\tconst query = this.searchInput.getValue();\n\t\tconst items = this.buildItems();\n\t\tthis.filteredItems = query ? fuzzyFilter(items, query, (i) => `${i.model.id} ${i.model.provider}`) : items;\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredItems.length - 1));\n\t\tthis.updateList();\n\t\tthis.footerText.setText(this.getFooterText());\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tif (this.filteredItems.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching models\"), 0, 0));\n\t\t\treturn;\n\t\t}\n\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);\n\t\tconst allEnabled = this.enabledIds === null;\n\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredItems[i]!;\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst prefix = isSelected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tconst modelText = isSelected ? theme.fg(\"accent\", item.model.id) : item.model.id;\n\t\t\tconst providerBadge = theme.fg(\"muted\", ` [${item.model.provider}]`);\n\t\t\tconst status = allEnabled ? \"\" : item.enabled ? theme.fg(\"success\", \" ✓\") : theme.fg(\"dim\", \" ✗\");\n\t\t\tthis.listContainer.addChild(new Text(`${prefix}${modelText}${providerBadge}${status}`, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredItems.length) {\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`), 0, 0),\n\t\t\t);\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getEditorKeybindings();\n\n\t\t// Navigation\n\t\tif (kb.matches(data, \"selectUp\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredItems.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"selectDown\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredItems.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\n\t\t// Alt+Up/Down - Reorder enabled models\n\t\tif (matchesKey(data, Key.alt(\"up\")) || matchesKey(data, Key.alt(\"down\"))) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item && isEnabled(this.enabledIds, item.fullId)) {\n\t\t\t\tconst delta = matchesKey(data, Key.alt(\"up\")) ? -1 : 1;\n\t\t\t\tconst enabledList = this.enabledIds ?? this.allIds;\n\t\t\t\tconst currentIndex = enabledList.indexOf(item.fullId);\n\t\t\t\tconst newIndex = currentIndex + delta;\n\t\t\t\t// Only move if within bounds\n\t\t\t\tif (newIndex >= 0 && newIndex < enabledList.length) {\n\t\t\t\t\tthis.enabledIds = move(this.enabledIds, this.allIds, item.fullId, delta);\n\t\t\t\t\tthis.isDirty = true;\n\t\t\t\t\tthis.selectedIndex += delta;\n\t\t\t\t\tthis.refresh();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Toggle on Enter\n\t\tif (matchesKey(data, Key.enter)) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item) {\n\t\t\t\tconst wasAllEnabled = this.enabledIds === null;\n\t\t\t\tthis.enabledIds = toggle(this.enabledIds, item.fullId);\n\t\t\t\tthis.isDirty = true;\n\t\t\t\tif (wasAllEnabled) this.callbacks.onClearAll();\n\t\t\t\tthis.callbacks.onModelToggle(item.fullId, isEnabled(this.enabledIds, item.fullId));\n\t\t\t\tthis.refresh();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+A - Enable all (filtered if search active, otherwise all)\n\t\tif (matchesKey(data, Key.ctrl(\"a\"))) {\n\t\t\tconst targetIds = this.searchInput.getValue() ? this.filteredItems.map((i) => i.fullId) : undefined;\n\t\t\tthis.enabledIds = enableAll(this.enabledIds, this.allIds, targetIds);\n\t\t\tthis.isDirty = true;\n\t\t\tthis.callbacks.onEnableAll(targetIds ?? this.allIds);\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+X - Clear all (filtered if search active, otherwise all)\n\t\tif (matchesKey(data, Key.ctrl(\"x\"))) {\n\t\t\tconst targetIds = this.searchInput.getValue() ? this.filteredItems.map((i) => i.fullId) : undefined;\n\t\t\tthis.enabledIds = clearAll(this.enabledIds, this.allIds, targetIds);\n\t\t\tthis.isDirty = true;\n\t\t\tthis.callbacks.onClearAll();\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+P - Toggle provider of current item\n\t\tif (matchesKey(data, Key.ctrl(\"p\"))) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item) {\n\t\t\t\tconst provider = item.model.provider;\n\t\t\t\tconst providerIds = this.allIds.filter((id) => this.modelsById.get(id)!.provider === provider);\n\t\t\t\tconst allEnabled = providerIds.every((id) => isEnabled(this.enabledIds, id));\n\t\t\t\tthis.enabledIds = allEnabled\n\t\t\t\t\t? clearAll(this.enabledIds, this.allIds, providerIds)\n\t\t\t\t\t: enableAll(this.enabledIds, this.allIds, providerIds);\n\t\t\t\tthis.isDirty = true;\n\t\t\t\tthis.callbacks.onToggleProvider(provider, providerIds, !allEnabled);\n\t\t\t\tthis.refresh();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+S - Save/persist to settings\n\t\tif (matchesKey(data, Key.ctrl(\"s\"))) {\n\t\t\tthis.callbacks.onPersist(this.enabledIds ?? [...this.allIds]);\n\t\t\tthis.isDirty = false;\n\t\t\tthis.footerText.setText(this.getFooterText());\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+C - clear search or cancel if empty\n\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\tif (this.searchInput.getValue()) {\n\t\t\t\tthis.searchInput.setValue(\"\");\n\t\t\t\tthis.refresh();\n\t\t\t} else {\n\t\t\t\tthis.callbacks.onCancel();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Escape - cancel\n\t\tif (matchesKey(data, Key.escape)) {\n\t\t\tthis.callbacks.onCancel();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass everything else to search input\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.refresh();\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
1
+ {"version":3,"file":"scoped-models-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/scoped-models-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Model } from \"@mariozechner/pi-ai\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetEditorKeybindings,\n\tInput,\n\tKey,\n\tmatchesKey,\n\tSpacer,\n\tText,\n} from \"@mariozechner/pi-tui\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\n\n// EnabledIds: null = all enabled (no filter), string[] = explicit ordered list\ntype EnabledIds = string[] | null;\n\nfunction isEnabled(enabledIds: EnabledIds, id: string): boolean {\n\treturn enabledIds === null || enabledIds.includes(id);\n}\n\nfunction toggle(enabledIds: EnabledIds, id: string): EnabledIds {\n\tif (enabledIds === null) return [id]; // First toggle: start with only this one\n\tconst index = enabledIds.indexOf(id);\n\tif (index >= 0) return [...enabledIds.slice(0, index), ...enabledIds.slice(index + 1)];\n\treturn [...enabledIds, id];\n}\n\nfunction enableAll(enabledIds: EnabledIds, allIds: string[], targetIds?: string[]): EnabledIds {\n\tif (enabledIds === null) return null; // Already all enabled\n\tconst targets = targetIds ?? allIds;\n\tconst result = [...enabledIds];\n\tfor (const id of targets) {\n\t\tif (!result.includes(id)) result.push(id);\n\t}\n\treturn result.length === allIds.length ? null : result;\n}\n\nfunction clearAll(enabledIds: EnabledIds, allIds: string[], targetIds?: string[]): EnabledIds {\n\tif (enabledIds === null) {\n\t\treturn targetIds ? allIds.filter((id) => !targetIds.includes(id)) : [];\n\t}\n\tconst targets = new Set(targetIds ?? enabledIds);\n\treturn enabledIds.filter((id) => !targets.has(id));\n}\n\nfunction move(enabledIds: EnabledIds, allIds: string[], id: string, delta: number): EnabledIds {\n\tconst list = enabledIds ?? [...allIds];\n\tconst index = list.indexOf(id);\n\tif (index < 0) return list;\n\tconst newIndex = index + delta;\n\tif (newIndex < 0 || newIndex >= list.length) return list;\n\tconst result = [...list];\n\t[result[index], result[newIndex]] = [result[newIndex], result[index]];\n\treturn result;\n}\n\nfunction getSortedIds(enabledIds: EnabledIds, allIds: string[]): string[] {\n\tif (enabledIds === null) return allIds;\n\tconst enabledSet = new Set(enabledIds);\n\treturn [...enabledIds, ...allIds.filter((id) => !enabledSet.has(id))];\n}\n\ninterface ModelItem {\n\tfullId: string;\n\tmodel: Model<any>;\n\tenabled: boolean;\n}\n\nexport interface ModelsConfig {\n\tallModels: Model<any>[];\n\tenabledModelIds: Set<string>;\n\t/** true if enabledModels setting is defined (empty = all enabled) */\n\thasEnabledModelsFilter: boolean;\n}\n\nexport interface ModelsCallbacks {\n\t/** Called when a model is toggled (session-only, no persist) */\n\tonModelToggle: (modelId: string, enabled: boolean) => void;\n\t/** Called when user wants to persist current selection to settings */\n\tonPersist: (enabledModelIds: string[]) => void;\n\t/** Called when user enables all models. Returns list of all model IDs. */\n\tonEnableAll: (allModelIds: string[]) => void;\n\t/** Called when user clears all models */\n\tonClearAll: () => void;\n\t/** Called when user toggles all models for a provider. Returns affected model IDs. */\n\tonToggleProvider: (provider: string, modelIds: string[], enabled: boolean) => void;\n\tonCancel: () => void;\n}\n\n/**\n * Component for enabling/disabling models for Ctrl+P cycling.\n * Changes are session-only until explicitly persisted with Ctrl+S.\n */\nexport class ScopedModelsSelectorComponent extends Container implements Focusable {\n\tprivate modelsById: Map<string, Model<any>> = new Map();\n\tprivate allIds: string[] = [];\n\tprivate enabledIds: EnabledIds = null;\n\tprivate filteredItems: ModelItem[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\tprivate listContainer: Container;\n\tprivate footerText: Text;\n\tprivate callbacks: ModelsCallbacks;\n\tprivate maxVisible = 15;\n\tprivate isDirty = false;\n\n\tconstructor(config: ModelsConfig, callbacks: ModelsCallbacks) {\n\t\tsuper();\n\t\tthis.callbacks = callbacks;\n\n\t\tfor (const model of config.allModels) {\n\t\t\tconst fullId = `${model.provider}/${model.id}`;\n\t\t\tthis.modelsById.set(fullId, model);\n\t\t\tthis.allIds.push(fullId);\n\t\t}\n\n\t\tthis.enabledIds = config.hasEnabledModelsFilter ? [...config.enabledModelIds] : null;\n\t\tthis.filteredItems = this.buildItems();\n\n\t\t// Header\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(theme.fg(\"accent\", theme.bold(\"Model Configuration\")), 0, 0));\n\t\tthis.addChild(new Text(theme.fg(\"muted\", \"Session-only. Ctrl+S to save to settings.\"), 0, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Search input\n\t\tthis.searchInput = new Input();\n\t\tthis.addChild(this.searchInput);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// List container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\t// Footer hint\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.footerText = new Text(this.getFooterText(), 0, 0);\n\t\tthis.addChild(this.footerText);\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.updateList();\n\t}\n\n\tprivate buildItems(): ModelItem[] {\n\t\t// Filter out IDs that no longer have a corresponding model (e.g., after logout)\n\t\treturn getSortedIds(this.enabledIds, this.allIds)\n\t\t\t.filter((id) => this.modelsById.has(id))\n\t\t\t.map((id) => ({\n\t\t\t\tfullId: id,\n\t\t\t\tmodel: this.modelsById.get(id)!,\n\t\t\t\tenabled: isEnabled(this.enabledIds, id),\n\t\t\t}));\n\t}\n\n\tprivate getFooterText(): string {\n\t\tconst enabledCount = this.enabledIds?.length ?? this.allIds.length;\n\t\tconst allEnabled = this.enabledIds === null;\n\t\tconst countText = allEnabled ? \"all enabled\" : `${enabledCount}/${this.allIds.length} enabled`;\n\t\tconst parts = [\"Enter toggle\", \"^A all\", \"^X clear\", \"^P provider\", \"Alt+↑↓ reorder\", \"^S save\", countText];\n\t\treturn this.isDirty\n\t\t\t? theme.fg(\"dim\", ` ${parts.join(\" · \")} `) + theme.fg(\"warning\", \"(unsaved)\")\n\t\t\t: theme.fg(\"dim\", ` ${parts.join(\" · \")}`);\n\t}\n\n\tprivate refresh(): void {\n\t\tconst query = this.searchInput.getValue();\n\t\tconst items = this.buildItems();\n\t\tthis.filteredItems = query ? fuzzyFilter(items, query, (i) => `${i.model.id} ${i.model.provider}`) : items;\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredItems.length - 1));\n\t\tthis.updateList();\n\t\tthis.footerText.setText(this.getFooterText());\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tif (this.filteredItems.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching models\"), 0, 0));\n\t\t\treturn;\n\t\t}\n\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);\n\t\tconst allEnabled = this.enabledIds === null;\n\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredItems[i]!;\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst prefix = isSelected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tconst modelText = isSelected ? theme.fg(\"accent\", item.model.id) : item.model.id;\n\t\t\tconst providerBadge = theme.fg(\"muted\", ` [${item.model.provider}]`);\n\t\t\tconst status = allEnabled ? \"\" : item.enabled ? theme.fg(\"success\", \" ✓\") : theme.fg(\"dim\", \" ✗\");\n\t\t\tthis.listContainer.addChild(new Text(`${prefix}${modelText}${providerBadge}${status}`, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredItems.length) {\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`), 0, 0),\n\t\t\t);\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getEditorKeybindings();\n\n\t\t// Navigation\n\t\tif (kb.matches(data, \"selectUp\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredItems.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"selectDown\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredItems.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\n\t\t// Alt+Up/Down - Reorder enabled models\n\t\tif (matchesKey(data, Key.alt(\"up\")) || matchesKey(data, Key.alt(\"down\"))) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item && isEnabled(this.enabledIds, item.fullId)) {\n\t\t\t\tconst delta = matchesKey(data, Key.alt(\"up\")) ? -1 : 1;\n\t\t\t\tconst enabledList = this.enabledIds ?? this.allIds;\n\t\t\t\tconst currentIndex = enabledList.indexOf(item.fullId);\n\t\t\t\tconst newIndex = currentIndex + delta;\n\t\t\t\t// Only move if within bounds\n\t\t\t\tif (newIndex >= 0 && newIndex < enabledList.length) {\n\t\t\t\t\tthis.enabledIds = move(this.enabledIds, this.allIds, item.fullId, delta);\n\t\t\t\t\tthis.isDirty = true;\n\t\t\t\t\tthis.selectedIndex += delta;\n\t\t\t\t\tthis.refresh();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Toggle on Enter\n\t\tif (matchesKey(data, Key.enter)) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item) {\n\t\t\t\tconst wasAllEnabled = this.enabledIds === null;\n\t\t\t\tthis.enabledIds = toggle(this.enabledIds, item.fullId);\n\t\t\t\tthis.isDirty = true;\n\t\t\t\tif (wasAllEnabled) this.callbacks.onClearAll();\n\t\t\t\tthis.callbacks.onModelToggle(item.fullId, isEnabled(this.enabledIds, item.fullId));\n\t\t\t\tthis.refresh();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+A - Enable all (filtered if search active, otherwise all)\n\t\tif (matchesKey(data, Key.ctrl(\"a\"))) {\n\t\t\tconst targetIds = this.searchInput.getValue() ? this.filteredItems.map((i) => i.fullId) : undefined;\n\t\t\tthis.enabledIds = enableAll(this.enabledIds, this.allIds, targetIds);\n\t\t\tthis.isDirty = true;\n\t\t\tthis.callbacks.onEnableAll(targetIds ?? this.allIds);\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+X - Clear all (filtered if search active, otherwise all)\n\t\tif (matchesKey(data, Key.ctrl(\"x\"))) {\n\t\t\tconst targetIds = this.searchInput.getValue() ? this.filteredItems.map((i) => i.fullId) : undefined;\n\t\t\tthis.enabledIds = clearAll(this.enabledIds, this.allIds, targetIds);\n\t\t\tthis.isDirty = true;\n\t\t\tthis.callbacks.onClearAll();\n\t\t\tthis.refresh();\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+P - Toggle provider of current item\n\t\tif (matchesKey(data, Key.ctrl(\"p\"))) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item) {\n\t\t\t\tconst provider = item.model.provider;\n\t\t\t\tconst providerIds = this.allIds.filter((id) => this.modelsById.get(id)!.provider === provider);\n\t\t\t\tconst allEnabled = providerIds.every((id) => isEnabled(this.enabledIds, id));\n\t\t\t\tthis.enabledIds = allEnabled\n\t\t\t\t\t? clearAll(this.enabledIds, this.allIds, providerIds)\n\t\t\t\t\t: enableAll(this.enabledIds, this.allIds, providerIds);\n\t\t\t\tthis.isDirty = true;\n\t\t\t\tthis.callbacks.onToggleProvider(provider, providerIds, !allEnabled);\n\t\t\t\tthis.refresh();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+S - Save/persist to settings\n\t\tif (matchesKey(data, Key.ctrl(\"s\"))) {\n\t\t\tthis.callbacks.onPersist(this.enabledIds ?? [...this.allIds]);\n\t\t\tthis.isDirty = false;\n\t\t\tthis.footerText.setText(this.getFooterText());\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+C - clear search or cancel if empty\n\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\tif (this.searchInput.getValue()) {\n\t\t\t\tthis.searchInput.setValue(\"\");\n\t\t\t\tthis.refresh();\n\t\t\t} else {\n\t\t\t\tthis.callbacks.onCancel();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Escape - cancel\n\t\tif (matchesKey(data, Key.escape)) {\n\t\t\tthis.callbacks.onCancel();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass everything else to search input\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.refresh();\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import type { SessionInfo } from "../../../core/session-manager.js";
2
- export type SortMode = "recent" | "relevance";
2
+ export type SortMode = "threaded" | "recent" | "relevance";
3
+ export type NameFilter = "all" | "named";
3
4
  export interface ParsedSearchQuery {
4
5
  mode: "tokens" | "regex";
5
6
  tokens: {
@@ -15,7 +16,8 @@ export interface MatchResult {
15
16
  /** Lower is better; only meaningful when matches === true */
16
17
  score: number;
17
18
  }
19
+ export declare function hasSessionName(session: SessionInfo): boolean;
18
20
  export declare function parseSearchQuery(query: string): ParsedSearchQuery;
19
21
  export declare function matchSession(session: SessionInfo, parsed: ParsedSearchQuery): MatchResult;
20
- export declare function filterAndSortSessions(sessions: SessionInfo[], query: string, sortMode: SortMode): SessionInfo[];
22
+ export declare function filterAndSortSessions(sessions: SessionInfo[], query: string, sortMode: SortMode, nameFilter?: NameFilter): SessionInfo[];
21
23
  //# sourceMappingURL=session-selector-search.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-selector-search.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/session-selector-search.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAEpE,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE9C,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IACzB,MAAM,EAAE;QAAE,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACtD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;CACd;AAUD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,CA2EjE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,iBAAiB,GAAG,WAAW,CAsCzF;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,WAAW,EAAE,CA+B/G","sourcesContent":["import { fuzzyMatch } from \"@mariozechner/pi-tui\";\nimport type { SessionInfo } from \"../../../core/session-manager.js\";\n\nexport type SortMode = \"recent\" | \"relevance\";\n\nexport interface ParsedSearchQuery {\n\tmode: \"tokens\" | \"regex\";\n\ttokens: { kind: \"fuzzy\" | \"phrase\"; value: string }[];\n\tregex: RegExp | null;\n\t/** If set, parsing failed and we should treat query as non-matching. */\n\terror?: string;\n}\n\nexport interface MatchResult {\n\tmatches: boolean;\n\t/** Lower is better; only meaningful when matches === true */\n\tscore: number;\n}\n\nfunction normalizeWhitespaceLower(text: string): string {\n\treturn text.toLowerCase().replace(/\\s+/g, \" \").trim();\n}\n\nfunction getSessionSearchText(session: SessionInfo): string {\n\treturn `${session.id} ${session.name ?? \"\"} ${session.allMessagesText} ${session.cwd}`;\n}\n\nexport function parseSearchQuery(query: string): ParsedSearchQuery {\n\tconst trimmed = query.trim();\n\tif (!trimmed) {\n\t\treturn { mode: \"tokens\", tokens: [], regex: null };\n\t}\n\n\t// Regex mode: re:<pattern>\n\tif (trimmed.startsWith(\"re:\")) {\n\t\tconst pattern = trimmed.slice(3).trim();\n\t\tif (!pattern) {\n\t\t\treturn { mode: \"regex\", tokens: [], regex: null, error: \"Empty regex\" };\n\t\t}\n\t\ttry {\n\t\t\treturn { mode: \"regex\", tokens: [], regex: new RegExp(pattern, \"i\") };\n\t\t} catch (err) {\n\t\t\tconst msg = err instanceof Error ? err.message : String(err);\n\t\t\treturn { mode: \"regex\", tokens: [], regex: null, error: msg };\n\t\t}\n\t}\n\n\t// Token mode with quote support.\n\t// Example: foo \"node cve\" bar\n\tconst tokens: { kind: \"fuzzy\" | \"phrase\"; value: string }[] = [];\n\tlet buf = \"\";\n\tlet inQuote = false;\n\tlet hadUnclosedQuote = false;\n\n\tconst flush = (kind: \"fuzzy\" | \"phrase\"): void => {\n\t\tconst v = buf.trim();\n\t\tbuf = \"\";\n\t\tif (!v) return;\n\t\ttokens.push({ kind, value: v });\n\t};\n\n\tfor (let i = 0; i < trimmed.length; i++) {\n\t\tconst ch = trimmed[i]!;\n\t\tif (ch === '\"') {\n\t\t\tif (inQuote) {\n\t\t\t\tflush(\"phrase\");\n\t\t\t\tinQuote = false;\n\t\t\t} else {\n\t\t\t\tflush(\"fuzzy\");\n\t\t\t\tinQuote = true;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!inQuote && /\\s/.test(ch)) {\n\t\t\tflush(\"fuzzy\");\n\t\t\tcontinue;\n\t\t}\n\n\t\tbuf += ch;\n\t}\n\n\tif (inQuote) {\n\t\thadUnclosedQuote = true;\n\t}\n\n\t// If quotes were unbalanced, fall back to plain whitespace tokenization.\n\tif (hadUnclosedQuote) {\n\t\treturn {\n\t\t\tmode: \"tokens\",\n\t\t\ttokens: trimmed\n\t\t\t\t.split(/\\s+/)\n\t\t\t\t.map((t) => t.trim())\n\t\t\t\t.filter((t) => t.length > 0)\n\t\t\t\t.map((t) => ({ kind: \"fuzzy\" as const, value: t })),\n\t\t\tregex: null,\n\t\t};\n\t}\n\n\tflush(inQuote ? \"phrase\" : \"fuzzy\");\n\n\treturn { mode: \"tokens\", tokens, regex: null };\n}\n\nexport function matchSession(session: SessionInfo, parsed: ParsedSearchQuery): MatchResult {\n\tconst text = getSessionSearchText(session);\n\n\tif (parsed.mode === \"regex\") {\n\t\tif (!parsed.regex) {\n\t\t\treturn { matches: false, score: 0 };\n\t\t}\n\t\tconst idx = text.search(parsed.regex);\n\t\tif (idx < 0) return { matches: false, score: 0 };\n\t\treturn { matches: true, score: idx * 0.1 };\n\t}\n\n\tif (parsed.tokens.length === 0) {\n\t\treturn { matches: true, score: 0 };\n\t}\n\n\tlet totalScore = 0;\n\tlet normalizedText: string | null = null;\n\n\tfor (const token of parsed.tokens) {\n\t\tif (token.kind === \"phrase\") {\n\t\t\tif (normalizedText === null) {\n\t\t\t\tnormalizedText = normalizeWhitespaceLower(text);\n\t\t\t}\n\t\t\tconst phrase = normalizeWhitespaceLower(token.value);\n\t\t\tif (!phrase) continue;\n\t\t\tconst idx = normalizedText.indexOf(phrase);\n\t\t\tif (idx < 0) return { matches: false, score: 0 };\n\t\t\ttotalScore += idx * 0.1;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst m = fuzzyMatch(token.value, text);\n\t\tif (!m.matches) return { matches: false, score: 0 };\n\t\ttotalScore += m.score;\n\t}\n\n\treturn { matches: true, score: totalScore };\n}\n\nexport function filterAndSortSessions(sessions: SessionInfo[], query: string, sortMode: SortMode): SessionInfo[] {\n\tconst trimmed = query.trim();\n\tif (!trimmed) return sessions;\n\n\tconst parsed = parseSearchQuery(query);\n\tif (parsed.error) return [];\n\n\t// Recent mode: filter only, keep incoming order.\n\tif (sortMode === \"recent\") {\n\t\tconst filtered: SessionInfo[] = [];\n\t\tfor (const s of sessions) {\n\t\t\tconst res = matchSession(s, parsed);\n\t\t\tif (res.matches) filtered.push(s);\n\t\t}\n\t\treturn filtered;\n\t}\n\n\t// Relevance mode: sort by score, tie-break by modified desc.\n\tconst scored: { session: SessionInfo; score: number }[] = [];\n\tfor (const s of sessions) {\n\t\tconst res = matchSession(s, parsed);\n\t\tif (!res.matches) continue;\n\t\tscored.push({ session: s, score: res.score });\n\t}\n\n\tscored.sort((a, b) => {\n\t\tif (a.score !== b.score) return a.score - b.score;\n\t\treturn b.session.modified.getTime() - a.session.modified.getTime();\n\t});\n\n\treturn scored.map((r) => r.session);\n}\n"]}
1
+ {"version":3,"file":"session-selector-search.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/session-selector-search.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAEpE,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE3D,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,OAAO,CAAC;AAEzC,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IACzB,MAAM,EAAE;QAAE,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACtD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;CACd;AAUD,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAE5D;AAOD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,CA2EjE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,iBAAiB,GAAG,WAAW,CAsCzF;AAED,wBAAgB,qBAAqB,CACpC,QAAQ,EAAE,WAAW,EAAE,EACvB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,QAAQ,EAClB,UAAU,GAAE,UAAkB,GAC5B,WAAW,EAAE,CAiCf","sourcesContent":["import { fuzzyMatch } from \"@mariozechner/pi-tui\";\nimport type { SessionInfo } from \"../../../core/session-manager.js\";\n\nexport type SortMode = \"threaded\" | \"recent\" | \"relevance\";\n\nexport type NameFilter = \"all\" | \"named\";\n\nexport interface ParsedSearchQuery {\n\tmode: \"tokens\" | \"regex\";\n\ttokens: { kind: \"fuzzy\" | \"phrase\"; value: string }[];\n\tregex: RegExp | null;\n\t/** If set, parsing failed and we should treat query as non-matching. */\n\terror?: string;\n}\n\nexport interface MatchResult {\n\tmatches: boolean;\n\t/** Lower is better; only meaningful when matches === true */\n\tscore: number;\n}\n\nfunction normalizeWhitespaceLower(text: string): string {\n\treturn text.toLowerCase().replace(/\\s+/g, \" \").trim();\n}\n\nfunction getSessionSearchText(session: SessionInfo): string {\n\treturn `${session.id} ${session.name ?? \"\"} ${session.allMessagesText} ${session.cwd}`;\n}\n\nexport function hasSessionName(session: SessionInfo): boolean {\n\treturn Boolean(session.name?.trim());\n}\n\nfunction matchesNameFilter(session: SessionInfo, filter: NameFilter): boolean {\n\tif (filter === \"all\") return true;\n\treturn hasSessionName(session);\n}\n\nexport function parseSearchQuery(query: string): ParsedSearchQuery {\n\tconst trimmed = query.trim();\n\tif (!trimmed) {\n\t\treturn { mode: \"tokens\", tokens: [], regex: null };\n\t}\n\n\t// Regex mode: re:<pattern>\n\tif (trimmed.startsWith(\"re:\")) {\n\t\tconst pattern = trimmed.slice(3).trim();\n\t\tif (!pattern) {\n\t\t\treturn { mode: \"regex\", tokens: [], regex: null, error: \"Empty regex\" };\n\t\t}\n\t\ttry {\n\t\t\treturn { mode: \"regex\", tokens: [], regex: new RegExp(pattern, \"i\") };\n\t\t} catch (err) {\n\t\t\tconst msg = err instanceof Error ? err.message : String(err);\n\t\t\treturn { mode: \"regex\", tokens: [], regex: null, error: msg };\n\t\t}\n\t}\n\n\t// Token mode with quote support.\n\t// Example: foo \"node cve\" bar\n\tconst tokens: { kind: \"fuzzy\" | \"phrase\"; value: string }[] = [];\n\tlet buf = \"\";\n\tlet inQuote = false;\n\tlet hadUnclosedQuote = false;\n\n\tconst flush = (kind: \"fuzzy\" | \"phrase\"): void => {\n\t\tconst v = buf.trim();\n\t\tbuf = \"\";\n\t\tif (!v) return;\n\t\ttokens.push({ kind, value: v });\n\t};\n\n\tfor (let i = 0; i < trimmed.length; i++) {\n\t\tconst ch = trimmed[i]!;\n\t\tif (ch === '\"') {\n\t\t\tif (inQuote) {\n\t\t\t\tflush(\"phrase\");\n\t\t\t\tinQuote = false;\n\t\t\t} else {\n\t\t\t\tflush(\"fuzzy\");\n\t\t\t\tinQuote = true;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!inQuote && /\\s/.test(ch)) {\n\t\t\tflush(\"fuzzy\");\n\t\t\tcontinue;\n\t\t}\n\n\t\tbuf += ch;\n\t}\n\n\tif (inQuote) {\n\t\thadUnclosedQuote = true;\n\t}\n\n\t// If quotes were unbalanced, fall back to plain whitespace tokenization.\n\tif (hadUnclosedQuote) {\n\t\treturn {\n\t\t\tmode: \"tokens\",\n\t\t\ttokens: trimmed\n\t\t\t\t.split(/\\s+/)\n\t\t\t\t.map((t) => t.trim())\n\t\t\t\t.filter((t) => t.length > 0)\n\t\t\t\t.map((t) => ({ kind: \"fuzzy\" as const, value: t })),\n\t\t\tregex: null,\n\t\t};\n\t}\n\n\tflush(inQuote ? \"phrase\" : \"fuzzy\");\n\n\treturn { mode: \"tokens\", tokens, regex: null };\n}\n\nexport function matchSession(session: SessionInfo, parsed: ParsedSearchQuery): MatchResult {\n\tconst text = getSessionSearchText(session);\n\n\tif (parsed.mode === \"regex\") {\n\t\tif (!parsed.regex) {\n\t\t\treturn { matches: false, score: 0 };\n\t\t}\n\t\tconst idx = text.search(parsed.regex);\n\t\tif (idx < 0) return { matches: false, score: 0 };\n\t\treturn { matches: true, score: idx * 0.1 };\n\t}\n\n\tif (parsed.tokens.length === 0) {\n\t\treturn { matches: true, score: 0 };\n\t}\n\n\tlet totalScore = 0;\n\tlet normalizedText: string | null = null;\n\n\tfor (const token of parsed.tokens) {\n\t\tif (token.kind === \"phrase\") {\n\t\t\tif (normalizedText === null) {\n\t\t\t\tnormalizedText = normalizeWhitespaceLower(text);\n\t\t\t}\n\t\t\tconst phrase = normalizeWhitespaceLower(token.value);\n\t\t\tif (!phrase) continue;\n\t\t\tconst idx = normalizedText.indexOf(phrase);\n\t\t\tif (idx < 0) return { matches: false, score: 0 };\n\t\t\ttotalScore += idx * 0.1;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst m = fuzzyMatch(token.value, text);\n\t\tif (!m.matches) return { matches: false, score: 0 };\n\t\ttotalScore += m.score;\n\t}\n\n\treturn { matches: true, score: totalScore };\n}\n\nexport function filterAndSortSessions(\n\tsessions: SessionInfo[],\n\tquery: string,\n\tsortMode: SortMode,\n\tnameFilter: NameFilter = \"all\",\n): SessionInfo[] {\n\tconst nameFiltered =\n\t\tnameFilter === \"all\" ? sessions : sessions.filter((session) => matchesNameFilter(session, nameFilter));\n\tconst trimmed = query.trim();\n\tif (!trimmed) return nameFiltered;\n\n\tconst parsed = parseSearchQuery(query);\n\tif (parsed.error) return [];\n\n\t// Recent mode: filter only, keep incoming order.\n\tif (sortMode === \"recent\") {\n\t\tconst filtered: SessionInfo[] = [];\n\t\tfor (const s of nameFiltered) {\n\t\t\tconst res = matchSession(s, parsed);\n\t\t\tif (res.matches) filtered.push(s);\n\t\t}\n\t\treturn filtered;\n\t}\n\n\t// Relevance mode: sort by score, tie-break by modified desc.\n\tconst scored: { session: SessionInfo; score: number }[] = [];\n\tfor (const s of nameFiltered) {\n\t\tconst res = matchSession(s, parsed);\n\t\tif (!res.matches) continue;\n\t\tscored.push({ session: s, score: res.score });\n\t}\n\n\tscored.sort((a, b) => {\n\t\tif (a.score !== b.score) return a.score - b.score;\n\t\treturn b.session.modified.getTime() - a.session.modified.getTime();\n\t});\n\n\treturn scored.map((r) => r.session);\n}\n"]}
@@ -5,6 +5,14 @@ function normalizeWhitespaceLower(text) {
5
5
  function getSessionSearchText(session) {
6
6
  return `${session.id} ${session.name ?? ""} ${session.allMessagesText} ${session.cwd}`;
7
7
  }
8
+ export function hasSessionName(session) {
9
+ return Boolean(session.name?.trim());
10
+ }
11
+ function matchesNameFilter(session, filter) {
12
+ if (filter === "all")
13
+ return true;
14
+ return hasSessionName(session);
15
+ }
8
16
  export function parseSearchQuery(query) {
9
17
  const trimmed = query.trim();
10
18
  if (!trimmed) {
@@ -111,17 +119,18 @@ export function matchSession(session, parsed) {
111
119
  }
112
120
  return { matches: true, score: totalScore };
113
121
  }
114
- export function filterAndSortSessions(sessions, query, sortMode) {
122
+ export function filterAndSortSessions(sessions, query, sortMode, nameFilter = "all") {
123
+ const nameFiltered = nameFilter === "all" ? sessions : sessions.filter((session) => matchesNameFilter(session, nameFilter));
115
124
  const trimmed = query.trim();
116
125
  if (!trimmed)
117
- return sessions;
126
+ return nameFiltered;
118
127
  const parsed = parseSearchQuery(query);
119
128
  if (parsed.error)
120
129
  return [];
121
130
  // Recent mode: filter only, keep incoming order.
122
131
  if (sortMode === "recent") {
123
132
  const filtered = [];
124
- for (const s of sessions) {
133
+ for (const s of nameFiltered) {
125
134
  const res = matchSession(s, parsed);
126
135
  if (res.matches)
127
136
  filtered.push(s);
@@ -130,7 +139,7 @@ export function filterAndSortSessions(sessions, query, sortMode) {
130
139
  }
131
140
  // Relevance mode: sort by score, tie-break by modified desc.
132
141
  const scored = [];
133
- for (const s of sessions) {
142
+ for (const s of nameFiltered) {
134
143
  const res = matchSession(s, parsed);
135
144
  if (!res.matches)
136
145
  continue;