@oharness/server 0.7.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser-pane.d.ts +48 -0
- package/dist/browser-pane.js +185 -0
- package/dist/browser-pane.js.map +1 -0
- package/dist/files.d.ts +40 -1
- package/dist/files.js +94 -3
- package/dist/files.js.map +1 -1
- package/dist/i18n.js +190 -0
- package/dist/i18n.js.map +1 -1
- package/dist/main.js +20 -0
- package/dist/main.js.map +1 -1
- package/dist/page/client/account.d.ts +8 -0
- package/dist/page/client/account.js +1895 -0
- package/dist/page/client/account.js.map +1 -0
- package/dist/page/client/artifacts.d.ts +7 -0
- package/dist/page/client/artifacts.js +767 -0
- package/dist/page/client/artifacts.js.map +1 -0
- package/dist/page/client/base.d.ts +8 -0
- package/dist/page/client/base.js +404 -0
- package/dist/page/client/base.js.map +1 -0
- package/dist/page/client/browser.d.ts +21 -0
- package/dist/page/client/browser.js +906 -0
- package/dist/page/client/browser.js.map +1 -0
- package/dist/page/client/composer.d.ts +8 -0
- package/dist/page/client/composer.js +1714 -0
- package/dist/page/client/composer.js.map +1 -0
- package/dist/page/client/dialogs.d.ts +7 -0
- package/dist/page/client/dialogs.js +724 -0
- package/dist/page/client/dialogs.js.map +1 -0
- package/dist/page/client/events.d.ts +7 -0
- package/dist/page/client/events.js +288 -0
- package/dist/page/client/events.js.map +1 -0
- package/dist/page/client/files.d.ts +7 -0
- package/dist/page/client/files.js +412 -0
- package/dist/page/client/files.js.map +1 -0
- package/dist/page/client/index.d.ts +16 -0
- package/dist/page/client/index.js +50 -0
- package/dist/page/client/index.js.map +1 -0
- package/dist/page/client/panels.d.ts +7 -0
- package/dist/page/client/panels.js +659 -0
- package/dist/page/client/panels.js.map +1 -0
- package/dist/page/client/pickers.d.ts +7 -0
- package/dist/page/client/pickers.js +624 -0
- package/dist/page/client/pickers.js.map +1 -0
- package/dist/page/client/routines.d.ts +6 -0
- package/dist/page/client/routines.js +928 -0
- package/dist/page/client/routines.js.map +1 -0
- package/dist/page/client/shell.d.ts +6 -0
- package/dist/page/client/shell.js +700 -0
- package/dist/page/client/shell.js.map +1 -0
- package/dist/page/client/state.d.ts +7 -0
- package/dist/page/client/state.js +1283 -0
- package/dist/page/client/state.js.map +1 -0
- package/dist/page/client/terminal.d.ts +7 -0
- package/dist/page/client/terminal.js +347 -0
- package/dist/page/client/terminal.js.map +1 -0
- package/dist/page/client/transcript.d.ts +8 -0
- package/dist/page/client/transcript.js +866 -0
- package/dist/page/client/transcript.js.map +1 -0
- package/dist/page/client/usage.d.ts +7 -0
- package/dist/page/client/usage.js +954 -0
- package/dist/page/client/usage.js.map +1 -0
- package/dist/page/style.d.ts +1 -0
- package/dist/page/style.js +3016 -0
- package/dist/page/style.js.map +1 -0
- package/dist/page.js +96 -13871
- package/dist/page.js.map +1 -1
- package/dist/server.d.ts +24 -0
- package/dist/server.js +492 -23
- package/dist/server.js.map +1 -1
- package/dist/terminal.d.ts +34 -0
- package/dist/terminal.js +211 -0
- package/dist/terminal.js.map +1 -0
- package/package.json +9 -8
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/page/client/state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2vC9B,CAAC"}
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The embedded terminal: a real shell per project, drawn by xterm.js in a
|
|
3
|
+
* panel under the conversation.
|
|
4
|
+
*
|
|
5
|
+
* One part of the client script; see ./index.ts for how the parts join.
|
|
6
|
+
*/
|
|
7
|
+
export const TERMINAL = String.raw `
|
|
8
|
+
// --- terminal ------------------------------------------------------------------
|
|
9
|
+
// The shell lives on the server (terminal.ts) and outlives this panel: hiding
|
|
10
|
+
// it, switching tabs or reloading the page all leave it running, and coming
|
|
11
|
+
// back reattaches with the recent output replayed.
|
|
12
|
+
|
|
13
|
+
const terminalPanel = document.getElementById("terminal");
|
|
14
|
+
const terminalBody = document.getElementById("terminal-body");
|
|
15
|
+
const terminalTabs = document.getElementById("terminal-tabs");
|
|
16
|
+
const terminalWhere = document.getElementById("terminal-where");
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Per project folder, its terminals as tabs: each entry holds the xterm
|
|
20
|
+
* instance, its element and its stream, and the group remembers which tab
|
|
21
|
+
* was in front so coming back to the project finds the same one.
|
|
22
|
+
*/
|
|
23
|
+
const terminalGroups = new Map();
|
|
24
|
+
/** The folder whose terminals are on show. */
|
|
25
|
+
let terminalCwd = null;
|
|
26
|
+
let terminalLib = null;
|
|
27
|
+
|
|
28
|
+
const TERMINAL_HEIGHT_KEY = "oharness.terminalHeight";
|
|
29
|
+
const TERMINAL_MIN = 120;
|
|
30
|
+
|
|
31
|
+
function terminalShownFor(cwd) {
|
|
32
|
+
return !terminalPanel.hidden && terminalCwd === cwd;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** xterm.js, fetched the first time a terminal opens rather than with the page. */
|
|
36
|
+
function loadTerminalLib() {
|
|
37
|
+
terminalLib ??= new Promise((resolve, reject) => {
|
|
38
|
+
const css = document.createElement("link");
|
|
39
|
+
css.rel = "stylesheet";
|
|
40
|
+
css.href = q("/vendor/xterm.css");
|
|
41
|
+
document.head.appendChild(css);
|
|
42
|
+
const script = (src) => new Promise((done, fail) => {
|
|
43
|
+
const node = document.createElement("script");
|
|
44
|
+
node.src = q(src);
|
|
45
|
+
node.onload = done;
|
|
46
|
+
node.onerror = () => fail(new Error("could not load " + src));
|
|
47
|
+
document.head.appendChild(node);
|
|
48
|
+
});
|
|
49
|
+
script("/vendor/xterm.js")
|
|
50
|
+
.then(() => script("/vendor/xterm-fit.js"))
|
|
51
|
+
.then(() => resolve({ Terminal: window.Terminal, FitAddon: window.FitAddon.FitAddon }))
|
|
52
|
+
.catch((err) => { terminalLib = null; reject(err); });
|
|
53
|
+
});
|
|
54
|
+
return terminalLib;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** The page's own colours, read when a terminal is made. */
|
|
58
|
+
function terminalTheme() {
|
|
59
|
+
const css = getComputedStyle(document.body);
|
|
60
|
+
const v = (name, fallback) => css.getPropertyValue(name).trim() || fallback;
|
|
61
|
+
return {
|
|
62
|
+
background: v("--bg", "#111"),
|
|
63
|
+
foreground: v("--fg", "#ddd"),
|
|
64
|
+
cursor: v("--fg", "#ddd"),
|
|
65
|
+
cursorAccent: v("--bg", "#111"),
|
|
66
|
+
selectionBackground: v("--sel", "#444"),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function terminalGroup(cwd) {
|
|
71
|
+
let group = terminalGroups.get(cwd);
|
|
72
|
+
if (!group) terminalGroups.set(cwd, (group = { tabs: [], active: null, loaded: false, seq: 0 }));
|
|
73
|
+
return group;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function terminalActive() {
|
|
77
|
+
const group = terminalCwd && terminalGroups.get(terminalCwd);
|
|
78
|
+
return group ? group.active : undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Show a project's terminals, starting a shell if it has none.
|
|
83
|
+
*
|
|
84
|
+
* Asked again for the project already on show it hides, which is what the
|
|
85
|
+
* tab's button wants; force keeps it open, for the menus.
|
|
86
|
+
*/
|
|
87
|
+
async function openTerminalIn(cwd, force) {
|
|
88
|
+
if (!cwd) return;
|
|
89
|
+
if (terminalShownFor(cwd) && !force) return hideTerminal();
|
|
90
|
+
track("panel_opened", { panel: "terminal" });
|
|
91
|
+
|
|
92
|
+
terminalCwd = cwd;
|
|
93
|
+
terminalWhere.textContent = projectName(cwd);
|
|
94
|
+
terminalWhere.title = cwd;
|
|
95
|
+
terminalPanel.hidden = false;
|
|
96
|
+
document.body.classList.add("terminal-open");
|
|
97
|
+
terminalApplyHeight();
|
|
98
|
+
paintTabs();
|
|
99
|
+
|
|
100
|
+
let lib;
|
|
101
|
+
try {
|
|
102
|
+
lib = await loadTerminalLib();
|
|
103
|
+
} catch (err) {
|
|
104
|
+
return void addNotice(String(err.message || err), "err");
|
|
105
|
+
}
|
|
106
|
+
if (terminalCwd !== cwd) return;
|
|
107
|
+
|
|
108
|
+
const group = terminalGroup(cwd);
|
|
109
|
+
if (!group.loaded) {
|
|
110
|
+
group.loaded = true;
|
|
111
|
+
// Shells this project already has — from before a reload, or from
|
|
112
|
+
// another window — come back as tabs rather than being started again.
|
|
113
|
+
const body = await getJson("/term/list?cwd=" + encodeURIComponent(cwd));
|
|
114
|
+
for (const found of body.terminals || []) {
|
|
115
|
+
const entry = terminalCreate(lib, cwd);
|
|
116
|
+
void terminalAttach(entry, found, true);
|
|
117
|
+
}
|
|
118
|
+
if (!group.tabs.length) await terminalNew(cwd);
|
|
119
|
+
else group.active = group.tabs[group.tabs.length - 1];
|
|
120
|
+
} else if (!group.tabs.length) {
|
|
121
|
+
await terminalNew(cwd);
|
|
122
|
+
}
|
|
123
|
+
if (terminalCwd === cwd) terminalSelect(group.active);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function hideTerminal() {
|
|
127
|
+
terminalPanel.hidden = true;
|
|
128
|
+
document.body.classList.remove("terminal-open");
|
|
129
|
+
paintTabs();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Keep the panel on the project of the tab in front. */
|
|
133
|
+
function terminalFollow(cwd) {
|
|
134
|
+
if (!terminalPanel.hidden && cwd && cwd !== terminalCwd) void openTerminalIn(cwd, true);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Another shell in the project on show, as a new tab in front. */
|
|
138
|
+
async function terminalNew(cwd) {
|
|
139
|
+
const lib = await loadTerminalLib();
|
|
140
|
+
const entry = terminalCreate(lib, cwd);
|
|
141
|
+
terminalGroup(cwd).active = entry;
|
|
142
|
+
terminalSelect(entry);
|
|
143
|
+
await terminalStart(entry);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function terminalCreate(lib, cwd) {
|
|
147
|
+
const group = terminalGroup(cwd);
|
|
148
|
+
const host = el("div", "terminal-host");
|
|
149
|
+
host.hidden = true;
|
|
150
|
+
terminalBody.appendChild(host);
|
|
151
|
+
const term = new lib.Terminal({
|
|
152
|
+
fontFamily: 'ui-monospace, "SF Mono", Menlo, Consolas, "Cascadia Mono", monospace',
|
|
153
|
+
fontSize: isPhone() ? 12 : 13,
|
|
154
|
+
lineHeight: 1.15,
|
|
155
|
+
cursorBlink: true,
|
|
156
|
+
scrollback: 5000,
|
|
157
|
+
theme: terminalTheme(),
|
|
158
|
+
});
|
|
159
|
+
const fit = new lib.FitAddon();
|
|
160
|
+
term.loadAddon(fit);
|
|
161
|
+
term.open(host);
|
|
162
|
+
const entry = {
|
|
163
|
+
cwd, host, term, fit, n: ++group.seq, name: "", id: null, source: null,
|
|
164
|
+
starting: false, exited: false, pending: "", sending: false,
|
|
165
|
+
};
|
|
166
|
+
group.tabs.push(entry);
|
|
167
|
+
|
|
168
|
+
term.onData((data) => {
|
|
169
|
+
// A shell that has exited restarts on the next key, the way a closed
|
|
170
|
+
// terminal tab is reopened by typing into it.
|
|
171
|
+
if (entry.exited) return void terminalStart(entry);
|
|
172
|
+
terminalSend(entry, data);
|
|
173
|
+
});
|
|
174
|
+
term.onResize(({ cols, rows }) => {
|
|
175
|
+
if (entry.id) void api("/term/resize", { id: entry.id, cols, rows });
|
|
176
|
+
});
|
|
177
|
+
return entry;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Bring one tab to the front of its project's panel. */
|
|
181
|
+
function terminalSelect(entry) {
|
|
182
|
+
if (!entry) return paintTerminalTabs();
|
|
183
|
+
const group = terminalGroup(entry.cwd);
|
|
184
|
+
group.active = entry;
|
|
185
|
+
for (const [cwd, other] of terminalGroups) {
|
|
186
|
+
for (const tab of other.tabs) tab.host.hidden = !(cwd === terminalCwd && tab === entry);
|
|
187
|
+
}
|
|
188
|
+
paintTerminalTabs();
|
|
189
|
+
terminalFit(entry);
|
|
190
|
+
entry.term.focus();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function paintTerminalTabs() {
|
|
194
|
+
terminalTabs.replaceChildren();
|
|
195
|
+
const group = terminalCwd && terminalGroups.get(terminalCwd);
|
|
196
|
+
if (!group) return;
|
|
197
|
+
for (const entry of group.tabs) {
|
|
198
|
+
const tab = el("div", "terminal-tab" + (entry === group.active ? " on" : ""));
|
|
199
|
+
tab.setAttribute("role", "tab");
|
|
200
|
+
tab.setAttribute("aria-selected", String(entry === group.active));
|
|
201
|
+
tab.appendChild(svgIcon("command"));
|
|
202
|
+
tab.appendChild(el("span", "terminal-tab-label", (entry.name || t("terminal.title")) + (entry.n > 1 ? " " + entry.n : "")));
|
|
203
|
+
const close = el("button", "terminal-tab-x");
|
|
204
|
+
close.type = "button";
|
|
205
|
+
close.title = t("terminal.close");
|
|
206
|
+
close.setAttribute("aria-label", t("terminal.close"));
|
|
207
|
+
close.appendChild(svgIcon("close"));
|
|
208
|
+
close.addEventListener("click", (event) => {
|
|
209
|
+
event.stopPropagation();
|
|
210
|
+
terminalClose(entry);
|
|
211
|
+
});
|
|
212
|
+
tab.appendChild(close);
|
|
213
|
+
tab.addEventListener("click", () => terminalSelect(entry));
|
|
214
|
+
// A middle click closes, as it does on a browser's tabs.
|
|
215
|
+
tab.addEventListener("auxclick", (event) => { if (event.button === 1) terminalClose(entry); });
|
|
216
|
+
terminalTabs.appendChild(tab);
|
|
217
|
+
}
|
|
218
|
+
const active = terminalTabs.querySelector(".terminal-tab.on");
|
|
219
|
+
if (active) active.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** End one shell and drop its tab; the last one closing hides the panel. */
|
|
223
|
+
function terminalClose(entry) {
|
|
224
|
+
const group = terminalGroup(entry.cwd);
|
|
225
|
+
if (entry.id) void api("/term/close", { id: entry.id });
|
|
226
|
+
if (entry.source) entry.source.close();
|
|
227
|
+
entry.id = null;
|
|
228
|
+
entry.term.dispose();
|
|
229
|
+
entry.host.remove();
|
|
230
|
+
const index = group.tabs.indexOf(entry);
|
|
231
|
+
group.tabs.splice(index, 1);
|
|
232
|
+
if (group.active === entry) group.active = group.tabs[Math.min(index, group.tabs.length - 1)] || null;
|
|
233
|
+
if (!group.tabs.length) return hideTerminal();
|
|
234
|
+
terminalSelect(group.active);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Keystrokes, in order.
|
|
239
|
+
*
|
|
240
|
+
* One request in flight at a time, with whatever was typed meanwhile sent
|
|
241
|
+
* together after it: two POSTs racing each other could land out of order, and
|
|
242
|
+
* a shell that receives "sl" for "ls" is worse than one that is a beat slow.
|
|
243
|
+
*/
|
|
244
|
+
function terminalSend(entry, data) {
|
|
245
|
+
entry.pending += data;
|
|
246
|
+
if (entry.sending || !entry.id) return;
|
|
247
|
+
entry.sending = true;
|
|
248
|
+
const flush = async () => {
|
|
249
|
+
while (entry.pending && entry.id) {
|
|
250
|
+
const chunk = entry.pending;
|
|
251
|
+
entry.pending = "";
|
|
252
|
+
await api("/term/input", { id: entry.id, data: chunk });
|
|
253
|
+
}
|
|
254
|
+
entry.sending = false;
|
|
255
|
+
};
|
|
256
|
+
void flush();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** A shell for this tab — its first, or a new one after the last exited. */
|
|
260
|
+
async function terminalStart(entry) {
|
|
261
|
+
if (entry.starting) return;
|
|
262
|
+
entry.starting = true;
|
|
263
|
+
const replace = entry.id;
|
|
264
|
+
if (entry.source) entry.source.close();
|
|
265
|
+
entry.source = null;
|
|
266
|
+
entry.id = null;
|
|
267
|
+
if (replace) entry.term.reset();
|
|
268
|
+
terminalFit(entry);
|
|
269
|
+
const body = await api("/term/open", {
|
|
270
|
+
cwd: entry.cwd, cols: entry.term.cols, rows: entry.term.rows, ...(replace ? { replace } : {}),
|
|
271
|
+
});
|
|
272
|
+
entry.starting = false;
|
|
273
|
+
if (!body || body.error) {
|
|
274
|
+
entry.term.writeln("\x1b[31m" + ((body && body.error) || "could not start a shell") + "\x1b[0m");
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
terminalAttach(entry, body, false);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** Follow one server-side shell: its output in, and a resize to match. */
|
|
281
|
+
function terminalAttach(entry, info, replay) {
|
|
282
|
+
entry.id = info.id;
|
|
283
|
+
entry.name = info.name || entry.name;
|
|
284
|
+
entry.exited = false;
|
|
285
|
+
paintTerminalTabs();
|
|
286
|
+
const source = new EventSource(q("/term/stream?id=" + encodeURIComponent(info.id)));
|
|
287
|
+
entry.source = source;
|
|
288
|
+
source.onmessage = (message) => {
|
|
289
|
+
const event = JSON.parse(message.data);
|
|
290
|
+
if (event.type === "data") entry.term.write(event.data);
|
|
291
|
+
else if (event.type === "exit") {
|
|
292
|
+
entry.exited = true;
|
|
293
|
+
entry.term.writeln("\r\n\x1b[2m" + t("terminal.exited") + "\x1b[0m");
|
|
294
|
+
} else if (event.type === "closed") {
|
|
295
|
+
source.close();
|
|
296
|
+
if (entry.id === info.id) entry.id = null;
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
// A reattached shell was sized for whatever window it was last drawn in.
|
|
300
|
+
if (replay) void api("/term/resize", { id: info.id, cols: entry.term.cols, rows: entry.term.rows });
|
|
301
|
+
if (entry.pending) terminalSend(entry, "");
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function terminalFit(entry) {
|
|
305
|
+
if (terminalPanel.hidden || entry.host.hidden) return;
|
|
306
|
+
try { entry.fit.fit(); } catch { /* not laid out yet */ }
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// --- its height ------------------------------------------------------------------
|
|
310
|
+
|
|
311
|
+
/** The most the terminal may take, leaving the conversation a readable strip. */
|
|
312
|
+
function terminalMaxHeight() {
|
|
313
|
+
const main = document.getElementById("main").getBoundingClientRect().height;
|
|
314
|
+
return Math.max(TERMINAL_MIN, main - (isPhone() ? 140 : 220));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function terminalApplyHeight(px) {
|
|
318
|
+
const saved = px ?? storedSize(TERMINAL_HEIGHT_KEY);
|
|
319
|
+
if (!saved) return void terminalPanel.style.removeProperty("--terminal-height");
|
|
320
|
+
const height = clamp(saved, TERMINAL_MIN, terminalMaxHeight());
|
|
321
|
+
terminalPanel.style.setProperty("--terminal-height", height + "px");
|
|
322
|
+
return height;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
let terminalDragged;
|
|
326
|
+
dragHandle(document.getElementById("terminal-resizer"), {
|
|
327
|
+
axis: "y",
|
|
328
|
+
start: () => terminalPanel.getBoundingClientRect().height,
|
|
329
|
+
move: (dx, dy, start) => { terminalDragged = terminalApplyHeight(start - dy); },
|
|
330
|
+
end: () => storeSize(TERMINAL_HEIGHT_KEY, terminalDragged),
|
|
331
|
+
reset: () => { storeSize(TERMINAL_HEIGHT_KEY); terminalApplyHeight(); },
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// Every size change reaches the shell as rows and columns: a drag, the window,
|
|
335
|
+
// the sidebar folding, a phone's keyboard coming up.
|
|
336
|
+
new ResizeObserver(() => {
|
|
337
|
+
const entry = terminalActive();
|
|
338
|
+
if (entry) terminalFit(entry);
|
|
339
|
+
}).observe(terminalBody);
|
|
340
|
+
window.addEventListener("resize", () => { if (!terminalPanel.hidden) terminalApplyHeight(); });
|
|
341
|
+
|
|
342
|
+
document.getElementById("terminal-hide").addEventListener("click", hideTerminal);
|
|
343
|
+
document.getElementById("terminal-new").addEventListener("click", () => {
|
|
344
|
+
if (terminalCwd) void terminalNew(terminalCwd);
|
|
345
|
+
});
|
|
346
|
+
`;
|
|
347
|
+
//# sourceMappingURL=terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.js","sourceRoot":"","sources":["../../../src/page/client/terminal.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmVjC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The transcript itself, the tool cards it is made of, and the session tabs
|
|
3
|
+
* above it. Tool presentation is shared with the approval dialog, so a call
|
|
4
|
+
* reads the same whether it is waiting on an answer or already done.
|
|
5
|
+
*
|
|
6
|
+
* One part of the client script; see ./index.ts for how the parts join.
|
|
7
|
+
*/
|
|
8
|
+
export declare const TRANSCRIPT: string;
|