@pablopunk/webr 0.2.1 → 0.3.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 +4 -4
- package/dist/client/_astro/{App.1VFY1ClT.css → App.96TkmFyF.css} +1 -1
- package/dist/client/_astro/App.DcDWCCM1.js +4 -0
- package/dist/client/apple-touch-icon.png +0 -0
- package/dist/client/icon-192.png +0 -0
- package/dist/client/icon-512.png +0 -0
- package/dist/server/chunks/{App_C2vmjHiY.mjs → App_DlKdj0Cm.mjs} +207 -214
- package/dist/server/chunks/{_id__BimjoFCd.mjs → _id__yn5rOSmn.mjs} +1 -1
- package/dist/server/chunks/{_tab__WmsbMlfC.mjs → _tab__C9ScWvmu.mjs} +1 -1
- package/dist/server/chunks/{new_bTXSW7C3.mjs → new_DkzcgJw-.mjs} +1 -1
- package/dist/server/chunks/{settings_DwzWmJBe.mjs → settings_DZDiF9cU.mjs} +1 -1
- package/dist/server/entry.mjs +5 -5
- package/package.json +2 -1
- package/server/rebuilt-restart.ts +7 -7
- package/server/run.ts +3 -0
- package/src/lib/models.ts +1 -1
- package/src/lib/pane-strip.ts +19 -0
- package/src/server/host.ts +6 -6
- package/src/server/runtime/herdr-actions.ts +6 -2
- package/src/server/runtime/manager.ts +27 -24
- package/src/server/runtime/reconcile.ts +2 -1
- package/src/server/runtime/target.ts +1 -0
- package/src/server/transport/herdr-target.ts +1 -0
- package/src/server/transport/registry.ts +1 -1
- package/src/server/transport/saved-machines.ts +2 -2
- package/src/server/websockets.ts +5 -9
- package/dist/client/_astro/App.CGyf2WDA.js +0 -4
- package/src/lib/terminal-split.ts +0 -23
|
Binary file
|
package/dist/client/icon-192.png
CHANGED
|
Binary file
|
package/dist/client/icon-512.png
CHANGED
|
Binary file
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { S as createAstro, f as renderHead, i as renderComponent, m as createRenderInstruction, p as addAttribute, s as renderSlot, u as renderTemplate } from "./server_CnGYDmxV.mjs";
|
|
2
2
|
import { t as createComponent } from "./compiler_BiU10Vds.mjs";
|
|
3
|
-
import { createContext, memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
4
|
-
import { Archive, ArchiveRestore, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, Check, ChevronDown, ChevronRight, Circle, CircleAlert, CircleCheck, CircleHelp, Command, Copy, Cpu, Folder, FolderTree, GitBranch, ImagePlus, LayoutList, LoaderCircle, MessageCircleQuestion, Mic, Monitor, Moon,
|
|
3
|
+
import { ViewTransition, createContext, memo, useCallback, useContext, useDeferredValue, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
4
|
+
import { Archive, ArchiveRestore, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, Bot, Check, ChevronDown, ChevronRight, Circle, CircleAlert, CircleCheck, CircleHelp, Command, Copy, Cpu, Folder, FolderTree, GitBranch, ImagePlus, LayoutList, LoaderCircle, MessageCircleQuestion, Mic, Monitor, Moon, PanelLeft, PanelLeftClose, PanelLeftOpen, Pencil, Plus, Search, Server, Settings2, Square, SquarePen, SquareTerminal, Sun, TerminalSquare, Trash2, TriangleAlert, X } from "lucide-react";
|
|
5
5
|
import { createRoot } from "react-dom/client";
|
|
6
6
|
import { AlertDialog } from "@base-ui/react/alert-dialog";
|
|
7
7
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -184,7 +184,8 @@ var shortcutLabels = {
|
|
|
184
184
|
previousPane: "Previous pane",
|
|
185
185
|
toggleSidebar: "Toggle sidebar",
|
|
186
186
|
newThread: "New thread",
|
|
187
|
-
|
|
187
|
+
openTerminal: "New terminal",
|
|
188
|
+
closePane: "Close pane"
|
|
188
189
|
};
|
|
189
190
|
var defaultShortcuts = {
|
|
190
191
|
nextThread: "ctrl+j",
|
|
@@ -193,14 +194,15 @@ var defaultShortcuts = {
|
|
|
193
194
|
previousPane: "ctrl+h",
|
|
194
195
|
toggleSidebar: "meta+b",
|
|
195
196
|
newThread: "ctrl+x",
|
|
196
|
-
|
|
197
|
+
openTerminal: "ctrl+e",
|
|
198
|
+
closePane: "ctrl+q"
|
|
197
199
|
};
|
|
198
200
|
function getShortcuts() {
|
|
199
201
|
try {
|
|
200
202
|
const saved = JSON.parse(localStorage.getItem("webr-shortcuts") ?? "{}");
|
|
201
203
|
return {
|
|
202
204
|
...defaultShortcuts,
|
|
203
|
-
...saved
|
|
205
|
+
...Object.fromEntries(Object.entries(saved).filter(([action]) => Object.hasOwn(defaultShortcuts, action)))
|
|
204
206
|
};
|
|
205
207
|
} catch {
|
|
206
208
|
return defaultShortcuts;
|
|
@@ -335,11 +337,15 @@ var renameProject = (project, name) => post$1("/api/projects/rename", {
|
|
|
335
337
|
logicalId: project.logicalId ?? project.id,
|
|
336
338
|
name
|
|
337
339
|
});
|
|
338
|
-
var
|
|
340
|
+
var openThreadTerminal = (thread, afterPaneId) => post$1(`/api/threads/${encodeURIComponent(thread.id)}/panes/terminal`, {
|
|
339
341
|
machineId: thread.machineId,
|
|
340
|
-
|
|
342
|
+
afterPaneId
|
|
343
|
+
});
|
|
344
|
+
var closeThreadPane = (thread, paneId, force) => post$1(`/api/threads/${encodeURIComponent(thread.id)}/panes/close`, {
|
|
345
|
+
machineId: thread.machineId,
|
|
346
|
+
paneId,
|
|
347
|
+
force
|
|
341
348
|
});
|
|
342
|
-
var closeThreadTerminal = (thread) => post$1(`/api/threads/${encodeURIComponent(thread.id)}/terminal/close`, { machineId: thread.machineId });
|
|
343
349
|
var deleteThreadPermanently = (thread) => post$1(`/api/threads/${encodeURIComponent(thread.id)}/delete`, { machineId: thread.machineId });
|
|
344
350
|
var deleteArchivedThreads = () => post$1("/api/archive/delete");
|
|
345
351
|
var plural = (count, one, many) => count === 1 ? one : many;
|
|
@@ -351,32 +357,15 @@ function deleteWarning(threads) {
|
|
|
351
357
|
if (!worktrees) return `${subject} permanently? This closes ${closes}. Your project files stay untouched.`;
|
|
352
358
|
return `${subject} permanently? This closes ${closes} and removes ${worktrees === count ? plural(count, "its worktree", "their worktrees") : `${worktrees} ${plural(worktrees, "worktree", "worktrees")}`}, including uncommitted changes.`;
|
|
353
359
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
return thread.panes.find((pane) => pane.kind === "shell" && pane.id !== main?.id);
|
|
360
|
-
};
|
|
361
|
-
var peekSide = (shell, area) => shell.x > area.x && shell.y === area.y ? "right" : "bottom";
|
|
362
|
-
var terminalDirection = (side) => side === "right" ? "right" : "down";
|
|
363
|
-
function hiddenTerminalLayout(thread, layout) {
|
|
364
|
-
const shell = threadShell(thread);
|
|
365
|
-
const agent = threadAgent(thread);
|
|
366
|
-
const shellRect = shell && layout.panes.find((rect) => rect.paneId === shell.id)?.rect;
|
|
367
|
-
const otherPanes = thread.panes.filter((pane) => pane.id !== shell?.id && pane.id !== agent?.id);
|
|
368
|
-
if (!thread.terminalHidden || !shell || !agent || !shellRect || otherPanes.length) return void 0;
|
|
369
|
-
return {
|
|
370
|
-
shell,
|
|
371
|
-
agent,
|
|
372
|
-
side: peekSide(shellRect, layout.area)
|
|
373
|
-
};
|
|
360
|
+
var canClosePane = (thread, paneId) => thread.panes.length > 1 && thread.panes.some((pane) => pane.id === paneId && pane.kind !== "agent");
|
|
361
|
+
var neighbourPane = (panes, at) => panes[at - 1] ?? panes[at + 1];
|
|
362
|
+
function paneToFocusAfterClosing(panes, windowStart, paneId) {
|
|
363
|
+
const at = panes.findIndex((pane) => pane.id === paneId);
|
|
364
|
+
return panes.slice(windowStart, windowStart + 2).find((pane) => pane.id !== paneId) ?? neighbourPane(panes, at);
|
|
374
365
|
}
|
|
375
|
-
function
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
const rect = layout?.panes.find((rect) => rect.paneId === shell?.id)?.rect;
|
|
379
|
-
return rect && layout ? terminalDirection(peekSide(rect, layout.area)) : narrow ? "down" : "right";
|
|
366
|
+
function windowStartShowing(count, start, index, size = 2) {
|
|
367
|
+
const shown = index < 0 ? start : index < start ? index : index >= start + size ? index - size + 1 : start;
|
|
368
|
+
return Math.max(0, Math.min(shown, count - size));
|
|
380
369
|
}
|
|
381
370
|
function span(name) {
|
|
382
371
|
const start = performance.now();
|
|
@@ -389,7 +378,7 @@ function startPerf() {}
|
|
|
389
378
|
//#endregion
|
|
390
379
|
//#region src/components/CommandPalette.tsx
|
|
391
380
|
var reportFailure$1 = (error) => alertDialog(error instanceof Error ? error.message : "Herdr could not complete this action.");
|
|
392
|
-
function CommandPalette({ open, onClose, projects, threads, thread, focusedPane, mode, onModeChange, onToggleSidebar, onFocusPane, onTerminalAction,
|
|
381
|
+
function CommandPalette({ open, onClose, projects, threads, thread, focusedPane, mode, onModeChange, onToggleSidebar, onFocusPane, onTerminalAction, onOpenTerminal, onClosePane }) {
|
|
393
382
|
const [query, setQuery] = useState("");
|
|
394
383
|
const [selected, setSelected] = useState(0);
|
|
395
384
|
const [shortcuts, setShortcuts] = useState(defaultShortcuts);
|
|
@@ -483,26 +472,17 @@ function CommandPalette({ open, onClose, projects, threads, thread, focusedPane,
|
|
|
483
472
|
navigator.clipboard.writeText(location.href);
|
|
484
473
|
}
|
|
485
474
|
},
|
|
486
|
-
...thread.bindingState === "attached" ?
|
|
487
|
-
label: "
|
|
475
|
+
...thread.bindingState === "attached" ? [{
|
|
476
|
+
label: "New terminal",
|
|
488
477
|
icon: /* @__PURE__ */ jsx(SquareTerminal, { size: 16 }),
|
|
489
|
-
shortcut: "
|
|
490
|
-
run:
|
|
491
|
-
}, {
|
|
492
|
-
label: "Close
|
|
478
|
+
shortcut: "openTerminal",
|
|
479
|
+
run: onOpenTerminal
|
|
480
|
+
}, ...canClosePane(thread, focusedPane) ? [{
|
|
481
|
+
label: "Close pane",
|
|
493
482
|
icon: /* @__PURE__ */ jsx(X, { size: 16 }),
|
|
494
|
-
|
|
495
|
-
run:
|
|
496
|
-
}] : [
|
|
497
|
-
label: "Toggle terminal (side)",
|
|
498
|
-
icon: /* @__PURE__ */ jsx(PanelRight, { size: 16 }),
|
|
499
|
-
shortcut: "toggleTerminal",
|
|
500
|
-
run: () => onToggleTerminal("right")
|
|
501
|
-
}, {
|
|
502
|
-
label: "Toggle terminal (below)",
|
|
503
|
-
icon: /* @__PURE__ */ jsx(PanelBottom, { size: 16 }),
|
|
504
|
-
run: () => onToggleTerminal("down")
|
|
505
|
-
}] : [],
|
|
483
|
+
shortcut: "closePane",
|
|
484
|
+
run: onClosePane
|
|
485
|
+
}] : []] : [],
|
|
506
486
|
...thread.bindingState === "attached" ? [
|
|
507
487
|
{
|
|
508
488
|
label: "Request terminal control",
|
|
@@ -565,8 +545,8 @@ function CommandPalette({ open, onClose, projects, threads, thread, focusedPane,
|
|
|
565
545
|
onToggleSidebar,
|
|
566
546
|
onFocusPane,
|
|
567
547
|
onTerminalAction,
|
|
568
|
-
|
|
569
|
-
|
|
548
|
+
onOpenTerminal,
|
|
549
|
+
onClosePane
|
|
570
550
|
]).filter((command) => `${command.label} ${command.detail ?? ""}`.toLowerCase().includes(query.toLowerCase().trim()));
|
|
571
551
|
if (!open) return null;
|
|
572
552
|
const run = (command) => {
|
|
@@ -1345,7 +1325,10 @@ var behaviorKey = "webr-new-session-behavior";
|
|
|
1345
1325
|
var legacyWorktreeKey = "webr-new-worktree";
|
|
1346
1326
|
var lastWorktreeKey = (machineId, projectId) => `webr-last-worktree:${machineId}:${projectId}`;
|
|
1347
1327
|
var isBehavior = (value) => value === "main" || value === "worktree" || value === "remember";
|
|
1348
|
-
var behaviorFromLegacySetting = () =>
|
|
1328
|
+
var behaviorFromLegacySetting = () => {
|
|
1329
|
+
const legacy = localStorage.getItem(legacyWorktreeKey);
|
|
1330
|
+
return legacy === null ? "remember" : legacy === "false" ? "main" : "worktree";
|
|
1331
|
+
};
|
|
1349
1332
|
function readNewSessionBehavior() {
|
|
1350
1333
|
const stored = localStorage.getItem(behaviorKey);
|
|
1351
1334
|
return isBehavior(stored) ? stored : behaviorFromLegacySetting();
|
|
@@ -2795,7 +2778,6 @@ function createRuntimeStore(bootstrap) {
|
|
|
2795
2778
|
threads: Object.fromEntries(Object.entries(state.threads).map(([id, thread]) => [id, {
|
|
2796
2779
|
...thread,
|
|
2797
2780
|
status: "unknown",
|
|
2798
|
-
panes: [],
|
|
2799
2781
|
bindingState: "detached"
|
|
2800
2782
|
}]))
|
|
2801
2783
|
};
|
|
@@ -2836,9 +2818,14 @@ function createRuntimeStore(bootstrap) {
|
|
|
2836
2818
|
}
|
|
2837
2819
|
var createUiStore = () => createStore((set) => ({
|
|
2838
2820
|
focusedPanes: {},
|
|
2821
|
+
paneWindows: {},
|
|
2839
2822
|
focus: (scope, paneId) => set((state) => ({ focusedPanes: {
|
|
2840
2823
|
...state.focusedPanes,
|
|
2841
2824
|
[scope]: paneId
|
|
2825
|
+
} })),
|
|
2826
|
+
showWindow: (scope, start) => set((state) => ({ paneWindows: {
|
|
2827
|
+
...state.paneWindows,
|
|
2828
|
+
[scope]: start
|
|
2842
2829
|
} }))
|
|
2843
2830
|
}));
|
|
2844
2831
|
function decodeFrame(packet) {
|
|
@@ -3914,7 +3901,7 @@ function themeColors() {
|
|
|
3914
3901
|
};
|
|
3915
3902
|
}
|
|
3916
3903
|
var TERMINAL_FONT_FAMILY = "\"JetBrains Mono\", \"Webr Symbols\", ui-monospace, Menlo, monospace";
|
|
3917
|
-
var TERMINAL_FONT_SIZE =
|
|
3904
|
+
var TERMINAL_FONT_SIZE = 15;
|
|
3918
3905
|
var loadTerminalFonts = () => typeof document === "undefined" || !document.fonts ? Promise.resolve() : Promise.all([
|
|
3919
3906
|
document.fonts.load(`400 ${TERMINAL_FONT_SIZE}px "JetBrains Mono"`),
|
|
3920
3907
|
document.fonts.load(`700 ${TERMINAL_FONT_SIZE}px "JetBrains Mono"`),
|
|
@@ -3922,6 +3909,7 @@ var loadTerminalFonts = () => typeof document === "undefined" || !document.fonts
|
|
|
3922
3909
|
]).then(() => void 0, () => void 0);
|
|
3923
3910
|
var FOCUS_IN = "\x1B[I";
|
|
3924
3911
|
var FOCUS_OUT = "\x1B[O";
|
|
3912
|
+
var finiteSize = (size) => size && Number.isFinite(size.cols) && Number.isFinite(size.rows) ? size : void 0;
|
|
3925
3913
|
function TerminalPane({ pane, machineId, threadId, active, canControl, onFocus }) {
|
|
3926
3914
|
const host = useRef(null);
|
|
3927
3915
|
const section = useRef(null);
|
|
@@ -4000,11 +3988,12 @@ function TerminalPane({ pane, machineId, threadId, active, canControl, onFocus }
|
|
|
4000
3988
|
});
|
|
4001
3989
|
const measuredViewport = () => {
|
|
4002
3990
|
try {
|
|
4003
|
-
return fit.proposeDimensions();
|
|
3991
|
+
return finiteSize(fit.proposeDimensions());
|
|
4004
3992
|
} catch {
|
|
4005
3993
|
return;
|
|
4006
3994
|
}
|
|
4007
3995
|
};
|
|
3996
|
+
const hostIsLaidOut = () => !!host.current?.isConnected && host.current.clientWidth > 0 && host.current.clientHeight > 0;
|
|
4008
3997
|
const viewport = () => {
|
|
4009
3998
|
const { cols, rows } = measuredViewport() ?? estimatedViewport();
|
|
4010
3999
|
return {
|
|
@@ -4034,13 +4023,14 @@ function TerminalPane({ pane, machineId, threadId, active, canControl, onFocus }
|
|
|
4034
4023
|
const resize = new ResizeObserver(() => {
|
|
4035
4024
|
clearTimeout(timer);
|
|
4036
4025
|
timer = setTimeout(() => {
|
|
4026
|
+
if (!hostIsLaidOut()) return;
|
|
4037
4027
|
const { cols, rows } = viewport();
|
|
4038
4028
|
control.current?.resize(cols, rows);
|
|
4039
4029
|
}, 150);
|
|
4040
4030
|
});
|
|
4041
4031
|
resize.observe(host.current);
|
|
4042
4032
|
const claimSize = () => {
|
|
4043
|
-
if (document.visibilityState !== "visible" || !document.hasFocus()) return;
|
|
4033
|
+
if (document.visibilityState !== "visible" || !document.hasFocus() || !hostIsLaidOut()) return;
|
|
4044
4034
|
const { cols, rows } = viewport();
|
|
4045
4035
|
control.current?.resize(cols, rows, true);
|
|
4046
4036
|
};
|
|
@@ -4216,70 +4206,6 @@ function TerminalPane({ pane, machineId, threadId, active, canControl, onFocus }
|
|
|
4216
4206
|
});
|
|
4217
4207
|
}
|
|
4218
4208
|
//#endregion
|
|
4219
|
-
//#region src/components/AdoptTab.tsx
|
|
4220
|
-
function AdoptTab({ thread, tabs }) {
|
|
4221
|
-
const [tabId, setTabId] = useState("");
|
|
4222
|
-
const [error, setError] = useState("");
|
|
4223
|
-
const [busy, setBusy] = useState(false);
|
|
4224
|
-
const selected = tabs.find((tab) => tab.tabId === tabId && !tab.bound);
|
|
4225
|
-
return /* @__PURE__ */ jsxs("form", {
|
|
4226
|
-
className: "adoption-picker",
|
|
4227
|
-
onSubmit: async (event) => {
|
|
4228
|
-
event.preventDefault();
|
|
4229
|
-
if (!selected || busy) return;
|
|
4230
|
-
setBusy(true);
|
|
4231
|
-
if (!await confirmDialog(`Bind this thread to ${selected.label} (${selected.tabId}) and its ${selected.panes.length} current terminals without sending the old prompt?`, { confirmLabel: "Bind" })) {
|
|
4232
|
-
setBusy(false);
|
|
4233
|
-
return;
|
|
4234
|
-
}
|
|
4235
|
-
setError("");
|
|
4236
|
-
try {
|
|
4237
|
-
if (!(await fetch(`/api/threads/${encodeURIComponent(thread.id)}/adopt`, {
|
|
4238
|
-
method: "POST",
|
|
4239
|
-
headers: { "Content-Type": "application/json" },
|
|
4240
|
-
body: JSON.stringify({
|
|
4241
|
-
machineId: thread.machineId,
|
|
4242
|
-
terminalIds: selected.panes.map((pane) => pane.terminalId)
|
|
4243
|
-
})
|
|
4244
|
-
})).ok) throw new Error("The tab changed or is already bound; select it again after refresh.");
|
|
4245
|
-
} catch (error) {
|
|
4246
|
-
setError(error instanceof Error ? error.message : "Adoption failed.");
|
|
4247
|
-
} finally {
|
|
4248
|
-
setBusy(false);
|
|
4249
|
-
}
|
|
4250
|
-
},
|
|
4251
|
-
children: [
|
|
4252
|
-
/* @__PURE__ */ jsxs("label", { children: ["Select the current native tab to adopt ", /* @__PURE__ */ jsxs("select", {
|
|
4253
|
-
"aria-label": "Native tab to adopt",
|
|
4254
|
-
value: tabId,
|
|
4255
|
-
onChange: (event) => setTabId(event.target.value),
|
|
4256
|
-
children: [/* @__PURE__ */ jsx("option", {
|
|
4257
|
-
value: "",
|
|
4258
|
-
children: "Select a tab…"
|
|
4259
|
-
}), tabs.map((tab) => /* @__PURE__ */ jsxs("option", {
|
|
4260
|
-
value: tab.tabId,
|
|
4261
|
-
disabled: tab.bound || !tab.panes.length,
|
|
4262
|
-
children: [
|
|
4263
|
-
tab.label,
|
|
4264
|
-
" · ",
|
|
4265
|
-
tab.tabId,
|
|
4266
|
-
tab.bound ? " · Already bound" : ""
|
|
4267
|
-
]
|
|
4268
|
-
}, tab.tabId))]
|
|
4269
|
-
})] }),
|
|
4270
|
-
selected && /* @__PURE__ */ jsx("p", { children: selected.panes.map((pane) => `${pane.title} (${pane.terminalId})`).join(", ") }),
|
|
4271
|
-
/* @__PURE__ */ jsx("button", {
|
|
4272
|
-
disabled: !selected || busy,
|
|
4273
|
-
children: "Adopt without restarting"
|
|
4274
|
-
}),
|
|
4275
|
-
error && /* @__PURE__ */ jsx("p", {
|
|
4276
|
-
role: "alert",
|
|
4277
|
-
children: error
|
|
4278
|
-
})
|
|
4279
|
-
]
|
|
4280
|
-
});
|
|
4281
|
-
}
|
|
4282
|
-
//#endregion
|
|
4283
4209
|
//#region src/components/LaunchProgress.tsx
|
|
4284
4210
|
function LaunchProgress({ thread }) {
|
|
4285
4211
|
const failed = launchFailed(thread);
|
|
@@ -4327,51 +4253,109 @@ function LaunchProgress({ thread }) {
|
|
|
4327
4253
|
});
|
|
4328
4254
|
}
|
|
4329
4255
|
//#endregion
|
|
4330
|
-
//#region src/components/
|
|
4331
|
-
var
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
height: `calc(100% - ${PEEK_SIZE})`
|
|
4342
|
-
};
|
|
4343
|
-
function TerminalPeek({ side, onOpen }) {
|
|
4256
|
+
//#region src/components/PaneStrip.tsx
|
|
4257
|
+
var PaneIcon = ({ pane }) => pane.kind === "agent" ? /* @__PURE__ */ jsx(Bot, {
|
|
4258
|
+
size: 14,
|
|
4259
|
+
strokeWidth: 1.8,
|
|
4260
|
+
"aria-hidden": "true"
|
|
4261
|
+
}) : /* @__PURE__ */ jsx(TerminalSquare, {
|
|
4262
|
+
size: 14,
|
|
4263
|
+
strokeWidth: 1.8,
|
|
4264
|
+
"aria-hidden": "true"
|
|
4265
|
+
});
|
|
4266
|
+
function CollapsedPane({ pane, side, onOpen }) {
|
|
4344
4267
|
return /* @__PURE__ */ jsx(Tooltip$1, {
|
|
4345
|
-
label:
|
|
4346
|
-
side: side === "
|
|
4268
|
+
label: `Show ${pane.title}`,
|
|
4269
|
+
side: side === "left" ? "right" : "left",
|
|
4347
4270
|
children: /* @__PURE__ */ jsxs("button", {
|
|
4348
4271
|
type: "button",
|
|
4349
|
-
className:
|
|
4350
|
-
"aria-label":
|
|
4272
|
+
className: "pane-collapsed",
|
|
4273
|
+
"aria-label": `Show ${pane.title}`,
|
|
4351
4274
|
onClick: onOpen,
|
|
4352
|
-
children: [/* @__PURE__ */ jsx(
|
|
4353
|
-
size: 14,
|
|
4354
|
-
strokeWidth: 1.8,
|
|
4355
|
-
"aria-hidden": "true"
|
|
4356
|
-
}), /* @__PURE__ */ jsx("span", { children: "Terminal" })]
|
|
4275
|
+
children: [/* @__PURE__ */ jsx(PaneIcon, { pane }), /* @__PURE__ */ jsx("span", { children: pane.title })]
|
|
4357
4276
|
})
|
|
4358
4277
|
});
|
|
4359
4278
|
}
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4279
|
+
var stripShape = (panes, windowStart) => JSON.stringify([windowStart, panes.map((pane) => pane.id)]);
|
|
4280
|
+
var panesInShape = (shape, panes) => {
|
|
4281
|
+
const [start, ids] = JSON.parse(shape);
|
|
4282
|
+
return {
|
|
4283
|
+
start,
|
|
4284
|
+
panes: ids.flatMap((id) => panes.filter((pane) => pane.id === id))
|
|
4285
|
+
};
|
|
4286
|
+
};
|
|
4287
|
+
/** Only a change of order or window runs as a transition, because a view transition covers the live terminals with snapshots while it animates. */
|
|
4288
|
+
var useAnimatedShape = (panes, windowStart) => panesInShape(useDeferredValue(stripShape(panes, windowStart)), panes);
|
|
4289
|
+
function PaneStrip({ panes: livePanes, windowStart: liveStart, onFocusPane, renderPane }) {
|
|
4290
|
+
const { panes, start } = useAnimatedShape(livePanes, liveStart);
|
|
4291
|
+
const isVisible = (index) => index >= start && index < start + 2;
|
|
4292
|
+
return /* @__PURE__ */ jsx("div", {
|
|
4293
|
+
className: `pane-strip ${panes.length > 1 ? "is-split" : ""}`,
|
|
4294
|
+
children: panes.map((pane, index) => /* @__PURE__ */ jsx(ViewTransition, {
|
|
4295
|
+
default: "pane-move",
|
|
4296
|
+
enter: "pane-enter",
|
|
4297
|
+
exit: "pane-exit",
|
|
4298
|
+
children: isVisible(index) ? /* @__PURE__ */ jsx("div", {
|
|
4299
|
+
className: "pane-slot",
|
|
4300
|
+
children: renderPane(pane)
|
|
4301
|
+
}) : /* @__PURE__ */ jsx(CollapsedPane, {
|
|
4302
|
+
pane,
|
|
4303
|
+
side: index < start ? "left" : "right",
|
|
4304
|
+
onOpen: () => onFocusPane(pane.id)
|
|
4305
|
+
})
|
|
4306
|
+
}, pane.id))
|
|
4307
|
+
});
|
|
4308
|
+
}
|
|
4309
|
+
function PaneTabs({ panes, focusedPane, onFocusPane, renderPane }) {
|
|
4310
|
+
const shownId = useDeferredValue(focusedPane);
|
|
4311
|
+
const shown = panes.find((pane) => pane.id === shownId) ?? panes[0];
|
|
4312
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
4313
|
+
className: "pane-tabbed",
|
|
4314
|
+
children: [panes.length > 1 && /* @__PURE__ */ jsx("div", {
|
|
4315
|
+
className: "pane-tabs",
|
|
4316
|
+
role: "tablist",
|
|
4317
|
+
"aria-label": "Panes",
|
|
4318
|
+
children: panes.map((pane) => /* @__PURE__ */ jsxs("button", {
|
|
4319
|
+
type: "button",
|
|
4320
|
+
role: "tab",
|
|
4321
|
+
"aria-selected": pane.id === shown?.id,
|
|
4322
|
+
className: "pane-tab",
|
|
4323
|
+
onClick: () => onFocusPane(pane.id),
|
|
4324
|
+
children: [/* @__PURE__ */ jsx(PaneIcon, { pane }), /* @__PURE__ */ jsx("span", { children: pane.title })]
|
|
4325
|
+
}, pane.id))
|
|
4326
|
+
}), shown && /* @__PURE__ */ jsx(ViewTransition, {
|
|
4327
|
+
enter: "pane-enter",
|
|
4328
|
+
exit: "pane-exit",
|
|
4329
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
4330
|
+
className: "pane-slot",
|
|
4331
|
+
role: "tabpanel",
|
|
4332
|
+
children: renderPane(shown)
|
|
4333
|
+
})
|
|
4334
|
+
}, shown.id)]
|
|
4335
|
+
});
|
|
4336
|
+
}
|
|
4337
|
+
//#endregion
|
|
4338
|
+
//#region src/components/ThreadView.tsx
|
|
4339
|
+
function ThreadView({ thread, connected, canControl, onFocusPane, focusedPane, windowStart, narrow }) {
|
|
4340
|
+
const detached = connected && thread.bindingState === "detached";
|
|
4341
|
+
useEffect(() => {
|
|
4342
|
+
if (detached) navigate(`/new?project=${encodeURIComponent(thread.projectId)}`);
|
|
4343
|
+
}, [detached, thread.projectId]);
|
|
4344
|
+
const renderPane = (pane) => pane.terminalId ? /* @__PURE__ */ jsx(TerminalPane, {
|
|
4345
|
+
pane,
|
|
4346
|
+
machineId: thread.machineId,
|
|
4347
|
+
threadId: thread.id,
|
|
4348
|
+
active: focusedPane === pane.id,
|
|
4349
|
+
canControl,
|
|
4350
|
+
onFocus: () => onFocusPane(pane.id)
|
|
4351
|
+
}, pane.terminalId) : null;
|
|
4373
4352
|
if (connected && (isLaunching(thread) || launchFailed(thread))) return /* @__PURE__ */ jsx(LaunchProgress, { thread });
|
|
4374
|
-
const
|
|
4353
|
+
const strip = {
|
|
4354
|
+
panes: thread.panes,
|
|
4355
|
+
focusedPane,
|
|
4356
|
+
onFocusPane,
|
|
4357
|
+
renderPane
|
|
4358
|
+
};
|
|
4375
4359
|
return /* @__PURE__ */ jsxs("main", {
|
|
4376
4360
|
className: "native-thread-layout",
|
|
4377
4361
|
"aria-label": `${thread.title} tab`,
|
|
@@ -4379,37 +4363,17 @@ function ThreadView({ thread, layouts, tabs, connected, canControl, onFocusPane,
|
|
|
4379
4363
|
(!connected || thread.bindingState !== "attached") && /* @__PURE__ */ jsx("p", {
|
|
4380
4364
|
className: "runtime-notice",
|
|
4381
4365
|
role: "status",
|
|
4382
|
-
children: !connected ? "Disconnected; input is disabled." :
|
|
4366
|
+
children: !connected ? "Disconnected; input is disabled." : "Waiting for the terminal to attach…"
|
|
4383
4367
|
}),
|
|
4384
4368
|
connected && thread.bindingState === "attached" && !canControl && /* @__PURE__ */ jsx("p", {
|
|
4385
4369
|
className: "runtime-notice",
|
|
4386
4370
|
role: "status",
|
|
4387
4371
|
children: "Terminal input is unavailable because this Herdr version is not supported."
|
|
4388
4372
|
}),
|
|
4389
|
-
connected && thread.bindingState === "
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
}
|
|
4393
|
-
connected && thread.bindingState === "attached" && visibleLayouts.map((layout) => {
|
|
4394
|
-
const hidden = hiddenTerminalLayout(thread, layout);
|
|
4395
|
-
const split = !hidden && layout.panes.filter((rect) => thread.panes.some((item) => item.id === rect.paneId)).length > 1;
|
|
4396
|
-
return /* @__PURE__ */ jsx("div", {
|
|
4397
|
-
className: `native-pane-layout ${split ? "is-split" : ""}`,
|
|
4398
|
-
children: hidden ? [pane(hidden.agent, besidePeek(hidden.side)), /* @__PURE__ */ jsx(TerminalPeek, {
|
|
4399
|
-
side: hidden.side,
|
|
4400
|
-
onOpen: onToggleTerminal
|
|
4401
|
-
}, "terminal-peek")] : thread.panes.map((item) => {
|
|
4402
|
-
const rect = layout.panes.find((rect) => rect.paneId === item.id)?.rect;
|
|
4403
|
-
if (!rect || !layout.area.width || !layout.area.height) return null;
|
|
4404
|
-
return pane(item, {
|
|
4405
|
-
left: `${(rect.x - layout.area.x) / layout.area.width * 100}%`,
|
|
4406
|
-
top: `${(rect.y - layout.area.y) / layout.area.height * 100}%`,
|
|
4407
|
-
width: `${rect.width / layout.area.width * 100}%`,
|
|
4408
|
-
height: `${rect.height / layout.area.height * 100}%`
|
|
4409
|
-
});
|
|
4410
|
-
})
|
|
4411
|
-
}, `${layout.workspaceId}:${layout.tabId}`);
|
|
4412
|
-
})
|
|
4373
|
+
connected && thread.bindingState === "attached" && (narrow ? /* @__PURE__ */ jsx(PaneTabs, { ...strip }) : /* @__PURE__ */ jsx(PaneStrip, {
|
|
4374
|
+
...strip,
|
|
4375
|
+
windowStart
|
|
4376
|
+
}))
|
|
4413
4377
|
]
|
|
4414
4378
|
});
|
|
4415
4379
|
}
|
|
@@ -4423,6 +4387,30 @@ function appShortcutAction(event, shortcuts) {
|
|
|
4423
4387
|
return Object.entries(shortcuts).find(([, combo]) => combo === keyCombo(event))?.[0];
|
|
4424
4388
|
}
|
|
4425
4389
|
//#endregion
|
|
4390
|
+
//#region src/client/pane-window.ts
|
|
4391
|
+
var NARROW_SCREEN = "(max-width: 760px)";
|
|
4392
|
+
function usePaneWindowStart(ui, scope, paneIds, focusedPane) {
|
|
4393
|
+
const stored = useStore(ui, (state) => state.paneWindows[scope] ?? 0);
|
|
4394
|
+
const start = windowStartShowing(paneIds.length, stored, paneIds.indexOf(focusedPane));
|
|
4395
|
+
useEffect(() => {
|
|
4396
|
+
if (start !== stored) ui.getState().showWindow(scope, start);
|
|
4397
|
+
}, [
|
|
4398
|
+
ui,
|
|
4399
|
+
scope,
|
|
4400
|
+
start,
|
|
4401
|
+
stored
|
|
4402
|
+
]);
|
|
4403
|
+
return start;
|
|
4404
|
+
}
|
|
4405
|
+
var subscribeToNarrowScreen = (onChange) => {
|
|
4406
|
+
if (typeof matchMedia !== "function") return () => {};
|
|
4407
|
+
const media = matchMedia(NARROW_SCREEN);
|
|
4408
|
+
media.addEventListener("change", onChange);
|
|
4409
|
+
return () => media.removeEventListener("change", onChange);
|
|
4410
|
+
};
|
|
4411
|
+
var isNarrowScreen = () => typeof matchMedia === "function" && matchMedia(NARROW_SCREEN).matches;
|
|
4412
|
+
var useNarrowScreen = () => useSyncExternalStore(subscribeToNarrowScreen, isNarrowScreen, () => false);
|
|
4413
|
+
//#endregion
|
|
4426
4414
|
//#region src/client/clear-done-on-visit.ts
|
|
4427
4415
|
function useClearDoneOnVisit(thread, paneId, enabled) {
|
|
4428
4416
|
const isDone = thread?.status === "done";
|
|
@@ -4515,32 +4503,37 @@ function RuntimeApp({ page, bootstrap, threadId, projectId }) {
|
|
|
4515
4503
|
window.removeEventListener("popstate", syncCollapsedFromUrl);
|
|
4516
4504
|
};
|
|
4517
4505
|
}, []);
|
|
4518
|
-
const
|
|
4519
|
-
const
|
|
4520
|
-
const
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4506
|
+
const reportPaneFailure = (error) => void alertDialog(error instanceof Error ? error.message : "Herdr could not change the pane.");
|
|
4507
|
+
const windowStart = usePaneWindowStart(ui, scope, thread?.panes.map((pane) => pane.id) ?? [], focusedPane);
|
|
4508
|
+
const narrow = useNarrowScreen();
|
|
4509
|
+
const openTerminal = useCallback(() => {
|
|
4510
|
+
if (!thread || !focusedPane) return;
|
|
4511
|
+
openThreadTerminal(thread, focusedPane).then(({ paneId }) => setFocusedPane(paneId), reportPaneFailure);
|
|
4524
4512
|
}, [
|
|
4525
4513
|
thread,
|
|
4526
|
-
|
|
4514
|
+
focusedPane,
|
|
4527
4515
|
setFocusedPane
|
|
4528
4516
|
]);
|
|
4529
|
-
const
|
|
4530
|
-
if (thread
|
|
4531
|
-
|
|
4517
|
+
const closePane = useCallback(async (paneId = focusedPane) => {
|
|
4518
|
+
if (!thread || !canClosePane(thread, paneId)) return;
|
|
4519
|
+
const pane = thread.panes.find((pane) => pane.id === paneId);
|
|
4520
|
+
const next = paneToFocusAfterClosing(thread.panes, windowStart, paneId);
|
|
4521
|
+
try {
|
|
4522
|
+
let result = await closeThreadPane(thread, paneId, false);
|
|
4523
|
+
if (result.busy && await confirmDialog(`${pane.title} is running a process. Close it anyway?`, {
|
|
4524
|
+
confirmLabel: "Close",
|
|
4525
|
+
danger: true
|
|
4526
|
+
})) result = await closeThreadPane(thread, paneId, true);
|
|
4527
|
+
if (result.closed && next) setFocusedPane(next.id);
|
|
4528
|
+
} catch (error) {
|
|
4529
|
+
reportPaneFailure(error);
|
|
4530
|
+
}
|
|
4532
4531
|
}, [
|
|
4533
4532
|
thread,
|
|
4534
|
-
|
|
4533
|
+
focusedPane,
|
|
4534
|
+
windowStart,
|
|
4535
4535
|
setFocusedPane
|
|
4536
4536
|
]);
|
|
4537
|
-
const closeTerminal = useCallback(() => {
|
|
4538
|
-
if (!thread) return;
|
|
4539
|
-
closeThreadTerminal(thread).then(() => {
|
|
4540
|
-
const agent = threadAgent(thread);
|
|
4541
|
-
if (agent) setFocusedPane(agent.id);
|
|
4542
|
-
}, reportTerminalFailure);
|
|
4543
|
-
}, [thread, setFocusedPane]);
|
|
4544
4537
|
const toggleSidebar = useCallback(() => {
|
|
4545
4538
|
if (window.matchMedia("(max-width: 760px)").matches) {
|
|
4546
4539
|
setMobileOpen((value) => !value);
|
|
@@ -4573,11 +4566,12 @@ function RuntimeApp({ page, bootstrap, threadId, projectId }) {
|
|
|
4573
4566
|
if (!action) return;
|
|
4574
4567
|
event.preventDefault();
|
|
4575
4568
|
if (action === "toggleSidebar") toggleSidebar();
|
|
4576
|
-
if (action === "
|
|
4569
|
+
if (action === "openTerminal") openTerminal();
|
|
4570
|
+
if (action === "closePane") closePane();
|
|
4577
4571
|
if (action === "newThread") navigate(`/new${thread ? `?project=${encodeURIComponent(thread.projectId)}` : ""}`);
|
|
4578
4572
|
if ((action === "nextPane" || action === "previousPane") && thread?.panes.length) {
|
|
4579
4573
|
const index = thread.panes.findIndex((pane) => pane.id === focusedPane);
|
|
4580
|
-
|
|
4574
|
+
setFocusedPane(thread.panes[(index + (action === "nextPane" ? 1 : -1) + thread.panes.length) % thread.panes.length].id);
|
|
4581
4575
|
}
|
|
4582
4576
|
if ((action === "nextThread" || action === "previousThread") && threads.length) {
|
|
4583
4577
|
const index = threads.findIndex((item) => item.id === thread?.id);
|
|
@@ -4592,8 +4586,8 @@ function RuntimeApp({ page, bootstrap, threadId, projectId }) {
|
|
|
4592
4586
|
threads,
|
|
4593
4587
|
paletteOpen,
|
|
4594
4588
|
toggleSidebar,
|
|
4595
|
-
|
|
4596
|
-
|
|
4589
|
+
openTerminal,
|
|
4590
|
+
closePane,
|
|
4597
4591
|
focusedPane,
|
|
4598
4592
|
setFocusedPane
|
|
4599
4593
|
]);
|
|
@@ -4619,13 +4613,12 @@ function RuntimeApp({ page, bootstrap, threadId, projectId }) {
|
|
|
4619
4613
|
children: [
|
|
4620
4614
|
page === "thread" && thread && /* @__PURE__ */ jsx(ThreadView, {
|
|
4621
4615
|
thread,
|
|
4622
|
-
layouts: projections[thread.machineId]?.layouts ?? [],
|
|
4623
|
-
tabs: projections[thread.machineId]?.availableTabs ?? [],
|
|
4624
4616
|
connected: gatewayConnected && !!projections[thread.machineId]?.connected,
|
|
4625
4617
|
canControl: !!machines.find((machine) => machine.id === thread.machineId)?.writable,
|
|
4626
4618
|
focusedPane,
|
|
4627
|
-
onFocusPane:
|
|
4628
|
-
|
|
4619
|
+
onFocusPane: setFocusedPane,
|
|
4620
|
+
windowStart,
|
|
4621
|
+
narrow
|
|
4629
4622
|
}),
|
|
4630
4623
|
page === "new" && /* @__PURE__ */ jsx(NewThreadView, {
|
|
4631
4624
|
projects,
|
|
@@ -4669,13 +4662,13 @@ function RuntimeApp({ page, bootstrap, threadId, projectId }) {
|
|
|
4669
4662
|
mode,
|
|
4670
4663
|
onModeChange: changeMode,
|
|
4671
4664
|
onToggleSidebar: toggleSidebar,
|
|
4672
|
-
onFocusPane:
|
|
4665
|
+
onFocusPane: setFocusedPane,
|
|
4673
4666
|
onTerminalAction: (action) => {
|
|
4674
4667
|
const pane = thread?.panes.find((pane) => pane.id === focusedPane);
|
|
4675
4668
|
if (thread && pane?.terminalId) terminals.command(thread.machineId, thread.id, pane.terminalId, action);
|
|
4676
4669
|
},
|
|
4677
|
-
|
|
4678
|
-
|
|
4670
|
+
onOpenTerminal: openTerminal,
|
|
4671
|
+
onClosePane: () => void closePane()
|
|
4679
4672
|
})
|
|
4680
4673
|
]
|
|
4681
4674
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { r as __exportAll } from "./rolldown-runtime_BMI-E3GI.mjs";
|
|
2
2
|
import { S as createAstro, i as renderComponent, u as renderTemplate } from "./server_CnGYDmxV.mjs";
|
|
3
3
|
import { t as createComponent } from "./compiler_BiU10Vds.mjs";
|
|
4
|
-
import { n as $$Base, t as App } from "./
|
|
4
|
+
import { n as $$Base, t as App } from "./App_DlKdj0Cm.mjs";
|
|
5
5
|
//#region src/pages/threads/[id].astro
|
|
6
6
|
var _id__exports = /* @__PURE__ */ __exportAll({
|
|
7
7
|
default: () => $$Id,
|