clay-server 2.5.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/LICENSE +21 -0
- package/README.md +281 -0
- package/bin/cli.js +2385 -0
- package/lib/cli-sessions.js +270 -0
- package/lib/config.js +237 -0
- package/lib/daemon.js +489 -0
- package/lib/ipc.js +112 -0
- package/lib/notes.js +120 -0
- package/lib/pages.js +664 -0
- package/lib/project.js +1433 -0
- package/lib/public/app.js +2795 -0
- package/lib/public/apple-touch-icon-dark.png +0 -0
- package/lib/public/apple-touch-icon.png +0 -0
- package/lib/public/css/base.css +264 -0
- package/lib/public/css/diff.css +128 -0
- package/lib/public/css/filebrowser.css +1114 -0
- package/lib/public/css/highlight.css +144 -0
- package/lib/public/css/icon-strip.css +296 -0
- package/lib/public/css/input.css +573 -0
- package/lib/public/css/menus.css +856 -0
- package/lib/public/css/messages.css +1445 -0
- package/lib/public/css/mobile-nav.css +354 -0
- package/lib/public/css/overlays.css +697 -0
- package/lib/public/css/rewind.css +505 -0
- package/lib/public/css/server-settings.css +761 -0
- package/lib/public/css/sidebar.css +936 -0
- package/lib/public/css/sticky-notes.css +358 -0
- package/lib/public/css/title-bar.css +314 -0
- package/lib/public/favicon-dark.svg +1 -0
- package/lib/public/favicon.svg +1 -0
- package/lib/public/icon-192-dark.png +0 -0
- package/lib/public/icon-192.png +0 -0
- package/lib/public/icon-512-dark.png +0 -0
- package/lib/public/icon-512.png +0 -0
- package/lib/public/icon-mono.svg +1 -0
- package/lib/public/index.html +762 -0
- package/lib/public/manifest.json +27 -0
- package/lib/public/modules/diff.js +398 -0
- package/lib/public/modules/events.js +21 -0
- package/lib/public/modules/filebrowser.js +1411 -0
- package/lib/public/modules/fileicons.js +172 -0
- package/lib/public/modules/icons.js +54 -0
- package/lib/public/modules/input.js +584 -0
- package/lib/public/modules/markdown.js +356 -0
- package/lib/public/modules/notifications.js +649 -0
- package/lib/public/modules/qrcode.js +70 -0
- package/lib/public/modules/rewind.js +345 -0
- package/lib/public/modules/server-settings.js +510 -0
- package/lib/public/modules/sidebar.js +1083 -0
- package/lib/public/modules/state.js +3 -0
- package/lib/public/modules/sticky-notes.js +688 -0
- package/lib/public/modules/terminal.js +697 -0
- package/lib/public/modules/theme.js +738 -0
- package/lib/public/modules/tools.js +1608 -0
- package/lib/public/modules/utils.js +56 -0
- package/lib/public/style.css +15 -0
- package/lib/public/sw.js +75 -0
- package/lib/push.js +124 -0
- package/lib/sdk-bridge.js +989 -0
- package/lib/server.js +582 -0
- package/lib/sessions.js +424 -0
- package/lib/terminal-manager.js +187 -0
- package/lib/terminal.js +24 -0
- package/lib/themes/ayu-light.json +9 -0
- package/lib/themes/catppuccin-latte.json +9 -0
- package/lib/themes/catppuccin-mocha.json +9 -0
- package/lib/themes/clay-light.json +10 -0
- package/lib/themes/clay.json +10 -0
- package/lib/themes/dracula.json +9 -0
- package/lib/themes/everforest-light.json +9 -0
- package/lib/themes/everforest.json +9 -0
- package/lib/themes/github-light.json +9 -0
- package/lib/themes/gruvbox-dark.json +9 -0
- package/lib/themes/gruvbox-light.json +9 -0
- package/lib/themes/monokai.json +9 -0
- package/lib/themes/nord-light.json +9 -0
- package/lib/themes/nord.json +9 -0
- package/lib/themes/one-dark.json +9 -0
- package/lib/themes/one-light.json +9 -0
- package/lib/themes/rose-pine-dawn.json +9 -0
- package/lib/themes/rose-pine.json +9 -0
- package/lib/themes/solarized-dark.json +9 -0
- package/lib/themes/solarized-light.json +9 -0
- package/lib/themes/tokyo-night-light.json +9 -0
- package/lib/themes/tokyo-night.json +9 -0
- package/lib/updater.js +97 -0
- package/package.json +47 -0
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
// server-settings.js — Full-screen server settings overlay
|
|
2
|
+
import { refreshIcons } from './icons.js';
|
|
3
|
+
import { getCurrentTheme, openSettingsThemePicker } from './theme.js';
|
|
4
|
+
import { showToast } from './utils.js';
|
|
5
|
+
|
|
6
|
+
var ctx = null;
|
|
7
|
+
var settingsEl = null;
|
|
8
|
+
var settingsBtn = null;
|
|
9
|
+
var closeBtn = null;
|
|
10
|
+
var navItems = null;
|
|
11
|
+
var sections = null;
|
|
12
|
+
var statsTimer = null;
|
|
13
|
+
|
|
14
|
+
export function initServerSettings(appCtx) {
|
|
15
|
+
ctx = appCtx;
|
|
16
|
+
settingsEl = document.getElementById("server-settings");
|
|
17
|
+
settingsBtn = document.getElementById("server-settings-btn");
|
|
18
|
+
closeBtn = document.getElementById("server-settings-close");
|
|
19
|
+
|
|
20
|
+
if (!settingsEl || !settingsBtn) return;
|
|
21
|
+
|
|
22
|
+
navItems = settingsEl.querySelectorAll(".settings-nav-item");
|
|
23
|
+
sections = settingsEl.querySelectorAll(".server-settings-section");
|
|
24
|
+
|
|
25
|
+
// Open settings
|
|
26
|
+
settingsBtn.addEventListener("click", function () {
|
|
27
|
+
openSettings();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Close settings
|
|
31
|
+
closeBtn.addEventListener("click", function () {
|
|
32
|
+
closeSettings();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// ESC to close
|
|
36
|
+
document.addEventListener("keydown", function (e) {
|
|
37
|
+
if (e.key === "Escape" && !settingsEl.classList.contains("hidden")) {
|
|
38
|
+
closeSettings();
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Nav item clicks
|
|
43
|
+
for (var i = 0; i < navItems.length; i++) {
|
|
44
|
+
navItems[i].addEventListener("click", function () {
|
|
45
|
+
var section = this.dataset.section;
|
|
46
|
+
switchSection(section);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Context view buttons
|
|
51
|
+
var contextViewEl = document.getElementById("settings-context-view");
|
|
52
|
+
if (contextViewEl) {
|
|
53
|
+
var btns = contextViewEl.querySelectorAll(".settings-btn-option");
|
|
54
|
+
for (var b = 0; b < btns.length; b++) {
|
|
55
|
+
btns[b].addEventListener("click", function () {
|
|
56
|
+
var view = this.dataset.view;
|
|
57
|
+
if (ctx.setContextView) ctx.setContextView(view);
|
|
58
|
+
if (ctx.applyContextView) ctx.applyContextView(view);
|
|
59
|
+
updateContextViewButtons();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Notification toggles
|
|
65
|
+
var notifAlert = document.getElementById("settings-notif-alert");
|
|
66
|
+
var notifSound = document.getElementById("settings-notif-sound");
|
|
67
|
+
var notifPush = document.getElementById("settings-notif-push");
|
|
68
|
+
|
|
69
|
+
if (notifAlert) {
|
|
70
|
+
notifAlert.addEventListener("change", function () {
|
|
71
|
+
var src = document.getElementById("notif-toggle-alert");
|
|
72
|
+
if (src) {
|
|
73
|
+
src.checked = this.checked;
|
|
74
|
+
src.dispatchEvent(new Event("change", { bubbles: true }));
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (notifSound) {
|
|
80
|
+
notifSound.addEventListener("change", function () {
|
|
81
|
+
var src = document.getElementById("notif-toggle-sound");
|
|
82
|
+
if (src) {
|
|
83
|
+
src.checked = this.checked;
|
|
84
|
+
src.dispatchEvent(new Event("change", { bubbles: true }));
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (notifPush) {
|
|
90
|
+
notifPush.addEventListener("change", function () {
|
|
91
|
+
var src = document.getElementById("notif-toggle-push");
|
|
92
|
+
if (src) {
|
|
93
|
+
src.checked = this.checked;
|
|
94
|
+
src.dispatchEvent(new Event("change", { bubbles: true }));
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Model item click
|
|
100
|
+
settingsEl.addEventListener("click", function (e) {
|
|
101
|
+
var modelItem = e.target.closest(".settings-model-item");
|
|
102
|
+
if (!modelItem) return;
|
|
103
|
+
var model = modelItem.dataset.model;
|
|
104
|
+
if (!model) return;
|
|
105
|
+
var ws = ctx.ws;
|
|
106
|
+
if (ws && ws.readyState === 1) {
|
|
107
|
+
ws.send(JSON.stringify({ type: "set_model", model: model }));
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// PIN buttons
|
|
112
|
+
var pinSetBtn = document.getElementById("settings-pin-set-btn");
|
|
113
|
+
var pinRemoveBtn = document.getElementById("settings-pin-remove-btn");
|
|
114
|
+
var pinSaveBtn = document.getElementById("settings-pin-save-btn");
|
|
115
|
+
var pinCancelBtn = document.getElementById("settings-pin-cancel-btn");
|
|
116
|
+
var pinInput = document.getElementById("settings-pin-input");
|
|
117
|
+
|
|
118
|
+
if (pinSetBtn) pinSetBtn.addEventListener("click", function () { showPinForm(); });
|
|
119
|
+
if (pinRemoveBtn) pinRemoveBtn.addEventListener("click", function () {
|
|
120
|
+
var ws = ctx.ws;
|
|
121
|
+
if (ws && ws.readyState === 1) {
|
|
122
|
+
ws.send(JSON.stringify({ type: "set_pin", pin: null }));
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
if (pinSaveBtn) pinSaveBtn.addEventListener("click", function () { submitPin(); });
|
|
126
|
+
if (pinCancelBtn) pinCancelBtn.addEventListener("click", function () { hidePinForm(); });
|
|
127
|
+
if (pinInput) pinInput.addEventListener("keydown", function (e) {
|
|
128
|
+
if (e.key === "Enter") { e.preventDefault(); submitPin(); }
|
|
129
|
+
if (e.key === "Escape") { e.preventDefault(); hidePinForm(); }
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Keep awake toggle
|
|
133
|
+
var keepAwakeToggle = document.getElementById("settings-keep-awake");
|
|
134
|
+
if (keepAwakeToggle) {
|
|
135
|
+
keepAwakeToggle.addEventListener("change", function () {
|
|
136
|
+
var ws = ctx.ws;
|
|
137
|
+
if (ws && ws.readyState === 1) {
|
|
138
|
+
ws.send(JSON.stringify({ type: "set_keep_awake", value: this.checked }));
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Shutdown server
|
|
144
|
+
var shutdownInput = document.getElementById("settings-shutdown-input");
|
|
145
|
+
var shutdownBtn = document.getElementById("settings-shutdown-btn");
|
|
146
|
+
|
|
147
|
+
if (shutdownInput && shutdownBtn) {
|
|
148
|
+
shutdownInput.addEventListener("input", function () {
|
|
149
|
+
var val = this.value.trim().toLowerCase();
|
|
150
|
+
shutdownBtn.disabled = val !== "shutdown";
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
shutdownInput.addEventListener("keydown", function (e) {
|
|
154
|
+
if (e.key === "Enter") {
|
|
155
|
+
e.preventDefault();
|
|
156
|
+
if (!shutdownBtn.disabled) shutdownBtn.click();
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
shutdownBtn.addEventListener("click", function () {
|
|
161
|
+
var val = shutdownInput.value.trim().toLowerCase();
|
|
162
|
+
if (val !== "shutdown") return;
|
|
163
|
+
var ws = ctx.ws;
|
|
164
|
+
if (ws && ws.readyState === 1) {
|
|
165
|
+
shutdownBtn.disabled = true;
|
|
166
|
+
shutdownBtn.textContent = "Shutting down...";
|
|
167
|
+
shutdownInput.disabled = true;
|
|
168
|
+
ws.send(JSON.stringify({ type: "shutdown_server" }));
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function switchSection(sectionName) {
|
|
175
|
+
for (var i = 0; i < navItems.length; i++) {
|
|
176
|
+
var isActive = navItems[i].dataset.section === sectionName;
|
|
177
|
+
navItems[i].classList.toggle("active", isActive);
|
|
178
|
+
// On mobile, scroll the active tab into view
|
|
179
|
+
if (isActive) {
|
|
180
|
+
navItems[i].scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
for (var j = 0; j < sections.length; j++) {
|
|
184
|
+
var isActive2 = sections[j].dataset.section === sectionName;
|
|
185
|
+
sections[j].classList.toggle("active", isActive2);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function openSettings() {
|
|
190
|
+
settingsEl.classList.remove("hidden");
|
|
191
|
+
settingsBtn.classList.add("active");
|
|
192
|
+
refreshIcons(settingsEl);
|
|
193
|
+
populateSettings();
|
|
194
|
+
requestDaemonConfig();
|
|
195
|
+
resetShutdownForm();
|
|
196
|
+
|
|
197
|
+
// Start periodic stats refresh
|
|
198
|
+
requestStats();
|
|
199
|
+
statsTimer = setInterval(requestStats, 5000);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function resetShutdownForm() {
|
|
203
|
+
var input = document.getElementById("settings-shutdown-input");
|
|
204
|
+
var btn = document.getElementById("settings-shutdown-btn");
|
|
205
|
+
var errorEl = document.getElementById("settings-shutdown-error");
|
|
206
|
+
if (input) { input.value = ""; input.disabled = false; }
|
|
207
|
+
if (btn) { btn.disabled = true; btn.textContent = "Shutdown"; }
|
|
208
|
+
if (errorEl) errorEl.classList.add("hidden");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function closeSettings() {
|
|
212
|
+
settingsEl.classList.add("hidden");
|
|
213
|
+
settingsBtn.classList.remove("active");
|
|
214
|
+
if (statsTimer) {
|
|
215
|
+
clearInterval(statsTimer);
|
|
216
|
+
statsTimer = null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function isSettingsOpen() {
|
|
221
|
+
return settingsEl && !settingsEl.classList.contains("hidden");
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function requestStats() {
|
|
225
|
+
var ws = ctx.ws;
|
|
226
|
+
if (ws && ws.readyState === 1) {
|
|
227
|
+
ws.send(JSON.stringify({ type: "process_stats" }));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function populateSettings() {
|
|
232
|
+
// Server name
|
|
233
|
+
var nameEl = document.getElementById("settings-server-name");
|
|
234
|
+
var projNameEl = document.getElementById("settings-project-name");
|
|
235
|
+
var cwdEl = document.getElementById("settings-project-cwd");
|
|
236
|
+
var versionEl = document.getElementById("settings-server-version");
|
|
237
|
+
var slugEl = document.getElementById("settings-project-slug");
|
|
238
|
+
var wsPathEl = document.getElementById("settings-ws-path");
|
|
239
|
+
var skipPermsEl = document.getElementById("settings-skip-perms");
|
|
240
|
+
|
|
241
|
+
var projectName = ctx.projectName || "-";
|
|
242
|
+
if (nameEl) nameEl.textContent = projectName;
|
|
243
|
+
if (projNameEl) projNameEl.textContent = projectName;
|
|
244
|
+
if (cwdEl) cwdEl.textContent = ctx.projectName || "-";
|
|
245
|
+
|
|
246
|
+
var footerVersion = document.getElementById("footer-version");
|
|
247
|
+
if (versionEl && footerVersion) {
|
|
248
|
+
versionEl.textContent = footerVersion.textContent || "-";
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (slugEl) slugEl.textContent = ctx.currentSlug || "(default)";
|
|
252
|
+
if (wsPathEl) wsPathEl.textContent = ctx.wsPath || "/ws";
|
|
253
|
+
|
|
254
|
+
// Skip permissions
|
|
255
|
+
var spBanner = document.getElementById("skip-perms-banner");
|
|
256
|
+
if (skipPermsEl) {
|
|
257
|
+
var isSkip = spBanner && !spBanner.classList.contains("hidden");
|
|
258
|
+
skipPermsEl.textContent = isSkip ? "Enabled" : "Disabled";
|
|
259
|
+
skipPermsEl.classList.toggle("settings-badge-on", isSkip);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Sync notification toggles
|
|
263
|
+
syncNotifToggles();
|
|
264
|
+
|
|
265
|
+
// Theme
|
|
266
|
+
updateThemeDisplay();
|
|
267
|
+
|
|
268
|
+
// Context view
|
|
269
|
+
updateContextViewButtons();
|
|
270
|
+
|
|
271
|
+
// Models
|
|
272
|
+
updateModelList();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function syncNotifToggles() {
|
|
276
|
+
var pairs = [
|
|
277
|
+
["notif-toggle-alert", "settings-notif-alert"],
|
|
278
|
+
["notif-toggle-sound", "settings-notif-sound"],
|
|
279
|
+
["notif-toggle-push", "settings-notif-push"],
|
|
280
|
+
];
|
|
281
|
+
for (var i = 0; i < pairs.length; i++) {
|
|
282
|
+
var src = document.getElementById(pairs[i][0]);
|
|
283
|
+
var dst = document.getElementById(pairs[i][1]);
|
|
284
|
+
if (src && dst) dst.checked = src.checked;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function updateThemeDisplay() {
|
|
289
|
+
var container = document.getElementById("settings-theme-picker-container");
|
|
290
|
+
if (container) {
|
|
291
|
+
openSettingsThemePicker(container);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function updateContextViewButtons() {
|
|
296
|
+
var view = "off";
|
|
297
|
+
try { view = localStorage.getItem("clay-context-view") || "off"; } catch (e) {}
|
|
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
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function updateModelList() {
|
|
305
|
+
var listEl = document.getElementById("settings-model-list");
|
|
306
|
+
var currentEl = document.getElementById("settings-current-model");
|
|
307
|
+
if (!listEl) return;
|
|
308
|
+
|
|
309
|
+
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 || "-";
|
|
321
|
+
|
|
322
|
+
listEl.innerHTML = "";
|
|
323
|
+
if (models.length === 0) {
|
|
324
|
+
listEl.innerHTML = '<div style="font-size:13px;color:var(--text-dimmer);">No models available</div>';
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
for (var i = 0; i < models.length; i++) {
|
|
329
|
+
var m = models[i];
|
|
330
|
+
var value = m.value || "";
|
|
331
|
+
var label = m.displayName || value;
|
|
332
|
+
var item = document.createElement("div");
|
|
333
|
+
item.className = "settings-model-item";
|
|
334
|
+
if (label === currentModel || value === currentModel) item.classList.add("active");
|
|
335
|
+
item.dataset.model = value;
|
|
336
|
+
item.textContent = label;
|
|
337
|
+
listEl.appendChild(item);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export function updateSettingsStats(data) {
|
|
342
|
+
if (!isSettingsOpen()) return;
|
|
343
|
+
var pid = document.getElementById("settings-status-pid");
|
|
344
|
+
var uptime = document.getElementById("settings-status-uptime");
|
|
345
|
+
var rss = document.getElementById("settings-status-rss");
|
|
346
|
+
var sessions = document.getElementById("settings-status-sessions");
|
|
347
|
+
var clients = document.getElementById("settings-status-clients");
|
|
348
|
+
|
|
349
|
+
if (pid) pid.textContent = String(data.pid);
|
|
350
|
+
if (uptime) uptime.textContent = formatUptime(data.uptime);
|
|
351
|
+
if (rss) rss.textContent = formatBytes(data.memory.rss);
|
|
352
|
+
if (sessions) sessions.textContent = String(data.sessions);
|
|
353
|
+
if (clients) clients.textContent = String(data.clients);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export function updateSettingsModels(current, models) {
|
|
357
|
+
if (!ctx) return;
|
|
358
|
+
ctx.currentModels = models;
|
|
359
|
+
ctx._currentModelValue = current;
|
|
360
|
+
if (isSettingsOpen()) {
|
|
361
|
+
updateModelList();
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// --- Daemon config ---
|
|
366
|
+
function requestDaemonConfig() {
|
|
367
|
+
var ws = ctx.ws;
|
|
368
|
+
if (ws && ws.readyState === 1) {
|
|
369
|
+
ws.send(JSON.stringify({ type: "get_daemon_config" }));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export function updateDaemonConfig(config) {
|
|
374
|
+
// Port
|
|
375
|
+
var portEl = document.getElementById("settings-port");
|
|
376
|
+
if (portEl) portEl.textContent = String(config.port || "-");
|
|
377
|
+
|
|
378
|
+
// TLS
|
|
379
|
+
var tlsEl = document.getElementById("settings-tls");
|
|
380
|
+
if (tlsEl) {
|
|
381
|
+
tlsEl.textContent = config.tls ? "Enabled" : "Disabled";
|
|
382
|
+
tlsEl.classList.toggle("settings-badge-green", !!config.tls);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Debug
|
|
386
|
+
var debugEl = document.getElementById("settings-debug");
|
|
387
|
+
if (debugEl) {
|
|
388
|
+
debugEl.textContent = config.debug ? "Enabled" : "Disabled";
|
|
389
|
+
debugEl.classList.toggle("settings-badge-on", !!config.debug);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// PIN status
|
|
393
|
+
updatePinStatus(!!config.pinEnabled);
|
|
394
|
+
|
|
395
|
+
// Keep awake
|
|
396
|
+
var keepAwakeToggle = document.getElementById("settings-keep-awake");
|
|
397
|
+
if (keepAwakeToggle) keepAwakeToggle.checked = !!config.keepAwake;
|
|
398
|
+
|
|
399
|
+
// Show keep awake card only on macOS
|
|
400
|
+
var keepAwakeCard = document.getElementById("settings-keep-awake-card");
|
|
401
|
+
if (keepAwakeCard) {
|
|
402
|
+
if (config.platform === "darwin") {
|
|
403
|
+
keepAwakeCard.classList.remove("hidden");
|
|
404
|
+
} else {
|
|
405
|
+
keepAwakeCard.classList.add("hidden");
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export function handleSetPinResult(msg) {
|
|
411
|
+
if (msg.ok) {
|
|
412
|
+
updatePinStatus(!!msg.pinEnabled);
|
|
413
|
+
hidePinForm();
|
|
414
|
+
showToast(msg.pinEnabled ? "PIN set successfully" : "PIN removed");
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export function handleKeepAwakeChanged(msg) {
|
|
419
|
+
var keepAwakeToggle = document.getElementById("settings-keep-awake");
|
|
420
|
+
if (keepAwakeToggle) keepAwakeToggle.checked = !!msg.keepAwake;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
export function handleShutdownResult(msg) {
|
|
424
|
+
var shutdownInput = document.getElementById("settings-shutdown-input");
|
|
425
|
+
var shutdownBtn = document.getElementById("settings-shutdown-btn");
|
|
426
|
+
var errorEl = document.getElementById("settings-shutdown-error");
|
|
427
|
+
|
|
428
|
+
if (msg.ok) {
|
|
429
|
+
if (shutdownBtn) shutdownBtn.textContent = "Server stopped";
|
|
430
|
+
showToast("Server is shutting down...");
|
|
431
|
+
} else {
|
|
432
|
+
if (shutdownBtn) {
|
|
433
|
+
shutdownBtn.textContent = "Shutdown";
|
|
434
|
+
shutdownBtn.disabled = false;
|
|
435
|
+
}
|
|
436
|
+
if (shutdownInput) shutdownInput.disabled = false;
|
|
437
|
+
if (errorEl) {
|
|
438
|
+
errorEl.textContent = msg.error || "Shutdown failed";
|
|
439
|
+
errorEl.classList.remove("hidden");
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// --- PIN form management ---
|
|
445
|
+
function showPinForm() {
|
|
446
|
+
var form = document.getElementById("settings-pin-form");
|
|
447
|
+
var input = document.getElementById("settings-pin-input");
|
|
448
|
+
var errorEl = document.getElementById("settings-pin-error");
|
|
449
|
+
if (form) form.classList.remove("hidden");
|
|
450
|
+
if (errorEl) errorEl.classList.add("hidden");
|
|
451
|
+
if (input) { input.value = ""; input.focus(); }
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function hidePinForm() {
|
|
455
|
+
var form = document.getElementById("settings-pin-form");
|
|
456
|
+
var input = document.getElementById("settings-pin-input");
|
|
457
|
+
var errorEl = document.getElementById("settings-pin-error");
|
|
458
|
+
if (form) form.classList.add("hidden");
|
|
459
|
+
if (input) input.value = "";
|
|
460
|
+
if (errorEl) errorEl.classList.add("hidden");
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function submitPin() {
|
|
464
|
+
var input = document.getElementById("settings-pin-input");
|
|
465
|
+
var errorEl = document.getElementById("settings-pin-error");
|
|
466
|
+
if (!input) return;
|
|
467
|
+
var pin = input.value.trim();
|
|
468
|
+
if (!/^\d{6}$/.test(pin)) {
|
|
469
|
+
if (errorEl) errorEl.classList.remove("hidden");
|
|
470
|
+
input.focus();
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
if (errorEl) errorEl.classList.add("hidden");
|
|
474
|
+
var ws = ctx.ws;
|
|
475
|
+
if (ws && ws.readyState === 1) {
|
|
476
|
+
ws.send(JSON.stringify({ type: "set_pin", pin: pin }));
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function updatePinStatus(enabled) {
|
|
481
|
+
var statusEl = document.getElementById("settings-pin-status");
|
|
482
|
+
var setBtn = document.getElementById("settings-pin-set-btn");
|
|
483
|
+
var removeBtn = document.getElementById("settings-pin-remove-btn");
|
|
484
|
+
var actionLabel = document.getElementById("settings-pin-action-label");
|
|
485
|
+
|
|
486
|
+
if (statusEl) {
|
|
487
|
+
statusEl.textContent = enabled ? "Enabled" : "Disabled";
|
|
488
|
+
statusEl.classList.toggle("settings-badge-green", enabled);
|
|
489
|
+
}
|
|
490
|
+
if (setBtn) setBtn.textContent = enabled ? "Change PIN" : "Set PIN";
|
|
491
|
+
if (removeBtn) removeBtn.classList.toggle("hidden", !enabled);
|
|
492
|
+
if (actionLabel) actionLabel.textContent = enabled ? "Change PIN" : "Set PIN";
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function formatBytes(n) {
|
|
496
|
+
if (n >= 1073741824) return (n / 1073741824).toFixed(1) + " GB";
|
|
497
|
+
if (n >= 1048576) return (n / 1048576).toFixed(1) + " MB";
|
|
498
|
+
if (n >= 1024) return (n / 1024).toFixed(1) + " KB";
|
|
499
|
+
return n + " B";
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function formatUptime(seconds) {
|
|
503
|
+
var d = Math.floor(seconds / 86400);
|
|
504
|
+
var h = Math.floor((seconds % 86400) / 3600);
|
|
505
|
+
var m = Math.floor((seconds % 3600) / 60);
|
|
506
|
+
var s = Math.floor(seconds % 60);
|
|
507
|
+
if (d > 0) return d + "d " + h + "h " + m + "m";
|
|
508
|
+
if (h > 0) return h + "h " + m + "m " + s + "s";
|
|
509
|
+
return m + "m " + s + "s";
|
|
510
|
+
}
|