A terminal panel beside an agent chat
A real shell on the chat's own connection, opened and closed like the inspector and never beside it. The modes govern the model; what a person types is theirs, since they hold the credential and could open the same shell with an ssh client. The model cannot see the panel -- a button copies the output you choose into the composer. The session outlives the socket: closing the panel leaves a build running, and coming back reattaches with the scrollback. Two tabs share one shell and the smaller window decides the size. It ends on an idle timeout, on deleting the chat, on disabling, moving or deleting the connection, and on a restart -- which says why rather than quietly opening a fresh shell that has lost the working directory. The nginx template's `Connection ""` is right for SSE and fails every WebSocket handshake, so `location /` now uses a `map $http_upgrade`; update.sh grows a drift check for it, because the only symptom on a stale vhost is a panel that cannot connect. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -41,6 +41,12 @@
|
||||
: "Switch to Moria (dark)");
|
||||
});
|
||||
|
||||
/* For anything holding colours as values rather than reading them from a
|
||||
variable. The terminal is the only such thing: xterm copies its palette
|
||||
at construction, so switching to Shire would otherwise leave a black
|
||||
rectangle in a light interface. */
|
||||
document.dispatchEvent(new CustomEvent("lembas:theme", { detail: { theme: name } }));
|
||||
|
||||
if (document.body.dataset.authenticated === "true") {
|
||||
fetch("/api/preferences/theme", {
|
||||
method: "POST",
|
||||
@@ -357,7 +363,48 @@
|
||||
revealInstall(false);
|
||||
});
|
||||
|
||||
/* --- Panels ------------------------------------------------------------- */
|
||||
/* A panel can be opened or closed by more than one control -- the button in
|
||||
the topbar and the panel's own Close -- and it can now also be closed by
|
||||
something nobody clicked, because two panels sharing the right-hand side
|
||||
of the screen must not both be open. So the state is applied to the panel
|
||||
and then *every* toggle pointing at it is brought in line. Setting
|
||||
aria-expanded on the clicked button alone left the other one lying. */
|
||||
function syncToggles(selector, open) {
|
||||
var toggles = document.querySelectorAll('[data-toggle="' + selector + '"]');
|
||||
for (var i = 0; i < toggles.length; i++) {
|
||||
toggles[i].setAttribute("aria-expanded", open ? "true" : "false");
|
||||
toggles[i].classList.toggle("is-active", open);
|
||||
}
|
||||
}
|
||||
|
||||
function setPanel(selector, open, group) {
|
||||
var panel = document.querySelector(selector);
|
||||
if (!panel) return;
|
||||
|
||||
/* One at a time down the right-hand side. Not only a narrow-screen
|
||||
concern: a 1280px window with the sidebar, the inspector and the
|
||||
terminal all open leaves the conversation about seventy pixels wide. */
|
||||
if (open && group) {
|
||||
var others = document.querySelectorAll('[data-toggle-group="' + group + '"]');
|
||||
for (var i = 0; i < others.length; i++) {
|
||||
var other = others[i].dataset.toggle;
|
||||
if (other && other !== selector) setPanel(other, false);
|
||||
}
|
||||
}
|
||||
|
||||
panel.toggleAttribute("hidden", !open);
|
||||
syncToggles(selector, open);
|
||||
/* What a panel needs to know it is visible. The terminal listens for this:
|
||||
xterm cannot measure itself inside a hidden element, so it has to be
|
||||
told rather than left to discover. */
|
||||
panel.dispatchEvent(
|
||||
new CustomEvent("lembas:toggle", { bubbles: true, detail: { open: open } })
|
||||
);
|
||||
}
|
||||
|
||||
window.lembas = {
|
||||
setPanel: setPanel,
|
||||
applyTheme: applyTheme,
|
||||
toggleTheme: toggleTheme,
|
||||
copyText: copyText,
|
||||
@@ -410,10 +457,7 @@
|
||||
event.preventDefault();
|
||||
var panel = document.querySelector(toggle.dataset.toggle);
|
||||
if (!panel) return;
|
||||
var nowOpen = panel.hasAttribute("hidden");
|
||||
panel.toggleAttribute("hidden");
|
||||
toggle.setAttribute("aria-expanded", nowOpen ? "true" : "false");
|
||||
toggle.classList.toggle("is-active", nowOpen);
|
||||
setPanel(toggle.dataset.toggle, panel.hasAttribute("hidden"), toggle.dataset.toggleGroup);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user