clay-server 2.5.1 → 2.7.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 +77 -11
- package/lib/cli-sessions.js +2 -7
- package/lib/config.js +86 -7
- package/lib/daemon.js +279 -6
- package/lib/ipc.js +12 -0
- package/lib/notes.js +4 -3
- package/lib/project.js +1174 -28
- package/lib/public/app.js +879 -31
- package/lib/public/css/diff.css +15 -4
- package/lib/public/css/filebrowser.css +363 -3
- package/lib/public/css/icon-strip.css +317 -1
- package/lib/public/css/input.css +127 -50
- package/lib/public/css/loop.css +780 -0
- package/lib/public/css/messages.css +1 -1
- package/lib/public/css/mobile-nav.css +6 -2
- package/lib/public/css/rewind.css +23 -0
- package/lib/public/css/server-settings.css +67 -20
- package/lib/public/css/sidebar.css +10 -4
- package/lib/public/css/skills.css +789 -0
- package/lib/public/css/sticky-notes.css +486 -0
- package/lib/public/css/title-bar.css +157 -7
- package/lib/public/index.html +366 -55
- package/lib/public/modules/diff.js +3 -3
- package/lib/public/modules/filebrowser.js +169 -45
- package/lib/public/modules/input.js +123 -56
- package/lib/public/modules/project-settings.js +906 -0
- package/lib/public/modules/qrcode.js +23 -26
- package/lib/public/modules/server-settings.js +449 -53
- package/lib/public/modules/sidebar.js +732 -1
- package/lib/public/modules/skills.js +794 -0
- package/lib/public/modules/sticky-notes.js +617 -52
- package/lib/public/modules/terminal.js +7 -0
- package/lib/public/modules/theme.js +9 -19
- package/lib/public/modules/tools.js +16 -2
- package/lib/public/style.css +2 -0
- package/lib/sdk-bridge.js +46 -7
- package/lib/server.js +305 -1
- package/lib/sessions.js +11 -5
- package/lib/utils.js +18 -0
- package/package.json +3 -2
|
@@ -0,0 +1,906 @@
|
|
|
1
|
+
// project-settings.js — Project settings panel (profile, defaults, instructions, env)
|
|
2
|
+
import { refreshIcons } from './icons.js';
|
|
3
|
+
import { showToast } from './utils.js';
|
|
4
|
+
|
|
5
|
+
var ctx = null;
|
|
6
|
+
var panelEl = null;
|
|
7
|
+
var navItems = null;
|
|
8
|
+
var sections = null;
|
|
9
|
+
var currentSlug = null;
|
|
10
|
+
var currentProject = null; // { slug, name, icon }
|
|
11
|
+
|
|
12
|
+
// Emoji categories (reuse from sidebar)
|
|
13
|
+
var EMOJI_CATEGORIES = null;
|
|
14
|
+
|
|
15
|
+
var MODE_OPTIONS = [
|
|
16
|
+
{ value: "default", label: "Default", desc: "Claude asks for permission before running tools and editing files." },
|
|
17
|
+
{ value: "plan", label: "Plan", desc: "Claude creates a plan first and asks for approval before making changes." },
|
|
18
|
+
{ value: "acceptEdits", label: "Auto-accept edits", desc: "File edits are applied automatically. Claude still asks before running commands." },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
var EFFORT_LEVELS = [
|
|
22
|
+
{ value: "low", desc: "Quick, concise responses. Best for simple questions." },
|
|
23
|
+
{ value: "medium", desc: "Balanced responses with moderate reasoning. Good for most tasks." },
|
|
24
|
+
{ value: "high", desc: "Thorough responses with deeper analysis. Good for complex tasks." },
|
|
25
|
+
{ value: "max", desc: "Maximum reasoning depth. Best for the most difficult problems." },
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
var MODEL_DESCRIPTIONS = {
|
|
29
|
+
"default": "Automatically selects the best model for the task.",
|
|
30
|
+
"sonnet": "Fast and capable. Great balance of speed and intelligence.",
|
|
31
|
+
"haiku": "Fastest model. Best for quick tasks and simple questions.",
|
|
32
|
+
"opus": "Most powerful model. Best for complex reasoning and analysis.",
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// ===== Init =====
|
|
36
|
+
export function initProjectSettings(appCtx, emojiCategories) {
|
|
37
|
+
ctx = appCtx;
|
|
38
|
+
EMOJI_CATEGORIES = emojiCategories;
|
|
39
|
+
panelEl = document.getElementById("project-settings");
|
|
40
|
+
if (!panelEl) return;
|
|
41
|
+
|
|
42
|
+
navItems = panelEl.querySelectorAll(".settings-nav-item");
|
|
43
|
+
sections = panelEl.querySelectorAll(".ps-section");
|
|
44
|
+
|
|
45
|
+
// Nav clicks
|
|
46
|
+
for (var i = 0; i < navItems.length; i++) {
|
|
47
|
+
navItems[i].addEventListener("click", function () {
|
|
48
|
+
switchSection(this.dataset.section);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Close button
|
|
53
|
+
var closeBtn = document.getElementById("project-settings-close");
|
|
54
|
+
if (closeBtn) {
|
|
55
|
+
closeBtn.addEventListener("click", function () {
|
|
56
|
+
closeProjectSettings();
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ESC key
|
|
61
|
+
document.addEventListener("keydown", function (e) {
|
|
62
|
+
if (e.key === "Escape" && panelEl && !panelEl.classList.contains("hidden")) {
|
|
63
|
+
closeProjectSettings();
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Profile: rename
|
|
68
|
+
var renameBtn = document.getElementById("ps-rename-btn");
|
|
69
|
+
var renameForm = document.getElementById("ps-rename-form");
|
|
70
|
+
var renameInput = document.getElementById("ps-rename-input");
|
|
71
|
+
var renameSave = document.getElementById("ps-rename-save");
|
|
72
|
+
var renameCancel = document.getElementById("ps-rename-cancel");
|
|
73
|
+
|
|
74
|
+
if (renameBtn) {
|
|
75
|
+
renameBtn.addEventListener("click", function () {
|
|
76
|
+
renameForm.classList.remove("hidden");
|
|
77
|
+
renameInput.value = currentProject ? currentProject.name || "" : "";
|
|
78
|
+
renameBtn.classList.add("hidden");
|
|
79
|
+
renameInput.focus();
|
|
80
|
+
renameInput.select();
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (renameSave) {
|
|
84
|
+
renameSave.addEventListener("click", function () { commitRename(); });
|
|
85
|
+
}
|
|
86
|
+
if (renameCancel) {
|
|
87
|
+
renameCancel.addEventListener("click", function () { cancelRename(); });
|
|
88
|
+
}
|
|
89
|
+
if (renameInput) {
|
|
90
|
+
renameInput.addEventListener("keydown", function (e) {
|
|
91
|
+
e.stopPropagation();
|
|
92
|
+
if (e.key === "Enter") { e.preventDefault(); commitRename(); }
|
|
93
|
+
if (e.key === "Escape") { e.preventDefault(); cancelRename(); }
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Profile: icon
|
|
98
|
+
var iconBtn = document.getElementById("ps-icon-btn");
|
|
99
|
+
var iconRemoveBtn = document.getElementById("ps-icon-remove-btn");
|
|
100
|
+
if (iconBtn) {
|
|
101
|
+
iconBtn.addEventListener("click", function () {
|
|
102
|
+
showPsEmojiPicker();
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
if (iconRemoveBtn) {
|
|
106
|
+
iconRemoveBtn.addEventListener("click", function () {
|
|
107
|
+
if (ctx.ws && ctx.connected) {
|
|
108
|
+
ctx.ws.send(JSON.stringify({ type: "set_project_icon", slug: currentSlug, icon: null }));
|
|
109
|
+
}
|
|
110
|
+
updateIconPreview(null);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Instructions: save
|
|
115
|
+
var instrSave = document.getElementById("ps-instructions-save");
|
|
116
|
+
if (instrSave) {
|
|
117
|
+
instrSave.addEventListener("click", function () { saveInstructions(); });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Environment: add button
|
|
121
|
+
var envAddBtn = document.getElementById("ps-env-add-btn");
|
|
122
|
+
if (envAddBtn) {
|
|
123
|
+
envAddBtn.addEventListener("click", function () {
|
|
124
|
+
addEnvRow("", "", true);
|
|
125
|
+
autoSaveEnv();
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Environment: tab switching
|
|
130
|
+
var envTabs = panelEl.querySelectorAll(".ps-env-tab");
|
|
131
|
+
var envTabContents = panelEl.querySelectorAll(".ps-env-tab-content");
|
|
132
|
+
for (var ti = 0; ti < envTabs.length; ti++) {
|
|
133
|
+
envTabs[ti].addEventListener("click", function () {
|
|
134
|
+
var tab = this.dataset.tab;
|
|
135
|
+
for (var a = 0; a < envTabs.length; a++) {
|
|
136
|
+
envTabs[a].classList.toggle("active", envTabs[a].dataset.tab === tab);
|
|
137
|
+
}
|
|
138
|
+
for (var b = 0; b < envTabContents.length; b++) {
|
|
139
|
+
envTabContents[b].classList.toggle("active", envTabContents[b].dataset.tab === tab);
|
|
140
|
+
}
|
|
141
|
+
if (tab === "shared") loadSharedEnv();
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Environment: shared env add button
|
|
146
|
+
var sharedEnvAddBtn = document.getElementById("ps-shared-env-add-btn");
|
|
147
|
+
if (sharedEnvAddBtn) {
|
|
148
|
+
sharedEnvAddBtn.addEventListener("click", function () {
|
|
149
|
+
addSharedEnvRow("", "", true);
|
|
150
|
+
autoSaveSharedEnv();
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ===== Open / Close =====
|
|
156
|
+
export function openProjectSettings(slug, project) {
|
|
157
|
+
if (!panelEl) return;
|
|
158
|
+
currentSlug = slug;
|
|
159
|
+
currentProject = project;
|
|
160
|
+
|
|
161
|
+
// Set nav title
|
|
162
|
+
var navTitle = document.getElementById("ps-nav-title");
|
|
163
|
+
if (navTitle) navTitle.textContent = project.name || slug;
|
|
164
|
+
|
|
165
|
+
// Reset to first section
|
|
166
|
+
switchSection("profile");
|
|
167
|
+
|
|
168
|
+
// Populate profile
|
|
169
|
+
populateProfile();
|
|
170
|
+
|
|
171
|
+
// Show panel
|
|
172
|
+
panelEl.classList.remove("hidden");
|
|
173
|
+
refreshIcons();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function closeProjectSettings() {
|
|
177
|
+
if (!panelEl) return;
|
|
178
|
+
panelEl.classList.add("hidden");
|
|
179
|
+
closePsEmojiPicker();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function isProjectSettingsOpen() {
|
|
183
|
+
return panelEl && !panelEl.classList.contains("hidden");
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ===== Section switching =====
|
|
187
|
+
function switchSection(name) {
|
|
188
|
+
for (var i = 0; i < navItems.length; i++) {
|
|
189
|
+
var active = navItems[i].dataset.section === name;
|
|
190
|
+
navItems[i].classList.toggle("active", active);
|
|
191
|
+
}
|
|
192
|
+
for (var j = 0; j < sections.length; j++) {
|
|
193
|
+
var active2 = sections[j].dataset.section === name;
|
|
194
|
+
sections[j].classList.toggle("active", active2);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Lazy-load section data
|
|
198
|
+
if (name === "defaults") populateDefaults();
|
|
199
|
+
if (name === "instructions") loadInstructions();
|
|
200
|
+
if (name === "environment") {
|
|
201
|
+
// Reset tabs to "project" tab
|
|
202
|
+
var envTabs = panelEl.querySelectorAll(".ps-env-tab");
|
|
203
|
+
var envTabContents = panelEl.querySelectorAll(".ps-env-tab-content");
|
|
204
|
+
for (var t = 0; t < envTabs.length; t++) {
|
|
205
|
+
envTabs[t].classList.toggle("active", envTabs[t].dataset.tab === "project");
|
|
206
|
+
}
|
|
207
|
+
for (var u = 0; u < envTabContents.length; u++) {
|
|
208
|
+
envTabContents[u].classList.toggle("active", envTabContents[u].dataset.tab === "project");
|
|
209
|
+
}
|
|
210
|
+
loadEnvironment();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ===== Profile =====
|
|
215
|
+
function populateProfile() {
|
|
216
|
+
var nameEl = document.getElementById("ps-project-name");
|
|
217
|
+
if (nameEl) nameEl.textContent = currentProject ? currentProject.name || "-" : "-";
|
|
218
|
+
|
|
219
|
+
// Reset rename form
|
|
220
|
+
var renameForm = document.getElementById("ps-rename-form");
|
|
221
|
+
var renameBtn = document.getElementById("ps-rename-btn");
|
|
222
|
+
if (renameForm) renameForm.classList.add("hidden");
|
|
223
|
+
if (renameBtn) renameBtn.classList.remove("hidden");
|
|
224
|
+
|
|
225
|
+
// Icon
|
|
226
|
+
updateIconPreview(currentProject ? currentProject.icon : null);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function commitRename() {
|
|
230
|
+
var renameInput = document.getElementById("ps-rename-input");
|
|
231
|
+
var nameEl = document.getElementById("ps-project-name");
|
|
232
|
+
var newName = renameInput ? renameInput.value.trim() : "";
|
|
233
|
+
if (newName && ctx.ws && ctx.connected) {
|
|
234
|
+
ctx.ws.send(JSON.stringify({ type: "set_project_title", slug: currentSlug, title: newName }));
|
|
235
|
+
if (nameEl) nameEl.textContent = newName;
|
|
236
|
+
if (currentProject) currentProject.name = newName;
|
|
237
|
+
var navTitle = document.getElementById("ps-nav-title");
|
|
238
|
+
if (navTitle) navTitle.textContent = newName;
|
|
239
|
+
}
|
|
240
|
+
cancelRename();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function cancelRename() {
|
|
244
|
+
var renameForm = document.getElementById("ps-rename-form");
|
|
245
|
+
var renameBtn = document.getElementById("ps-rename-btn");
|
|
246
|
+
if (renameForm) renameForm.classList.add("hidden");
|
|
247
|
+
if (renameBtn) renameBtn.classList.remove("hidden");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function updateIconPreview(icon) {
|
|
251
|
+
var preview = document.getElementById("ps-icon-preview");
|
|
252
|
+
var removeBtn = document.getElementById("ps-icon-remove-btn");
|
|
253
|
+
if (preview) {
|
|
254
|
+
preview.textContent = icon || "";
|
|
255
|
+
if (typeof twemoji !== "undefined" && icon) {
|
|
256
|
+
twemoji.parse(preview, { folder: "svg", ext: ".svg" });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (removeBtn) {
|
|
260
|
+
removeBtn.classList.toggle("hidden", !icon);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ===== Emoji picker (inline in settings) =====
|
|
265
|
+
var psEmojiPickerEl = null;
|
|
266
|
+
|
|
267
|
+
function closePsEmojiPicker() {
|
|
268
|
+
if (psEmojiPickerEl) {
|
|
269
|
+
psEmojiPickerEl.remove();
|
|
270
|
+
psEmojiPickerEl = null;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function showPsEmojiPicker() {
|
|
275
|
+
closePsEmojiPicker();
|
|
276
|
+
if (!EMOJI_CATEGORIES) return;
|
|
277
|
+
|
|
278
|
+
var anchor = document.getElementById("ps-emoji-picker-anchor");
|
|
279
|
+
if (!anchor) return;
|
|
280
|
+
|
|
281
|
+
var picker = document.createElement("div");
|
|
282
|
+
picker.className = "emoji-picker";
|
|
283
|
+
picker.style.position = "relative";
|
|
284
|
+
picker.style.left = "0";
|
|
285
|
+
picker.style.top = "0";
|
|
286
|
+
picker.style.marginTop = "8px";
|
|
287
|
+
picker.addEventListener("click", function (e) { e.stopPropagation(); });
|
|
288
|
+
|
|
289
|
+
// Header
|
|
290
|
+
var header = document.createElement("div");
|
|
291
|
+
header.className = "emoji-picker-header";
|
|
292
|
+
header.textContent = "Choose Icon";
|
|
293
|
+
picker.appendChild(header);
|
|
294
|
+
|
|
295
|
+
// Category tabs
|
|
296
|
+
var tabBar = document.createElement("div");
|
|
297
|
+
tabBar.className = "emoji-picker-tabs";
|
|
298
|
+
var tabBtns = [];
|
|
299
|
+
|
|
300
|
+
for (var t = 0; t < EMOJI_CATEGORIES.length; t++) {
|
|
301
|
+
(function (cat, idx) {
|
|
302
|
+
var tab = document.createElement("button");
|
|
303
|
+
tab.className = "emoji-picker-tab" + (idx === 0 ? " active" : "");
|
|
304
|
+
tab.textContent = cat.icon;
|
|
305
|
+
tab.title = cat.label;
|
|
306
|
+
tab.addEventListener("click", function (e) {
|
|
307
|
+
e.stopPropagation();
|
|
308
|
+
switchCategory(idx);
|
|
309
|
+
});
|
|
310
|
+
tabBar.appendChild(tab);
|
|
311
|
+
tabBtns.push(tab);
|
|
312
|
+
})(EMOJI_CATEGORIES[t], t);
|
|
313
|
+
}
|
|
314
|
+
picker.appendChild(tabBar);
|
|
315
|
+
|
|
316
|
+
// Grid
|
|
317
|
+
var scrollArea = document.createElement("div");
|
|
318
|
+
scrollArea.className = "emoji-picker-scroll";
|
|
319
|
+
var grid = document.createElement("div");
|
|
320
|
+
grid.className = "emoji-picker-grid";
|
|
321
|
+
scrollArea.appendChild(grid);
|
|
322
|
+
picker.appendChild(scrollArea);
|
|
323
|
+
|
|
324
|
+
function buildGrid(emojis) {
|
|
325
|
+
grid.innerHTML = "";
|
|
326
|
+
for (var i = 0; i < emojis.length; i++) {
|
|
327
|
+
(function (emoji) {
|
|
328
|
+
var btn = document.createElement("button");
|
|
329
|
+
btn.className = "emoji-picker-item";
|
|
330
|
+
btn.textContent = emoji;
|
|
331
|
+
btn.addEventListener("click", function (e) {
|
|
332
|
+
e.stopPropagation();
|
|
333
|
+
closePsEmojiPicker();
|
|
334
|
+
if (ctx.ws && ctx.connected) {
|
|
335
|
+
ctx.ws.send(JSON.stringify({ type: "set_project_icon", slug: currentSlug, icon: emoji }));
|
|
336
|
+
}
|
|
337
|
+
updateIconPreview(emoji);
|
|
338
|
+
});
|
|
339
|
+
grid.appendChild(btn);
|
|
340
|
+
})(emojis[i]);
|
|
341
|
+
}
|
|
342
|
+
if (typeof twemoji !== "undefined") {
|
|
343
|
+
twemoji.parse(grid, { folder: "svg", ext: ".svg" });
|
|
344
|
+
}
|
|
345
|
+
scrollArea.scrollTop = 0;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function switchCategory(idx) {
|
|
349
|
+
for (var j = 0; j < tabBtns.length; j++) {
|
|
350
|
+
tabBtns[j].classList.toggle("active", j === idx);
|
|
351
|
+
}
|
|
352
|
+
buildGrid(EMOJI_CATEGORIES[idx].emojis);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
buildGrid(EMOJI_CATEGORIES[0].emojis);
|
|
356
|
+
if (typeof twemoji !== "undefined") {
|
|
357
|
+
twemoji.parse(tabBar, { folder: "svg", ext: ".svg" });
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
anchor.innerHTML = "";
|
|
361
|
+
anchor.appendChild(picker);
|
|
362
|
+
psEmojiPickerEl = picker;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// ===== Defaults =====
|
|
366
|
+
function getModelDesc(model) {
|
|
367
|
+
if (!model) return "";
|
|
368
|
+
var lower = (model.value || model).toLowerCase();
|
|
369
|
+
for (var key in MODEL_DESCRIPTIONS) {
|
|
370
|
+
if (lower.indexOf(key) !== -1) return MODEL_DESCRIPTIONS[key];
|
|
371
|
+
}
|
|
372
|
+
return "";
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function isSonnetModel(model) {
|
|
376
|
+
if (!model) return false;
|
|
377
|
+
return model.toLowerCase().indexOf("sonnet") !== -1;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function populateDefaults() {
|
|
381
|
+
var models = ctx.currentModels || [];
|
|
382
|
+
var model = ctx.currentModel || "";
|
|
383
|
+
var mode = ctx.currentMode || "default";
|
|
384
|
+
var effort = ctx.currentEffort || "medium";
|
|
385
|
+
var betas = ctx.currentBetas || [];
|
|
386
|
+
|
|
387
|
+
// Model list
|
|
388
|
+
var modelList = document.getElementById("ps-model-list");
|
|
389
|
+
if (modelList) {
|
|
390
|
+
modelList.innerHTML = "";
|
|
391
|
+
for (var i = 0; i < models.length; i++) {
|
|
392
|
+
(function (m) {
|
|
393
|
+
var item = document.createElement("div");
|
|
394
|
+
item.className = "settings-model-item" + (m.value === model ? " active" : "");
|
|
395
|
+
|
|
396
|
+
var nameSpan = document.createElement("span");
|
|
397
|
+
nameSpan.className = "settings-model-name";
|
|
398
|
+
nameSpan.textContent = m.displayName || m.value;
|
|
399
|
+
item.appendChild(nameSpan);
|
|
400
|
+
|
|
401
|
+
var desc = getModelDesc(m.value);
|
|
402
|
+
if (desc) {
|
|
403
|
+
var descSpan = document.createElement("span");
|
|
404
|
+
descSpan.className = "settings-model-desc";
|
|
405
|
+
descSpan.textContent = desc;
|
|
406
|
+
item.appendChild(descSpan);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
item.addEventListener("click", function () {
|
|
410
|
+
if (ctx.ws && ctx.connected) {
|
|
411
|
+
ctx.ws.send(JSON.stringify({ type: "set_project_default_model", model: m.value }));
|
|
412
|
+
}
|
|
413
|
+
var items = modelList.querySelectorAll(".settings-model-item");
|
|
414
|
+
for (var j = 0; j < items.length; j++) items[j].classList.remove("active");
|
|
415
|
+
item.classList.add("active");
|
|
416
|
+
// Show/hide beta card based on Sonnet
|
|
417
|
+
updateBetaCard("ps", m.value);
|
|
418
|
+
});
|
|
419
|
+
modelList.appendChild(item);
|
|
420
|
+
})(models[i]);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Beta 1M toggle
|
|
425
|
+
updateBetaCard("ps", model);
|
|
426
|
+
var beta1m = document.getElementById("ps-beta-1m");
|
|
427
|
+
if (beta1m) {
|
|
428
|
+
var hasBeta = false;
|
|
429
|
+
for (var bi = 0; bi < betas.length; bi++) {
|
|
430
|
+
if (betas[bi].indexOf("context-1m") !== -1) { hasBeta = true; break; }
|
|
431
|
+
}
|
|
432
|
+
beta1m.checked = hasBeta;
|
|
433
|
+
beta1m.onchange = function () {
|
|
434
|
+
toggleBeta1m(this.checked);
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Mode list
|
|
439
|
+
var modeList = document.getElementById("ps-mode-list");
|
|
440
|
+
if (modeList) {
|
|
441
|
+
modeList.innerHTML = "";
|
|
442
|
+
for (var k = 0; k < MODE_OPTIONS.length; k++) {
|
|
443
|
+
(function (opt) {
|
|
444
|
+
var item = document.createElement("div");
|
|
445
|
+
item.className = "settings-model-item" + (opt.value === mode ? " active" : "");
|
|
446
|
+
|
|
447
|
+
var nameSpan = document.createElement("span");
|
|
448
|
+
nameSpan.className = "settings-model-name";
|
|
449
|
+
nameSpan.textContent = opt.label;
|
|
450
|
+
item.appendChild(nameSpan);
|
|
451
|
+
|
|
452
|
+
var descSpan = document.createElement("span");
|
|
453
|
+
descSpan.className = "settings-model-desc";
|
|
454
|
+
descSpan.textContent = opt.desc;
|
|
455
|
+
item.appendChild(descSpan);
|
|
456
|
+
|
|
457
|
+
item.addEventListener("click", function () {
|
|
458
|
+
if (ctx.ws && ctx.connected) {
|
|
459
|
+
ctx.ws.send(JSON.stringify({ type: "set_project_default_mode", mode: opt.value }));
|
|
460
|
+
}
|
|
461
|
+
var items = modeList.querySelectorAll(".settings-model-item");
|
|
462
|
+
for (var j = 0; j < items.length; j++) items[j].classList.remove("active");
|
|
463
|
+
item.classList.add("active");
|
|
464
|
+
});
|
|
465
|
+
modeList.appendChild(item);
|
|
466
|
+
})(MODE_OPTIONS[k]);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Effort bar
|
|
471
|
+
var effortBar = document.getElementById("ps-effort-bar");
|
|
472
|
+
if (effortBar) {
|
|
473
|
+
effortBar.innerHTML = "";
|
|
474
|
+
for (var e = 0; e < EFFORT_LEVELS.length; e++) {
|
|
475
|
+
(function (lvl) {
|
|
476
|
+
var btn = document.createElement("button");
|
|
477
|
+
btn.className = "settings-btn-option" + (lvl.value === effort ? " active" : "");
|
|
478
|
+
btn.textContent = lvl.value.charAt(0).toUpperCase() + lvl.value.slice(1);
|
|
479
|
+
btn.title = lvl.desc;
|
|
480
|
+
btn.addEventListener("click", function () {
|
|
481
|
+
if (ctx.ws && ctx.connected) {
|
|
482
|
+
ctx.ws.send(JSON.stringify({ type: "set_project_default_effort", effort: lvl.value }));
|
|
483
|
+
}
|
|
484
|
+
var btns = effortBar.querySelectorAll(".settings-btn-option");
|
|
485
|
+
for (var j = 0; j < btns.length; j++) btns[j].classList.remove("active");
|
|
486
|
+
btn.classList.add("active");
|
|
487
|
+
});
|
|
488
|
+
effortBar.appendChild(btn);
|
|
489
|
+
})(EFFORT_LEVELS[e]);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function updateBetaCard(prefix, model) {
|
|
495
|
+
var card = document.getElementById(prefix + "-beta-card");
|
|
496
|
+
if (card) {
|
|
497
|
+
card.style.display = isSonnetModel(model) ? "" : "none";
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function toggleBeta1m(enable) {
|
|
502
|
+
var betas = ctx.currentBetas || [];
|
|
503
|
+
var newBetas;
|
|
504
|
+
if (enable) {
|
|
505
|
+
newBetas = betas.slice();
|
|
506
|
+
newBetas.push("context-1m-2025-08-07");
|
|
507
|
+
} else {
|
|
508
|
+
newBetas = [];
|
|
509
|
+
for (var i = 0; i < betas.length; i++) {
|
|
510
|
+
if (betas[i].indexOf("context-1m") === -1) {
|
|
511
|
+
newBetas.push(betas[i]);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
if (ctx.ws && ctx.connected) {
|
|
516
|
+
ctx.ws.send(JSON.stringify({ type: "set_betas", betas: newBetas }));
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// ===== Instructions (CLAUDE.md) =====
|
|
521
|
+
function loadInstructions() {
|
|
522
|
+
var editor = document.getElementById("ps-instructions-editor");
|
|
523
|
+
var status = document.getElementById("ps-instructions-status");
|
|
524
|
+
var saveStatus = document.getElementById("ps-instructions-save-status");
|
|
525
|
+
if (saveStatus) saveStatus.textContent = "";
|
|
526
|
+
|
|
527
|
+
if (status) status.textContent = "Loading...";
|
|
528
|
+
|
|
529
|
+
if (ctx.ws && ctx.connected) {
|
|
530
|
+
ctx.ws.send(JSON.stringify({ type: "fs_read", path: "CLAUDE.md" }));
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
export function handleInstructionsRead(msg) {
|
|
535
|
+
var editor = document.getElementById("ps-instructions-editor");
|
|
536
|
+
var status = document.getElementById("ps-instructions-status");
|
|
537
|
+
if (!editor) return;
|
|
538
|
+
|
|
539
|
+
if (msg.error) {
|
|
540
|
+
editor.value = "";
|
|
541
|
+
if (status) status.textContent = "No CLAUDE.md file found. Save to create one.";
|
|
542
|
+
} else {
|
|
543
|
+
editor.value = msg.content || "";
|
|
544
|
+
if (status) status.textContent = "";
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function saveInstructions() {
|
|
549
|
+
var editor = document.getElementById("ps-instructions-editor");
|
|
550
|
+
var saveStatus = document.getElementById("ps-instructions-save-status");
|
|
551
|
+
if (!editor) return;
|
|
552
|
+
|
|
553
|
+
if (ctx.ws && ctx.connected) {
|
|
554
|
+
ctx.ws.send(JSON.stringify({ type: "fs_write", path: "CLAUDE.md", content: editor.value }));
|
|
555
|
+
if (saveStatus) saveStatus.textContent = "Saving...";
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
export function handleInstructionsWrite(msg) {
|
|
560
|
+
var saveStatus = document.getElementById("ps-instructions-save-status");
|
|
561
|
+
if (!saveStatus) return;
|
|
562
|
+
if (msg.ok) {
|
|
563
|
+
saveStatus.textContent = "Saved";
|
|
564
|
+
setTimeout(function () { saveStatus.textContent = ""; }, 2000);
|
|
565
|
+
} else {
|
|
566
|
+
saveStatus.textContent = "Error: " + (msg.error || "Failed to save");
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// ===== Environment (key-value list) =====
|
|
571
|
+
var envSaveTimer = null;
|
|
572
|
+
|
|
573
|
+
function loadEnvironment() {
|
|
574
|
+
var saveStatus = document.getElementById("ps-env-save-status");
|
|
575
|
+
if (saveStatus) saveStatus.textContent = "";
|
|
576
|
+
|
|
577
|
+
if (ctx.ws && ctx.connected) {
|
|
578
|
+
ctx.ws.send(JSON.stringify({ type: "get_project_env", slug: currentSlug }));
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
export function handleProjectEnv(msg) {
|
|
583
|
+
var notice = document.getElementById("ps-env-override-notice");
|
|
584
|
+
if (notice) notice.classList.toggle("hidden", !msg.hasEnvrc);
|
|
585
|
+
|
|
586
|
+
// Parse envrc string into key-value pairs
|
|
587
|
+
var list = document.getElementById("ps-env-list");
|
|
588
|
+
if (!list) return;
|
|
589
|
+
list.innerHTML = "";
|
|
590
|
+
|
|
591
|
+
var pairs = parseEnvString(msg.envrc || "");
|
|
592
|
+
for (var i = 0; i < pairs.length; i++) {
|
|
593
|
+
addEnvRow(pairs[i].key, pairs[i].value, false);
|
|
594
|
+
}
|
|
595
|
+
refreshIcons();
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// Check if text looks like env format: first line starts with a valid VAR_NAME=
|
|
599
|
+
export function looksLikeEnv(text) {
|
|
600
|
+
var first = text.split("\n")[0].trim();
|
|
601
|
+
if (first.indexOf("export ") === 0) first = first.substring(7);
|
|
602
|
+
return /^[A-Za-z_][A-Za-z0-9_]*=/.test(first);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
export function parseEnvString(str) {
|
|
606
|
+
var pairs = [];
|
|
607
|
+
if (!str) return pairs;
|
|
608
|
+
var lines = str.split("\n");
|
|
609
|
+
for (var i = 0; i < lines.length; i++) {
|
|
610
|
+
var line = lines[i].trim();
|
|
611
|
+
if (!line || line.charAt(0) === "#") continue;
|
|
612
|
+
// Strip leading "export "
|
|
613
|
+
if (line.indexOf("export ") === 0) line = line.substring(7);
|
|
614
|
+
var eq = line.indexOf("=");
|
|
615
|
+
if (eq === -1) continue;
|
|
616
|
+
var key = line.substring(0, eq).trim();
|
|
617
|
+
var val = line.substring(eq + 1).trim();
|
|
618
|
+
// Strip surrounding quotes
|
|
619
|
+
if ((val.charAt(0) === '"' && val.charAt(val.length - 1) === '"') ||
|
|
620
|
+
(val.charAt(0) === "'" && val.charAt(val.length - 1) === "'")) {
|
|
621
|
+
val = val.substring(1, val.length - 1);
|
|
622
|
+
}
|
|
623
|
+
if (key) pairs.push({ key: key, value: val });
|
|
624
|
+
}
|
|
625
|
+
return pairs;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
function buildEnvString() {
|
|
629
|
+
var list = document.getElementById("ps-env-list");
|
|
630
|
+
if (!list) return "";
|
|
631
|
+
var rows = list.querySelectorAll(".ps-env-row");
|
|
632
|
+
var lines = [];
|
|
633
|
+
for (var i = 0; i < rows.length; i++) {
|
|
634
|
+
var keyInput = rows[i].querySelector(".ps-env-key");
|
|
635
|
+
var valInput = rows[i].querySelector(".ps-env-val");
|
|
636
|
+
var key = keyInput ? keyInput.value.trim() : "";
|
|
637
|
+
var val = valInput ? valInput.value : "";
|
|
638
|
+
if (key) lines.push("export " + key + "=" + val);
|
|
639
|
+
}
|
|
640
|
+
return lines.join("\n");
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function addEnvRow(key, value, focus) {
|
|
644
|
+
var list = document.getElementById("ps-env-list");
|
|
645
|
+
if (!list) return;
|
|
646
|
+
|
|
647
|
+
var row = document.createElement("div");
|
|
648
|
+
row.className = "ps-env-row";
|
|
649
|
+
|
|
650
|
+
var keyInput = document.createElement("input");
|
|
651
|
+
keyInput.type = "text";
|
|
652
|
+
keyInput.className = "ps-env-key";
|
|
653
|
+
keyInput.placeholder = "KEY";
|
|
654
|
+
keyInput.value = key;
|
|
655
|
+
keyInput.spellcheck = false;
|
|
656
|
+
keyInput.autocomplete = "off";
|
|
657
|
+
|
|
658
|
+
var valInput = document.createElement("input");
|
|
659
|
+
valInput.type = "text";
|
|
660
|
+
valInput.className = "ps-env-val";
|
|
661
|
+
valInput.placeholder = "value";
|
|
662
|
+
valInput.value = value;
|
|
663
|
+
valInput.spellcheck = false;
|
|
664
|
+
valInput.autocomplete = "off";
|
|
665
|
+
|
|
666
|
+
var delBtn = document.createElement("button");
|
|
667
|
+
delBtn.className = "ps-env-del";
|
|
668
|
+
delBtn.title = "Remove";
|
|
669
|
+
delBtn.innerHTML = '<i data-lucide="x"></i>';
|
|
670
|
+
|
|
671
|
+
delBtn.addEventListener("click", function () {
|
|
672
|
+
row.remove();
|
|
673
|
+
autoSaveEnv();
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
// Auto-save on change
|
|
677
|
+
keyInput.addEventListener("input", function () { autoSaveEnv(); });
|
|
678
|
+
valInput.addEventListener("input", function () { autoSaveEnv(); });
|
|
679
|
+
|
|
680
|
+
// Paste detection: if pasting KEY=VALUE content into key field, parse it
|
|
681
|
+
keyInput.addEventListener("paste", function (e) {
|
|
682
|
+
var text = (e.clipboardData || window.clipboardData).getData("text");
|
|
683
|
+
if (text && looksLikeEnv(text)) {
|
|
684
|
+
e.preventDefault();
|
|
685
|
+
var pairs = parseEnvString(text);
|
|
686
|
+
if (pairs.length > 0) {
|
|
687
|
+
// Fill current row with first pair
|
|
688
|
+
keyInput.value = pairs[0].key;
|
|
689
|
+
valInput.value = pairs[0].value;
|
|
690
|
+
// Add remaining as new rows
|
|
691
|
+
for (var p = 1; p < pairs.length; p++) {
|
|
692
|
+
addEnvRow(pairs[p].key, pairs[p].value, false);
|
|
693
|
+
}
|
|
694
|
+
autoSaveEnv();
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
// Also handle paste into value field
|
|
700
|
+
valInput.addEventListener("paste", function (e) {
|
|
701
|
+
var text = (e.clipboardData || window.clipboardData).getData("text");
|
|
702
|
+
if (text && text.indexOf("\n") !== -1 && text.indexOf("=") !== -1) {
|
|
703
|
+
e.preventDefault();
|
|
704
|
+
var pairs = parseEnvString(text);
|
|
705
|
+
if (pairs.length > 0) {
|
|
706
|
+
keyInput.value = pairs[0].key;
|
|
707
|
+
valInput.value = pairs[0].value;
|
|
708
|
+
for (var p = 1; p < pairs.length; p++) {
|
|
709
|
+
addEnvRow(pairs[p].key, pairs[p].value, false);
|
|
710
|
+
}
|
|
711
|
+
autoSaveEnv();
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
row.appendChild(keyInput);
|
|
717
|
+
row.appendChild(valInput);
|
|
718
|
+
row.appendChild(delBtn);
|
|
719
|
+
list.appendChild(row);
|
|
720
|
+
refreshIcons();
|
|
721
|
+
|
|
722
|
+
if (focus) keyInput.focus();
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function autoSaveEnv() {
|
|
726
|
+
if (envSaveTimer) clearTimeout(envSaveTimer);
|
|
727
|
+
envSaveTimer = setTimeout(function () {
|
|
728
|
+
var envrc = buildEnvString();
|
|
729
|
+
if (ctx.ws && ctx.connected) {
|
|
730
|
+
ctx.ws.send(JSON.stringify({ type: "set_project_env", slug: currentSlug, envrc: envrc }));
|
|
731
|
+
var saveStatus = document.getElementById("ps-env-save-status");
|
|
732
|
+
if (saveStatus) {
|
|
733
|
+
saveStatus.textContent = "Saved";
|
|
734
|
+
setTimeout(function () { saveStatus.textContent = ""; }, 2000);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}, 800);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
export function handleProjectEnvSaved(msg) {
|
|
741
|
+
var saveStatus = document.getElementById("ps-env-save-status");
|
|
742
|
+
if (!saveStatus) return;
|
|
743
|
+
if (msg.ok) {
|
|
744
|
+
saveStatus.textContent = "Saved";
|
|
745
|
+
setTimeout(function () { saveStatus.textContent = ""; }, 2000);
|
|
746
|
+
} else {
|
|
747
|
+
saveStatus.textContent = "Error: " + (msg.error || "Failed to save");
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// ===== Shared Environment (via tabs) =====
|
|
752
|
+
var sharedEnvSaveTimer = null;
|
|
753
|
+
|
|
754
|
+
function loadSharedEnv() {
|
|
755
|
+
var saveStatus = document.getElementById("ps-shared-env-save-status");
|
|
756
|
+
if (saveStatus) saveStatus.textContent = "";
|
|
757
|
+
|
|
758
|
+
if (ctx.ws && ctx.connected) {
|
|
759
|
+
ctx.ws.send(JSON.stringify({ type: "get_shared_env" }));
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
export function handleProjectSharedEnv(msg) {
|
|
764
|
+
var list = document.getElementById("ps-shared-env-list");
|
|
765
|
+
if (!list) return;
|
|
766
|
+
list.innerHTML = "";
|
|
767
|
+
|
|
768
|
+
var pairs = parseEnvString(msg.envrc || "");
|
|
769
|
+
for (var i = 0; i < pairs.length; i++) {
|
|
770
|
+
addSharedEnvRow(pairs[i].key, pairs[i].value, false);
|
|
771
|
+
}
|
|
772
|
+
refreshIcons();
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
export function handleProjectSharedEnvSaved(msg) {
|
|
776
|
+
var saveStatus = document.getElementById("ps-shared-env-save-status");
|
|
777
|
+
if (!saveStatus) return;
|
|
778
|
+
if (msg.ok) {
|
|
779
|
+
saveStatus.textContent = "Saved";
|
|
780
|
+
setTimeout(function () { saveStatus.textContent = ""; }, 2000);
|
|
781
|
+
} else {
|
|
782
|
+
saveStatus.textContent = "Error: " + (msg.error || "Failed to save");
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
function buildSharedEnvString() {
|
|
787
|
+
var list = document.getElementById("ps-shared-env-list");
|
|
788
|
+
if (!list) return "";
|
|
789
|
+
var rows = list.querySelectorAll(".ps-env-row");
|
|
790
|
+
var lines = [];
|
|
791
|
+
for (var i = 0; i < rows.length; i++) {
|
|
792
|
+
var keyInput = rows[i].querySelector(".ps-env-key");
|
|
793
|
+
var valInput = rows[i].querySelector(".ps-env-val");
|
|
794
|
+
var key = keyInput ? keyInput.value.trim() : "";
|
|
795
|
+
var val = valInput ? valInput.value : "";
|
|
796
|
+
if (key) lines.push("export " + key + "=" + val);
|
|
797
|
+
}
|
|
798
|
+
return lines.join("\n");
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
function addSharedEnvRow(key, value, focus) {
|
|
802
|
+
var list = document.getElementById("ps-shared-env-list");
|
|
803
|
+
if (!list) return;
|
|
804
|
+
|
|
805
|
+
var row = document.createElement("div");
|
|
806
|
+
row.className = "ps-env-row";
|
|
807
|
+
|
|
808
|
+
var keyInput = document.createElement("input");
|
|
809
|
+
keyInput.type = "text";
|
|
810
|
+
keyInput.className = "ps-env-key";
|
|
811
|
+
keyInput.placeholder = "KEY";
|
|
812
|
+
keyInput.value = key;
|
|
813
|
+
keyInput.spellcheck = false;
|
|
814
|
+
keyInput.autocomplete = "off";
|
|
815
|
+
|
|
816
|
+
var valInput = document.createElement("input");
|
|
817
|
+
valInput.type = "text";
|
|
818
|
+
valInput.className = "ps-env-val";
|
|
819
|
+
valInput.placeholder = "value";
|
|
820
|
+
valInput.value = value;
|
|
821
|
+
valInput.spellcheck = false;
|
|
822
|
+
valInput.autocomplete = "off";
|
|
823
|
+
|
|
824
|
+
var delBtn = document.createElement("button");
|
|
825
|
+
delBtn.className = "ps-env-del";
|
|
826
|
+
delBtn.title = "Remove";
|
|
827
|
+
delBtn.innerHTML = '<i data-lucide="x"></i>';
|
|
828
|
+
|
|
829
|
+
delBtn.addEventListener("click", function () {
|
|
830
|
+
row.remove();
|
|
831
|
+
autoSaveSharedEnv();
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
keyInput.addEventListener("input", function () { autoSaveSharedEnv(); });
|
|
835
|
+
valInput.addEventListener("input", function () { autoSaveSharedEnv(); });
|
|
836
|
+
|
|
837
|
+
// Paste detection
|
|
838
|
+
keyInput.addEventListener("paste", function (e) {
|
|
839
|
+
var text = (e.clipboardData || window.clipboardData).getData("text");
|
|
840
|
+
if (text && looksLikeEnv(text)) {
|
|
841
|
+
e.preventDefault();
|
|
842
|
+
var pairs = parseEnvString(text);
|
|
843
|
+
if (pairs.length > 0) {
|
|
844
|
+
keyInput.value = pairs[0].key;
|
|
845
|
+
valInput.value = pairs[0].value;
|
|
846
|
+
for (var p = 1; p < pairs.length; p++) {
|
|
847
|
+
addSharedEnvRow(pairs[p].key, pairs[p].value, false);
|
|
848
|
+
}
|
|
849
|
+
autoSaveSharedEnv();
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
valInput.addEventListener("paste", function (e) {
|
|
855
|
+
var text = (e.clipboardData || window.clipboardData).getData("text");
|
|
856
|
+
if (text && text.indexOf("\n") !== -1 && text.indexOf("=") !== -1) {
|
|
857
|
+
e.preventDefault();
|
|
858
|
+
var pairs = parseEnvString(text);
|
|
859
|
+
if (pairs.length > 0) {
|
|
860
|
+
keyInput.value = pairs[0].key;
|
|
861
|
+
valInput.value = pairs[0].value;
|
|
862
|
+
for (var p = 1; p < pairs.length; p++) {
|
|
863
|
+
addSharedEnvRow(pairs[p].key, pairs[p].value, false);
|
|
864
|
+
}
|
|
865
|
+
autoSaveSharedEnv();
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
});
|
|
869
|
+
|
|
870
|
+
row.appendChild(keyInput);
|
|
871
|
+
row.appendChild(valInput);
|
|
872
|
+
row.appendChild(delBtn);
|
|
873
|
+
list.appendChild(row);
|
|
874
|
+
refreshIcons();
|
|
875
|
+
|
|
876
|
+
if (focus) keyInput.focus();
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
function autoSaveSharedEnv() {
|
|
880
|
+
if (sharedEnvSaveTimer) clearTimeout(sharedEnvSaveTimer);
|
|
881
|
+
sharedEnvSaveTimer = setTimeout(function () {
|
|
882
|
+
var envrc = buildSharedEnvString();
|
|
883
|
+
if (ctx.ws && ctx.connected) {
|
|
884
|
+
ctx.ws.send(JSON.stringify({ type: "set_shared_env", envrc: envrc }));
|
|
885
|
+
var saveStatus = document.getElementById("ps-shared-env-save-status");
|
|
886
|
+
if (saveStatus) {
|
|
887
|
+
saveStatus.textContent = "Saved";
|
|
888
|
+
setTimeout(function () { saveStatus.textContent = ""; }, 2000);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
}, 800);
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// ===== Update from external events =====
|
|
895
|
+
export function updateProjectSettingsIcon(icon) {
|
|
896
|
+
if (currentProject) currentProject.icon = icon;
|
|
897
|
+
updateIconPreview(icon);
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
export function updateProjectSettingsName(name) {
|
|
901
|
+
if (currentProject) currentProject.name = name;
|
|
902
|
+
var nameEl = document.getElementById("ps-project-name");
|
|
903
|
+
if (nameEl) nameEl.textContent = name || "-";
|
|
904
|
+
var navTitle = document.getElementById("ps-nav-title");
|
|
905
|
+
if (navTitle) navTitle.textContent = name || "-";
|
|
906
|
+
}
|