clay-server 3.6.0-beta.1 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -196
- package/bin/cli.js +7 -42
- package/lib/builtin-mates.js +1 -1
- package/lib/cli-wordmark.js +32 -0
- package/lib/pages.js +47 -35
- package/lib/public/app.js +2 -10
- package/lib/public/apple-touch-icon.png +0 -0
- package/lib/public/clay-studio-favicon-32.png +0 -0
- package/lib/public/clay-studio-symbol.png +0 -0
- package/lib/public/clay-studio-wordmark.svg +20 -0
- package/lib/public/css/base.css +143 -61
- package/lib/public/css/filebrowser.css +54 -8
- package/lib/public/css/icon-strip.css +33 -19
- package/lib/public/css/input.css +15 -5
- package/lib/public/css/messages.css +17 -14
- package/lib/public/css/mobile-nav.css +5 -5
- package/lib/public/css/overlays.css +58 -6
- package/lib/public/css/pane.css +0 -1
- package/lib/public/css/session-actions.css +9 -3
- package/lib/public/css/sidebar.css +104 -161
- package/lib/public/css/title-bar.css +51 -18
- package/lib/public/css/user-settings.css +114 -8
- package/lib/public/icon-192.png +0 -0
- package/lib/public/icon-512.png +0 -0
- package/lib/public/index.html +32 -76
- package/lib/public/manifest.json +1 -6
- package/lib/public/modules/app-connection.js +0 -3
- package/lib/public/modules/app-favicon.js +22 -29
- package/lib/public/modules/app-messages.js +1 -2
- package/lib/public/modules/app-notifications.js +3 -3
- package/lib/public/modules/command-palette.js +1 -1
- package/lib/public/modules/server-settings.js +0 -77
- package/lib/public/modules/sidebar-projects.js +1 -1
- package/lib/public/modules/sidebar-sessions.js +18 -5
- package/lib/public/modules/sidebar.js +2 -2
- package/lib/public/modules/theme.js +88 -249
- package/lib/public/modules/user-settings.js +2 -3
- package/lib/public/style.css +10 -10
- package/lib/public/sw.js +4 -2
- package/lib/themes/clay-dark.json +11 -0
- package/lib/themes/clay-light.json +11 -0
- package/package.json +1 -1
- package/lib/public/apple-touch-icon-dark.png +0 -0
- package/lib/public/clay-logo.png +0 -0
- package/lib/public/favicon-banded-32.png +0 -0
- package/lib/public/favicon-banded.png +0 -0
- package/lib/public/favicon-dark.svg +0 -1
- package/lib/public/favicon.svg +0 -1
- package/lib/public/icon-192-dark.png +0 -0
- package/lib/public/icon-512-dark.png +0 -0
- package/lib/public/icon-banded-76.png +0 -0
- package/lib/public/icon-banded-96.png +0 -0
- package/lib/public/icon-mono.svg +0 -1
- package/lib/public/modules/ascii-logo.js +0 -442
- package/lib/public/wordmark-banded-20.png +0 -0
- package/lib/public/wordmark-banded-32.png +0 -0
- package/lib/public/wordmark-banded-64.png +0 -0
- package/lib/public/wordmark-banded-80.png +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { refreshIcons } from './icons.js';
|
|
2
2
|
import { setTerminalTheme } from './terminal.js';
|
|
3
3
|
import { setTuiSessionTheme } from './session-tui-view.js';
|
|
4
4
|
import { setTuiAttentionTheme } from './tui-attention.js';
|
|
@@ -58,81 +58,26 @@ function luminance(hex) {
|
|
|
58
58
|
return (0.299 * c.r + 0.587 * c.g + 0.114 * c.b) / 255;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
var
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
|
|
72
|
-
"--code-bg": "#22242e",
|
|
73
|
-
"--border": "#44475a",
|
|
74
|
-
"--border-subtle": "#333644",
|
|
75
|
-
"--input-bg": "#3d3e51",
|
|
76
|
-
"--user-bubble": "#404154",
|
|
77
|
-
"--error": "#ff5555",
|
|
78
|
-
"--success": "#50fa7b",
|
|
79
|
-
"--warning": "#f1fa8c",
|
|
80
|
-
"--sidebar-bg": "#242631",
|
|
81
|
-
"--sidebar-hover": "#2f2f3f",
|
|
82
|
-
"--sidebar-active": "#3d3e51",
|
|
83
|
-
"--filebrowser-bg": "#2e3042",
|
|
84
|
-
"--filebrowser-border": "#383a4d",
|
|
85
|
-
"--accent-8": "rgba(255, 184, 108, 0.08)",
|
|
86
|
-
"--accent-12": "rgba(255, 184, 108, 0.12)",
|
|
87
|
-
"--accent-15": "rgba(255, 184, 108, 0.15)",
|
|
88
|
-
"--accent-20": "rgba(255, 184, 108, 0.20)",
|
|
89
|
-
"--accent-25": "rgba(255, 184, 108, 0.25)",
|
|
90
|
-
"--accent-30": "rgba(255, 184, 108, 0.30)",
|
|
91
|
-
"--accent2": "#80bfff",
|
|
92
|
-
"--accent2-hover": "#8fc7ff",
|
|
93
|
-
"--accent2-bg": "rgba(128, 191, 255, 0.12)",
|
|
94
|
-
"--accent2-8": "rgba(128, 191, 255, 0.08)",
|
|
95
|
-
"--accent2-12": "rgba(128, 191, 255, 0.12)",
|
|
96
|
-
"--accent2-15": "rgba(128, 191, 255, 0.15)",
|
|
97
|
-
"--accent2-20": "rgba(128, 191, 255, 0.20)",
|
|
98
|
-
"--accent2-25": "rgba(128, 191, 255, 0.25)",
|
|
99
|
-
"--accent2-30": "rgba(128, 191, 255, 0.30)",
|
|
100
|
-
"--error-8": "rgba(255, 85, 85, 0.08)",
|
|
101
|
-
"--error-12": "rgba(255, 85, 85, 0.12)",
|
|
102
|
-
"--error-15": "rgba(255, 85, 85, 0.15)",
|
|
103
|
-
"--error-25": "rgba(255, 85, 85, 0.25)",
|
|
104
|
-
"--success-8": "rgba(80, 250, 123, 0.08)",
|
|
105
|
-
"--success-12": "rgba(80, 250, 123, 0.12)",
|
|
106
|
-
"--success-15": "rgba(80, 250, 123, 0.15)",
|
|
107
|
-
"--success-25": "rgba(80, 250, 123, 0.25)",
|
|
108
|
-
"--warning-bg": "rgba(241, 250, 140, 0.12)",
|
|
109
|
-
"--overlay-rgb": "255,255,255",
|
|
110
|
-
"--shadow-rgb": "0,0,0",
|
|
111
|
-
"--hl-comment": "#6272a4",
|
|
112
|
-
"--hl-keyword": "#ff79c6",
|
|
113
|
-
"--hl-string": "#50fa7b",
|
|
114
|
-
"--hl-number": "#ffb86c",
|
|
115
|
-
"--hl-function": "#80bfff",
|
|
116
|
-
"--hl-variable": "#ff5555",
|
|
117
|
-
"--hl-type": "#f1fa8c",
|
|
118
|
-
"--hl-constant": "#ffb86c",
|
|
119
|
-
"--hl-tag": "#ff5555",
|
|
120
|
-
"--hl-attr": "#80bfff",
|
|
121
|
-
"--hl-regexp": "#8be9fd",
|
|
122
|
-
"--hl-meta": "#bd93f9",
|
|
123
|
-
"--hl-builtin": "#ffb86c",
|
|
124
|
-
"--hl-symbol": "#bd93f9",
|
|
125
|
-
"--hl-addition": "#50fa7b",
|
|
126
|
-
"--hl-deletion": "#ff5555"
|
|
61
|
+
var DEFAULT_DARK_THEME_ID = "clay-dark";
|
|
62
|
+
var DEFAULT_LIGHT_THEME_ID = "clay-light";
|
|
63
|
+
|
|
64
|
+
// Embedded defaults keep the first render branded before /api/themes loads.
|
|
65
|
+
var defaultDarkFallback = {
|
|
66
|
+
name: "Clay Studio Dark", variant: "dark",
|
|
67
|
+
base00: "171715", base01: "20201D", base02: "393934", base03: "686861",
|
|
68
|
+
base04: "A0A098", base05: "D1D1CA", base06: "E8E8E2", base07: "FAFAF5",
|
|
69
|
+
base08: "FF7479", base09: "7776FF", base0A: "E0B45B", base0B: "07E5A3",
|
|
70
|
+
base0C: "5FD2C0", base0D: "8AA7FF", base0E: "C39AF3", base0F: "D69A7D",
|
|
71
|
+
accent2: "07E5A3", link: "7F7EC8"
|
|
127
72
|
};
|
|
128
73
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
74
|
+
var defaultLightFallback = {
|
|
75
|
+
name: "Clay Studio Light", variant: "light",
|
|
76
|
+
base00: "F7F7F3", base01: "EEEEEA", base02: "D7D7D0", base03: "A2A29B",
|
|
77
|
+
base04: "73736C", base05: "44443F", base06: "252522", base07: "151513",
|
|
78
|
+
base08: "C84B50", base09: "5857FC", base0A: "956A19", base0B: "087B59",
|
|
79
|
+
base0C: "187A7C", base0D: "3F62B4", base0E: "7A52A8", base0F: "9A5A46",
|
|
80
|
+
accent2: "07E5A3", link: "4948B8"
|
|
136
81
|
};
|
|
137
82
|
|
|
138
83
|
// --- Compute CSS variables from a base16 palette ---
|
|
@@ -146,6 +91,7 @@ function computeVars(theme) {
|
|
|
146
91
|
|
|
147
92
|
var isLight = theme.variant === "light";
|
|
148
93
|
var accent2 = theme.accent2 ? "#" + theme.accent2 : b.base0D;
|
|
94
|
+
var link = theme.link ? "#" + theme.link : b.base0D;
|
|
149
95
|
|
|
150
96
|
return {
|
|
151
97
|
"--bg": b.base00,
|
|
@@ -157,6 +103,8 @@ function computeVars(theme) {
|
|
|
157
103
|
"--accent": b.base09,
|
|
158
104
|
"--accent-hover": isLight ? darken(b.base09, 0.12) : lighten(b.base09, 0.12),
|
|
159
105
|
"--accent-bg": hexToRgba(b.base09, 0.12),
|
|
106
|
+
"--link": link,
|
|
107
|
+
"--link-hover": isLight ? darken(link, 0.10) : lighten(link, 0.10),
|
|
160
108
|
"--code-bg": isLight ? darken(b.base00, 0.03) : darken(b.base00, 0.15),
|
|
161
109
|
"--border": b.base02,
|
|
162
110
|
"--border-subtle": mixColors(b.base00, b.base02, 0.6),
|
|
@@ -216,6 +164,8 @@ function computeVars(theme) {
|
|
|
216
164
|
};
|
|
217
165
|
}
|
|
218
166
|
|
|
167
|
+
var defaultExactVars = computeVars(defaultDarkFallback);
|
|
168
|
+
|
|
219
169
|
function computeTerminalTheme(theme) {
|
|
220
170
|
var b = {};
|
|
221
171
|
var keys = ["base00","base01","base02","base03","base04","base05","base06","base07",
|
|
@@ -250,7 +200,7 @@ function computeTerminalTheme(theme) {
|
|
|
250
200
|
}
|
|
251
201
|
|
|
252
202
|
function computeMermaidVars(theme) {
|
|
253
|
-
var vars = currentThemeId ===
|
|
203
|
+
var vars = currentThemeId === DEFAULT_DARK_THEME_ID ? defaultExactVars : computeVars(theme);
|
|
254
204
|
var isLight = theme.variant === "light";
|
|
255
205
|
return {
|
|
256
206
|
darkMode: !isLight,
|
|
@@ -269,7 +219,7 @@ function computeMermaidVars(theme) {
|
|
|
269
219
|
var themes = {};
|
|
270
220
|
var customSet = {}; // ids that came from ~/.clay/themes/
|
|
271
221
|
var themesLoaded = false;
|
|
272
|
-
var currentThemeId =
|
|
222
|
+
var currentThemeId = DEFAULT_DARK_THEME_ID;
|
|
273
223
|
var changeCallbacks = [];
|
|
274
224
|
var STORAGE_KEY = "clay-theme";
|
|
275
225
|
var MODE_KEY = "clay-mode"; // "light" | "dark" | null (system)
|
|
@@ -279,7 +229,10 @@ var WIDE_KEY = "clay-wide-view"; // cached from server, "bubble" | "channel"
|
|
|
279
229
|
// --- Helpers ---
|
|
280
230
|
|
|
281
231
|
function getTheme(id) {
|
|
282
|
-
|
|
232
|
+
if (themes[id]) return themes[id];
|
|
233
|
+
if (id === DEFAULT_DARK_THEME_ID) return defaultDarkFallback;
|
|
234
|
+
if (id === DEFAULT_LIGHT_THEME_ID) return defaultLightFallback;
|
|
235
|
+
return null;
|
|
283
236
|
}
|
|
284
237
|
|
|
285
238
|
function isCustom(id) {
|
|
@@ -289,7 +242,7 @@ function isCustom(id) {
|
|
|
289
242
|
// --- Public API ---
|
|
290
243
|
|
|
291
244
|
export function getCurrentTheme() {
|
|
292
|
-
return getTheme(currentThemeId) ||
|
|
245
|
+
return getTheme(currentThemeId) || defaultDarkFallback;
|
|
293
246
|
}
|
|
294
247
|
|
|
295
248
|
export function getThemeId() {
|
|
@@ -302,7 +255,7 @@ export function getThemeColor(baseKey) {
|
|
|
302
255
|
}
|
|
303
256
|
|
|
304
257
|
export function getComputedVar(varName) {
|
|
305
|
-
if (currentThemeId ===
|
|
258
|
+
if (currentThemeId === DEFAULT_DARK_THEME_ID && !themesLoaded) return defaultExactVars[varName] || "";
|
|
306
259
|
var theme = getCurrentTheme();
|
|
307
260
|
var vars = computeVars(theme);
|
|
308
261
|
return vars[varName] || "";
|
|
@@ -330,11 +283,11 @@ export function getThemes() {
|
|
|
330
283
|
|
|
331
284
|
export function applyTheme(themeId, fromPicker) {
|
|
332
285
|
var theme = getTheme(themeId);
|
|
333
|
-
if (!theme) themeId = "
|
|
286
|
+
if (!theme) themeId = getEffectiveMode() === "light" ? DEFAULT_LIGHT_THEME_ID : DEFAULT_DARK_THEME_ID;
|
|
334
287
|
theme = getTheme(themeId);
|
|
335
288
|
currentThemeId = themeId;
|
|
336
289
|
|
|
337
|
-
var vars = (themeId ===
|
|
290
|
+
var vars = (themeId === DEFAULT_DARK_THEME_ID && !themesLoaded) ? defaultExactVars : computeVars(theme);
|
|
338
291
|
var root = document.documentElement;
|
|
339
292
|
var varNames = Object.keys(vars);
|
|
340
293
|
for (var i = 0; i < varNames.length; i++) {
|
|
@@ -383,8 +336,8 @@ export function applyTheme(themeId, fromPicker) {
|
|
|
383
336
|
|
|
384
337
|
// --- Favicon update on theme change ---
|
|
385
338
|
function updateMascotSvgs(vars, isLight) {
|
|
386
|
-
var faviconEl = document.querySelector('link[rel="icon"]
|
|
387
|
-
if (faviconEl) faviconEl.setAttribute("href", "favicon-
|
|
339
|
+
var faviconEl = document.querySelector('link[rel="icon"]');
|
|
340
|
+
if (faviconEl) faviconEl.setAttribute("href", "clay-studio-favicon-32.png");
|
|
388
341
|
}
|
|
389
342
|
|
|
390
343
|
// --- Theme loading from server ---
|
|
@@ -412,8 +365,9 @@ function loadThemes() {
|
|
|
412
365
|
}
|
|
413
366
|
}
|
|
414
367
|
|
|
415
|
-
//
|
|
416
|
-
if (!themes
|
|
368
|
+
// Keep both Clay Studio defaults available even if a bundled file is missing.
|
|
369
|
+
if (!themes[DEFAULT_DARK_THEME_ID]) themes[DEFAULT_DARK_THEME_ID] = defaultDarkFallback;
|
|
370
|
+
if (!themes[DEFAULT_LIGHT_THEME_ID]) themes[DEFAULT_LIGHT_THEME_ID] = defaultLightFallback;
|
|
417
371
|
|
|
418
372
|
themesLoaded = true;
|
|
419
373
|
|
|
@@ -424,9 +378,12 @@ function loadThemes() {
|
|
|
424
378
|
// (before this, only defaultExactVars was used as fallback)
|
|
425
379
|
applyTheme(currentThemeId);
|
|
426
380
|
}).catch(function () {
|
|
427
|
-
// API unavailable — keep
|
|
428
|
-
themes
|
|
381
|
+
// API unavailable — keep the embedded Clay Studio pair.
|
|
382
|
+
themes[DEFAULT_DARK_THEME_ID] = defaultDarkFallback;
|
|
383
|
+
themes[DEFAULT_LIGHT_THEME_ID] = defaultLightFallback;
|
|
429
384
|
themesLoaded = true;
|
|
385
|
+
if (pickerEl) rebuildPicker();
|
|
386
|
+
applyTheme(currentThemeId);
|
|
430
387
|
});
|
|
431
388
|
}
|
|
432
389
|
|
|
@@ -469,14 +426,14 @@ function themeIdForMode(mode) {
|
|
|
469
426
|
var skin = null;
|
|
470
427
|
try { skin = localStorage.getItem(SKIN_KEY); } catch (e) {}
|
|
471
428
|
|
|
472
|
-
// Default skin pair
|
|
429
|
+
// Default Clay Studio skin pair.
|
|
473
430
|
if (!skin) {
|
|
474
|
-
return mode === "light" ?
|
|
431
|
+
return mode === "light" ? DEFAULT_LIGHT_THEME_ID : DEFAULT_DARK_THEME_ID;
|
|
475
432
|
}
|
|
476
433
|
|
|
477
434
|
// Custom skin — try to find the counterpart
|
|
478
435
|
var current = getTheme(skin);
|
|
479
|
-
if (!current) return mode === "light" ?
|
|
436
|
+
if (!current) return mode === "light" ? DEFAULT_LIGHT_THEME_ID : DEFAULT_DARK_THEME_ID;
|
|
480
437
|
|
|
481
438
|
// Already the right variant?
|
|
482
439
|
if (current.variant === mode) return skin;
|
|
@@ -489,10 +446,10 @@ function themeIdForMode(mode) {
|
|
|
489
446
|
|
|
490
447
|
if (mode === "light") {
|
|
491
448
|
if (lightId && themes[lightId]) return lightId;
|
|
492
|
-
return
|
|
449
|
+
return DEFAULT_LIGHT_THEME_ID;
|
|
493
450
|
} else {
|
|
494
451
|
if (darkId && themes[darkId]) return darkId;
|
|
495
|
-
return
|
|
452
|
+
return DEFAULT_DARK_THEME_ID;
|
|
496
453
|
}
|
|
497
454
|
}
|
|
498
455
|
|
|
@@ -616,58 +573,36 @@ function buildPickerContent() {
|
|
|
616
573
|
}
|
|
617
574
|
}
|
|
618
575
|
|
|
619
|
-
// Clay default themes always first in their section
|
|
576
|
+
// Clay Studio default themes always first in their section.
|
|
620
577
|
function pinFirst(arr, pinId) {
|
|
621
578
|
var idx = arr.indexOf(pinId);
|
|
622
579
|
if (idx > 0) { arr.splice(idx, 1); arr.unshift(pinId); }
|
|
623
580
|
}
|
|
624
|
-
pinFirst(darkIds,
|
|
625
|
-
pinFirst(lightIds,
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
var
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
581
|
+
pinFirst(darkIds, DEFAULT_DARK_THEME_ID);
|
|
582
|
+
pinFirst(lightIds, DEFAULT_LIGHT_THEME_ID);
|
|
583
|
+
|
|
584
|
+
function appendThemeGroup(label, ids) {
|
|
585
|
+
if (ids.length === 0) return;
|
|
586
|
+
var group = document.createElement("div");
|
|
587
|
+
group.className = "theme-picker-group";
|
|
588
|
+
|
|
589
|
+
var header = document.createElement("div");
|
|
590
|
+
header.className = "theme-picker-header";
|
|
591
|
+
header.textContent = label;
|
|
592
|
+
group.appendChild(header);
|
|
593
|
+
|
|
594
|
+
var list = document.createElement("div");
|
|
595
|
+
list.className = "theme-picker-section";
|
|
596
|
+
for (var j = 0; j < ids.length; j++) {
|
|
597
|
+
list.appendChild(createThemeItem(ids[j], themes[ids[j]]));
|
|
638
598
|
}
|
|
639
|
-
|
|
599
|
+
group.appendChild(list);
|
|
600
|
+
pickerEl.appendChild(group);
|
|
640
601
|
}
|
|
641
602
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
lightHeader.className = "theme-picker-header";
|
|
646
|
-
lightHeader.textContent = "Light";
|
|
647
|
-
pickerEl.appendChild(lightHeader);
|
|
648
|
-
|
|
649
|
-
var lightList = document.createElement("div");
|
|
650
|
-
lightList.className = "theme-picker-section";
|
|
651
|
-
for (var l = 0; l < lightIds.length; l++) {
|
|
652
|
-
lightList.appendChild(createThemeItem(lightIds[l], themes[lightIds[l]]));
|
|
653
|
-
}
|
|
654
|
-
pickerEl.appendChild(lightList);
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
// Custom section
|
|
658
|
-
if (customIds.length > 0) {
|
|
659
|
-
var customHeader = document.createElement("div");
|
|
660
|
-
customHeader.className = "theme-picker-header";
|
|
661
|
-
customHeader.textContent = "Custom";
|
|
662
|
-
pickerEl.appendChild(customHeader);
|
|
663
|
-
|
|
664
|
-
var customList = document.createElement("div");
|
|
665
|
-
customList.className = "theme-picker-section";
|
|
666
|
-
for (var c = 0; c < customIds.length; c++) {
|
|
667
|
-
customList.appendChild(createThemeItem(customIds[c], themes[customIds[c]]));
|
|
668
|
-
}
|
|
669
|
-
pickerEl.appendChild(customList);
|
|
670
|
-
}
|
|
603
|
+
appendThemeGroup("Dark", darkIds);
|
|
604
|
+
appendThemeGroup("Light", lightIds);
|
|
605
|
+
appendThemeGroup("Custom", customIds);
|
|
671
606
|
}
|
|
672
607
|
|
|
673
608
|
function createThemePicker() {
|
|
@@ -681,34 +616,34 @@ function createThemePicker() {
|
|
|
681
616
|
return pickerEl;
|
|
682
617
|
}
|
|
683
618
|
|
|
619
|
+
export function mountThemePicker(container) {
|
|
620
|
+
if (!container) return;
|
|
621
|
+
var picker = createThemePicker();
|
|
622
|
+
picker.classList.add("theme-picker-embedded");
|
|
623
|
+
if (picker.parentNode !== container) container.appendChild(picker);
|
|
624
|
+
rebuildPicker();
|
|
625
|
+
}
|
|
626
|
+
|
|
684
627
|
function rebuildPicker() {
|
|
685
628
|
if (!pickerEl) return;
|
|
686
629
|
buildPickerContent();
|
|
687
630
|
}
|
|
688
631
|
|
|
689
|
-
var pickerVisible = false;
|
|
690
|
-
|
|
691
|
-
function togglePicker() {
|
|
692
|
-
// Legacy — no longer used as floating popover
|
|
693
|
-
// Picker is now embedded in server settings
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
function closePicker() {
|
|
697
|
-
// Legacy — no longer needed
|
|
698
|
-
}
|
|
699
|
-
|
|
700
632
|
// --- Init ---
|
|
701
633
|
export function initTheme() {
|
|
702
|
-
//
|
|
634
|
+
// Preserve an explicitly selected skin. Previously auto-applied Ayu Light
|
|
635
|
+
// and Dracula values have no SKIN_KEY, so they migrate to the Clay pair.
|
|
703
636
|
var saved = null;
|
|
637
|
+
var savedSkin = null;
|
|
704
638
|
try { saved = localStorage.getItem(STORAGE_KEY); } catch (e) {}
|
|
639
|
+
try { savedSkin = localStorage.getItem(SKIN_KEY); } catch (e) {}
|
|
705
640
|
|
|
706
|
-
if (saved) {
|
|
641
|
+
if (saved && savedSkin) {
|
|
707
642
|
currentThemeId = saved;
|
|
708
643
|
} else {
|
|
709
|
-
// No
|
|
644
|
+
// No explicit skin — use the Clay Studio pair in the system mode.
|
|
710
645
|
var mode = getSystemMode();
|
|
711
|
-
currentThemeId = mode === "light" ?
|
|
646
|
+
currentThemeId = mode === "light" ? DEFAULT_LIGHT_THEME_ID : DEFAULT_DARK_THEME_ID;
|
|
712
647
|
}
|
|
713
648
|
|
|
714
649
|
// Apply wide view state from localStorage
|
|
@@ -717,8 +652,7 @@ export function initTheme() {
|
|
|
717
652
|
// Load all themes from server, then apply properly
|
|
718
653
|
loadThemes();
|
|
719
654
|
|
|
720
|
-
//
|
|
721
|
-
// Wired up in user-settings.js initUserSettings()
|
|
655
|
+
// The theme picker is mounted in User Settings by user-settings.js.
|
|
722
656
|
|
|
723
657
|
// Listen for system preference changes (only applies if user has no manual override)
|
|
724
658
|
if (window.matchMedia) {
|
|
@@ -743,99 +677,4 @@ export function initTheme() {
|
|
|
743
677
|
// Set initial toggle icon
|
|
744
678
|
updateToggleIcon();
|
|
745
679
|
|
|
746
|
-
var islandToggle = document.getElementById("user-theme-toggle-btn");
|
|
747
|
-
if (islandToggle && !islandToggle._clayThemeBound) {
|
|
748
|
-
islandToggle._clayThemeBound = true;
|
|
749
|
-
// Lock down static skin icon (independent of mode) and open the
|
|
750
|
-
// theme picker popover anchored to the button.
|
|
751
|
-
islandToggle.innerHTML = iconHtml("palette");
|
|
752
|
-
islandToggle.title = "Themes";
|
|
753
|
-
islandToggle.setAttribute("aria-label", "Themes");
|
|
754
|
-
refreshIcons();
|
|
755
|
-
islandToggle.addEventListener("click", function (e) {
|
|
756
|
-
e.stopPropagation();
|
|
757
|
-
toggleIslandThemePicker(islandToggle);
|
|
758
|
-
});
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
// --- Floating popover anchored to the user island button ---
|
|
763
|
-
//
|
|
764
|
-
// Mounts the picker (built by createThemePicker) into document.body as a
|
|
765
|
-
// fixed-position overlay aligned under the anchor's right edge. Click
|
|
766
|
-
// outside or Escape closes it. The picker DOM is shared with any other
|
|
767
|
-
// embed point (settings page); we just reparent and reposition.
|
|
768
|
-
|
|
769
|
-
var islandPickerOpen = false;
|
|
770
|
-
var islandPickerOutsideHandler = null;
|
|
771
|
-
var islandPickerKeyHandler = null;
|
|
772
|
-
|
|
773
|
-
function positionPickerNearAnchor(anchorEl) {
|
|
774
|
-
if (!pickerEl || !anchorEl) return;
|
|
775
|
-
var rect = anchorEl.getBoundingClientRect();
|
|
776
|
-
var picker = pickerEl;
|
|
777
|
-
// Render hidden first to measure
|
|
778
|
-
picker.style.left = "0px";
|
|
779
|
-
picker.style.top = "0px";
|
|
780
|
-
var pw = picker.offsetWidth || 240;
|
|
781
|
-
var ph = picker.offsetHeight || 280;
|
|
782
|
-
var vw = window.innerWidth;
|
|
783
|
-
var vh = window.innerHeight;
|
|
784
|
-
// Right-align to anchor, drop below by 8px. Flip upward if not enough
|
|
785
|
-
// room below. Clamp into viewport on both axes.
|
|
786
|
-
var left = rect.right - pw;
|
|
787
|
-
if (left < 8) left = 8;
|
|
788
|
-
if (left + pw > vw - 8) left = vw - pw - 8;
|
|
789
|
-
var top = rect.bottom + 8;
|
|
790
|
-
if (top + ph > vh - 8) {
|
|
791
|
-
var flipped = rect.top - ph - 8;
|
|
792
|
-
if (flipped >= 8) top = flipped;
|
|
793
|
-
else top = Math.max(8, vh - ph - 8);
|
|
794
|
-
}
|
|
795
|
-
picker.style.left = left + "px";
|
|
796
|
-
picker.style.top = top + "px";
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
function closeIslandThemePicker() {
|
|
800
|
-
if (!islandPickerOpen) return;
|
|
801
|
-
islandPickerOpen = false;
|
|
802
|
-
if (pickerEl) pickerEl.classList.remove("visible");
|
|
803
|
-
if (islandPickerOutsideHandler) {
|
|
804
|
-
document.removeEventListener("mousedown", islandPickerOutsideHandler, true);
|
|
805
|
-
islandPickerOutsideHandler = null;
|
|
806
|
-
}
|
|
807
|
-
if (islandPickerKeyHandler) {
|
|
808
|
-
document.removeEventListener("keydown", islandPickerKeyHandler, true);
|
|
809
|
-
islandPickerKeyHandler = null;
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
export function toggleIslandThemePicker(anchorEl) {
|
|
814
|
-
if (islandPickerOpen) {
|
|
815
|
-
closeIslandThemePicker();
|
|
816
|
-
return;
|
|
817
|
-
}
|
|
818
|
-
if (!pickerEl) createThemePicker();
|
|
819
|
-
if (pickerEl.parentNode !== document.body) {
|
|
820
|
-
document.body.appendChild(pickerEl);
|
|
821
|
-
}
|
|
822
|
-
positionPickerNearAnchor(anchorEl);
|
|
823
|
-
pickerEl.classList.add("visible");
|
|
824
|
-
islandPickerOpen = true;
|
|
825
|
-
|
|
826
|
-
// Click anywhere outside picker (and not the anchor) closes it. Use
|
|
827
|
-
// capture phase + mousedown so the click that opened the picker
|
|
828
|
-
// doesn't immediately close it.
|
|
829
|
-
islandPickerOutsideHandler = function (ev) {
|
|
830
|
-
if (!pickerEl) return;
|
|
831
|
-
if (pickerEl.contains(ev.target)) return;
|
|
832
|
-
if (anchorEl && anchorEl.contains(ev.target)) return;
|
|
833
|
-
closeIslandThemePicker();
|
|
834
|
-
};
|
|
835
|
-
document.addEventListener("mousedown", islandPickerOutsideHandler, true);
|
|
836
|
-
|
|
837
|
-
islandPickerKeyHandler = function (ev) {
|
|
838
|
-
if (ev.key === "Escape") closeIslandThemePicker();
|
|
839
|
-
};
|
|
840
|
-
document.addEventListener("keydown", islandPickerKeyHandler, true);
|
|
841
680
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import { refreshIcons } from './icons.js';
|
|
5
5
|
import { showToast } from './utils.js';
|
|
6
|
-
import { getChatLayout, setChatLayout } from './theme.js';
|
|
6
|
+
import { getChatLayout, mountThemePicker, setChatLayout } from './theme.js';
|
|
7
7
|
import { applyTerminalFont } from './terminal-prefs.js';
|
|
8
8
|
import { showEmailSetupModal, getEmailAccountListCache } from './context-sources.js';
|
|
9
9
|
import { setSTTLang } from './stt.js';
|
|
@@ -231,8 +231,7 @@ export function initUserSettings(appCtx) {
|
|
|
231
231
|
});
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
-
|
|
235
|
-
// No appearance-section mount required.
|
|
234
|
+
mountThemePicker(document.getElementById('us-theme-picker'));
|
|
236
235
|
|
|
237
236
|
// Terminal font picker lives in the title bar (visible only while a
|
|
238
237
|
// TUI session is active). See lib/public/modules/header-tui-font.js.
|
package/lib/public/style.css
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
@import url("css/base.css");
|
|
2
|
-
@import url("css/icon-strip.css");
|
|
3
|
-
@import url("css/title-bar.css?v=
|
|
1
|
+
@import url("css/base.css?v=20260828-type1");
|
|
2
|
+
@import url("css/icon-strip.css?v=20260828-surfaces4");
|
|
3
|
+
@import url("css/title-bar.css?v=20260828-project-header1");
|
|
4
4
|
@import url("css/pane.css");
|
|
5
5
|
@import url("css/worker-proposal.css");
|
|
6
|
-
@import url("css/session-actions.css");
|
|
7
|
-
@import url("css/sidebar.css");
|
|
8
|
-
@import url("css/overlays.css");
|
|
6
|
+
@import url("css/session-actions.css?v=20260828-contrast1");
|
|
7
|
+
@import url("css/sidebar.css?v=20260828-session-list3");
|
|
8
|
+
@import url("css/overlays.css?v=20260827-brand3");
|
|
9
9
|
@import url("css/menus.css");
|
|
10
|
-
@import url("css/messages.css?v=
|
|
10
|
+
@import url("css/messages.css?v=20260828-delegated-muted1");
|
|
11
11
|
@import url("css/rewind.css");
|
|
12
|
-
@import url("css/input.css");
|
|
13
|
-
@import url("css/filebrowser.css");
|
|
12
|
+
@import url("css/input.css?v=20260828-composer-depth1");
|
|
13
|
+
@import url("css/filebrowser.css?v=20260828-workbench4");
|
|
14
14
|
@import url("css/git-panel.css");
|
|
15
15
|
@import url("css/diff.css");
|
|
16
16
|
@import url("css/highlight.css");
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
@import url("css/playbook.css");
|
|
27
27
|
@import url("css/stt.css");
|
|
28
28
|
@import url("css/profile.css");
|
|
29
|
-
@import url("css/user-settings.css");
|
|
29
|
+
@import url("css/user-settings.css?v=20260828-type1");
|
|
30
30
|
@import url("css/admin.css");
|
|
31
31
|
@import url("css/session-search.css");
|
|
32
32
|
@import url("css/tooltip.css");
|
package/lib/public/sw.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var CACHE_NAME = "clay-offline-
|
|
1
|
+
var CACHE_NAME = "clay-offline-v4";
|
|
2
2
|
|
|
3
3
|
self.addEventListener("install", function (event) {
|
|
4
4
|
event.waitUntil(self.skipWaiting());
|
|
@@ -68,7 +68,7 @@ self.addEventListener("fetch", function (event) {
|
|
|
68
68
|
// If even the index page is not cached, show a minimal offline page
|
|
69
69
|
if (indexCached) return indexCached;
|
|
70
70
|
return new Response(
|
|
71
|
-
"<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Clay</title>" +
|
|
71
|
+
"<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Clay Studio</title>" +
|
|
72
72
|
"<style>body{margin:0;background:#000;color:#888;display:flex;" +
|
|
73
73
|
"align-items:center;justify-content:center;height:100vh;" +
|
|
74
74
|
"font-family:monospace;font-size:1.2em}</style></head>" +
|
|
@@ -97,6 +97,8 @@ self.addEventListener("push", function (event) {
|
|
|
97
97
|
body: data.body || "",
|
|
98
98
|
tag: data.tag || "clay",
|
|
99
99
|
data: data,
|
|
100
|
+
icon: "/clay-studio-symbol.png",
|
|
101
|
+
badge: "/clay-studio-favicon-32.png",
|
|
100
102
|
};
|
|
101
103
|
|
|
102
104
|
if (data.type === "permission_request") {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Clay Studio Dark",
|
|
3
|
+
"author": "Clay Studio",
|
|
4
|
+
"variant": "dark",
|
|
5
|
+
"base00": "171715", "base01": "20201D", "base02": "393934", "base03": "686861",
|
|
6
|
+
"base04": "A0A098", "base05": "D1D1CA", "base06": "E8E8E2", "base07": "FAFAF5",
|
|
7
|
+
"base08": "FF7479", "base09": "7776FF", "base0A": "E0B45B", "base0B": "07E5A3",
|
|
8
|
+
"base0C": "5FD2C0", "base0D": "8AA7FF", "base0E": "C39AF3", "base0F": "D69A7D",
|
|
9
|
+
"accent2": "07E5A3",
|
|
10
|
+
"link": "7F7EC8"
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Clay Studio Light",
|
|
3
|
+
"author": "Clay Studio",
|
|
4
|
+
"variant": "light",
|
|
5
|
+
"base00": "F7F7F3", "base01": "EEEEEA", "base02": "D7D7D0", "base03": "A2A29B",
|
|
6
|
+
"base04": "73736C", "base05": "44443F", "base06": "252522", "base07": "151513",
|
|
7
|
+
"base08": "C84B50", "base09": "5857FC", "base0A": "956A19", "base0B": "087B59",
|
|
8
|
+
"base0C": "187A7C", "base0D": "3F62B4", "base0E": "7A52A8", "base0F": "9A5A46",
|
|
9
|
+
"accent2": "07E5A3",
|
|
10
|
+
"link": "4948B8"
|
|
11
|
+
}
|
package/package.json
CHANGED
|
Binary file
|
package/lib/public/clay-logo.png
DELETED
|
Binary file
|
|
Binary file
|
|
Binary file
|