tokmon 0.28.2 → 0.28.3
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 +156 -234
- package/THIRD_PARTY_NOTICES.md +62 -0
- package/dist/{bootstrap-ink-IMT3S5HU.js → bootstrap-ink-WG4WG3R2.js} +1579 -1136
- package/dist/chunk-7CH6S7BR.js +447 -0
- package/dist/{chunk-LCAHTRGP.js → chunk-CFKUZCMB.js} +56 -29
- package/dist/chunk-HNWEJ6MS.js +1094 -0
- package/dist/{chunk-WUK3MQUB.js → chunk-JZSZEHVD.js} +167 -10
- package/dist/{chunk-6ZEW2M6P.js → chunk-P57DVFNH.js} +125 -25
- package/dist/{chunk-LKYOWBD4.js → chunk-P5JN5DDX.js} +21 -42
- package/dist/chunk-TN6V7XSL.js +512 -0
- package/dist/{chunk-J3JW3RFP.js → chunk-USKOQIE3.js} +95 -13
- package/dist/chunk-UYPDMVW5.js +21 -0
- package/dist/{cli-command-DCENRCO3.js → cli-command-MD3RJRP4.js} +249 -15
- package/dist/cli.js +6 -6
- package/dist/{config-HXFJTNLM.js → config-3DGZ47F7.js} +23 -3
- package/dist/daemon-EMOA36WB.js +377 -0
- package/dist/daemon-handle-JHE5MKMI.js +11 -0
- package/dist/server-EPSH52DR.js +12 -0
- package/dist/web/assets/{Area-CMXvOw33.js → Area-stRnp8Km.js} +1 -1
- package/dist/web/assets/analytics-moZZt9L_.js +2 -0
- package/dist/web/assets/breakdown-km9w7myU.js +4 -0
- package/dist/web/assets/button-DrX04Cc2.js +1 -0
- package/dist/web/assets/{chart-cZoLgpmG.js → chart-D0CtuL2k.js} +1 -1
- package/dist/web/assets/explore-B11uNJV1.js +22 -0
- package/dist/web/assets/index-RDhXsCNG.js +83 -0
- package/dist/web/assets/index-ZmOS8p9O.css +1 -0
- package/dist/web/assets/models-DYbZnY34.js +2 -0
- package/dist/web/assets/overview-CruICbfJ.js +2 -0
- package/dist/web/assets/panel-xckM3_GC.js +1 -0
- package/dist/web/assets/settings-sheet-G-BTSIF6.js +1 -0
- package/dist/web/assets/share-sheet-weIdd4DW.js +2 -0
- package/dist/web/assets/timeline-BHgniliZ.js +1 -0
- package/dist/web/index.html +30 -4
- package/package.json +13 -4
- package/site/THIRD_PARTY_NOTICES.md +83 -0
- package/site/public/fonts/OFL.txt +93 -0
- package/dist/chunk-44ZSQEOS.js +0 -299
- package/dist/chunk-HP5UZCXP.js +0 -436
- package/dist/chunk-M7XMV36F.js +0 -275
- package/dist/daemon-6Y4O7NXS.js +0 -241
- package/dist/daemon-handle-GVCYVDPO.js +0 -10
- package/dist/server-GXXOWUVZ.js +0 -11
- package/dist/web/assets/analytics-DqdZXOJL.js +0 -2
- package/dist/web/assets/breakdown-kOSSJ3mI.js +0 -4
- package/dist/web/assets/explore-vcsM8K9I.js +0 -22
- package/dist/web/assets/index-BErCXT7f.css +0 -1
- package/dist/web/assets/index-DH3dOnbg.js +0 -83
- package/dist/web/assets/models-B5ikm83_.js +0 -2
- package/dist/web/assets/overview-DD1kP8CP.js +0 -2
- package/dist/web/assets/panel-DTQBwExW.js +0 -1
- package/dist/web/assets/primitives-C36qJlA2.js +0 -1
- package/dist/web/assets/settings-sheet-_i2zTQjf.js +0 -1
- package/dist/web/assets/share-sheet-BLHmrFbm.js +0 -2
- package/dist/web/assets/timeline-B4uG-Emo.js +0 -1
- package/dist/web/assets/use-dialog-trap-bfaXEMz9.js +0 -1
|
@@ -0,0 +1,1094 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/config.ts
|
|
4
|
+
import { chmod, readFile, writeFile, mkdir, rename, unlink } from "fs/promises";
|
|
5
|
+
import { chmodSync, mkdirSync, renameSync, unlinkSync, writeFileSync } from "fs";
|
|
6
|
+
import { join, isAbsolute } from "path";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
|
|
9
|
+
// src/providers/types.ts
|
|
10
|
+
var PROVIDER_IDS = ["claude", "codex", "cursor", "copilot", "pi", "opencode", "antigravity", "gemini", "grok"];
|
|
11
|
+
|
|
12
|
+
// src/theme/catalog.ts
|
|
13
|
+
var IMPORTED_THEME_IDS = [
|
|
14
|
+
"vscode",
|
|
15
|
+
"monokai",
|
|
16
|
+
"dracula",
|
|
17
|
+
"github",
|
|
18
|
+
"nord",
|
|
19
|
+
"one-dark-pro",
|
|
20
|
+
"solarized",
|
|
21
|
+
"tokyo-night",
|
|
22
|
+
"catppuccin",
|
|
23
|
+
"midnight",
|
|
24
|
+
"forest",
|
|
25
|
+
"sunset",
|
|
26
|
+
"cyberpunk",
|
|
27
|
+
"synthwave",
|
|
28
|
+
"luxury",
|
|
29
|
+
"minimal"
|
|
30
|
+
];
|
|
31
|
+
var IMPORTED_THEME_CATALOG = {
|
|
32
|
+
vscode: {
|
|
33
|
+
name: "VS Code",
|
|
34
|
+
light: { name: "Light+", primary: "#007ACC", background: "#FFFFFF", foreground: "#333333", card: "#F3F3F3", cardForeground: "#1E1E1E", border: "#E5E5E5", accent: "#0066BF", accentForeground: "#FFFFFF", muted: "#F6F6F6", mutedForeground: "#6E6E6E" },
|
|
35
|
+
dark: { name: "Dark+", primary: "#007ACC", background: "#1E1E1E", foreground: "#CCCCCC", card: "#252526", cardForeground: "#CCCCCC", border: "#3E3E42", accent: "#0E639C", accentForeground: "#FFFFFF", muted: "#2D2D30", mutedForeground: "#858585" }
|
|
36
|
+
},
|
|
37
|
+
monokai: {
|
|
38
|
+
name: "Monokai",
|
|
39
|
+
light: { name: "Monokai Light", primary: "#A6E22E", background: "#F8F8F2", foreground: "#272822", card: "#E6E6E0", cardForeground: "#272822", border: "#C4C4B8", accent: "#66D9EF", accentForeground: "#272822", muted: "#E6E6E0", mutedForeground: "#75715E" },
|
|
40
|
+
dark: { name: "Monokai", primary: "#A6E22E", background: "#272822", foreground: "#F8F8F2", card: "#3E3D32", cardForeground: "#F8F8F2", border: "#75715E", accent: "#66D9EF", accentForeground: "#272822", muted: "#49483E", mutedForeground: "#75715E" }
|
|
41
|
+
},
|
|
42
|
+
dracula: {
|
|
43
|
+
name: "Dracula",
|
|
44
|
+
light: { name: "Dracula Light", primary: "#BD93F9", background: "#F8F8F2", foreground: "#282A36", card: "#E6E6E0", cardForeground: "#282A36", border: "#C4C4B8", accent: "#FF79C6", accentForeground: "#282A36", muted: "#E6E6E0", mutedForeground: "#6272A4" },
|
|
45
|
+
dark: { name: "Dracula", primary: "#BD93F9", background: "#282A36", foreground: "#F8F8F2", card: "#343746", cardForeground: "#F8F8F2", border: "#44475A", accent: "#FF79C6", accentForeground: "#282A36", muted: "#44475A", mutedForeground: "#6272A4" }
|
|
46
|
+
},
|
|
47
|
+
github: {
|
|
48
|
+
name: "GitHub",
|
|
49
|
+
light: { name: "GitHub Light", primary: "#0969DA", background: "#FFFFFF", foreground: "#24292F", card: "#F6F8FA", cardForeground: "#24292F", border: "#D0D7DE", accent: "#0969DA", accentForeground: "#FFFFFF", muted: "#F6F8FA", mutedForeground: "#57606A" },
|
|
50
|
+
dark: { name: "GitHub Dark", primary: "#58A6FF", background: "#0D1117", foreground: "#C9D1D9", card: "#161B22", cardForeground: "#C9D1D9", border: "#30363D", accent: "#1F6FEB", accentForeground: "#FFFFFF", muted: "#161B22", mutedForeground: "#8B949E" }
|
|
51
|
+
},
|
|
52
|
+
nord: {
|
|
53
|
+
name: "Nord",
|
|
54
|
+
light: { name: "Nord Light", primary: "#5E81AC", background: "#ECEFF4", foreground: "#2E3440", card: "#E5E9F0", cardForeground: "#2E3440", border: "#D8DEE9", accent: "#88C0D0", accentForeground: "#2E3440", muted: "#E5E9F0", mutedForeground: "#4C566A" },
|
|
55
|
+
dark: { name: "Nord", primary: "#88C0D0", background: "#2E3440", foreground: "#D8DEE9", card: "#3B4252", cardForeground: "#E5E9F0", border: "#434C5E", accent: "#5E81AC", accentForeground: "#ECEFF4", muted: "#434C5E", mutedForeground: "#88C0D0" }
|
|
56
|
+
},
|
|
57
|
+
"one-dark-pro": {
|
|
58
|
+
name: "One Dark Pro",
|
|
59
|
+
light: { name: "One Light Pro", primary: "#528BFF", background: "#FAFAFA", foreground: "#383A42", card: "#F0F0F0", cardForeground: "#383A42", border: "#E0E0E0", accent: "#61AFEF", accentForeground: "#FAFAFA", muted: "#F0F0F0", mutedForeground: "#5C6370" },
|
|
60
|
+
dark: { name: "One Dark Pro", primary: "#61AFEF", background: "#282C34", foreground: "#ABB2BF", card: "#2C313C", cardForeground: "#ABB2BF", border: "#3E4451", accent: "#528BFF", accentForeground: "#FFFFFF", muted: "#3E4451", mutedForeground: "#5C6370" }
|
|
61
|
+
},
|
|
62
|
+
solarized: {
|
|
63
|
+
name: "Solarized",
|
|
64
|
+
light: { name: "Solarized Light", primary: "#268BD2", background: "#FDF6E3", foreground: "#657B83", card: "#EEE8D5", cardForeground: "#586E75", border: "#93A1A1", accent: "#2AA198", accentForeground: "#FDF6E3", muted: "#FDF6E3", mutedForeground: "#839496" },
|
|
65
|
+
dark: { name: "Solarized Dark", primary: "#268BD2", background: "#002B36", foreground: "#839496", card: "#073642", cardForeground: "#93A1A1", border: "#586E75", accent: "#2AA198", accentForeground: "#FDF6E3", muted: "#073642", mutedForeground: "#657B83" }
|
|
66
|
+
},
|
|
67
|
+
"tokyo-night": {
|
|
68
|
+
name: "Tokyo Night",
|
|
69
|
+
light: { name: "Tokyo Night Day", primary: "#2E7DE9", background: "#E1E2E7", foreground: "#343B58", card: "#D5D6DB", cardForeground: "#343B58", border: "#9699A3", accent: "#7AA2F7", accentForeground: "#343B58", muted: "#D5D6DB", mutedForeground: "#565F89" },
|
|
70
|
+
dark: { name: "Tokyo Night", primary: "#7AA2F7", background: "#1A1B26", foreground: "#C0CAF5", card: "#24283B", cardForeground: "#C0CAF5", border: "#414868", accent: "#BB9AF7", accentForeground: "#1A1B26", muted: "#2F3549", mutedForeground: "#565F89" }
|
|
71
|
+
},
|
|
72
|
+
catppuccin: {
|
|
73
|
+
name: "Catppuccin",
|
|
74
|
+
light: { name: "Catppuccin Latte", primary: "#1E66F5", background: "#EFF1F5", foreground: "#4C4F69", card: "#E6E9EF", cardForeground: "#4C4F69", border: "#BCC0CC", accent: "#EA76CB", accentForeground: "#EFF1F5", muted: "#E6E9EF", mutedForeground: "#6C6F85" },
|
|
75
|
+
dark: { name: "Catppuccin Mocha", primary: "#89B4FA", background: "#1E1E2E", foreground: "#CDD6F4", card: "#313244", cardForeground: "#CDD6F4", border: "#45475A", accent: "#F38BA8", accentForeground: "#1E1E2E", muted: "#313244", mutedForeground: "#6C7086" }
|
|
76
|
+
},
|
|
77
|
+
midnight: {
|
|
78
|
+
name: "Midnight",
|
|
79
|
+
light: { name: "Midnight Light", primary: "#3B82F6", background: "#F8FAFC", foreground: "#0F172A", card: "#E2E8F0", cardForeground: "#0F172A", border: "#CBD5E1", accent: "#1D4ED8", accentForeground: "#F8FAFC", muted: "#E2E8F0", mutedForeground: "#64748B" },
|
|
80
|
+
dark: { name: "Midnight", primary: "#3B82F6", background: "#0F172A", foreground: "#F8FAFC", card: "#1E293B", cardForeground: "#F8FAFC", border: "#334155", accent: "#1D4ED8", accentForeground: "#FFFFFF", muted: "#1E293B", mutedForeground: "#94A3B8" }
|
|
81
|
+
},
|
|
82
|
+
forest: {
|
|
83
|
+
name: "Forest",
|
|
84
|
+
light: { name: "Forest Light", primary: "#22C55E", background: "#F0FDF4", foreground: "#052E16", card: "#DCFCE7", cardForeground: "#052E16", border: "#BBF7D0", accent: "#15803D", accentForeground: "#F0FDF4", muted: "#DCFCE7", mutedForeground: "#166534" },
|
|
85
|
+
dark: { name: "Forest", primary: "#22C55E", background: "#052E16", foreground: "#DCFCE7", card: "#14532D", cardForeground: "#DCFCE7", border: "#166534", accent: "#15803D", accentForeground: "#FFFFFF", muted: "#14532D", mutedForeground: "#86EFAC" }
|
|
86
|
+
},
|
|
87
|
+
sunset: {
|
|
88
|
+
name: "Sunset",
|
|
89
|
+
light: { name: "Sunset Light", primary: "#F97316", background: "#FFF7ED", foreground: "#431407", card: "#FFEDD5", cardForeground: "#431407", border: "#FED7AA", accent: "#EA580C", accentForeground: "#FFF7ED", muted: "#FFEDD5", mutedForeground: "#9A3412" },
|
|
90
|
+
dark: { name: "Sunset", primary: "#F97316", background: "#431407", foreground: "#FFEDD5", card: "#7C2D12", cardForeground: "#FFEDD5", border: "#9A3412", accent: "#EA580C", accentForeground: "#FFFFFF", muted: "#7C2D12", mutedForeground: "#FDBA74" }
|
|
91
|
+
},
|
|
92
|
+
cyberpunk: {
|
|
93
|
+
name: "Cyberpunk",
|
|
94
|
+
light: { name: "Cyberpunk Light", primary: "#F472B6", background: "#FFFFFF", foreground: "#000000", card: "#F3F4F6", cardForeground: "#000000", border: "#E5E7EB", accent: "#E879F9", accentForeground: "#000000", muted: "#F3F4F6", mutedForeground: "#22D3EE" },
|
|
95
|
+
dark: { name: "Cyberpunk", primary: "#F472B6", background: "#000000", foreground: "#22D3EE", card: "#111111", cardForeground: "#E879F9", border: "#F472B6", accent: "#FFFF00", accentForeground: "#000000", muted: "#111111", mutedForeground: "#22D3EE" }
|
|
96
|
+
},
|
|
97
|
+
synthwave: {
|
|
98
|
+
name: "Synthwave",
|
|
99
|
+
light: { name: "Synthwave Light", primary: "#D946EF", background: "#FAF5FF", foreground: "#2A0A3B", card: "#F3E8FF", cardForeground: "#2A0A3B", border: "#E9D5FF", accent: "#06B6D4", accentForeground: "#FAF5FF", muted: "#F3E8FF", mutedForeground: "#C026D3" },
|
|
100
|
+
dark: { name: "Synthwave", primary: "#D946EF", background: "#2A0A3B", foreground: "#F5D0FE", card: "#4A1D66", cardForeground: "#F5D0FE", border: "#C026D3", accent: "#06B6D4", accentForeground: "#FFFFFF", muted: "#4A1D66", mutedForeground: "#E879F9" }
|
|
101
|
+
},
|
|
102
|
+
luxury: {
|
|
103
|
+
name: "Luxury",
|
|
104
|
+
light: { name: "Luxury Light", primary: "#D4AF37", background: "#FAFAF9", foreground: "#1C1917", card: "#F5F5F4", cardForeground: "#1C1917", border: "#E7E5E4", accent: "#B45309", accentForeground: "#FAFAF9", muted: "#F5F5F4", mutedForeground: "#78716C" },
|
|
105
|
+
dark: { name: "Luxury", primary: "#D4AF37", background: "#1C1917", foreground: "#E7E5E4", card: "#292524", cardForeground: "#D4AF37", border: "#78716C", accent: "#B45309", accentForeground: "#FFFFFF", muted: "#292524", mutedForeground: "#A8A29E" }
|
|
106
|
+
},
|
|
107
|
+
minimal: {
|
|
108
|
+
name: "Minimal",
|
|
109
|
+
light: { name: "Minimal", primary: "#18181B", background: "#FFFFFF", foreground: "#18181B", card: "#FFFFFF", cardForeground: "#18181B", border: "#E4E4E7", accent: "#F4F4F5", accentForeground: "#18181B", muted: "#FAFAFA", mutedForeground: "#71717A" },
|
|
110
|
+
dark: { name: "Minimal Dark", primary: "#FAFAFA", background: "#18181B", foreground: "#FAFAFA", card: "#27272A", cardForeground: "#FAFAFA", border: "#3F3F46", accent: "#27272A", accentForeground: "#FAFAFA", muted: "#27272A", mutedForeground: "#A1A1AA" }
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// src/theme/index.ts
|
|
115
|
+
var BUILT_IN_THEME_PRESET_IDS = ["tokmon", "phosphor", ...IMPORTED_THEME_IDS];
|
|
116
|
+
var THEME_PRESET_IDS = [...BUILT_IN_THEME_PRESET_IDS, "custom"];
|
|
117
|
+
var IMPORTED_THEME_HINTS = {
|
|
118
|
+
vscode: "VS Code Light+ and Dark+",
|
|
119
|
+
monokai: "Classic editor green and cyan",
|
|
120
|
+
dracula: "Purple and pink on deep slate",
|
|
121
|
+
github: "GitHub light and dark",
|
|
122
|
+
nord: "Arctic blue-gray palette",
|
|
123
|
+
"one-dark-pro": "Atom-inspired editor palette",
|
|
124
|
+
solarized: "Precision blue and teal",
|
|
125
|
+
"tokyo-night": "Cool blue and violet",
|
|
126
|
+
catppuccin: "Latte and Mocha palettes",
|
|
127
|
+
midnight: "Deep navy and bright blue",
|
|
128
|
+
forest: "Natural green surfaces",
|
|
129
|
+
sunset: "Warm orange and earth tones",
|
|
130
|
+
cyberpunk: "Neon cyan, pink, and yellow",
|
|
131
|
+
synthwave: "Purple and electric cyan",
|
|
132
|
+
luxury: "Gold on warm neutrals",
|
|
133
|
+
minimal: "High-contrast monochrome"
|
|
134
|
+
};
|
|
135
|
+
var THEME_PRESET_OPTIONS = [
|
|
136
|
+
{ id: "tokmon", name: "Tokmon", hint: "Calm system utility", darkOnly: false, custom: false },
|
|
137
|
+
{ id: "phosphor", name: "Phosphor", hint: "Black terminal green", darkOnly: true, custom: false },
|
|
138
|
+
...IMPORTED_THEME_IDS.map((id) => ({
|
|
139
|
+
id,
|
|
140
|
+
name: IMPORTED_THEME_CATALOG[id].name,
|
|
141
|
+
hint: IMPORTED_THEME_HINTS[id],
|
|
142
|
+
darkOnly: false,
|
|
143
|
+
custom: false
|
|
144
|
+
})),
|
|
145
|
+
{ id: "custom", name: "Custom", hint: "Your shared palette", darkOnly: false, custom: true }
|
|
146
|
+
];
|
|
147
|
+
function isThemePreset(value) {
|
|
148
|
+
return typeof value === "string" && THEME_PRESET_IDS.includes(value);
|
|
149
|
+
}
|
|
150
|
+
function isBuiltInThemePreset(value) {
|
|
151
|
+
return typeof value === "string" && BUILT_IN_THEME_PRESET_IDS.includes(value);
|
|
152
|
+
}
|
|
153
|
+
function themePresetOption(preset) {
|
|
154
|
+
return THEME_PRESET_OPTIONS.find((option) => option.id === preset) ?? THEME_PRESET_OPTIONS[0];
|
|
155
|
+
}
|
|
156
|
+
function isDarkOnlyThemePreset(preset) {
|
|
157
|
+
return themePresetOption(preset).darkOnly;
|
|
158
|
+
}
|
|
159
|
+
var EDITABLE_THEME_TOKEN_KEYS = [
|
|
160
|
+
"canvas",
|
|
161
|
+
"panel",
|
|
162
|
+
"inset",
|
|
163
|
+
"insetStrong",
|
|
164
|
+
"chrome",
|
|
165
|
+
"line",
|
|
166
|
+
"lineStrong",
|
|
167
|
+
"lineFaint",
|
|
168
|
+
"text",
|
|
169
|
+
"textDim",
|
|
170
|
+
"textFaint",
|
|
171
|
+
"textStrong",
|
|
172
|
+
"accent",
|
|
173
|
+
"cost",
|
|
174
|
+
"positive"
|
|
175
|
+
];
|
|
176
|
+
var DEFAULT_APPEARANCE = Object.freeze({
|
|
177
|
+
version: 1,
|
|
178
|
+
mode: "auto",
|
|
179
|
+
preset: "tokmon",
|
|
180
|
+
terminal: "ansi"
|
|
181
|
+
});
|
|
182
|
+
var TOKMON_DARK = {
|
|
183
|
+
canvas: "#0a0a0a",
|
|
184
|
+
panel: "#101011",
|
|
185
|
+
inset: "#161617",
|
|
186
|
+
insetStrong: "#1d1d1e",
|
|
187
|
+
chrome: "#1e1f22",
|
|
188
|
+
line: "#262627",
|
|
189
|
+
lineStrong: "#343435",
|
|
190
|
+
lineFaint: "#1a1a1b",
|
|
191
|
+
text: "#d4d6d6",
|
|
192
|
+
textDim: "#8d9090",
|
|
193
|
+
textFaint: "#585b5b",
|
|
194
|
+
textStrong: "#f3f5f5",
|
|
195
|
+
accent: "#79be7e",
|
|
196
|
+
cost: "#d9c074",
|
|
197
|
+
positive: "#6caa71",
|
|
198
|
+
ok: "#79be7e",
|
|
199
|
+
warn: "#e0b84c",
|
|
200
|
+
crit: "#e5584b",
|
|
201
|
+
unknown: "#8d9090"
|
|
202
|
+
};
|
|
203
|
+
var TOKMON_LIGHT = {
|
|
204
|
+
canvas: "#f4f5f5",
|
|
205
|
+
panel: "#ffffff",
|
|
206
|
+
inset: "#eceeee",
|
|
207
|
+
insetStrong: "#e1e4e4",
|
|
208
|
+
chrome: "#f4f5f5",
|
|
209
|
+
line: "#d4d9d9",
|
|
210
|
+
lineStrong: "#bcc2c2",
|
|
211
|
+
lineFaint: "#e7eaea",
|
|
212
|
+
text: "#2b2e2e",
|
|
213
|
+
textDim: "#5a6060",
|
|
214
|
+
textFaint: "#979d9d",
|
|
215
|
+
textStrong: "#0d1010",
|
|
216
|
+
accent: "#2f8f57",
|
|
217
|
+
cost: "#9a7b1c",
|
|
218
|
+
positive: "#2f8f57",
|
|
219
|
+
ok: "#2f8f57",
|
|
220
|
+
warn: "#9a7b1c",
|
|
221
|
+
crit: "#b0403a",
|
|
222
|
+
unknown: "#5a6060"
|
|
223
|
+
};
|
|
224
|
+
var PHOSPHOR_DARK = {
|
|
225
|
+
canvas: "#000000",
|
|
226
|
+
panel: "#050705",
|
|
227
|
+
inset: "#0a0f0b",
|
|
228
|
+
insetStrong: "#101710",
|
|
229
|
+
chrome: "#070b08",
|
|
230
|
+
line: "#1a2a1e",
|
|
231
|
+
lineStrong: "#2c4634",
|
|
232
|
+
lineFaint: "#101812",
|
|
233
|
+
text: "#c6f7d3",
|
|
234
|
+
textDim: "#7fc98d",
|
|
235
|
+
textFaint: "#4c7d57",
|
|
236
|
+
textStrong: "#eafff0",
|
|
237
|
+
accent: "#35f38a",
|
|
238
|
+
cost: "#ffd24a",
|
|
239
|
+
positive: "#35f38a",
|
|
240
|
+
ok: "#35f38a",
|
|
241
|
+
warn: "#ffc44d",
|
|
242
|
+
crit: "#ff6b5b",
|
|
243
|
+
unknown: "#7fc98d"
|
|
244
|
+
};
|
|
245
|
+
var PHOSPHOR_LIGHT = {
|
|
246
|
+
canvas: "#f3f5f0",
|
|
247
|
+
panel: "#ffffff",
|
|
248
|
+
inset: "#e8ede7",
|
|
249
|
+
insetStrong: "#dce5dc",
|
|
250
|
+
chrome: "#f3f5f0",
|
|
251
|
+
line: "#cad7ca",
|
|
252
|
+
lineStrong: "#a9bca9",
|
|
253
|
+
lineFaint: "#e4ebe4",
|
|
254
|
+
text: "#0b2610",
|
|
255
|
+
textDim: "#426b49",
|
|
256
|
+
textFaint: "#748c78",
|
|
257
|
+
textStrong: "#031507",
|
|
258
|
+
accent: "#128a2e",
|
|
259
|
+
cost: "#826500",
|
|
260
|
+
positive: "#128a2e",
|
|
261
|
+
ok: "#128a2e",
|
|
262
|
+
warn: "#826500",
|
|
263
|
+
crit: "#b23a32",
|
|
264
|
+
unknown: "#5c705f"
|
|
265
|
+
};
|
|
266
|
+
var HEX_COLOR = /^#[0-9a-f]{6}$/i;
|
|
267
|
+
function normalizeHexColor(value) {
|
|
268
|
+
return typeof value === "string" && HEX_COLOR.test(value) ? value.toLowerCase() : null;
|
|
269
|
+
}
|
|
270
|
+
function channels(hex) {
|
|
271
|
+
return [Number.parseInt(hex.slice(1, 3), 16), Number.parseInt(hex.slice(3, 5), 16), Number.parseInt(hex.slice(5, 7), 16)];
|
|
272
|
+
}
|
|
273
|
+
function hexChannel(value) {
|
|
274
|
+
return Math.round(Math.max(0, Math.min(255, value))).toString(16).padStart(2, "0");
|
|
275
|
+
}
|
|
276
|
+
function mixColors(from, to, amount) {
|
|
277
|
+
const [fr, fg, fb] = channels(from);
|
|
278
|
+
const [tr, tg, tb] = channels(to);
|
|
279
|
+
const ratio = Math.max(0, Math.min(1, amount));
|
|
280
|
+
return `#${hexChannel(fr + (tr - fr) * ratio)}${hexChannel(fg + (tg - fg) * ratio)}${hexChannel(fb + (tb - fb) * ratio)}`;
|
|
281
|
+
}
|
|
282
|
+
function relativeLuminance(hex) {
|
|
283
|
+
const normalized = normalizeHexColor(hex);
|
|
284
|
+
if (!normalized) throw new TypeError("expected a #RRGGBB color");
|
|
285
|
+
const linear = channels(normalized).map((channel) => {
|
|
286
|
+
const value = channel / 255;
|
|
287
|
+
return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
|
|
288
|
+
});
|
|
289
|
+
return 0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2];
|
|
290
|
+
}
|
|
291
|
+
function contrastRatio(first, second) {
|
|
292
|
+
const a = relativeLuminance(first);
|
|
293
|
+
const b = relativeLuminance(second);
|
|
294
|
+
return (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);
|
|
295
|
+
}
|
|
296
|
+
function accessibleColor(color, backgrounds, minimum, toward) {
|
|
297
|
+
const intended = normalizeHexColor(color);
|
|
298
|
+
if (backgrounds.every((background) => contrastRatio(intended, background) >= minimum)) return intended;
|
|
299
|
+
const target = [toward, "#000000", "#ffffff"].reduce((best, candidate) => Math.min(...backgrounds.map((background) => contrastRatio(candidate, background))) > Math.min(...backgrounds.map((background) => contrastRatio(best, background))) ? candidate : best);
|
|
300
|
+
let low = 0;
|
|
301
|
+
let high = 1;
|
|
302
|
+
for (let i = 0; i < 16; i++) {
|
|
303
|
+
const mid = (low + high) / 2;
|
|
304
|
+
const candidate = mixColors(intended, target, mid);
|
|
305
|
+
if (backgrounds.every((background) => contrastRatio(candidate, background) >= minimum)) high = mid;
|
|
306
|
+
else low = mid;
|
|
307
|
+
}
|
|
308
|
+
return mixColors(intended, target, high);
|
|
309
|
+
}
|
|
310
|
+
function importedTokens(colors, mode) {
|
|
311
|
+
const canvas = normalizeHexColor(colors.background);
|
|
312
|
+
const panel = normalizeHexColor(colors.card);
|
|
313
|
+
const rawText = normalizeHexColor(colors.foreground);
|
|
314
|
+
const contrastTarget = mode === "dark" ? "#ffffff" : "#000000";
|
|
315
|
+
const text = accessibleColor(rawText, [canvas, panel], 4.5, contrastTarget);
|
|
316
|
+
const textStrong = accessibleColor(colors.cardForeground, [canvas, panel], 4.5, contrastTarget);
|
|
317
|
+
const textDim = accessibleColor(colors.mutedForeground, [canvas], 3, text);
|
|
318
|
+
const accent = accessibleColor(colors.primary, [canvas, panel], 3, text);
|
|
319
|
+
const cost = accessibleColor(colors.accent, [panel], 3, text);
|
|
320
|
+
const positive = accessibleColor(colors.primary, [panel], 3, text);
|
|
321
|
+
const truth = mode === "dark" ? TOKMON_DARK : TOKMON_LIGHT;
|
|
322
|
+
return {
|
|
323
|
+
canvas,
|
|
324
|
+
panel,
|
|
325
|
+
inset: normalizeHexColor(colors.muted),
|
|
326
|
+
insetStrong: mixColors(colors.muted, colors.foreground, 0.08),
|
|
327
|
+
chrome: canvas,
|
|
328
|
+
line: normalizeHexColor(colors.border),
|
|
329
|
+
lineStrong: mixColors(colors.border, colors.foreground, 0.18),
|
|
330
|
+
lineFaint: mixColors(colors.background, colors.border, 0.5),
|
|
331
|
+
text,
|
|
332
|
+
textDim,
|
|
333
|
+
textFaint: normalizeHexColor(colors.mutedForeground),
|
|
334
|
+
textStrong,
|
|
335
|
+
accent,
|
|
336
|
+
cost,
|
|
337
|
+
positive,
|
|
338
|
+
ok: accessibleColor(truth.ok, [panel], 3, text),
|
|
339
|
+
warn: accessibleColor(truth.warn, [panel], 3, text),
|
|
340
|
+
crit: accessibleColor(truth.crit, [panel], 3, text),
|
|
341
|
+
unknown: textDim
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
var IMPORTED_PRESETS = Object.fromEntries(IMPORTED_THEME_IDS.map((id) => [
|
|
345
|
+
id,
|
|
346
|
+
{
|
|
347
|
+
light: importedTokens(IMPORTED_THEME_CATALOG[id].light, "light"),
|
|
348
|
+
dark: importedTokens(IMPORTED_THEME_CATALOG[id].dark, "dark")
|
|
349
|
+
}
|
|
350
|
+
]));
|
|
351
|
+
var PRESETS = {
|
|
352
|
+
tokmon: { light: TOKMON_LIGHT, dark: TOKMON_DARK },
|
|
353
|
+
phosphor: { light: PHOSPHOR_LIGHT, dark: PHOSPHOR_DARK },
|
|
354
|
+
...IMPORTED_PRESETS
|
|
355
|
+
};
|
|
356
|
+
var CONTRAST_RULES = [
|
|
357
|
+
["text", "canvas", 4.5],
|
|
358
|
+
["text", "panel", 4.5],
|
|
359
|
+
["textStrong", "canvas", 4.5],
|
|
360
|
+
["textStrong", "panel", 4.5],
|
|
361
|
+
["textDim", "canvas", 3],
|
|
362
|
+
["accent", "canvas", 3],
|
|
363
|
+
["accent", "panel", 3],
|
|
364
|
+
["cost", "panel", 3],
|
|
365
|
+
["positive", "panel", 3],
|
|
366
|
+
["ok", "panel", 3],
|
|
367
|
+
["warn", "panel", 3],
|
|
368
|
+
["crit", "panel", 3]
|
|
369
|
+
];
|
|
370
|
+
function validateThemeContrast(tokens) {
|
|
371
|
+
return CONTRAST_RULES.flatMap(([foreground, background, minimum]) => {
|
|
372
|
+
const actual = contrastRatio(tokens[foreground], tokens[background]);
|
|
373
|
+
return actual + Number.EPSILON < minimum ? [{ foreground, background, minimum, actual }] : [];
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
function isRecord(value) {
|
|
377
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
378
|
+
}
|
|
379
|
+
function cleanOverrides(value, mode, base, reasons) {
|
|
380
|
+
if (!isRecord(value)) {
|
|
381
|
+
if (value !== void 0) reasons.push(`custom.${mode} was not an object`);
|
|
382
|
+
return {};
|
|
383
|
+
}
|
|
384
|
+
const overrides = {};
|
|
385
|
+
for (const key of EDITABLE_THEME_TOKEN_KEYS) {
|
|
386
|
+
if (!(key in value)) continue;
|
|
387
|
+
const color = normalizeHexColor(value[key]);
|
|
388
|
+
if (color) overrides[key] = color;
|
|
389
|
+
else reasons.push(`custom.${mode}.${key} was not a #RRGGBB color`);
|
|
390
|
+
}
|
|
391
|
+
for (; ; ) {
|
|
392
|
+
const candidate = { ...base, ...overrides };
|
|
393
|
+
const failure = validateThemeContrast(candidate)[0];
|
|
394
|
+
if (!failure) break;
|
|
395
|
+
const foreground = failure.foreground;
|
|
396
|
+
const background = failure.background;
|
|
397
|
+
if (foreground in overrides) {
|
|
398
|
+
delete overrides[foreground];
|
|
399
|
+
reasons.push(`custom.${mode}.${foreground} did not meet WCAG contrast`);
|
|
400
|
+
} else if (background in overrides) {
|
|
401
|
+
delete overrides[background];
|
|
402
|
+
reasons.push(`custom.${mode}.${background} did not meet WCAG contrast`);
|
|
403
|
+
} else {
|
|
404
|
+
break;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return overrides;
|
|
408
|
+
}
|
|
409
|
+
function repairAppearance(input) {
|
|
410
|
+
const reasons = [];
|
|
411
|
+
const raw = isRecord(input) ? input : {};
|
|
412
|
+
if (raw !== input && input !== void 0) reasons.push("appearance was not an object");
|
|
413
|
+
const mode = raw.mode === "light" || raw.mode === "dark" || raw.mode === "auto" ? raw.mode : DEFAULT_APPEARANCE.mode;
|
|
414
|
+
if (raw.mode !== void 0 && raw.mode !== mode) reasons.push("appearance.mode was invalid");
|
|
415
|
+
const preset = isThemePreset(raw.preset) ? raw.preset : DEFAULT_APPEARANCE.preset;
|
|
416
|
+
if (raw.preset !== void 0 && raw.preset !== preset) reasons.push("appearance.preset was invalid");
|
|
417
|
+
const terminal = raw.terminal === "dark" || raw.terminal === "light" || raw.terminal === "off" || raw.terminal === "ansi" ? raw.terminal : DEFAULT_APPEARANCE.terminal;
|
|
418
|
+
if (raw.terminal !== void 0 && raw.terminal !== terminal) reasons.push("appearance.terminal was invalid");
|
|
419
|
+
if (raw.version !== void 0 && raw.version !== 1) reasons.push("appearance.version was invalid");
|
|
420
|
+
let custom;
|
|
421
|
+
if (preset === "custom" || raw.custom !== void 0) {
|
|
422
|
+
const rawCustom = isRecord(raw.custom) ? raw.custom : {};
|
|
423
|
+
if (raw.custom !== void 0 && rawCustom !== raw.custom) reasons.push("appearance.custom was not an object");
|
|
424
|
+
const base = isBuiltInThemePreset(rawCustom.base) ? rawCustom.base : "tokmon";
|
|
425
|
+
if (rawCustom.base !== void 0 && rawCustom.base !== base) reasons.push("custom.base was invalid");
|
|
426
|
+
custom = {
|
|
427
|
+
base,
|
|
428
|
+
light: cleanOverrides(rawCustom.light, "light", PRESETS[base].light, reasons),
|
|
429
|
+
dark: cleanOverrides(rawCustom.dark, "dark", PRESETS[base].dark, reasons)
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
const appearance = { version: 1, mode, preset, terminal, ...custom ? { custom } : {} };
|
|
433
|
+
return { appearance, repaired: reasons.length > 0 || JSON.stringify(raw) !== JSON.stringify(appearance), reasons };
|
|
434
|
+
}
|
|
435
|
+
function normalizeAppearance(input) {
|
|
436
|
+
return repairAppearance(input).appearance;
|
|
437
|
+
}
|
|
438
|
+
function addDerived(tokens) {
|
|
439
|
+
return {
|
|
440
|
+
...tokens,
|
|
441
|
+
card: mixColors(tokens.panel, tokens.text, 0.06),
|
|
442
|
+
cardHover: mixColors(tokens.panel, tokens.text, 0.1),
|
|
443
|
+
divider: tokens.line,
|
|
444
|
+
track: mixColors(tokens.panel, tokens.text, 0.1),
|
|
445
|
+
accentTint: mixColors(tokens.panel, tokens.accent, 0.14),
|
|
446
|
+
accentOn: contrastRatio(tokens.accent, "#000000") >= contrastRatio(tokens.accent, "#ffffff") ? "#000000" : "#ffffff",
|
|
447
|
+
focusRing: tokens.accent
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
function resolveTheme(input, systemMode = "dark") {
|
|
451
|
+
const appearance = normalizeAppearance(input);
|
|
452
|
+
const requestedMode = appearance.mode === "auto" ? systemMode : appearance.mode;
|
|
453
|
+
const mode = isDarkOnlyThemePreset(appearance.preset) ? "dark" : requestedMode;
|
|
454
|
+
const preset = appearance.preset === "custom" ? appearance.custom?.base ?? "tokmon" : appearance.preset;
|
|
455
|
+
const base = PRESETS[preset][mode];
|
|
456
|
+
const overrides = appearance.preset === "custom" ? appearance.custom?.[mode] ?? {} : {};
|
|
457
|
+
return { mode, tokens: addDerived({ ...base, ...overrides }) };
|
|
458
|
+
}
|
|
459
|
+
function resolveTerminalTheme(input, terminalIsDark = true) {
|
|
460
|
+
const appearance = normalizeAppearance(input);
|
|
461
|
+
if (appearance.terminal === "off" || appearance.terminal === "ansi") return null;
|
|
462
|
+
return resolveTheme(appearance, appearance.terminal === "dark" ? "dark" : appearance.terminal === "light" ? "light" : terminalIsDark ? "dark" : "light");
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// src/config-schema.ts
|
|
466
|
+
var DESKTOP_GRAPH_RANGES = [7, 14, 30];
|
|
467
|
+
function normalizePins(raw, legacy) {
|
|
468
|
+
const source = Array.isArray(raw) ? raw : legacy && typeof legacy === "string" && legacy.trim() ? [legacy] : [];
|
|
469
|
+
return normalizeStringIdList(source, 2);
|
|
470
|
+
}
|
|
471
|
+
function normalizeStringIdList(raw, max) {
|
|
472
|
+
if (!Array.isArray(raw)) return [];
|
|
473
|
+
const seen = /* @__PURE__ */ new Set();
|
|
474
|
+
const out = [];
|
|
475
|
+
for (const value of raw) {
|
|
476
|
+
if (typeof value !== "string") continue;
|
|
477
|
+
const id = value.trim();
|
|
478
|
+
if (!id || seen.has(id)) continue;
|
|
479
|
+
seen.add(id);
|
|
480
|
+
out.push(id);
|
|
481
|
+
if (out.length >= max) break;
|
|
482
|
+
}
|
|
483
|
+
return out;
|
|
484
|
+
}
|
|
485
|
+
var MAX_PINNED_PROVIDERS = 2;
|
|
486
|
+
function cleanProviderSelection(values, knownProviderIds, max = Number.POSITIVE_INFINITY) {
|
|
487
|
+
return normalizeStringIdList(values.filter((value) => knownProviderIds.has(value)), max);
|
|
488
|
+
}
|
|
489
|
+
function toggleProviderSelection(values, providerId, knownProviderIds, max = Number.POSITIVE_INFINITY) {
|
|
490
|
+
const current = cleanProviderSelection(values, knownProviderIds, max);
|
|
491
|
+
if (current.includes(providerId)) return current.filter((id) => id !== providerId);
|
|
492
|
+
if (!knownProviderIds.has(providerId) || current.length >= max) return current;
|
|
493
|
+
return [...current, providerId];
|
|
494
|
+
}
|
|
495
|
+
function moveProviderSelection(values, providerId, direction) {
|
|
496
|
+
const next = [...values];
|
|
497
|
+
const index = next.indexOf(providerId);
|
|
498
|
+
const target = index + direction;
|
|
499
|
+
if (index < 0 || target < 0 || target >= next.length) return next;
|
|
500
|
+
[next[index], next[target]] = [next[target], next[index]];
|
|
501
|
+
return next;
|
|
502
|
+
}
|
|
503
|
+
var DEFAULT_TRAY_CONFIG = {
|
|
504
|
+
enabled: true,
|
|
505
|
+
showMenuBarText: true,
|
|
506
|
+
displayMetric: "smartHeadroom",
|
|
507
|
+
pollIntervalSec: 30,
|
|
508
|
+
activeTimeoutMin: 10,
|
|
509
|
+
graceMin: 30,
|
|
510
|
+
promotionHoldMin: 5,
|
|
511
|
+
lowWatermarkPct: 20,
|
|
512
|
+
criticalWatermarkPct: 5,
|
|
513
|
+
pinnedAccount: null,
|
|
514
|
+
pins: [],
|
|
515
|
+
pinnedProviders: [],
|
|
516
|
+
launchAtLogin: false,
|
|
517
|
+
theme: "dark"
|
|
518
|
+
};
|
|
519
|
+
var MAX_EXPANDED_PROVIDERS = 32;
|
|
520
|
+
var DEFAULT_DESKTOP_CONFIG = {
|
|
521
|
+
expandedProviders: [],
|
|
522
|
+
graphRangeDays: 14
|
|
523
|
+
};
|
|
524
|
+
var DEFAULT_ACCOUNT_DETECTION_CONFIG = {
|
|
525
|
+
enabled: true,
|
|
526
|
+
disabledProviders: [],
|
|
527
|
+
excludedAccounts: []
|
|
528
|
+
};
|
|
529
|
+
var DEFAULTS = {
|
|
530
|
+
revision: 0,
|
|
531
|
+
interval: 2,
|
|
532
|
+
billingInterval: 5,
|
|
533
|
+
clearScreen: true,
|
|
534
|
+
privacyMode: true,
|
|
535
|
+
privacyToggleKey: "p",
|
|
536
|
+
timezone: null,
|
|
537
|
+
accounts: [],
|
|
538
|
+
activeAccountId: null,
|
|
539
|
+
disabledProviders: [],
|
|
540
|
+
onboarded: false,
|
|
541
|
+
dashboardLayout: "grid",
|
|
542
|
+
defaultFocus: "all",
|
|
543
|
+
ascii: "auto",
|
|
544
|
+
allowNetworkAccess: false,
|
|
545
|
+
allowedHosts: [],
|
|
546
|
+
resetDisplay: "relative",
|
|
547
|
+
knownProviders: [],
|
|
548
|
+
appearance: { ...DEFAULT_APPEARANCE },
|
|
549
|
+
accountDetection: { ...DEFAULT_ACCOUNT_DETECTION_CONFIG },
|
|
550
|
+
tray: { ...DEFAULT_TRAY_CONFIG },
|
|
551
|
+
desktop: { ...DEFAULT_DESKTOP_CONFIG }
|
|
552
|
+
};
|
|
553
|
+
var LEGACY_KNOWN = ["claude", "codex", "cursor"];
|
|
554
|
+
var ACCENT_COLORS = ["cyan", "magenta", "green", "yellow", "blue", "red"];
|
|
555
|
+
var PROVIDER_ORDER = [...PROVIDER_IDS];
|
|
556
|
+
var COLOR_PALETTE = [
|
|
557
|
+
"cyan",
|
|
558
|
+
"magenta",
|
|
559
|
+
"green",
|
|
560
|
+
"yellow",
|
|
561
|
+
"blue",
|
|
562
|
+
"red",
|
|
563
|
+
"cyanBright",
|
|
564
|
+
"magentaBright",
|
|
565
|
+
"greenBright"
|
|
566
|
+
];
|
|
567
|
+
var PROVIDER_META = {
|
|
568
|
+
claude: { name: "Claude", color: "green" },
|
|
569
|
+
codex: { name: "Codex", color: "cyan" },
|
|
570
|
+
cursor: { name: "Cursor", color: "magenta" },
|
|
571
|
+
copilot: { name: "Copilot", color: "white" },
|
|
572
|
+
pi: { name: "Pi", color: "blue" },
|
|
573
|
+
opencode: { name: "opencode", color: "yellow" },
|
|
574
|
+
antigravity: { name: "Antigravity", color: "red" },
|
|
575
|
+
gemini: { name: "Gemini", color: "greenBright" },
|
|
576
|
+
grok: { name: "Grok", color: "yellowBright" }
|
|
577
|
+
};
|
|
578
|
+
var EMAIL_RE = /[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/i;
|
|
579
|
+
var EMAIL_RE_GLOBAL = /[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi;
|
|
580
|
+
function containsEmail(value) {
|
|
581
|
+
return typeof value === "string" && EMAIL_RE.test(value);
|
|
582
|
+
}
|
|
583
|
+
function redactEmail(value) {
|
|
584
|
+
return value.replace(EMAIL_RE_GLOBAL, "[redacted]");
|
|
585
|
+
}
|
|
586
|
+
function getTrackedAccountRows(config, trackedProviders = PROVIDER_ORDER.filter((pid) => !config.disabledProviders.includes(pid)), autoAccounts) {
|
|
587
|
+
const tracked = new Set(trackedProviders);
|
|
588
|
+
const configuredIds = /* @__PURE__ */ new Set();
|
|
589
|
+
const configuredKeys = /* @__PURE__ */ new Set();
|
|
590
|
+
const rowIds = /* @__PURE__ */ new Set();
|
|
591
|
+
const rowKeys = /* @__PURE__ */ new Set();
|
|
592
|
+
const rows = [];
|
|
593
|
+
const detectionEnabled = config.accountDetection.enabled;
|
|
594
|
+
const detectorDisabled = new Set(config.accountDetection.disabledProviders);
|
|
595
|
+
const keyFor = (providerId, homeDir) => `${providerId}:${homeDir && homeDir !== "~" ? homeDir : "~"}`;
|
|
596
|
+
const excludedKeys = new Set(
|
|
597
|
+
config.accountDetection.excludedAccounts.map((ref) => keyFor(ref.providerId, ref.homeDir))
|
|
598
|
+
);
|
|
599
|
+
const rememberRow = (row) => {
|
|
600
|
+
rowIds.add(row.id);
|
|
601
|
+
rowKeys.add(keyFor(row.providerId, row.homeDir));
|
|
602
|
+
rows.push(row);
|
|
603
|
+
};
|
|
604
|
+
config.accounts.forEach((account, explicitIndex) => {
|
|
605
|
+
const meta = PROVIDER_META[account.providerId];
|
|
606
|
+
configuredIds.add(account.id);
|
|
607
|
+
configuredKeys.add(keyFor(account.providerId, account.homeDir));
|
|
608
|
+
rememberRow({
|
|
609
|
+
id: account.id,
|
|
610
|
+
providerId: account.providerId,
|
|
611
|
+
name: account.name,
|
|
612
|
+
homeDir: account.homeDir || "~",
|
|
613
|
+
color: account.color || meta.color,
|
|
614
|
+
source: "configured",
|
|
615
|
+
explicitId: account.id,
|
|
616
|
+
explicitIndex
|
|
617
|
+
});
|
|
618
|
+
});
|
|
619
|
+
if (autoAccounts) {
|
|
620
|
+
for (const account of autoAccounts) {
|
|
621
|
+
if (!detectionEnabled || detectorDisabled.has(account.providerId)) continue;
|
|
622
|
+
if (config.disabledProviders.includes(account.providerId)) continue;
|
|
623
|
+
const key = keyFor(account.providerId, account.homeDir);
|
|
624
|
+
if (excludedKeys.has(key)) continue;
|
|
625
|
+
if (configuredIds.has(account.id) || configuredKeys.has(key) || rowIds.has(account.id) || rowKeys.has(key)) continue;
|
|
626
|
+
const meta = PROVIDER_META[account.providerId];
|
|
627
|
+
rememberRow({
|
|
628
|
+
id: account.id,
|
|
629
|
+
providerId: account.providerId,
|
|
630
|
+
name: account.name,
|
|
631
|
+
homeDir: account.homeDir || "~",
|
|
632
|
+
color: account.color || meta.color,
|
|
633
|
+
source: "auto"
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
for (const providerId of PROVIDER_ORDER) {
|
|
638
|
+
if (!detectionEnabled || detectorDisabled.has(providerId)) continue;
|
|
639
|
+
if (config.disabledProviders.includes(providerId)) continue;
|
|
640
|
+
if (!tracked.has(providerId)) continue;
|
|
641
|
+
const key = keyFor(providerId, "~");
|
|
642
|
+
if (excludedKeys.has(key)) continue;
|
|
643
|
+
if (configuredIds.has(providerId) || configuredKeys.has(key) || rowIds.has(providerId) || rowKeys.has(key)) continue;
|
|
644
|
+
const meta = PROVIDER_META[providerId];
|
|
645
|
+
rememberRow({
|
|
646
|
+
id: providerId,
|
|
647
|
+
providerId,
|
|
648
|
+
name: meta.name,
|
|
649
|
+
homeDir: "~",
|
|
650
|
+
color: meta.color,
|
|
651
|
+
source: "auto"
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
for (const ref of config.accountDetection.excludedAccounts) {
|
|
655
|
+
if (configuredKeys.has(keyFor(ref.providerId, ref.homeDir))) continue;
|
|
656
|
+
const meta = PROVIDER_META[ref.providerId];
|
|
657
|
+
rememberRow({
|
|
658
|
+
id: `ignored:${ref.providerId}:${ref.homeDir}`,
|
|
659
|
+
providerId: ref.providerId,
|
|
660
|
+
name: `${meta.name} account`,
|
|
661
|
+
homeDir: ref.homeDir,
|
|
662
|
+
color: meta.color,
|
|
663
|
+
source: "ignored",
|
|
664
|
+
excludedRef: ref
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
return rows;
|
|
668
|
+
}
|
|
669
|
+
function providerDetectionEnabled(config, providerId) {
|
|
670
|
+
return config.enabled && !config.disabledProviders.includes(providerId);
|
|
671
|
+
}
|
|
672
|
+
function setProviderDetectionEnabled(config, providerId, enabled) {
|
|
673
|
+
const without = config.disabledProviders.filter((id) => id !== providerId);
|
|
674
|
+
return {
|
|
675
|
+
...config,
|
|
676
|
+
disabledProviders: enabled ? without : [...without, providerId]
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
function setDetectedAccountExcluded(config, ref, excluded) {
|
|
680
|
+
const homeDir = ref.homeDir.trim() || "~";
|
|
681
|
+
const matches = (candidate) => candidate.providerId === ref.providerId && candidate.homeDir === homeDir;
|
|
682
|
+
const without = config.excludedAccounts.filter((candidate) => !matches(candidate));
|
|
683
|
+
return {
|
|
684
|
+
...config,
|
|
685
|
+
excludedAccounts: excluded ? [...without, { providerId: ref.providerId, homeDir }] : without
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
function clampNum(v, fallback, min) {
|
|
689
|
+
return typeof v === "number" && Number.isFinite(v) && v >= min ? v : fallback;
|
|
690
|
+
}
|
|
691
|
+
function finiteInRange(v, fallback, min, max) {
|
|
692
|
+
return typeof v === "number" && Number.isFinite(v) && v >= min && v <= max ? v : fallback;
|
|
693
|
+
}
|
|
694
|
+
function isValidTimezone(tz) {
|
|
695
|
+
try {
|
|
696
|
+
new Intl.DateTimeFormat("en-CA", { timeZone: tz });
|
|
697
|
+
return true;
|
|
698
|
+
} catch {
|
|
699
|
+
return false;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
function isRecord2(value) {
|
|
703
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
704
|
+
}
|
|
705
|
+
function validProvider(value) {
|
|
706
|
+
return PROVIDER_IDS.includes(value);
|
|
707
|
+
}
|
|
708
|
+
function sameJson(a, b) {
|
|
709
|
+
try {
|
|
710
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
711
|
+
} catch {
|
|
712
|
+
return false;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
function normalizeAllowedHost(value) {
|
|
716
|
+
if (typeof value !== "string") return null;
|
|
717
|
+
const candidate = value.trim();
|
|
718
|
+
if (!candidate) return null;
|
|
719
|
+
try {
|
|
720
|
+
const url = new URL(`http://${candidate}`);
|
|
721
|
+
if (url.username || url.password || url.port || url.pathname !== "/" || url.search || url.hash) return null;
|
|
722
|
+
const hostname = url.hostname.toLowerCase().replace(/\.$/, "");
|
|
723
|
+
if (!hostname || hostname.length > 253 || hostname.includes("*")) return null;
|
|
724
|
+
const labels = hostname.split(".");
|
|
725
|
+
if (labels.some((label) => !label || label.length > 63 || !/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(label))) return null;
|
|
726
|
+
return hostname;
|
|
727
|
+
} catch {
|
|
728
|
+
return null;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
function normalizeAllowedHosts(value) {
|
|
732
|
+
if (!Array.isArray(value)) return [];
|
|
733
|
+
const hosts = value.map(normalizeAllowedHost).filter((host) => host !== null);
|
|
734
|
+
return [...new Set(hosts)];
|
|
735
|
+
}
|
|
736
|
+
function repairConfig(input) {
|
|
737
|
+
const reasons = [];
|
|
738
|
+
const parsed = isRecord2(input) ? input : {};
|
|
739
|
+
if (parsed !== input) reasons.push("config root was not an object");
|
|
740
|
+
const rawAccounts = Array.isArray(parsed.accounts) ? parsed.accounts : [];
|
|
741
|
+
if (!Array.isArray(parsed.accounts) && parsed.accounts !== void 0) reasons.push("accounts was not an array");
|
|
742
|
+
const accounts = [];
|
|
743
|
+
const accountIds = /* @__PURE__ */ new Set();
|
|
744
|
+
rawAccounts.forEach((raw, index) => {
|
|
745
|
+
if (!isRecord2(raw)) {
|
|
746
|
+
reasons.push(`accounts[${index}] was not an object`);
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
const providerId = raw.providerId ?? "claude";
|
|
750
|
+
if (!validProvider(providerId)) {
|
|
751
|
+
reasons.push(`accounts[${index}].providerId was invalid`);
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
const id = typeof raw.id === "string" && raw.id.trim() ? raw.id.trim() : "";
|
|
755
|
+
const name = typeof raw.name === "string" && raw.name.trim() ? raw.name.trim() : "";
|
|
756
|
+
if (!id || !name) {
|
|
757
|
+
reasons.push(`accounts[${index}] was missing id or name`);
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
if (accountIds.has(id)) {
|
|
761
|
+
reasons.push(`accounts[${index}].id was duplicated`);
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
accountIds.add(id);
|
|
765
|
+
accounts.push({
|
|
766
|
+
id,
|
|
767
|
+
providerId,
|
|
768
|
+
name,
|
|
769
|
+
homeDir: typeof raw.homeDir === "string" && raw.homeDir.trim() ? raw.homeDir : "~",
|
|
770
|
+
...typeof raw.color === "string" && raw.color.trim() ? { color: raw.color } : {}
|
|
771
|
+
});
|
|
772
|
+
});
|
|
773
|
+
const disabledProviders = (Array.isArray(parsed.disabledProviders) ? parsed.disabledProviders : []).filter(validProvider);
|
|
774
|
+
if (parsed.disabledProviders !== void 0 && !Array.isArray(parsed.disabledProviders)) {
|
|
775
|
+
reasons.push("disabledProviders was not an array");
|
|
776
|
+
}
|
|
777
|
+
const knownProviders = Array.isArray(parsed.knownProviders) ? parsed.knownProviders.filter(validProvider) : parsed.onboarded === true ? [...LEGACY_KNOWN] : [];
|
|
778
|
+
if (parsed.knownProviders !== void 0 && !Array.isArray(parsed.knownProviders)) {
|
|
779
|
+
reasons.push("knownProviders was not an array");
|
|
780
|
+
}
|
|
781
|
+
const allowedHosts = normalizeAllowedHosts(parsed.allowedHosts);
|
|
782
|
+
if (parsed.allowedHosts !== void 0 && !sameJson(parsed.allowedHosts, allowedHosts)) {
|
|
783
|
+
reasons.push("allowedHosts contained invalid or duplicate hostnames");
|
|
784
|
+
}
|
|
785
|
+
const appearanceRepair = repairAppearance(parsed.appearance);
|
|
786
|
+
if (parsed.appearance !== void 0 && appearanceRepair.repaired) {
|
|
787
|
+
reasons.push(...appearanceRepair.reasons);
|
|
788
|
+
}
|
|
789
|
+
const rawAccountDetection = isRecord2(parsed.accountDetection) ? parsed.accountDetection : {};
|
|
790
|
+
if (parsed.accountDetection !== void 0 && !isRecord2(parsed.accountDetection)) {
|
|
791
|
+
reasons.push("accountDetection was not an object");
|
|
792
|
+
}
|
|
793
|
+
const detectionDisabledProviders = Array.isArray(rawAccountDetection.disabledProviders) ? [...new Set(rawAccountDetection.disabledProviders.filter(validProvider))] : [];
|
|
794
|
+
const excludedAccounts = [];
|
|
795
|
+
const excludedKeys = /* @__PURE__ */ new Set();
|
|
796
|
+
if (Array.isArray(rawAccountDetection.excludedAccounts)) {
|
|
797
|
+
rawAccountDetection.excludedAccounts.forEach((raw, index) => {
|
|
798
|
+
if (!isRecord2(raw) || !validProvider(raw.providerId) || typeof raw.homeDir !== "string" || !raw.homeDir.trim()) {
|
|
799
|
+
reasons.push(`accountDetection.excludedAccounts[${index}] was invalid`);
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
const homeDir = raw.homeDir.trim();
|
|
803
|
+
const key = `${raw.providerId}:${homeDir}`;
|
|
804
|
+
if (excludedKeys.has(key)) return;
|
|
805
|
+
excludedKeys.add(key);
|
|
806
|
+
excludedAccounts.push({ providerId: raw.providerId, homeDir });
|
|
807
|
+
});
|
|
808
|
+
} else if (rawAccountDetection.excludedAccounts !== void 0) {
|
|
809
|
+
reasons.push("accountDetection.excludedAccounts was not an array");
|
|
810
|
+
}
|
|
811
|
+
const accountDetection = {
|
|
812
|
+
enabled: typeof rawAccountDetection.enabled === "boolean" ? rawAccountDetection.enabled : DEFAULT_ACCOUNT_DETECTION_CONFIG.enabled,
|
|
813
|
+
disabledProviders: detectionDisabledProviders,
|
|
814
|
+
excludedAccounts
|
|
815
|
+
};
|
|
816
|
+
if (parsed.accountDetection !== void 0 && !sameJson(parsed.accountDetection, accountDetection)) {
|
|
817
|
+
reasons.push("accountDetection contained invalid settings");
|
|
818
|
+
}
|
|
819
|
+
const rawTray = isRecord2(parsed.tray) ? parsed.tray : {};
|
|
820
|
+
if (parsed.tray !== void 0 && !isRecord2(parsed.tray)) reasons.push("tray was not an object");
|
|
821
|
+
const tray = {
|
|
822
|
+
enabled: typeof rawTray.enabled === "boolean" ? rawTray.enabled : DEFAULT_TRAY_CONFIG.enabled,
|
|
823
|
+
showMenuBarText: typeof rawTray.showMenuBarText === "boolean" ? rawTray.showMenuBarText : DEFAULT_TRAY_CONFIG.showMenuBarText,
|
|
824
|
+
displayMetric: rawTray.displayMetric === "tightestRemaining" || rawTray.displayMetric === "smartHeadroom" ? rawTray.displayMetric : DEFAULT_TRAY_CONFIG.displayMetric,
|
|
825
|
+
pollIntervalSec: finiteInRange(rawTray.pollIntervalSec, DEFAULT_TRAY_CONFIG.pollIntervalSec, 1, 86400),
|
|
826
|
+
activeTimeoutMin: finiteInRange(rawTray.activeTimeoutMin, DEFAULT_TRAY_CONFIG.activeTimeoutMin, 1, 1440),
|
|
827
|
+
graceMin: finiteInRange(rawTray.graceMin, DEFAULT_TRAY_CONFIG.graceMin, 1, 10080),
|
|
828
|
+
promotionHoldMin: finiteInRange(rawTray.promotionHoldMin, DEFAULT_TRAY_CONFIG.promotionHoldMin, 0, 1440),
|
|
829
|
+
lowWatermarkPct: finiteInRange(rawTray.lowWatermarkPct, DEFAULT_TRAY_CONFIG.lowWatermarkPct, 0, 100),
|
|
830
|
+
criticalWatermarkPct: finiteInRange(rawTray.criticalWatermarkPct, DEFAULT_TRAY_CONFIG.criticalWatermarkPct, 0, 100),
|
|
831
|
+
pinnedAccount: typeof rawTray.pinnedAccount === "string" && rawTray.pinnedAccount.trim() ? rawTray.pinnedAccount.trim() : null,
|
|
832
|
+
pins: normalizePins(rawTray.pins, typeof rawTray.pinnedAccount === "string" ? rawTray.pinnedAccount : null),
|
|
833
|
+
// Provider-scoped pins are the source of truth. The account→provider migration
|
|
834
|
+
// needs the live snapshot, so it happens in the renderer; here we only enforce
|
|
835
|
+
// the persisted invariant (unique non-empty strings, ≤2, order preserved).
|
|
836
|
+
pinnedProviders: normalizeStringIdList(rawTray.pinnedProviders, MAX_PINNED_PROVIDERS),
|
|
837
|
+
launchAtLogin: typeof rawTray.launchAtLogin === "boolean" ? rawTray.launchAtLogin : DEFAULT_TRAY_CONFIG.launchAtLogin,
|
|
838
|
+
theme: rawTray.theme === "dark" ? rawTray.theme : DEFAULT_TRAY_CONFIG.theme
|
|
839
|
+
};
|
|
840
|
+
if (tray.graceMin < tray.activeTimeoutMin) {
|
|
841
|
+
tray.graceMin = Math.max(DEFAULT_TRAY_CONFIG.graceMin, tray.activeTimeoutMin);
|
|
842
|
+
}
|
|
843
|
+
if (tray.criticalWatermarkPct > tray.lowWatermarkPct) {
|
|
844
|
+
tray.criticalWatermarkPct = Math.min(DEFAULT_TRAY_CONFIG.criticalWatermarkPct, tray.lowWatermarkPct);
|
|
845
|
+
}
|
|
846
|
+
if (parsed.tray !== void 0 && !sameJson(parsed.tray, tray)) reasons.push("tray contained invalid settings");
|
|
847
|
+
const rawDesktop = isRecord2(parsed.desktop) ? parsed.desktop : {};
|
|
848
|
+
if (parsed.desktop !== void 0 && !isRecord2(parsed.desktop)) reasons.push("desktop was not an object");
|
|
849
|
+
const desktop = {
|
|
850
|
+
expandedProviders: normalizeStringIdList(rawDesktop.expandedProviders, MAX_EXPANDED_PROVIDERS),
|
|
851
|
+
graphRangeDays: DESKTOP_GRAPH_RANGES.includes(rawDesktop.graphRangeDays) ? rawDesktop.graphRangeDays : DEFAULT_DESKTOP_CONFIG.graphRangeDays
|
|
852
|
+
};
|
|
853
|
+
if (parsed.desktop !== void 0 && !sameJson(parsed.desktop, desktop)) reasons.push("desktop contained invalid settings");
|
|
854
|
+
const activeAccountId = typeof parsed.activeAccountId === "string" && (accountIds.has(parsed.activeAccountId) || PROVIDER_IDS.includes(parsed.activeAccountId)) ? parsed.activeAccountId : null;
|
|
855
|
+
if (parsed.activeAccountId !== void 0 && parsed.activeAccountId !== null && activeAccountId === null) {
|
|
856
|
+
reasons.push("activeAccountId did not match a known account/provider");
|
|
857
|
+
}
|
|
858
|
+
const timezone = typeof parsed.timezone === "string" && parsed.timezone.trim() && isValidTimezone(parsed.timezone.trim()) ? parsed.timezone.trim() : null;
|
|
859
|
+
if (parsed.timezone !== void 0 && parsed.timezone !== null && timezone === null) {
|
|
860
|
+
reasons.push("timezone was invalid");
|
|
861
|
+
}
|
|
862
|
+
const config = {
|
|
863
|
+
...DEFAULTS,
|
|
864
|
+
revision: typeof parsed.revision === "number" && Number.isSafeInteger(parsed.revision) && parsed.revision >= 0 ? parsed.revision : DEFAULTS.revision,
|
|
865
|
+
interval: clampNum(parsed.interval, DEFAULTS.interval, 1),
|
|
866
|
+
billingInterval: clampNum(parsed.billingInterval, DEFAULTS.billingInterval, 1),
|
|
867
|
+
clearScreen: typeof parsed.clearScreen === "boolean" ? parsed.clearScreen : DEFAULTS.clearScreen,
|
|
868
|
+
privacyMode: typeof parsed.privacyMode === "boolean" ? parsed.privacyMode : DEFAULTS.privacyMode,
|
|
869
|
+
privacyToggleKey: typeof parsed.privacyToggleKey === "string" && parsed.privacyToggleKey.length === 1 ? parsed.privacyToggleKey : DEFAULTS.privacyToggleKey,
|
|
870
|
+
timezone,
|
|
871
|
+
accounts,
|
|
872
|
+
activeAccountId,
|
|
873
|
+
disabledProviders,
|
|
874
|
+
onboarded: parsed.onboarded === true,
|
|
875
|
+
dashboardLayout: parsed.dashboardLayout === "single" ? "single" : "grid",
|
|
876
|
+
defaultFocus: parsed.defaultFocus === "last" ? "last" : "all",
|
|
877
|
+
ascii: parsed.ascii === "on" ? "on" : parsed.ascii === "off" ? "off" : "auto",
|
|
878
|
+
allowNetworkAccess: parsed.allowNetworkAccess === true,
|
|
879
|
+
allowedHosts,
|
|
880
|
+
resetDisplay: parsed.resetDisplay === "absolute" ? "absolute" : "relative",
|
|
881
|
+
knownProviders,
|
|
882
|
+
appearance: appearanceRepair.appearance,
|
|
883
|
+
accountDetection,
|
|
884
|
+
tray,
|
|
885
|
+
desktop
|
|
886
|
+
};
|
|
887
|
+
for (const key of Object.keys(DEFAULTS)) {
|
|
888
|
+
if (!(key in parsed)) reasons.push(`missing ${key}`);
|
|
889
|
+
}
|
|
890
|
+
return { config, repaired: reasons.length > 0 || !sameJson(parsed, config), reasons };
|
|
891
|
+
}
|
|
892
|
+
function normalizeConfig(parsed) {
|
|
893
|
+
try {
|
|
894
|
+
return repairConfig(parsed).config;
|
|
895
|
+
} catch {
|
|
896
|
+
return {
|
|
897
|
+
...DEFAULTS,
|
|
898
|
+
appearance: { ...DEFAULT_APPEARANCE },
|
|
899
|
+
accountDetection: { ...DEFAULT_ACCOUNT_DETECTION_CONFIG },
|
|
900
|
+
tray: { ...DEFAULT_TRAY_CONFIG },
|
|
901
|
+
desktop: { ...DEFAULT_DESKTOP_CONFIG }
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
function slugify(value) {
|
|
906
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 48);
|
|
907
|
+
}
|
|
908
|
+
function generateAccountId(name, existing) {
|
|
909
|
+
const base = slugify(name) || "account";
|
|
910
|
+
const taken = new Set(existing.map((a) => a.id));
|
|
911
|
+
if (!taken.has(base)) return base;
|
|
912
|
+
for (let i = 2; i < 1e3; i++) {
|
|
913
|
+
const candidate = `${base}_${i}`;
|
|
914
|
+
if (!taken.has(candidate)) return candidate;
|
|
915
|
+
}
|
|
916
|
+
return `${base}_${Date.now()}`;
|
|
917
|
+
}
|
|
918
|
+
function pickAccentColor(existing) {
|
|
919
|
+
const used = new Set(existing.map((a) => a.color).filter(Boolean));
|
|
920
|
+
for (const c of ACCENT_COLORS) {
|
|
921
|
+
if (!used.has(c)) return c;
|
|
922
|
+
}
|
|
923
|
+
return ACCENT_COLORS[existing.length % ACCENT_COLORS.length];
|
|
924
|
+
}
|
|
925
|
+
function sanitizeTyped(input) {
|
|
926
|
+
if (!input) return "";
|
|
927
|
+
return input.replace(/\x1b\][\s\S]*?(?:\x07|\x1b\\)/g, "").replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "").replace(/\x1bO./g, "").replace(/\x1b/g, "").replace(/[\x00-\x1f\x7f-\x9f]/g, "").replace(/\[20[01]~/g, "");
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// src/config.ts
|
|
931
|
+
function envDir(name) {
|
|
932
|
+
const v = process.env[name];
|
|
933
|
+
return v && v.trim() && isAbsolute(v.trim()) ? v.trim() : void 0;
|
|
934
|
+
}
|
|
935
|
+
function configDir() {
|
|
936
|
+
if (process.platform === "win32") {
|
|
937
|
+
return join(envDir("APPDATA") ?? join(homedir(), "AppData", "Roaming"), "tokmon");
|
|
938
|
+
}
|
|
939
|
+
return join(envDir("XDG_CONFIG_HOME") ?? join(homedir(), ".config"), "tokmon");
|
|
940
|
+
}
|
|
941
|
+
function configLocation() {
|
|
942
|
+
return join(configDir(), "config.json");
|
|
943
|
+
}
|
|
944
|
+
function cacheDir() {
|
|
945
|
+
if (process.platform === "win32") {
|
|
946
|
+
return join(envDir("LOCALAPPDATA") ?? envDir("APPDATA") ?? join(homedir(), "AppData", "Local"), "tokmon", "cache");
|
|
947
|
+
}
|
|
948
|
+
if (process.platform === "darwin") {
|
|
949
|
+
return join(homedir(), "Library", "Caches", "tokmon");
|
|
950
|
+
}
|
|
951
|
+
return join(envDir("XDG_CACHE_HOME") ?? join(homedir(), ".cache"), "tokmon");
|
|
952
|
+
}
|
|
953
|
+
function snapshotCacheFile() {
|
|
954
|
+
return join(cacheDir(), "web-snapshot.json");
|
|
955
|
+
}
|
|
956
|
+
async function loadConfig() {
|
|
957
|
+
let raw;
|
|
958
|
+
try {
|
|
959
|
+
raw = await readFile(configLocation(), "utf-8");
|
|
960
|
+
} catch {
|
|
961
|
+
return { ...DEFAULTS };
|
|
962
|
+
}
|
|
963
|
+
try {
|
|
964
|
+
await chmod(configDir(), 448);
|
|
965
|
+
await chmod(configLocation(), 384);
|
|
966
|
+
} catch {
|
|
967
|
+
}
|
|
968
|
+
let parsed;
|
|
969
|
+
try {
|
|
970
|
+
parsed = JSON.parse(raw);
|
|
971
|
+
} catch {
|
|
972
|
+
try {
|
|
973
|
+
const dir = configDir();
|
|
974
|
+
await mkdir(dir, { recursive: true, mode: 448 });
|
|
975
|
+
await chmod(dir, 448);
|
|
976
|
+
const backup = configLocation() + ".bak";
|
|
977
|
+
await writeFile(backup, raw, { mode: 384 });
|
|
978
|
+
await chmod(backup, 384);
|
|
979
|
+
} catch {
|
|
980
|
+
}
|
|
981
|
+
return { ...DEFAULTS };
|
|
982
|
+
}
|
|
983
|
+
const repaired = repairConfig(parsed);
|
|
984
|
+
if (repaired.repaired) {
|
|
985
|
+
try {
|
|
986
|
+
await saveConfig(repaired.config);
|
|
987
|
+
} catch {
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
return repaired.config;
|
|
991
|
+
}
|
|
992
|
+
var saveQueue = Promise.resolve();
|
|
993
|
+
var tempSequence = 0;
|
|
994
|
+
function configTempFile(dir) {
|
|
995
|
+
tempSequence = (tempSequence + 1) % Number.MAX_SAFE_INTEGER;
|
|
996
|
+
return join(dir, `config.json.${process.pid}.${Date.now()}.${tempSequence}.tmp`);
|
|
997
|
+
}
|
|
998
|
+
function configJson(config) {
|
|
999
|
+
return JSON.stringify(normalizeConfig(config), null, 2) + "\n";
|
|
1000
|
+
}
|
|
1001
|
+
function saveConfig(config) {
|
|
1002
|
+
const write = async () => {
|
|
1003
|
+
const dir = configDir();
|
|
1004
|
+
await mkdir(dir, { recursive: true, mode: 448 });
|
|
1005
|
+
await chmod(dir, 448);
|
|
1006
|
+
const tmp = configTempFile(dir);
|
|
1007
|
+
try {
|
|
1008
|
+
await writeFile(tmp, configJson(config), { mode: 384 });
|
|
1009
|
+
await chmod(tmp, 384);
|
|
1010
|
+
await rename(tmp, configLocation());
|
|
1011
|
+
} catch (error) {
|
|
1012
|
+
try {
|
|
1013
|
+
await unlink(tmp);
|
|
1014
|
+
} catch {
|
|
1015
|
+
}
|
|
1016
|
+
throw error;
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1019
|
+
const operation = saveQueue.catch(() => void 0).then(write);
|
|
1020
|
+
saveQueue = operation;
|
|
1021
|
+
return operation;
|
|
1022
|
+
}
|
|
1023
|
+
function saveConfigSync(config) {
|
|
1024
|
+
const dir = configDir();
|
|
1025
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
1026
|
+
chmodSync(dir, 448);
|
|
1027
|
+
const tmp = configTempFile(dir);
|
|
1028
|
+
try {
|
|
1029
|
+
writeFileSync(tmp, configJson(config), { mode: 384 });
|
|
1030
|
+
chmodSync(tmp, 384);
|
|
1031
|
+
renameSync(tmp, configLocation());
|
|
1032
|
+
} catch (error) {
|
|
1033
|
+
try {
|
|
1034
|
+
unlinkSync(tmp);
|
|
1035
|
+
} catch {
|
|
1036
|
+
}
|
|
1037
|
+
throw error;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
function expandHome(p) {
|
|
1041
|
+
if (!p) return homedir();
|
|
1042
|
+
if (p === "~" || p === "~/" || p === "~\\") return homedir();
|
|
1043
|
+
if (p.startsWith("~/") || p.startsWith("~\\")) return join(homedir(), p.slice(2));
|
|
1044
|
+
return p;
|
|
1045
|
+
}
|
|
1046
|
+
function findAccount(config, id) {
|
|
1047
|
+
if (!id) return null;
|
|
1048
|
+
return config.accounts.find((a) => a.id === id) ?? null;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
export {
|
|
1052
|
+
PROVIDER_IDS,
|
|
1053
|
+
BUILT_IN_THEME_PRESET_IDS,
|
|
1054
|
+
THEME_PRESET_IDS,
|
|
1055
|
+
themePresetOption,
|
|
1056
|
+
isDarkOnlyThemePreset,
|
|
1057
|
+
resolveTerminalTheme,
|
|
1058
|
+
DESKTOP_GRAPH_RANGES,
|
|
1059
|
+
MAX_PINNED_PROVIDERS,
|
|
1060
|
+
cleanProviderSelection,
|
|
1061
|
+
toggleProviderSelection,
|
|
1062
|
+
moveProviderSelection,
|
|
1063
|
+
DEFAULT_TRAY_CONFIG,
|
|
1064
|
+
DEFAULT_ACCOUNT_DETECTION_CONFIG,
|
|
1065
|
+
DEFAULTS,
|
|
1066
|
+
ACCENT_COLORS,
|
|
1067
|
+
COLOR_PALETTE,
|
|
1068
|
+
PROVIDER_META,
|
|
1069
|
+
containsEmail,
|
|
1070
|
+
redactEmail,
|
|
1071
|
+
getTrackedAccountRows,
|
|
1072
|
+
providerDetectionEnabled,
|
|
1073
|
+
setProviderDetectionEnabled,
|
|
1074
|
+
setDetectedAccountExcluded,
|
|
1075
|
+
clampNum,
|
|
1076
|
+
isValidTimezone,
|
|
1077
|
+
normalizeAllowedHost,
|
|
1078
|
+
normalizeAllowedHosts,
|
|
1079
|
+
repairConfig,
|
|
1080
|
+
normalizeConfig,
|
|
1081
|
+
slugify,
|
|
1082
|
+
generateAccountId,
|
|
1083
|
+
pickAccentColor,
|
|
1084
|
+
sanitizeTyped,
|
|
1085
|
+
envDir,
|
|
1086
|
+
configLocation,
|
|
1087
|
+
cacheDir,
|
|
1088
|
+
snapshotCacheFile,
|
|
1089
|
+
loadConfig,
|
|
1090
|
+
saveConfig,
|
|
1091
|
+
saveConfigSync,
|
|
1092
|
+
expandHome,
|
|
1093
|
+
findAccount
|
|
1094
|
+
};
|