@oh-my-pi/pi-coding-agent 3.14.0 → 3.15.1
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 +89 -0
- package/docs/theme.md +38 -5
- package/examples/sdk/11-sessions.ts +2 -2
- package/package.json +7 -4
- package/src/cli/file-processor.ts +51 -2
- package/src/cli/plugin-cli.ts +25 -19
- package/src/cli/update-cli.ts +4 -3
- package/src/core/agent-session.ts +31 -4
- package/src/core/compaction/branch-summarization.ts +4 -32
- package/src/core/compaction/compaction.ts +6 -84
- package/src/core/compaction/utils.ts +2 -3
- package/src/core/custom-tools/types.ts +2 -0
- package/src/core/export-html/index.ts +1 -1
- package/src/core/hooks/tool-wrapper.ts +0 -1
- package/src/core/hooks/types.ts +2 -2
- package/src/core/plugins/doctor.ts +9 -1
- package/src/core/sdk.ts +2 -1
- package/src/core/session-manager.ts +518 -40
- package/src/core/settings-manager.ts +174 -0
- package/src/core/system-prompt.ts +9 -14
- package/src/core/title-generator.ts +2 -8
- package/src/core/tools/ask.ts +19 -37
- package/src/core/tools/bash.ts +2 -37
- package/src/core/tools/edit.ts +2 -9
- package/src/core/tools/exa/render.ts +52 -48
- package/src/core/tools/find.ts +10 -8
- package/src/core/tools/grep.ts +45 -17
- package/src/core/tools/ls.ts +22 -2
- 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 +3 -0
- package/src/core/tools/lsp/index.ts +107 -55
- package/src/core/tools/lsp/render.ts +192 -79
- package/src/core/tools/lsp/types.ts +27 -0
- package/src/core/tools/lsp/utils.ts +62 -22
- package/src/core/tools/notebook.ts +9 -1
- package/src/core/tools/output.ts +37 -14
- package/src/core/tools/read.ts +349 -34
- package/src/core/tools/renderers.ts +290 -89
- package/src/core/tools/review.ts +12 -5
- package/src/core/tools/task/agents.ts +5 -5
- package/src/core/tools/task/commands.ts +3 -3
- package/src/core/tools/task/executor.ts +33 -1
- package/src/core/tools/task/index.ts +93 -6
- package/src/core/tools/task/render.ts +147 -66
- package/src/core/tools/task/types.ts +14 -9
- package/src/core/tools/web-fetch.ts +242 -103
- package/src/core/tools/web-search/index.ts +64 -20
- package/src/core/tools/web-search/providers/exa.ts +68 -172
- package/src/core/tools/web-search/render.ts +264 -74
- package/src/core/tools/write.ts +2 -8
- package/src/main.ts +10 -6
- package/src/modes/cleanup.ts +23 -0
- package/src/modes/index.ts +9 -4
- package/src/modes/interactive/components/bash-execution.ts +6 -3
- package/src/modes/interactive/components/branch-summary-message.ts +1 -1
- package/src/modes/interactive/components/compaction-summary-message.ts +1 -1
- package/src/modes/interactive/components/dynamic-border.ts +1 -1
- package/src/modes/interactive/components/extensions/extension-dashboard.ts +4 -5
- package/src/modes/interactive/components/extensions/extension-list.ts +18 -16
- package/src/modes/interactive/components/extensions/inspector-panel.ts +8 -8
- package/src/modes/interactive/components/hook-editor.ts +1 -0
- package/src/modes/interactive/components/hook-message.ts +2 -2
- package/src/modes/interactive/components/hook-selector.ts +1 -1
- package/src/modes/interactive/components/model-selector.ts +22 -9
- package/src/modes/interactive/components/oauth-selector.ts +20 -4
- package/src/modes/interactive/components/plugin-settings.ts +4 -2
- package/src/modes/interactive/components/session-selector.ts +9 -6
- package/src/modes/interactive/components/settings-defs.ts +285 -1
- package/src/modes/interactive/components/settings-selector.ts +176 -3
- 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 +172 -223
- package/src/modes/interactive/components/tool-execution.ts +446 -211
- package/src/modes/interactive/components/tree-selector.ts +17 -6
- package/src/modes/interactive/components/ttsr-notification.ts +4 -4
- package/src/modes/interactive/components/welcome.ts +27 -19
- package/src/modes/interactive/interactive-mode.ts +99 -13
- package/src/modes/interactive/theme/dark.json +3 -2
- package/src/modes/interactive/theme/defaults/alabaster.json +99 -0
- package/src/modes/interactive/theme/defaults/amethyst.json +103 -0
- package/src/modes/interactive/theme/defaults/anthracite.json +100 -0
- package/src/modes/interactive/theme/defaults/basalt.json +90 -0
- package/src/modes/interactive/theme/defaults/birch.json +101 -0
- package/src/modes/interactive/theme/defaults/dark-abyss.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-arctic.json +111 -0
- package/src/modes/interactive/theme/defaults/dark-aurora.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-catppuccin.json +106 -0
- package/src/modes/interactive/theme/defaults/dark-cavern.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-copper.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-cosmos.json +96 -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-eclipse.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-ember.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-equinox.json +96 -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-lavender.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-lunar.json +95 -0
- package/src/modes/interactive/theme/defaults/dark-midnight.json +94 -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-nebula.json +96 -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-rainforest.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-reef.json +97 -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-sakura.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-slate.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-solarized.json +96 -0
- package/src/modes/interactive/theme/defaults/dark-solstice.json +96 -0
- package/src/modes/interactive/theme/defaults/dark-starfall.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-sunset.json +106 -0
- package/src/modes/interactive/theme/defaults/dark-swamp.json +96 -0
- package/src/modes/interactive/theme/defaults/dark-synthwave.json +102 -0
- package/src/modes/interactive/theme/defaults/dark-taiga.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-terminal.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-tokyo-night.json +108 -0
- package/src/modes/interactive/theme/defaults/dark-tundra.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-twilight.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-volcanic.json +97 -0
- package/src/modes/interactive/theme/defaults/graphite.json +99 -0
- package/src/modes/interactive/theme/defaults/index.ts +195 -0
- package/src/modes/interactive/theme/defaults/light-arctic.json +106 -0
- package/src/modes/interactive/theme/defaults/light-aurora-day.json +97 -0
- package/src/modes/interactive/theme/defaults/light-canyon.json +97 -0
- package/src/modes/interactive/theme/defaults/light-catppuccin.json +105 -0
- package/src/modes/interactive/theme/defaults/light-cirrus.json +96 -0
- package/src/modes/interactive/theme/defaults/light-coral.json +94 -0
- package/src/modes/interactive/theme/defaults/light-cyberpunk.json +103 -0
- package/src/modes/interactive/theme/defaults/light-dawn.json +96 -0
- package/src/modes/interactive/theme/defaults/light-dunes.json +97 -0
- package/src/modes/interactive/theme/defaults/light-eucalyptus.json +94 -0
- package/src/modes/interactive/theme/defaults/light-forest.json +107 -0
- package/src/modes/interactive/theme/defaults/light-frost.json +94 -0
- package/src/modes/interactive/theme/defaults/light-github.json +114 -0
- package/src/modes/interactive/theme/defaults/light-glacier.json +97 -0
- package/src/modes/interactive/theme/defaults/light-gruvbox.json +115 -0
- package/src/modes/interactive/theme/defaults/light-haze.json +96 -0
- package/src/modes/interactive/theme/defaults/light-honeycomb.json +94 -0
- package/src/modes/interactive/theme/defaults/light-lagoon.json +97 -0
- package/src/modes/interactive/theme/defaults/light-lavender.json +94 -0
- package/src/modes/interactive/theme/defaults/light-meadow.json +97 -0
- package/src/modes/interactive/theme/defaults/light-mint.json +94 -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-opal.json +97 -0
- package/src/modes/interactive/theme/defaults/light-orchard.json +97 -0
- package/src/modes/interactive/theme/defaults/light-paper.json +94 -0
- package/src/modes/interactive/theme/defaults/light-prism.json +96 -0
- package/src/modes/interactive/theme/defaults/light-retro.json +105 -0
- package/src/modes/interactive/theme/defaults/light-sand.json +94 -0
- package/src/modes/interactive/theme/defaults/light-savanna.json +97 -0
- package/src/modes/interactive/theme/defaults/light-solarized.json +101 -0
- package/src/modes/interactive/theme/defaults/light-soleil.json +96 -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/defaults/light-wetland.json +97 -0
- package/src/modes/interactive/theme/defaults/light-zenith.json +95 -0
- package/src/modes/interactive/theme/defaults/limestone.json +100 -0
- package/src/modes/interactive/theme/defaults/mahogany.json +104 -0
- package/src/modes/interactive/theme/defaults/marble.json +99 -0
- package/src/modes/interactive/theme/defaults/obsidian.json +90 -0
- package/src/modes/interactive/theme/defaults/onyx.json +90 -0
- package/src/modes/interactive/theme/defaults/pearl.json +99 -0
- package/src/modes/interactive/theme/defaults/porcelain.json +90 -0
- package/src/modes/interactive/theme/defaults/quartz.json +102 -0
- package/src/modes/interactive/theme/defaults/sandstone.json +101 -0
- package/src/modes/interactive/theme/defaults/titanium.json +89 -0
- package/src/modes/interactive/theme/light.json +3 -2
- package/src/modes/interactive/theme/theme-schema.json +120 -4
- package/src/modes/interactive/theme/theme.ts +1228 -14
- package/src/prompts/branch-summary-preamble.md +3 -0
- package/src/prompts/branch-summary.md +28 -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/init.md +30 -0
- package/src/{core/tools/task/bundled-agents → prompts}/reviewer.md +6 -0
- package/src/prompts/summarization-system.md +3 -0
- package/src/prompts/system-prompt.md +27 -0
- package/src/{core/tools/task/bundled-agents → prompts}/task.md +2 -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/commands/init.md +0 -20
- /package/src/{core/tools/task/bundled-commands → prompts}/architect-plan.md +0 -0
- /package/src/{core/tools/task/bundled-agents → prompts}/browser.md +0 -0
- /package/src/{core/tools/task/bundled-agents → prompts}/explore.md +0 -0
- /package/src/{core/tools/task/bundled-commands → prompts}/implement-with-critic.md +0 -0
- /package/src/{core/tools/task/bundled-commands → prompts}/implement.md +0 -0
- /package/src/{core/tools/task/bundled-agents → prompts}/plan.md +0 -0
|
@@ -10,7 +10,13 @@
|
|
|
10
10
|
|
|
11
11
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
12
12
|
import { getCapabilities } from "@oh-my-pi/pi-tui";
|
|
13
|
-
import type {
|
|
13
|
+
import type {
|
|
14
|
+
SettingsManager,
|
|
15
|
+
StatusLinePreset,
|
|
16
|
+
StatusLineSeparatorStyle,
|
|
17
|
+
SymbolPreset,
|
|
18
|
+
} from "../../../core/settings-manager";
|
|
19
|
+
import { getPreset } from "./status-line/presets";
|
|
14
20
|
|
|
15
21
|
// Setting value types
|
|
16
22
|
export type SettingValue = boolean | string;
|
|
@@ -208,6 +214,20 @@ export const SETTINGS_DEFS: SettingDef[] = [
|
|
|
208
214
|
set: (sm, v) => sm.setTheme(v),
|
|
209
215
|
getOptions: () => [], // Filled dynamically from context
|
|
210
216
|
},
|
|
217
|
+
{
|
|
218
|
+
id: "symbolPreset",
|
|
219
|
+
tab: "config",
|
|
220
|
+
type: "submenu",
|
|
221
|
+
label: "Symbol preset",
|
|
222
|
+
description: "Icon/symbol style (overrides theme default)",
|
|
223
|
+
get: (sm) => sm.getSymbolPreset() ?? "unicode",
|
|
224
|
+
set: (sm, v) => sm.setSymbolPreset(v as SymbolPreset),
|
|
225
|
+
getOptions: () => [
|
|
226
|
+
{ value: "unicode", label: "Unicode", description: "Standard Unicode symbols (default)" },
|
|
227
|
+
{ value: "nerd", label: "Nerd Font", description: "Nerd Font icons (requires Nerd Font)" },
|
|
228
|
+
{ value: "ascii", label: "ASCII", description: "ASCII-only characters (maximum compatibility)" },
|
|
229
|
+
],
|
|
230
|
+
},
|
|
211
231
|
|
|
212
232
|
// LSP tab
|
|
213
233
|
{
|
|
@@ -293,6 +313,270 @@ export const SETTINGS_DEFS: SettingDef[] = [
|
|
|
293
313
|
get: (sm) => sm.getExaSettings().enableWebsets,
|
|
294
314
|
set: (sm, v) => sm.setExaWebsetsEnabled(v),
|
|
295
315
|
},
|
|
316
|
+
|
|
317
|
+
// Status Line tab
|
|
318
|
+
{
|
|
319
|
+
id: "statusLinePreset",
|
|
320
|
+
tab: "status",
|
|
321
|
+
type: "submenu",
|
|
322
|
+
label: "Preset",
|
|
323
|
+
description: "Pre-built status line configurations",
|
|
324
|
+
get: (sm) => sm.getStatusLinePreset(),
|
|
325
|
+
set: (sm, v) => sm.setStatusLinePreset(v as StatusLinePreset),
|
|
326
|
+
getOptions: () => [
|
|
327
|
+
{ value: "default", label: "Default", description: "Model, path, git, context, tokens, cost" },
|
|
328
|
+
{ value: "minimal", label: "Minimal", description: "Path and git only" },
|
|
329
|
+
{ value: "compact", label: "Compact", description: "Model, git, cost, context" },
|
|
330
|
+
{ value: "full", label: "Full", description: "All segments including time" },
|
|
331
|
+
{ value: "nerd", label: "Nerd", description: "Maximum info with Nerd Font icons" },
|
|
332
|
+
{ value: "ascii", label: "ASCII", description: "No special characters" },
|
|
333
|
+
{ value: "custom", label: "Custom", description: "User-defined segments" },
|
|
334
|
+
],
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
id: "statusLineSeparator",
|
|
338
|
+
tab: "status",
|
|
339
|
+
type: "submenu",
|
|
340
|
+
label: "Separator style",
|
|
341
|
+
description: "Style of separators between segments",
|
|
342
|
+
get: (sm) => {
|
|
343
|
+
const settings = sm.getStatusLineSettings();
|
|
344
|
+
if (settings.separator) return settings.separator;
|
|
345
|
+
return getPreset(sm.getStatusLinePreset()).separator;
|
|
346
|
+
},
|
|
347
|
+
set: (sm, v) => sm.setStatusLineSeparator(v as StatusLineSeparatorStyle),
|
|
348
|
+
getOptions: () => [
|
|
349
|
+
{ value: "powerline", label: "Powerline", description: "Solid arrows (requires Nerd Font)" },
|
|
350
|
+
{ value: "powerline-thin", label: "Thin chevron", description: "Thin arrows (requires Nerd Font)" },
|
|
351
|
+
{ value: "slash", label: "Slash", description: "Forward slashes" },
|
|
352
|
+
{ value: "pipe", label: "Pipe", description: "Vertical pipes" },
|
|
353
|
+
{ value: "block", label: "Block", description: "Solid blocks" },
|
|
354
|
+
{ value: "none", label: "None", description: "Space only" },
|
|
355
|
+
{ value: "ascii", label: "ASCII", description: "Greater-than signs" },
|
|
356
|
+
],
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
id: "statusLineShowHooks",
|
|
360
|
+
tab: "status",
|
|
361
|
+
type: "boolean",
|
|
362
|
+
label: "Show hook status",
|
|
363
|
+
description: "Display hook status messages below status line",
|
|
364
|
+
get: (sm) => sm.getStatusLineShowHookStatus(),
|
|
365
|
+
set: (sm, v) => sm.setStatusLineShowHookStatus(v),
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
id: "statusLineSegments",
|
|
369
|
+
tab: "status",
|
|
370
|
+
type: "submenu",
|
|
371
|
+
label: "Configure segments",
|
|
372
|
+
description: "Choose and arrange status line segments",
|
|
373
|
+
get: () => "configure...",
|
|
374
|
+
set: () => {}, // Handled specially
|
|
375
|
+
getOptions: () => [{ value: "open", label: "Open segment editor..." }],
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
id: "statusLineModelThinking",
|
|
379
|
+
tab: "status",
|
|
380
|
+
type: "enum",
|
|
381
|
+
label: "Model thinking level",
|
|
382
|
+
description: "Show thinking level in the model segment",
|
|
383
|
+
values: ["default", "on", "off"],
|
|
384
|
+
get: (sm) => {
|
|
385
|
+
const value = sm.getStatusLineSegmentOptions().model?.showThinkingLevel;
|
|
386
|
+
if (value === undefined) return "default";
|
|
387
|
+
return value ? "on" : "off";
|
|
388
|
+
},
|
|
389
|
+
set: (sm, v) => {
|
|
390
|
+
if (v === "default") {
|
|
391
|
+
sm.clearStatusLineSegmentOption("model", "showThinkingLevel");
|
|
392
|
+
} else {
|
|
393
|
+
sm.setStatusLineSegmentOption("model", "showThinkingLevel", v === "on");
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
id: "statusLinePathAbbreviate",
|
|
399
|
+
tab: "status",
|
|
400
|
+
type: "enum",
|
|
401
|
+
label: "Path abbreviate",
|
|
402
|
+
description: "Use ~ and strip home prefix in path segment",
|
|
403
|
+
values: ["default", "on", "off"],
|
|
404
|
+
get: (sm) => {
|
|
405
|
+
const value = sm.getStatusLineSegmentOptions().path?.abbreviate;
|
|
406
|
+
if (value === undefined) return "default";
|
|
407
|
+
return value ? "on" : "off";
|
|
408
|
+
},
|
|
409
|
+
set: (sm, v) => {
|
|
410
|
+
if (v === "default") {
|
|
411
|
+
sm.clearStatusLineSegmentOption("path", "abbreviate");
|
|
412
|
+
} else {
|
|
413
|
+
sm.setStatusLineSegmentOption("path", "abbreviate", v === "on");
|
|
414
|
+
}
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
id: "statusLinePathMaxLength",
|
|
419
|
+
tab: "status",
|
|
420
|
+
type: "submenu",
|
|
421
|
+
label: "Path max length",
|
|
422
|
+
description: "Maximum length for displayed path",
|
|
423
|
+
get: (sm) => {
|
|
424
|
+
const value = sm.getStatusLineSegmentOptions().path?.maxLength;
|
|
425
|
+
return typeof value === "number" ? String(value) : "default";
|
|
426
|
+
},
|
|
427
|
+
set: (sm, v) => {
|
|
428
|
+
if (v === "default") {
|
|
429
|
+
sm.clearStatusLineSegmentOption("path", "maxLength");
|
|
430
|
+
} else {
|
|
431
|
+
sm.setStatusLineSegmentOption("path", "maxLength", Number.parseInt(v, 10));
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
getOptions: () => [
|
|
435
|
+
{ value: "default", label: "Preset default" },
|
|
436
|
+
{ value: "20", label: "20" },
|
|
437
|
+
{ value: "30", label: "30" },
|
|
438
|
+
{ value: "40", label: "40" },
|
|
439
|
+
{ value: "50", label: "50" },
|
|
440
|
+
{ value: "60", label: "60" },
|
|
441
|
+
{ value: "80", label: "80" },
|
|
442
|
+
],
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
id: "statusLinePathStripWorkPrefix",
|
|
446
|
+
tab: "status",
|
|
447
|
+
type: "enum",
|
|
448
|
+
label: "Path strip /work",
|
|
449
|
+
description: "Strip /work prefix in path segment",
|
|
450
|
+
values: ["default", "on", "off"],
|
|
451
|
+
get: (sm) => {
|
|
452
|
+
const value = sm.getStatusLineSegmentOptions().path?.stripWorkPrefix;
|
|
453
|
+
if (value === undefined) return "default";
|
|
454
|
+
return value ? "on" : "off";
|
|
455
|
+
},
|
|
456
|
+
set: (sm, v) => {
|
|
457
|
+
if (v === "default") {
|
|
458
|
+
sm.clearStatusLineSegmentOption("path", "stripWorkPrefix");
|
|
459
|
+
} else {
|
|
460
|
+
sm.setStatusLineSegmentOption("path", "stripWorkPrefix", v === "on");
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
id: "statusLineGitShowBranch",
|
|
466
|
+
tab: "status",
|
|
467
|
+
type: "enum",
|
|
468
|
+
label: "Git show branch",
|
|
469
|
+
description: "Show branch name in git segment",
|
|
470
|
+
values: ["default", "on", "off"],
|
|
471
|
+
get: (sm) => {
|
|
472
|
+
const value = sm.getStatusLineSegmentOptions().git?.showBranch;
|
|
473
|
+
if (value === undefined) return "default";
|
|
474
|
+
return value ? "on" : "off";
|
|
475
|
+
},
|
|
476
|
+
set: (sm, v) => {
|
|
477
|
+
if (v === "default") {
|
|
478
|
+
sm.clearStatusLineSegmentOption("git", "showBranch");
|
|
479
|
+
} else {
|
|
480
|
+
sm.setStatusLineSegmentOption("git", "showBranch", v === "on");
|
|
481
|
+
}
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
id: "statusLineGitShowStaged",
|
|
486
|
+
tab: "status",
|
|
487
|
+
type: "enum",
|
|
488
|
+
label: "Git show staged",
|
|
489
|
+
description: "Show staged file count in git segment",
|
|
490
|
+
values: ["default", "on", "off"],
|
|
491
|
+
get: (sm) => {
|
|
492
|
+
const value = sm.getStatusLineSegmentOptions().git?.showStaged;
|
|
493
|
+
if (value === undefined) return "default";
|
|
494
|
+
return value ? "on" : "off";
|
|
495
|
+
},
|
|
496
|
+
set: (sm, v) => {
|
|
497
|
+
if (v === "default") {
|
|
498
|
+
sm.clearStatusLineSegmentOption("git", "showStaged");
|
|
499
|
+
} else {
|
|
500
|
+
sm.setStatusLineSegmentOption("git", "showStaged", v === "on");
|
|
501
|
+
}
|
|
502
|
+
},
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
id: "statusLineGitShowUnstaged",
|
|
506
|
+
tab: "status",
|
|
507
|
+
type: "enum",
|
|
508
|
+
label: "Git show unstaged",
|
|
509
|
+
description: "Show unstaged file count in git segment",
|
|
510
|
+
values: ["default", "on", "off"],
|
|
511
|
+
get: (sm) => {
|
|
512
|
+
const value = sm.getStatusLineSegmentOptions().git?.showUnstaged;
|
|
513
|
+
if (value === undefined) return "default";
|
|
514
|
+
return value ? "on" : "off";
|
|
515
|
+
},
|
|
516
|
+
set: (sm, v) => {
|
|
517
|
+
if (v === "default") {
|
|
518
|
+
sm.clearStatusLineSegmentOption("git", "showUnstaged");
|
|
519
|
+
} else {
|
|
520
|
+
sm.setStatusLineSegmentOption("git", "showUnstaged", v === "on");
|
|
521
|
+
}
|
|
522
|
+
},
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
id: "statusLineGitShowUntracked",
|
|
526
|
+
tab: "status",
|
|
527
|
+
type: "enum",
|
|
528
|
+
label: "Git show untracked",
|
|
529
|
+
description: "Show untracked file count in git segment",
|
|
530
|
+
values: ["default", "on", "off"],
|
|
531
|
+
get: (sm) => {
|
|
532
|
+
const value = sm.getStatusLineSegmentOptions().git?.showUntracked;
|
|
533
|
+
if (value === undefined) return "default";
|
|
534
|
+
return value ? "on" : "off";
|
|
535
|
+
},
|
|
536
|
+
set: (sm, v) => {
|
|
537
|
+
if (v === "default") {
|
|
538
|
+
sm.clearStatusLineSegmentOption("git", "showUntracked");
|
|
539
|
+
} else {
|
|
540
|
+
sm.setStatusLineSegmentOption("git", "showUntracked", v === "on");
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
id: "statusLineTimeFormat",
|
|
546
|
+
tab: "status",
|
|
547
|
+
type: "enum",
|
|
548
|
+
label: "Time format",
|
|
549
|
+
description: "Clock segment time format",
|
|
550
|
+
values: ["default", "12h", "24h"],
|
|
551
|
+
get: (sm) => sm.getStatusLineSegmentOptions().time?.format ?? "default",
|
|
552
|
+
set: (sm, v) => {
|
|
553
|
+
if (v === "default") {
|
|
554
|
+
sm.clearStatusLineSegmentOption("time", "format");
|
|
555
|
+
} else {
|
|
556
|
+
sm.setStatusLineSegmentOption("time", "format", v);
|
|
557
|
+
}
|
|
558
|
+
},
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
id: "statusLineTimeShowSeconds",
|
|
562
|
+
tab: "status",
|
|
563
|
+
type: "enum",
|
|
564
|
+
label: "Time show seconds",
|
|
565
|
+
description: "Include seconds in clock segment",
|
|
566
|
+
values: ["default", "on", "off"],
|
|
567
|
+
get: (sm) => {
|
|
568
|
+
const value = sm.getStatusLineSegmentOptions().time?.showSeconds;
|
|
569
|
+
if (value === undefined) return "default";
|
|
570
|
+
return value ? "on" : "off";
|
|
571
|
+
},
|
|
572
|
+
set: (sm, v) => {
|
|
573
|
+
if (v === "default") {
|
|
574
|
+
sm.clearStatusLineSegmentOption("time", "showSeconds");
|
|
575
|
+
} else {
|
|
576
|
+
sm.setStatusLineSegmentOption("time", "showSeconds", v === "on");
|
|
577
|
+
}
|
|
578
|
+
},
|
|
579
|
+
},
|
|
296
580
|
];
|
|
297
581
|
|
|
298
582
|
/**
|
|
@@ -16,11 +16,13 @@ import {
|
|
|
16
16
|
type TabBarTheme,
|
|
17
17
|
Text,
|
|
18
18
|
} from "@oh-my-pi/pi-tui";
|
|
19
|
-
import type { SettingsManager } from "../../../core/settings-manager";
|
|
19
|
+
import type { SettingsManager, StatusLineSettings } from "../../../core/settings-manager";
|
|
20
20
|
import { getSelectListTheme, getSettingsListTheme, theme } from "../theme/theme";
|
|
21
21
|
import { DynamicBorder } from "./dynamic-border";
|
|
22
22
|
import { PluginSettingsComponent } from "./plugin-settings";
|
|
23
23
|
import { getSettingsForTab, type SettingDef } from "./settings-defs";
|
|
24
|
+
import { getPreset } from "./status-line/presets";
|
|
25
|
+
import { StatusLineSegmentEditorComponent } from "./status-line-segment-editor";
|
|
24
26
|
|
|
25
27
|
function getTabBarTheme(): TabBarTheme {
|
|
26
28
|
return {
|
|
@@ -36,6 +38,8 @@ function getTabBarTheme(): TabBarTheme {
|
|
|
36
38
|
*/
|
|
37
39
|
class SelectSubmenu extends Container {
|
|
38
40
|
private selectList: SelectList;
|
|
41
|
+
private previewText: Text | null = null;
|
|
42
|
+
private getPreview: (() => string) | undefined;
|
|
39
43
|
|
|
40
44
|
constructor(
|
|
41
45
|
title: string,
|
|
@@ -45,8 +49,10 @@ class SelectSubmenu extends Container {
|
|
|
45
49
|
onSelect: (value: string) => void,
|
|
46
50
|
onCancel: () => void,
|
|
47
51
|
onSelectionChange?: (value: string) => void,
|
|
52
|
+
getPreview?: () => string,
|
|
48
53
|
) {
|
|
49
54
|
super();
|
|
55
|
+
this.getPreview = getPreview;
|
|
50
56
|
|
|
51
57
|
// Title
|
|
52
58
|
this.addChild(new Text(theme.bold(theme.fg("accent", title)), 0, 0));
|
|
@@ -57,6 +63,14 @@ class SelectSubmenu extends Container {
|
|
|
57
63
|
this.addChild(new Text(theme.fg("muted", description), 0, 0));
|
|
58
64
|
}
|
|
59
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
|
+
|
|
60
74
|
// Spacer
|
|
61
75
|
this.addChild(new Spacer(1));
|
|
62
76
|
|
|
@@ -78,6 +92,8 @@ class SelectSubmenu extends Container {
|
|
|
78
92
|
if (onSelectionChange) {
|
|
79
93
|
this.selectList.onSelectionChange = (item) => {
|
|
80
94
|
onSelectionChange(item.value);
|
|
95
|
+
// Update preview after the preview callback has applied changes
|
|
96
|
+
this.updatePreview();
|
|
81
97
|
};
|
|
82
98
|
}
|
|
83
99
|
|
|
@@ -88,6 +104,12 @@ class SelectSubmenu extends Container {
|
|
|
88
104
|
this.addChild(new Text(theme.fg("dim", " Enter to select · Esc to go back"), 0, 0));
|
|
89
105
|
}
|
|
90
106
|
|
|
107
|
+
private updatePreview(): void {
|
|
108
|
+
if (this.previewText && this.getPreview) {
|
|
109
|
+
this.previewText.setText(this.getPreview());
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
91
113
|
handleInput(data: string): void {
|
|
92
114
|
this.selectList.handleInput(data);
|
|
93
115
|
}
|
|
@@ -97,6 +119,7 @@ type TabId = string;
|
|
|
97
119
|
|
|
98
120
|
const SETTINGS_TABS: Tab[] = [
|
|
99
121
|
{ id: "config", label: "Config" },
|
|
122
|
+
{ id: "status", label: "Status" },
|
|
100
123
|
{ id: "lsp", label: "LSP" },
|
|
101
124
|
{ id: "exa", label: "Exa" },
|
|
102
125
|
{ id: "plugins", label: "Plugins" },
|
|
@@ -128,6 +151,10 @@ export interface SettingsCallbacks {
|
|
|
128
151
|
onChange: SettingChangeHandler;
|
|
129
152
|
/** Called for theme preview while browsing */
|
|
130
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;
|
|
131
158
|
/** Called when plugins change */
|
|
132
159
|
onPluginsChanged?: () => void;
|
|
133
160
|
/** Called when settings panel is closed */
|
|
@@ -143,6 +170,9 @@ export class SettingsSelectorComponent extends Container {
|
|
|
143
170
|
private currentList: SettingsList | null = null;
|
|
144
171
|
private currentSubmenu: Container | null = null;
|
|
145
172
|
private pluginComponent: PluginSettingsComponent | null = null;
|
|
173
|
+
private statusPreviewContainer: Container | null = null;
|
|
174
|
+
private statusPreviewText: Text | null = null;
|
|
175
|
+
private currentTabId: TabId = "config";
|
|
146
176
|
|
|
147
177
|
private settingsManager: SettingsManager;
|
|
148
178
|
private context: SettingsRuntimeContext;
|
|
@@ -176,6 +206,8 @@ export class SettingsSelectorComponent extends Container {
|
|
|
176
206
|
}
|
|
177
207
|
|
|
178
208
|
private switchToTab(tabId: TabId): void {
|
|
209
|
+
this.currentTabId = tabId;
|
|
210
|
+
|
|
179
211
|
// Remove current content
|
|
180
212
|
if (this.currentList) {
|
|
181
213
|
this.removeChild(this.currentList);
|
|
@@ -185,6 +217,11 @@ export class SettingsSelectorComponent extends Container {
|
|
|
185
217
|
this.removeChild(this.pluginComponent);
|
|
186
218
|
this.pluginComponent = null;
|
|
187
219
|
}
|
|
220
|
+
if (this.statusPreviewContainer) {
|
|
221
|
+
this.removeChild(this.statusPreviewContainer);
|
|
222
|
+
this.statusPreviewContainer = null;
|
|
223
|
+
this.statusPreviewText = null;
|
|
224
|
+
}
|
|
188
225
|
|
|
189
226
|
// Remove bottom border temporarily
|
|
190
227
|
const bottomBorder = this.children[this.children.length - 1];
|
|
@@ -262,6 +299,11 @@ export class SettingsSelectorComponent extends Container {
|
|
|
262
299
|
currentValue: string,
|
|
263
300
|
done: (value?: string) => void,
|
|
264
301
|
): Container {
|
|
302
|
+
// Special case: segment editor
|
|
303
|
+
if (def.id === "statusLineSegments") {
|
|
304
|
+
return this.createSegmentEditor(done);
|
|
305
|
+
}
|
|
306
|
+
|
|
265
307
|
let options = def.getOptions(this.settingsManager);
|
|
266
308
|
|
|
267
309
|
// Special case: inject runtime options
|
|
@@ -274,8 +316,55 @@ export class SettingsSelectorComponent extends Container {
|
|
|
274
316
|
options = this.context.availableThemes.map((t) => ({ value: t, label: t }));
|
|
275
317
|
}
|
|
276
318
|
|
|
277
|
-
|
|
278
|
-
|
|
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;
|
|
279
368
|
|
|
280
369
|
return new SelectSubmenu(
|
|
281
370
|
def.label,
|
|
@@ -294,9 +383,48 @@ export class SettingsSelectorComponent extends Container {
|
|
|
294
383
|
done();
|
|
295
384
|
},
|
|
296
385
|
onPreview,
|
|
386
|
+
getPreview,
|
|
297
387
|
);
|
|
298
388
|
}
|
|
299
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
|
+
|
|
300
428
|
/**
|
|
301
429
|
* Show a settings tab using definitions.
|
|
302
430
|
*/
|
|
@@ -311,6 +439,17 @@ export class SettingsSelectorComponent extends Container {
|
|
|
311
439
|
}
|
|
312
440
|
}
|
|
313
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
|
+
|
|
314
453
|
this.currentList = new SettingsList(
|
|
315
454
|
items,
|
|
316
455
|
10,
|
|
@@ -324,6 +463,11 @@ export class SettingsSelectorComponent extends Container {
|
|
|
324
463
|
const boolValue = newValue === "true";
|
|
325
464
|
def.set(this.settingsManager, boolValue);
|
|
326
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
|
+
}
|
|
327
471
|
} else if (def.type === "enum") {
|
|
328
472
|
def.set(this.settingsManager, newValue);
|
|
329
473
|
this.callbacks.onChange(id, newValue);
|
|
@@ -336,6 +480,35 @@ export class SettingsSelectorComponent extends Container {
|
|
|
336
480
|
this.addChild(this.currentList);
|
|
337
481
|
}
|
|
338
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
|
+
|
|
339
512
|
private showPluginsTab(): void {
|
|
340
513
|
this.pluginComponent = new PluginSettingsComponent(this.context.cwd, {
|
|
341
514
|
onClose: () => this.callbacks.onCancel(),
|