@bismawy/pi-arnative 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.
@@ -0,0 +1,354 @@
1
+ /**
2
+ * Token usage data layer — the single source for /usage and the Model tab.
3
+ * - Scans local Pi session history (~/.pi/agent/sessions/*.jsonl).
4
+ * - mtime+size keyed cache in ~/.pi/agent/arnative-usage-cache.json, read into
5
+ * memory once (parsing 4MB per header frame cost ~50ms and lagged long chats).
6
+ * - No double counting: the active session is already in its own file, so it is
7
+ * NOT re-added from sessionManager (that inflated the ↑↓ totals).
8
+ */
9
+ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
10
+ import { dirname, join } from "node:path";
11
+ import { getAgentDir } from "@earendil-works/pi-coding-agent";
12
+
13
+ export interface UsageEntry {
14
+ provider: string;
15
+ model: string;
16
+ input: number;
17
+ output: number;
18
+ cacheRead: number;
19
+ cost: number;
20
+ }
21
+
22
+ export interface FileCacheItem {
23
+ mtimeMs: number;
24
+ size: number;
25
+ v?: number;
26
+ entries: UsageEntry[];
27
+ }
28
+
29
+ export interface UsageModelStats {
30
+ name: string;
31
+ sessions: number;
32
+ msgs: number;
33
+ input: number;
34
+ output: number;
35
+ cacheRead: number;
36
+ totalTokens: number;
37
+ cost: number;
38
+ }
39
+
40
+ export interface UsageProviderStats {
41
+ name: string;
42
+ sessions: number;
43
+ msgs: number;
44
+ input: number;
45
+ output: number;
46
+ cacheRead: number;
47
+ totalTokens: number;
48
+ cost: number;
49
+ models: UsageModelStats[];
50
+ }
51
+
52
+ export interface UsageSummary {
53
+ providers: UsageProviderStats[];
54
+ totals: {
55
+ sessions: number;
56
+ msgs: number;
57
+ input: number;
58
+ output: number;
59
+ cacheRead: number;
60
+ totalTokens: number;
61
+ cost: number;
62
+ };
63
+ }
64
+
65
+ /** Token count with k/M/B units; 0 or negative = "-". */
66
+ export function formatTokens(n: number): string {
67
+ if (!n || n <= 0) return "-";
68
+ if (n < 1000) return String(n);
69
+ if (n < 1_000_000) {
70
+ const k = n / 1000;
71
+ return `${k < 10 ? k.toFixed(1) : Math.round(k)}k`;
72
+ }
73
+ if (n < 1_000_000_000) {
74
+ const m = n / 1_000_000;
75
+ return `${m.toFixed(1).replace(/\.0$/, "")}M`;
76
+ }
77
+ const b = n / 1_000_000_000;
78
+ return `${b.toFixed(1).replace(/\.0$/, "")}B`;
79
+ }
80
+
81
+ const CACHE_FILE_PATH = join(getAgentDir(), "arnative-usage-cache.json");
82
+ const CACHE_VERSION = 2; // v2: per-entry cost
83
+
84
+ // Disk cache is memoized; save writes memory + disk together.
85
+ let diskMemo: Map<string, FileCacheItem> | null = null;
86
+
87
+ function loadDiskCache(): Map<string, FileCacheItem> {
88
+ if (diskMemo) return diskMemo;
89
+ diskMemo = new Map<string, FileCacheItem>();
90
+ try {
91
+ if (existsSync(CACHE_FILE_PATH)) {
92
+ const raw = JSON.parse(readFileSync(CACHE_FILE_PATH, "utf8")) as Record<string, FileCacheItem>;
93
+ for (const [k, v] of Object.entries(raw)) {
94
+ diskMemo.set(k, v);
95
+ }
96
+ }
97
+ } catch {
98
+ // ignore
99
+ }
100
+ return diskMemo;
101
+ }
102
+
103
+ function saveDiskCache(map: Map<string, FileCacheItem>): void {
104
+ try {
105
+ const obj: Record<string, FileCacheItem> = {};
106
+ for (const [k, v] of map.entries()) {
107
+ obj[k] = v;
108
+ }
109
+ const dir = dirname(CACHE_FILE_PATH);
110
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
111
+ writeFileSync(CACHE_FILE_PATH, JSON.stringify(obj), "utf8");
112
+ } catch {
113
+ // ignore
114
+ }
115
+ }
116
+
117
+ function scanSessionFiles(dir: string): string[] {
118
+ const files: string[] = [];
119
+ try {
120
+ if (!existsSync(dir)) return files;
121
+ const list = readdirSync(dir, { withFileTypes: true });
122
+ for (const ent of list) {
123
+ const full = join(dir, ent.name);
124
+ if (ent.isDirectory()) {
125
+ files.push(...scanSessionFiles(full));
126
+ } else if (ent.isFile() && ent.name.endsWith(".jsonl")) {
127
+ files.push(full);
128
+ }
129
+ }
130
+ } catch {
131
+ // ignore
132
+ }
133
+ return files;
134
+ }
135
+
136
+ function parseSessionFile(filePath: string): UsageEntry[] {
137
+ const entries: UsageEntry[] = [];
138
+ try {
139
+ const content = readFileSync(filePath, "utf8");
140
+ const lines = content.split("\n");
141
+ for (const line of lines) {
142
+ if (!line || !line.includes('"role":"assistant"')) continue;
143
+ try {
144
+ const obj = JSON.parse(line);
145
+ const m = obj?.message;
146
+ if (m?.role === "assistant") {
147
+ const provider = String(m.provider || "unknown");
148
+ const model = String(m.model || "unknown");
149
+ const u = m.usage || {};
150
+ entries.push({
151
+ provider,
152
+ model,
153
+ input: Number(u.input) || 0,
154
+ output: Number(u.output) || 0,
155
+ cacheRead: Number(u.cacheRead) || 0,
156
+ cost: Number(u.cost?.total) || 0,
157
+ });
158
+ }
159
+ } catch {
160
+ // unparsable line
161
+ }
162
+ }
163
+ } catch {
164
+ // unreadable file
165
+ }
166
+ return entries;
167
+ }
168
+
169
+ let cachedSummary: UsageSummary | null = null;
170
+ let lastScanMs = 0;
171
+
172
+ export function collectUsageSummary(forceScan = false): UsageSummary {
173
+ const now = Date.now();
174
+ if (!forceScan && cachedSummary && now - lastScanMs < 5000) {
175
+ return cachedSummary;
176
+ }
177
+
178
+ const sessionsDir = join(getAgentDir(), "sessions");
179
+ const files = scanSessionFiles(sessionsDir);
180
+ const diskCache = loadDiskCache();
181
+ let cacheDirty = false;
182
+
183
+ const providerMap = new Map<
184
+ string,
185
+ {
186
+ sessions: Set<string>;
187
+ msgs: number;
188
+ input: number;
189
+ output: number;
190
+ cacheRead: number;
191
+ cost: number;
192
+ models: Map<string, { sessions: Set<string>; msgs: number; input: number; output: number; cacheRead: number; cost: number }>;
193
+ }
194
+ >();
195
+
196
+ for (const file of files) {
197
+ let stat;
198
+ try {
199
+ stat = statSync(file);
200
+ } catch {
201
+ continue;
202
+ }
203
+
204
+ let fileEntries: UsageEntry[];
205
+ const cached = diskCache.get(file);
206
+ if (cached && cached.v === CACHE_VERSION && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) {
207
+ fileEntries = cached.entries;
208
+ } else {
209
+ fileEntries = parseSessionFile(file);
210
+ diskCache.set(file, {
211
+ mtimeMs: stat.mtimeMs,
212
+ size: stat.size,
213
+ v: CACHE_VERSION,
214
+ entries: fileEntries,
215
+ });
216
+ cacheDirty = true;
217
+ }
218
+
219
+ for (const ent of fileEntries) {
220
+ if (!providerMap.has(ent.provider)) {
221
+ providerMap.set(ent.provider, {
222
+ sessions: new Set(),
223
+ msgs: 0,
224
+ input: 0,
225
+ output: 0,
226
+ cacheRead: 0,
227
+ cost: 0,
228
+ models: new Map(),
229
+ });
230
+ }
231
+ const p = providerMap.get(ent.provider)!;
232
+ p.sessions.add(file);
233
+ p.msgs++;
234
+ p.input += ent.input;
235
+ p.output += ent.output;
236
+ p.cacheRead += ent.cacheRead;
237
+ p.cost += ent.cost;
238
+
239
+ if (!p.models.has(ent.model)) {
240
+ p.models.set(ent.model, {
241
+ sessions: new Set(),
242
+ msgs: 0,
243
+ input: 0,
244
+ output: 0,
245
+ cacheRead: 0,
246
+ cost: 0,
247
+ });
248
+ }
249
+ const m = p.models.get(ent.model)!;
250
+ m.sessions.add(file);
251
+ m.msgs++;
252
+ m.input += ent.input;
253
+ m.output += ent.output;
254
+ m.cacheRead += ent.cacheRead;
255
+ m.cost += ent.cost;
256
+ }
257
+ }
258
+
259
+ if (cacheDirty) {
260
+ saveDiskCache(diskCache);
261
+ }
262
+
263
+ // Fold per-provider results into totals
264
+ let totalMsgs = 0;
265
+ let totalInp = 0;
266
+ let totalOut = 0;
267
+ let totalCache = 0;
268
+ let totalCost = 0;
269
+
270
+ const allSessions = new Set<string>();
271
+ const providers: UsageProviderStats[] = [];
272
+
273
+ for (const [pName, pData] of providerMap.entries()) {
274
+ for (const s of pData.sessions) allSessions.add(s);
275
+ totalMsgs += pData.msgs;
276
+ totalInp += pData.input;
277
+ totalOut += pData.output;
278
+ totalCache += pData.cacheRead;
279
+ totalCost += pData.cost;
280
+
281
+ const models: UsageModelStats[] = [];
282
+ for (const [mName, mData] of pData.models.entries()) {
283
+ models.push({
284
+ name: mName,
285
+ sessions: mData.sessions.size,
286
+ msgs: mData.msgs,
287
+ input: mData.input,
288
+ output: mData.output,
289
+ cacheRead: mData.cacheRead,
290
+ totalTokens: mData.input + mData.output,
291
+ cost: mData.cost,
292
+ });
293
+ }
294
+ models.sort((a, b) => b.totalTokens - a.totalTokens);
295
+
296
+ providers.push({
297
+ name: pName,
298
+ sessions: pData.sessions.size,
299
+ msgs: pData.msgs,
300
+ input: pData.input,
301
+ output: pData.output,
302
+ cacheRead: pData.cacheRead,
303
+ totalTokens: pData.input + pData.output,
304
+ cost: pData.cost,
305
+ models,
306
+ });
307
+ }
308
+
309
+ providers.sort((a, b) => b.totalTokens - a.totalTokens);
310
+
311
+ cachedSummary = {
312
+ providers,
313
+ totals: {
314
+ sessions: allSessions.size,
315
+ msgs: totalMsgs,
316
+ input: totalInp,
317
+ output: totalOut,
318
+ cacheRead: totalCache,
319
+ totalTokens: totalInp + totalOut,
320
+ cost: totalCost,
321
+ },
322
+ };
323
+ lastScanMs = now;
324
+ return cachedSummary;
325
+ }
326
+
327
+ // Match model names across id shapes ("gemini-3.8-flash", "vendor/gemini-…").
328
+ const modelMatches = (mNameLower: string, targetLower: string): boolean =>
329
+ !targetLower ||
330
+ mNameLower === targetLower ||
331
+ mNameLower.endsWith("/" + targetLower) ||
332
+ mNameLower.split(":")[0] === targetLower ||
333
+ targetLower.endsWith("/" + mNameLower);
334
+
335
+ /** All-time usage for one model (same numbers as /usage; the active session is already in its own file). */
336
+ export function getModelAllTimeUsage(modelId?: string): { input: number; output: number; cacheRead: number } {
337
+ const summary = collectUsageSummary();
338
+ let inp = 0;
339
+ let out = 0;
340
+ let read = 0;
341
+
342
+ const target = (modelId || "").toLowerCase();
343
+ for (const p of summary.providers) {
344
+ for (const m of p.models) {
345
+ if (modelMatches(m.name.toLowerCase(), target)) {
346
+ inp += m.input;
347
+ out += m.output;
348
+ read += m.cacheRead;
349
+ }
350
+ }
351
+ }
352
+
353
+ return { input: inp, output: out, cacheRead: read };
354
+ }
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@bismawy/pi-arnative",
3
+ "version": "0.1.0",
4
+ "description": "Arnative look for Pi: 11 warm-neutral themes, rounded tool boxes, branded header with tabs, 2-line footer with git/model/status, transcript clock, and a /usage dashboard.",
5
+ "keywords": [
6
+ "pi-package",
7
+ "pi-theme",
8
+ "pi-extension"
9
+ ],
10
+ "type": "module",
11
+ "license": "MIT",
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/bismawy/pi-arnative.git"
18
+ },
19
+ "homepage": "https://github.com/bismawy/pi-arnative#readme",
20
+ "files": [
21
+ "extensions",
22
+ "lib",
23
+ "themes",
24
+ "README.md"
25
+ ],
26
+ "pi": {
27
+ "extensions": [
28
+ "./extensions/*.ts"
29
+ ],
30
+ "themes": [
31
+ "./themes/*.json"
32
+ ]
33
+ },
34
+ "peerDependencies": {
35
+ "@earendil-works/pi-ai": "*",
36
+ "@earendil-works/pi-coding-agent": "*",
37
+ "@earendil-works/pi-tui": "*"
38
+ },
39
+ "scripts": {
40
+ "test": "node lib/box.ts && node lib/ansi.ts && node themes/gen-themes.mjs --check && node extensions/tools.ts && node extensions/footer.ts && node extensions/timestamps.ts && node extensions/section-headers.ts && node extensions/ui-render-tweaks.ts && node extensions/usage.ts",
41
+ "themes": "node themes/gen-themes.mjs"
42
+ }
43
+ }
@@ -0,0 +1,94 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
3
+ "name": "arnative-cyberpunk",
4
+ "vars": {
5
+ "cyan": "#00f0ff",
6
+ "blue": "#2a7fd4",
7
+ "green": "#00ff9f",
8
+ "red": "#ff4d6d",
9
+ "yellow": "#ffe600",
10
+ "text": "#eaeaf5",
11
+ "gray": "#8b88a8",
12
+ "dimGray": "#6f6a9a",
13
+ "darkGray": "#4a4763",
14
+ "accent": "#ff2e97",
15
+ "softCyan": "#7fd8f0",
16
+ "searchBg": "#5f3349",
17
+ "selectedBg": "#2b2350",
18
+ "userMsgBg": "#16131f",
19
+ "toolPendingBg": "#0d0b16",
20
+ "toolSuccessBg": "#0d2430",
21
+ "toolErrorBg": "#3a1524",
22
+ "customMsgBg": "#1a1329"
23
+ },
24
+ "colors": {
25
+ "accent": "accent",
26
+ "tint": "softCyan",
27
+ "border": "blue",
28
+ "borderAccent": "cyan",
29
+ "borderMuted": "darkGray",
30
+ "success": "green",
31
+ "error": "red",
32
+ "warning": "yellow",
33
+ "muted": "gray",
34
+ "dim": "dimGray",
35
+ "text": "text",
36
+ "thinkingText": "gray",
37
+
38
+ "selectedBg": "selectedBg",
39
+ "scrollbarTrack": "darkGray",
40
+ "scrollbarThumb": "softCyan",
41
+ "searchMatchBg": "searchBg",
42
+ "searchMatchText": "text",
43
+ "userMessageBg": "userMsgBg",
44
+ "userMessageText": "text",
45
+ "customMessageBg": "customMsgBg",
46
+ "customMessageText": "text",
47
+ "customMessageLabel": "#00f0ff",
48
+ "toolPendingBg": "toolPendingBg",
49
+ "toolSuccessBg": "toolSuccessBg",
50
+ "toolErrorBg": "toolErrorBg",
51
+ "toolTitle": "text",
52
+ "toolOutput": "gray",
53
+
54
+ "mdHeading": "#ffe600",
55
+ "mdLink": "#00f0ff",
56
+ "mdLinkUrl": "dimGray",
57
+ "mdCode": "accent",
58
+ "mdCodeBlock": "green",
59
+ "mdCodeBlockBorder": "gray",
60
+ "mdQuote": "gray",
61
+ "mdQuoteBorder": "gray",
62
+ "mdHr": "gray",
63
+ "mdListBullet": "accent",
64
+
65
+ "toolDiffAdded": "green",
66
+ "toolDiffRemoved": "red",
67
+ "toolDiffContext": "gray",
68
+
69
+ "syntaxComment": "#6f6a9a",
70
+ "syntaxKeyword": "#00f0ff",
71
+ "syntaxFunction": "#ffe600",
72
+ "syntaxVariable": "#ffd9f2",
73
+ "syntaxString": "#ff2e97",
74
+ "syntaxNumber": "#ff9a3c",
75
+ "syntaxType": "#00ff9f",
76
+ "syntaxOperator": "#c8c8e8",
77
+ "syntaxPunctuation": "#9a96c0",
78
+
79
+ "thinkingOff": "darkGray",
80
+ "thinkingMinimal": "#33285c",
81
+ "thinkingLow": "#4d3a94",
82
+ "thinkingMedium": "#7a4fd6",
83
+ "thinkingHigh": "#b84fe8",
84
+ "thinkingXhigh": "#ff4fc3",
85
+ "thinkingMax": "accent",
86
+
87
+ "bashMode": "green"
88
+ },
89
+ "export": {
90
+ "pageBg": "#0d0b16",
91
+ "cardBg": "#16131f",
92
+ "infoBg": "#3a2a10"
93
+ }
94
+ }
@@ -0,0 +1,94 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
3
+ "name": "arnative-dracula",
4
+ "vars": {
5
+ "cyan": "#8be9fd",
6
+ "blue": "#6272a4",
7
+ "green": "#50fa7b",
8
+ "red": "#ff6363",
9
+ "yellow": "#f1fa8c",
10
+ "text": "#f8f8f2",
11
+ "gray": "#8e96ba",
12
+ "dimGray": "#6272a4",
13
+ "darkGray": "#4a4e63",
14
+ "accent": "#bd93f9",
15
+ "softCyan": "#c9b8f2",
16
+ "searchBg": "#59427b",
17
+ "selectedBg": "#44475a",
18
+ "userMsgBg": "#343746",
19
+ "toolPendingBg": "#21222c",
20
+ "toolSuccessBg": "#26332e",
21
+ "toolErrorBg": "#412936",
22
+ "customMsgBg": "#383a52"
23
+ },
24
+ "colors": {
25
+ "accent": "accent",
26
+ "tint": "softCyan",
27
+ "border": "blue",
28
+ "borderAccent": "cyan",
29
+ "borderMuted": "darkGray",
30
+ "success": "green",
31
+ "error": "red",
32
+ "warning": "yellow",
33
+ "muted": "gray",
34
+ "dim": "dimGray",
35
+ "text": "text",
36
+ "thinkingText": "gray",
37
+
38
+ "selectedBg": "selectedBg",
39
+ "scrollbarTrack": "darkGray",
40
+ "scrollbarThumb": "softCyan",
41
+ "searchMatchBg": "searchBg",
42
+ "searchMatchText": "text",
43
+ "userMessageBg": "userMsgBg",
44
+ "userMessageText": "text",
45
+ "customMessageBg": "customMsgBg",
46
+ "customMessageText": "text",
47
+ "customMessageLabel": "#ff79c6",
48
+ "toolPendingBg": "toolPendingBg",
49
+ "toolSuccessBg": "toolSuccessBg",
50
+ "toolErrorBg": "toolErrorBg",
51
+ "toolTitle": "text",
52
+ "toolOutput": "gray",
53
+
54
+ "mdHeading": "#f1fa8c",
55
+ "mdLink": "#8be9fd",
56
+ "mdLinkUrl": "dimGray",
57
+ "mdCode": "accent",
58
+ "mdCodeBlock": "green",
59
+ "mdCodeBlockBorder": "gray",
60
+ "mdQuote": "gray",
61
+ "mdQuoteBorder": "gray",
62
+ "mdHr": "gray",
63
+ "mdListBullet": "accent",
64
+
65
+ "toolDiffAdded": "green",
66
+ "toolDiffRemoved": "red",
67
+ "toolDiffContext": "gray",
68
+
69
+ "syntaxComment": "#6272a4",
70
+ "syntaxKeyword": "#ff79c6",
71
+ "syntaxFunction": "#50fa7b",
72
+ "syntaxVariable": "#8be9fd",
73
+ "syntaxString": "#f1fa8c",
74
+ "syntaxNumber": "#bd93f9",
75
+ "syntaxType": "#8be9fd",
76
+ "syntaxOperator": "#ff79c6",
77
+ "syntaxPunctuation": "#f8f8f2",
78
+
79
+ "thinkingOff": "darkGray",
80
+ "thinkingMinimal": "#464a5d",
81
+ "thinkingLow": "#6272a4",
82
+ "thinkingMedium": "#7a6fd0",
83
+ "thinkingHigh": "#bd93f9",
84
+ "thinkingXhigh": "#ff79c6",
85
+ "thinkingMax": "accent",
86
+
87
+ "bashMode": "green"
88
+ },
89
+ "export": {
90
+ "pageBg": "#21222c",
91
+ "cardBg": "#282a36",
92
+ "infoBg": "#44475a"
93
+ }
94
+ }
@@ -0,0 +1,94 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
3
+ "name": "arnative-emerald",
4
+ "vars": {
5
+ "cyan": "#a7f3d0",
6
+ "blue": "#34b98a",
7
+ "green": "#b5bd68",
8
+ "red": "#d07272",
9
+ "yellow": "#ffff00",
10
+ "text": "#d4d4d4",
11
+ "gray": "#8d8d8d",
12
+ "dimGray": "#666666",
13
+ "darkGray": "#505050",
14
+ "accent": "#6ee7b7",
15
+ "softCyan": "#7eceaf",
16
+ "searchBg": "#356351",
17
+ "selectedBg": "#324840",
18
+ "userMsgBg": "#25312d",
19
+ "toolPendingBg": "#1f2925",
20
+ "toolSuccessBg": "#202828",
21
+ "toolErrorBg": "#342222",
22
+ "customMsgBg": "#1f3028"
23
+ },
24
+ "colors": {
25
+ "accent": "accent",
26
+ "tint": "softCyan",
27
+ "border": "blue",
28
+ "borderAccent": "cyan",
29
+ "borderMuted": "darkGray",
30
+ "success": "green",
31
+ "error": "red",
32
+ "warning": "yellow",
33
+ "muted": "gray",
34
+ "dim": "dimGray",
35
+ "text": "text",
36
+ "thinkingText": "gray",
37
+
38
+ "selectedBg": "selectedBg",
39
+ "scrollbarTrack": "darkGray",
40
+ "scrollbarThumb": "softCyan",
41
+ "searchMatchBg": "searchBg",
42
+ "searchMatchText": "text",
43
+ "userMessageBg": "userMsgBg",
44
+ "userMessageText": "text",
45
+ "customMessageBg": "customMsgBg",
46
+ "customMessageText": "text",
47
+ "customMessageLabel": "#9575cd",
48
+ "toolPendingBg": "toolPendingBg",
49
+ "toolSuccessBg": "toolSuccessBg",
50
+ "toolErrorBg": "toolErrorBg",
51
+ "toolTitle": "text",
52
+ "toolOutput": "gray",
53
+
54
+ "mdHeading": "#f0c674",
55
+ "mdLink": "#81a2be",
56
+ "mdLinkUrl": "dimGray",
57
+ "mdCode": "accent",
58
+ "mdCodeBlock": "green",
59
+ "mdCodeBlockBorder": "gray",
60
+ "mdQuote": "gray",
61
+ "mdQuoteBorder": "gray",
62
+ "mdHr": "gray",
63
+ "mdListBullet": "accent",
64
+
65
+ "toolDiffAdded": "green",
66
+ "toolDiffRemoved": "red",
67
+ "toolDiffContext": "gray",
68
+
69
+ "syntaxComment": "#6A9955",
70
+ "syntaxKeyword": "#569CD6",
71
+ "syntaxFunction": "#DCDCAA",
72
+ "syntaxVariable": "#9CDCFE",
73
+ "syntaxString": "#CE9178",
74
+ "syntaxNumber": "#B5CEA8",
75
+ "syntaxType": "#4EC9B0",
76
+ "syntaxOperator": "#D4D4D4",
77
+ "syntaxPunctuation": "#D4D4D4",
78
+
79
+ "thinkingOff": "darkGray",
80
+ "thinkingMinimal": "#305f4d",
81
+ "thinkingLow": "#3c8066",
82
+ "thinkingMedium": "#50aa87",
83
+ "thinkingHigh": "#41e1a4",
84
+ "thinkingXhigh": "#4af2b2",
85
+ "thinkingMax": "accent",
86
+
87
+ "bashMode": "green"
88
+ },
89
+ "export": {
90
+ "pageBg": "#18181e",
91
+ "cardBg": "#1e1e24",
92
+ "infoBg": "#3c3728"
93
+ }
94
+ }