@bastani/atomic 0.6.6-1 → 0.6.7-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/README.md +22 -16
- package/dist/sdk/components/compact-switcher.d.ts.map +1 -1
- package/dist/sdk/components/connectors.d.ts +1 -0
- package/dist/sdk/components/connectors.d.ts.map +1 -1
- package/dist/sdk/components/edge.d.ts +1 -1
- package/dist/sdk/components/edge.d.ts.map +1 -1
- package/dist/sdk/components/graph-theme.d.ts.map +1 -1
- package/dist/sdk/components/header.d.ts.map +1 -1
- package/dist/sdk/components/node-card.d.ts.map +1 -1
- package/dist/sdk/components/orchestrator-panel.d.ts +7 -1
- package/dist/sdk/components/orchestrator-panel.d.ts.map +1 -1
- package/dist/sdk/components/renderer-background.d.ts +9 -0
- package/dist/sdk/components/renderer-background.d.ts.map +1 -0
- package/dist/sdk/components/session-graph-panel.d.ts.map +1 -1
- package/dist/sdk/components/statusline.d.ts.map +1 -1
- package/dist/sdk/components/tui-diagnostics.d.ts +56 -0
- package/dist/sdk/components/tui-diagnostics.d.ts.map +1 -0
- package/dist/sdk/components/workflow-picker-panel.d.ts +2 -1
- package/dist/sdk/components/workflow-picker-panel.d.ts.map +1 -1
- package/dist/sdk/providers/copilot.d.ts +3 -2
- package/dist/sdk/providers/copilot.d.ts.map +1 -1
- package/dist/sdk/runtime/executor.d.ts.map +1 -1
- package/dist/sdk/runtime/theme.d.ts +4 -0
- package/dist/sdk/runtime/theme.d.ts.map +1 -1
- package/dist/theme/colors.d.ts +2 -0
- package/dist/theme/colors.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/cli.ts +3 -3
- package/src/commands/cli/chat/index.ts +10 -4
- package/src/commands/cli/management-commands.ts +4 -3
- package/src/commands/cli/session.test.ts +79 -6
- package/src/commands/cli/session.ts +65 -9
- package/src/completions/fish.ts +9 -3
- package/src/completions/powershell.ts +27 -3
- package/src/completions/zsh.ts +9 -2
- package/src/sdk/components/compact-switcher.tsx +10 -5
- package/src/sdk/components/connectors.ts +4 -0
- package/src/sdk/components/edge.tsx +5 -3
- package/src/sdk/components/graph-theme.ts +2 -3
- package/src/sdk/components/header.tsx +21 -9
- package/src/sdk/components/node-card.tsx +13 -7
- package/src/sdk/components/orchestrator-panel.tsx +47 -2
- package/src/sdk/components/renderer-background.ts +49 -0
- package/src/sdk/components/session-graph-panel.tsx +9 -2
- package/src/sdk/components/statusline.tsx +26 -22
- package/src/sdk/components/tui-diagnostics.ts +273 -0
- package/src/sdk/components/workflow-picker-panel.tsx +33 -22
- package/src/sdk/providers/copilot.ts +10 -4
- package/src/sdk/runtime/executor.ts +28 -1
- package/src/sdk/runtime/theme.ts +28 -36
- package/src/services/system/install-ui.ts +16 -17
- package/src/theme/colors.ts +14 -9
- package/src/theme/logo.ts +23 -12
|
@@ -43,6 +43,11 @@ import { useLatest } from "./hooks.ts";
|
|
|
43
43
|
import { resolveTheme, type TerminalTheme } from "../runtime/theme.ts";
|
|
44
44
|
import type { AgentType, WorkflowInput, WorkflowDefinition, Registry } from "../types.ts";
|
|
45
45
|
import { ErrorBoundary } from "./error-boundary.tsx";
|
|
46
|
+
import {
|
|
47
|
+
requestRendererBackgroundRepaint,
|
|
48
|
+
resetRendererTerminalBackground,
|
|
49
|
+
setRendererBackground,
|
|
50
|
+
} from "./renderer-background.ts";
|
|
46
51
|
|
|
47
52
|
// ─── Theme ──────────────────────────────────────
|
|
48
53
|
// The picker uses a slightly extended palette vs. the base terminal theme:
|
|
@@ -68,26 +73,21 @@ export interface PickerTheme {
|
|
|
68
73
|
borderActive: string;
|
|
69
74
|
}
|
|
70
75
|
|
|
71
|
-
export function buildPickerTheme(base: TerminalTheme
|
|
72
|
-
// For dark mode the prototype values track Catppuccin Mocha. For light
|
|
73
|
-
// mode we derive muted variants from the base palette — the specific
|
|
74
|
-
// extras (`info`, `mauve`, the three-level background ladder) have no
|
|
75
|
-
// direct entries in `TerminalTheme`, so we pick close-enough Catppuccin
|
|
76
|
-
// values to keep the picker visually consistent with the orchestrator.
|
|
76
|
+
export function buildPickerTheme(base: TerminalTheme): PickerTheme {
|
|
77
77
|
return {
|
|
78
78
|
background: base.bg,
|
|
79
|
-
backgroundPanel:
|
|
80
|
-
backgroundElement:
|
|
79
|
+
backgroundPanel: base.backgroundPanel,
|
|
80
|
+
backgroundElement: base.backgroundElement,
|
|
81
81
|
surface: base.surface,
|
|
82
82
|
text: base.text,
|
|
83
|
-
textMuted:
|
|
83
|
+
textMuted: base.textMuted,
|
|
84
84
|
textDim: base.dim,
|
|
85
85
|
primary: base.accent,
|
|
86
86
|
success: base.success,
|
|
87
87
|
error: base.error,
|
|
88
88
|
warning: base.warning,
|
|
89
|
-
info:
|
|
90
|
-
mauve:
|
|
89
|
+
info: base.info,
|
|
90
|
+
mauve: base.mauve,
|
|
91
91
|
border: base.borderDim,
|
|
92
92
|
borderActive: base.border,
|
|
93
93
|
};
|
|
@@ -324,7 +324,7 @@ const WorkflowList = memo(function WorkflowList({
|
|
|
324
324
|
|
|
325
325
|
if (rows.length === 0) {
|
|
326
326
|
return (
|
|
327
|
-
<box paddingLeft={2} paddingTop={2}>
|
|
327
|
+
<box paddingLeft={2} paddingTop={2} backgroundColor={theme.backgroundPanel}>
|
|
328
328
|
<text>
|
|
329
329
|
<span fg={theme.textDim}>no matches</span>
|
|
330
330
|
</text>
|
|
@@ -333,7 +333,7 @@ const WorkflowList = memo(function WorkflowList({
|
|
|
333
333
|
}
|
|
334
334
|
|
|
335
335
|
return (
|
|
336
|
-
<box flexDirection="column">
|
|
336
|
+
<box flexDirection="column" backgroundColor={theme.backgroundPanel}>
|
|
337
337
|
{rows.map((row, i) => {
|
|
338
338
|
if (row.kind === "section") {
|
|
339
339
|
const ag = row.agent;
|
|
@@ -343,6 +343,7 @@ const WorkflowList = memo(function WorkflowList({
|
|
|
343
343
|
height={2}
|
|
344
344
|
paddingTop={1}
|
|
345
345
|
paddingLeft={2}
|
|
346
|
+
backgroundColor={theme.backgroundPanel}
|
|
346
347
|
>
|
|
347
348
|
<text>
|
|
348
349
|
<span fg={theme[AGENT_COLOR[ag]]}>
|
|
@@ -362,7 +363,7 @@ const WorkflowList = memo(function WorkflowList({
|
|
|
362
363
|
key={`wf-${wf.agent}-${wf.name}`}
|
|
363
364
|
height={1}
|
|
364
365
|
flexDirection="row"
|
|
365
|
-
backgroundColor={isFocused ? theme.border :
|
|
366
|
+
backgroundColor={isFocused ? theme.border : theme.backgroundPanel}
|
|
366
367
|
paddingLeft={1}
|
|
367
368
|
paddingRight={2}
|
|
368
369
|
>
|
|
@@ -545,6 +546,7 @@ function TextAreaContent({
|
|
|
545
546
|
onInput: (value: string) => void;
|
|
546
547
|
}) {
|
|
547
548
|
const theme = usePickerTheme();
|
|
549
|
+
const backgroundColor = focused ? theme.backgroundPanel : theme.backgroundElement;
|
|
548
550
|
const instanceRef = useRef<TextareaRenderable | null>(null);
|
|
549
551
|
const onInputRef = useLatest(onInput);
|
|
550
552
|
const lastTextRef = useRef(value);
|
|
@@ -593,8 +595,8 @@ function TextAreaContent({
|
|
|
593
595
|
placeholder={placeholder}
|
|
594
596
|
focused={focused}
|
|
595
597
|
textColor={theme.text}
|
|
596
|
-
backgroundColor=
|
|
597
|
-
focusedBackgroundColor=
|
|
598
|
+
backgroundColor={backgroundColor}
|
|
599
|
+
focusedBackgroundColor={backgroundColor}
|
|
598
600
|
focusedTextColor={theme.text}
|
|
599
601
|
placeholderColor={theme.textDim}
|
|
600
602
|
wrapMode="word"
|
|
@@ -616,6 +618,7 @@ function StringContent({
|
|
|
616
618
|
onInput: (value: string) => void;
|
|
617
619
|
}) {
|
|
618
620
|
const theme = usePickerTheme();
|
|
621
|
+
const backgroundColor = focused ? theme.backgroundPanel : theme.backgroundElement;
|
|
619
622
|
return (
|
|
620
623
|
<input
|
|
621
624
|
value={value}
|
|
@@ -623,8 +626,8 @@ function StringContent({
|
|
|
623
626
|
focused={focused}
|
|
624
627
|
onInput={onInput}
|
|
625
628
|
textColor={theme.text}
|
|
626
|
-
backgroundColor=
|
|
627
|
-
focusedBackgroundColor=
|
|
629
|
+
backgroundColor={backgroundColor}
|
|
630
|
+
focusedBackgroundColor={backgroundColor}
|
|
628
631
|
focusedTextColor={theme.text}
|
|
629
632
|
flexGrow={1}
|
|
630
633
|
/>
|
|
@@ -883,7 +886,7 @@ function InputPhase({
|
|
|
883
886
|
renderBefore={syncScrollFrame}
|
|
884
887
|
style={{
|
|
885
888
|
rootOptions: {
|
|
886
|
-
backgroundColor:
|
|
889
|
+
backgroundColor: theme.background,
|
|
887
890
|
border: false,
|
|
888
891
|
},
|
|
889
892
|
contentOptions: {
|
|
@@ -1481,6 +1484,7 @@ export class WorkflowPickerPanel {
|
|
|
1481
1484
|
private renderer: CliRenderer;
|
|
1482
1485
|
private root: Root;
|
|
1483
1486
|
private destroyed = false;
|
|
1487
|
+
private terminalBackgroundSynced: boolean;
|
|
1484
1488
|
private resolveSelection: ((r: WorkflowPickerResult | null) => void) | null =
|
|
1485
1489
|
null;
|
|
1486
1490
|
private selectionPromise: Promise<WorkflowPickerResult | null>;
|
|
@@ -1488,14 +1492,17 @@ export class WorkflowPickerPanel {
|
|
|
1488
1492
|
private constructor(
|
|
1489
1493
|
renderer: CliRenderer,
|
|
1490
1494
|
options: WorkflowPickerPanelOptions,
|
|
1495
|
+
{ syncTerminalBackground = false }: { syncTerminalBackground?: boolean } = {},
|
|
1491
1496
|
) {
|
|
1492
1497
|
this.renderer = renderer;
|
|
1498
|
+
this.terminalBackgroundSynced = syncTerminalBackground;
|
|
1493
1499
|
this.selectionPromise = new Promise((resolve) => {
|
|
1494
1500
|
this.resolveSelection = resolve;
|
|
1495
1501
|
});
|
|
1496
1502
|
|
|
1497
|
-
const
|
|
1498
|
-
|
|
1503
|
+
const termTheme = resolveTheme(renderer.themeMode);
|
|
1504
|
+
setRendererBackground(renderer, termTheme.bg, { syncTerminalDefault: syncTerminalBackground });
|
|
1505
|
+
const theme = buildPickerTheme(termTheme);
|
|
1499
1506
|
// Filter registry to only the workflows for the selected agent.
|
|
1500
1507
|
const workflows = options.registry
|
|
1501
1508
|
.list()
|
|
@@ -1528,6 +1535,7 @@ export class WorkflowPickerPanel {
|
|
|
1528
1535
|
/>
|
|
1529
1536
|
</ErrorBoundary>,
|
|
1530
1537
|
);
|
|
1538
|
+
requestRendererBackgroundRepaint(this.renderer);
|
|
1531
1539
|
}
|
|
1532
1540
|
|
|
1533
1541
|
/**
|
|
@@ -1550,7 +1558,7 @@ export class WorkflowPickerPanel {
|
|
|
1550
1558
|
"SIGFPE",
|
|
1551
1559
|
],
|
|
1552
1560
|
});
|
|
1553
|
-
return new WorkflowPickerPanel(renderer, options);
|
|
1561
|
+
return new WorkflowPickerPanel(renderer, options, { syncTerminalBackground: true });
|
|
1554
1562
|
}
|
|
1555
1563
|
|
|
1556
1564
|
/** Create with an externally-provided renderer (e.g. a test renderer). */
|
|
@@ -1580,6 +1588,9 @@ export class WorkflowPickerPanel {
|
|
|
1580
1588
|
this.resolveSelection = null;
|
|
1581
1589
|
}
|
|
1582
1590
|
try {
|
|
1591
|
+
if (this.terminalBackgroundSynced) {
|
|
1592
|
+
resetRendererTerminalBackground(this.renderer);
|
|
1593
|
+
}
|
|
1583
1594
|
this.renderer.destroy();
|
|
1584
1595
|
} catch (err) {
|
|
1585
1596
|
console.error("[WorkflowPickerPanel] destroy failed:", err);
|
|
@@ -94,11 +94,15 @@ export function enumeratePathCandidates(cmd: string, pathEnv: string): string[]
|
|
|
94
94
|
return results;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
export
|
|
97
|
+
export type CommandPathResolver = (cmd: string) => string | null;
|
|
98
|
+
|
|
99
|
+
export function resolveCopilotCliPath(
|
|
100
|
+
resolveCommandPath: CommandPathResolver = getCommandPath,
|
|
101
|
+
): string | undefined {
|
|
98
102
|
const envPath = process.env["COPILOT_CLI_PATH"];
|
|
99
103
|
if (envPath) return envPath;
|
|
100
104
|
|
|
101
|
-
const primary =
|
|
105
|
+
const primary = resolveCommandPath("copilot");
|
|
102
106
|
if (primary === null) return undefined;
|
|
103
107
|
if (!isCopilotShim(primary)) return primary;
|
|
104
108
|
|
|
@@ -119,11 +123,13 @@ export function resolveCopilotCliPath(): string | undefined {
|
|
|
119
123
|
* - `cliPath` from {@link resolveCopilotCliPath} when resolvable; omitted
|
|
120
124
|
* otherwise so the SDK falls back to its bundled CLI.
|
|
121
125
|
*/
|
|
122
|
-
export function copilotSdkLaunchOptions(
|
|
126
|
+
export function copilotSdkLaunchOptions(
|
|
127
|
+
resolveCommandPath: CommandPathResolver = getCommandPath,
|
|
128
|
+
): CopilotClientOptions {
|
|
123
129
|
const options: CopilotClientOptions = {
|
|
124
130
|
env: copilotSubprocessEnv(),
|
|
125
131
|
};
|
|
126
|
-
const cliPath = resolveCopilotCliPath();
|
|
132
|
+
const cliPath = resolveCopilotCliPath(resolveCommandPath);
|
|
127
133
|
if (cliPath !== undefined) {
|
|
128
134
|
options.cliPath = cliPath;
|
|
129
135
|
}
|
|
@@ -504,7 +504,11 @@ export async function executeWorkflow(
|
|
|
504
504
|
const launcherExt = isWin ? "ps1" : "sh";
|
|
505
505
|
const launcherPath = join(sessionsBaseDir, `orchestrator.${launcherExt}`);
|
|
506
506
|
const logPath = join(sessionsBaseDir, "orchestrator.log");
|
|
507
|
-
const launcherEnvVars =
|
|
507
|
+
const launcherEnvVars = {
|
|
508
|
+
...(agent === "claude" ? atomicTempEnv() : {}),
|
|
509
|
+
...terminalCapabilityEnv(),
|
|
510
|
+
...workflowDiagnosticsEnv(),
|
|
511
|
+
};
|
|
508
512
|
|
|
509
513
|
// Inputs are passed through as base64-encoded JSON so long multiline
|
|
510
514
|
// text values survive shell quoting without any further escaping.
|
|
@@ -579,6 +583,29 @@ export async function executeWorkflow(
|
|
|
579
583
|
return { id: workflowRunId, tmuxSessionName };
|
|
580
584
|
}
|
|
581
585
|
|
|
586
|
+
function workflowDiagnosticsEnv(): Record<string, string> {
|
|
587
|
+
const keys = [
|
|
588
|
+
"ATOMIC_TUI_DIAGNOSTICS",
|
|
589
|
+
"ATOMIC_TUI_DIAGNOSTICS_DIR",
|
|
590
|
+
"ATOMIC_TUI_DIAGNOSTICS_INTERVAL_MS",
|
|
591
|
+
"ATOMIC_TUI_DIAGNOSTICS_MAX",
|
|
592
|
+
"ATOMIC_TUI_DIAGNOSTICS_OPENTUI_DUMP",
|
|
593
|
+
];
|
|
594
|
+
const env: Record<string, string> = {};
|
|
595
|
+
for (const key of keys) {
|
|
596
|
+
const value = process.env[key];
|
|
597
|
+
if (value !== undefined) env[key] = value;
|
|
598
|
+
}
|
|
599
|
+
return env;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function terminalCapabilityEnv(): Record<string, string> {
|
|
603
|
+
const env: Record<string, string> = {};
|
|
604
|
+
const colorterm = process.env.COLORTERM;
|
|
605
|
+
if (colorterm !== undefined) env.COLORTERM = colorterm;
|
|
606
|
+
return env;
|
|
607
|
+
}
|
|
608
|
+
|
|
582
609
|
/**
|
|
583
610
|
* Print a short banner telling the user the workflow is running in the
|
|
584
611
|
* background and how to attach to it. Written to stdout so scripts can
|
package/src/sdk/runtime/theme.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { ThemeMode } from "@opentui/core";
|
|
11
|
+
import { flavors, type CatppuccinFlavor } from "@catppuccin/palette";
|
|
11
12
|
|
|
12
13
|
// ---------------------------------------------------------------------------
|
|
13
14
|
// Theme type
|
|
@@ -15,56 +16,47 @@ import type { ThemeMode } from "@opentui/core";
|
|
|
15
16
|
|
|
16
17
|
export interface TerminalTheme {
|
|
17
18
|
bg: string;
|
|
19
|
+
backgroundPanel: string;
|
|
20
|
+
backgroundElement: string;
|
|
18
21
|
surface: string;
|
|
19
22
|
selection: string;
|
|
20
23
|
border: string;
|
|
21
24
|
borderDim: string;
|
|
22
25
|
accent: string;
|
|
23
26
|
text: string;
|
|
27
|
+
textMuted: string;
|
|
24
28
|
dim: string;
|
|
29
|
+
info: string;
|
|
25
30
|
success: string;
|
|
26
31
|
error: string;
|
|
27
32
|
warning: string;
|
|
28
33
|
mauve: string;
|
|
29
34
|
}
|
|
30
35
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
// ---------------------------------------------------------------------------
|
|
36
|
+
function buildTerminalTheme(flavor: CatppuccinFlavor): TerminalTheme {
|
|
37
|
+
const { colors } = flavor;
|
|
38
|
+
return {
|
|
39
|
+
bg: colors.base.hex,
|
|
40
|
+
backgroundPanel: colors.mantle.hex,
|
|
41
|
+
backgroundElement: colors.crust.hex,
|
|
42
|
+
surface: colors.surface0.hex,
|
|
43
|
+
selection: colors.surface1.hex,
|
|
44
|
+
border: colors.overlay0.hex,
|
|
45
|
+
borderDim: colors.surface2.hex,
|
|
46
|
+
accent: colors.blue.hex,
|
|
47
|
+
text: colors.text.hex,
|
|
48
|
+
textMuted: flavor.dark ? colors.subtext0.hex : colors.subtext1.hex,
|
|
49
|
+
dim: colors.overlay1.hex,
|
|
50
|
+
info: colors.sky.hex,
|
|
51
|
+
success: colors.green.hex,
|
|
52
|
+
error: colors.red.hex,
|
|
53
|
+
warning: colors.yellow.hex,
|
|
54
|
+
mauve: colors.mauve.hex,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
53
57
|
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
surface: "#ccd0da", // Surface0
|
|
57
|
-
selection: "#bcc0cc", // Surface1
|
|
58
|
-
border: "#9ca0b0", // Overlay0
|
|
59
|
-
borderDim: "#acb0be", // Surface2
|
|
60
|
-
accent: "#1e66f5", // Blue
|
|
61
|
-
text: "#4c4f69", // Text
|
|
62
|
-
dim: "#8c8fa1", // Overlay1
|
|
63
|
-
success: "#40a02b", // Green
|
|
64
|
-
error: "#d20f39", // Red
|
|
65
|
-
warning: "#df8e1d", // Yellow
|
|
66
|
-
mauve: "#8839ef", // Mauve
|
|
67
|
-
};
|
|
58
|
+
const CATPPUCCIN_MOCHA = buildTerminalTheme(flavors.mocha);
|
|
59
|
+
const CATPPUCCIN_LATTE = buildTerminalTheme(flavors.latte);
|
|
68
60
|
|
|
69
61
|
// ---------------------------------------------------------------------------
|
|
70
62
|
// Public API
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* library, just what auto-sync needs to stop being visually noisy.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
import { COLORS } from "../../theme/colors.ts";
|
|
25
|
+
import { COLORS, PALETTE, paletteRgb, type PaletteKey } from "../../theme/colors.ts";
|
|
26
26
|
import {
|
|
27
27
|
supportsTrueColor,
|
|
28
28
|
supports256Color,
|
|
@@ -34,9 +34,9 @@ const BAR_EMPTY = "・";
|
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* Semantic bar states mapped to Catppuccin Mocha colors:
|
|
37
|
-
* progress → Yellow
|
|
38
|
-
* success → Green
|
|
39
|
-
* error → Red
|
|
37
|
+
* progress → Yellow (warm accent; "in flight")
|
|
38
|
+
* success → Green (universal "completed")
|
|
39
|
+
* error → Red (universal "failed")
|
|
40
40
|
*
|
|
41
41
|
* The empty track stays dim regardless — only the filled portion carries
|
|
42
42
|
* the status signal, which keeps the bar legible while still telegraphing
|
|
@@ -44,17 +44,16 @@ const BAR_EMPTY = "・";
|
|
|
44
44
|
*/
|
|
45
45
|
type BarState = "progress" | "success" | "error";
|
|
46
46
|
|
|
47
|
+
const BAR_STATE_PALETTE: Record<BarState, PaletteKey> = {
|
|
48
|
+
progress: "warning",
|
|
49
|
+
success: "success",
|
|
50
|
+
error: "error",
|
|
51
|
+
};
|
|
52
|
+
|
|
47
53
|
function fillColor(state: BarState): string {
|
|
48
54
|
if (supportsTrueColor()) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return "\x1b[38;2;166;227;161m"; // Catppuccin Green #a6e3a1
|
|
52
|
-
case "error":
|
|
53
|
-
return "\x1b[38;2;243;139;168m"; // Catppuccin Red #f38ba8
|
|
54
|
-
case "progress":
|
|
55
|
-
default:
|
|
56
|
-
return "\x1b[38;2;249;226;175m"; // Catppuccin Yellow #f9e2af
|
|
57
|
-
}
|
|
55
|
+
const [r, g, b] = PALETTE[BAR_STATE_PALETTE[state]];
|
|
56
|
+
return `\x1b[38;2;${r};${g};${b}m`;
|
|
58
57
|
}
|
|
59
58
|
if (supports256Color()) {
|
|
60
59
|
switch (state) {
|
|
@@ -78,7 +77,7 @@ function fillColor(state: BarState): string {
|
|
|
78
77
|
}
|
|
79
78
|
}
|
|
80
79
|
|
|
81
|
-
type RGB = [number, number, number];
|
|
80
|
+
type RGB = readonly [number, number, number];
|
|
82
81
|
|
|
83
82
|
/**
|
|
84
83
|
* Gradient endpoints for the filled bar segment. Each state interpolates
|
|
@@ -88,12 +87,12 @@ type RGB = [number, number, number];
|
|
|
88
87
|
function gradientEndpoints(state: BarState): { start: RGB; end: RGB } {
|
|
89
88
|
switch (state) {
|
|
90
89
|
case "success":
|
|
91
|
-
return { start:
|
|
90
|
+
return { start: paletteRgb("teal"), end: paletteRgb("green") };
|
|
92
91
|
case "error":
|
|
93
|
-
return { start:
|
|
92
|
+
return { start: paletteRgb("maroon"), end: paletteRgb("red") };
|
|
94
93
|
case "progress":
|
|
95
94
|
default:
|
|
96
|
-
return { start:
|
|
95
|
+
return { start: paletteRgb("peach"), end: paletteRgb("yellow") };
|
|
97
96
|
}
|
|
98
97
|
}
|
|
99
98
|
|
package/src/theme/colors.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { supportsColor, supportsTrueColor } from "../services/system/detect.ts";
|
|
2
|
+
import { flavors, type ColorName } from "@catppuccin/palette";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* ANSI color and formatting codes for CLI output
|
|
@@ -31,20 +32,24 @@ export const COLORS = supportsColor() ? ANSI_CODES : NO_COLORS;
|
|
|
31
32
|
//
|
|
32
33
|
// Truecolor terminals get the full palette via 24-bit ANSI SGR; legacy
|
|
33
34
|
// terminals degrade to basic ANSI; NO_COLOR emits plain text.
|
|
34
|
-
// Hex values mirror .impeccable.md and src/sdk/runtime/theme.ts.
|
|
35
35
|
// ---------------------------------------------------------------------------
|
|
36
36
|
|
|
37
37
|
export type PaletteKey = "text" | "dim" | "accent" | "success" | "error" | "warning" | "mauve" | "info";
|
|
38
38
|
|
|
39
|
+
export function paletteRgb(name: ColorName): readonly [number, number, number] {
|
|
40
|
+
const { r, g, b } = flavors.mocha.colors[name].rgb;
|
|
41
|
+
return [r, g, b];
|
|
42
|
+
}
|
|
43
|
+
|
|
39
44
|
export const PALETTE: Record<PaletteKey, readonly [number, number, number]> = {
|
|
40
|
-
text:
|
|
41
|
-
dim:
|
|
42
|
-
accent:
|
|
43
|
-
success:
|
|
44
|
-
error:
|
|
45
|
-
warning:
|
|
46
|
-
mauve:
|
|
47
|
-
info:
|
|
45
|
+
text: paletteRgb("text"),
|
|
46
|
+
dim: paletteRgb("overlay1"),
|
|
47
|
+
accent: paletteRgb("blue"),
|
|
48
|
+
success: paletteRgb("green"),
|
|
49
|
+
error: paletteRgb("red"),
|
|
50
|
+
warning: paletteRgb("yellow"),
|
|
51
|
+
mauve: paletteRgb("mauve"),
|
|
52
|
+
info: paletteRgb("sky"),
|
|
48
53
|
};
|
|
49
54
|
|
|
50
55
|
export interface PaintOptions {
|
package/src/theme/logo.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
supports256Color,
|
|
10
10
|
supportsColor,
|
|
11
11
|
} from "../services/system/detect.ts";
|
|
12
|
+
import { flavors, type CatppuccinFlavor, type ColorName } from "@catppuccin/palette";
|
|
12
13
|
|
|
13
14
|
export const ATOMIC_BLOCK_LOGO = [
|
|
14
15
|
"█▀▀█ ▀▀█▀▀ █▀▀█ █▀▄▀█ ▀█▀ █▀▀",
|
|
@@ -16,17 +17,27 @@ export const ATOMIC_BLOCK_LOGO = [
|
|
|
16
17
|
"▀ ▀ ▀ ▀▀▀▀ ▀ ▀ ▀▀▀ ▀▀▀",
|
|
17
18
|
];
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
|
|
20
|
+
const GRADIENT_COLOR_NAMES = [
|
|
21
|
+
"rosewater",
|
|
22
|
+
"flamingo",
|
|
23
|
+
"pink",
|
|
24
|
+
"mauve",
|
|
25
|
+
"lavender",
|
|
26
|
+
"blue",
|
|
27
|
+
"sapphire",
|
|
28
|
+
"sky",
|
|
29
|
+
"teal",
|
|
30
|
+
] as const satisfies readonly ColorName[];
|
|
24
31
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
function gradientFromFlavor(flavor: CatppuccinFlavor): string[] {
|
|
33
|
+
return GRADIENT_COLOR_NAMES.map((name) => flavor.colors[name].hex);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Catppuccin gradient (dark terminal). */
|
|
37
|
+
export const GRADIENT_DARK = gradientFromFlavor(flavors.mocha);
|
|
38
|
+
|
|
39
|
+
/** Catppuccin gradient (light terminal). */
|
|
40
|
+
export const GRADIENT_LIGHT = gradientFromFlavor(flavors.latte);
|
|
30
41
|
|
|
31
42
|
/** 256-color approximation of the gradient. */
|
|
32
43
|
export const GRADIENT_256 = [224, 218, 219, 183, 147, 111, 117, 159, 115];
|
|
@@ -40,7 +51,7 @@ function hexToRgb(hex: string): [number, number, number] {
|
|
|
40
51
|
];
|
|
41
52
|
}
|
|
42
53
|
|
|
43
|
-
function interpolateHex(gradient: string[], t: number): [number, number, number] {
|
|
54
|
+
function interpolateHex(gradient: readonly string[], t: number): [number, number, number] {
|
|
44
55
|
const pos = Math.max(0, Math.min(1, t)) * (gradient.length - 1);
|
|
45
56
|
const lo = Math.floor(pos);
|
|
46
57
|
const hi = Math.min(lo + 1, gradient.length - 1);
|
|
@@ -60,7 +71,7 @@ function interpolate256(gradient: number[], t: number): number {
|
|
|
60
71
|
return gradient[lo]!;
|
|
61
72
|
}
|
|
62
73
|
|
|
63
|
-
export function colorizeLineTrueColor(line: string, gradient: string[]): string {
|
|
74
|
+
export function colorizeLineTrueColor(line: string, gradient: readonly string[]): string {
|
|
64
75
|
let out = "";
|
|
65
76
|
const len = line.length;
|
|
66
77
|
for (let i = 0; i < len; i++) {
|