@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,412 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The files pane — a project's tree beside the file picked from it — and the
|
|
3
|
+
* terminal that opens in the same folder.
|
|
4
|
+
*
|
|
5
|
+
* One part of the client script; see ./index.ts for how the parts join.
|
|
6
|
+
*/
|
|
7
|
+
export const FILES = String.raw `
|
|
8
|
+
// --- files pane --------------------------------------------------------------
|
|
9
|
+
// Opened from a project's menu in the sidebar or from a session's tab, and
|
|
10
|
+
// always about one folder: the tree is fetched a directory at a time as it is
|
|
11
|
+
// unfolded, so a repository with a node_modules costs nothing until somebody
|
|
12
|
+
// actually walks into it.
|
|
13
|
+
|
|
14
|
+
const explorer = document.getElementById("explorer");
|
|
15
|
+
const explorerTitle = document.getElementById("explorer-title");
|
|
16
|
+
const explorerTree = document.getElementById("explorer-tree");
|
|
17
|
+
const explorerView = document.getElementById("explorer-view");
|
|
18
|
+
|
|
19
|
+
/** The project folder on show, absolute; null while the pane has never opened. */
|
|
20
|
+
let explorerCwd = null;
|
|
21
|
+
/** Each listed folder's children, keyed by path relative to the project. */
|
|
22
|
+
let explorerChildren = new Map();
|
|
23
|
+
/** Unfolded folders, remembered per project so a return finds them as left. */
|
|
24
|
+
const explorerOpenDirs = new Map();
|
|
25
|
+
/** The file on the right, relative to the project. */
|
|
26
|
+
let explorerSelected = null;
|
|
27
|
+
/** Markdown shows rendered unless somebody asked for the source. */
|
|
28
|
+
let explorerSource = false;
|
|
29
|
+
|
|
30
|
+
/** Past this a file is shown as plain text: the highlighter is one regex pass. */
|
|
31
|
+
const EXPLORER_HIGHLIGHT_MAX = 200 * 1024;
|
|
32
|
+
const EXPLORER_MARKDOWN = /\.(md|markdown|mdx)$/i;
|
|
33
|
+
|
|
34
|
+
function explorerIsOpen() {
|
|
35
|
+
return explorer.classList.contains("open");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function explorerDirs() {
|
|
39
|
+
let dirs = explorerOpenDirs.get(explorerCwd);
|
|
40
|
+
if (!dirs) explorerOpenDirs.set(explorerCwd, (dirs = new Set()));
|
|
41
|
+
return dirs;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Show one project's files.
|
|
46
|
+
*
|
|
47
|
+
* The same project asked for twice is a toggle, which is what the tab's button
|
|
48
|
+
* wants; the project menu passes force so that choosing "Files" never closes
|
|
49
|
+
* the thing it just asked to see.
|
|
50
|
+
*/
|
|
51
|
+
async function openExplorer(cwd, force) {
|
|
52
|
+
if (!cwd) return;
|
|
53
|
+
if (explorerIsOpen() && explorerCwd === cwd && !force) return closeExplorer();
|
|
54
|
+
track("panel_opened", { panel: "files" });
|
|
55
|
+
|
|
56
|
+
// One column beside the transcript at a time: the review pane, the artifact
|
|
57
|
+
// drawer and the browser are all the same shape, and two of them open at
|
|
58
|
+
// once leaves the conversation a strip down the middle.
|
|
59
|
+
review.classList.remove("open");
|
|
60
|
+
shelf.classList.remove("open");
|
|
61
|
+
if (browserPane.classList.contains("open")) browserOpen(false);
|
|
62
|
+
|
|
63
|
+
if (explorerCwd !== cwd) {
|
|
64
|
+
explorerCwd = cwd;
|
|
65
|
+
explorerChildren = new Map();
|
|
66
|
+
explorerSelected = null;
|
|
67
|
+
explorerSource = false;
|
|
68
|
+
}
|
|
69
|
+
explorerTitle.textContent = projectName(cwd);
|
|
70
|
+
explorerTitle.title = cwd;
|
|
71
|
+
explorer.classList.add("open");
|
|
72
|
+
paintTabs();
|
|
73
|
+
await explorerLoad();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function closeExplorer() {
|
|
77
|
+
explorer.classList.remove("open");
|
|
78
|
+
paintTabs();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Keep the pane on the project of whichever tab is in front. */
|
|
82
|
+
function explorerFollow(cwd) {
|
|
83
|
+
if (explorerIsOpen() && cwd && cwd !== explorerCwd) void openExplorer(cwd, true);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Every folder on screen, fetched again: what the refresh button does. */
|
|
87
|
+
async function explorerLoad() {
|
|
88
|
+
const cwd = explorerCwd;
|
|
89
|
+
const dirs = ["", ...explorerDirs()];
|
|
90
|
+
const bodies = await Promise.all(dirs.map((dir) => explorerFetch(dir)));
|
|
91
|
+
if (cwd !== explorerCwd) return;
|
|
92
|
+
explorerChildren = new Map();
|
|
93
|
+
dirs.forEach((dir, index) => {
|
|
94
|
+
const body = bodies[index];
|
|
95
|
+
if (body.error) explorerDirs().delete(dir);
|
|
96
|
+
else explorerChildren.set(dir, body.entries || []);
|
|
97
|
+
});
|
|
98
|
+
paintExplorerTree();
|
|
99
|
+
if (explorerSelected) await explorerShow(explorerSelected);
|
|
100
|
+
else explorerView.replaceChildren(el("p", "muted explorer-hint", t("files.pick")));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function explorerFetch(dir) {
|
|
104
|
+
return getJson("/files/tree?cwd=" + encodeURIComponent(explorerCwd) + "&dir=" + encodeURIComponent(dir));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function explorerToggleDir(dir) {
|
|
108
|
+
const dirs = explorerDirs();
|
|
109
|
+
if (dirs.has(dir)) {
|
|
110
|
+
dirs.delete(dir);
|
|
111
|
+
return paintExplorerTree();
|
|
112
|
+
}
|
|
113
|
+
dirs.add(dir);
|
|
114
|
+
if (!explorerChildren.has(dir)) {
|
|
115
|
+
const cwd = explorerCwd;
|
|
116
|
+
const body = await explorerFetch(dir);
|
|
117
|
+
if (cwd !== explorerCwd) return;
|
|
118
|
+
if (body.error) {
|
|
119
|
+
dirs.delete(dir);
|
|
120
|
+
return void addNotice(body.error, "err");
|
|
121
|
+
}
|
|
122
|
+
explorerChildren.set(dir, body.entries || []);
|
|
123
|
+
}
|
|
124
|
+
paintExplorerTree();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function paintExplorerTree() {
|
|
128
|
+
const scroll = explorerTree.scrollTop;
|
|
129
|
+
explorerTree.replaceChildren();
|
|
130
|
+
const root = explorerChildren.get("") || [];
|
|
131
|
+
if (!root.length) explorerTree.appendChild(el("p", "muted explorer-hint", t("files.empty")));
|
|
132
|
+
const dirs = explorerDirs();
|
|
133
|
+
|
|
134
|
+
const walk = (entries, depth) => {
|
|
135
|
+
for (const entry of entries) {
|
|
136
|
+
const row = el("button", "explorer-row" + (entry.path === explorerSelected ? " on" : ""));
|
|
137
|
+
row.type = "button";
|
|
138
|
+
row.setAttribute("role", "treeitem");
|
|
139
|
+
row.style.paddingLeft = 8 + depth * 14 + "px";
|
|
140
|
+
row.title = entry.path;
|
|
141
|
+
const open = entry.dir && dirs.has(entry.path);
|
|
142
|
+
if (entry.dir) row.setAttribute("aria-expanded", String(open));
|
|
143
|
+
const caret = el("span", "explorer-caret");
|
|
144
|
+
if (entry.dir) caret.appendChild(svgIcon(open ? "caret" : "chevronRight"));
|
|
145
|
+
row.appendChild(caret);
|
|
146
|
+
row.appendChild(svgIcon(entry.dir ? (open ? "folderOpen" : "folder") : "file"));
|
|
147
|
+
row.appendChild(el("span", "explorer-name", entry.name));
|
|
148
|
+
row.addEventListener("click", () => {
|
|
149
|
+
if (entry.dir) void explorerToggleDir(entry.path);
|
|
150
|
+
else void explorerShow(entry.path);
|
|
151
|
+
});
|
|
152
|
+
explorerTree.appendChild(row);
|
|
153
|
+
if (open) walk(explorerChildren.get(entry.path) || [], depth + 1);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
walk(root, 0);
|
|
157
|
+
explorerTree.scrollTop = scroll;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function explorerSize(bytes) {
|
|
161
|
+
if (bytes < 1024) return bytes + " B";
|
|
162
|
+
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB";
|
|
163
|
+
return (bytes / 1024 / 1024).toFixed(1) + " MB";
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function explorerShow(file) {
|
|
167
|
+
const cwd = explorerCwd;
|
|
168
|
+
if (explorerSelected !== file) explorerSource = false;
|
|
169
|
+
explorerSelected = file;
|
|
170
|
+
for (const row of explorerTree.children) row.classList.toggle("on", row.title === file);
|
|
171
|
+
|
|
172
|
+
const body = await getJson("/files/read?cwd=" + encodeURIComponent(cwd) + "&path=" + encodeURIComponent(file));
|
|
173
|
+
if (cwd !== explorerCwd || file !== explorerSelected) return;
|
|
174
|
+
explorerView.replaceChildren();
|
|
175
|
+
if (body.error) return void explorerView.appendChild(el("p", "muted explorer-hint", body.error));
|
|
176
|
+
|
|
177
|
+
const bar = el("div", "explorer-bar");
|
|
178
|
+
bar.appendChild(el("span", "explorer-path mono", body.path));
|
|
179
|
+
bar.appendChild(el("span", "explorer-size", explorerSize(body.size)));
|
|
180
|
+
|
|
181
|
+
const markdown = body.kind === "text" && EXPLORER_MARKDOWN.test(body.path);
|
|
182
|
+
if (markdown) {
|
|
183
|
+
const flip = el("button", "explorer-btn", t(explorerSource ? "files.rendered" : "files.source"));
|
|
184
|
+
flip.type = "button";
|
|
185
|
+
flip.addEventListener("click", () => {
|
|
186
|
+
explorerSource = !explorerSource;
|
|
187
|
+
void explorerShow(file);
|
|
188
|
+
});
|
|
189
|
+
bar.appendChild(flip);
|
|
190
|
+
}
|
|
191
|
+
// A reference is a path relative to the session's own tree, so it is only
|
|
192
|
+
// offered when the tree on show is that one.
|
|
193
|
+
const active = activeId && panes.get(activeId);
|
|
194
|
+
if (active && active.cwd === cwd) {
|
|
195
|
+
const mention = el("button", "explorer-btn", t("files.mention"));
|
|
196
|
+
mention.type = "button";
|
|
197
|
+
mention.addEventListener("click", () => {
|
|
198
|
+
addRef("file", body.path);
|
|
199
|
+
input.focus();
|
|
200
|
+
});
|
|
201
|
+
bar.appendChild(mention);
|
|
202
|
+
}
|
|
203
|
+
explorerView.appendChild(bar);
|
|
204
|
+
|
|
205
|
+
const content = el("div", "explorer-content");
|
|
206
|
+
const raw = q("/files/raw?cwd=" + encodeURIComponent(cwd) + "&path=" + encodeURIComponent(body.path));
|
|
207
|
+
if (body.kind === "image") {
|
|
208
|
+
const img = el("img", "explorer-img");
|
|
209
|
+
img.alt = body.path;
|
|
210
|
+
img.src = raw;
|
|
211
|
+
content.appendChild(img);
|
|
212
|
+
} else if (body.kind === "video" || body.kind === "audio") {
|
|
213
|
+
const media = el(body.kind, "explorer-" + body.kind);
|
|
214
|
+
media.controls = true;
|
|
215
|
+
media.preload = "metadata";
|
|
216
|
+
media.src = raw;
|
|
217
|
+
content.appendChild(media);
|
|
218
|
+
} else if (body.kind === "pdf") {
|
|
219
|
+
content.classList.add("explorer-fill");
|
|
220
|
+
const frame = el("iframe", "explorer-pdf");
|
|
221
|
+
frame.title = body.path;
|
|
222
|
+
frame.src = raw;
|
|
223
|
+
content.appendChild(frame);
|
|
224
|
+
} else if (body.kind === "text" && markdown && !explorerSource) {
|
|
225
|
+
const md = renderMarkdown(body.content);
|
|
226
|
+
md.classList.add("explorer-md");
|
|
227
|
+
content.appendChild(md);
|
|
228
|
+
} else if (body.kind === "text") {
|
|
229
|
+
content.appendChild(explorerCode(body.content, body.path));
|
|
230
|
+
} else {
|
|
231
|
+
content.appendChild(el("p", "muted explorer-hint",
|
|
232
|
+
body.kind === "large" ? t("files.large", { size: explorerSize(body.size) }) : t("files.binary")));
|
|
233
|
+
}
|
|
234
|
+
explorerView.appendChild(content);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Source with a line-number gutter; the numbers are not part of a selection. */
|
|
238
|
+
function explorerCode(source, file) {
|
|
239
|
+
const text = source.replace(/\r\n?/g, "\n");
|
|
240
|
+
const lines = text.endsWith("\n") ? text.slice(0, -1).split("\n") : text.split("\n");
|
|
241
|
+
const wrap = el("div", "explorer-code");
|
|
242
|
+
const gutter = el("pre", "explorer-gutter");
|
|
243
|
+
gutter.setAttribute("aria-hidden", "true");
|
|
244
|
+
gutter.textContent = lines.map((_, index) => index + 1).join("\n");
|
|
245
|
+
const pre = el("pre", "code explorer-src");
|
|
246
|
+
const ext = (file.match(/\.([^./]+)$/) || [])[1] || "";
|
|
247
|
+
if (text.length > EXPLORER_HIGHLIGHT_MAX) pre.textContent = text;
|
|
248
|
+
else pre.appendChild(highlight(text, ext));
|
|
249
|
+
wrap.appendChild(gutter);
|
|
250
|
+
wrap.appendChild(pre);
|
|
251
|
+
return wrap;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** The tab's two ways into its tree: the files pane and a terminal. */
|
|
255
|
+
function tabProjectButtons(id, cwd) {
|
|
256
|
+
const files = el("button", "tab-tool");
|
|
257
|
+
files.type = "button";
|
|
258
|
+
files.title = t("files.open");
|
|
259
|
+
files.setAttribute("aria-label", t("files.open"));
|
|
260
|
+
files.classList.toggle("on", id === activeId && explorerIsOpen() && explorerCwd === cwd);
|
|
261
|
+
files.appendChild(svgIcon("folder"));
|
|
262
|
+
files.addEventListener("click", async (event) => {
|
|
263
|
+
event.stopPropagation();
|
|
264
|
+
if (id !== activeId) {
|
|
265
|
+
await focusTab(id);
|
|
266
|
+
return void openExplorer(cwd, true);
|
|
267
|
+
}
|
|
268
|
+
void openExplorer(cwd);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const term = el("button", "tab-tool");
|
|
272
|
+
term.type = "button";
|
|
273
|
+
term.title = t("files.terminalTab");
|
|
274
|
+
term.setAttribute("aria-label", t("files.terminalTab"));
|
|
275
|
+
term.appendChild(svgIcon("command"));
|
|
276
|
+
term.classList.toggle("on", id === activeId && terminalShownFor(cwd));
|
|
277
|
+
term.addEventListener("click", async (event) => {
|
|
278
|
+
event.stopPropagation();
|
|
279
|
+
if (id !== activeId) {
|
|
280
|
+
await focusTab(id);
|
|
281
|
+
return void openTerminalIn(cwd, true);
|
|
282
|
+
}
|
|
283
|
+
void openTerminalIn(cwd);
|
|
284
|
+
});
|
|
285
|
+
return [files, term];
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
document.getElementById("explorer-close").addEventListener("click", closeExplorer);
|
|
289
|
+
document.getElementById("explorer-refresh").addEventListener("click", () => void explorerLoad());
|
|
290
|
+
document.getElementById("explorer-terminal").addEventListener("click", () => void openTerminalIn(explorerCwd, true));
|
|
291
|
+
|
|
292
|
+
// --- dragging a size -----------------------------------------------------------
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* A handle that resizes something while it is dragged.
|
|
296
|
+
*
|
|
297
|
+
* Pointer events, so a finger drags it as well as a mouse; the move handler is
|
|
298
|
+
* given the distance from where the drag started and does its own clamping.
|
|
299
|
+
* Arrow keys nudge it for anyone not using a pointer at all.
|
|
300
|
+
*/
|
|
301
|
+
function dragHandle(handle, opts) {
|
|
302
|
+
handle.addEventListener("pointerdown", (event) => {
|
|
303
|
+
if (event.button !== 0 || (opts.enabled && !opts.enabled())) return;
|
|
304
|
+
event.preventDefault();
|
|
305
|
+
const x0 = event.clientX;
|
|
306
|
+
const y0 = event.clientY;
|
|
307
|
+
const state = opts.start();
|
|
308
|
+
try { handle.setPointerCapture(event.pointerId); } catch { /* the window listeners keep it going */ }
|
|
309
|
+
document.body.classList.add("dragging", opts.axis === "x" ? "dragging-x" : "dragging-y");
|
|
310
|
+
const move = (next) => opts.move(next.clientX - x0, next.clientY - y0, state);
|
|
311
|
+
const done = () => {
|
|
312
|
+
window.removeEventListener("pointermove", move);
|
|
313
|
+
window.removeEventListener("pointerup", done);
|
|
314
|
+
window.removeEventListener("pointercancel", done);
|
|
315
|
+
document.body.classList.remove("dragging", "dragging-x", "dragging-y");
|
|
316
|
+
if (opts.end) opts.end();
|
|
317
|
+
};
|
|
318
|
+
window.addEventListener("pointermove", move);
|
|
319
|
+
window.addEventListener("pointerup", done);
|
|
320
|
+
window.addEventListener("pointercancel", done);
|
|
321
|
+
});
|
|
322
|
+
handle.addEventListener("keydown", (event) => {
|
|
323
|
+
const keys = opts.axis === "x" ? ["ArrowLeft", "ArrowRight"] : ["ArrowUp", "ArrowDown"];
|
|
324
|
+
if (!keys.includes(event.key) || (opts.enabled && !opts.enabled())) return;
|
|
325
|
+
event.preventDefault();
|
|
326
|
+
const state = opts.start();
|
|
327
|
+
const step = event.key === keys[0] ? -16 : 16;
|
|
328
|
+
opts.move(opts.axis === "x" ? step : 0, opts.axis === "x" ? 0 : step, state);
|
|
329
|
+
if (opts.end) opts.end();
|
|
330
|
+
});
|
|
331
|
+
if (opts.reset) handle.addEventListener("dblclick", opts.reset);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/** A remembered number, or undefined; storage can be missing or refuse. */
|
|
335
|
+
function storedSize(key) {
|
|
336
|
+
try {
|
|
337
|
+
const n = Number(localStorage.getItem(key));
|
|
338
|
+
return Number.isFinite(n) && n > 0 ? n : undefined;
|
|
339
|
+
} catch {
|
|
340
|
+
return undefined;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function storeSize(key, value) {
|
|
344
|
+
try {
|
|
345
|
+
if (value === undefined) localStorage.removeItem(key);
|
|
346
|
+
else localStorage.setItem(key, String(Math.round(value)));
|
|
347
|
+
} catch { /* the size still holds for this page */ }
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const clamp = (value, min, max) => Math.min(Math.max(value, min), Math.max(min, max));
|
|
351
|
+
|
|
352
|
+
// The pane's width, from its left edge. Desktop only: on a phone it is the
|
|
353
|
+
// whole screen.
|
|
354
|
+
const EXPLORER_WIDTH_KEY = "oharness.explorerWidth";
|
|
355
|
+
const EXPLORER_TREE_KEY = "oharness.explorerTreeWidth";
|
|
356
|
+
const EXPLORER_TREE_H_KEY = "oharness.explorerTreeHeight";
|
|
357
|
+
|
|
358
|
+
function explorerSetWidth(px) {
|
|
359
|
+
const split = document.getElementById("split").getBoundingClientRect().width;
|
|
360
|
+
const width = clamp(px, 360, split - 360);
|
|
361
|
+
explorer.style.setProperty("--explorer-width", width + "px");
|
|
362
|
+
return width;
|
|
363
|
+
}
|
|
364
|
+
function explorerSetTree(px) {
|
|
365
|
+
const body = document.getElementById("explorer-body").getBoundingClientRect();
|
|
366
|
+
if (isPhone()) {
|
|
367
|
+
const height = clamp(px, 80, body.height - 120);
|
|
368
|
+
explorer.style.setProperty("--explorer-tree-height", height + "px");
|
|
369
|
+
return height;
|
|
370
|
+
}
|
|
371
|
+
const width = clamp(px, 140, body.width - 200);
|
|
372
|
+
explorer.style.setProperty("--explorer-tree-width", width + "px");
|
|
373
|
+
return width;
|
|
374
|
+
}
|
|
375
|
+
{
|
|
376
|
+
const width = storedSize(EXPLORER_WIDTH_KEY);
|
|
377
|
+
if (width) explorer.style.setProperty("--explorer-width", width + "px");
|
|
378
|
+
const tree = storedSize(EXPLORER_TREE_KEY);
|
|
379
|
+
if (tree) explorer.style.setProperty("--explorer-tree-width", tree + "px");
|
|
380
|
+
const treeH = storedSize(EXPLORER_TREE_H_KEY);
|
|
381
|
+
if (treeH) explorer.style.setProperty("--explorer-tree-height", treeH + "px");
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
let explorerDragged;
|
|
385
|
+
dragHandle(document.getElementById("explorer-resizer"), {
|
|
386
|
+
axis: "x",
|
|
387
|
+
enabled: () => !isPhone(),
|
|
388
|
+
start: () => explorer.getBoundingClientRect().width,
|
|
389
|
+
move: (dx, dy, start) => { explorerDragged = explorerSetWidth(start - dx); },
|
|
390
|
+
end: () => storeSize(EXPLORER_WIDTH_KEY, explorerDragged),
|
|
391
|
+
reset: () => { explorer.style.removeProperty("--explorer-width"); storeSize(EXPLORER_WIDTH_KEY); },
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
const explorerSplit = document.getElementById("explorer-split");
|
|
395
|
+
let explorerTreeDragged;
|
|
396
|
+
// Beside the file on a desktop, above it on a phone: the same handle drags
|
|
397
|
+
// whichever way the two are stacked.
|
|
398
|
+
dragHandle(explorerSplit, {
|
|
399
|
+
get axis() { return isPhone() ? "y" : "x"; },
|
|
400
|
+
start: () => {
|
|
401
|
+
const rect = explorerTree.getBoundingClientRect();
|
|
402
|
+
return isPhone() ? rect.height : rect.width;
|
|
403
|
+
},
|
|
404
|
+
move: (dx, dy, start) => { explorerTreeDragged = explorerSetTree(start + (isPhone() ? dy : dx)); },
|
|
405
|
+
end: () => storeSize(isPhone() ? EXPLORER_TREE_H_KEY : EXPLORER_TREE_KEY, explorerTreeDragged),
|
|
406
|
+
reset: () => {
|
|
407
|
+
explorer.style.removeProperty(isPhone() ? "--explorer-tree-height" : "--explorer-tree-width");
|
|
408
|
+
storeSize(isPhone() ? EXPLORER_TREE_H_KEY : EXPLORER_TREE_KEY);
|
|
409
|
+
},
|
|
410
|
+
});
|
|
411
|
+
`;
|
|
412
|
+
//# sourceMappingURL=files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../../src/page/client/files.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoZ9B,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The client script, assembled.
|
|
3
|
+
*
|
|
4
|
+
* It was one template literal of eleven thousand lines, and it is split here
|
|
5
|
+
* along the section dividers it already carried. The parts are plain text and
|
|
6
|
+
* nothing interpolates into them, so the only thing that matters is that they
|
|
7
|
+
* join back exactly: each opens with the newline after its backtick and closes
|
|
8
|
+
* flush against its last line, which leaves one newline between neighbours and
|
|
9
|
+
* none doubled. The newline the closing delimiter used to contribute is added
|
|
10
|
+
* back at the end.
|
|
11
|
+
*
|
|
12
|
+
* Order is the whole contract. The script is one scope — a helper declared in
|
|
13
|
+
* an earlier part is what a later one calls — so this list is the file, and
|
|
14
|
+
* rearranging it rearranges the program.
|
|
15
|
+
*/
|
|
16
|
+
export declare const APP_SCRIPT: string;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { BASE } from "./base.js";
|
|
2
|
+
import { PICKERS } from "./pickers.js";
|
|
3
|
+
import { TRANSCRIPT } from "./transcript.js";
|
|
4
|
+
import { STATE } from "./state.js";
|
|
5
|
+
import { EVENTS } from "./events.js";
|
|
6
|
+
import { DIALOGS } from "./dialogs.js";
|
|
7
|
+
import { COMPOSER } from "./composer.js";
|
|
8
|
+
import { ARTIFACTS } from "./artifacts.js";
|
|
9
|
+
import { ACCOUNT } from "./account.js";
|
|
10
|
+
import { ROUTINES } from "./routines.js";
|
|
11
|
+
import { USAGE } from "./usage.js";
|
|
12
|
+
import { PANELS } from "./panels.js";
|
|
13
|
+
import { BROWSER } from "./browser.js";
|
|
14
|
+
import { FILES } from "./files.js";
|
|
15
|
+
import { TERMINAL } from "./terminal.js";
|
|
16
|
+
import { SHELL } from "./shell.js";
|
|
17
|
+
/**
|
|
18
|
+
* The client script, assembled.
|
|
19
|
+
*
|
|
20
|
+
* It was one template literal of eleven thousand lines, and it is split here
|
|
21
|
+
* along the section dividers it already carried. The parts are plain text and
|
|
22
|
+
* nothing interpolates into them, so the only thing that matters is that they
|
|
23
|
+
* join back exactly: each opens with the newline after its backtick and closes
|
|
24
|
+
* flush against its last line, which leaves one newline between neighbours and
|
|
25
|
+
* none doubled. The newline the closing delimiter used to contribute is added
|
|
26
|
+
* back at the end.
|
|
27
|
+
*
|
|
28
|
+
* Order is the whole contract. The script is one scope — a helper declared in
|
|
29
|
+
* an earlier part is what a later one calls — so this list is the file, and
|
|
30
|
+
* rearranging it rearranges the program.
|
|
31
|
+
*/
|
|
32
|
+
export const APP_SCRIPT = [
|
|
33
|
+
BASE,
|
|
34
|
+
PICKERS,
|
|
35
|
+
TRANSCRIPT,
|
|
36
|
+
STATE,
|
|
37
|
+
EVENTS,
|
|
38
|
+
DIALOGS,
|
|
39
|
+
COMPOSER,
|
|
40
|
+
ARTIFACTS,
|
|
41
|
+
ACCOUNT,
|
|
42
|
+
ROUTINES,
|
|
43
|
+
USAGE,
|
|
44
|
+
PANELS,
|
|
45
|
+
FILES,
|
|
46
|
+
TERMINAL,
|
|
47
|
+
BROWSER,
|
|
48
|
+
SHELL,
|
|
49
|
+
].join("") + "\n";
|
|
50
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/page/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,UAAU,GACrB;IACA,IAAI;IACJ,OAAO;IACP,UAAU;IACV,KAAK;IACL,MAAM;IACN,OAAO;IACP,QAAQ;IACR,SAAS;IACT,OAAO;IACP,QAAQ;IACR,KAAK;IACL,MAAM;IACN,KAAK;IACL,QAAQ;IACR,OAAO;IACP,KAAK;CACJ,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The remaining side panels — every artifact, the counts in the sidebar,
|
|
3
|
+
* plugin pages and the memory page — and the navigation between them.
|
|
4
|
+
*
|
|
5
|
+
* One part of the client script; see ./index.ts for how the parts join.
|
|
6
|
+
*/
|
|
7
|
+
export declare const PANELS: string;
|