@firstpick/pi-package-webui 0.2.0 → 0.2.2
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 +80 -136
- package/bin/pi-webui.mjs +67 -1
- package/package.json +1 -1
- package/public/app.js +219 -29
- package/public/index.html +24 -4
- package/public/service-worker.js +1 -1
- package/public/styles.css +200 -7
- package/start-webui.ps1 +45 -0
- package/start-webui.sh +59 -49
- package/tests/mobile-static.test.mjs +34 -3
package/public/app.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
const $ = (selector) => document.querySelector(selector);
|
|
2
2
|
|
|
3
3
|
const elements = {
|
|
4
|
-
|
|
4
|
+
webuiVersionBadge: $("#webuiVersionBadge"),
|
|
5
|
+
webuiDevBadge: $("#webuiDevBadge"),
|
|
5
6
|
tabBar: $("#tabBar"),
|
|
6
7
|
terminalTabsToggleButton: $("#terminalTabsToggleButton"),
|
|
7
8
|
newTabButton: $("#newTabButton"),
|
|
8
9
|
closeAllTabsButton: $("#closeAllTabsButton"),
|
|
9
10
|
statusBar: $("#statusBar"),
|
|
10
11
|
serverOfflinePanel: $("#serverOfflinePanel"),
|
|
12
|
+
serverRestartPanel: $("#serverRestartPanel"),
|
|
13
|
+
serverRestartMessage: $("#serverRestartMessage"),
|
|
11
14
|
serverOfflineCommand: $("#serverOfflineCommand"),
|
|
12
15
|
serverOfflineSlashCommand: $("#serverOfflineSlashCommand"),
|
|
13
16
|
copyServerCommandButton: $("#copyServerCommandButton"),
|
|
@@ -60,7 +63,9 @@ const elements = {
|
|
|
60
63
|
backgroundStatus: $("#backgroundStatus"),
|
|
61
64
|
networkStatus: $("#networkStatus"),
|
|
62
65
|
openNetworkButton: $("#openNetworkButton"),
|
|
63
|
-
|
|
66
|
+
serverActionSelect: $("#serverActionSelect"),
|
|
67
|
+
runServerActionButton: $("#runServerActionButton"),
|
|
68
|
+
serverActionStatus: $("#serverActionStatus"),
|
|
64
69
|
agentDoneNotificationsToggle: $("#agentDoneNotificationsToggle"),
|
|
65
70
|
agentDoneNotificationsStatus: $("#agentDoneNotificationsStatus"),
|
|
66
71
|
optionalFeaturesBox: $("#optionalFeaturesBox"),
|
|
@@ -150,12 +155,15 @@ let pathSuggestAbortController = null;
|
|
|
150
155
|
let latestStats = null;
|
|
151
156
|
let latestWorkspace = null;
|
|
152
157
|
let latestNetwork = null;
|
|
158
|
+
let webuiVersion = "";
|
|
159
|
+
let webuiDevServer = false;
|
|
153
160
|
let latestCodexUsage = null;
|
|
154
161
|
let codexUsageError = null;
|
|
155
162
|
let codexUsageLoading = false;
|
|
156
163
|
let refreshCodexUsageTimer = null;
|
|
157
164
|
let codexUsageRenderTimer = null;
|
|
158
165
|
let backendOffline = false;
|
|
166
|
+
let serverRestartInProgress = false;
|
|
159
167
|
let backendOfflineNoticeShown = false;
|
|
160
168
|
let latestMessages = [];
|
|
161
169
|
let promptHistoryByTab = new Map();
|
|
@@ -263,6 +271,7 @@ const mobileViewMedia = window.matchMedia?.(MOBILE_VIEW_QUERY) || null;
|
|
|
263
271
|
const statusEntries = new Map();
|
|
264
272
|
const widgets = new Map();
|
|
265
273
|
const todoProgressWidgetExpandedByTab = new Map();
|
|
274
|
+
const releaseNpmOutputExpandedByTab = new Map();
|
|
266
275
|
const liveToolRuns = new Map();
|
|
267
276
|
const liveToolCards = new Map();
|
|
268
277
|
const liveToolRenderQueue = new Map();
|
|
@@ -899,13 +908,22 @@ function renderServerOfflinePanel() {
|
|
|
899
908
|
if (elements.serverOfflineSlashCommand) elements.serverOfflineSlashCommand.textContent = serverStartSlashCommandText();
|
|
900
909
|
}
|
|
901
910
|
|
|
911
|
+
function setServerRestartOverlay(active, message = "Waiting for the server to come back…") {
|
|
912
|
+
serverRestartInProgress = !!active;
|
|
913
|
+
document.body.classList.toggle("server-restarting", serverRestartInProgress);
|
|
914
|
+
if (elements.serverRestartPanel) elements.serverRestartPanel.hidden = !serverRestartInProgress;
|
|
915
|
+
if (elements.serverRestartMessage) elements.serverRestartMessage.textContent = message;
|
|
916
|
+
if (serverRestartInProgress && elements.serverOfflinePanel) elements.serverOfflinePanel.hidden = true;
|
|
917
|
+
}
|
|
918
|
+
|
|
902
919
|
function setBackendOffline(offline, error) {
|
|
903
920
|
backendOffline = !!offline;
|
|
904
|
-
|
|
905
|
-
|
|
921
|
+
const showOfflinePanel = backendOffline && !serverRestartInProgress;
|
|
922
|
+
document.body.classList.toggle("server-offline", showOfflinePanel);
|
|
923
|
+
if (elements.serverOfflinePanel) elements.serverOfflinePanel.hidden = !showOfflinePanel;
|
|
906
924
|
renderServerOfflinePanel();
|
|
907
925
|
if (backendOffline) {
|
|
908
|
-
if (!backendOfflineNoticeShown) {
|
|
926
|
+
if (!serverRestartInProgress && !backendOfflineNoticeShown) {
|
|
909
927
|
backendOfflineNoticeShown = true;
|
|
910
928
|
addEvent(`Pi Web UI server is offline${error?.message ? `: ${error.message}` : ""}`, "warn");
|
|
911
929
|
}
|
|
@@ -1015,6 +1033,52 @@ async function api(path, { method = "GET", body, tabId = activeTabId, scoped = t
|
|
|
1015
1033
|
return data;
|
|
1016
1034
|
}
|
|
1017
1035
|
|
|
1036
|
+
function formatWebuiVersion(version) {
|
|
1037
|
+
const text = String(version || "").trim();
|
|
1038
|
+
if (!text) return "";
|
|
1039
|
+
return text.startsWith("v") ? text : `v${text}`;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
function isWebuiDevMetadata(data) {
|
|
1043
|
+
return data?.webuiDev === true || String(data?.webuiMode || "").toLowerCase() === "dev";
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
function renderWebuiVersion() {
|
|
1047
|
+
const badge = elements.webuiVersionBadge;
|
|
1048
|
+
if (!badge) return;
|
|
1049
|
+
const label = formatWebuiVersion(webuiVersion);
|
|
1050
|
+
badge.hidden = !label;
|
|
1051
|
+
badge.textContent = label;
|
|
1052
|
+
if (label) badge.title = `Pi Web UI ${label}`;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
function renderWebuiDevBadge() {
|
|
1056
|
+
const badge = elements.webuiDevBadge;
|
|
1057
|
+
if (!badge) return;
|
|
1058
|
+
badge.hidden = !webuiDevServer;
|
|
1059
|
+
badge.title = "Pi Web UI dev server";
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
function setWebuiVersion(version) {
|
|
1063
|
+
const text = String(version || "").trim();
|
|
1064
|
+
if (text === webuiVersion) return;
|
|
1065
|
+
webuiVersion = text;
|
|
1066
|
+
renderWebuiVersion();
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
function setWebuiDevServer(dev) {
|
|
1070
|
+
const next = !!dev;
|
|
1071
|
+
if (next === webuiDevServer) return;
|
|
1072
|
+
webuiDevServer = next;
|
|
1073
|
+
renderWebuiDevBadge();
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
async function refreshWebuiVersion() {
|
|
1077
|
+
const health = await api("/api/health", { scoped: false });
|
|
1078
|
+
setWebuiVersion(health.webuiVersion);
|
|
1079
|
+
setWebuiDevServer(isWebuiDevMetadata(health));
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1018
1082
|
function formatBytes(bytes) {
|
|
1019
1083
|
const value = Number(bytes) || 0;
|
|
1020
1084
|
if (value < 1024) return `${value} B`;
|
|
@@ -2357,7 +2421,6 @@ function resetActiveTabUi() {
|
|
|
2357
2421
|
}
|
|
2358
2422
|
elements.commandsBox.textContent = "Loading…";
|
|
2359
2423
|
elements.commandsBox.classList.add("muted");
|
|
2360
|
-
elements.sessionLine.textContent = activeTab() ? "Connecting…" : "No terminal tabs.";
|
|
2361
2424
|
renderWidgets();
|
|
2362
2425
|
renderGitWorkflow();
|
|
2363
2426
|
renderFooter();
|
|
@@ -3899,13 +3962,6 @@ function renderStatus() {
|
|
|
3899
3962
|
updateComposerModeButtons();
|
|
3900
3963
|
const running = state?.isStreaming ? "running" : "idle";
|
|
3901
3964
|
const compacting = state?.isCompacting ? " · compacting" : "";
|
|
3902
|
-
const queue = state?.pendingMessageCount ? ` · queued ${state.pendingMessageCount}` : "";
|
|
3903
|
-
const extra = [...statusEntries.entries()].map(([key, value]) => formatStatusEntry(key, value)).filter(Boolean).join(" · ");
|
|
3904
|
-
const statusText = state?.isStreaming ? "Running" : "Idle";
|
|
3905
|
-
const compactingText = state?.isCompacting ? " · Compacting" : "";
|
|
3906
|
-
const queueText = state?.pendingMessageCount ? ` · Queue: ${state.pendingMessageCount}` : "";
|
|
3907
|
-
|
|
3908
|
-
elements.sessionLine.textContent = `Status: ${statusText}${compactingText}${queueText}${extra ? ` · ${extra}` : ""} · Model: ${modelLabel(state?.model)} · Session: ${shortSessionLabel(state)}`;
|
|
3909
3965
|
|
|
3910
3966
|
elements.stateDetails.replaceChildren();
|
|
3911
3967
|
const details = {
|
|
@@ -4181,6 +4237,26 @@ function releaseNpmStreamHeader(label, lineCount, { live = false } = {}) {
|
|
|
4181
4237
|
return header;
|
|
4182
4238
|
}
|
|
4183
4239
|
|
|
4240
|
+
function renderReleaseNpmOutputDetails(key, streamHeader, terminal, controls = null) {
|
|
4241
|
+
const tabId = activeTabId || "default";
|
|
4242
|
+
const stateKey = `${tabId}:${key}`;
|
|
4243
|
+
const node = make("details", "release-npm-output-details");
|
|
4244
|
+
node.open = releaseNpmOutputExpandedByTab.get(stateKey) !== false;
|
|
4245
|
+
node.addEventListener("toggle", () => {
|
|
4246
|
+
releaseNpmOutputExpandedByTab.set(stateKey, node.open);
|
|
4247
|
+
if (node.open) requestAnimationFrame(() => { terminal.scrollTop = terminal.scrollHeight; });
|
|
4248
|
+
});
|
|
4249
|
+
|
|
4250
|
+
const summary = make("summary", "release-npm-output-summary");
|
|
4251
|
+
summary.title = "Expand or collapse command output in the Web UI";
|
|
4252
|
+
const toggle = make("span", "release-npm-output-toggle", "›");
|
|
4253
|
+
toggle.setAttribute("aria-hidden", "true");
|
|
4254
|
+
summary.append(toggle, streamHeader);
|
|
4255
|
+
node.append(summary, terminal);
|
|
4256
|
+
if (controls) node.append(controls);
|
|
4257
|
+
return node;
|
|
4258
|
+
}
|
|
4259
|
+
|
|
4184
4260
|
function renderReleaseNpmOutputWidget() {
|
|
4185
4261
|
if (!isOptionalFeatureEnabled("releaseNpm")) return null;
|
|
4186
4262
|
const outputLines = getWidgetLines("release-npm:output");
|
|
@@ -4216,8 +4292,9 @@ function renderReleaseNpmOutputWidget() {
|
|
|
4216
4292
|
}
|
|
4217
4293
|
|
|
4218
4294
|
const controls = make("div", "release-npm-controls", details.controls || "Controls: /release-toggle expands/collapses · /release-abort stops subprocess");
|
|
4219
|
-
|
|
4220
|
-
|
|
4295
|
+
const outputDetails = renderReleaseNpmOutputDetails("release-npm:output", streamHeader, terminal, controls);
|
|
4296
|
+
node.append(header, outputDetails);
|
|
4297
|
+
requestAnimationFrame(() => { if (outputDetails.open) terminal.scrollTop = terminal.scrollHeight; });
|
|
4221
4298
|
return node;
|
|
4222
4299
|
}
|
|
4223
4300
|
|
|
@@ -4246,8 +4323,9 @@ function renderReleaseNpmLogWidget() {
|
|
|
4246
4323
|
for (const line of logLines) {
|
|
4247
4324
|
appendReleaseNpmTerminalLine(terminal, line);
|
|
4248
4325
|
}
|
|
4249
|
-
|
|
4250
|
-
|
|
4326
|
+
const outputDetails = renderReleaseNpmOutputDetails("release-npm:logs", streamHeader, terminal);
|
|
4327
|
+
node.append(header, outputDetails);
|
|
4328
|
+
requestAnimationFrame(() => { if (outputDetails.open) terminal.scrollTop = terminal.scrollHeight; });
|
|
4251
4329
|
return node;
|
|
4252
4330
|
}
|
|
4253
4331
|
|
|
@@ -4286,8 +4364,9 @@ function renderReleaseAurOutputWidget() {
|
|
|
4286
4364
|
}
|
|
4287
4365
|
|
|
4288
4366
|
const controls = make("div", "release-npm-controls", details.controls || "Controls: /release-aur toggle expands/collapses · /release-aur abort stops subprocess");
|
|
4289
|
-
|
|
4290
|
-
|
|
4367
|
+
const outputDetails = renderReleaseNpmOutputDetails("release-aur:output", streamHeader, terminal, controls);
|
|
4368
|
+
node.append(header, outputDetails);
|
|
4369
|
+
requestAnimationFrame(() => { if (outputDetails.open) terminal.scrollTop = terminal.scrollHeight; });
|
|
4291
4370
|
return node;
|
|
4292
4371
|
}
|
|
4293
4372
|
|
|
@@ -4316,8 +4395,9 @@ function renderReleaseAurLogWidget() {
|
|
|
4316
4395
|
for (const line of logLines) {
|
|
4317
4396
|
appendReleaseNpmTerminalLine(terminal, line);
|
|
4318
4397
|
}
|
|
4319
|
-
|
|
4320
|
-
|
|
4398
|
+
const outputDetails = renderReleaseNpmOutputDetails("release-aur:logs", streamHeader, terminal);
|
|
4399
|
+
node.append(header, outputDetails);
|
|
4400
|
+
requestAnimationFrame(() => { if (outputDetails.open) terminal.scrollTop = terminal.scrollHeight; });
|
|
4321
4401
|
return node;
|
|
4322
4402
|
}
|
|
4323
4403
|
|
|
@@ -8293,6 +8373,7 @@ async function refreshAll(tabContext = activeTabContext()) {
|
|
|
8293
8373
|
refreshStats(tabContext),
|
|
8294
8374
|
refreshWorkspace(tabContext),
|
|
8295
8375
|
refreshNetworkStatus(),
|
|
8376
|
+
refreshWebuiVersion(),
|
|
8296
8377
|
]);
|
|
8297
8378
|
if (!isCurrentTabContext(tabContext)) return;
|
|
8298
8379
|
for (const result of results) {
|
|
@@ -8404,12 +8485,108 @@ async function closeNetworkAccess() {
|
|
|
8404
8485
|
}
|
|
8405
8486
|
}
|
|
8406
8487
|
|
|
8488
|
+
function setServerActionStatus(message = "", level = "info") {
|
|
8489
|
+
const status = elements.serverActionStatus;
|
|
8490
|
+
if (!status) return;
|
|
8491
|
+
status.textContent = message;
|
|
8492
|
+
status.hidden = !message;
|
|
8493
|
+
status.className = `server-action-status ${level} ${message ? "" : "muted"}`.trim();
|
|
8494
|
+
}
|
|
8495
|
+
|
|
8496
|
+
function updateServerActionButton() {
|
|
8497
|
+
const action = elements.serverActionSelect?.value || "";
|
|
8498
|
+
const button = elements.runServerActionButton;
|
|
8499
|
+
if (!button) return;
|
|
8500
|
+
button.disabled = !action;
|
|
8501
|
+
button.textContent = action === "restart" ? "Restart" : action === "stop" ? "Stop" : "Run";
|
|
8502
|
+
button.classList.toggle("danger", action === "stop");
|
|
8503
|
+
if (action) setServerActionStatus(action === "restart" ? "Ready to restart the Web UI server." : "Ready to stop the Web UI server.", "info");
|
|
8504
|
+
else setServerActionStatus();
|
|
8505
|
+
}
|
|
8506
|
+
|
|
8507
|
+
function setServerActionBusy(label) {
|
|
8508
|
+
if (elements.serverActionSelect) elements.serverActionSelect.disabled = true;
|
|
8509
|
+
if (elements.runServerActionButton) {
|
|
8510
|
+
elements.runServerActionButton.disabled = true;
|
|
8511
|
+
elements.runServerActionButton.textContent = label;
|
|
8512
|
+
}
|
|
8513
|
+
}
|
|
8514
|
+
|
|
8515
|
+
function resetServerActionControls() {
|
|
8516
|
+
if (elements.serverActionSelect) {
|
|
8517
|
+
elements.serverActionSelect.disabled = false;
|
|
8518
|
+
elements.serverActionSelect.value = "";
|
|
8519
|
+
}
|
|
8520
|
+
updateServerActionButton();
|
|
8521
|
+
}
|
|
8522
|
+
|
|
8523
|
+
async function waitForServerRestart() {
|
|
8524
|
+
for (let attempt = 0; attempt < 40; attempt++) {
|
|
8525
|
+
await delay(attempt === 0 ? 900 : 500);
|
|
8526
|
+
const message = `Restarting… reconnect attempt ${attempt + 1}/40`;
|
|
8527
|
+
setServerActionStatus(message, "warn");
|
|
8528
|
+
setServerRestartOverlay(true, message);
|
|
8529
|
+
try {
|
|
8530
|
+
await api("/api/health", { scoped: false });
|
|
8531
|
+
setBackendOffline(false);
|
|
8532
|
+
await initializeTabs();
|
|
8533
|
+
setServerRestartOverlay(false);
|
|
8534
|
+
setServerActionStatus("Server restarted and reconnected.", "success");
|
|
8535
|
+
addEvent("Pi Web UI server restarted", "warn");
|
|
8536
|
+
return true;
|
|
8537
|
+
} catch (error) {
|
|
8538
|
+
setBackendOffline(true, error);
|
|
8539
|
+
}
|
|
8540
|
+
}
|
|
8541
|
+
return false;
|
|
8542
|
+
}
|
|
8543
|
+
|
|
8544
|
+
async function restartServer() {
|
|
8545
|
+
if (!confirm("Restart the Pi Web UI server?\n\nThis briefly disconnects browser clients and restarts the Pi tabs managed by this Web UI.")) return;
|
|
8546
|
+
|
|
8547
|
+
setServerActionBusy("Restarting…");
|
|
8548
|
+
setServerActionStatus("Restart requested. Waiting for the server to come back…", "warn");
|
|
8549
|
+
setServerRestartOverlay(true, "Restart requested. Waiting for the server to come back…");
|
|
8550
|
+
try {
|
|
8551
|
+
await api("/api/restart", { method: "POST", scoped: false });
|
|
8552
|
+
addEvent("Pi Web UI server restart requested", "warn");
|
|
8553
|
+
} catch (error) {
|
|
8554
|
+
if (!error?.backendOffline) {
|
|
8555
|
+
const missingRestartEndpoint = error.statusCode === 404 || /not found/i.test(error.message || "");
|
|
8556
|
+
const message = missingRestartEndpoint
|
|
8557
|
+
? "Restart is not available in the currently running server. Stop/start manually once to load the new backend."
|
|
8558
|
+
: error.message || String(error);
|
|
8559
|
+
addEvent(message, "error");
|
|
8560
|
+
setServerRestartOverlay(false);
|
|
8561
|
+
resetServerActionControls();
|
|
8562
|
+
setServerActionStatus(message, "error");
|
|
8563
|
+
return;
|
|
8564
|
+
}
|
|
8565
|
+
addEvent("Pi Web UI server connection dropped during restart request", "warn");
|
|
8566
|
+
}
|
|
8567
|
+
|
|
8568
|
+
setBackendOffline(true, new Error("restart requested from side panel"));
|
|
8569
|
+
const restarted = await waitForServerRestart();
|
|
8570
|
+
if (elements.serverActionSelect) {
|
|
8571
|
+
elements.serverActionSelect.disabled = false;
|
|
8572
|
+
elements.serverActionSelect.value = "";
|
|
8573
|
+
}
|
|
8574
|
+
updateServerActionButton();
|
|
8575
|
+
if (restarted) {
|
|
8576
|
+
setServerActionStatus("Server restarted and reconnected.", "success");
|
|
8577
|
+
} else {
|
|
8578
|
+
setServerRestartOverlay(false);
|
|
8579
|
+
setBackendOffline(true, new Error("restart reconnect timed out"));
|
|
8580
|
+
setServerActionStatus("Restart requested, but the server did not reconnect automatically.", "error");
|
|
8581
|
+
addEvent("Pi Web UI server did not come back online after restart request", "error");
|
|
8582
|
+
}
|
|
8583
|
+
}
|
|
8584
|
+
|
|
8407
8585
|
async function stopServer() {
|
|
8408
8586
|
if (!confirm("Stop the Pi Web UI server?\n\nThis disconnects all browser clients and stops the Pi tabs managed by this Web UI.")) return;
|
|
8409
8587
|
|
|
8410
|
-
|
|
8411
|
-
|
|
8412
|
-
button.textContent = "Stopping…";
|
|
8588
|
+
setServerActionBusy("Stopping…");
|
|
8589
|
+
setServerActionStatus("Stop requested. The Web UI will disconnect.", "warn");
|
|
8413
8590
|
try {
|
|
8414
8591
|
await api("/api/shutdown", { method: "POST", scoped: false });
|
|
8415
8592
|
addEvent("Pi Web UI server stop requested", "warn");
|
|
@@ -8421,11 +8598,17 @@ async function stopServer() {
|
|
|
8421
8598
|
return;
|
|
8422
8599
|
}
|
|
8423
8600
|
addEvent(error.message || String(error), "error");
|
|
8424
|
-
|
|
8425
|
-
|
|
8601
|
+
resetServerActionControls();
|
|
8602
|
+
setServerActionStatus(error.message || String(error), "error");
|
|
8426
8603
|
}
|
|
8427
8604
|
}
|
|
8428
8605
|
|
|
8606
|
+
async function runSelectedServerAction() {
|
|
8607
|
+
const action = elements.serverActionSelect?.value || "";
|
|
8608
|
+
if (action === "restart") await restartServer();
|
|
8609
|
+
else if (action === "stop") await stopServer();
|
|
8610
|
+
}
|
|
8611
|
+
|
|
8429
8612
|
function appShortcutModelLabel(model) {
|
|
8430
8613
|
return model ? `${model.provider}/${model.id}` : "unknown model";
|
|
8431
8614
|
}
|
|
@@ -8840,8 +9023,11 @@ function showNextDialog() {
|
|
|
8840
9023
|
button.type = "button";
|
|
8841
9024
|
if (isGuardrailDialog && /^Block$/i.test(optionLabel)) button.classList.add("guardrail-safe-action");
|
|
8842
9025
|
if (isGuardrailDialog && /^Allow/i.test(optionLabel)) button.classList.add("guardrail-allow-action");
|
|
8843
|
-
if (isReleaseDialog && /^Yes
|
|
8844
|
-
if (isReleaseDialog && /^
|
|
9026
|
+
if (isReleaseDialog && /^(?:Yes|All eligible packages\b|Publish selected packages \([1-9]\d*\))/.test(optionLabel)) button.classList.add("primary", "release-publish-action");
|
|
9027
|
+
if (isReleaseDialog && /^Publish selected packages \(select at least one\)$/i.test(optionLabel)) button.classList.add("release-publish-disabled-action");
|
|
9028
|
+
if (isReleaseDialog && /^\[x\]/.test(optionLabel)) button.classList.add("release-target-option", "release-target-selected");
|
|
9029
|
+
if (isReleaseDialog && /^\[ \]/.test(optionLabel)) button.classList.add("release-target-option");
|
|
9030
|
+
if (isReleaseDialog && /^(?:No|Cancel)$/i.test(optionLabel)) button.classList.add("release-cancel-action");
|
|
8845
9031
|
button.addEventListener("click", () => sendDialogResponse({ type: "extension_ui_response", id: request.id, value: optionLabel, tabId: request.tabId }));
|
|
8846
9032
|
options.append(button);
|
|
8847
9033
|
}
|
|
@@ -8889,6 +9075,8 @@ function handleEvent(event) {
|
|
|
8889
9075
|
const tabContext = activeTabContext(event.tabId || activeTabId);
|
|
8890
9076
|
switch (event.type) {
|
|
8891
9077
|
case "webui_connected":
|
|
9078
|
+
setWebuiVersion(event.version);
|
|
9079
|
+
setWebuiDevServer(isWebuiDevMetadata(event));
|
|
8892
9080
|
addEvent(`connected to ${event.tabTitle || "terminal"} for ${event.cwd}`);
|
|
8893
9081
|
scheduleForegroundReconcile("event stream reconnect", 0);
|
|
8894
9082
|
break;
|
|
@@ -9299,7 +9487,9 @@ if (elements.backgroundClearButton) {
|
|
|
9299
9487
|
elements.backgroundClearButton.addEventListener("click", () => clearCustomBackground().catch((error) => addEvent(error.message || String(error), "error")));
|
|
9300
9488
|
}
|
|
9301
9489
|
elements.openNetworkButton.addEventListener("click", openToNetwork);
|
|
9302
|
-
elements.
|
|
9490
|
+
elements.serverActionSelect.addEventListener("change", updateServerActionButton);
|
|
9491
|
+
elements.runServerActionButton.addEventListener("click", () => runSelectedServerAction().catch((error) => addEvent(error.message || String(error), "error")));
|
|
9492
|
+
updateServerActionButton();
|
|
9303
9493
|
elements.agentDoneNotificationsToggle.addEventListener("change", () => {
|
|
9304
9494
|
setAgentDoneNotificationsEnabled(elements.agentDoneNotificationsToggle.checked, {
|
|
9305
9495
|
requestPermission: elements.agentDoneNotificationsToggle.checked,
|
package/public/index.html
CHANGED
|
@@ -36,6 +36,15 @@
|
|
|
36
36
|
</div>
|
|
37
37
|
</section>
|
|
38
38
|
|
|
39
|
+
<section id="serverRestartPanel" class="server-restart-panel" aria-live="assertive" hidden>
|
|
40
|
+
<div class="server-restart-card" role="status">
|
|
41
|
+
<div class="server-restart-spinner" aria-hidden="true"></div>
|
|
42
|
+
<span class="server-restart-kicker">Restarting</span>
|
|
43
|
+
<h1>Restarting Pi Web UI server</h1>
|
|
44
|
+
<p id="serverRestartMessage">Waiting for the server to come back…</p>
|
|
45
|
+
</div>
|
|
46
|
+
</section>
|
|
47
|
+
|
|
39
48
|
<main class="layout">
|
|
40
49
|
<section class="chat-panel">
|
|
41
50
|
<header class="terminal-tabs-shell">
|
|
@@ -151,8 +160,11 @@
|
|
|
151
160
|
<div class="side-panel-header">
|
|
152
161
|
<div>
|
|
153
162
|
<span class="side-panel-kicker">Pi Web UI</span>
|
|
154
|
-
<strong
|
|
155
|
-
|
|
163
|
+
<strong class="side-panel-title">
|
|
164
|
+
<span>Control Deck</span>
|
|
165
|
+
<span id="webuiVersionBadge" class="webui-version-badge" aria-label="Pi Web UI version" hidden></span>
|
|
166
|
+
<span id="webuiDevBadge" class="webui-dev-badge" aria-label="Pi Web UI dev server" hidden>DEV</span>
|
|
167
|
+
</strong>
|
|
156
168
|
</div>
|
|
157
169
|
<button id="toggleSidePanelButton" class="side-panel-toggle-button" type="button" aria-controls="sidePanel" aria-expanded="true" aria-label="Collapse side panel" title="Collapse side panel">
|
|
158
170
|
<span class="side-panel-button-icon" aria-hidden="true">
|
|
@@ -217,8 +229,16 @@
|
|
|
217
229
|
<button id="openNetworkButton" type="button">Open to network</button>
|
|
218
230
|
</div>
|
|
219
231
|
<div class="control-field server-control-field">
|
|
220
|
-
<label>Server</label>
|
|
221
|
-
<
|
|
232
|
+
<label for="serverActionSelect">Server</label>
|
|
233
|
+
<div class="server-action-row">
|
|
234
|
+
<select id="serverActionSelect" title="Server action" aria-label="Server action">
|
|
235
|
+
<option value="" selected>Choose action…</option>
|
|
236
|
+
<option value="restart">Restart Server</option>
|
|
237
|
+
<option value="stop">Stop Server</option>
|
|
238
|
+
</select>
|
|
239
|
+
<button id="runServerActionButton" type="button" disabled>Run</button>
|
|
240
|
+
</div>
|
|
241
|
+
<div id="serverActionStatus" class="server-action-status muted" role="status" aria-live="polite"></div>
|
|
222
242
|
</div>
|
|
223
243
|
<div class="control-field notification-control-field">
|
|
224
244
|
<span class="control-label">Notifications</span>
|
package/public/service-worker.js
CHANGED