pi-umbra 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.
Files changed (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +27 -0
  3. package/node_modules/pi-umbra-ask/LICENSE +21 -0
  4. package/node_modules/pi-umbra-ask/README.md +21 -0
  5. package/node_modules/pi-umbra-ask/checks/umbra-ask.check.ts +132 -0
  6. package/node_modules/pi-umbra-ask/extensions/umbra-ask.ts +484 -0
  7. package/node_modules/pi-umbra-ask/lib/clean.ts +25 -0
  8. package/node_modules/pi-umbra-ask/package.json +37 -0
  9. package/node_modules/pi-umbra-copy-chat/LICENSE +21 -0
  10. package/node_modules/pi-umbra-copy-chat/README.md +19 -0
  11. package/node_modules/pi-umbra-copy-chat/extensions/umbra-copy-chat.ts +154 -0
  12. package/node_modules/pi-umbra-copy-chat/package.json +32 -0
  13. package/node_modules/pi-umbra-help/LICENSE +21 -0
  14. package/node_modules/pi-umbra-help/README.md +21 -0
  15. package/node_modules/pi-umbra-help/checks/umbra-help.check.ts +108 -0
  16. package/node_modules/pi-umbra-help/extensions/umbra-help.ts +223 -0
  17. package/node_modules/pi-umbra-help/lib/umbra-settings.ts +52 -0
  18. package/node_modules/pi-umbra-help/package.json +34 -0
  19. package/node_modules/pi-umbra-inputbar/LICENSE +21 -0
  20. package/node_modules/pi-umbra-inputbar/README.md +19 -0
  21. package/node_modules/pi-umbra-inputbar/checks/umbra-inputbar.check.ts +53 -0
  22. package/node_modules/pi-umbra-inputbar/extensions/umbra-inputbar.ts +130 -0
  23. package/node_modules/pi-umbra-inputbar/package.json +33 -0
  24. package/node_modules/pi-umbra-preview/LICENSE +21 -0
  25. package/node_modules/pi-umbra-preview/README.md +32 -0
  26. package/node_modules/pi-umbra-preview/checks/umbra-preview.check.ts +38 -0
  27. package/node_modules/pi-umbra-preview/extensions/umbra-preview.ts +147 -0
  28. package/node_modules/pi-umbra-preview/package.json +33 -0
  29. package/node_modules/pi-umbra-rename/LICENSE +21 -0
  30. package/node_modules/pi-umbra-rename/README.md +19 -0
  31. package/node_modules/pi-umbra-rename/checks/umbra-rename.check.ts +55 -0
  32. package/node_modules/pi-umbra-rename/extensions/umbra-rename.ts +22 -0
  33. package/node_modules/pi-umbra-rename/package.json +33 -0
  34. package/node_modules/pi-umbra-shimmer/LICENSE +21 -0
  35. package/node_modules/pi-umbra-shimmer/README.md +19 -0
  36. package/node_modules/pi-umbra-shimmer/checks/umbra-shimmer.check.ts +89 -0
  37. package/node_modules/pi-umbra-shimmer/extensions/umbra-shimmer.ts +183 -0
  38. package/node_modules/pi-umbra-shimmer/package.json +33 -0
  39. package/node_modules/pi-umbra-skill-matcher/LICENSE +21 -0
  40. package/node_modules/pi-umbra-skill-matcher/README.md +19 -0
  41. package/node_modules/pi-umbra-skill-matcher/checks/umbra-skill-matcher.check.ts +94 -0
  42. package/node_modules/pi-umbra-skill-matcher/extensions/umbra-skill-matcher.ts +124 -0
  43. package/node_modules/pi-umbra-skill-matcher/package.json +35 -0
  44. package/node_modules/pi-umbra-theme/LICENSE +21 -0
  45. package/node_modules/pi-umbra-theme/README.md +38 -0
  46. package/node_modules/pi-umbra-theme/checks/umbra-background.check.ts +158 -0
  47. package/node_modules/pi-umbra-theme/checks/umbra-footer.check.ts +72 -0
  48. package/node_modules/pi-umbra-theme/checks/umbra-gutter.check.ts +59 -0
  49. package/node_modules/pi-umbra-theme/checks/umbra-toolbox.check.ts +71 -0
  50. package/node_modules/pi-umbra-theme/checks/umbra-working.check.ts +120 -0
  51. package/node_modules/pi-umbra-theme/extensions/umbra-background.ts +155 -0
  52. package/node_modules/pi-umbra-theme/extensions/umbra-footer.ts +150 -0
  53. package/node_modules/pi-umbra-theme/extensions/umbra-gutter.ts +35 -0
  54. package/node_modules/pi-umbra-theme/extensions/umbra-toolbox.ts +129 -0
  55. package/node_modules/pi-umbra-theme/extensions/umbra-working.ts +209 -0
  56. package/node_modules/pi-umbra-theme/lib/umbra-format.ts +16 -0
  57. package/node_modules/pi-umbra-theme/lib/umbra-settings.ts +52 -0
  58. package/node_modules/pi-umbra-theme/package.json +45 -0
  59. package/node_modules/pi-umbra-theme/themes/umbra-astral-veil.json +88 -0
  60. package/node_modules/pi-umbra-theme/themes/umbra-deep-current.json +88 -0
  61. package/node_modules/pi-umbra-theme/themes/umbra-ember-ash.json +88 -0
  62. package/node_modules/pi-umbra-theme/themes/umbra-onyx-slate.json +88 -0
  63. package/node_modules/pi-umbra-theme/themes/umbra-tidal-drift.json +88 -0
  64. package/node_modules/pi-umbra-theme/themes/umbra-venom-dusk.json +88 -0
  65. package/node_modules/pi-umbra-theme/themes/umbra-violet-forge.json +88 -0
  66. package/package.json +68 -0
@@ -0,0 +1,209 @@
1
+ // The status line beside the spinner, which pi leaves as a fixed "Working" for the whole turn.
2
+ // Here it says which of pi's tools is running, how long the turn has taken, and how many output
3
+ // tokens it has cost so far.
4
+ //
5
+ // The words map onto pi's own `ToolCallEvent` union (core/extensions/types.d.ts): eight typed
6
+ // tools grouped into four verbs, plus `CustomToolCallEvent` - every extension-registered tool -
7
+ // under one word. So the grouping is not a guess about which tools exist; it is that union.
8
+ //
9
+ // "thought for Ns" appears on the Thinking line alone and counts from zero at each thinking
10
+ // block, so it reads as the length of this thought rather than a share of the turn.
11
+ //
12
+ // This extension also owns the spinner frames. They used to be installed by umbra-inputbar,
13
+ // which is where the theme poll below comes from: pi drops the working indicator when the theme
14
+ // instance is swapped, so the frames have to go back on. Two extensions writing the same frames
15
+ // would mean the last one to run wins, so there is exactly one now.
16
+ import type { AgentMessage } from "@earendil-works/pi-ai";
17
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
18
+ import { compact, elapsed } from "../lib/umbra-format.ts";
19
+ import { type UmbraSettings, readUmbraSettings } from "../lib/umbra-settings.ts";
20
+
21
+ export type Settings = NonNullable<UmbraSettings["working"]>;
22
+
23
+ // One entry per tool: the word shown while it runs, the animation, and how long one turn of that
24
+ // animation takes. Keyed by pi's own tool names, so `settings.working.tools` names the same thing
25
+ // pi does and everything about one tool sits in one place.
26
+ //
27
+ // Three keys are not tools. `thinking` and `idle` are states, and `custom` is the fallback for
28
+ // every extension-registered tool - pi's own `CustomToolCallEvent`. A real tool named `custom`
29
+ // would win over it, which is the right way round.
30
+ //
31
+ // Every glyph measures one column through pi's `visibleWidth`. A two-column frame shifts the whole
32
+ // status line on every tick, which is what ruled the trigrams (U+2630..) out of Listing. Listing
33
+ // bounces rather than sweeping one way: a vertical sweep that resets jumps the full cell height
34
+ // and reads as a stutter, which a rotation never does - the shape comes back on its own.
35
+ const ARC = ["◜", "◠", "◝", "◞", "◡", "◟"];
36
+ const SCAN = ["⎺", "⎻", "⎼", "⎽", "⎼", "⎻"];
37
+ const CORNER = ["▖", "▘", "▝", "▗"];
38
+ const CLOCK = ["◴", "◷", "◶", "◵"];
39
+ const STAR = ["✶", "✸", "✹", "✺", "✻", "✼"];
40
+ const BRAILLE = ["⣻", "⢿", "⡿", "⣟", "⣯", "⣷", "⣾", "⣽"];
41
+ const DIAMOND = ["◇", "◈", "◆", "◈"];
42
+
43
+ export type Entry = { label: string; cycleMs: number; frames: string[] };
44
+
45
+ /**
46
+ * One full turn of the animation, in milliseconds. The sets are 4, 6 and 8 frames long, so a fixed
47
+ * per-frame interval would run them at three different speeds. This is the number held steady and
48
+ * the per-frame interval is derived from it, so changing a set's length changes how fast its
49
+ * frames go by rather than how long the animation takes.
50
+ *
51
+ * Milliseconds, with the unit in the name. `intervalMs` is what pi's own API calls the
52
+ * neighbouring number, and a bare `0.48` would leave both the unit and the scope to be guessed at
53
+ * - the two ways this field would be filled in wrong.
54
+ */
55
+ const CYCLE_MS = 480;
56
+
57
+ const DEFAULTS: Record<string, Omit<Entry, "cycleMs"> & { cycleMs?: number }> = {
58
+ read: { label: "Reading", frames: ARC },
59
+ ls: { label: "Listing", frames: SCAN },
60
+ find: { label: "Listing", frames: SCAN },
61
+ grep: { label: "Listing", frames: SCAN },
62
+ write: { label: "Editing", frames: CORNER },
63
+ edit: { label: "Editing", frames: CORNER },
64
+ bash: { label: "Shell", frames: CLOCK },
65
+ powershell: { label: "Shell", frames: CLOCK },
66
+ thinking: { label: "Thinking", frames: STAR },
67
+ idle: { label: "Working", frames: BRAILLE },
68
+ custom: { label: "Tool call", frames: DIAMOND },
69
+ };
70
+
71
+ export const THINKING = "thinking";
72
+ export const IDLE = "idle";
73
+ export const CUSTOM = "custom";
74
+
75
+ // 250ms is the theme poll umbra-inputbar used. The line itself only changes once a second, and
76
+ // setWorkingMessage is skipped when the text is unchanged, so the extra ticks cost nothing.
77
+ const TICK_MS = 250;
78
+
79
+ export type Turn = {
80
+ startedAt: number;
81
+ /** The key the line is currently showing: a tool name, or thinking/idle/custom. */
82
+ key: string;
83
+ /** When the current thinking block began. Undefined whenever the key is not `thinking`. */
84
+ thinkingAt: number | undefined;
85
+ /** Output tokens from messages already ended this turn. */
86
+ settled: number;
87
+ /** Output tokens of the message still streaming. */
88
+ streaming: number;
89
+ };
90
+
91
+ export type Options = { tokens: boolean; elapsed: boolean };
92
+
93
+ export const newTurn = (now: number): Turn => ({
94
+ startedAt: now,
95
+ key: IDLE,
96
+ thinkingAt: undefined,
97
+ settled: 0,
98
+ streaming: 0,
99
+ });
100
+
101
+ /**
102
+ * Settings merge field by field, so `{ fabric_exec: { label: "Fabric" } }` renames it and keeps
103
+ * the fallback animation. A `frames` array replaces outright rather than merging, which is what
104
+ * lets a three-glyph override stay three; an empty one is not an override.
105
+ */
106
+ export const entryFor = (key: string, settings: Settings = {}): Entry => {
107
+ const base = DEFAULTS[key] ?? DEFAULTS[CUSTOM]!;
108
+ const named = settings.tools?.[key] ?? {};
109
+ return {
110
+ label: named.label ?? base.label,
111
+ cycleMs: named.cycleMs ?? base.cycleMs ?? CYCLE_MS,
112
+ frames: named.frames?.length ? named.frames : base.frames,
113
+ };
114
+ };
115
+
116
+ export const labelFor = (key: string, settings: Settings = {}): string => entryFor(key, settings).label;
117
+
118
+ export const indicatorFor = (key: string, settings: Settings = {}): { frames: string[]; intervalMs: number } => {
119
+ const { cycleMs, frames } = entryFor(key, settings);
120
+ return { frames, intervalMs: Math.round(cycleMs / frames.length) };
121
+ };
122
+
123
+ export const statusLine = (turn: Turn, now: number, options: Options, settings: Settings = {}): string => {
124
+ const parts: string[] = [];
125
+ if (options.elapsed) parts.push(elapsed(now - turn.startedAt));
126
+ const tokens = turn.settled + turn.streaming;
127
+ if (options.tokens && tokens > 0) parts.push(`↓ ${compact(tokens)} tokens`);
128
+ if (turn.key === THINKING && turn.thinkingAt !== undefined) {
129
+ parts.push(`thought for ${elapsed(now - turn.thinkingAt)}`);
130
+ }
131
+ const label = labelFor(turn.key, settings);
132
+ return parts.length === 0 ? `${label}…` : `${label}… (${parts.join(" · ")})`;
133
+ };
134
+
135
+ const outputOf = (message: AgentMessage): number =>
136
+ (message as { usage?: { output?: number } }).usage?.output ?? 0;
137
+
138
+ export default function working(pi: ExtensionAPI) {
139
+ pi.on("session_start", (_event, ctx: ExtensionContext) => {
140
+ const settings = readUmbraSettings().working ?? {};
141
+ const options: Options = { tokens: settings.tokens !== false, elapsed: settings.elapsed !== false };
142
+ let turn: Turn | undefined;
143
+ let sent: string | undefined;
144
+ let theme: unknown;
145
+ let installed: string | undefined; // the key whose frames are on the indicator
146
+
147
+ const paint = () => {
148
+ // Two reasons to reinstall: the key moved, or the theme instance was swapped. pi clears
149
+ // the indicator with the old theme and would otherwise fall back to its own frames.
150
+ const key = turn?.key ?? IDLE;
151
+ if (ctx.ui.theme !== theme || key !== installed) {
152
+ theme = ctx.ui.theme;
153
+ installed = key;
154
+ ctx.ui.setWorkingIndicator(indicatorFor(key, settings));
155
+ }
156
+ if (turn === undefined) return;
157
+ const line = statusLine(turn, Date.now(), options, settings);
158
+ if (line === sent) return;
159
+ sent = line;
160
+ ctx.ui.setWorkingMessage(line);
161
+ };
162
+
163
+ setInterval(paint, TICK_MS).unref();
164
+
165
+ // pi resets its own working message when a turn opens, so ours has to be written after.
166
+ pi.on("agent_start", () => {
167
+ turn = newTurn(Date.now());
168
+ sent = undefined;
169
+ paint();
170
+ });
171
+ pi.on("agent_end", () => {
172
+ turn = undefined;
173
+ sent = undefined;
174
+ ctx.ui.setWorkingMessage();
175
+ });
176
+
177
+ pi.on("tool_call", (event) => {
178
+ if (turn === undefined) return;
179
+ // The raw tool name is the key; entryFor maps it, and an unmapped one lands on `custom`.
180
+ turn.key = event.toolName;
181
+ turn.thinkingAt = undefined;
182
+ paint();
183
+ });
184
+ pi.on("tool_result", () => {
185
+ if (turn === undefined) return;
186
+ turn.key = IDLE;
187
+ paint();
188
+ });
189
+
190
+ pi.on("message_update", (event) => {
191
+ if (turn === undefined) return;
192
+ turn.streaming = outputOf(event.message);
193
+ const kind = (event.assistantMessageEvent as { type?: string }).type;
194
+ if (kind === "thinking_start") {
195
+ turn.key = THINKING;
196
+ turn.thinkingAt = Date.now();
197
+ } else if (kind === "thinking_end") {
198
+ turn.key = IDLE;
199
+ turn.thinkingAt = undefined;
200
+ }
201
+ paint();
202
+ });
203
+ pi.on("message_end", (event) => {
204
+ if (turn === undefined) return;
205
+ turn.settled += outputOf(event.message);
206
+ turn.streaming = 0;
207
+ });
208
+ });
209
+ }
@@ -0,0 +1,16 @@
1
+ // Numbers the status line and the footer both print. They sit next to each other on screen, so a
2
+ // count that reads "1.2k" in one place and "1200" in the other is the kind of drift that only
3
+ // shows up in a screenshot.
4
+
5
+ /** 695 → "695", 7013 → "7.0k", 224310 → "224k", 1000000 → "1000k" */
6
+ export const compact = (count: number): string => {
7
+ if (count < 1000) return `${count}`;
8
+ if (count < 100_000) return `${(count / 1000).toFixed(1)}k`;
9
+ return `${Math.round(count / 1000)}k`;
10
+ };
11
+
12
+ /** 4200 → "4s", 65000 → "1m 5s". Seconds alone stop reading as a duration past a minute. */
13
+ export const elapsed = (ms: number): string => {
14
+ const total = Math.max(0, Math.floor(ms / 1000));
15
+ return total < 60 ? `${total}s` : `${Math.floor(total / 60)}m ${total % 60}s`;
16
+ };
@@ -0,0 +1,52 @@
1
+ // One settings key for every umbra extension, read from pi's own settings file.
2
+ //
3
+ // This lives outside extensions/ because pi loads every .ts in that folder as an extension, and
4
+ // keeping the reader free of pi imports is what lets a check exercise it without resolving pi's
5
+ // packages.
6
+ import { readFileSync } from "node:fs";
7
+
8
+ export type UmbraSettings = {
9
+ /** "auto" follows the active theme, "off" leaves the terminal alone, "#rrggbb" is literal. */
10
+ background?: string;
11
+ messages?: {
12
+ /** Draw model text as a quote block, so it reads apart from your own lines. */
13
+ assistantPrefix?: boolean;
14
+ };
15
+ tools?: {
16
+ /** "boxed" frames every tool call; anything else leaves pi's own rows alone. */
17
+ chrome?: string;
18
+ };
19
+ working?: {
20
+ /**
21
+ * One entry per pi tool name, plus `thinking`, `idle` and `custom` for the three states
22
+ * that are not tools. Fields merge over the defaults one by one, so naming only `label`
23
+ * keeps the animation. A `frames` array replaces rather than merges.
24
+ */
25
+ tools?: Record<string, { label?: string; cycleMs?: number; frames?: string[] }>;
26
+ /** Show "↓ 180 tokens". */
27
+ tokens?: boolean;
28
+ /** Show "(5s)". */
29
+ elapsed?: boolean;
30
+ };
31
+ };
32
+
33
+ const SETTINGS_KEY = "piUmbraTheme";
34
+
35
+ const agentDirectory = (): string =>
36
+ process.env.PI_AGENT_DIR ?? `${process.env.USERPROFILE ?? process.env.HOME}/.pi/agent`;
37
+
38
+ /**
39
+ * Never throws: a missing or half-written settings file means "no preferences expressed", which
40
+ * is the same answer as an empty block, and a theme extension is no place to fail a session over.
41
+ */
42
+ export const readUmbraSettings = (): UmbraSettings => {
43
+ try {
44
+ const raw = readFileSync(`${agentDirectory()}/settings.json`, "utf8");
45
+ // SAFETY: every branch below re-checks the field it reads, so a settings file of any
46
+ // shape yields defaults rather than a wrong type reaching a caller.
47
+ const parsed = JSON.parse(raw) as Record<string, UmbraSettings | undefined>;
48
+ return parsed[SETTINGS_KEY] ?? {};
49
+ } catch {
50
+ return {};
51
+ }
52
+ };
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "pi-umbra-theme",
3
+ "version": "0.1.0",
4
+ "description": "pi's look in one package: seven umbra themes and the terminal background they set, a status footer, a gutter beside model text, a frame around every tool call, and a working line that names the running tool.",
5
+ "keywords": [
6
+ "pi-package",
7
+ "pi",
8
+ "pi-extension",
9
+ "pi-theme",
10
+ "tui",
11
+ "theme",
12
+ "footer"
13
+ ],
14
+ "author": "grkn",
15
+ "license": "MIT",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/grknbyk/pi-umbra.git",
19
+ "directory": "pi-umbra-theme"
20
+ },
21
+ "homepage": "https://github.com/grknbyk/pi-umbra/tree/main/pi-umbra-theme#readme",
22
+ "bugs": "https://github.com/grknbyk/pi-umbra/issues",
23
+ "type": "module",
24
+ "files": [
25
+ "extensions",
26
+ "checks",
27
+ "lib",
28
+ "themes",
29
+ "README.md",
30
+ "LICENSE"
31
+ ],
32
+ "pi": {
33
+ "extensions": [
34
+ "./extensions"
35
+ ],
36
+ "themes": [
37
+ "./themes"
38
+ ]
39
+ },
40
+ "peerDependencies": {
41
+ "@earendil-works/pi-ai": "*",
42
+ "@earendil-works/pi-coding-agent": "*",
43
+ "@earendil-works/pi-tui": "*"
44
+ }
45
+ }
@@ -0,0 +1,88 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
3
+ "name": "umbra-astral-veil",
4
+ "vars": {
5
+ "text": "#c0c0cc",
6
+ "gray": "#82828c",
7
+ "dimGray": "#777885",
8
+ "darkGray": "#2a2b35",
9
+ "accent": "#7fc9d6",
10
+ "selectedBg": "#202028",
11
+ "userMsgBg": "#18181f",
12
+ "toolPendingBg": "#0f1014",
13
+ "toolSuccessBg": "#0f1014",
14
+ "toolErrorBg": "#0f1014",
15
+ "customMsgBg": "#282335",
16
+ "errorFg": "#d16996",
17
+ "warningFg": "#cd9731",
18
+ "successFg": "#7fc9d6",
19
+ "diffAdded": "#be95ff",
20
+ "diffRemoved": "#d16996",
21
+ "accentLine": "#5d83c1",
22
+ "accentBright": "#a5d9e2",
23
+ "accentFaint": "#507b85"
24
+ },
25
+ "colors": {
26
+ "accent": "accent",
27
+ "border": "#7fc9d6",
28
+ "borderAccent": "accentBright",
29
+ "borderMuted": "accentFaint",
30
+ "success": "successFg",
31
+ "error": "errorFg",
32
+ "warning": "warningFg",
33
+ "muted": "gray",
34
+ "dim": "dimGray",
35
+ "text": "text",
36
+ "thinkingText": "gray",
37
+ "selectedBg": "selectedBg",
38
+ "scrollbarTrack": "darkGray",
39
+ "scrollbarThumb": "text",
40
+ "searchMatchBg": "selectedBg",
41
+ "searchMatchText": "text",
42
+ "userMessageBg": "userMsgBg",
43
+ "userMessageText": "text",
44
+ "customMessageBg": "customMsgBg",
45
+ "customMessageText": "text",
46
+ "customMessageLabel": "#ff76ad",
47
+ "toolPendingBg": "toolPendingBg",
48
+ "toolSuccessBg": "toolSuccessBg",
49
+ "toolErrorBg": "toolErrorBg",
50
+ "toolTitle": "#7fc9d6",
51
+ "toolOutput": "gray",
52
+ "mdHeading": "#ae88fa",
53
+ "mdLink": "#c2bbd3",
54
+ "mdLinkUrl": "#777885",
55
+ "mdCode": "#7fc9d6",
56
+ "mdCodeBlock": "#7fc9d6",
57
+ "mdCodeBlockBorder": "gray",
58
+ "mdQuote": "#82828c",
59
+ "mdQuoteBorder": "#82828c",
60
+ "mdHr": "#82828c",
61
+ "mdListBullet": "#7fc9d6",
62
+ "toolDiffAdded": "diffAdded",
63
+ "toolDiffRemoved": "diffRemoved",
64
+ "toolDiffContext": "gray",
65
+ "syntaxComment": "#6f6f6f",
66
+ "syntaxKeyword": "#ff76ad",
67
+ "syntaxFunction": "#ae88fa",
68
+ "syntaxVariable": "#ffffff",
69
+ "syntaxString": "#c2bbd3",
70
+ "syntaxNumber": "#7fd3d6",
71
+ "syntaxType": "#ae88fa",
72
+ "syntaxOperator": "#ff76ad",
73
+ "syntaxPunctuation": "#777885",
74
+ "thinkingOff": "#7fc9d6",
75
+ "thinkingMinimal": "#48626d",
76
+ "thinkingLow": "#669aa6",
77
+ "thinkingMedium": "#7fc9d6",
78
+ "thinkingHigh": "#99d4de",
79
+ "thinkingXhigh": "#b0dee6",
80
+ "thinkingMax": "#c7e7ed",
81
+ "bashMode": "#7fc9d6"
82
+ },
83
+ "export": {
84
+ "pageBg": "#0f1014",
85
+ "cardBg": "#191a1d",
86
+ "infoBg": "#36373c"
87
+ }
88
+ }
@@ -0,0 +1,88 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
3
+ "name": "umbra-deep-current",
4
+ "vars": {
5
+ "text": "#ffffff",
6
+ "gray": "#abacaf",
7
+ "dimGray": "#464b5d",
8
+ "darkGray": "#31333b",
9
+ "accent": "#717cb4",
10
+ "selectedBg": "#2e334a",
11
+ "userMsgBg": "#1a1c25",
12
+ "toolPendingBg": "#0f111a",
13
+ "toolSuccessBg": "#0f111a",
14
+ "toolErrorBg": "#0f111a",
15
+ "customMsgBg": "#1f263a",
16
+ "errorFg": "#ff5370",
17
+ "warningFg": "#ffcb6b",
18
+ "successFg": "#c3e88d",
19
+ "diffAdded": "#c3e88d",
20
+ "diffRemoved": "#ff5370",
21
+ "accentLine": "#89ddff",
22
+ "accentBright": "#9ca3ca",
23
+ "accentFaint": "#484f73"
24
+ },
25
+ "colors": {
26
+ "accent": "accent",
27
+ "border": "#717cb4",
28
+ "borderAccent": "accentBright",
29
+ "borderMuted": "accentFaint",
30
+ "success": "successFg",
31
+ "error": "errorFg",
32
+ "warning": "warningFg",
33
+ "muted": "gray",
34
+ "dim": "dimGray",
35
+ "text": "text",
36
+ "thinkingText": "gray",
37
+ "selectedBg": "selectedBg",
38
+ "scrollbarTrack": "darkGray",
39
+ "scrollbarThumb": "text",
40
+ "searchMatchBg": "selectedBg",
41
+ "searchMatchText": "text",
42
+ "userMessageBg": "userMsgBg",
43
+ "userMessageText": "text",
44
+ "customMessageBg": "customMsgBg",
45
+ "customMessageText": "text",
46
+ "customMessageLabel": "#c792ea",
47
+ "toolPendingBg": "toolPendingBg",
48
+ "toolSuccessBg": "toolSuccessBg",
49
+ "toolErrorBg": "toolErrorBg",
50
+ "toolTitle": "#717cb4",
51
+ "toolOutput": "gray",
52
+ "mdHeading": "#ffcb6b",
53
+ "mdLink": "#82aaff",
54
+ "mdLinkUrl": "#f78c6c",
55
+ "mdCode": "#575a67",
56
+ "mdCodeBlock": "#575a67",
57
+ "mdCodeBlockBorder": "gray",
58
+ "mdQuote": "#8f93a2",
59
+ "mdQuoteBorder": "#8f93a2",
60
+ "mdHr": "#323643",
61
+ "mdListBullet": "#8f93a2",
62
+ "toolDiffAdded": "diffAdded",
63
+ "toolDiffRemoved": "diffRemoved",
64
+ "toolDiffContext": "gray",
65
+ "syntaxComment": "#464b5d",
66
+ "syntaxKeyword": "#89ddff",
67
+ "syntaxFunction": "#82aaff",
68
+ "syntaxVariable": "#ffffff",
69
+ "syntaxString": "#c3e88d",
70
+ "syntaxNumber": "#f78c6c",
71
+ "syntaxType": "#ffcb6b",
72
+ "syntaxOperator": "#c792ea",
73
+ "syntaxPunctuation": "#89ddff",
74
+ "thinkingOff": "#717cb4",
75
+ "thinkingMinimal": "#474f73",
76
+ "thinkingLow": "#5e6796",
77
+ "thinkingMedium": "#717cb4",
78
+ "thinkingHigh": "#8d96c3",
79
+ "thinkingXhigh": "#a7aed1",
80
+ "thinkingMax": "#c1c5de",
81
+ "bashMode": "#c3e88d"
82
+ },
83
+ "export": {
84
+ "pageBg": "#0f111a",
85
+ "cardBg": "#191b23",
86
+ "infoBg": "#3f362a"
87
+ }
88
+ }
@@ -0,0 +1,88 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
3
+ "name": "umbra-ember-ash",
4
+ "vars": {
5
+ "text": "#d6deeb",
6
+ "gray": "#8b9099",
7
+ "dimGray": "#575656",
8
+ "darkGray": "#333333",
9
+ "accent": "#7e57c2",
10
+ "selectedBg": "#1f1f1f",
11
+ "userMsgBg": "#121212",
12
+ "toolPendingBg": "#000000",
13
+ "toolSuccessBg": "#000000",
14
+ "toolErrorBg": "#000000",
15
+ "customMsgBg": "#121824",
16
+ "errorFg": "#ef5350",
17
+ "warningFg": "#b39554",
18
+ "successFg": "#22da6e",
19
+ "diffAdded": "#addb67",
20
+ "diffRemoved": "#ef5350",
21
+ "accentLine": "#21c7a8",
22
+ "accentBright": "#a589d4",
23
+ "accentFaint": "#493271"
24
+ },
25
+ "colors": {
26
+ "accent": "accent",
27
+ "border": "#7e57c2",
28
+ "borderAccent": "accentBright",
29
+ "borderMuted": "accentFaint",
30
+ "success": "successFg",
31
+ "error": "errorFg",
32
+ "warning": "warningFg",
33
+ "muted": "gray",
34
+ "dim": "dimGray",
35
+ "text": "text",
36
+ "thinkingText": "gray",
37
+ "selectedBg": "selectedBg",
38
+ "scrollbarTrack": "darkGray",
39
+ "scrollbarThumb": "text",
40
+ "searchMatchBg": "selectedBg",
41
+ "searchMatchText": "text",
42
+ "userMessageBg": "userMsgBg",
43
+ "userMessageText": "text",
44
+ "customMessageBg": "customMsgBg",
45
+ "customMessageText": "text",
46
+ "customMessageLabel": "#c792ea",
47
+ "toolPendingBg": "toolPendingBg",
48
+ "toolSuccessBg": "toolSuccessBg",
49
+ "toolErrorBg": "toolErrorBg",
50
+ "toolTitle": "#7e57c2",
51
+ "toolOutput": "gray",
52
+ "mdHeading": "#7ed0ff",
53
+ "mdLink": "#fede5d",
54
+ "mdLinkUrl": "#72f1b8",
55
+ "mdCode": "#72f1b8",
56
+ "mdCodeBlock": "#eeeeee",
57
+ "mdCodeBlockBorder": "gray",
58
+ "mdQuote": "#72f1b8",
59
+ "mdQuoteBorder": "#72f1b8",
60
+ "mdHr": "#eeeeee",
61
+ "mdListBullet": "#7ed0ff",
62
+ "toolDiffAdded": "diffAdded",
63
+ "toolDiffRemoved": "diffRemoved",
64
+ "toolDiffContext": "gray",
65
+ "syntaxComment": "#848bbd",
66
+ "syntaxKeyword": "#fede5d",
67
+ "syntaxFunction": "#36f9f6",
68
+ "syntaxVariable": "#ff7edb",
69
+ "syntaxString": "#ff8b39",
70
+ "syntaxNumber": "#f97e72",
71
+ "syntaxType": "#fe4450",
72
+ "syntaxOperator": "#eeeeee",
73
+ "syntaxPunctuation": "#575656",
74
+ "thinkingOff": "#7e57c2",
75
+ "thinkingMinimal": "#4d4065",
76
+ "thinkingLow": "#684c97",
77
+ "thinkingMedium": "#7e57c2",
78
+ "thinkingHigh": "#9879ce",
79
+ "thinkingXhigh": "#af97d9",
80
+ "thinkingMax": "#c6b5e4",
81
+ "bashMode": "#22da6e"
82
+ },
83
+ "export": {
84
+ "pageBg": "#000000",
85
+ "cardBg": "#0a0a0a",
86
+ "infoBg": "#2f271c"
87
+ }
88
+ }
@@ -0,0 +1,88 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
3
+ "name": "umbra-onyx-slate",
4
+ "vars": {
5
+ "text": "#c6cbd4",
6
+ "gray": "#81848a",
7
+ "dimGray": "#676e95",
8
+ "darkGray": "#222222",
9
+ "accent": "#50bdfb",
10
+ "selectedBg": "#222222",
11
+ "userMsgBg": "#121212",
12
+ "toolPendingBg": "#000000",
13
+ "toolSuccessBg": "#000000",
14
+ "toolErrorBg": "#000000",
15
+ "customMsgBg": "#121824",
16
+ "errorFg": "#ff5370",
17
+ "warningFg": "#ffcb6b",
18
+ "successFg": "#c3e88d",
19
+ "diffAdded": "#c3e88d",
20
+ "diffRemoved": "#f07178",
21
+ "accentLine": "#89ddff",
22
+ "accentBright": "#84d1fc",
23
+ "accentFaint": "#2e6e92"
24
+ },
25
+ "colors": {
26
+ "accent": "accent",
27
+ "border": "#50bdfb",
28
+ "borderAccent": "accentBright",
29
+ "borderMuted": "accentFaint",
30
+ "success": "successFg",
31
+ "error": "errorFg",
32
+ "warning": "warningFg",
33
+ "muted": "gray",
34
+ "dim": "dimGray",
35
+ "text": "text",
36
+ "thinkingText": "gray",
37
+ "selectedBg": "selectedBg",
38
+ "scrollbarTrack": "darkGray",
39
+ "scrollbarThumb": "text",
40
+ "searchMatchBg": "selectedBg",
41
+ "searchMatchText": "text",
42
+ "userMessageBg": "userMsgBg",
43
+ "userMessageText": "text",
44
+ "customMessageBg": "customMsgBg",
45
+ "customMessageText": "text",
46
+ "customMessageLabel": "#c792ea",
47
+ "toolPendingBg": "toolPendingBg",
48
+ "toolSuccessBg": "toolSuccessBg",
49
+ "toolErrorBg": "toolErrorBg",
50
+ "toolTitle": "#50bdfb",
51
+ "toolOutput": "gray",
52
+ "mdHeading": "#ffcb6b",
53
+ "mdLink": "#82aaff",
54
+ "mdLinkUrl": "#f78c6c",
55
+ "mdCode": "#e8ecf2",
56
+ "mdCodeBlock": "#5e6174",
57
+ "mdCodeBlockBorder": "gray",
58
+ "mdQuote": "#9aa0ac",
59
+ "mdQuoteBorder": "#9aa0ac",
60
+ "mdHr": "#4e5579",
61
+ "mdListBullet": "#9aa0ac",
62
+ "toolDiffAdded": "diffAdded",
63
+ "toolDiffRemoved": "diffRemoved",
64
+ "toolDiffContext": "gray",
65
+ "syntaxComment": "#676e95",
66
+ "syntaxKeyword": "#89ddff",
67
+ "syntaxFunction": "#82aaff",
68
+ "syntaxVariable": "#c6cbd4",
69
+ "syntaxString": "#c3e88d",
70
+ "syntaxNumber": "#f78c6c",
71
+ "syntaxType": "#ffcb6b",
72
+ "syntaxOperator": "#c792ea",
73
+ "syntaxPunctuation": "#89ddff",
74
+ "thinkingOff": "#50bdfb",
75
+ "thinkingMinimal": "#32586e",
76
+ "thinkingLow": "#428fba",
77
+ "thinkingMedium": "#50bdfb",
78
+ "thinkingHigh": "#73cafc",
79
+ "thinkingXhigh": "#93d6fd",
80
+ "thinkingMax": "#b2e2fd",
81
+ "bashMode": "#c3e88d"
82
+ },
83
+ "export": {
84
+ "pageBg": "#000000",
85
+ "cardBg": "#0a0a0a",
86
+ "infoBg": "#332915"
87
+ }
88
+ }