@oh-my-pi/pi-coding-agent 0.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.
- package/CHANGELOG.md +1629 -0
- package/README.md +1041 -0
- package/docs/compaction.md +403 -0
- package/docs/config-usage.md +113 -0
- package/docs/custom-tools.md +541 -0
- package/docs/extension-loading.md +1004 -0
- package/docs/hooks.md +867 -0
- package/docs/rpc.md +1040 -0
- package/docs/sdk.md +994 -0
- package/docs/session-tree-plan.md +441 -0
- package/docs/session.md +240 -0
- package/docs/skills.md +290 -0
- package/docs/theme.md +670 -0
- package/docs/tree.md +197 -0
- package/docs/tui.md +341 -0
- package/examples/README.md +21 -0
- package/examples/custom-tools/README.md +124 -0
- package/examples/custom-tools/hello/index.ts +20 -0
- package/examples/custom-tools/question/index.ts +84 -0
- package/examples/custom-tools/subagent/README.md +172 -0
- package/examples/custom-tools/subagent/agents/planner.md +37 -0
- package/examples/custom-tools/subagent/agents/scout.md +50 -0
- package/examples/custom-tools/subagent/agents/worker.md +24 -0
- package/examples/custom-tools/subagent/agents.ts +156 -0
- package/examples/custom-tools/subagent/commands/implement-and-review.md +10 -0
- package/examples/custom-tools/subagent/commands/implement.md +10 -0
- package/examples/custom-tools/subagent/commands/scout-and-plan.md +9 -0
- package/examples/custom-tools/subagent/index.ts +1002 -0
- package/examples/custom-tools/todo/index.ts +212 -0
- package/examples/hooks/README.md +56 -0
- package/examples/hooks/auto-commit-on-exit.ts +49 -0
- package/examples/hooks/confirm-destructive.ts +59 -0
- package/examples/hooks/custom-compaction.ts +116 -0
- package/examples/hooks/dirty-repo-guard.ts +52 -0
- package/examples/hooks/file-trigger.ts +41 -0
- package/examples/hooks/git-checkpoint.ts +53 -0
- package/examples/hooks/handoff.ts +150 -0
- package/examples/hooks/permission-gate.ts +34 -0
- package/examples/hooks/protected-paths.ts +30 -0
- package/examples/hooks/qna.ts +119 -0
- package/examples/hooks/snake.ts +343 -0
- package/examples/hooks/status-line.ts +40 -0
- package/examples/sdk/01-minimal.ts +22 -0
- package/examples/sdk/02-custom-model.ts +49 -0
- package/examples/sdk/03-custom-prompt.ts +44 -0
- package/examples/sdk/04-skills.ts +44 -0
- package/examples/sdk/05-tools.ts +90 -0
- package/examples/sdk/06-hooks.ts +61 -0
- package/examples/sdk/07-context-files.ts +36 -0
- package/examples/sdk/08-slash-commands.ts +42 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +55 -0
- package/examples/sdk/10-settings.ts +38 -0
- package/examples/sdk/11-sessions.ts +48 -0
- package/examples/sdk/12-full-control.ts +95 -0
- package/examples/sdk/README.md +154 -0
- package/package.json +89 -0
- package/src/bun-imports.d.ts +16 -0
- package/src/capability/context-file.ts +40 -0
- package/src/capability/extension.ts +48 -0
- package/src/capability/hook.ts +40 -0
- package/src/capability/index.ts +616 -0
- package/src/capability/instruction.ts +37 -0
- package/src/capability/mcp.ts +52 -0
- package/src/capability/prompt.ts +35 -0
- package/src/capability/rule.ts +56 -0
- package/src/capability/settings.ts +35 -0
- package/src/capability/skill.ts +49 -0
- package/src/capability/slash-command.ts +40 -0
- package/src/capability/system-prompt.ts +35 -0
- package/src/capability/tool.ts +38 -0
- package/src/capability/types.ts +166 -0
- package/src/cli/args.ts +259 -0
- package/src/cli/file-processor.ts +121 -0
- package/src/cli/list-models.ts +104 -0
- package/src/cli/plugin-cli.ts +661 -0
- package/src/cli/session-picker.ts +41 -0
- package/src/cli/update-cli.ts +274 -0
- package/src/cli.ts +10 -0
- package/src/config.ts +391 -0
- package/src/core/agent-session.ts +2178 -0
- package/src/core/auth-storage.ts +258 -0
- package/src/core/bash-executor.ts +197 -0
- package/src/core/compaction/branch-summarization.ts +315 -0
- package/src/core/compaction/compaction.ts +664 -0
- package/src/core/compaction/index.ts +7 -0
- package/src/core/compaction/utils.ts +153 -0
- package/src/core/custom-commands/bundled/review/index.ts +156 -0
- package/src/core/custom-commands/index.ts +15 -0
- package/src/core/custom-commands/loader.ts +226 -0
- package/src/core/custom-commands/types.ts +112 -0
- package/src/core/custom-tools/index.ts +22 -0
- package/src/core/custom-tools/loader.ts +248 -0
- package/src/core/custom-tools/types.ts +185 -0
- package/src/core/custom-tools/wrapper.ts +29 -0
- package/src/core/exec.ts +139 -0
- package/src/core/export-html/index.ts +159 -0
- package/src/core/export-html/template.css +774 -0
- package/src/core/export-html/template.generated.ts +2 -0
- package/src/core/export-html/template.html +45 -0
- package/src/core/export-html/template.js +1185 -0
- package/src/core/export-html/template.macro.ts +24 -0
- package/src/core/file-mentions.ts +54 -0
- package/src/core/hooks/index.ts +16 -0
- package/src/core/hooks/loader.ts +288 -0
- package/src/core/hooks/runner.ts +434 -0
- package/src/core/hooks/tool-wrapper.ts +98 -0
- package/src/core/hooks/types.ts +770 -0
- package/src/core/index.ts +53 -0
- package/src/core/logger.ts +112 -0
- package/src/core/mcp/client.ts +185 -0
- package/src/core/mcp/config.ts +248 -0
- package/src/core/mcp/index.ts +45 -0
- package/src/core/mcp/loader.ts +99 -0
- package/src/core/mcp/manager.ts +235 -0
- package/src/core/mcp/tool-bridge.ts +156 -0
- package/src/core/mcp/transports/http.ts +316 -0
- package/src/core/mcp/transports/index.ts +6 -0
- package/src/core/mcp/transports/stdio.ts +252 -0
- package/src/core/mcp/types.ts +228 -0
- package/src/core/messages.ts +211 -0
- package/src/core/model-registry.ts +334 -0
- package/src/core/model-resolver.ts +494 -0
- package/src/core/plugins/doctor.ts +67 -0
- package/src/core/plugins/index.ts +38 -0
- package/src/core/plugins/installer.ts +189 -0
- package/src/core/plugins/loader.ts +339 -0
- package/src/core/plugins/manager.ts +672 -0
- package/src/core/plugins/parser.ts +105 -0
- package/src/core/plugins/paths.ts +37 -0
- package/src/core/plugins/types.ts +190 -0
- package/src/core/sdk.ts +900 -0
- package/src/core/session-manager.ts +1837 -0
- package/src/core/settings-manager.ts +860 -0
- package/src/core/skills.ts +352 -0
- package/src/core/slash-commands.ts +132 -0
- package/src/core/system-prompt.ts +442 -0
- package/src/core/timings.ts +25 -0
- package/src/core/title-generator.ts +110 -0
- package/src/core/tools/ask.ts +193 -0
- package/src/core/tools/bash-interceptor.ts +120 -0
- package/src/core/tools/bash.ts +91 -0
- package/src/core/tools/context.ts +32 -0
- package/src/core/tools/edit-diff.ts +487 -0
- package/src/core/tools/edit.ts +140 -0
- package/src/core/tools/exa/company.ts +59 -0
- package/src/core/tools/exa/index.ts +63 -0
- package/src/core/tools/exa/linkedin.ts +59 -0
- package/src/core/tools/exa/mcp-client.ts +368 -0
- package/src/core/tools/exa/render.ts +200 -0
- package/src/core/tools/exa/researcher.ts +90 -0
- package/src/core/tools/exa/search.ts +338 -0
- package/src/core/tools/exa/types.ts +167 -0
- package/src/core/tools/exa/websets.ts +248 -0
- package/src/core/tools/find.ts +244 -0
- package/src/core/tools/grep.ts +584 -0
- package/src/core/tools/index.ts +283 -0
- package/src/core/tools/ls.ts +142 -0
- package/src/core/tools/lsp/client.ts +767 -0
- package/src/core/tools/lsp/clients/biome-client.ts +207 -0
- package/src/core/tools/lsp/clients/index.ts +49 -0
- package/src/core/tools/lsp/clients/lsp-linter-client.ts +98 -0
- package/src/core/tools/lsp/config.ts +845 -0
- package/src/core/tools/lsp/edits.ts +110 -0
- package/src/core/tools/lsp/index.ts +1364 -0
- package/src/core/tools/lsp/render.ts +560 -0
- package/src/core/tools/lsp/rust-analyzer.ts +145 -0
- package/src/core/tools/lsp/types.ts +495 -0
- package/src/core/tools/lsp/utils.ts +526 -0
- package/src/core/tools/notebook.ts +182 -0
- package/src/core/tools/output.ts +198 -0
- package/src/core/tools/path-utils.ts +61 -0
- package/src/core/tools/read.ts +507 -0
- package/src/core/tools/renderers.ts +820 -0
- package/src/core/tools/review.ts +275 -0
- package/src/core/tools/rulebook.ts +124 -0
- package/src/core/tools/task/agents.ts +158 -0
- package/src/core/tools/task/artifacts.ts +114 -0
- package/src/core/tools/task/commands.ts +157 -0
- package/src/core/tools/task/discovery.ts +217 -0
- package/src/core/tools/task/executor.ts +531 -0
- package/src/core/tools/task/index.ts +548 -0
- package/src/core/tools/task/model-resolver.ts +176 -0
- package/src/core/tools/task/parallel.ts +38 -0
- package/src/core/tools/task/render.ts +502 -0
- package/src/core/tools/task/subprocess-tool-registry.ts +89 -0
- package/src/core/tools/task/types.ts +142 -0
- package/src/core/tools/truncate.ts +265 -0
- package/src/core/tools/web-fetch.ts +2511 -0
- package/src/core/tools/web-search/auth.ts +199 -0
- package/src/core/tools/web-search/index.ts +583 -0
- package/src/core/tools/web-search/providers/anthropic.ts +198 -0
- package/src/core/tools/web-search/providers/exa.ts +196 -0
- package/src/core/tools/web-search/providers/perplexity.ts +195 -0
- package/src/core/tools/web-search/render.ts +372 -0
- package/src/core/tools/web-search/types.ts +180 -0
- package/src/core/tools/write.ts +63 -0
- package/src/core/ttsr.ts +211 -0
- package/src/core/utils.ts +187 -0
- package/src/discovery/agents-md.ts +75 -0
- package/src/discovery/builtin.ts +647 -0
- package/src/discovery/claude.ts +623 -0
- package/src/discovery/cline.ts +104 -0
- package/src/discovery/codex.ts +571 -0
- package/src/discovery/cursor.ts +266 -0
- package/src/discovery/gemini.ts +368 -0
- package/src/discovery/github.ts +120 -0
- package/src/discovery/helpers.test.ts +127 -0
- package/src/discovery/helpers.ts +249 -0
- package/src/discovery/index.ts +84 -0
- package/src/discovery/mcp-json.ts +127 -0
- package/src/discovery/vscode.ts +99 -0
- package/src/discovery/windsurf.ts +219 -0
- package/src/index.ts +192 -0
- package/src/main.ts +507 -0
- package/src/migrations.ts +156 -0
- package/src/modes/cleanup.ts +23 -0
- package/src/modes/index.ts +48 -0
- package/src/modes/interactive/components/armin.ts +382 -0
- package/src/modes/interactive/components/assistant-message.ts +86 -0
- package/src/modes/interactive/components/bash-execution.ts +199 -0
- package/src/modes/interactive/components/bordered-loader.ts +41 -0
- package/src/modes/interactive/components/branch-summary-message.ts +42 -0
- package/src/modes/interactive/components/compaction-summary-message.ts +45 -0
- package/src/modes/interactive/components/custom-editor.ts +122 -0
- package/src/modes/interactive/components/diff.ts +147 -0
- package/src/modes/interactive/components/dynamic-border.ts +25 -0
- package/src/modes/interactive/components/extensions/extension-dashboard.ts +296 -0
- package/src/modes/interactive/components/extensions/extension-list.ts +479 -0
- package/src/modes/interactive/components/extensions/index.ts +9 -0
- package/src/modes/interactive/components/extensions/inspector-panel.ts +313 -0
- package/src/modes/interactive/components/extensions/state-manager.ts +558 -0
- package/src/modes/interactive/components/extensions/types.ts +191 -0
- package/src/modes/interactive/components/hook-editor.ts +117 -0
- package/src/modes/interactive/components/hook-input.ts +64 -0
- package/src/modes/interactive/components/hook-message.ts +96 -0
- package/src/modes/interactive/components/hook-selector.ts +91 -0
- package/src/modes/interactive/components/model-selector.ts +560 -0
- package/src/modes/interactive/components/oauth-selector.ts +136 -0
- package/src/modes/interactive/components/plugin-settings.ts +481 -0
- package/src/modes/interactive/components/queue-mode-selector.ts +56 -0
- package/src/modes/interactive/components/session-selector.ts +220 -0
- package/src/modes/interactive/components/settings-defs.ts +597 -0
- package/src/modes/interactive/components/settings-selector.ts +545 -0
- package/src/modes/interactive/components/show-images-selector.ts +45 -0
- package/src/modes/interactive/components/status-line/index.ts +4 -0
- package/src/modes/interactive/components/status-line/presets.ts +94 -0
- package/src/modes/interactive/components/status-line/segments.ts +350 -0
- package/src/modes/interactive/components/status-line/separators.ts +55 -0
- package/src/modes/interactive/components/status-line/types.ts +81 -0
- package/src/modes/interactive/components/status-line-segment-editor.ts +357 -0
- package/src/modes/interactive/components/status-line.ts +384 -0
- package/src/modes/interactive/components/theme-selector.ts +62 -0
- package/src/modes/interactive/components/thinking-selector.ts +64 -0
- package/src/modes/interactive/components/tool-execution.ts +946 -0
- package/src/modes/interactive/components/tree-selector.ts +877 -0
- package/src/modes/interactive/components/ttsr-notification.ts +82 -0
- package/src/modes/interactive/components/user-message-selector.ts +159 -0
- package/src/modes/interactive/components/user-message.ts +18 -0
- package/src/modes/interactive/components/visual-truncate.ts +50 -0
- package/src/modes/interactive/components/welcome.ts +228 -0
- package/src/modes/interactive/interactive-mode.ts +2669 -0
- package/src/modes/interactive/theme/dark.json +102 -0
- package/src/modes/interactive/theme/defaults/dark-arctic.json +111 -0
- package/src/modes/interactive/theme/defaults/dark-catppuccin.json +106 -0
- package/src/modes/interactive/theme/defaults/dark-cyberpunk.json +109 -0
- package/src/modes/interactive/theme/defaults/dark-dracula.json +105 -0
- package/src/modes/interactive/theme/defaults/dark-forest.json +103 -0
- package/src/modes/interactive/theme/defaults/dark-github.json +112 -0
- package/src/modes/interactive/theme/defaults/dark-gruvbox.json +119 -0
- package/src/modes/interactive/theme/defaults/dark-monochrome.json +101 -0
- package/src/modes/interactive/theme/defaults/dark-monokai.json +105 -0
- package/src/modes/interactive/theme/defaults/dark-nord.json +104 -0
- package/src/modes/interactive/theme/defaults/dark-ocean.json +108 -0
- package/src/modes/interactive/theme/defaults/dark-one.json +107 -0
- package/src/modes/interactive/theme/defaults/dark-retro.json +99 -0
- package/src/modes/interactive/theme/defaults/dark-rose-pine.json +95 -0
- package/src/modes/interactive/theme/defaults/dark-solarized.json +96 -0
- package/src/modes/interactive/theme/defaults/dark-sunset.json +106 -0
- package/src/modes/interactive/theme/defaults/dark-synthwave.json +102 -0
- package/src/modes/interactive/theme/defaults/dark-tokyo-night.json +108 -0
- package/src/modes/interactive/theme/defaults/index.ts +67 -0
- package/src/modes/interactive/theme/defaults/light-arctic.json +106 -0
- package/src/modes/interactive/theme/defaults/light-catppuccin.json +105 -0
- package/src/modes/interactive/theme/defaults/light-cyberpunk.json +103 -0
- package/src/modes/interactive/theme/defaults/light-forest.json +107 -0
- package/src/modes/interactive/theme/defaults/light-github.json +114 -0
- package/src/modes/interactive/theme/defaults/light-gruvbox.json +115 -0
- package/src/modes/interactive/theme/defaults/light-monochrome.json +100 -0
- package/src/modes/interactive/theme/defaults/light-ocean.json +106 -0
- package/src/modes/interactive/theme/defaults/light-one.json +105 -0
- package/src/modes/interactive/theme/defaults/light-retro.json +105 -0
- package/src/modes/interactive/theme/defaults/light-solarized.json +101 -0
- package/src/modes/interactive/theme/defaults/light-sunset.json +106 -0
- package/src/modes/interactive/theme/defaults/light-synthwave.json +105 -0
- package/src/modes/interactive/theme/defaults/light-tokyo-night.json +118 -0
- package/src/modes/interactive/theme/light.json +99 -0
- package/src/modes/interactive/theme/theme-schema.json +424 -0
- package/src/modes/interactive/theme/theme.ts +2211 -0
- package/src/modes/print-mode.ts +163 -0
- package/src/modes/rpc/rpc-client.ts +527 -0
- package/src/modes/rpc/rpc-mode.ts +494 -0
- package/src/modes/rpc/rpc-types.ts +203 -0
- package/src/prompts/architect-plan.md +10 -0
- package/src/prompts/branch-summary-preamble.md +3 -0
- package/src/prompts/branch-summary.md +28 -0
- package/src/prompts/browser.md +71 -0
- package/src/prompts/compaction-summary.md +34 -0
- package/src/prompts/compaction-turn-prefix.md +16 -0
- package/src/prompts/compaction-update-summary.md +41 -0
- package/src/prompts/explore.md +82 -0
- package/src/prompts/implement-with-critic.md +11 -0
- package/src/prompts/implement.md +11 -0
- package/src/prompts/init.md +30 -0
- package/src/prompts/plan.md +54 -0
- package/src/prompts/reviewer.md +81 -0
- package/src/prompts/summarization-system.md +3 -0
- package/src/prompts/system-prompt.md +27 -0
- package/src/prompts/task.md +56 -0
- package/src/prompts/title-system.md +8 -0
- package/src/prompts/tools/ask.md +24 -0
- package/src/prompts/tools/bash.md +23 -0
- package/src/prompts/tools/edit.md +9 -0
- package/src/prompts/tools/find.md +6 -0
- package/src/prompts/tools/grep.md +12 -0
- package/src/prompts/tools/lsp.md +14 -0
- package/src/prompts/tools/output.md +23 -0
- package/src/prompts/tools/read.md +25 -0
- package/src/prompts/tools/web-fetch.md +8 -0
- package/src/prompts/tools/web-search.md +10 -0
- package/src/prompts/tools/write.md +10 -0
- package/src/utils/changelog.ts +99 -0
- package/src/utils/clipboard.ts +265 -0
- package/src/utils/fuzzy.ts +108 -0
- package/src/utils/mime.ts +30 -0
- package/src/utils/shell-snapshot.ts +218 -0
- package/src/utils/shell.ts +364 -0
- package/src/utils/tools-manager.ts +265 -0
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
2
|
+
import {
|
|
3
|
+
Container,
|
|
4
|
+
isArrowLeft,
|
|
5
|
+
isArrowRight,
|
|
6
|
+
isEscape,
|
|
7
|
+
isShiftTab,
|
|
8
|
+
isTab,
|
|
9
|
+
type SelectItem,
|
|
10
|
+
SelectList,
|
|
11
|
+
type SettingItem,
|
|
12
|
+
SettingsList,
|
|
13
|
+
Spacer,
|
|
14
|
+
type Tab,
|
|
15
|
+
TabBar,
|
|
16
|
+
type TabBarTheme,
|
|
17
|
+
Text,
|
|
18
|
+
} from "@oh-my-pi/pi-tui";
|
|
19
|
+
import type { SettingsManager, StatusLineSettings } from "../../../core/settings-manager";
|
|
20
|
+
import { getSelectListTheme, getSettingsListTheme, theme } from "../theme/theme";
|
|
21
|
+
import { DynamicBorder } from "./dynamic-border";
|
|
22
|
+
import { PluginSettingsComponent } from "./plugin-settings";
|
|
23
|
+
import { getSettingsForTab, type SettingDef } from "./settings-defs";
|
|
24
|
+
import { getPreset } from "./status-line/presets";
|
|
25
|
+
import { StatusLineSegmentEditorComponent } from "./status-line-segment-editor";
|
|
26
|
+
|
|
27
|
+
function getTabBarTheme(): TabBarTheme {
|
|
28
|
+
return {
|
|
29
|
+
label: (text) => theme.bold(theme.fg("accent", text)),
|
|
30
|
+
activeTab: (text) => theme.bold(theme.bg("selectedBg", theme.fg("text", text))),
|
|
31
|
+
inactiveTab: (text) => theme.fg("muted", text),
|
|
32
|
+
hint: (text) => theme.fg("dim", text),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A submenu component for selecting from a list of options.
|
|
38
|
+
*/
|
|
39
|
+
class SelectSubmenu extends Container {
|
|
40
|
+
private selectList: SelectList;
|
|
41
|
+
private previewText: Text | null = null;
|
|
42
|
+
private getPreview: (() => string) | undefined;
|
|
43
|
+
|
|
44
|
+
constructor(
|
|
45
|
+
title: string,
|
|
46
|
+
description: string,
|
|
47
|
+
options: SelectItem[],
|
|
48
|
+
currentValue: string,
|
|
49
|
+
onSelect: (value: string) => void,
|
|
50
|
+
onCancel: () => void,
|
|
51
|
+
onSelectionChange?: (value: string) => void,
|
|
52
|
+
getPreview?: () => string,
|
|
53
|
+
) {
|
|
54
|
+
super();
|
|
55
|
+
this.getPreview = getPreview;
|
|
56
|
+
|
|
57
|
+
// Title
|
|
58
|
+
this.addChild(new Text(theme.bold(theme.fg("accent", title)), 0, 0));
|
|
59
|
+
|
|
60
|
+
// Description
|
|
61
|
+
if (description) {
|
|
62
|
+
this.addChild(new Spacer(1));
|
|
63
|
+
this.addChild(new Text(theme.fg("muted", description), 0, 0));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Preview (if provided)
|
|
67
|
+
if (getPreview) {
|
|
68
|
+
this.addChild(new Spacer(1));
|
|
69
|
+
this.addChild(new Text(theme.fg("muted", "Preview:"), 0, 0));
|
|
70
|
+
this.previewText = new Text(getPreview(), 0, 0);
|
|
71
|
+
this.addChild(this.previewText);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Spacer
|
|
75
|
+
this.addChild(new Spacer(1));
|
|
76
|
+
|
|
77
|
+
// Select list
|
|
78
|
+
this.selectList = new SelectList(options, Math.min(options.length, 10), getSelectListTheme());
|
|
79
|
+
|
|
80
|
+
// Pre-select current value
|
|
81
|
+
const currentIndex = options.findIndex((o) => o.value === currentValue);
|
|
82
|
+
if (currentIndex !== -1) {
|
|
83
|
+
this.selectList.setSelectedIndex(currentIndex);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
this.selectList.onSelect = (item) => {
|
|
87
|
+
onSelect(item.value);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
this.selectList.onCancel = onCancel;
|
|
91
|
+
|
|
92
|
+
if (onSelectionChange) {
|
|
93
|
+
this.selectList.onSelectionChange = (item) => {
|
|
94
|
+
onSelectionChange(item.value);
|
|
95
|
+
// Update preview after the preview callback has applied changes
|
|
96
|
+
this.updatePreview();
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
this.addChild(this.selectList);
|
|
101
|
+
|
|
102
|
+
// Hint
|
|
103
|
+
this.addChild(new Spacer(1));
|
|
104
|
+
this.addChild(new Text(theme.fg("dim", " Enter to select · Esc to go back"), 0, 0));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private updatePreview(): void {
|
|
108
|
+
if (this.previewText && this.getPreview) {
|
|
109
|
+
this.previewText.setText(this.getPreview());
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
handleInput(data: string): void {
|
|
114
|
+
this.selectList.handleInput(data);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
type TabId = string;
|
|
119
|
+
|
|
120
|
+
const SETTINGS_TABS: Tab[] = [
|
|
121
|
+
{ id: "config", label: "Config" },
|
|
122
|
+
{ id: "status", label: "Status" },
|
|
123
|
+
{ id: "lsp", label: "LSP" },
|
|
124
|
+
{ id: "exa", label: "Exa" },
|
|
125
|
+
{ id: "plugins", label: "Plugins" },
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Dynamic context for settings that need runtime data.
|
|
130
|
+
* Some settings (like thinking level) are managed by the session, not SettingsManager.
|
|
131
|
+
*/
|
|
132
|
+
export interface SettingsRuntimeContext {
|
|
133
|
+
/** Available thinking levels (from session) */
|
|
134
|
+
availableThinkingLevels: ThinkingLevel[];
|
|
135
|
+
/** Current thinking level (from session) */
|
|
136
|
+
thinkingLevel: ThinkingLevel;
|
|
137
|
+
/** Available themes */
|
|
138
|
+
availableThemes: string[];
|
|
139
|
+
/** Working directory for plugins tab */
|
|
140
|
+
cwd: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Callback when any setting changes.
|
|
145
|
+
* The handler should dispatch based on settingId.
|
|
146
|
+
*/
|
|
147
|
+
export type SettingChangeHandler = (settingId: string, newValue: string | boolean) => void;
|
|
148
|
+
|
|
149
|
+
export interface SettingsCallbacks {
|
|
150
|
+
/** Called when any setting value changes */
|
|
151
|
+
onChange: SettingChangeHandler;
|
|
152
|
+
/** Called for theme preview while browsing */
|
|
153
|
+
onThemePreview?: (theme: string) => void;
|
|
154
|
+
/** Called for status line preview while configuring - updates actual status line */
|
|
155
|
+
onStatusLinePreview?: (settings: Partial<StatusLineSettings>) => void;
|
|
156
|
+
/** Get current rendered status line for inline preview */
|
|
157
|
+
getStatusLinePreview?: () => string;
|
|
158
|
+
/** Called when plugins change */
|
|
159
|
+
onPluginsChanged?: () => void;
|
|
160
|
+
/** Called when settings panel is closed */
|
|
161
|
+
onCancel: () => void;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Main tabbed settings selector component.
|
|
166
|
+
* Uses declarative settings definitions from settings-defs.ts.
|
|
167
|
+
*/
|
|
168
|
+
export class SettingsSelectorComponent extends Container {
|
|
169
|
+
private tabBar: TabBar;
|
|
170
|
+
private currentList: SettingsList | null = null;
|
|
171
|
+
private currentSubmenu: Container | null = null;
|
|
172
|
+
private pluginComponent: PluginSettingsComponent | null = null;
|
|
173
|
+
private statusPreviewContainer: Container | null = null;
|
|
174
|
+
private statusPreviewText: Text | null = null;
|
|
175
|
+
private currentTabId: TabId = "config";
|
|
176
|
+
|
|
177
|
+
private settingsManager: SettingsManager;
|
|
178
|
+
private context: SettingsRuntimeContext;
|
|
179
|
+
private callbacks: SettingsCallbacks;
|
|
180
|
+
|
|
181
|
+
constructor(settingsManager: SettingsManager, context: SettingsRuntimeContext, callbacks: SettingsCallbacks) {
|
|
182
|
+
super();
|
|
183
|
+
|
|
184
|
+
this.settingsManager = settingsManager;
|
|
185
|
+
this.context = context;
|
|
186
|
+
this.callbacks = callbacks;
|
|
187
|
+
|
|
188
|
+
// Add top border
|
|
189
|
+
this.addChild(new DynamicBorder());
|
|
190
|
+
|
|
191
|
+
// Tab bar
|
|
192
|
+
this.tabBar = new TabBar("Settings", SETTINGS_TABS, getTabBarTheme());
|
|
193
|
+
this.tabBar.onTabChange = () => {
|
|
194
|
+
this.switchToTab(this.tabBar.getActiveTab().id as TabId);
|
|
195
|
+
};
|
|
196
|
+
this.addChild(this.tabBar);
|
|
197
|
+
|
|
198
|
+
// Spacer after tab bar
|
|
199
|
+
this.addChild(new Spacer(1));
|
|
200
|
+
|
|
201
|
+
// Initialize with first tab
|
|
202
|
+
this.switchToTab("config");
|
|
203
|
+
|
|
204
|
+
// Add bottom border
|
|
205
|
+
this.addChild(new DynamicBorder());
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
private switchToTab(tabId: TabId): void {
|
|
209
|
+
this.currentTabId = tabId;
|
|
210
|
+
|
|
211
|
+
// Remove current content
|
|
212
|
+
if (this.currentList) {
|
|
213
|
+
this.removeChild(this.currentList);
|
|
214
|
+
this.currentList = null;
|
|
215
|
+
}
|
|
216
|
+
if (this.pluginComponent) {
|
|
217
|
+
this.removeChild(this.pluginComponent);
|
|
218
|
+
this.pluginComponent = null;
|
|
219
|
+
}
|
|
220
|
+
if (this.statusPreviewContainer) {
|
|
221
|
+
this.removeChild(this.statusPreviewContainer);
|
|
222
|
+
this.statusPreviewContainer = null;
|
|
223
|
+
this.statusPreviewText = null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Remove bottom border temporarily
|
|
227
|
+
const bottomBorder = this.children[this.children.length - 1];
|
|
228
|
+
this.removeChild(bottomBorder);
|
|
229
|
+
|
|
230
|
+
if (tabId === "plugins") {
|
|
231
|
+
this.showPluginsTab();
|
|
232
|
+
} else {
|
|
233
|
+
this.showSettingsTab(tabId);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Re-add bottom border
|
|
237
|
+
this.addChild(bottomBorder);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Convert a setting definition to a SettingItem for the UI.
|
|
242
|
+
*/
|
|
243
|
+
private defToItem(def: SettingDef): SettingItem | null {
|
|
244
|
+
// Check condition
|
|
245
|
+
if (def.type === "boolean" && def.condition && !def.condition()) {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const currentValue = this.getCurrentValue(def);
|
|
250
|
+
|
|
251
|
+
switch (def.type) {
|
|
252
|
+
case "boolean":
|
|
253
|
+
return {
|
|
254
|
+
id: def.id,
|
|
255
|
+
label: def.label,
|
|
256
|
+
description: def.description,
|
|
257
|
+
currentValue: currentValue ? "true" : "false",
|
|
258
|
+
values: ["true", "false"],
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
case "enum":
|
|
262
|
+
return {
|
|
263
|
+
id: def.id,
|
|
264
|
+
label: def.label,
|
|
265
|
+
description: def.description,
|
|
266
|
+
currentValue: currentValue as string,
|
|
267
|
+
values: [...def.values],
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
case "submenu":
|
|
271
|
+
return {
|
|
272
|
+
id: def.id,
|
|
273
|
+
label: def.label,
|
|
274
|
+
description: def.description,
|
|
275
|
+
currentValue: currentValue as string,
|
|
276
|
+
submenu: (cv, done) => this.createSubmenu(def, cv, done),
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Get the current value for a setting, using runtime context for special cases.
|
|
283
|
+
*/
|
|
284
|
+
private getCurrentValue(def: SettingDef): string | boolean {
|
|
285
|
+
// Special cases that come from runtime context instead of SettingsManager
|
|
286
|
+
switch (def.id) {
|
|
287
|
+
case "thinkingLevel":
|
|
288
|
+
return this.context.thinkingLevel;
|
|
289
|
+
default:
|
|
290
|
+
return def.get(this.settingsManager);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Create a submenu for a submenu-type setting.
|
|
296
|
+
*/
|
|
297
|
+
private createSubmenu(
|
|
298
|
+
def: SettingDef & { type: "submenu" },
|
|
299
|
+
currentValue: string,
|
|
300
|
+
done: (value?: string) => void,
|
|
301
|
+
): Container {
|
|
302
|
+
// Special case: segment editor
|
|
303
|
+
if (def.id === "statusLineSegments") {
|
|
304
|
+
return this.createSegmentEditor(done);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
let options = def.getOptions(this.settingsManager);
|
|
308
|
+
|
|
309
|
+
// Special case: inject runtime options
|
|
310
|
+
if (def.id === "thinkingLevel") {
|
|
311
|
+
options = this.context.availableThinkingLevels.map((level) => {
|
|
312
|
+
const baseOpt = def.getOptions(this.settingsManager).find((o) => o.value === level);
|
|
313
|
+
return baseOpt || { value: level, label: level };
|
|
314
|
+
});
|
|
315
|
+
} else if (def.id === "theme") {
|
|
316
|
+
options = this.context.availableThemes.map((t) => ({ value: t, label: t }));
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Preview handlers
|
|
320
|
+
let onPreview: ((value: string) => void) | undefined;
|
|
321
|
+
let onPreviewCancel: (() => void) | undefined;
|
|
322
|
+
|
|
323
|
+
if (def.id === "theme") {
|
|
324
|
+
onPreview = this.callbacks.onThemePreview;
|
|
325
|
+
onPreviewCancel = () => this.callbacks.onThemePreview?.(currentValue);
|
|
326
|
+
} else if (def.id === "statusLinePreset") {
|
|
327
|
+
onPreview = (value) => {
|
|
328
|
+
const presetDef = getPreset((value as StatusLineSettings["preset"]) ?? "default");
|
|
329
|
+
this.callbacks.onStatusLinePreview?.({
|
|
330
|
+
preset: value as StatusLineSettings["preset"],
|
|
331
|
+
leftSegments: presetDef.leftSegments,
|
|
332
|
+
rightSegments: presetDef.rightSegments,
|
|
333
|
+
separator: presetDef.separator,
|
|
334
|
+
});
|
|
335
|
+
this.updateStatusPreview();
|
|
336
|
+
};
|
|
337
|
+
onPreviewCancel = () => {
|
|
338
|
+
const currentPreset = this.settingsManager.getStatusLinePreset();
|
|
339
|
+
const presetDef = getPreset(currentPreset);
|
|
340
|
+
this.callbacks.onStatusLinePreview?.({
|
|
341
|
+
preset: currentPreset,
|
|
342
|
+
leftSegments: presetDef.leftSegments,
|
|
343
|
+
rightSegments: presetDef.rightSegments,
|
|
344
|
+
separator: presetDef.separator,
|
|
345
|
+
});
|
|
346
|
+
this.updateStatusPreview();
|
|
347
|
+
};
|
|
348
|
+
} else if (def.id === "statusLineSeparator") {
|
|
349
|
+
onPreview = (value) => {
|
|
350
|
+
this.callbacks.onStatusLinePreview?.({
|
|
351
|
+
separator: value as StatusLineSettings["separator"],
|
|
352
|
+
});
|
|
353
|
+
this.updateStatusPreview();
|
|
354
|
+
};
|
|
355
|
+
onPreviewCancel = () => {
|
|
356
|
+
const currentSettings = this.settingsManager.getStatusLineSettings();
|
|
357
|
+
const separator =
|
|
358
|
+
currentSettings.separator ?? getPreset(this.settingsManager.getStatusLinePreset()).separator;
|
|
359
|
+
this.callbacks.onStatusLinePreview?.({
|
|
360
|
+
separator,
|
|
361
|
+
});
|
|
362
|
+
this.updateStatusPreview();
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Provide status line preview for theme selection
|
|
367
|
+
const getPreview = def.id === "theme" ? this.callbacks.getStatusLinePreview : undefined;
|
|
368
|
+
|
|
369
|
+
return new SelectSubmenu(
|
|
370
|
+
def.label,
|
|
371
|
+
def.description,
|
|
372
|
+
options,
|
|
373
|
+
currentValue,
|
|
374
|
+
(value) => {
|
|
375
|
+
// Persist to SettingsManager
|
|
376
|
+
def.set(this.settingsManager, value);
|
|
377
|
+
// Notify for side effects
|
|
378
|
+
this.callbacks.onChange(def.id, value);
|
|
379
|
+
done(value);
|
|
380
|
+
},
|
|
381
|
+
() => {
|
|
382
|
+
onPreviewCancel?.();
|
|
383
|
+
done();
|
|
384
|
+
},
|
|
385
|
+
onPreview,
|
|
386
|
+
getPreview,
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Create the segment editor component.
|
|
392
|
+
*/
|
|
393
|
+
private createSegmentEditor(done: (value?: string) => void): Container {
|
|
394
|
+
const currentSettings = this.settingsManager.getStatusLineSettings();
|
|
395
|
+
const preset = currentSettings.preset ?? "default";
|
|
396
|
+
const presetDef = getPreset(preset);
|
|
397
|
+
|
|
398
|
+
const leftSegments = currentSettings.leftSegments ?? presetDef.leftSegments;
|
|
399
|
+
const rightSegments = currentSettings.rightSegments ?? presetDef.rightSegments;
|
|
400
|
+
|
|
401
|
+
return new StatusLineSegmentEditorComponent(leftSegments, rightSegments, {
|
|
402
|
+
onSave: (left, right) => {
|
|
403
|
+
this.settingsManager.setStatusLineLeftSegments(left);
|
|
404
|
+
this.settingsManager.setStatusLineRightSegments(right);
|
|
405
|
+
this.callbacks.onChange("statusLineSegments", "saved");
|
|
406
|
+
this.callbacks.onStatusLinePreview?.({ leftSegments: left, rightSegments: right });
|
|
407
|
+
this.updateStatusPreview();
|
|
408
|
+
done("saved");
|
|
409
|
+
},
|
|
410
|
+
onCancel: () => {
|
|
411
|
+
// Restore preview to saved state
|
|
412
|
+
const saved = this.settingsManager.getStatusLineSettings();
|
|
413
|
+
const savedPreset = getPreset(saved.preset ?? "default");
|
|
414
|
+
this.callbacks.onStatusLinePreview?.({
|
|
415
|
+
leftSegments: saved.leftSegments ?? savedPreset.leftSegments,
|
|
416
|
+
rightSegments: saved.rightSegments ?? savedPreset.rightSegments,
|
|
417
|
+
});
|
|
418
|
+
this.updateStatusPreview();
|
|
419
|
+
done();
|
|
420
|
+
},
|
|
421
|
+
onPreview: (left, right) => {
|
|
422
|
+
this.callbacks.onStatusLinePreview?.({ leftSegments: left, rightSegments: right });
|
|
423
|
+
this.updateStatusPreview();
|
|
424
|
+
},
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Show a settings tab using definitions.
|
|
430
|
+
*/
|
|
431
|
+
private showSettingsTab(tabId: string): void {
|
|
432
|
+
const defs = getSettingsForTab(tabId);
|
|
433
|
+
const items: SettingItem[] = [];
|
|
434
|
+
|
|
435
|
+
for (const def of defs) {
|
|
436
|
+
const item = this.defToItem(def);
|
|
437
|
+
if (item) {
|
|
438
|
+
items.push(item);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Add status line preview for status tab
|
|
443
|
+
if (tabId === "status") {
|
|
444
|
+
this.statusPreviewContainer = new Container();
|
|
445
|
+
this.statusPreviewContainer.addChild(new Spacer(1));
|
|
446
|
+
this.statusPreviewContainer.addChild(new Text(theme.fg("muted", "Preview:"), 0, 0));
|
|
447
|
+
this.statusPreviewText = new Text(this.getStatusPreviewString(), 0, 0);
|
|
448
|
+
this.statusPreviewContainer.addChild(this.statusPreviewText);
|
|
449
|
+
this.statusPreviewContainer.addChild(new Spacer(1));
|
|
450
|
+
this.addChild(this.statusPreviewContainer);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
this.currentList = new SettingsList(
|
|
454
|
+
items,
|
|
455
|
+
10,
|
|
456
|
+
getSettingsListTheme(),
|
|
457
|
+
(id, newValue) => {
|
|
458
|
+
const def = defs.find((d) => d.id === id);
|
|
459
|
+
if (!def) return;
|
|
460
|
+
|
|
461
|
+
// Persist to SettingsManager based on type
|
|
462
|
+
if (def.type === "boolean") {
|
|
463
|
+
const boolValue = newValue === "true";
|
|
464
|
+
def.set(this.settingsManager, boolValue);
|
|
465
|
+
this.callbacks.onChange(id, boolValue);
|
|
466
|
+
|
|
467
|
+
// Trigger status line preview for status tab boolean settings
|
|
468
|
+
if (tabId === "status") {
|
|
469
|
+
this.triggerStatusLinePreview();
|
|
470
|
+
}
|
|
471
|
+
} else if (def.type === "enum") {
|
|
472
|
+
def.set(this.settingsManager, newValue);
|
|
473
|
+
this.callbacks.onChange(id, newValue);
|
|
474
|
+
}
|
|
475
|
+
// Submenu types are handled in createSubmenu
|
|
476
|
+
},
|
|
477
|
+
() => this.callbacks.onCancel(),
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
this.addChild(this.currentList);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Get the status line preview string.
|
|
485
|
+
*/
|
|
486
|
+
private getStatusPreviewString(): string {
|
|
487
|
+
if (this.callbacks.getStatusLinePreview) {
|
|
488
|
+
return this.callbacks.getStatusLinePreview();
|
|
489
|
+
}
|
|
490
|
+
return theme.fg("dim", "(preview not available)");
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Trigger status line preview with current settings.
|
|
495
|
+
*/
|
|
496
|
+
private triggerStatusLinePreview(): void {
|
|
497
|
+
const settings = this.settingsManager.getStatusLineSettings();
|
|
498
|
+
this.callbacks.onStatusLinePreview?.(settings);
|
|
499
|
+
// Update inline preview
|
|
500
|
+
this.updateStatusPreview();
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Update the inline status preview text.
|
|
505
|
+
*/
|
|
506
|
+
private updateStatusPreview(): void {
|
|
507
|
+
if (this.statusPreviewText && this.currentTabId === "status") {
|
|
508
|
+
this.statusPreviewText.setText(this.getStatusPreviewString());
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
private showPluginsTab(): void {
|
|
513
|
+
this.pluginComponent = new PluginSettingsComponent(this.context.cwd, {
|
|
514
|
+
onClose: () => this.callbacks.onCancel(),
|
|
515
|
+
onPluginChanged: () => this.callbacks.onPluginsChanged?.(),
|
|
516
|
+
});
|
|
517
|
+
this.addChild(this.pluginComponent);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
getFocusComponent(): SettingsList | PluginSettingsComponent {
|
|
521
|
+
// Return the current focusable component - one of these will always be set
|
|
522
|
+
return (this.currentList || this.pluginComponent)!;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
handleInput(data: string): void {
|
|
526
|
+
// Handle tab switching first (tab, shift+tab, or left/right arrows)
|
|
527
|
+
if (isTab(data) || isShiftTab(data) || isArrowLeft(data) || isArrowRight(data)) {
|
|
528
|
+
this.tabBar.handleInput(data);
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Escape at top level cancels
|
|
533
|
+
if (isEscape(data) && !this.currentSubmenu) {
|
|
534
|
+
this.callbacks.onCancel();
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Pass to current content
|
|
539
|
+
if (this.currentList) {
|
|
540
|
+
this.currentList.handleInput(data);
|
|
541
|
+
} else if (this.pluginComponent) {
|
|
542
|
+
this.pluginComponent.handleInput(data);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Container, type SelectItem, SelectList } from "@oh-my-pi/pi-tui";
|
|
2
|
+
import { getSelectListTheme } from "../theme/theme";
|
|
3
|
+
import { DynamicBorder } from "./dynamic-border";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Component that renders a show images selector with borders
|
|
7
|
+
*/
|
|
8
|
+
export class ShowImagesSelectorComponent extends Container {
|
|
9
|
+
private selectList: SelectList;
|
|
10
|
+
|
|
11
|
+
constructor(currentValue: boolean, onSelect: (show: boolean) => void, onCancel: () => void) {
|
|
12
|
+
super();
|
|
13
|
+
|
|
14
|
+
const items: SelectItem[] = [
|
|
15
|
+
{ value: "yes", label: "Yes", description: "Show images inline in terminal" },
|
|
16
|
+
{ value: "no", label: "No", description: "Show text placeholder instead" },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
// Add top border
|
|
20
|
+
this.addChild(new DynamicBorder());
|
|
21
|
+
|
|
22
|
+
// Create selector
|
|
23
|
+
this.selectList = new SelectList(items, 5, getSelectListTheme());
|
|
24
|
+
|
|
25
|
+
// Preselect current value
|
|
26
|
+
this.selectList.setSelectedIndex(currentValue ? 0 : 1);
|
|
27
|
+
|
|
28
|
+
this.selectList.onSelect = (item) => {
|
|
29
|
+
onSelect(item.value === "yes");
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
this.selectList.onCancel = () => {
|
|
33
|
+
onCancel();
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
this.addChild(this.selectList);
|
|
37
|
+
|
|
38
|
+
// Add bottom border
|
|
39
|
+
this.addChild(new DynamicBorder());
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
getSelectList(): SelectList {
|
|
43
|
+
return this.selectList;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { PresetDef, StatusLinePreset } from "./types";
|
|
2
|
+
|
|
3
|
+
export const STATUS_LINE_PRESETS: Record<StatusLinePreset, PresetDef> = {
|
|
4
|
+
default: {
|
|
5
|
+
// Matches current behavior
|
|
6
|
+
leftSegments: ["pi", "model", "path", "git", "context_pct", "token_total", "cost"],
|
|
7
|
+
rightSegments: [],
|
|
8
|
+
separator: "powerline-thin",
|
|
9
|
+
segmentOptions: {
|
|
10
|
+
model: { showThinkingLevel: true },
|
|
11
|
+
path: { abbreviate: true, maxLength: 40, stripWorkPrefix: true },
|
|
12
|
+
git: { showBranch: true, showStaged: true, showUnstaged: true, showUntracked: true },
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
minimal: {
|
|
17
|
+
leftSegments: ["path", "git"],
|
|
18
|
+
rightSegments: ["context_pct"],
|
|
19
|
+
separator: "slash",
|
|
20
|
+
segmentOptions: {
|
|
21
|
+
path: { abbreviate: true, maxLength: 30 },
|
|
22
|
+
git: { showBranch: true, showStaged: false, showUnstaged: false, showUntracked: false },
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
compact: {
|
|
27
|
+
leftSegments: ["model", "git"],
|
|
28
|
+
rightSegments: ["cost", "context_pct"],
|
|
29
|
+
separator: "powerline-thin",
|
|
30
|
+
segmentOptions: {
|
|
31
|
+
model: { showThinkingLevel: false },
|
|
32
|
+
git: { showBranch: true, showStaged: true, showUnstaged: true, showUntracked: false },
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
full: {
|
|
37
|
+
leftSegments: ["pi", "hostname", "model", "path", "git", "subagents"],
|
|
38
|
+
rightSegments: ["token_in", "token_out", "cache_read", "cost", "context_pct", "time_spent", "time"],
|
|
39
|
+
separator: "powerline",
|
|
40
|
+
segmentOptions: {
|
|
41
|
+
model: { showThinkingLevel: true },
|
|
42
|
+
path: { abbreviate: true, maxLength: 50 },
|
|
43
|
+
git: { showBranch: true, showStaged: true, showUnstaged: true, showUntracked: true },
|
|
44
|
+
time: { format: "24h", showSeconds: false },
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
nerd: {
|
|
49
|
+
// Full preset with all Nerd Font icons
|
|
50
|
+
leftSegments: ["pi", "hostname", "model", "path", "git", "session", "subagents"],
|
|
51
|
+
rightSegments: [
|
|
52
|
+
"token_in",
|
|
53
|
+
"token_out",
|
|
54
|
+
"cache_read",
|
|
55
|
+
"cache_write",
|
|
56
|
+
"cost",
|
|
57
|
+
"context_pct",
|
|
58
|
+
"context_total",
|
|
59
|
+
"time_spent",
|
|
60
|
+
"time",
|
|
61
|
+
],
|
|
62
|
+
separator: "powerline",
|
|
63
|
+
segmentOptions: {
|
|
64
|
+
model: { showThinkingLevel: true },
|
|
65
|
+
path: { abbreviate: true, maxLength: 60 },
|
|
66
|
+
git: { showBranch: true, showStaged: true, showUnstaged: true, showUntracked: true },
|
|
67
|
+
time: { format: "24h", showSeconds: true },
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
ascii: {
|
|
72
|
+
// No Nerd Font dependencies
|
|
73
|
+
leftSegments: ["model", "path", "git"],
|
|
74
|
+
rightSegments: ["token_total", "cost", "context_pct"],
|
|
75
|
+
separator: "ascii",
|
|
76
|
+
segmentOptions: {
|
|
77
|
+
model: { showThinkingLevel: true },
|
|
78
|
+
path: { abbreviate: true, maxLength: 40 },
|
|
79
|
+
git: { showBranch: true, showStaged: true, showUnstaged: true, showUntracked: true },
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
custom: {
|
|
84
|
+
// User-defined - these are just defaults that get overridden
|
|
85
|
+
leftSegments: ["model", "path", "git"],
|
|
86
|
+
rightSegments: ["token_total", "cost", "context_pct"],
|
|
87
|
+
separator: "powerline-thin",
|
|
88
|
+
segmentOptions: {},
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export function getPreset(name: StatusLinePreset): PresetDef {
|
|
93
|
+
return STATUS_LINE_PRESETS[name] ?? STATUS_LINE_PRESETS.default;
|
|
94
|
+
}
|