@bakapiano/ccsm 0.22.3 → 0.22.5
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/CLAUDE.md +538 -538
- package/README.md +189 -189
- package/bin/ccsm.js +235 -235
- package/lib/cliActivity.js +139 -139
- package/lib/codexSeed.js +183 -183
- package/lib/config.js +274 -274
- package/lib/devices.js +229 -229
- package/lib/folders.js +124 -124
- package/lib/localCliSessions.js +519 -519
- package/lib/persistedSessions.js +129 -129
- package/lib/tunnel.js +621 -621
- package/lib/webTerminal.js +225 -225
- package/lib/workspace.js +233 -233
- package/package.json +57 -57
- package/public/css/base.css +99 -99
- package/public/css/cards.css +183 -183
- package/public/css/feedback.css +504 -504
- package/public/css/forms.css +453 -453
- package/public/css/layout.css +176 -176
- package/public/css/modal.css +190 -190
- package/public/css/responsive.css +176 -176
- package/public/css/sidebar.css +707 -707
- package/public/css/terminals.css +645 -543
- package/public/css/tokens.css +81 -81
- package/public/css/wco.css +196 -196
- package/public/css/widgets.css +2725 -2725
- package/public/index.html +152 -152
- package/public/js/api.js +371 -371
- package/public/js/backend.js +149 -149
- package/public/js/components/App.js +73 -73
- package/public/js/components/DirectoryPicker.js +203 -203
- package/public/js/components/EntityFormModal.js +153 -153
- package/public/js/components/Modal.js +57 -57
- package/public/js/components/OfflineBanner.js +67 -67
- package/public/js/components/PageTitleBar.js +13 -13
- package/public/js/components/PendingApprovalOverlay.js +128 -128
- package/public/js/components/Picker.js +179 -179
- package/public/js/components/Popover.js +55 -55
- package/public/js/components/RestartOverlay.js +36 -36
- package/public/js/components/Sidebar.js +380 -380
- package/public/js/components/TerminalInstance.js +159 -22
- package/public/js/components/TerminalResizeDebouncer.js +126 -0
- package/public/js/components/TerminalView.js +15 -2
- package/public/js/components/XtermTerminal.js +74 -15
- package/public/js/components/useDragSort.js +67 -67
- package/public/js/dialog.js +67 -67
- package/public/js/icons.js +212 -212
- package/public/js/main.js +296 -296
- package/public/js/pages/AboutPage.js +90 -90
- package/public/js/pages/ConfigurePage.js +713 -713
- package/public/js/pages/LaunchPage.js +421 -421
- package/public/js/pages/RemotePage.js +743 -743
- package/public/js/pages/SessionsPage.js +199 -80
- package/public/js/state.js +335 -335
- package/public/manifest.webmanifest +25 -0
- package/public/setup/index.html +567 -0
- package/scripts/dev.js +149 -149
- package/scripts/install.js +153 -153
- package/scripts/restart-helper.js +96 -96
- package/scripts/upgrade-helper.js +687 -687
- package/server.js +1807 -1807
package/public/index.html
CHANGED
|
@@ -1,152 +1,152 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<!-- maximum-scale=1 stops iOS Safari from auto-zooming the page
|
|
6
|
-
when the user taps a sub-16px input. xterm's helper textarea
|
|
7
|
-
inherits the terminal's own font size (11px on phones); if we
|
|
8
|
-
instead bumped it to 16px via CSS, xterm would measure the
|
|
9
|
-
cell off the inflated textarea and render every glyph ~50%
|
|
10
|
-
oversized. Locking page zoom here lets us leave the textarea
|
|
11
|
-
alone. -->
|
|
12
|
-
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
|
13
|
-
<!-- Bleeds the cream surface into the Edge/Chrome --app= title bar
|
|
14
|
-
so it visually disappears against the body. The browser does
|
|
15
|
-
honor this in standalone app windows. -->
|
|
16
|
-
<meta name="theme-color" content="#f6f8fa" />
|
|
17
|
-
<meta name="color-scheme" content="light" />
|
|
18
|
-
<title>CCSM</title>
|
|
19
|
-
<!-- All asset paths are RELATIVE so the same index.html works when
|
|
20
|
-
served from localhost:7777/ (backend bundle) AND from
|
|
21
|
-
https://bakapiano.github.io/ccsm/v1/ (GH Pages hosted). -->
|
|
22
|
-
<link rel="icon" type="image/svg+xml" href="./favicon.svg" />
|
|
23
|
-
<!-- Point at the ROOT manifest (/ccsm/manifest.webmanifest) so the
|
|
24
|
-
installable PWA is scoped to /ccsm/, covering every per-version
|
|
25
|
-
subdir. If we shipped a per-version manifest the browser would
|
|
26
|
-
scope the PWA to /ccsm/X.Y.Z/ — then the version-guard's
|
|
27
|
-
redirect to ../ on backend upgrade would fall out of scope and
|
|
28
|
-
the OS would re-show an address bar. -->
|
|
29
|
-
<link rel="manifest" href="../manifest.webmanifest" />
|
|
30
|
-
<!-- Apply theme (accent + light/dark) BEFORE stylesheets/paint to
|
|
31
|
-
avoid a flash of the default light tokens.css bg. Mirrors
|
|
32
|
-
applyTheme()/applyAccentCssVars() in state.js — keep the two in
|
|
33
|
-
sync. Resolves 'system' against the OS, sets data-theme so the
|
|
34
|
-
[data-theme="dark"] CSS overrides apply from the first frame, and
|
|
35
|
-
derives the accent-tinted palette for the chosen ground. -->
|
|
36
|
-
<script>
|
|
37
|
-
(function () {
|
|
38
|
-
try {
|
|
39
|
-
var hex = localStorage.getItem('ccsm.accent');
|
|
40
|
-
if (!/^#[0-9a-fA-F]{6}$/.test(hex || '')) hex = '#2f6fa3';
|
|
41
|
-
var mode = localStorage.getItem('ccsm.theme');
|
|
42
|
-
if (mode !== 'light' && mode !== 'dark' && mode !== 'system') mode = 'system';
|
|
43
|
-
var dark = mode === 'dark' || (mode === 'system'
|
|
44
|
-
&& window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
|
45
|
-
|
|
46
|
-
var n = parseInt(hex.slice(1), 16);
|
|
47
|
-
var A = { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 };
|
|
48
|
-
var toHex = function (v) { v = Math.max(0, Math.min(255, Math.round(v))); var s = v.toString(16); return s.length < 2 ? '0' + s : s; };
|
|
49
|
-
var rgb = function (c) { return '#' + toHex(c.r) + toHex(c.g) + toHex(c.b); };
|
|
50
|
-
var lerp = function (c1, c2, t) { return { r: c1.r + (c2.r - c1.r) * t, g: c1.g + (c2.g - c1.g) * t, b: c1.b + (c2.b - c1.b) * t }; };
|
|
51
|
-
var WHITE = { r: 255, g: 255, b: 255 }, BLACK = { r: 0, g: 0, b: 0 };
|
|
52
|
-
var DARK_BASE = { r: 0x18, g: 0x16, b: 0x12 }, LIGHT_INK = { r: 0xec, g: 0xe7, b: 0xda };
|
|
53
|
-
|
|
54
|
-
var root = document.documentElement.style;
|
|
55
|
-
var set = function (o) { for (var k in o) root.setProperty(k, o[k]); };
|
|
56
|
-
var vars;
|
|
57
|
-
if (dark) {
|
|
58
|
-
var bg = lerp(DARK_BASE, A, 0.06);
|
|
59
|
-
var lift = function (t) { return rgb(lerp(bg, LIGHT_INK, t)); };
|
|
60
|
-
vars = {
|
|
61
|
-
'--accent': hex,
|
|
62
|
-
'--accent-deep': rgb(lerp(A, LIGHT_INK, 0.18)),
|
|
63
|
-
'--accent-soft': 'rgba(' + A.r + ',' + A.g + ',' + A.b + ',0.18)',
|
|
64
|
-
'--accent-softer': 'rgba(' + A.r + ',' + A.g + ',' + A.b + ',0.07)',
|
|
65
|
-
'--bg': rgb(bg), '--bg-elev': lift(0.05), '--sidebar-bg': rgb(bg),
|
|
66
|
-
'--sidebar-hover': lift(0.09), '--sidebar-active': lift(0.15),
|
|
67
|
-
'--border': lift(0.14), '--border-soft': lift(0.09), '--border-strong': lift(0.24),
|
|
68
|
-
'--ui-bg': lift(0.05), '--ui-border': lift(0.16), '--ui-border-soft': lift(0.10),
|
|
69
|
-
'--ink': rgb(LIGHT_INK),
|
|
70
|
-
'--ink-mid': rgb(lerp(LIGHT_INK, DARK_BASE, 0.28)),
|
|
71
|
-
'--ink-muted': rgb(lerp(LIGHT_INK, DARK_BASE, 0.45)),
|
|
72
|
-
'--ink-faint': rgb(lerp(LIGHT_INK, DARK_BASE, 0.60)),
|
|
73
|
-
};
|
|
74
|
-
} else {
|
|
75
|
-
var mix = function (t) { return rgb(lerp(WHITE, A, t)); };
|
|
76
|
-
vars = {
|
|
77
|
-
'--accent': hex,
|
|
78
|
-
'--accent-deep': rgb(lerp(A, BLACK, 0.2)),
|
|
79
|
-
'--accent-soft': 'rgba(' + A.r + ',' + A.g + ',' + A.b + ',0.10)',
|
|
80
|
-
'--accent-softer': 'rgba(' + A.r + ',' + A.g + ',' + A.b + ',0.04)',
|
|
81
|
-
'--bg': mix(0.04), '--bg-elev': '#ffffff', '--sidebar-bg': mix(0.04),
|
|
82
|
-
'--sidebar-hover': mix(0.10), '--sidebar-active': mix(0.15),
|
|
83
|
-
'--border': mix(0.15), '--border-soft': mix(0.12), '--border-strong': mix(0.25),
|
|
84
|
-
'--ui-bg': mix(0.10), '--ui-border': '#d8d4c6', '--ui-border-soft': '#e6e2d4',
|
|
85
|
-
'--ink': '#1a1815', '--ink-mid': '#534e44', '--ink-muted': '#8a8475', '--ink-faint': '#b5af9d',
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
document.documentElement.dataset.theme = dark ? 'dark' : 'light';
|
|
89
|
-
document.documentElement.style.colorScheme = dark ? 'dark' : 'light';
|
|
90
|
-
set(vars);
|
|
91
|
-
var meta = document.querySelector('meta[name="theme-color"]');
|
|
92
|
-
if (meta) meta.setAttribute('content', vars['--bg']);
|
|
93
|
-
} catch (_) {}
|
|
94
|
-
})();
|
|
95
|
-
</script>
|
|
96
|
-
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
97
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
98
|
-
<link
|
|
99
|
-
rel="stylesheet"
|
|
100
|
-
href="https://fonts.googleapis.com/css2?family=Geist:wght@300..700&family=Geist+Mono:wght@400..600&family=JetBrains+Mono:wght@400..600&display=swap"
|
|
101
|
-
/>
|
|
102
|
-
<link rel="stylesheet" href="./css/tokens.css" />
|
|
103
|
-
<link rel="stylesheet" href="./css/base.css" />
|
|
104
|
-
<link rel="stylesheet" href="./css/layout.css" />
|
|
105
|
-
<link rel="stylesheet" href="./css/sidebar.css" />
|
|
106
|
-
<link rel="stylesheet" href="./css/cards.css" />
|
|
107
|
-
<link rel="stylesheet" href="./css/tables.css" />
|
|
108
|
-
<link rel="stylesheet" href="./css/forms.css" />
|
|
109
|
-
<link rel="stylesheet" href="./css/widgets.css" />
|
|
110
|
-
<link rel="stylesheet" href="./css/feedback.css" />
|
|
111
|
-
<link rel="stylesheet" href="./css/modal.css" />
|
|
112
|
-
<link rel="stylesheet" href="./css/terminals.css" />
|
|
113
|
-
<link rel="stylesheet" href="./css/wco.css" />
|
|
114
|
-
<link rel="stylesheet" href="./css/responsive.css" />
|
|
115
|
-
<!-- Loaded last so its [data-theme="dark"] rules win the cascade. -->
|
|
116
|
-
<link rel="stylesheet" href="./css/dark.css" />
|
|
117
|
-
|
|
118
|
-
<script type="importmap">
|
|
119
|
-
{
|
|
120
|
-
"imports": {
|
|
121
|
-
"preact": "https://esm.sh/preact@10.27.0",
|
|
122
|
-
"preact/hooks": "https://esm.sh/preact@10.27.0/hooks",
|
|
123
|
-
"preact/compat": "https://esm.sh/preact@10.27.0/compat",
|
|
124
|
-
"@preact/signals": "https://esm.sh/@preact/signals@1.3.2?deps=preact@10.27.0",
|
|
125
|
-
"htm": "https://esm.sh/htm@3.1.1",
|
|
126
|
-
"@xterm/xterm": "https://esm.sh/@xterm/xterm@5.5.0",
|
|
127
|
-
"@xterm/addon-fit": "https://esm.sh/@xterm/addon-fit@0.10.0?deps=@xterm/xterm@5.5.0",
|
|
128
|
-
"@xterm/addon-web-links": "https://esm.sh/@xterm/addon-web-links@0.11.0?deps=@xterm/xterm@5.5.0",
|
|
129
|
-
"@xterm/addon-clipboard": "https://esm.sh/@xterm/addon-clipboard@0.1.0?deps=@xterm/xterm@5.5.0",
|
|
130
|
-
"@xterm/addon-webgl": "https://esm.sh/@xterm/addon-webgl@0.18.0?deps=@xterm/xterm@5.5.0"
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
</script>
|
|
134
|
-
<link rel="stylesheet" href="https://esm.sh/@xterm/xterm@5.5.0/css/xterm.css" />
|
|
135
|
-
</head>
|
|
136
|
-
<body>
|
|
137
|
-
<div id="app"></div>
|
|
138
|
-
<script type="module" src="./js/main.js"></script>
|
|
139
|
-
<script>
|
|
140
|
-
// Dev hot-reload — only active when the page itself loads from a
|
|
141
|
-
// local backend (the /api/dev/ping endpoint exists only on a dev
|
|
142
|
-
// checkout). On the GH Pages copy this skips entirely.
|
|
143
|
-
if (location.hostname === 'localhost' || location.hostname === '127.0.0.1') {
|
|
144
|
-
fetch('/api/dev/ping', { cache: 'no-store' }).then((r) => {
|
|
145
|
-
if (!r.ok) return;
|
|
146
|
-
const es = new EventSource('/api/dev/reload');
|
|
147
|
-
es.addEventListener('reload', () => location.reload());
|
|
148
|
-
}).catch(() => {});
|
|
149
|
-
}
|
|
150
|
-
</script>
|
|
151
|
-
</body>
|
|
152
|
-
</html>
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<!-- maximum-scale=1 stops iOS Safari from auto-zooming the page
|
|
6
|
+
when the user taps a sub-16px input. xterm's helper textarea
|
|
7
|
+
inherits the terminal's own font size (11px on phones); if we
|
|
8
|
+
instead bumped it to 16px via CSS, xterm would measure the
|
|
9
|
+
cell off the inflated textarea and render every glyph ~50%
|
|
10
|
+
oversized. Locking page zoom here lets us leave the textarea
|
|
11
|
+
alone. -->
|
|
12
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
|
13
|
+
<!-- Bleeds the cream surface into the Edge/Chrome --app= title bar
|
|
14
|
+
so it visually disappears against the body. The browser does
|
|
15
|
+
honor this in standalone app windows. -->
|
|
16
|
+
<meta name="theme-color" content="#f6f8fa" />
|
|
17
|
+
<meta name="color-scheme" content="light" />
|
|
18
|
+
<title>CCSM</title>
|
|
19
|
+
<!-- All asset paths are RELATIVE so the same index.html works when
|
|
20
|
+
served from localhost:7777/ (backend bundle) AND from
|
|
21
|
+
https://bakapiano.github.io/ccsm/v1/ (GH Pages hosted). -->
|
|
22
|
+
<link rel="icon" type="image/svg+xml" href="./favicon.svg" />
|
|
23
|
+
<!-- Point at the ROOT manifest (/ccsm/manifest.webmanifest) so the
|
|
24
|
+
installable PWA is scoped to /ccsm/, covering every per-version
|
|
25
|
+
subdir. If we shipped a per-version manifest the browser would
|
|
26
|
+
scope the PWA to /ccsm/X.Y.Z/ — then the version-guard's
|
|
27
|
+
redirect to ../ on backend upgrade would fall out of scope and
|
|
28
|
+
the OS would re-show an address bar. -->
|
|
29
|
+
<link rel="manifest" href="../manifest.webmanifest" />
|
|
30
|
+
<!-- Apply theme (accent + light/dark) BEFORE stylesheets/paint to
|
|
31
|
+
avoid a flash of the default light tokens.css bg. Mirrors
|
|
32
|
+
applyTheme()/applyAccentCssVars() in state.js — keep the two in
|
|
33
|
+
sync. Resolves 'system' against the OS, sets data-theme so the
|
|
34
|
+
[data-theme="dark"] CSS overrides apply from the first frame, and
|
|
35
|
+
derives the accent-tinted palette for the chosen ground. -->
|
|
36
|
+
<script>
|
|
37
|
+
(function () {
|
|
38
|
+
try {
|
|
39
|
+
var hex = localStorage.getItem('ccsm.accent');
|
|
40
|
+
if (!/^#[0-9a-fA-F]{6}$/.test(hex || '')) hex = '#2f6fa3';
|
|
41
|
+
var mode = localStorage.getItem('ccsm.theme');
|
|
42
|
+
if (mode !== 'light' && mode !== 'dark' && mode !== 'system') mode = 'system';
|
|
43
|
+
var dark = mode === 'dark' || (mode === 'system'
|
|
44
|
+
&& window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
|
45
|
+
|
|
46
|
+
var n = parseInt(hex.slice(1), 16);
|
|
47
|
+
var A = { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 };
|
|
48
|
+
var toHex = function (v) { v = Math.max(0, Math.min(255, Math.round(v))); var s = v.toString(16); return s.length < 2 ? '0' + s : s; };
|
|
49
|
+
var rgb = function (c) { return '#' + toHex(c.r) + toHex(c.g) + toHex(c.b); };
|
|
50
|
+
var lerp = function (c1, c2, t) { return { r: c1.r + (c2.r - c1.r) * t, g: c1.g + (c2.g - c1.g) * t, b: c1.b + (c2.b - c1.b) * t }; };
|
|
51
|
+
var WHITE = { r: 255, g: 255, b: 255 }, BLACK = { r: 0, g: 0, b: 0 };
|
|
52
|
+
var DARK_BASE = { r: 0x18, g: 0x16, b: 0x12 }, LIGHT_INK = { r: 0xec, g: 0xe7, b: 0xda };
|
|
53
|
+
|
|
54
|
+
var root = document.documentElement.style;
|
|
55
|
+
var set = function (o) { for (var k in o) root.setProperty(k, o[k]); };
|
|
56
|
+
var vars;
|
|
57
|
+
if (dark) {
|
|
58
|
+
var bg = lerp(DARK_BASE, A, 0.06);
|
|
59
|
+
var lift = function (t) { return rgb(lerp(bg, LIGHT_INK, t)); };
|
|
60
|
+
vars = {
|
|
61
|
+
'--accent': hex,
|
|
62
|
+
'--accent-deep': rgb(lerp(A, LIGHT_INK, 0.18)),
|
|
63
|
+
'--accent-soft': 'rgba(' + A.r + ',' + A.g + ',' + A.b + ',0.18)',
|
|
64
|
+
'--accent-softer': 'rgba(' + A.r + ',' + A.g + ',' + A.b + ',0.07)',
|
|
65
|
+
'--bg': rgb(bg), '--bg-elev': lift(0.05), '--sidebar-bg': rgb(bg),
|
|
66
|
+
'--sidebar-hover': lift(0.09), '--sidebar-active': lift(0.15),
|
|
67
|
+
'--border': lift(0.14), '--border-soft': lift(0.09), '--border-strong': lift(0.24),
|
|
68
|
+
'--ui-bg': lift(0.05), '--ui-border': lift(0.16), '--ui-border-soft': lift(0.10),
|
|
69
|
+
'--ink': rgb(LIGHT_INK),
|
|
70
|
+
'--ink-mid': rgb(lerp(LIGHT_INK, DARK_BASE, 0.28)),
|
|
71
|
+
'--ink-muted': rgb(lerp(LIGHT_INK, DARK_BASE, 0.45)),
|
|
72
|
+
'--ink-faint': rgb(lerp(LIGHT_INK, DARK_BASE, 0.60)),
|
|
73
|
+
};
|
|
74
|
+
} else {
|
|
75
|
+
var mix = function (t) { return rgb(lerp(WHITE, A, t)); };
|
|
76
|
+
vars = {
|
|
77
|
+
'--accent': hex,
|
|
78
|
+
'--accent-deep': rgb(lerp(A, BLACK, 0.2)),
|
|
79
|
+
'--accent-soft': 'rgba(' + A.r + ',' + A.g + ',' + A.b + ',0.10)',
|
|
80
|
+
'--accent-softer': 'rgba(' + A.r + ',' + A.g + ',' + A.b + ',0.04)',
|
|
81
|
+
'--bg': mix(0.04), '--bg-elev': '#ffffff', '--sidebar-bg': mix(0.04),
|
|
82
|
+
'--sidebar-hover': mix(0.10), '--sidebar-active': mix(0.15),
|
|
83
|
+
'--border': mix(0.15), '--border-soft': mix(0.12), '--border-strong': mix(0.25),
|
|
84
|
+
'--ui-bg': mix(0.10), '--ui-border': '#d8d4c6', '--ui-border-soft': '#e6e2d4',
|
|
85
|
+
'--ink': '#1a1815', '--ink-mid': '#534e44', '--ink-muted': '#8a8475', '--ink-faint': '#b5af9d',
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
document.documentElement.dataset.theme = dark ? 'dark' : 'light';
|
|
89
|
+
document.documentElement.style.colorScheme = dark ? 'dark' : 'light';
|
|
90
|
+
set(vars);
|
|
91
|
+
var meta = document.querySelector('meta[name="theme-color"]');
|
|
92
|
+
if (meta) meta.setAttribute('content', vars['--bg']);
|
|
93
|
+
} catch (_) {}
|
|
94
|
+
})();
|
|
95
|
+
</script>
|
|
96
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
97
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
98
|
+
<link
|
|
99
|
+
rel="stylesheet"
|
|
100
|
+
href="https://fonts.googleapis.com/css2?family=Geist:wght@300..700&family=Geist+Mono:wght@400..600&family=JetBrains+Mono:wght@400..600&display=swap"
|
|
101
|
+
/>
|
|
102
|
+
<link rel="stylesheet" href="./css/tokens.css" />
|
|
103
|
+
<link rel="stylesheet" href="./css/base.css" />
|
|
104
|
+
<link rel="stylesheet" href="./css/layout.css" />
|
|
105
|
+
<link rel="stylesheet" href="./css/sidebar.css" />
|
|
106
|
+
<link rel="stylesheet" href="./css/cards.css" />
|
|
107
|
+
<link rel="stylesheet" href="./css/tables.css" />
|
|
108
|
+
<link rel="stylesheet" href="./css/forms.css" />
|
|
109
|
+
<link rel="stylesheet" href="./css/widgets.css" />
|
|
110
|
+
<link rel="stylesheet" href="./css/feedback.css" />
|
|
111
|
+
<link rel="stylesheet" href="./css/modal.css" />
|
|
112
|
+
<link rel="stylesheet" href="./css/terminals.css" />
|
|
113
|
+
<link rel="stylesheet" href="./css/wco.css" />
|
|
114
|
+
<link rel="stylesheet" href="./css/responsive.css" />
|
|
115
|
+
<!-- Loaded last so its [data-theme="dark"] rules win the cascade. -->
|
|
116
|
+
<link rel="stylesheet" href="./css/dark.css" />
|
|
117
|
+
|
|
118
|
+
<script type="importmap">
|
|
119
|
+
{
|
|
120
|
+
"imports": {
|
|
121
|
+
"preact": "https://esm.sh/preact@10.27.0",
|
|
122
|
+
"preact/hooks": "https://esm.sh/preact@10.27.0/hooks",
|
|
123
|
+
"preact/compat": "https://esm.sh/preact@10.27.0/compat",
|
|
124
|
+
"@preact/signals": "https://esm.sh/@preact/signals@1.3.2?deps=preact@10.27.0",
|
|
125
|
+
"htm": "https://esm.sh/htm@3.1.1",
|
|
126
|
+
"@xterm/xterm": "https://esm.sh/@xterm/xterm@5.5.0",
|
|
127
|
+
"@xterm/addon-fit": "https://esm.sh/@xterm/addon-fit@0.10.0?deps=@xterm/xterm@5.5.0",
|
|
128
|
+
"@xterm/addon-web-links": "https://esm.sh/@xterm/addon-web-links@0.11.0?deps=@xterm/xterm@5.5.0",
|
|
129
|
+
"@xterm/addon-clipboard": "https://esm.sh/@xterm/addon-clipboard@0.1.0?deps=@xterm/xterm@5.5.0",
|
|
130
|
+
"@xterm/addon-webgl": "https://esm.sh/@xterm/addon-webgl@0.18.0?deps=@xterm/xterm@5.5.0"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
</script>
|
|
134
|
+
<link rel="stylesheet" href="https://esm.sh/@xterm/xterm@5.5.0/css/xterm.css" />
|
|
135
|
+
</head>
|
|
136
|
+
<body>
|
|
137
|
+
<div id="app"></div>
|
|
138
|
+
<script type="module" src="./js/main.js"></script>
|
|
139
|
+
<script>
|
|
140
|
+
// Dev hot-reload — only active when the page itself loads from a
|
|
141
|
+
// local backend (the /api/dev/ping endpoint exists only on a dev
|
|
142
|
+
// checkout). On the GH Pages copy this skips entirely.
|
|
143
|
+
if (location.hostname === 'localhost' || location.hostname === '127.0.0.1') {
|
|
144
|
+
fetch('/api/dev/ping', { cache: 'no-store' }).then((r) => {
|
|
145
|
+
if (!r.ok) return;
|
|
146
|
+
const es = new EventSource('/api/dev/reload');
|
|
147
|
+
es.addEventListener('reload', () => location.reload());
|
|
148
|
+
}).catch(() => {});
|
|
149
|
+
}
|
|
150
|
+
</script>
|
|
151
|
+
</body>
|
|
152
|
+
</html>
|