@pablopunk/webr 0.2.0 → 0.3.1
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 +10 -1
- package/dist/client/_astro/{App.1VFY1ClT.css → App.7T8H5D2I.css} +1 -1
- package/dist/client/_astro/App.Dthb8mGW.js +4 -0
- package/dist/server/chunks/{App_C2vmjHiY.mjs → App_D7QqB19a.mjs} +202 -212
- package/dist/server/chunks/{_id__BimjoFCd.mjs → _id__z7LJBEaG.mjs} +1 -1
- package/dist/server/chunks/{_tab__WmsbMlfC.mjs → _tab__D7iMaNBo.mjs} +1 -1
- package/dist/server/chunks/{new_bTXSW7C3.mjs → new_BUdVQQYr.mjs} +1 -1
- package/dist/server/chunks/{settings_DwzWmJBe.mjs → settings_DJoINWN6.mjs} +1 -1
- package/dist/server/entry.mjs +5 -5
- package/package.json +2 -1
- package/server/cli.ts +14 -3
- package/server/rebuilt-restart.ts +26 -0
- package/server/run.ts +7 -0
- package/server/update/index.ts +4 -2
- 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
|
@@ -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) => {
|
|
@@ -2795,7 +2775,6 @@ function createRuntimeStore(bootstrap) {
|
|
|
2795
2775
|
threads: Object.fromEntries(Object.entries(state.threads).map(([id, thread]) => [id, {
|
|
2796
2776
|
...thread,
|
|
2797
2777
|
status: "unknown",
|
|
2798
|
-
panes: [],
|
|
2799
2778
|
bindingState: "detached"
|
|
2800
2779
|
}]))
|
|
2801
2780
|
};
|
|
@@ -2836,9 +2815,14 @@ function createRuntimeStore(bootstrap) {
|
|
|
2836
2815
|
}
|
|
2837
2816
|
var createUiStore = () => createStore((set) => ({
|
|
2838
2817
|
focusedPanes: {},
|
|
2818
|
+
paneWindows: {},
|
|
2839
2819
|
focus: (scope, paneId) => set((state) => ({ focusedPanes: {
|
|
2840
2820
|
...state.focusedPanes,
|
|
2841
2821
|
[scope]: paneId
|
|
2822
|
+
} })),
|
|
2823
|
+
showWindow: (scope, start) => set((state) => ({ paneWindows: {
|
|
2824
|
+
...state.paneWindows,
|
|
2825
|
+
[scope]: start
|
|
2842
2826
|
} }))
|
|
2843
2827
|
}));
|
|
2844
2828
|
function decodeFrame(packet) {
|
|
@@ -3922,6 +3906,7 @@ var loadTerminalFonts = () => typeof document === "undefined" || !document.fonts
|
|
|
3922
3906
|
]).then(() => void 0, () => void 0);
|
|
3923
3907
|
var FOCUS_IN = "\x1B[I";
|
|
3924
3908
|
var FOCUS_OUT = "\x1B[O";
|
|
3909
|
+
var finiteSize = (size) => size && Number.isFinite(size.cols) && Number.isFinite(size.rows) ? size : void 0;
|
|
3925
3910
|
function TerminalPane({ pane, machineId, threadId, active, canControl, onFocus }) {
|
|
3926
3911
|
const host = useRef(null);
|
|
3927
3912
|
const section = useRef(null);
|
|
@@ -4000,11 +3985,12 @@ function TerminalPane({ pane, machineId, threadId, active, canControl, onFocus }
|
|
|
4000
3985
|
});
|
|
4001
3986
|
const measuredViewport = () => {
|
|
4002
3987
|
try {
|
|
4003
|
-
return fit.proposeDimensions();
|
|
3988
|
+
return finiteSize(fit.proposeDimensions());
|
|
4004
3989
|
} catch {
|
|
4005
3990
|
return;
|
|
4006
3991
|
}
|
|
4007
3992
|
};
|
|
3993
|
+
const hostIsLaidOut = () => !!host.current?.isConnected && host.current.clientWidth > 0 && host.current.clientHeight > 0;
|
|
4008
3994
|
const viewport = () => {
|
|
4009
3995
|
const { cols, rows } = measuredViewport() ?? estimatedViewport();
|
|
4010
3996
|
return {
|
|
@@ -4034,13 +4020,14 @@ function TerminalPane({ pane, machineId, threadId, active, canControl, onFocus }
|
|
|
4034
4020
|
const resize = new ResizeObserver(() => {
|
|
4035
4021
|
clearTimeout(timer);
|
|
4036
4022
|
timer = setTimeout(() => {
|
|
4023
|
+
if (!hostIsLaidOut()) return;
|
|
4037
4024
|
const { cols, rows } = viewport();
|
|
4038
4025
|
control.current?.resize(cols, rows);
|
|
4039
4026
|
}, 150);
|
|
4040
4027
|
});
|
|
4041
4028
|
resize.observe(host.current);
|
|
4042
4029
|
const claimSize = () => {
|
|
4043
|
-
if (document.visibilityState !== "visible" || !document.hasFocus()) return;
|
|
4030
|
+
if (document.visibilityState !== "visible" || !document.hasFocus() || !hostIsLaidOut()) return;
|
|
4044
4031
|
const { cols, rows } = viewport();
|
|
4045
4032
|
control.current?.resize(cols, rows, true);
|
|
4046
4033
|
};
|
|
@@ -4216,70 +4203,6 @@ function TerminalPane({ pane, machineId, threadId, active, canControl, onFocus }
|
|
|
4216
4203
|
});
|
|
4217
4204
|
}
|
|
4218
4205
|
//#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
4206
|
//#region src/components/LaunchProgress.tsx
|
|
4284
4207
|
function LaunchProgress({ thread }) {
|
|
4285
4208
|
const failed = launchFailed(thread);
|
|
@@ -4327,51 +4250,109 @@ function LaunchProgress({ thread }) {
|
|
|
4327
4250
|
});
|
|
4328
4251
|
}
|
|
4329
4252
|
//#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 }) {
|
|
4253
|
+
//#region src/components/PaneStrip.tsx
|
|
4254
|
+
var PaneIcon = ({ pane }) => pane.kind === "agent" ? /* @__PURE__ */ jsx(Bot, {
|
|
4255
|
+
size: 14,
|
|
4256
|
+
strokeWidth: 1.8,
|
|
4257
|
+
"aria-hidden": "true"
|
|
4258
|
+
}) : /* @__PURE__ */ jsx(TerminalSquare, {
|
|
4259
|
+
size: 14,
|
|
4260
|
+
strokeWidth: 1.8,
|
|
4261
|
+
"aria-hidden": "true"
|
|
4262
|
+
});
|
|
4263
|
+
function CollapsedPane({ pane, side, onOpen }) {
|
|
4344
4264
|
return /* @__PURE__ */ jsx(Tooltip$1, {
|
|
4345
|
-
label:
|
|
4346
|
-
side: side === "
|
|
4265
|
+
label: `Show ${pane.title}`,
|
|
4266
|
+
side: side === "left" ? "right" : "left",
|
|
4347
4267
|
children: /* @__PURE__ */ jsxs("button", {
|
|
4348
4268
|
type: "button",
|
|
4349
|
-
className:
|
|
4350
|
-
"aria-label":
|
|
4269
|
+
className: "pane-collapsed",
|
|
4270
|
+
"aria-label": `Show ${pane.title}`,
|
|
4351
4271
|
onClick: onOpen,
|
|
4352
|
-
children: [/* @__PURE__ */ jsx(
|
|
4353
|
-
size: 14,
|
|
4354
|
-
strokeWidth: 1.8,
|
|
4355
|
-
"aria-hidden": "true"
|
|
4356
|
-
}), /* @__PURE__ */ jsx("span", { children: "Terminal" })]
|
|
4272
|
+
children: [/* @__PURE__ */ jsx(PaneIcon, { pane }), /* @__PURE__ */ jsx("span", { children: pane.title })]
|
|
4357
4273
|
})
|
|
4358
4274
|
});
|
|
4359
4275
|
}
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4276
|
+
var stripShape = (panes, windowStart) => JSON.stringify([windowStart, panes.map((pane) => pane.id)]);
|
|
4277
|
+
var panesInShape = (shape, panes) => {
|
|
4278
|
+
const [start, ids] = JSON.parse(shape);
|
|
4279
|
+
return {
|
|
4280
|
+
start,
|
|
4281
|
+
panes: ids.flatMap((id) => panes.filter((pane) => pane.id === id))
|
|
4282
|
+
};
|
|
4283
|
+
};
|
|
4284
|
+
/** Only a change of order or window runs as a transition, because a view transition covers the live terminals with snapshots while it animates. */
|
|
4285
|
+
var useAnimatedShape = (panes, windowStart) => panesInShape(useDeferredValue(stripShape(panes, windowStart)), panes);
|
|
4286
|
+
function PaneStrip({ panes: livePanes, windowStart: liveStart, onFocusPane, renderPane }) {
|
|
4287
|
+
const { panes, start } = useAnimatedShape(livePanes, liveStart);
|
|
4288
|
+
const isVisible = (index) => index >= start && index < start + 2;
|
|
4289
|
+
return /* @__PURE__ */ jsx("div", {
|
|
4290
|
+
className: `pane-strip ${panes.length > 1 ? "is-split" : ""}`,
|
|
4291
|
+
children: panes.map((pane, index) => /* @__PURE__ */ jsx(ViewTransition, {
|
|
4292
|
+
default: "pane-move",
|
|
4293
|
+
enter: "pane-enter",
|
|
4294
|
+
exit: "pane-exit",
|
|
4295
|
+
children: isVisible(index) ? /* @__PURE__ */ jsx("div", {
|
|
4296
|
+
className: "pane-slot",
|
|
4297
|
+
children: renderPane(pane)
|
|
4298
|
+
}) : /* @__PURE__ */ jsx(CollapsedPane, {
|
|
4299
|
+
pane,
|
|
4300
|
+
side: index < start ? "left" : "right",
|
|
4301
|
+
onOpen: () => onFocusPane(pane.id)
|
|
4302
|
+
})
|
|
4303
|
+
}, pane.id))
|
|
4304
|
+
});
|
|
4305
|
+
}
|
|
4306
|
+
function PaneTabs({ panes, focusedPane, onFocusPane, renderPane }) {
|
|
4307
|
+
const shownId = useDeferredValue(focusedPane);
|
|
4308
|
+
const shown = panes.find((pane) => pane.id === shownId) ?? panes[0];
|
|
4309
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
4310
|
+
className: "pane-tabbed",
|
|
4311
|
+
children: [panes.length > 1 && /* @__PURE__ */ jsx("div", {
|
|
4312
|
+
className: "pane-tabs",
|
|
4313
|
+
role: "tablist",
|
|
4314
|
+
"aria-label": "Panes",
|
|
4315
|
+
children: panes.map((pane) => /* @__PURE__ */ jsxs("button", {
|
|
4316
|
+
type: "button",
|
|
4317
|
+
role: "tab",
|
|
4318
|
+
"aria-selected": pane.id === shown?.id,
|
|
4319
|
+
className: "pane-tab",
|
|
4320
|
+
onClick: () => onFocusPane(pane.id),
|
|
4321
|
+
children: [/* @__PURE__ */ jsx(PaneIcon, { pane }), /* @__PURE__ */ jsx("span", { children: pane.title })]
|
|
4322
|
+
}, pane.id))
|
|
4323
|
+
}), shown && /* @__PURE__ */ jsx(ViewTransition, {
|
|
4324
|
+
enter: "pane-enter",
|
|
4325
|
+
exit: "pane-exit",
|
|
4326
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
4327
|
+
className: "pane-slot",
|
|
4328
|
+
role: "tabpanel",
|
|
4329
|
+
children: renderPane(shown)
|
|
4330
|
+
})
|
|
4331
|
+
}, shown.id)]
|
|
4332
|
+
});
|
|
4333
|
+
}
|
|
4334
|
+
//#endregion
|
|
4335
|
+
//#region src/components/ThreadView.tsx
|
|
4336
|
+
function ThreadView({ thread, connected, canControl, onFocusPane, focusedPane, windowStart, narrow }) {
|
|
4337
|
+
const detached = connected && thread.bindingState === "detached";
|
|
4338
|
+
useEffect(() => {
|
|
4339
|
+
if (detached) navigate(`/new?project=${encodeURIComponent(thread.projectId)}`);
|
|
4340
|
+
}, [detached, thread.projectId]);
|
|
4341
|
+
const renderPane = (pane) => pane.terminalId ? /* @__PURE__ */ jsx(TerminalPane, {
|
|
4342
|
+
pane,
|
|
4343
|
+
machineId: thread.machineId,
|
|
4344
|
+
threadId: thread.id,
|
|
4345
|
+
active: focusedPane === pane.id,
|
|
4346
|
+
canControl,
|
|
4347
|
+
onFocus: () => onFocusPane(pane.id)
|
|
4348
|
+
}, pane.terminalId) : null;
|
|
4373
4349
|
if (connected && (isLaunching(thread) || launchFailed(thread))) return /* @__PURE__ */ jsx(LaunchProgress, { thread });
|
|
4374
|
-
const
|
|
4350
|
+
const strip = {
|
|
4351
|
+
panes: thread.panes,
|
|
4352
|
+
focusedPane,
|
|
4353
|
+
onFocusPane,
|
|
4354
|
+
renderPane
|
|
4355
|
+
};
|
|
4375
4356
|
return /* @__PURE__ */ jsxs("main", {
|
|
4376
4357
|
className: "native-thread-layout",
|
|
4377
4358
|
"aria-label": `${thread.title} tab`,
|
|
@@ -4379,37 +4360,17 @@ function ThreadView({ thread, layouts, tabs, connected, canControl, onFocusPane,
|
|
|
4379
4360
|
(!connected || thread.bindingState !== "attached") && /* @__PURE__ */ jsx("p", {
|
|
4380
4361
|
className: "runtime-notice",
|
|
4381
4362
|
role: "status",
|
|
4382
|
-
children: !connected ? "Disconnected; input is disabled." :
|
|
4363
|
+
children: !connected ? "Disconnected; input is disabled." : "Waiting for the terminal to attach…"
|
|
4383
4364
|
}),
|
|
4384
4365
|
connected && thread.bindingState === "attached" && !canControl && /* @__PURE__ */ jsx("p", {
|
|
4385
4366
|
className: "runtime-notice",
|
|
4386
4367
|
role: "status",
|
|
4387
4368
|
children: "Terminal input is unavailable because this Herdr version is not supported."
|
|
4388
4369
|
}),
|
|
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
|
-
})
|
|
4370
|
+
connected && thread.bindingState === "attached" && (narrow ? /* @__PURE__ */ jsx(PaneTabs, { ...strip }) : /* @__PURE__ */ jsx(PaneStrip, {
|
|
4371
|
+
...strip,
|
|
4372
|
+
windowStart
|
|
4373
|
+
}))
|
|
4413
4374
|
]
|
|
4414
4375
|
});
|
|
4415
4376
|
}
|
|
@@ -4423,6 +4384,30 @@ function appShortcutAction(event, shortcuts) {
|
|
|
4423
4384
|
return Object.entries(shortcuts).find(([, combo]) => combo === keyCombo(event))?.[0];
|
|
4424
4385
|
}
|
|
4425
4386
|
//#endregion
|
|
4387
|
+
//#region src/client/pane-window.ts
|
|
4388
|
+
var NARROW_SCREEN = "(max-width: 760px)";
|
|
4389
|
+
function usePaneWindowStart(ui, scope, paneIds, focusedPane) {
|
|
4390
|
+
const stored = useStore(ui, (state) => state.paneWindows[scope] ?? 0);
|
|
4391
|
+
const start = windowStartShowing(paneIds.length, stored, paneIds.indexOf(focusedPane));
|
|
4392
|
+
useEffect(() => {
|
|
4393
|
+
if (start !== stored) ui.getState().showWindow(scope, start);
|
|
4394
|
+
}, [
|
|
4395
|
+
ui,
|
|
4396
|
+
scope,
|
|
4397
|
+
start,
|
|
4398
|
+
stored
|
|
4399
|
+
]);
|
|
4400
|
+
return start;
|
|
4401
|
+
}
|
|
4402
|
+
var subscribeToNarrowScreen = (onChange) => {
|
|
4403
|
+
if (typeof matchMedia !== "function") return () => {};
|
|
4404
|
+
const media = matchMedia(NARROW_SCREEN);
|
|
4405
|
+
media.addEventListener("change", onChange);
|
|
4406
|
+
return () => media.removeEventListener("change", onChange);
|
|
4407
|
+
};
|
|
4408
|
+
var isNarrowScreen = () => typeof matchMedia === "function" && matchMedia(NARROW_SCREEN).matches;
|
|
4409
|
+
var useNarrowScreen = () => useSyncExternalStore(subscribeToNarrowScreen, isNarrowScreen, () => false);
|
|
4410
|
+
//#endregion
|
|
4426
4411
|
//#region src/client/clear-done-on-visit.ts
|
|
4427
4412
|
function useClearDoneOnVisit(thread, paneId, enabled) {
|
|
4428
4413
|
const isDone = thread?.status === "done";
|
|
@@ -4515,32 +4500,37 @@ function RuntimeApp({ page, bootstrap, threadId, projectId }) {
|
|
|
4515
4500
|
window.removeEventListener("popstate", syncCollapsedFromUrl);
|
|
4516
4501
|
};
|
|
4517
4502
|
}, []);
|
|
4518
|
-
const
|
|
4519
|
-
const
|
|
4520
|
-
const
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4503
|
+
const reportPaneFailure = (error) => void alertDialog(error instanceof Error ? error.message : "Herdr could not change the pane.");
|
|
4504
|
+
const windowStart = usePaneWindowStart(ui, scope, thread?.panes.map((pane) => pane.id) ?? [], focusedPane);
|
|
4505
|
+
const narrow = useNarrowScreen();
|
|
4506
|
+
const openTerminal = useCallback(() => {
|
|
4507
|
+
if (!thread || !focusedPane) return;
|
|
4508
|
+
openThreadTerminal(thread, focusedPane).then(({ paneId }) => setFocusedPane(paneId), reportPaneFailure);
|
|
4524
4509
|
}, [
|
|
4525
4510
|
thread,
|
|
4526
|
-
|
|
4511
|
+
focusedPane,
|
|
4527
4512
|
setFocusedPane
|
|
4528
4513
|
]);
|
|
4529
|
-
const
|
|
4530
|
-
if (thread
|
|
4531
|
-
|
|
4514
|
+
const closePane = useCallback(async (paneId = focusedPane) => {
|
|
4515
|
+
if (!thread || !canClosePane(thread, paneId)) return;
|
|
4516
|
+
const pane = thread.panes.find((pane) => pane.id === paneId);
|
|
4517
|
+
const next = paneToFocusAfterClosing(thread.panes, windowStart, paneId);
|
|
4518
|
+
try {
|
|
4519
|
+
let result = await closeThreadPane(thread, paneId, false);
|
|
4520
|
+
if (result.busy && await confirmDialog(`${pane.title} is running a process. Close it anyway?`, {
|
|
4521
|
+
confirmLabel: "Close",
|
|
4522
|
+
danger: true
|
|
4523
|
+
})) result = await closeThreadPane(thread, paneId, true);
|
|
4524
|
+
if (result.closed && next) setFocusedPane(next.id);
|
|
4525
|
+
} catch (error) {
|
|
4526
|
+
reportPaneFailure(error);
|
|
4527
|
+
}
|
|
4532
4528
|
}, [
|
|
4533
4529
|
thread,
|
|
4534
|
-
|
|
4530
|
+
focusedPane,
|
|
4531
|
+
windowStart,
|
|
4535
4532
|
setFocusedPane
|
|
4536
4533
|
]);
|
|
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
4534
|
const toggleSidebar = useCallback(() => {
|
|
4545
4535
|
if (window.matchMedia("(max-width: 760px)").matches) {
|
|
4546
4536
|
setMobileOpen((value) => !value);
|
|
@@ -4573,11 +4563,12 @@ function RuntimeApp({ page, bootstrap, threadId, projectId }) {
|
|
|
4573
4563
|
if (!action) return;
|
|
4574
4564
|
event.preventDefault();
|
|
4575
4565
|
if (action === "toggleSidebar") toggleSidebar();
|
|
4576
|
-
if (action === "
|
|
4566
|
+
if (action === "openTerminal") openTerminal();
|
|
4567
|
+
if (action === "closePane") closePane();
|
|
4577
4568
|
if (action === "newThread") navigate(`/new${thread ? `?project=${encodeURIComponent(thread.projectId)}` : ""}`);
|
|
4578
4569
|
if ((action === "nextPane" || action === "previousPane") && thread?.panes.length) {
|
|
4579
4570
|
const index = thread.panes.findIndex((pane) => pane.id === focusedPane);
|
|
4580
|
-
|
|
4571
|
+
setFocusedPane(thread.panes[(index + (action === "nextPane" ? 1 : -1) + thread.panes.length) % thread.panes.length].id);
|
|
4581
4572
|
}
|
|
4582
4573
|
if ((action === "nextThread" || action === "previousThread") && threads.length) {
|
|
4583
4574
|
const index = threads.findIndex((item) => item.id === thread?.id);
|
|
@@ -4592,8 +4583,8 @@ function RuntimeApp({ page, bootstrap, threadId, projectId }) {
|
|
|
4592
4583
|
threads,
|
|
4593
4584
|
paletteOpen,
|
|
4594
4585
|
toggleSidebar,
|
|
4595
|
-
|
|
4596
|
-
|
|
4586
|
+
openTerminal,
|
|
4587
|
+
closePane,
|
|
4597
4588
|
focusedPane,
|
|
4598
4589
|
setFocusedPane
|
|
4599
4590
|
]);
|
|
@@ -4619,13 +4610,12 @@ function RuntimeApp({ page, bootstrap, threadId, projectId }) {
|
|
|
4619
4610
|
children: [
|
|
4620
4611
|
page === "thread" && thread && /* @__PURE__ */ jsx(ThreadView, {
|
|
4621
4612
|
thread,
|
|
4622
|
-
layouts: projections[thread.machineId]?.layouts ?? [],
|
|
4623
|
-
tabs: projections[thread.machineId]?.availableTabs ?? [],
|
|
4624
4613
|
connected: gatewayConnected && !!projections[thread.machineId]?.connected,
|
|
4625
4614
|
canControl: !!machines.find((machine) => machine.id === thread.machineId)?.writable,
|
|
4626
4615
|
focusedPane,
|
|
4627
|
-
onFocusPane:
|
|
4628
|
-
|
|
4616
|
+
onFocusPane: setFocusedPane,
|
|
4617
|
+
windowStart,
|
|
4618
|
+
narrow
|
|
4629
4619
|
}),
|
|
4630
4620
|
page === "new" && /* @__PURE__ */ jsx(NewThreadView, {
|
|
4631
4621
|
projects,
|
|
@@ -4669,13 +4659,13 @@ function RuntimeApp({ page, bootstrap, threadId, projectId }) {
|
|
|
4669
4659
|
mode,
|
|
4670
4660
|
onModeChange: changeMode,
|
|
4671
4661
|
onToggleSidebar: toggleSidebar,
|
|
4672
|
-
onFocusPane:
|
|
4662
|
+
onFocusPane: setFocusedPane,
|
|
4673
4663
|
onTerminalAction: (action) => {
|
|
4674
4664
|
const pane = thread?.panes.find((pane) => pane.id === focusedPane);
|
|
4675
4665
|
if (thread && pane?.terminalId) terminals.command(thread.machineId, thread.id, pane.terminalId, action);
|
|
4676
4666
|
},
|
|
4677
|
-
|
|
4678
|
-
|
|
4667
|
+
onOpenTerminal: openTerminal,
|
|
4668
|
+
onClosePane: () => void closePane()
|
|
4679
4669
|
})
|
|
4680
4670
|
]
|
|
4681
4671
|
});
|
|
@@ -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_D7QqB19a.mjs";
|
|
5
5
|
//#region src/pages/threads/[id].astro
|
|
6
6
|
var _id__exports = /* @__PURE__ */ __exportAll({
|
|
7
7
|
default: () => $$Id,
|
|
@@ -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_D7QqB19a.mjs";
|
|
5
5
|
//#region src/pages/sessions/[session]/tabs/[tab].astro
|
|
6
6
|
var _tab__exports = /* @__PURE__ */ __exportAll({
|
|
7
7
|
default: () => $$Tab,
|
|
@@ -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_D7QqB19a.mjs";
|
|
5
5
|
//#region src/pages/new.astro
|
|
6
6
|
var new_exports = /* @__PURE__ */ __exportAll({
|
|
7
7
|
default: () => $$New,
|
|
@@ -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_D7QqB19a.mjs";
|
|
5
5
|
//#region src/pages/settings.astro
|
|
6
6
|
var settings_exports = /* @__PURE__ */ __exportAll({
|
|
7
7
|
default: () => $$Settings,
|