@timurproko/a1 0.2.3-dev.651 → 0.2.3-dev.655
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/dist/app/session-shell/session-shell.js +9 -2
- package/dist/contracts/owned-ui/model.d.ts +5 -0
- package/dist/foundation/startup/startup-descriptor.js +2 -2
- package/dist/integrations/pi/components/resources/builtin-themes.d.ts +114 -126
- package/dist/integrations/pi/components/resources/builtin-themes.js +145 -157
- package/dist/integrations/pi/components/shell-footer-status.js +12 -8
- package/dist/integrations/pi/components/shell-presenters-info.js +1 -1
- package/dist/integrations/pi/components/shell-selectors-dialogs.js +1 -0
- package/dist/integrations/pi/components/shell-shared-facade.js +1 -2
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +2 -2
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +2 -2
- package/dist/integrations/pi/components/upstream/assets/earendil-image.json +2 -2
- package/dist/integrations/pi/components/upstream/components/countdown-timer.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/daxnuts.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/daxnuts.js +1 -1
- package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-editor.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-editor.js +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-input.js +1 -1
- package/dist/integrations/pi/components/upstream/components/extension-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/components/mermaid.js +1 -1
- package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/owned-editor.js +1 -1
- package/dist/integrations/pi/components/upstream/components/pi-logo.d.ts +2 -0
- package/dist/integrations/pi/components/upstream/components/pi-logo.js +21 -0
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/session-footer.js +13 -6
- package/dist/integrations/pi/components/upstream/components/session-selector-search.js +1 -1
- package/dist/integrations/pi/components/upstream/components/session-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/components/skill-invocation-message.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/skill-invocation-message.js +1 -1
- package/dist/integrations/pi/components/upstream/components/status-indicator.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/status-indicator.js +1 -1
- package/dist/integrations/pi/components/upstream/components/themed-text.d.ts +16 -0
- package/dist/integrations/pi/components/upstream/components/themed-text.js +28 -0
- package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/thinking-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/components/tool-execution.js +25 -3
- package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/components/tree-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/components/trust-selector.js +1 -1
- package/dist/integrations/pi/components/upstream/external-editor.js +1 -1
- package/dist/integrations/pi/components/upstream/history/editor-core.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/history/editor-core.js +5 -3
- package/dist/integrations/pi/components/upstream/history/kill-ring.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/history/kill-ring.js +1 -1
- package/dist/integrations/pi/components/upstream/history/printable-key.js +1 -1
- package/dist/integrations/pi/components/upstream/history/text-helpers.js +1 -1
- package/dist/integrations/pi/components/upstream/history/undo-stack.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/history/undo-stack.js +1 -1
- package/dist/integrations/pi/components/upstream/history/word-navigation.js +1 -1
- package/dist/integrations/pi/components/upstream/model-search.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/theme/system-theme.d.ts +60 -0
- package/dist/integrations/pi/components/upstream/theme/system-theme.js +695 -0
- package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +1 -1
- package/dist/integrations/pi/components/upstream/theme/theme.d.ts +6 -2
- package/dist/integrations/pi/components/upstream/theme/theme.js +22 -7
- package/dist/integrations/pi/engine/adapter.js +6 -0
- package/dist/integrations/pi/engine/pi-settings-metadata.json +10 -0
- package/dist/integrations/pi/engine/resources/changelog.json +4 -4
- package/dist/integrations/pi/engine/session-integration.d.ts +2 -2
- package/dist/integrations/pi/engine/session-integration.js +1 -1
- package/dist/integrations/pi/engine/settings-bridge.d.ts +1 -1
- package/dist/integrations/pi/engine/settings-bridge.js +9 -0
- package/dist/integrations/pi/engine/settings-port.js +4 -1
- package/dist/integrations/pi/engine/workflows.d.ts +2 -1
- package/dist/integrations/pi/engine/workflows.js +1 -0
- package/dist/integrations/pi/startup-public.js +1925 -1309
- package/dist/integrations/pi/startup-public.manifest.json +1611 -929
- package/dist/integrations/pi/tui-runtime/adapter.d.ts +2 -0
- package/dist/integrations/pi/tui-runtime/adapter.js +5 -0
- package/dist/integrations/pi/tui-runtime/contracts.d.ts +2 -1
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +1 -1
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +1 -1
- package/dist/native/darwin-arm64/manifest.json +3 -3
- package/dist/native/darwin-arm64/process-guardian +0 -0
- package/dist/native/linux-x64/manifest.json +3 -3
- package/dist/native/linux-x64/process-guardian +0 -0
- package/dist/native/win32-x64/manifest.json +3 -3
- package/dist/native/win32-x64/process-guardian.exe +0 -0
- package/dist/runtime-payload-inventory.json +40622 -27400
- package/docs/ci-release-runbook.md +10 -7
- package/package.json +3 -3
|
@@ -0,0 +1,695 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provenance: @earendil-works/pi-coding-agent 0.99.2 (MIT), commit 005af57d88ee23b33778f343a9595b32e67ff788,
|
|
3
|
+
* packages/coding-agent/src/modes/interactive/theme/system-theme.ts.
|
|
4
|
+
* Modifications: Copied the system palette generator with ESM import remapping and strict
|
|
5
|
+
* indexed-access assertions; A1 retains explicit owned theme selection.
|
|
6
|
+
* Deviations: none.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* The `system` theme: pi's colors derived from the terminal's own theme.
|
|
10
|
+
*
|
|
11
|
+
* Every token belongs to a color family (its hue) and has contrast rules: it must reach a contrast level
|
|
12
|
+
* on the background and on the panels it is drawn on. Hue and saturation come from the terminal's palette
|
|
13
|
+
* color for the family's ANSI slot, or from the family's own hue when the terminal reports no palette.
|
|
14
|
+
* Lightness comes from the rules alone. Colors are built in OKHSL, whose saturation is relative to the
|
|
15
|
+
* sRGB gamut, and fade toward gray near black and white.
|
|
16
|
+
*
|
|
17
|
+
* A contrast level is a target-lightness curve: the OKLab lightness a token needs, given the lightness of
|
|
18
|
+
* the surface below it. The curves were fitted to the reference theme design from the "Pi themes: system
|
|
19
|
+
* and light/dark" review. On dark backgrounds they aim for nearly fixed lightness; on light backgrounds the
|
|
20
|
+
* required difference grows as the background darkens.
|
|
21
|
+
*
|
|
22
|
+
* Depending on what the terminal reports, the theme is generated in one of three tiers:
|
|
23
|
+
* - background and palette: hues from the palette, lightness from the background;
|
|
24
|
+
* - background only: the families' own hues, lightness from the background;
|
|
25
|
+
* - nothing: ANSI palette indices and the default colors, which the terminal renders itself.
|
|
26
|
+
*/
|
|
27
|
+
import { colorToOkhsl, colorToOklch, okhslColor, oklabToOkhslLightness, rgbColor, } from "@earendil-works/pi-tui";
|
|
28
|
+
export const SYSTEM_THEME_NAME = "system";
|
|
29
|
+
const FAMILIES = {
|
|
30
|
+
neutral: { hue: 231.49, saturation: { min: 0.02, max: 0.08 }, slot: 8 },
|
|
31
|
+
blue: { hue: 231.49, saturation: { min: 0.1, max: 0.68 }, slot: 4 },
|
|
32
|
+
green: { hue: 158.68, saturation: { min: 0.1, max: 0.76 }, slot: 2 },
|
|
33
|
+
red: { hue: 20, saturation: { min: 0.1, max: 0.92 }, slot: 1 },
|
|
34
|
+
yellow: { hue: 82.36, saturation: { min: 0.5, max: 1 }, slot: 3 },
|
|
35
|
+
orange: { hue: 52, saturation: { min: 0.12, max: 0.85 }, slot: 3 },
|
|
36
|
+
violet: { hue: 295, saturation: { min: 0.2, max: 0.6 }, slot: 5 },
|
|
37
|
+
calamine: { hue: 202.43, saturation: { min: 0.1, max: 0.74 }, slot: 6 },
|
|
38
|
+
thinkingSlate: { hue: 231.49, saturation: { min: 0.08, max: 0.2 }, slot: 4 },
|
|
39
|
+
thinkingBlue: { hue: 231.49, saturation: { min: 0.2, max: 0.45 }, slot: 4 },
|
|
40
|
+
thinkingPeriwinkle: {
|
|
41
|
+
hue: 263.25,
|
|
42
|
+
saturation: { min: 0.3, max: 0.6 },
|
|
43
|
+
slot: 6,
|
|
44
|
+
},
|
|
45
|
+
thinkingViolet: { hue: 295, saturation: { min: 0.4, max: 0.75 }, slot: 5 },
|
|
46
|
+
thinkingMagenta: {
|
|
47
|
+
hue: 337.5,
|
|
48
|
+
saturation: { min: 0.5, max: 0.85 },
|
|
49
|
+
slot: 13,
|
|
50
|
+
},
|
|
51
|
+
thinkingRed: { hue: 20, saturation: { min: 0.95, max: 1 }, slot: 1 },
|
|
52
|
+
};
|
|
53
|
+
const TOKEN_FAMILIES = {
|
|
54
|
+
selectedBg: "blue",
|
|
55
|
+
searchMatchBg: "orange",
|
|
56
|
+
userMessageBg: "blue",
|
|
57
|
+
customMessageBg: "violet",
|
|
58
|
+
toolPendingBg: "neutral",
|
|
59
|
+
toolSuccessBg: "green",
|
|
60
|
+
toolErrorBg: "red",
|
|
61
|
+
text: "neutral",
|
|
62
|
+
userMessageText: "neutral",
|
|
63
|
+
customMessageText: "neutral",
|
|
64
|
+
toolTitle: "neutral",
|
|
65
|
+
syntaxOperator: "neutral",
|
|
66
|
+
syntaxPunctuation: "neutral",
|
|
67
|
+
muted: "neutral",
|
|
68
|
+
dim: "neutral",
|
|
69
|
+
thinkingText: "neutral",
|
|
70
|
+
toolOutput: "neutral",
|
|
71
|
+
mdLinkUrl: "neutral",
|
|
72
|
+
mdQuote: "neutral",
|
|
73
|
+
mdQuoteBorder: "neutral",
|
|
74
|
+
mdHr: "neutral",
|
|
75
|
+
mdCodeBlockBorder: "neutral",
|
|
76
|
+
toolDiffContext: "neutral",
|
|
77
|
+
syntaxComment: "neutral",
|
|
78
|
+
scrollbarTrack: "neutral",
|
|
79
|
+
scrollbarThumb: "neutral",
|
|
80
|
+
searchMatchText: "neutral",
|
|
81
|
+
borderMuted: "neutral",
|
|
82
|
+
accent: "violet",
|
|
83
|
+
borderAccent: "violet",
|
|
84
|
+
customMessageLabel: "violet",
|
|
85
|
+
mdCode: "violet",
|
|
86
|
+
mdListBullet: "violet",
|
|
87
|
+
syntaxType: "violet",
|
|
88
|
+
border: "blue",
|
|
89
|
+
mdLink: "blue",
|
|
90
|
+
syntaxKeyword: "blue",
|
|
91
|
+
syntaxVariable: "calamine",
|
|
92
|
+
success: "green",
|
|
93
|
+
mdCodeBlock: "green",
|
|
94
|
+
toolDiffAdded: "green",
|
|
95
|
+
bashMode: "green",
|
|
96
|
+
syntaxNumber: "green",
|
|
97
|
+
error: "red",
|
|
98
|
+
toolDiffRemoved: "red",
|
|
99
|
+
warning: "yellow",
|
|
100
|
+
mdHeading: "yellow",
|
|
101
|
+
syntaxFunction: "yellow",
|
|
102
|
+
syntaxString: "orange",
|
|
103
|
+
thinkingOff: "neutral",
|
|
104
|
+
thinkingMinimal: "thinkingSlate",
|
|
105
|
+
thinkingLow: "thinkingBlue",
|
|
106
|
+
thinkingMedium: "thinkingPeriwinkle",
|
|
107
|
+
thinkingHigh: "thinkingViolet",
|
|
108
|
+
thinkingXhigh: "thinkingMagenta",
|
|
109
|
+
thinkingMax: "thinkingRed",
|
|
110
|
+
};
|
|
111
|
+
/** Palette slots for tokens that would otherwise share a hue with a similar token. */
|
|
112
|
+
const TOKEN_SLOTS = {
|
|
113
|
+
syntaxString: 2,
|
|
114
|
+
syntaxNumber: 5,
|
|
115
|
+
searchMatchBg: 3,
|
|
116
|
+
};
|
|
117
|
+
const LEVELS = {
|
|
118
|
+
panel: {
|
|
119
|
+
dark: {
|
|
120
|
+
coefficients: [0.29131, -0.39746, 2.33185, -0.85524, -1.2076, 0.86276],
|
|
121
|
+
reachable: [0, 0.979],
|
|
122
|
+
},
|
|
123
|
+
light: {
|
|
124
|
+
coefficients: [
|
|
125
|
+
-3.74073, 27.94549, -78.44258, 112.6798, -79.60015, 22.11277,
|
|
126
|
+
],
|
|
127
|
+
reachable: [0.348, 1],
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
track: {
|
|
131
|
+
dark: {
|
|
132
|
+
coefficients: [0.39028, -0.23015, 0.83573, 2.43829, -4.38292, 2.01582],
|
|
133
|
+
reachable: [0, 0.946],
|
|
134
|
+
},
|
|
135
|
+
light: {
|
|
136
|
+
coefficients: [
|
|
137
|
+
-5.24921, 38.37322, -107.28833, 152.10005, -106.17127, 29.18061,
|
|
138
|
+
],
|
|
139
|
+
reachable: [0.368, 1],
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
thinking0: {
|
|
143
|
+
dark: {
|
|
144
|
+
coefficients: [0.52988, -0.05809, -0.30924, 4.63567, -6.52933, 2.89108],
|
|
145
|
+
reachable: [0, 0.873],
|
|
146
|
+
},
|
|
147
|
+
light: {
|
|
148
|
+
coefficients: [
|
|
149
|
+
-28.27749, 182.85284, -469.62416, 603.15916, -384.59976, 97.35147,
|
|
150
|
+
],
|
|
151
|
+
reachable: [0.51, 1],
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
thinking1: {
|
|
155
|
+
dark: {
|
|
156
|
+
coefficients: [0.55278, -0.03667, -0.45659, 4.95347, -6.90265, 3.0706],
|
|
157
|
+
reachable: [0, 0.858],
|
|
158
|
+
},
|
|
159
|
+
light: {
|
|
160
|
+
coefficients: [
|
|
161
|
+
-37.10484, 235.86282, -596.62344, 754.3633, -474.00763, 118.3551,
|
|
162
|
+
],
|
|
163
|
+
reachable: [0.535, 1],
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
thinking2: {
|
|
167
|
+
dark: {
|
|
168
|
+
coefficients: [0.57486, -0.01765, -0.58987, 5.25227, -7.27175, 3.25532],
|
|
169
|
+
reachable: [0, 0.842],
|
|
170
|
+
},
|
|
171
|
+
light: {
|
|
172
|
+
coefficients: [
|
|
173
|
+
-59.89653, 377.05024, -945.07843, 1182.03145, -734.96375, 181.68658,
|
|
174
|
+
],
|
|
175
|
+
reachable: [0.556, 1],
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
thinking3: {
|
|
179
|
+
dark: {
|
|
180
|
+
coefficients: [0.59621, -0.00062, -0.71148, 5.53588, -7.6392, 3.44606],
|
|
181
|
+
reachable: [0, 0.827],
|
|
182
|
+
},
|
|
183
|
+
light: {
|
|
184
|
+
coefficients: [
|
|
185
|
+
-72.07122, 445.84082, -1099.57352, 1353.88793, -829.53392, 202.26164,
|
|
186
|
+
],
|
|
187
|
+
reachable: [0.58, 1],
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
thinking4: {
|
|
191
|
+
dark: {
|
|
192
|
+
coefficients: [0.61691, 0.01462, -0.82288, 5.80651, -8.00641, 3.64333],
|
|
193
|
+
reachable: [0, 0.811],
|
|
194
|
+
},
|
|
195
|
+
light: {
|
|
196
|
+
coefficients: [
|
|
197
|
+
-110.14338, 674.21488, -1645.75941, 2004.32367, -1215.15899, 293.3183,
|
|
198
|
+
],
|
|
199
|
+
reachable: [0.6, 1],
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
thinking5: {
|
|
203
|
+
dark: {
|
|
204
|
+
coefficients: [0.63702, 0.02826, -0.92498, 6.06465, -8.37246, 3.84651],
|
|
205
|
+
reachable: [0, 0.795],
|
|
206
|
+
},
|
|
207
|
+
light: {
|
|
208
|
+
coefficients: [
|
|
209
|
+
-175.47701, 1063.54495, -2570.70594, 3098.80776, -1860.15527, 444.76392,
|
|
210
|
+
],
|
|
211
|
+
reachable: [0.62, 1],
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
thinking6: {
|
|
215
|
+
dark: {
|
|
216
|
+
coefficients: [0.65658, 0.04044, -1.01835, 6.30989, -8.73529, 4.05439],
|
|
217
|
+
reachable: [0, 0.779],
|
|
218
|
+
},
|
|
219
|
+
light: {
|
|
220
|
+
coefficients: [
|
|
221
|
+
-183.81712, 1094.70055, -2602.68539, 3088.71276, -1826.91131, 430.75931,
|
|
222
|
+
],
|
|
223
|
+
reachable: [0.643, 1],
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
subtle: {
|
|
227
|
+
dark: {
|
|
228
|
+
coefficients: [0.56762, -0.02475, -0.5383, 5.12628, -7.10931, 3.17324],
|
|
229
|
+
reachable: [0, 0.848],
|
|
230
|
+
},
|
|
231
|
+
light: {
|
|
232
|
+
coefficients: [
|
|
233
|
+
-232.85459, 1376.54473, -3249.11801, 3827.91186, -2248.29472, 526.55751,
|
|
234
|
+
],
|
|
235
|
+
reachable: [0.657, 1],
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
thumb: {
|
|
239
|
+
dark: {
|
|
240
|
+
coefficients: [0.60323, 0.00278, -0.73328, 5.57157, -7.68067, 3.46933],
|
|
241
|
+
reachable: [0, 0.823],
|
|
242
|
+
},
|
|
243
|
+
light: {
|
|
244
|
+
coefficients: [
|
|
245
|
+
-82.89897, 511.01355, -1255.98095, 1540.76821, -940.68087, 228.58523,
|
|
246
|
+
],
|
|
247
|
+
reachable: [0.586, 1],
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
readable: {
|
|
251
|
+
dark: {
|
|
252
|
+
coefficients: [0.66937, 0.04704, -1.06871, 6.43941, -8.9332, 4.17229],
|
|
253
|
+
reachable: [0, 0.77],
|
|
254
|
+
},
|
|
255
|
+
light: {
|
|
256
|
+
coefficients: [
|
|
257
|
+
-1554.52576, 8733.56817, -19604.93507, 21977.72696, -12300.99599,
|
|
258
|
+
2749.81288,
|
|
259
|
+
],
|
|
260
|
+
reachable: [0.751, 1],
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
emphasis: {
|
|
264
|
+
dark: {
|
|
265
|
+
coefficients: [0.7303, 0.07695, -1.31626, 7.1681, -10.14436, 4.92846],
|
|
266
|
+
reachable: [0, 0.712],
|
|
267
|
+
},
|
|
268
|
+
light: {
|
|
269
|
+
coefficients: [
|
|
270
|
+
-4948.31942, 26870.91986, -58334.48399, 63280.17197, -34298.01053,
|
|
271
|
+
7430.30146,
|
|
272
|
+
],
|
|
273
|
+
reachable: [0.811, 1],
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
textOnPanel: {
|
|
277
|
+
dark: {
|
|
278
|
+
coefficients: [0.86713, 0.05232, -0.89428, 4.79014, -5.5432, 1.75023],
|
|
279
|
+
reachable: [0, 0.542],
|
|
280
|
+
},
|
|
281
|
+
light: {
|
|
282
|
+
coefficients: [
|
|
283
|
+
-8570.89457, 43954.60805, -90084.00702, 92220.6791, -47152.15802,
|
|
284
|
+
9632.27113,
|
|
285
|
+
],
|
|
286
|
+
reachable: [0.867, 1],
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
text: {
|
|
290
|
+
dark: {
|
|
291
|
+
coefficients: [0.89242, 0.02311, -0.44862, 2.34417, -0.06084, -2.63844],
|
|
292
|
+
reachable: [0, 0.5],
|
|
293
|
+
},
|
|
294
|
+
light: {
|
|
295
|
+
coefficients: [
|
|
296
|
+
-2004.67048, 6664.47299, -6060.70202, -1792.61209, 5133.82359,
|
|
297
|
+
-1939.85583,
|
|
298
|
+
],
|
|
299
|
+
reachable: [0.894, 1],
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
};
|
|
303
|
+
const TOOL_PANELS = [
|
|
304
|
+
"toolPendingBg",
|
|
305
|
+
"toolSuccessBg",
|
|
306
|
+
"toolErrorBg",
|
|
307
|
+
];
|
|
308
|
+
const MESSAGE_PANELS = ["userMessageBg", "customMessageBg"];
|
|
309
|
+
const PANELS = [
|
|
310
|
+
"userMessageBg",
|
|
311
|
+
"toolPendingBg",
|
|
312
|
+
"toolSuccessBg",
|
|
313
|
+
"toolErrorBg",
|
|
314
|
+
"selectedBg",
|
|
315
|
+
"searchMatchBg",
|
|
316
|
+
"customMessageBg",
|
|
317
|
+
];
|
|
318
|
+
const THINKING = [
|
|
319
|
+
"thinkingOff",
|
|
320
|
+
"thinkingMinimal",
|
|
321
|
+
"thinkingLow",
|
|
322
|
+
"thinkingMedium",
|
|
323
|
+
"thinkingHigh",
|
|
324
|
+
"thinkingXhigh",
|
|
325
|
+
"thinkingMax",
|
|
326
|
+
];
|
|
327
|
+
const THINKING_LEVELS = [
|
|
328
|
+
"thinking0",
|
|
329
|
+
"thinking1",
|
|
330
|
+
"thinking2",
|
|
331
|
+
"thinking3",
|
|
332
|
+
"thinking4",
|
|
333
|
+
"thinking5",
|
|
334
|
+
"thinking6",
|
|
335
|
+
];
|
|
336
|
+
const each = (tokens, on, level) => tokens.map((token) => ({ token, on, level }));
|
|
337
|
+
const RULES = [
|
|
338
|
+
...each(PANELS, ["background"], "panel"),
|
|
339
|
+
{ token: "text", on: ["background"], level: "text" },
|
|
340
|
+
{ token: "text", on: ["selectedBg"], level: "textOnPanel" },
|
|
341
|
+
{ token: "userMessageText", on: ["userMessageBg"], level: "textOnPanel" },
|
|
342
|
+
{ token: "toolTitle", on: TOOL_PANELS, level: "textOnPanel" },
|
|
343
|
+
...each(["accent", "success", "error", "warning"], ["background", "selectedBg", ...TOOL_PANELS], "readable"),
|
|
344
|
+
{
|
|
345
|
+
token: "muted",
|
|
346
|
+
on: ["background", "selectedBg", "customMessageBg", ...TOOL_PANELS],
|
|
347
|
+
level: "readable",
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
token: "dim",
|
|
351
|
+
on: ["background", "selectedBg", "customMessageBg", ...TOOL_PANELS],
|
|
352
|
+
level: "subtle",
|
|
353
|
+
},
|
|
354
|
+
{ token: "thinkingText", on: ["background"], level: "readable" },
|
|
355
|
+
{
|
|
356
|
+
token: "customMessageText",
|
|
357
|
+
on: ["customMessageBg", ...TOOL_PANELS],
|
|
358
|
+
level: "readable",
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
token: "customMessageLabel",
|
|
362
|
+
on: ["background", "customMessageBg", "selectedBg", ...TOOL_PANELS],
|
|
363
|
+
level: "readable",
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
token: "toolOutput",
|
|
367
|
+
on: ["background", ...TOOL_PANELS],
|
|
368
|
+
level: "readable",
|
|
369
|
+
},
|
|
370
|
+
...each([
|
|
371
|
+
"mdHeading",
|
|
372
|
+
"mdLink",
|
|
373
|
+
"mdLinkUrl",
|
|
374
|
+
"mdCode",
|
|
375
|
+
"mdQuote",
|
|
376
|
+
"mdCodeBlockBorder",
|
|
377
|
+
"mdListBullet",
|
|
378
|
+
], ["background", ...MESSAGE_PANELS], "readable"),
|
|
379
|
+
{
|
|
380
|
+
token: "mdCodeBlock",
|
|
381
|
+
on: ["background", ...MESSAGE_PANELS, ...TOOL_PANELS],
|
|
382
|
+
level: "readable",
|
|
383
|
+
},
|
|
384
|
+
...each(["toolDiffAdded", "toolDiffRemoved", "toolDiffContext"], ["background", ...TOOL_PANELS], "readable"),
|
|
385
|
+
...each([
|
|
386
|
+
"syntaxComment",
|
|
387
|
+
"syntaxKeyword",
|
|
388
|
+
"syntaxFunction",
|
|
389
|
+
"syntaxVariable",
|
|
390
|
+
"syntaxString",
|
|
391
|
+
"syntaxNumber",
|
|
392
|
+
"syntaxType",
|
|
393
|
+
"syntaxOperator",
|
|
394
|
+
"syntaxPunctuation",
|
|
395
|
+
], ["background", ...MESSAGE_PANELS, ...TOOL_PANELS], "readable"),
|
|
396
|
+
{ token: "searchMatchText", on: ["searchMatchBg"], level: "readable" },
|
|
397
|
+
...each(["bashMode", "border", "borderAccent"], ["background"], "readable"),
|
|
398
|
+
{ token: "borderMuted", on: ["background"], level: "subtle" },
|
|
399
|
+
...each(["mdQuoteBorder", "mdHr"], ["background", ...MESSAGE_PANELS, ...TOOL_PANELS], "readable"),
|
|
400
|
+
{ token: "scrollbarTrack", on: ["background"], level: "track" },
|
|
401
|
+
{ token: "scrollbarThumb", on: ["scrollbarTrack"], level: "thumb" },
|
|
402
|
+
...THINKING.map((token, index) => ({
|
|
403
|
+
token,
|
|
404
|
+
on: ["background"],
|
|
405
|
+
level: THINKING_LEVELS[index],
|
|
406
|
+
})),
|
|
407
|
+
];
|
|
408
|
+
/** Relaxation compresses levels stronger than this one toward it before weakening all levels. */
|
|
409
|
+
const READABLE_FLOOR = {
|
|
410
|
+
dark: "readable",
|
|
411
|
+
light: "subtle",
|
|
412
|
+
};
|
|
413
|
+
/** Body text uses the terminal's foreground when it reaches this level, which is clearly stronger than muted. */
|
|
414
|
+
const FOREGROUND_LEVEL = "emphasis";
|
|
415
|
+
/** Text-level tokens that take the terminal's foreground. */
|
|
416
|
+
const FOREGROUND_TOKENS = [
|
|
417
|
+
"text",
|
|
418
|
+
"userMessageText",
|
|
419
|
+
"toolTitle",
|
|
420
|
+
];
|
|
421
|
+
/** WCAG 2 contrast ratio that body text must reach on the surfaces it is drawn on. */
|
|
422
|
+
const TEXT_MINIMUM_WCAG_CONTRAST = 4.5;
|
|
423
|
+
/** Tokens in dependency order: every surface before the tokens drawn on it. */
|
|
424
|
+
const SOLVE_ORDER = (() => {
|
|
425
|
+
const order = [];
|
|
426
|
+
const visit = (token) => {
|
|
427
|
+
if (order.includes(token))
|
|
428
|
+
return;
|
|
429
|
+
for (const rule of RULES) {
|
|
430
|
+
if (rule.token !== token)
|
|
431
|
+
continue;
|
|
432
|
+
for (const surface of rule.on)
|
|
433
|
+
if (surface !== "background")
|
|
434
|
+
visit(surface);
|
|
435
|
+
}
|
|
436
|
+
order.push(token);
|
|
437
|
+
};
|
|
438
|
+
for (const rule of RULES)
|
|
439
|
+
visit(rule.token);
|
|
440
|
+
return order;
|
|
441
|
+
})();
|
|
442
|
+
/** OKLab lightness of an sRGB color, 0-1. */
|
|
443
|
+
function oklabLightness(color) {
|
|
444
|
+
return colorToOklch(rgbColor(color.r, color.g, color.b)).l;
|
|
445
|
+
}
|
|
446
|
+
/** WCAG 2 relative luminance. */
|
|
447
|
+
export function relativeLuminance({ r, g, b }) {
|
|
448
|
+
const linear = (channel) => {
|
|
449
|
+
const value = channel / 255;
|
|
450
|
+
return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
|
|
451
|
+
};
|
|
452
|
+
return 0.2126 * linear(r) + 0.7152 * linear(g) + 0.0722 * linear(b);
|
|
453
|
+
}
|
|
454
|
+
/** WCAG 2 contrast ratio, 1-21. */
|
|
455
|
+
export function wcagContrast(first, second) {
|
|
456
|
+
const a = relativeLuminance(first);
|
|
457
|
+
const b = relativeLuminance(second);
|
|
458
|
+
return (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Whether a terminal is dark or light, from its reported colors: the direction of its own foreground
|
|
462
|
+
* when text can be readable that way, otherwise dark when white text has more contrast on the
|
|
463
|
+
* background than black text.
|
|
464
|
+
*/
|
|
465
|
+
export function terminalAppearance(background, foreground) {
|
|
466
|
+
const white = { r: 255, g: 255, b: 255 };
|
|
467
|
+
const black = { r: 0, g: 0, b: 0 };
|
|
468
|
+
const whiteContrast = wcagContrast(white, background);
|
|
469
|
+
const blackContrast = wcagContrast(black, background);
|
|
470
|
+
if (foreground) {
|
|
471
|
+
const foregroundL = oklabLightness(foreground);
|
|
472
|
+
const backgroundL = oklabLightness(background);
|
|
473
|
+
if (Math.abs(foregroundL - backgroundL) > 0.05) {
|
|
474
|
+
const appearance = foregroundL > backgroundL ? "dark" : "light";
|
|
475
|
+
const best = appearance === "dark" ? whiteContrast : blackContrast;
|
|
476
|
+
if (best >= TEXT_MINIMUM_WCAG_CONTRAST)
|
|
477
|
+
return appearance;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return whiteContrast >= blackContrast ? "dark" : "light";
|
|
481
|
+
}
|
|
482
|
+
// ============================================================================
|
|
483
|
+
// Generation
|
|
484
|
+
// ============================================================================
|
|
485
|
+
const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
486
|
+
function hexOf({ r, g, b }) {
|
|
487
|
+
return `#${[r, g, b].map((channel) => Math.round(channel).toString(16).padStart(2, "0")).join("")}`;
|
|
488
|
+
}
|
|
489
|
+
/** Saturation weight at a lightness: a Gaussian (center 0.5, sigma 0.25), 0 at black and white, 1 in the middle. */
|
|
490
|
+
function bellWeight(lightness) {
|
|
491
|
+
const gaussian = (x) => Math.exp(-((x - 0.5) ** 2) / (2 * 0.25 ** 2));
|
|
492
|
+
return (gaussian(lightness) - gaussian(0)) / (1 - gaussian(0));
|
|
493
|
+
}
|
|
494
|
+
/** A family's saturation curve relative to its maximum: 1 at mid lightness, `min / max` at black and white. */
|
|
495
|
+
function saturationCurve({ saturation: { min, max } }, lightness) {
|
|
496
|
+
const floor = max > 0 ? min / max : 1;
|
|
497
|
+
return floor + (1 - floor) * bellWeight(lightness);
|
|
498
|
+
}
|
|
499
|
+
/** The target lightness for a level on a surface, or undefined where the level cannot be reached. */
|
|
500
|
+
function levelTarget(level, appearance, surfaceL) {
|
|
501
|
+
const curve = LEVELS[level][appearance];
|
|
502
|
+
if (surfaceL < curve.reachable[0] || surfaceL > curve.reachable[1])
|
|
503
|
+
return undefined;
|
|
504
|
+
return curve.coefficients.reduce((sum, coefficient, power) => sum + coefficient * surfaceL ** power, 0);
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Generate the system theme's colors from the terminal's reported colors.
|
|
508
|
+
*/
|
|
509
|
+
export function generateSystemThemeColors(input) {
|
|
510
|
+
const saturation = clamp(input.saturation ?? 1, 0, 1);
|
|
511
|
+
const { background, foreground } = input;
|
|
512
|
+
if (!background)
|
|
513
|
+
return indexedColors(saturation, input.appearanceHint);
|
|
514
|
+
const palette = input.palette?.length === 16 ? input.palette.map(okhslOf) : undefined;
|
|
515
|
+
const appearance = terminalAppearance(background, foreground);
|
|
516
|
+
const lighter = appearance === "dark";
|
|
517
|
+
const extreme = lighter ? 1 : 0;
|
|
518
|
+
const backgroundL = oklabLightness(background);
|
|
519
|
+
/**
|
|
520
|
+
* A token's color at an OKLab lightness. With a palette, the palette color's saturation applies at its
|
|
521
|
+
* own lightness and falls off toward black and white along the family's curve, never rising above it.
|
|
522
|
+
*/
|
|
523
|
+
const paint = (token, oklabL) => {
|
|
524
|
+
const lightness = oklabToOkhslLightness(oklabL);
|
|
525
|
+
const family = FAMILIES[TOKEN_FAMILIES[token]];
|
|
526
|
+
if (!palette) {
|
|
527
|
+
const { min, max } = family.saturation;
|
|
528
|
+
return okhslColor(family.hue, (min + (max - min) * bellWeight(lightness)) * saturation, lightness);
|
|
529
|
+
}
|
|
530
|
+
return anchored(palette[TOKEN_SLOTS[token] ?? family.slot], family, lightness, saturation);
|
|
531
|
+
};
|
|
532
|
+
/**
|
|
533
|
+
* The lightness a rule needs on a surface, relaxed by `t`: from 0 to 1, levels stronger than the readable
|
|
534
|
+
* floor move toward it; from 1 to 2, all levels move toward the surface itself.
|
|
535
|
+
*/
|
|
536
|
+
const target = (level, surfaceL, t) => {
|
|
537
|
+
const reached = levelTarget(level, appearance, surfaceL);
|
|
538
|
+
if (reached === undefined && t === 0)
|
|
539
|
+
return undefined;
|
|
540
|
+
const distance = (reached ?? extreme) - surfaceL;
|
|
541
|
+
const floor = (levelTarget(READABLE_FLOOR[appearance], appearance, surfaceL) ??
|
|
542
|
+
extreme) - surfaceL;
|
|
543
|
+
const compressed = Math.abs(distance) > Math.abs(floor)
|
|
544
|
+
? distance - (distance - floor) * Math.min(t, 1)
|
|
545
|
+
: distance;
|
|
546
|
+
return surfaceL + compressed * (1 - Math.max(0, t - 1));
|
|
547
|
+
};
|
|
548
|
+
/**
|
|
549
|
+
* Keep a panel light enough (or dark enough) that white (or black) text still reaches the body text
|
|
550
|
+
* minimum on it. This only matters for backgrounds near mid-gray, where it barely does on the background.
|
|
551
|
+
*/
|
|
552
|
+
const extremeText = lighter
|
|
553
|
+
? { r: 255, g: 255, b: 255 }
|
|
554
|
+
: { r: 0, g: 0, b: 0 };
|
|
555
|
+
const readable = (color) => wcagContrast(extremeText, color) >= TEXT_MINIMUM_WCAG_CONTRAST;
|
|
556
|
+
const limitPanel = (token, l) => {
|
|
557
|
+
const color = paint(token, l);
|
|
558
|
+
if (readable(color))
|
|
559
|
+
return color;
|
|
560
|
+
let [low, high] = [backgroundL, l];
|
|
561
|
+
for (let index = 0; index < 20; index++) {
|
|
562
|
+
const middle = (low + high) / 2;
|
|
563
|
+
if (readable(paint(token, middle)))
|
|
564
|
+
low = middle;
|
|
565
|
+
else
|
|
566
|
+
high = middle;
|
|
567
|
+
}
|
|
568
|
+
return paint(token, low);
|
|
569
|
+
};
|
|
570
|
+
const solve = (t) => {
|
|
571
|
+
const colors = new Map([
|
|
572
|
+
["background", background],
|
|
573
|
+
]);
|
|
574
|
+
for (const token of SOLVE_ORDER) {
|
|
575
|
+
const targets = [];
|
|
576
|
+
for (const rule of RULES) {
|
|
577
|
+
if (rule.token !== token)
|
|
578
|
+
continue;
|
|
579
|
+
for (const surface of rule.on) {
|
|
580
|
+
const value = target(rule.level, oklabLightness(colors.get(surface) ?? background), t);
|
|
581
|
+
if (value === undefined || value < 0 || value > 1)
|
|
582
|
+
return undefined;
|
|
583
|
+
targets.push(value);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
const l = lighter ? Math.max(...targets) : Math.min(...targets);
|
|
587
|
+
colors.set(token, PANELS.includes(token)
|
|
588
|
+
? limitPanel(token, l)
|
|
589
|
+
: paint(token, l));
|
|
590
|
+
}
|
|
591
|
+
return colors;
|
|
592
|
+
};
|
|
593
|
+
let relaxation = 0;
|
|
594
|
+
let colors = solve(0);
|
|
595
|
+
if (!colors) {
|
|
596
|
+
// Mid-gray backgrounds cannot fit every level: relax as little as possible. Full relaxation always fits.
|
|
597
|
+
let [low, high] = [0, 2];
|
|
598
|
+
colors = solve(high);
|
|
599
|
+
for (let index = 0; index < 20; index++) {
|
|
600
|
+
const middle = (low + high) / 2;
|
|
601
|
+
const attempt = solve(middle);
|
|
602
|
+
if (attempt)
|
|
603
|
+
[high, colors] = [middle, attempt];
|
|
604
|
+
else
|
|
605
|
+
low = middle;
|
|
606
|
+
}
|
|
607
|
+
relaxation = high;
|
|
608
|
+
}
|
|
609
|
+
const solved = colors ?? new Map();
|
|
610
|
+
const surfacesOf = (token) => RULES.filter((rule) => rule.token === token).flatMap((rule) => rule.on.map((surface) => solved.get(surface) ?? background));
|
|
611
|
+
const result = {};
|
|
612
|
+
for (const token of Object.keys(TOKEN_FAMILIES)) {
|
|
613
|
+
const color = solved.get(token);
|
|
614
|
+
result[token] = color ? hexOf(color) : "";
|
|
615
|
+
}
|
|
616
|
+
for (const token of FOREGROUND_TOKENS) {
|
|
617
|
+
const surfaces = surfacesOf(token);
|
|
618
|
+
// Body text uses the terminal's own foreground where it is clearly stronger than muted text; otherwise
|
|
619
|
+
// the foreground's hue at just enough lightness.
|
|
620
|
+
let text = solved.get(token);
|
|
621
|
+
if (foreground) {
|
|
622
|
+
const targets = surfaces.map((surface) => target(FOREGROUND_LEVEL, oklabLightness(surface), relaxation));
|
|
623
|
+
if (targets.every((value) => value !== undefined && value >= 0 && value <= 1)) {
|
|
624
|
+
const needed = lighter
|
|
625
|
+
? Math.max(...targets)
|
|
626
|
+
: Math.min(...targets);
|
|
627
|
+
const foregroundL = oklabLightness(foreground);
|
|
628
|
+
if (lighter ? foregroundL >= needed : foregroundL <= needed) {
|
|
629
|
+
result[token] = "";
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
632
|
+
text = anchored(okhslOf(foreground), FAMILIES.neutral, oklabToOkhslLightness(needed), saturation);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
// Body text keeps at least 4.5:1 on the surfaces it is drawn on, even on relaxed mid-gray backgrounds.
|
|
636
|
+
if (text)
|
|
637
|
+
result[token] = hexOf(withTextContrast(text, surfaces, lighter));
|
|
638
|
+
}
|
|
639
|
+
return { colors: result, dim: [], appearance };
|
|
640
|
+
}
|
|
641
|
+
function okhslOf({ r, g, b }) {
|
|
642
|
+
return colorToOkhsl(rgbColor(r, g, b));
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* A source color's hue at another OKHSL lightness. Its saturation applies at its own lightness and falls off
|
|
646
|
+
* toward black and white along the family's saturation curve, never rising above it.
|
|
647
|
+
*/
|
|
648
|
+
function anchored(source, family, lightness, saturation) {
|
|
649
|
+
const anchor = saturationCurve(family, source.l);
|
|
650
|
+
const falloff = anchor > 0 ? Math.min(1, saturationCurve(family, lightness) / anchor) : 1;
|
|
651
|
+
return okhslColor(source.h, source.s * falloff * saturation, lightness);
|
|
652
|
+
}
|
|
653
|
+
/** Move a text color toward white or black until it reaches the WCAG minimum on every surface. */
|
|
654
|
+
function withTextContrast(color, surfaces, lighter) {
|
|
655
|
+
const meets = (candidate) => surfaces.every((surface) => wcagContrast(candidate, surface) >= TEXT_MINIMUM_WCAG_CONTRAST);
|
|
656
|
+
if (meets(color))
|
|
657
|
+
return color;
|
|
658
|
+
const { h, s, l } = okhslOf(color);
|
|
659
|
+
const at = (lightness) => okhslColor(h, s, lightness);
|
|
660
|
+
const extreme = lighter ? 1 : 0;
|
|
661
|
+
if (!meets(at(extreme)))
|
|
662
|
+
return at(extreme);
|
|
663
|
+
let [low, high] = [l, extreme];
|
|
664
|
+
for (let index = 0; index < 20; index++) {
|
|
665
|
+
const middle = (low + high) / 2;
|
|
666
|
+
if (meets(at(middle)))
|
|
667
|
+
high = middle;
|
|
668
|
+
else
|
|
669
|
+
low = middle;
|
|
670
|
+
}
|
|
671
|
+
return at(high);
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Colors for terminals that reported nothing: the terminal renders ANSI indices 0-15 and the default
|
|
675
|
+
* colors with its own theme, so they fit any background. Neutral tokens below body text are faint (SGR 2)
|
|
676
|
+
* instead of bright black, which some themes make nearly invisible. Panels have no background.
|
|
677
|
+
*/
|
|
678
|
+
function indexedColors(saturation, appearance) {
|
|
679
|
+
const colors = {};
|
|
680
|
+
const dim = [];
|
|
681
|
+
for (const [token, familyName] of Object.entries(TOKEN_FAMILIES)) {
|
|
682
|
+
if (PANELS.includes(token)) {
|
|
683
|
+
colors[token] = "";
|
|
684
|
+
continue;
|
|
685
|
+
}
|
|
686
|
+
const neutral = familyName === "neutral";
|
|
687
|
+
colors[token] =
|
|
688
|
+
!neutral && saturation > 0
|
|
689
|
+
? (TOKEN_SLOTS[token] ?? FAMILIES[familyName].slot)
|
|
690
|
+
: "";
|
|
691
|
+
if (neutral && !FOREGROUND_TOKENS.includes(token))
|
|
692
|
+
dim.push(token);
|
|
693
|
+
}
|
|
694
|
+
return { colors, dim, appearance };
|
|
695
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Provenance: @earendil-works/pi-coding-agent 0.
|
|
2
|
+
* Provenance: @earendil-works/pi-coding-agent 0.99.2 (MIT), commit 005af57d88ee23b33778f343a9595b32e67ff788,
|
|
3
3
|
* packages/coding-agent/src/modes/interactive/theme/theme-controller.ts.
|
|
4
4
|
* Modifications: Source-synchronized controller port: renamed owner class, injected dependency-free
|
|
5
5
|
* settings/runtime ports, remapped private theme helpers to the public-backed A1 theme adapter, and
|