clay-server 2.5.0 → 2.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/bin/claude-relay.js +6 -0
- package/bin/cli.js +26 -8
- package/lib/cli-sessions.js +2 -7
- package/lib/config.js +78 -5
- package/lib/daemon.js +233 -2
- package/lib/notes.js +3 -2
- package/lib/project.js +465 -28
- package/lib/public/app.js +187 -24
- package/lib/public/css/base.css +61 -61
- package/lib/public/css/diff.css +3 -4
- package/lib/public/css/filebrowser.css +362 -2
- package/lib/public/css/icon-strip.css +317 -1
- package/lib/public/css/input.css +127 -50
- package/lib/public/css/messages.css +1 -1
- package/lib/public/css/mobile-nav.css +8 -4
- package/lib/public/css/overlays.css +9 -6
- package/lib/public/css/server-settings.css +67 -20
- package/lib/public/css/sidebar.css +10 -101
- package/lib/public/css/skills.css +730 -0
- package/lib/public/css/title-bar.css +82 -4
- package/lib/public/index.html +277 -70
- package/lib/public/modules/input.js +119 -56
- package/lib/public/modules/project-settings.js +906 -0
- package/lib/public/modules/server-settings.js +409 -53
- package/lib/public/modules/sidebar.js +720 -1
- package/lib/public/modules/skills.js +710 -0
- package/lib/public/modules/terminal.js +7 -0
- package/lib/public/modules/theme.js +88 -89
- package/lib/public/style.css +1 -0
- package/lib/sdk-bridge.js +18 -7
- package/lib/server.js +305 -1
- package/lib/sessions.js +9 -4
- package/lib/utils.js +18 -0
- package/package.json +3 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// server-settings.js — Full-screen server settings overlay
|
|
2
2
|
import { refreshIcons } from './icons.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { showToast, copyToClipboard } from './utils.js';
|
|
4
|
+
import { parseEnvString, looksLikeEnv } from './project-settings.js';
|
|
5
5
|
|
|
6
6
|
var ctx = null;
|
|
7
7
|
var settingsEl = null;
|
|
@@ -11,6 +11,26 @@ var navItems = null;
|
|
|
11
11
|
var sections = null;
|
|
12
12
|
var statsTimer = null;
|
|
13
13
|
|
|
14
|
+
var SS_MODE_OPTIONS = [
|
|
15
|
+
{ value: "default", label: "Default", desc: "Claude asks for permission before running tools and editing files." },
|
|
16
|
+
{ value: "plan", label: "Plan", desc: "Claude creates a plan first and asks for approval before making changes." },
|
|
17
|
+
{ value: "acceptEdits", label: "Auto-accept edits", desc: "File edits are applied automatically. Claude still asks before running commands." },
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
var SS_EFFORT_LEVELS = [
|
|
21
|
+
{ value: "low", desc: "Quick, concise responses. Best for simple questions." },
|
|
22
|
+
{ value: "medium", desc: "Balanced responses with moderate reasoning. Good for most tasks." },
|
|
23
|
+
{ value: "high", desc: "Thorough responses with deeper analysis. Good for complex tasks." },
|
|
24
|
+
{ value: "max", desc: "Maximum reasoning depth. Best for the most difficult problems." },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
var SS_MODEL_DESCRIPTIONS = {
|
|
28
|
+
"default": "Automatically selects the best model for the task.",
|
|
29
|
+
"sonnet": "Fast and capable. Great balance of speed and intelligence.",
|
|
30
|
+
"haiku": "Fastest model. Best for quick tasks and simple questions.",
|
|
31
|
+
"opus": "Most powerful model. Best for complex reasoning and analysis.",
|
|
32
|
+
};
|
|
33
|
+
|
|
14
34
|
export function initServerSettings(appCtx) {
|
|
15
35
|
ctx = appCtx;
|
|
16
36
|
settingsEl = document.getElementById("server-settings");
|
|
@@ -47,18 +67,22 @@ export function initServerSettings(appCtx) {
|
|
|
47
67
|
});
|
|
48
68
|
}
|
|
49
69
|
|
|
50
|
-
//
|
|
51
|
-
var
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (
|
|
59
|
-
|
|
70
|
+
// Copyable command blocks
|
|
71
|
+
var copyables = settingsEl.querySelectorAll(".settings-copyable");
|
|
72
|
+
for (var c = 0; c < copyables.length; c++) {
|
|
73
|
+
copyables[c].addEventListener("click", function () {
|
|
74
|
+
var text = this.dataset.copy;
|
|
75
|
+
if (!text) return;
|
|
76
|
+
var btn = this.querySelector(".settings-copy-btn");
|
|
77
|
+
copyToClipboard(text).then(function () {
|
|
78
|
+
if (btn) {
|
|
79
|
+
var orig = btn.textContent;
|
|
80
|
+
btn.textContent = "✓";
|
|
81
|
+
setTimeout(function () { btn.textContent = orig; }, 1500);
|
|
82
|
+
}
|
|
83
|
+
showToast("Copied to clipboard");
|
|
60
84
|
});
|
|
61
|
-
}
|
|
85
|
+
});
|
|
62
86
|
}
|
|
63
87
|
|
|
64
88
|
// Notification toggles
|
|
@@ -104,7 +128,7 @@ export function initServerSettings(appCtx) {
|
|
|
104
128
|
if (!model) return;
|
|
105
129
|
var ws = ctx.ws;
|
|
106
130
|
if (ws && ws.readyState === 1) {
|
|
107
|
-
ws.send(JSON.stringify({ type: "
|
|
131
|
+
ws.send(JSON.stringify({ type: "set_server_default_model", model: model }));
|
|
108
132
|
}
|
|
109
133
|
});
|
|
110
134
|
|
|
@@ -140,6 +164,21 @@ export function initServerSettings(appCtx) {
|
|
|
140
164
|
});
|
|
141
165
|
}
|
|
142
166
|
|
|
167
|
+
// Global CLAUDE.md: save button
|
|
168
|
+
var ssClaudeMdSave = document.getElementById("ss-claudemd-save");
|
|
169
|
+
if (ssClaudeMdSave) {
|
|
170
|
+
ssClaudeMdSave.addEventListener("click", function () { saveGlobalClaudeMd(); });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Shared environment: add button
|
|
174
|
+
var ssEnvAddBtn = document.getElementById("ss-env-add-btn");
|
|
175
|
+
if (ssEnvAddBtn) {
|
|
176
|
+
ssEnvAddBtn.addEventListener("click", function () {
|
|
177
|
+
addSharedEnvRow("", "", true);
|
|
178
|
+
autoSaveSharedEnv();
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
143
182
|
// Shutdown server
|
|
144
183
|
var shutdownInput = document.getElementById("settings-shutdown-input");
|
|
145
184
|
var shutdownBtn = document.getElementById("settings-shutdown-btn");
|
|
@@ -184,6 +223,10 @@ function switchSection(sectionName) {
|
|
|
184
223
|
var isActive2 = sections[j].dataset.section === sectionName;
|
|
185
224
|
sections[j].classList.toggle("active", isActive2);
|
|
186
225
|
}
|
|
226
|
+
|
|
227
|
+
// Lazy-load section data
|
|
228
|
+
if (sectionName === "claudemd") loadGlobalClaudeMd();
|
|
229
|
+
if (sectionName === "environment") loadSharedEnv();
|
|
187
230
|
}
|
|
188
231
|
|
|
189
232
|
function openSettings() {
|
|
@@ -262,14 +305,11 @@ function populateSettings() {
|
|
|
262
305
|
// Sync notification toggles
|
|
263
306
|
syncNotifToggles();
|
|
264
307
|
|
|
265
|
-
//
|
|
266
|
-
updateThemeDisplay();
|
|
267
|
-
|
|
268
|
-
// Context view
|
|
269
|
-
updateContextViewButtons();
|
|
270
|
-
|
|
271
|
-
// Models
|
|
308
|
+
// Session defaults
|
|
272
309
|
updateModelList();
|
|
310
|
+
updateModeList();
|
|
311
|
+
updateEffortBar();
|
|
312
|
+
updateSsBetaCard();
|
|
273
313
|
}
|
|
274
314
|
|
|
275
315
|
function syncNotifToggles() {
|
|
@@ -285,39 +325,26 @@ function syncNotifToggles() {
|
|
|
285
325
|
}
|
|
286
326
|
}
|
|
287
327
|
|
|
288
|
-
function
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
328
|
+
function ssGetModelDesc(model) {
|
|
329
|
+
if (!model) return "";
|
|
330
|
+
var lower = model.toLowerCase();
|
|
331
|
+
for (var key in SS_MODEL_DESCRIPTIONS) {
|
|
332
|
+
if (lower.indexOf(key) !== -1) return SS_MODEL_DESCRIPTIONS[key];
|
|
292
333
|
}
|
|
334
|
+
return "";
|
|
293
335
|
}
|
|
294
336
|
|
|
295
|
-
function
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
var btns = document.querySelectorAll("#settings-context-view .settings-btn-option");
|
|
299
|
-
for (var i = 0; i < btns.length; i++) {
|
|
300
|
-
btns[i].classList.toggle("active", btns[i].dataset.view === view);
|
|
301
|
-
}
|
|
337
|
+
function ssIsSonnetModel(model) {
|
|
338
|
+
if (!model) return false;
|
|
339
|
+
return model.toLowerCase().indexOf("sonnet") !== -1;
|
|
302
340
|
}
|
|
303
341
|
|
|
304
342
|
function updateModelList() {
|
|
305
343
|
var listEl = document.getElementById("settings-model-list");
|
|
306
|
-
var currentEl = document.getElementById("settings-current-model");
|
|
307
344
|
if (!listEl) return;
|
|
308
345
|
|
|
309
346
|
var models = ctx.currentModels || [];
|
|
310
|
-
var currentModel = ctx._currentModelValue || "";
|
|
311
|
-
|
|
312
|
-
// Look up display name for settings panel
|
|
313
|
-
var displayName = currentModel;
|
|
314
|
-
for (var j = 0; j < models.length; j++) {
|
|
315
|
-
if (models[j].value === currentModel && models[j].displayName) {
|
|
316
|
-
displayName = models[j].displayName;
|
|
317
|
-
break;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
if (currentEl) currentEl.textContent = displayName || "-";
|
|
347
|
+
var currentModel = ctx.currentModel || ctx._currentModelValue || "";
|
|
321
348
|
|
|
322
349
|
listEl.innerHTML = "";
|
|
323
350
|
if (models.length === 0) {
|
|
@@ -326,15 +353,145 @@ function updateModelList() {
|
|
|
326
353
|
}
|
|
327
354
|
|
|
328
355
|
for (var i = 0; i < models.length; i++) {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
356
|
+
(function (m) {
|
|
357
|
+
var value = m.value || "";
|
|
358
|
+
var label = m.displayName || value;
|
|
359
|
+
var item = document.createElement("div");
|
|
360
|
+
item.className = "settings-model-item";
|
|
361
|
+
if (value === currentModel) item.classList.add("active");
|
|
362
|
+
item.dataset.model = value;
|
|
363
|
+
|
|
364
|
+
var nameSpan = document.createElement("span");
|
|
365
|
+
nameSpan.className = "settings-model-name";
|
|
366
|
+
nameSpan.textContent = label;
|
|
367
|
+
item.appendChild(nameSpan);
|
|
368
|
+
|
|
369
|
+
var desc = ssGetModelDesc(value);
|
|
370
|
+
if (desc) {
|
|
371
|
+
var descSpan = document.createElement("span");
|
|
372
|
+
descSpan.className = "settings-model-desc";
|
|
373
|
+
descSpan.textContent = desc;
|
|
374
|
+
item.appendChild(descSpan);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
item.addEventListener("click", function () {
|
|
378
|
+
var ws = ctx.ws;
|
|
379
|
+
if (ws && ws.readyState === 1) {
|
|
380
|
+
ws.send(JSON.stringify({ type: "set_model", model: value }));
|
|
381
|
+
}
|
|
382
|
+
var items = listEl.querySelectorAll(".settings-model-item");
|
|
383
|
+
for (var j = 0; j < items.length; j++) items[j].classList.remove("active");
|
|
384
|
+
item.classList.add("active");
|
|
385
|
+
updateSsBetaCard(value);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
listEl.appendChild(item);
|
|
389
|
+
})(models[i]);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function updateModeList() {
|
|
394
|
+
var listEl = document.getElementById("ss-mode-list");
|
|
395
|
+
if (!listEl) return;
|
|
396
|
+
|
|
397
|
+
var currentMode = ctx.currentMode || "default";
|
|
398
|
+
listEl.innerHTML = "";
|
|
399
|
+
|
|
400
|
+
for (var i = 0; i < SS_MODE_OPTIONS.length; i++) {
|
|
401
|
+
(function (opt) {
|
|
402
|
+
var item = document.createElement("div");
|
|
403
|
+
item.className = "settings-model-item" + (opt.value === currentMode ? " active" : "");
|
|
404
|
+
|
|
405
|
+
var nameSpan = document.createElement("span");
|
|
406
|
+
nameSpan.className = "settings-model-name";
|
|
407
|
+
nameSpan.textContent = opt.label;
|
|
408
|
+
item.appendChild(nameSpan);
|
|
409
|
+
|
|
410
|
+
var descSpan = document.createElement("span");
|
|
411
|
+
descSpan.className = "settings-model-desc";
|
|
412
|
+
descSpan.textContent = opt.desc;
|
|
413
|
+
item.appendChild(descSpan);
|
|
414
|
+
|
|
415
|
+
item.addEventListener("click", function () {
|
|
416
|
+
var ws = ctx.ws;
|
|
417
|
+
if (ws && ws.readyState === 1) {
|
|
418
|
+
ws.send(JSON.stringify({ type: "set_server_default_mode", mode: opt.value }));
|
|
419
|
+
}
|
|
420
|
+
var items = listEl.querySelectorAll(".settings-model-item");
|
|
421
|
+
for (var j = 0; j < items.length; j++) items[j].classList.remove("active");
|
|
422
|
+
item.classList.add("active");
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
listEl.appendChild(item);
|
|
426
|
+
})(SS_MODE_OPTIONS[i]);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function updateEffortBar() {
|
|
431
|
+
var bar = document.getElementById("ss-effort-bar");
|
|
432
|
+
if (!bar) return;
|
|
433
|
+
|
|
434
|
+
var currentEffort = ctx.currentEffort || "medium";
|
|
435
|
+
bar.innerHTML = "";
|
|
436
|
+
|
|
437
|
+
for (var i = 0; i < SS_EFFORT_LEVELS.length; i++) {
|
|
438
|
+
(function (lvl) {
|
|
439
|
+
var btn = document.createElement("button");
|
|
440
|
+
btn.className = "settings-btn-option" + (lvl.value === currentEffort ? " active" : "");
|
|
441
|
+
btn.textContent = lvl.value.charAt(0).toUpperCase() + lvl.value.slice(1);
|
|
442
|
+
btn.title = lvl.desc;
|
|
443
|
+
btn.addEventListener("click", function () {
|
|
444
|
+
var ws = ctx.ws;
|
|
445
|
+
if (ws && ws.readyState === 1) {
|
|
446
|
+
ws.send(JSON.stringify({ type: "set_server_default_effort", effort: lvl.value }));
|
|
447
|
+
}
|
|
448
|
+
var btns = bar.querySelectorAll(".settings-btn-option");
|
|
449
|
+
for (var j = 0; j < btns.length; j++) btns[j].classList.remove("active");
|
|
450
|
+
btn.classList.add("active");
|
|
451
|
+
});
|
|
452
|
+
bar.appendChild(btn);
|
|
453
|
+
})(SS_EFFORT_LEVELS[i]);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function updateSsBetaCard(overrideModel) {
|
|
458
|
+
var model = overrideModel || ctx.currentModel || ctx._currentModelValue || "";
|
|
459
|
+
var card = document.getElementById("ss-beta-card");
|
|
460
|
+
if (card) {
|
|
461
|
+
card.style.display = ssIsSonnetModel(model) ? "" : "none";
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
var toggle = document.getElementById("ss-beta-1m");
|
|
465
|
+
if (toggle) {
|
|
466
|
+
var betas = ctx.currentBetas || [];
|
|
467
|
+
var hasBeta = false;
|
|
468
|
+
for (var i = 0; i < betas.length; i++) {
|
|
469
|
+
if (betas[i].indexOf("context-1m") !== -1) { hasBeta = true; break; }
|
|
470
|
+
}
|
|
471
|
+
toggle.checked = hasBeta;
|
|
472
|
+
toggle.onchange = function () {
|
|
473
|
+
ssToggleBeta1m(this.checked);
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function ssToggleBeta1m(enable) {
|
|
479
|
+
var betas = ctx.currentBetas || [];
|
|
480
|
+
var newBetas;
|
|
481
|
+
if (enable) {
|
|
482
|
+
newBetas = betas.slice();
|
|
483
|
+
newBetas.push("context-1m-2025-08-07");
|
|
484
|
+
} else {
|
|
485
|
+
newBetas = [];
|
|
486
|
+
for (var i = 0; i < betas.length; i++) {
|
|
487
|
+
if (betas[i].indexOf("context-1m") === -1) {
|
|
488
|
+
newBetas.push(betas[i]);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
var ws = ctx.ws;
|
|
493
|
+
if (ws && ws.readyState === 1) {
|
|
494
|
+
ws.send(JSON.stringify({ type: "set_betas", betas: newBetas }));
|
|
338
495
|
}
|
|
339
496
|
}
|
|
340
497
|
|
|
@@ -359,6 +516,9 @@ export function updateSettingsModels(current, models) {
|
|
|
359
516
|
ctx._currentModelValue = current;
|
|
360
517
|
if (isSettingsOpen()) {
|
|
361
518
|
updateModelList();
|
|
519
|
+
updateModeList();
|
|
520
|
+
updateEffortBar();
|
|
521
|
+
updateSsBetaCard();
|
|
362
522
|
}
|
|
363
523
|
}
|
|
364
524
|
|
|
@@ -492,6 +652,202 @@ function updatePinStatus(enabled) {
|
|
|
492
652
|
if (actionLabel) actionLabel.textContent = enabled ? "Change PIN" : "Set PIN";
|
|
493
653
|
}
|
|
494
654
|
|
|
655
|
+
// ===== Global CLAUDE.md =====
|
|
656
|
+
function loadGlobalClaudeMd() {
|
|
657
|
+
var editor = document.getElementById("ss-claudemd-editor");
|
|
658
|
+
var status = document.getElementById("ss-claudemd-status");
|
|
659
|
+
var saveStatus = document.getElementById("ss-claudemd-save-status");
|
|
660
|
+
if (saveStatus) saveStatus.textContent = "";
|
|
661
|
+
if (status) status.textContent = "Loading...";
|
|
662
|
+
|
|
663
|
+
var ws = ctx.ws;
|
|
664
|
+
if (ws && ws.readyState === 1) {
|
|
665
|
+
ws.send(JSON.stringify({ type: "read_global_claude_md" }));
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
export function handleGlobalClaudeMdRead(msg) {
|
|
670
|
+
var editor = document.getElementById("ss-claudemd-editor");
|
|
671
|
+
var status = document.getElementById("ss-claudemd-status");
|
|
672
|
+
if (!editor) return;
|
|
673
|
+
|
|
674
|
+
if (msg.error) {
|
|
675
|
+
editor.value = "";
|
|
676
|
+
if (status) status.textContent = "No global CLAUDE.md found. Save to create one.";
|
|
677
|
+
} else {
|
|
678
|
+
editor.value = msg.content || "";
|
|
679
|
+
if (status) status.textContent = "";
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function saveGlobalClaudeMd() {
|
|
684
|
+
var editor = document.getElementById("ss-claudemd-editor");
|
|
685
|
+
var saveStatus = document.getElementById("ss-claudemd-save-status");
|
|
686
|
+
if (!editor) return;
|
|
687
|
+
|
|
688
|
+
var ws = ctx.ws;
|
|
689
|
+
if (ws && ws.readyState === 1) {
|
|
690
|
+
ws.send(JSON.stringify({ type: "write_global_claude_md", content: editor.value }));
|
|
691
|
+
if (saveStatus) saveStatus.textContent = "Saving...";
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
export function handleGlobalClaudeMdWrite(msg) {
|
|
696
|
+
var saveStatus = document.getElementById("ss-claudemd-save-status");
|
|
697
|
+
if (!saveStatus) return;
|
|
698
|
+
if (msg.ok) {
|
|
699
|
+
saveStatus.textContent = "Saved";
|
|
700
|
+
setTimeout(function () { saveStatus.textContent = ""; }, 2000);
|
|
701
|
+
} else {
|
|
702
|
+
saveStatus.textContent = "Error: " + (msg.error || "Failed to save");
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// ===== Shared Environment Variables =====
|
|
707
|
+
var sharedEnvSaveTimer = null;
|
|
708
|
+
|
|
709
|
+
function loadSharedEnv() {
|
|
710
|
+
var saveStatus = document.getElementById("ss-env-save-status");
|
|
711
|
+
if (saveStatus) saveStatus.textContent = "";
|
|
712
|
+
|
|
713
|
+
var ws = ctx.ws;
|
|
714
|
+
if (ws && ws.readyState === 1) {
|
|
715
|
+
ws.send(JSON.stringify({ type: "get_shared_env" }));
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
export function handleSharedEnv(msg) {
|
|
720
|
+
var list = document.getElementById("ss-env-list");
|
|
721
|
+
if (!list) return;
|
|
722
|
+
list.innerHTML = "";
|
|
723
|
+
|
|
724
|
+
var pairs = parseEnvString(msg.envrc || "");
|
|
725
|
+
for (var i = 0; i < pairs.length; i++) {
|
|
726
|
+
addSharedEnvRow(pairs[i].key, pairs[i].value, false);
|
|
727
|
+
}
|
|
728
|
+
refreshIcons();
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
export function handleSharedEnvSaved(msg) {
|
|
732
|
+
var saveStatus = document.getElementById("ss-env-save-status");
|
|
733
|
+
if (!saveStatus) return;
|
|
734
|
+
if (msg.ok) {
|
|
735
|
+
saveStatus.textContent = "Saved";
|
|
736
|
+
setTimeout(function () { saveStatus.textContent = ""; }, 2000);
|
|
737
|
+
} else {
|
|
738
|
+
saveStatus.textContent = "Error: " + (msg.error || "Failed to save");
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
function buildSharedEnvString() {
|
|
743
|
+
var list = document.getElementById("ss-env-list");
|
|
744
|
+
if (!list) return "";
|
|
745
|
+
var rows = list.querySelectorAll(".ps-env-row");
|
|
746
|
+
var lines = [];
|
|
747
|
+
for (var i = 0; i < rows.length; i++) {
|
|
748
|
+
var keyInput = rows[i].querySelector(".ps-env-key");
|
|
749
|
+
var valInput = rows[i].querySelector(".ps-env-val");
|
|
750
|
+
var key = keyInput ? keyInput.value.trim() : "";
|
|
751
|
+
var val = valInput ? valInput.value : "";
|
|
752
|
+
if (key) lines.push("export " + key + "=" + val);
|
|
753
|
+
}
|
|
754
|
+
return lines.join("\n");
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
function addSharedEnvRow(key, value, focus) {
|
|
758
|
+
var list = document.getElementById("ss-env-list");
|
|
759
|
+
if (!list) return;
|
|
760
|
+
|
|
761
|
+
var row = document.createElement("div");
|
|
762
|
+
row.className = "ps-env-row";
|
|
763
|
+
|
|
764
|
+
var keyInput = document.createElement("input");
|
|
765
|
+
keyInput.type = "text";
|
|
766
|
+
keyInput.className = "ps-env-key";
|
|
767
|
+
keyInput.placeholder = "KEY";
|
|
768
|
+
keyInput.value = key;
|
|
769
|
+
keyInput.spellcheck = false;
|
|
770
|
+
keyInput.autocomplete = "off";
|
|
771
|
+
|
|
772
|
+
var valInput = document.createElement("input");
|
|
773
|
+
valInput.type = "text";
|
|
774
|
+
valInput.className = "ps-env-val";
|
|
775
|
+
valInput.placeholder = "value";
|
|
776
|
+
valInput.value = value;
|
|
777
|
+
valInput.spellcheck = false;
|
|
778
|
+
valInput.autocomplete = "off";
|
|
779
|
+
|
|
780
|
+
var delBtn = document.createElement("button");
|
|
781
|
+
delBtn.className = "ps-env-del";
|
|
782
|
+
delBtn.title = "Remove";
|
|
783
|
+
delBtn.innerHTML = '<i data-lucide="x"></i>';
|
|
784
|
+
|
|
785
|
+
delBtn.addEventListener("click", function () {
|
|
786
|
+
row.remove();
|
|
787
|
+
autoSaveSharedEnv();
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
keyInput.addEventListener("input", function () { autoSaveSharedEnv(); });
|
|
791
|
+
valInput.addEventListener("input", function () { autoSaveSharedEnv(); });
|
|
792
|
+
|
|
793
|
+
// Paste detection
|
|
794
|
+
keyInput.addEventListener("paste", function (e) {
|
|
795
|
+
var text = (e.clipboardData || window.clipboardData).getData("text");
|
|
796
|
+
if (text && looksLikeEnv(text)) {
|
|
797
|
+
e.preventDefault();
|
|
798
|
+
var pairs = parseEnvString(text);
|
|
799
|
+
if (pairs.length > 0) {
|
|
800
|
+
keyInput.value = pairs[0].key;
|
|
801
|
+
valInput.value = pairs[0].value;
|
|
802
|
+
for (var p = 1; p < pairs.length; p++) {
|
|
803
|
+
addSharedEnvRow(pairs[p].key, pairs[p].value, false);
|
|
804
|
+
}
|
|
805
|
+
autoSaveSharedEnv();
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
valInput.addEventListener("paste", function (e) {
|
|
811
|
+
var text = (e.clipboardData || window.clipboardData).getData("text");
|
|
812
|
+
if (text && text.indexOf("\n") !== -1 && text.indexOf("=") !== -1) {
|
|
813
|
+
e.preventDefault();
|
|
814
|
+
var pairs = parseEnvString(text);
|
|
815
|
+
if (pairs.length > 0) {
|
|
816
|
+
keyInput.value = pairs[0].key;
|
|
817
|
+
valInput.value = pairs[0].value;
|
|
818
|
+
for (var p = 1; p < pairs.length; p++) {
|
|
819
|
+
addSharedEnvRow(pairs[p].key, pairs[p].value, false);
|
|
820
|
+
}
|
|
821
|
+
autoSaveSharedEnv();
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
row.appendChild(keyInput);
|
|
827
|
+
row.appendChild(valInput);
|
|
828
|
+
row.appendChild(delBtn);
|
|
829
|
+
list.appendChild(row);
|
|
830
|
+
refreshIcons();
|
|
831
|
+
|
|
832
|
+
if (focus) keyInput.focus();
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
function autoSaveSharedEnv() {
|
|
836
|
+
if (sharedEnvSaveTimer) clearTimeout(sharedEnvSaveTimer);
|
|
837
|
+
sharedEnvSaveTimer = setTimeout(function () {
|
|
838
|
+
var envrc = buildSharedEnvString();
|
|
839
|
+
var ws = ctx.ws;
|
|
840
|
+
if (ws && ws.readyState === 1) {
|
|
841
|
+
ws.send(JSON.stringify({ type: "set_shared_env", envrc: envrc }));
|
|
842
|
+
var saveStatus = document.getElementById("ss-env-save-status");
|
|
843
|
+
if (saveStatus) {
|
|
844
|
+
saveStatus.textContent = "Saved";
|
|
845
|
+
setTimeout(function () { saveStatus.textContent = ""; }, 2000);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}, 800);
|
|
849
|
+
}
|
|
850
|
+
|
|
495
851
|
function formatBytes(n) {
|
|
496
852
|
if (n >= 1073741824) return (n / 1073741824).toFixed(1) + " GB";
|
|
497
853
|
if (n >= 1048576) return (n / 1048576).toFixed(1) + " MB";
|