codebay 0.7.0 → 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/.mochi/import-css/xterm-1yy1hf1g.css +252 -0
- package/.mochi/manifest.json +326 -207
- package/.mochi/svelte-client/HydratableIsland-k6q8esj4.js +1 -0
- package/.mochi/svelte-client/_hydrate-AppShell_2rtnxn6pgp2rh-sg96rp17.js +2 -0
- package/.mochi/svelte-client/_hydrate-AvatarsView_257s9yq1wwr4f-92hepzby.js +14 -0
- package/.mochi/svelte-client/_hydrate-InstanceView_2ma45l9bybfhl-p3f649gk.js +1 -0
- package/.mochi/svelte-client/_hydrate-SettingsView_8hmarwmf8onq-dzmgmv78.js +29 -0
- package/.mochi/svelte-client/chunk-2j95sv83.js +1 -0
- package/.mochi/svelte-client/chunk-6rbs0mhx.js +1 -0
- package/.mochi/svelte-client/chunk-7wkvn2vx.js +2 -0
- package/.mochi/svelte-client/chunk-a2psjmrm.js +2 -0
- package/.mochi/svelte-client/chunk-m497q88s.js +2 -0
- package/.mochi/svelte-client/{chunk-fqg2qzj8.js → chunk-m4fzsd5b.js} +1 -1
- package/.mochi/svelte-client/chunk-mpv3qx28.js +36 -0
- package/.mochi/svelte-client/chunk-rs41by6w.js +2 -0
- package/.mochi/svelte-client/{chunk-qj7zy2d9.js → chunk-v65n8th6.js} +4 -4
- package/.mochi/svelte-compile/{App-6rcf0d74.server.js → App-7vjcp38v.server.js} +368 -37
- package/.mochi/svelte-compile/{Avatars-pzyz0tv3.server.js → Avatars-e2cfc95t.server.js} +130 -25
- package/.mochi/svelte-compile/{ClientStats-z3tn9ad6.server.js → ClientStats-4cphmrnp.server.js} +1 -1
- package/.mochi/svelte-compile/{DefaultError-wh77vebf.server.js → DefaultError-je52k17d.server.js} +1 -1
- package/.mochi/svelte-compile/{Instance-49ksrp11.server.js → Instance-h78n2dsv.server.js} +16 -13
- package/.mochi/svelte-compile/{Settings-sz5d6qj7.server.js → Settings-tav2asn7.server.js} +153 -127
- package/.mochi/svelte-compile/{chunk-fpe16ywq.js → chunk-60mpagr9.js} +17 -7
- package/.mochi/svelte-compile/chunk-9nqc2r0y.js +9706 -0
- package/.mochi/svelte-compile/{chunk-6ghzacpg.js → chunk-bpqwf1nw.js} +36 -4
- package/.mochi/svelte-compile/chunk-jx6h1xzp.js +20 -0
- package/.mochi/svelte-compile/{chunk-q8rbesaw.js → chunk-m8ygpyhr.js} +5 -4
- package/.mochi/svelte-compile/chunk-rtdne1m8.js +31 -0
- package/.mochi/svelte-compile/{chunk-we0ggtws.js → chunk-xpt1a7jm.js} +2 -1
- package/.mochi/svelte-css/AvatarEditor-2mdq8ss346drw.css +1 -0
- package/.mochi/svelte-css/{AvatarsView-2ughbzc26luyb.css → AvatarsView-2llo48w0wt0xn.css} +1 -1
- package/.mochi/svelte-css/FolderBrowser-2pxp9z1o7zixk.css +1 -0
- package/.mochi/svelte-css/{IdeBar-2qoqi2ya9zdn3.css → IdeBar-10xodudcx57qr.css} +1 -1
- package/.mochi/svelte-css/{SettingsView-3uycxa8z548m4.css → SettingsView-2qfauz68axro1.css} +1 -1
- package/.mochi/svelte-css/TerminalPane-3bwgk6ulzq6b3.css +1 -0
- package/.mochi/svelte-css/TerminalSplit-2416xfuln89a3.css +1 -0
- package/README.md +3 -0
- package/migrations/0005_instance_mode.sql +1 -0
- package/migrations/0006_terminal_split.sql +1 -0
- package/package.json +3 -1
- package/src/avatars/serialize.ts +35 -6
- package/src/components/AppShell.svelte +46 -8
- package/src/components/Avatar.svelte +3 -2
- package/src/components/AvatarEditor.svelte +96 -19
- package/src/components/AvatarsView.svelte +0 -0
- package/src/components/DashboardView.svelte +22 -7
- package/src/components/FolderBrowser.svelte +88 -8
- package/src/components/HealthBox.svelte +12 -5
- package/src/components/IdeBar.svelte +10 -0
- package/src/components/InstanceCard.svelte +3 -1
- package/src/components/InstanceView.svelte +1 -0
- package/src/components/SettingsView.svelte +108 -0
- package/src/components/TerminalPane.svelte +274 -0
- package/src/components/TerminalSplit.svelte +154 -0
- package/src/container-injections/claude-code-ide-extension.ts +2 -0
- package/src/container-injections/claude-code-update.ts +43 -0
- package/src/container-injections/ttyd.ts +49 -0
- package/src/index.ts +2 -1
- package/src/lib/config.server.ts +3 -0
- package/src/lib/db.server.ts +14 -4
- package/src/lib/devcontainer.server.ts +140 -24
- package/src/lib/health.server.ts +1 -1
- package/src/lib/injections.server.ts +18 -5
- package/src/lib/instances.server.ts +64 -10
- package/src/lib/proxy.server.ts +87 -11
- package/src/pages/App.svelte +5 -3
- package/src/pages/Settings.svelte +4 -0
- package/src/routes.ts +55 -6
- package/src/types.ts +18 -0
- package/.mochi/svelte-client/HydratableIsland-qyjx1d98.js +0 -1
- package/.mochi/svelte-client/_hydrate-AppShell_2rtnxn6pgp2rh-fkna7bj2.js +0 -2
- package/.mochi/svelte-client/_hydrate-AvatarsView_257s9yq1wwr4f-89wtkb86.js +0 -13
- package/.mochi/svelte-client/_hydrate-InstanceView_2ma45l9bybfhl-eamnbyqk.js +0 -1
- package/.mochi/svelte-client/_hydrate-SettingsView_8hmarwmf8onq-kdra4mcb.js +0 -28
- package/.mochi/svelte-client/chunk-330mrspw.js +0 -2
- package/.mochi/svelte-client/chunk-4jgkak13.js +0 -1
- package/.mochi/svelte-client/chunk-k5m4kt20.js +0 -2
- package/.mochi/svelte-client/chunk-rpdphch7.js +0 -2
- package/.mochi/svelte-compile/chunk-bacb4b0r.js +0 -14
- package/.mochi/svelte-css/AvatarEditor-2ewwtejaml4e2.css +0 -1
- package/.mochi/svelte-css/FolderBrowser-1x428zb3f2kti.css +0 -1
- /package/.mochi/svelte-compile/{chunk-btmcxhqy.js → chunk-brjv9pyd.js} +0 -0
|
@@ -1,15 +1,47 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
Icon
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-60mpagr9.js";
|
|
5
5
|
|
|
6
|
-
// node_modules/@lucide/svelte/dist/icons/
|
|
6
|
+
// node_modules/@lucide/svelte/dist/icons/terminal.svelte
|
|
7
7
|
import"svelte/internal/flags/async";
|
|
8
8
|
import * as $ from "svelte/internal/server";
|
|
9
|
+
function Terminal($$renderer, $$props) {
|
|
10
|
+
let { $$slots, $$events, ...props } = $$props;
|
|
11
|
+
const iconNode = [
|
|
12
|
+
["path", { d: "M12 19h8" }],
|
|
13
|
+
["path", { d: "m4 17 6-6-6-6" }]
|
|
14
|
+
];
|
|
15
|
+
Icon($$renderer, $.spread_props([{ name: "terminal" }, props, { iconNode }]));
|
|
16
|
+
}
|
|
17
|
+
// node_modules/@lucide/svelte/dist/icons/layout-template.svelte
|
|
18
|
+
import"svelte/internal/flags/async";
|
|
19
|
+
import * as $2 from "svelte/internal/server";
|
|
20
|
+
function Layout_template($$renderer, $$props) {
|
|
21
|
+
let { $$slots, $$events, ...props } = $$props;
|
|
22
|
+
const iconNode = [
|
|
23
|
+
[
|
|
24
|
+
"rect",
|
|
25
|
+
{ width: "18", height: "7", x: "3", y: "3", rx: "1" }
|
|
26
|
+
],
|
|
27
|
+
[
|
|
28
|
+
"rect",
|
|
29
|
+
{ width: "9", height: "7", x: "3", y: "14", rx: "1" }
|
|
30
|
+
],
|
|
31
|
+
[
|
|
32
|
+
"rect",
|
|
33
|
+
{ width: "5", height: "7", x: "16", y: "14", rx: "1" }
|
|
34
|
+
]
|
|
35
|
+
];
|
|
36
|
+
Icon($$renderer, $2.spread_props([{ name: "layout-template" }, props, { iconNode }]));
|
|
37
|
+
}
|
|
38
|
+
// node_modules/@lucide/svelte/dist/icons/plus.svelte
|
|
39
|
+
import"svelte/internal/flags/async";
|
|
40
|
+
import * as $3 from "svelte/internal/server";
|
|
9
41
|
function Plus($$renderer, $$props) {
|
|
10
42
|
let { $$slots, $$events, ...props } = $$props;
|
|
11
43
|
const iconNode = [["path", { d: "M5 12h14" }], ["path", { d: "M12 5v14" }]];
|
|
12
|
-
Icon($$renderer,
|
|
44
|
+
Icon($$renderer, $3.spread_props([{ name: "plus" }, props, { iconNode }]));
|
|
13
45
|
}
|
|
14
46
|
// src/settings.ts
|
|
15
47
|
var SOUND_KEY = "codebay.sound";
|
|
@@ -78,4 +110,4 @@ function playChime(state) {
|
|
|
78
110
|
el.play().then(() => console.log(`[chime] playChime(${state}) \u2014 playing`)).catch((err) => console.warn(`[chime] playChime(${state}) \u2014 play rejected:`, err?.message ?? err));
|
|
79
111
|
}
|
|
80
112
|
|
|
81
|
-
export { Plus, soundEnabled, setSoundEnabled, unlockAudio, playChime };
|
|
113
|
+
export { Terminal, Layout_template, Plus, soundEnabled, setSoundEnabled, unlockAudio, playChime };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
Icon
|
|
4
|
+
} from "./chunk-60mpagr9.js";
|
|
5
|
+
|
|
6
|
+
// node_modules/@lucide/svelte/dist/icons/rotate-ccw.svelte
|
|
7
|
+
import"svelte/internal/flags/async";
|
|
8
|
+
import * as $ from "svelte/internal/server";
|
|
9
|
+
function Rotate_ccw($$renderer, $$props) {
|
|
10
|
+
let { $$slots, $$events, ...props } = $$props;
|
|
11
|
+
const iconNode = [
|
|
12
|
+
[
|
|
13
|
+
"path",
|
|
14
|
+
{ d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }
|
|
15
|
+
],
|
|
16
|
+
["path", { d: "M3 3v5h5" }]
|
|
17
|
+
];
|
|
18
|
+
Icon($$renderer, $.spread_props([{ name: "rotate-ccw" }, props, { iconNode }]));
|
|
19
|
+
}
|
|
20
|
+
export { Rotate_ccw };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
Icon
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-60mpagr9.js";
|
|
5
5
|
|
|
6
6
|
// src/avatars/anchor.ts
|
|
7
7
|
var art = {
|
|
@@ -1166,7 +1166,8 @@ function Avatar($$renderer, $$props) {
|
|
|
1166
1166
|
}
|
|
1167
1167
|
const s = $.derived(() => Math.max(1, Math.round(scale)));
|
|
1168
1168
|
const gap = $.derived(() => s() >= 2 ? 0.5 : 0);
|
|
1169
|
-
const
|
|
1169
|
+
const resolved = $.derived(() => art63 ?? pickAvatar(id));
|
|
1170
|
+
const cells = $.derived(() => decode(resolved()));
|
|
1170
1171
|
const grid = $.derived(() => Array.from({ length: 100 }, (_, i) => {
|
|
1171
1172
|
const r = Math.floor(i / 10);
|
|
1172
1173
|
const c = i % 10;
|
|
@@ -1177,7 +1178,7 @@ function Avatar($$renderer, $$props) {
|
|
|
1177
1178
|
interactive,
|
|
1178
1179
|
pressed,
|
|
1179
1180
|
ghosting
|
|
1180
|
-
})} role="img"${$.attr("aria-label", name)}${$.attr("title", name)}${$.attr_style(`width:${$.stringify(10 * s())}px;height:${$.stringify(10 * s())}px;--gap:${$.stringify(gap())}px;--bx:${$.stringify(bx)};--by:${$.stringify(by)};--spin:${$.stringify(spin)}deg;--hue:${$.stringify(hue)}deg`)}><!--[-->`);
|
|
1181
|
+
})} role="img"${$.attr("aria-label", name)}${$.attr("title", resolved().name)}${$.attr_style(`width:${$.stringify(10 * s())}px;height:${$.stringify(10 * s())}px;--gap:${$.stringify(gap())}px;--bx:${$.stringify(bx)};--by:${$.stringify(by)};--spin:${$.stringify(spin)}deg;--hue:${$.stringify(hue)}deg`)}><!--[-->`);
|
|
1181
1182
|
const each_array = $.ensure_array_like(grid());
|
|
1182
1183
|
for (let i = 0, $$length = each_array.length;i < $$length; i++) {
|
|
1183
1184
|
let cell = each_array[i];
|
|
@@ -1869,4 +1870,4 @@ function AppBar($$renderer, $$props) {
|
|
|
1869
1870
|
$$renderer.push(`<!----></header>`);
|
|
1870
1871
|
}
|
|
1871
1872
|
|
|
1872
|
-
export { OFF, ON, GRAY, ROWS, COLS, avatars, findAvatar, Avatar, Button, Toaster, dist_default, AppBar };
|
|
1873
|
+
export { OFF, ON, GRAY, ROWS, COLS, decode, avatars, findAvatar, Avatar, Button, Toaster, dist_default, AppBar };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import"./chunk-xpt1a7jm.js";
|
|
3
|
+
|
|
4
|
+
// node_modules/@xterm/addon-fit/lib/addon-fit.mjs
|
|
5
|
+
var h = 2;
|
|
6
|
+
var _ = 1;
|
|
7
|
+
var o = class {
|
|
8
|
+
activate(e) {
|
|
9
|
+
this._terminal = e;
|
|
10
|
+
}
|
|
11
|
+
dispose() {}
|
|
12
|
+
fit() {
|
|
13
|
+
let e = this.proposeDimensions();
|
|
14
|
+
if (!e || !this._terminal || isNaN(e.cols) || isNaN(e.rows))
|
|
15
|
+
return;
|
|
16
|
+
let t = this._terminal._core;
|
|
17
|
+
(this._terminal.rows !== e.rows || this._terminal.cols !== e.cols) && (t._renderService.clear(), this._terminal.resize(e.cols, e.rows));
|
|
18
|
+
}
|
|
19
|
+
proposeDimensions() {
|
|
20
|
+
if (!this._terminal || !this._terminal.element || !this._terminal.element.parentElement)
|
|
21
|
+
return;
|
|
22
|
+
let t = this._terminal._core._renderService.dimensions;
|
|
23
|
+
if (t.css.cell.width === 0 || t.css.cell.height === 0)
|
|
24
|
+
return;
|
|
25
|
+
let s = this._terminal.options.scrollback === 0 ? 0 : this._terminal.options.overviewRuler?.width || 14, r = window.getComputedStyle(this._terminal.element.parentElement), l = parseInt(r.getPropertyValue("height")), a = Math.max(0, parseInt(r.getPropertyValue("width"))), i = window.getComputedStyle(this._terminal.element), n = { top: parseInt(i.getPropertyValue("padding-top")), bottom: parseInt(i.getPropertyValue("padding-bottom")), right: parseInt(i.getPropertyValue("padding-right")), left: parseInt(i.getPropertyValue("padding-left")) }, m = n.top + n.bottom, d = n.right + n.left, c = l - m, p = a - d - s;
|
|
26
|
+
return { cols: Math.max(h, Math.floor(p / t.css.cell.width)), rows: Math.max(_, Math.floor(c / t.css.cell.height)) };
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
export {
|
|
30
|
+
o as FitAddon
|
|
31
|
+
};
|
|
@@ -30,5 +30,6 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
30
30
|
return to;
|
|
31
31
|
};
|
|
32
32
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
|
+
var __require = import.meta.require;
|
|
33
34
|
|
|
34
|
-
export { __toESM, __commonJS };
|
|
35
|
+
export { __toESM, __commonJS, __require };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.editor.svelte-qu6vp4{display:flex;background:var(--bg-card);border:1px solid var(--rule);overflow:hidden;flex-direction:column;width:100%}.head.svelte-qu6vp4{display:flex;border-bottom:1px solid var(--ink);background:var(--ink);justify-content:space-between;align-items: center;gap:12px;padding:14px 18px}.head.svelte-qu6vp4 h2:where(.svelte-qu6vp4){font-family:var(--font-display);text-transform:uppercase;letter-spacing:.06em;color:var(--bg);margin:0;font-size:17px;font-weight:700}.head-cancel.svelte-qu6vp4{display:inline-flex;border:1px solid var(--bg);color:var(--bg);font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;cursor:pointer;background:0 0;flex:none;align-items: center;gap:6px;padding:5px 10px;font-size:11px}.head-cancel.svelte-qu6vp4:hover{background:var(--bg);color:var(--ink)}.head-cancel.svelte-qu6vp4:focus-visible{outline:none;box-shadow:0 0 0 2px var(--rule-soft)}.body.svelte-qu6vp4{display:flex;flex-wrap:wrap;gap:18px;padding:18px}.grid-frame.svelte-qu6vp4{display:grid;grid-template-columns:repeat(10,40px);grid-template-rows:repeat(10,40px);border:1px solid var(--ink);background:var(--bg);flex:none;width:400px;height:400px}.ring-cell.svelte-qu6vp4{box-sizing:border-box;background-clip:content-box;background-color:var(--rule-soft);opacity:.4;padding:0 1px 1px 0}.grid.svelte-qu6vp4{display:grid;grid-template-columns:repeat(8,1fr);grid-template-rows:repeat(8,1fr);grid-column:2/10;grid-row:2/10;cursor:crosshair;touch-action:none}.cell.svelte-qu6vp4{box-sizing:border-box;background-clip:content-box;background-color:var(--rule-soft);padding:0 1px 1px 0}.cell.gray.svelte-qu6vp4{background-color:var(--led-gray)}.cell.on.svelte-qu6vp4{background-color:var(--ink)}.side.svelte-qu6vp4{display:flex;flex-direction:column;flex:1;gap:16px;min-width:160px}.preview-label.svelte-qu6vp4{text-transform:uppercase;letter-spacing:.06em;color:var(--ink-faint);font-size:11px;font-family:var(--font-mono)}.preview-row.svelte-qu6vp4{display:flex;align-items: flex-end;gap:10px;margin-top:6px}.name-field.svelte-qu6vp4{display:flex;flex-direction:column;gap:6px}.name-field.svelte-qu6vp4 input:where(.svelte-qu6vp4){font-family:var(--font-mono);border:1px solid var(--ink);background:var(--bg);color:var(--ink);padding:8px 12px;font-size:13px}.name-field.svelte-qu6vp4 input:where(.svelte-qu6vp4)::placeholder{color:var(--ink-faint)}.name-field.svelte-qu6vp4 input[readonly]:where(.svelte-qu6vp4){color:var(--ink-soft);cursor:default}.name-field.svelte-qu6vp4 input:where(.svelte-qu6vp4):focus{outline:none;box-shadow:inset 3px 3px 0 var(--rule-soft)}.mode-field.svelte-qu6vp4{display:flex;flex-direction:column;gap:6px}.modes.svelte-qu6vp4{display:flex;border:1px solid var(--ink)}.mode.svelte-qu6vp4{border:none;border-right:1px solid var(--ink);background:var(--bg);color:var(--ink);font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;cursor:pointer;flex:1;padding:7px 6px;font-size:11px}.mode.svelte-qu6vp4:last-child{border-right:none}.mode.active.svelte-qu6vp4{background:var(--ink);color:var(--bg)}.mode.svelte-qu6vp4:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--rule-soft)}.tools.svelte-qu6vp4{display:flex;gap:8px}.foot.svelte-qu6vp4{display:flex;border-top:1px solid var(--ink);flex-wrap:wrap;justify-content:space-between;align-items: center;gap:12px;padding:14px 18px}.hint.svelte-qu6vp4{font-family:var(--font-mono);color:var(--ink-faint);flex:1;min-width:180px;font-size:12px}.actions.svelte-qu6vp4{display:flex;gap:8px}.actions.svelte-qu6vp4 .btn{white-space:nowrap}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
.page.svelte-fkq7sr{display:flex;flex-direction:column;min-height:100vh}.title.svelte-fkq7sr{display:inline-flex;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.08em;color:var(--ink-soft);align-items: center;padding:0 14px;font-size:12px}.content.svelte-fkq7sr{display:flex;flex-direction:column;flex:1;align-items: center;gap:24px;padding:32px 20px}.content.svelte-fkq7sr>*{width:100%;max-width:720px}.catalog.svelte-fkq7sr{background:var(--bg-card);border:1px solid var(--rule);padding:18px}.catalog.svelte-fkq7sr header:where(.svelte-fkq7sr){margin-bottom:20px}.catalog.svelte-fkq7sr h2:where(.svelte-fkq7sr){font-family:var(--font-display);text-transform:uppercase;letter-spacing:.06em;margin:0;font-size:17px;font-weight:700}.catalog.svelte-fkq7sr header:where(.svelte-fkq7sr) p:where(.svelte-fkq7sr){font-family:var(--font-mono);color:var(--ink-soft);margin:4px 0 0;font-size:12px}ul.svelte-fkq7sr{list-style:none;display:grid;grid-template-columns:repeat(auto-fill,minmax(110px,1fr));gap:20px;margin:0;padding:0}li.svelte-fkq7sr{display:flex;flex-direction:column;align-items: center;gap:8px}.label.svelte-fkq7sr{display:inline-flex;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.08em;color:var(--ink-soft);align-items: center;gap:4px;font-size:11px}.robot-mark.svelte-fkq7sr{display:inline-flex;color:var(--ink-faint)}
|
|
1
|
+
.page.svelte-fkq7sr{display:flex;flex-direction:column;min-height:100vh}.title.svelte-fkq7sr{display:inline-flex;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.08em;color:var(--ink-soft);align-items: center;padding:0 14px;font-size:12px}.content.svelte-fkq7sr{display:flex;flex-direction:column;flex:1;align-items: center;gap:24px;padding:32px 20px}.content.svelte-fkq7sr>*{width:100%;max-width:720px}.catalog.svelte-fkq7sr{background:var(--bg-card);border:1px solid var(--rule);padding:18px}.catalog.svelte-fkq7sr header:where(.svelte-fkq7sr){margin-bottom:20px}.catalog.svelte-fkq7sr h2:where(.svelte-fkq7sr){font-family:var(--font-display);text-transform:uppercase;letter-spacing:.06em;margin:0;font-size:17px;font-weight:700}.catalog.svelte-fkq7sr header:where(.svelte-fkq7sr) p:where(.svelte-fkq7sr){font-family:var(--font-mono);color:var(--ink-soft);margin:4px 0 0;font-size:12px}ul.svelte-fkq7sr{list-style:none;display:grid;grid-template-columns:repeat(auto-fill,minmax(110px,1fr));gap:20px;margin:0;padding:0}li.svelte-fkq7sr{display:flex;flex-direction:column;align-items: center;gap:8px}.label.svelte-fkq7sr{display:inline-flex;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.08em;color:var(--ink-soft);align-items: center;gap:4px;font-size:11px}.robot-mark.svelte-fkq7sr{display:inline-flex;color:var(--ink-faint)}.editor-slot.svelte-fkq7sr{display:flex}.editor-slot.svelte-fkq7sr>*{width:100%}.edit-btn.svelte-fkq7sr{display:inline-flex;border:1px solid var(--rule);background:var(--bg);color:var(--ink-soft);font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.08em;cursor:pointer;align-items: center;gap:4px;padding:4px 8px;font-size:10px}.edit-btn.svelte-fkq7sr:hover{border-color:var(--ink);color:var(--ink)}.edit-btn.active.svelte-fkq7sr{background:var(--ink);border-color:var(--ink);color:var(--bg)}.edit-btn.svelte-fkq7sr:focus-visible{outline:none;box-shadow:0 0 0 2px var(--rule-soft)}.edit-btn.svelte-fkq7sr:disabled{cursor:default}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.overlay.svelte-oq97c4{position:fixed;background:color-mix(in srgb,var(--ink)50%,transparent);display:grid;z-index:50;place-items: center;padding:24px;inset:0}.modal.svelte-oq97c4{display:flex;background:var(--bg-card);overflow:hidden;box-shadow:8px 8px 0 var(--ink);flex-direction:column;width:min(620px,100%);max-height:80vh}.head.svelte-oq97c4{display:flex;border-bottom:1px solid var(--ink);background:var(--ink);justify-content:space-between;align-items: center;padding:14px 18px}.head.svelte-oq97c4 h2:where(.svelte-oq97c4){font-family:var(--font-display);text-transform:uppercase;letter-spacing:.06em;color:var(--bg);margin:0;font-size:17px;font-weight:700}.x.svelte-oq97c4{display:inline-flex;cursor:pointer;color:var(--bg);background:0 0;border:none;align-items: center}.x.svelte-oq97c4:hover{opacity:.7}.mode.svelte-oq97c4{display:flex;border-bottom:1px solid var(--rule-soft);align-items: center;gap:12px;padding:12px 18px}.mode-label.svelte-oq97c4{text-transform:uppercase;letter-spacing:.06em;color:var(--ink-faint);font-size:11px}.mode-toggle.svelte-oq97c4{display:flex;border:1px solid var(--ink)}.mode-btn.svelte-oq97c4{display:inline-flex;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.05em;background:var(--bg);color:var(--ink);cursor:pointer;border:none;align-items: center;gap:6px;padding:7px 14px;font-size:12px;font-weight:700}.mode-btn.svelte-oq97c4+.mode-btn:where(.svelte-oq97c4){border-left:1px solid var(--ink)}.mode-btn.active.svelte-oq97c4{background:var(--ink);color:var(--bg)}.clone.svelte-oq97c4{border-bottom:1px solid var(--rule-soft);padding:12px 18px}.clone-label.svelte-oq97c4{text-transform:uppercase;letter-spacing:.06em;color:var(--ink-faint);padding-bottom:6px;font-size:11px}.clone-row.svelte-oq97c4{display:flex;gap:8px}.clone-url.svelte-oq97c4,.clone-branch.svelte-oq97c4{font-family:var(--font-mono);border:1px solid var(--ink);background:var(--bg);color:var(--ink);padding:8px 12px;font-size:13px}.clone-url.svelte-oq97c4{flex:1;min-width:0}.clone-branch.svelte-oq97c4{flex:none;width:140px}.clone-url.svelte-oq97c4::placeholder,.clone-branch.svelte-oq97c4::placeholder{color:var(--ink-faint)}.clone-url.svelte-oq97c4:focus,.clone-branch.svelte-oq97c4:focus{outline:none;box-shadow:inset 3px 3px 0 var(--rule-soft)}.clone-btn.svelte-oq97c4{font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.05em;border:1px solid var(--ink);background:var(--ink);color:var(--bg);cursor:pointer;white-space:nowrap;padding:8px 14px;font-size:12px;font-weight:700}.clone-btn.svelte-oq97c4:hover:not(:disabled){background:var(--bg-card);color:var(--ink)}.clone-btn.svelte-oq97c4:disabled{opacity:.4;cursor:not-allowed}.crumbs.svelte-oq97c4{border-bottom:1px solid var(--rule-soft);overflow-x:auto;overflow-y:hidden;flex-shrink:0;min-height:18px;padding:10px 18px}.crumbs.svelte-oq97c4 code:where(.svelte-oq97c4){font-family:var(--font-mono);color:var(--ink-soft);white-space:nowrap;font-size:12px}.recent.svelte-oq97c4{border-bottom:1px solid var(--rule-soft);overflow:auto;flex-shrink:0;max-height:40vh;padding:8px}.recent-label.svelte-oq97c4{text-transform:uppercase;letter-spacing:.06em;color:var(--ink-faint);padding:2px 10px 6px;font-size:11px}.recent-row.svelte-oq97c4{position:relative;display:flex;align-items: center}.recent-pick.svelte-oq97c4{display:flex;cursor:pointer;font:inherit;text-align:left;color:var(--ink);background:0 0;border:none;border-radius:8px;flex:1;align-items: center;gap:10px;min-width:0;padding:8px 10px}.recent-pick.svelte-oq97c4:hover{background:var(--ink);color:var(--bg)}.recent-pick.svelte-oq97c4:hover .recent-path:where(.svelte-oq97c4){color:var(--bg)}.recent-text.svelte-oq97c4{display:flex;flex-direction:column;min-width:0}.recent-name.svelte-oq97c4{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.recent-path.svelte-oq97c4{font-family:var(--font-mono);color:var(--ink-faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px}.recent-x.svelte-oq97c4{position:absolute;display:inline-flex;background:var(--bg-card);color:var(--ink-soft);cursor:pointer;opacity:0;border:none;border-radius:6px;align-items: center;padding:5px 7px;line-height:1;top:50%;right:8px;transform:translateY(-50%)}.recent-row.svelte-oq97c4:hover .recent-x:where(.svelte-oq97c4){opacity:1}.recent-x.svelte-oq97c4:hover{color:var(--bg);background:var(--ink)}.recent-toggle.svelte-oq97c4{cursor:pointer;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.05em;color:var(--ink);background:0 0;border:none;margin:4px 6px 2px;padding:4px 6px;font-size:12px}.recent-toggle.svelte-oq97c4:hover{text-decoration:underline}.search.svelte-oq97c4{border-bottom:1px solid var(--rule-soft);padding:10px 18px}.search.svelte-oq97c4 input:where(.svelte-oq97c4){font-family:var(--font-mono);border:1px solid var(--ink);background:var(--bg);color:var(--ink);width:100%;padding:8px 12px;font-size:13px}.search.svelte-oq97c4 input:where(.svelte-oq97c4)::placeholder{color:var(--ink-faint)}.search.svelte-oq97c4 input:where(.svelte-oq97c4):focus{outline:none;box-shadow:inset 3px 3px 0 var(--rule-soft)}.list.svelte-oq97c4{overflow:auto;flex:1;padding:8px}.row.svelte-oq97c4{display:flex;border:1px solid #0000;border-radius:8px;align-items: center;gap:6px;padding:2px 4px}.row.first-match.svelte-oq97c4{border-color:var(--rule-soft)}.row.svelte-oq97c4 .nav:where(.svelte-oq97c4){display:flex;cursor:pointer;font:inherit;text-align:left;color:var(--ink);background:0 0;border:none;border-radius:8px;flex:1;align-items: center;gap:10px;padding:9px 10px}.row.svelte-oq97c4 .nav:where(.svelte-oq97c4):hover{background:var(--ink);color:var(--bg)}.row.svelte-oq97c4 .nav:where(.svelte-oq97c4):hover .icon:where(.svelte-oq97c4){color:var(--bg)}.ename.svelte-oq97c4{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.badge.svelte-oq97c4{font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.08em;color:var(--ink);border:1px solid var(--ink);background:0 0;padding:2px 5px;font-size:9px;font-weight:600}.row.svelte-oq97c4 .nav:where(.svelte-oq97c4):hover .badge:where(.svelte-oq97c4){color:var(--bg);border-color:var(--bg)}.pick-inline.svelte-oq97c4{font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.05em;border:1px solid var(--ink);background:var(--bg-card);cursor:pointer;color:var(--ink);padding:6px 10px;font-size:11px;font-weight:600}.pick-inline.svelte-oq97c4:hover{background:var(--ink);color:var(--bg)}.muted.svelte-oq97c4{color:var(--ink-faint);text-align:center;padding:18px}.muted.err.svelte-oq97c4{color:var(--ink);font-weight:600}.warn.svelte-oq97c4{display:flex;background:var(--warn-bg);color:var(--warn-ink);font-family:var(--font-mono);border-top:1px solid var(--warn-line);align-items: flex-start;gap:8px;padding:10px 18px;font-size:12.5px}.warn.svelte-oq97c4 svg{flex:none;margin-top:1px}.warn.svelte-oq97c4 code:where(.svelte-oq97c4){font-family:var(--font-mono);border:1px solid var(--warn-line);background:#ffffff59;padding:0 5px;font-size:.9em}.foot.svelte-oq97c4{display:flex;border-top:1px solid var(--ink);justify-content:space-between;align-items: center;gap:12px;padding:14px 18px}.hint.svelte-oq97c4{font-family:var(--font-mono);color:var(--ink-faint);font-size:12px}.primary.svelte-oq97c4{font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;border:1px solid var(--ink);background:var(--ink);color:var(--bg);cursor:pointer;white-space:nowrap;padding:9px 16px;font-size:13px;font-weight:700}.primary.svelte-oq97c4:hover:not(:disabled){background:var(--bg-card);color:var(--ink)}.primary.svelte-oq97c4:disabled{opacity:.4;cursor:not-allowed}.icon.svelte-oq97c4{display:inline-flex;color:var(--ink-soft);align-items: center}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
.cog.svelte-r39acv{display:inline-flex;color:var(--ink);border-left:1px solid var(--rule);flex:none;justify-content:center;align-items: center;width:48px;margin-left:auto}.cog.svelte-r39acv:hover{background:var(--ink);color:var(--bg)}.tabs.svelte-r39acv{display:flex;overflow-x:auto;align-items:stretch;min-width:0}.tab.svelte-r39acv{display:inline-flex;border-right:1px solid var(--rule-soft);align-items:stretch}.tab.active.svelte-r39acv{background:var(--ink)}.tab.attn-done.svelte-r39acv{animation:svelte-r39acv-attn-pulse-green 1.8s ease-in-out infinite}.tab.attn-waiting.svelte-r39acv{animation:svelte-r39acv-attn-pulse-amber 1.8s ease-in-out infinite}@keyframes svelte-r39acv-attn-pulse-green{0%,to{background:0 0;box-shadow:inset 0 -2px 0 color-mix(in srgb,var(--attn-done)40%,transparent)}50%{background:color-mix(in srgb,var(--attn-done)32%,transparent);box-shadow:inset 0 -2px 0 var(--attn-done)}}@keyframes svelte-r39acv-attn-pulse-amber{0%,to{background:0 0;box-shadow:inset 0 -2px 0 color-mix(in srgb,var(--attn-waiting)40%,transparent)}50%{background:color-mix(in srgb,var(--attn-waiting)32%,transparent);box-shadow:inset 0 -2px 0 var(--attn-waiting)}}@media (prefers-reduced-motion:reduce){.tab.attn-done.svelte-r39acv,.tab.attn-waiting.svelte-r39acv{animation-duration:0s;background:color-mix(in srgb,var(--attn-done)28%,transparent)}.tab.attn-waiting.svelte-r39acv{background:color-mix(in srgb,var(--attn-waiting)28%,transparent)}}.tab-label.svelte-r39acv{appearance:none;cursor:pointer;color:var(--ink-soft);font-family:var(--font-mono);display:inline-flex;text-transform:uppercase;letter-spacing:.06em;background:0 0;border:0;align-items: center;gap:10px;max-width:240px;padding:0 12px;font-size:12px}.tab-name.svelte-r39acv{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tab.svelte-r39acv:not(.active) .tab-label:where(.svelte-r39acv):hover{color:var(--ink)}.tab.active.svelte-r39acv .tab-label:where(.svelte-r39acv){color:var(--bg)}.tab-label.editing.svelte-r39acv{cursor:default}.tab-name-edit.svelte-r39acv{font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;border:1px solid var(--ink-soft);background:var(--bg);color:var(--ink);outline:none;width:130px;margin:0;padding:2px 4px;font-size:12px}
|
|
1
|
+
.cog.svelte-r39acv{display:inline-flex;color:var(--ink);border-left:1px solid var(--rule);flex:none;justify-content:center;align-items: center;width:48px;margin-left:auto}.cog.svelte-r39acv:hover{background:var(--ink);color:var(--bg)}.tabs.svelte-r39acv{display:flex;overflow-x:auto;align-items:stretch;min-width:0}.tab.svelte-r39acv{display:inline-flex;border-right:1px solid var(--rule-soft);align-items:stretch}.tab.active.svelte-r39acv{background:var(--ink)}.tab.attn-done.svelte-r39acv{animation:svelte-r39acv-attn-pulse-green 1.8s ease-in-out infinite}.tab.attn-waiting.svelte-r39acv{animation:svelte-r39acv-attn-pulse-amber 1.8s ease-in-out infinite}@keyframes svelte-r39acv-attn-pulse-green{0%,to{background:0 0;box-shadow:inset 0 -2px 0 color-mix(in srgb,var(--attn-done)40%,transparent)}50%{background:color-mix(in srgb,var(--attn-done)32%,transparent);box-shadow:inset 0 -2px 0 var(--attn-done)}}@keyframes svelte-r39acv-attn-pulse-amber{0%,to{background:0 0;box-shadow:inset 0 -2px 0 color-mix(in srgb,var(--attn-waiting)40%,transparent)}50%{background:color-mix(in srgb,var(--attn-waiting)32%,transparent);box-shadow:inset 0 -2px 0 var(--attn-waiting)}}@media (prefers-reduced-motion:reduce){.tab.attn-done.svelte-r39acv,.tab.attn-waiting.svelte-r39acv{animation-duration:0s;background:color-mix(in srgb,var(--attn-done)28%,transparent)}.tab.attn-waiting.svelte-r39acv{background:color-mix(in srgb,var(--attn-waiting)28%,transparent)}}.tab-label.svelte-r39acv{appearance:none;cursor:pointer;color:var(--ink-soft);font-family:var(--font-mono);display:inline-flex;text-transform:uppercase;letter-spacing:.06em;background:0 0;border:0;align-items: center;gap:10px;max-width:240px;padding:0 12px;font-size:12px}.tab-name.svelte-r39acv{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tab-mode.svelte-r39acv{display:inline-flex;opacity:.75;flex:none;align-items: center}.tab.svelte-r39acv:not(.active) .tab-label:where(.svelte-r39acv):hover{color:var(--ink)}.tab.active.svelte-r39acv .tab-label:where(.svelte-r39acv){color:var(--bg)}.tab-label.editing.svelte-r39acv{cursor:default}.tab-name-edit.svelte-r39acv{font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;border:1px solid var(--ink-soft);background:var(--bg);color:var(--ink);outline:none;width:130px;margin:0;padding:2px 4px;font-size:12px}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
.page.svelte-1n6ht58{display:flex;flex-direction:column;min-height:100vh}.title.svelte-1n6ht58{display:inline-flex;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.08em;color:var(--ink-soft);align-items: center;padding:0 14px;font-size:12px}.content.svelte-1n6ht58{display:flex;flex-direction:column;flex:1;align-items: center;gap:16px;padding:32px 20px}.card.svelte-1n6ht58{background:var(--bg-card);border:1px solid var(--rule);width:100%;max-width:560px;height:max-content}.danger-card.svelte-1n6ht58{border-color:var(--danger)}.disabled-card.svelte-1n6ht58{opacity:.55;pointer-events:none}.danger-card.svelte-1n6ht58 .btn{white-space:nowrap;flex:none}.version.svelte-1n6ht58{font-family:var(--font-mono);letter-spacing:.08em;color:var(--ink-soft);font-size:11px}.shutting.svelte-1n6ht58{font-family:var(--font-mono);color:var(--danger);text-align:right;flex:none;max-width:200px;font-size:12px;line-height:1.4}.row.svelte-1n6ht58{display:flex;justify-content:space-between;align-items: center;gap:16px;padding:18px}.row.divided.svelte-1n6ht58{border-top:1px solid var(--rule);align-items: flex-start}.row.divided.svelte-1n6ht58 .btn{white-space:nowrap;flex:none}.sub.svelte-1n6ht58{padding:0 18px 14px}code.svelte-1n6ht58{font-family:var(--font-mono);background:var(--bg);border:1px solid var(--rule);border-radius:3px;padding:1px 4px;font-size:.92em}.label.svelte-1n6ht58{display:flex;color:var(--ink);align-items: flex-start;gap:12px;min-width:0}.label.svelte-1n6ht58>svg{flex:none}.text.svelte-1n6ht58{min-width:0}.name.svelte-1n6ht58{font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;font-size:13px}.arch.svelte-1n6ht58{letter-spacing:.04em;color:var(--ink-soft);border:1px solid var(--rule);vertical-align:middle;border-radius:3px;margin-left:8px;padding:2px 6px;font-size:11px}.tip.svelte-1n6ht58{color:var(--ink-soft);background:color-mix(in srgb,var(--info)10%,transparent);border:1px solid color-mix(in srgb,var(--info)35%,transparent);border-radius:4px;width:100%;margin-top:4px;padding:8px 10px}.desc.svelte-1n6ht58{font-family:var(--font-mono);color:var(--ink-faint);margin-top:4px;font-size:12px;line-height:1.4}.image-row.svelte-1n6ht58,.token-row.svelte-1n6ht58{flex-wrap:wrap}.image-controls.svelte-1n6ht58{display:flex;flex:1;justify-content:flex-end;align-items: center;gap:8px;min-width:220px}.image-input.svelte-1n6ht58{font-family:var(--font-mono);color:var(--ink);background:var(--bg);border:1px solid var(--rule);flex:1;min-width:0;padding:8px 10px;font-size:12px}.image-input.svelte-1n6ht58:focus-visible{outline:2px solid var(--ink);outline-offset:1px}.image-controls.svelte-1n6ht58 .btn{flex:none}.msg.svelte-1n6ht58{font-family:var(--font-mono);width:100%;margin-top:2px;font-size:12px;line-height:1.4}.msg.error.svelte-1n6ht58{color:var(--danger)}.msg.ok.svelte-1n6ht58{color:var(--ink-soft)}.pets.svelte-1n6ht58{display:grid;grid-template-columns:repeat(auto-fill,minmax(44px,1fr));gap:6px}.pets.svelte-1n6ht58 form:where(.svelte-1n6ht58){display:contents}.pet.svelte-1n6ht58:disabled{cursor:progress}.pet.svelte-1n6ht58{display:flex;appearance:none;cursor:pointer;background:0 0;border:1px solid #0000;justify-content:center;align-items: center;padding:4px}.pet.svelte-1n6ht58:hover{border-color:var(--rule)}.pet.selected.svelte-1n6ht58{border-color:var(--attn-done);background:var(--switch-on-bg)}.pet.svelte-1n6ht58:focus-visible{outline:2px solid var(--ink);outline-offset:1px}.switch.svelte-1n6ht58{position:relative;cursor:pointer;flex:none}.switch.svelte-1n6ht58 input:where(.svelte-1n6ht58){position:absolute;opacity:0;cursor:pointer;margin:0;inset:0}.track.svelte-1n6ht58{display:block;border:1px solid var(--rule);background:var(--bg);border-radius:999px;width:44px;height:24px;transition:background .15s}.thumb.svelte-1n6ht58{display:block;background:var(--ink-faint);border-radius:999px;width:18px;height:18px;margin:2px;transition:transform .15s,background .15s}.switch.svelte-1n6ht58 input:where(.svelte-1n6ht58):checked+.track:where(.svelte-1n6ht58){background:var(--switch-on-bg)}.switch.svelte-1n6ht58 input:where(.svelte-1n6ht58):checked+.track:where(.svelte-1n6ht58) .thumb:where(.svelte-1n6ht58){background:var(--ink);transform:translate(20px)}.switch.svelte-1n6ht58 input:where(.svelte-1n6ht58):focus-visible+.track:where(.svelte-1n6ht58){outline:2px solid var(--ink);outline-offset:2px}.model-fields.svelte-1n6ht58{display:flex;flex-direction:column;flex:1;gap:6px;min-width:220px}.model-row.svelte-1n6ht58{display:flex;align-items: center;gap:8px}.model-label.svelte-1n6ht58{font-family:var(--font-mono);color:var(--ink-soft);text-align:right;flex:none;width:72px;font-size:11px}.model-row.svelte-1n6ht58 .image-input:where(.svelte-1n6ht58){flex:1}.model-save-row.svelte-1n6ht58{display:flex;justify-content:flex-end;margin-top:4px}.var-row.svelte-1n6ht58{flex-direction:column;align-items:stretch;gap:10px}.var-list.svelte-1n6ht58{display:flex;flex-direction:column;gap:6px}.var-item.svelte-1n6ht58{display:flex;background:var(--bg);border:1px solid var(--rule);align-items: center;gap:8px;padding:6px 8px}.var-name.svelte-1n6ht58{font-family:var(--font-mono);color:var(--ink);word-break:break-all;flex:1;min-width:0;font-size:12px}.var-status.svelte-1n6ht58{font-family:var(--font-mono);letter-spacing:.04em;color:var(--danger);flex:none;font-size:11px}.var-status.present.svelte-1n6ht58{color:var(--ink-soft)}.var-remove.svelte-1n6ht58{display:inline-flex;color:var(--ink-soft);border:1px solid var(--rule);cursor:pointer;background:0 0;flex:none;justify-content:center;align-items: center;width:22px;height:22px;padding:0}.var-remove.svelte-1n6ht58:hover:not(:disabled){color:var(--danger);border-color:var(--danger)}.var-remove.svelte-1n6ht58:disabled{opacity:.4;cursor:not-allowed}.var-empty.svelte-1n6ht58{font-family:var(--font-mono);color:var(--ink-faint);font-size:12px}.var-add.svelte-1n6ht58{display:flex;gap:8px}.var-add.svelte-1n6ht58 .btn{flex:none}
|
|
1
|
+
.page.svelte-1n6ht58{display:flex;flex-direction:column;min-height:100vh}.title.svelte-1n6ht58{display:inline-flex;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.08em;color:var(--ink-soft);align-items: center;padding:0 14px;font-size:12px}.content.svelte-1n6ht58{display:flex;flex-direction:column;flex:1;align-items: center;gap:16px;padding:32px 20px}.card.svelte-1n6ht58{background:var(--bg-card);border:1px solid var(--rule);width:100%;max-width:560px;height:max-content}.danger-card.svelte-1n6ht58{border-color:var(--danger)}.disabled-card.svelte-1n6ht58{opacity:.55;pointer-events:none}.danger-card.svelte-1n6ht58 .btn{white-space:nowrap;flex:none}.version.svelte-1n6ht58{font-family:var(--font-mono);letter-spacing:.08em;color:var(--ink-soft);font-size:11px}.shutting.svelte-1n6ht58{font-family:var(--font-mono);color:var(--danger);text-align:right;flex:none;max-width:200px;font-size:12px;line-height:1.4}.row.svelte-1n6ht58{display:flex;justify-content:space-between;align-items: center;gap:16px;padding:18px}.row.divided.svelte-1n6ht58{border-top:1px solid var(--rule);align-items: flex-start}.row.divided.svelte-1n6ht58 .btn{white-space:nowrap;flex:none}.sub.svelte-1n6ht58{padding:0 18px 14px}code.svelte-1n6ht58{font-family:var(--font-mono);background:var(--bg);border:1px solid var(--rule);border-radius:3px;padding:1px 4px;font-size:.92em}.label.svelte-1n6ht58{display:flex;color:var(--ink);align-items: flex-start;gap:12px;min-width:0}.label.svelte-1n6ht58>svg{flex:none}.text.svelte-1n6ht58{min-width:0}.name.svelte-1n6ht58{font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;font-size:13px}.arch.svelte-1n6ht58{letter-spacing:.04em;color:var(--ink-soft);border:1px solid var(--rule);vertical-align:middle;border-radius:3px;margin-left:8px;padding:2px 6px;font-size:11px}.tip.svelte-1n6ht58{color:var(--ink-soft);background:color-mix(in srgb,var(--info)10%,transparent);border:1px solid color-mix(in srgb,var(--info)35%,transparent);border-radius:4px;width:100%;margin-top:4px;padding:8px 10px}.desc.svelte-1n6ht58{font-family:var(--font-mono);color:var(--ink-faint);margin-top:4px;font-size:12px;line-height:1.4}.image-row.svelte-1n6ht58,.token-row.svelte-1n6ht58{flex-wrap:wrap}.image-controls.svelte-1n6ht58{display:flex;flex:1;justify-content:flex-end;align-items: center;gap:8px;min-width:220px}.image-input.svelte-1n6ht58{font-family:var(--font-mono);color:var(--ink);background:var(--bg);border:1px solid var(--rule);flex:1;min-width:0;padding:8px 10px;font-size:12px}.image-input.svelte-1n6ht58:focus-visible{outline:2px solid var(--ink);outline-offset:1px}.image-controls.svelte-1n6ht58 .btn{flex:none}.msg.svelte-1n6ht58{font-family:var(--font-mono);width:100%;margin-top:2px;font-size:12px;line-height:1.4}.msg.error.svelte-1n6ht58{color:var(--danger)}.msg.ok.svelte-1n6ht58{color:var(--ink-soft)}.pets.svelte-1n6ht58{display:grid;grid-template-columns:repeat(auto-fill,minmax(44px,1fr));gap:6px}.pets.svelte-1n6ht58 form:where(.svelte-1n6ht58){display:contents}.pet.svelte-1n6ht58:disabled{cursor:progress}.pet.svelte-1n6ht58{display:flex;appearance:none;cursor:pointer;background:0 0;border:1px solid #0000;justify-content:center;align-items: center;padding:4px}.pet.svelte-1n6ht58:hover{border-color:var(--rule)}.pet.selected.svelte-1n6ht58{border-color:var(--attn-done);background:var(--switch-on-bg)}.pet.svelte-1n6ht58:focus-visible{outline:2px solid var(--ink);outline-offset:1px}.mode-toggle.svelte-1n6ht58{display:flex;border:1px solid var(--ink);flex:none}.mode-btn.svelte-1n6ht58{display:inline-flex;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.05em;background:var(--bg);color:var(--ink);cursor:pointer;border:none;align-items: center;gap:6px;padding:7px 14px;font-size:12px;font-weight:700}.mode-btn.svelte-1n6ht58+.mode-btn:where(.svelte-1n6ht58){border-left:1px solid var(--ink)}.mode-btn.active.svelte-1n6ht58{background:var(--ink);color:var(--bg)}.mode-btn.svelte-1n6ht58:disabled{cursor:not-allowed}.switch.svelte-1n6ht58{position:relative;cursor:pointer;flex:none}.switch.svelte-1n6ht58 input:where(.svelte-1n6ht58){position:absolute;opacity:0;cursor:pointer;margin:0;inset:0}.track.svelte-1n6ht58{display:block;border:1px solid var(--rule);background:var(--bg);border-radius:999px;width:44px;height:24px;transition:background .15s}.thumb.svelte-1n6ht58{display:block;background:var(--ink-faint);border-radius:999px;width:18px;height:18px;margin:2px;transition:transform .15s,background .15s}.switch.svelte-1n6ht58 input:where(.svelte-1n6ht58):checked+.track:where(.svelte-1n6ht58){background:var(--switch-on-bg)}.switch.svelte-1n6ht58 input:where(.svelte-1n6ht58):checked+.track:where(.svelte-1n6ht58) .thumb:where(.svelte-1n6ht58){background:var(--ink);transform:translate(20px)}.switch.svelte-1n6ht58 input:where(.svelte-1n6ht58):focus-visible+.track:where(.svelte-1n6ht58){outline:2px solid var(--ink);outline-offset:2px}.model-fields.svelte-1n6ht58{display:flex;flex-direction:column;flex:1;gap:6px;min-width:220px}.model-row.svelte-1n6ht58{display:flex;align-items: center;gap:8px}.model-label.svelte-1n6ht58{font-family:var(--font-mono);color:var(--ink-soft);text-align:right;flex:none;width:72px;font-size:11px}.model-row.svelte-1n6ht58 .image-input:where(.svelte-1n6ht58){flex:1}.model-save-row.svelte-1n6ht58{display:flex;justify-content:flex-end;margin-top:4px}.var-row.svelte-1n6ht58{flex-direction:column;align-items:stretch;gap:10px}.var-list.svelte-1n6ht58{display:flex;flex-direction:column;gap:6px}.var-item.svelte-1n6ht58{display:flex;background:var(--bg);border:1px solid var(--rule);align-items: center;gap:8px;padding:6px 8px}.var-name.svelte-1n6ht58{font-family:var(--font-mono);color:var(--ink);word-break:break-all;flex:1;min-width:0;font-size:12px}.var-status.svelte-1n6ht58{font-family:var(--font-mono);letter-spacing:.04em;color:var(--danger);flex:none;font-size:11px}.var-status.present.svelte-1n6ht58{color:var(--ink-soft)}.var-remove.svelte-1n6ht58{display:inline-flex;color:var(--ink-soft);border:1px solid var(--rule);cursor:pointer;background:0 0;flex:none;justify-content:center;align-items: center;width:22px;height:22px;padding:0}.var-remove.svelte-1n6ht58:hover:not(:disabled){color:var(--danger);border-color:var(--danger)}.var-remove.svelte-1n6ht58:disabled{opacity:.4;cursor:not-allowed}.var-empty.svelte-1n6ht58{font-family:var(--font-mono);color:var(--ink-faint);font-size:12px}.var-add.svelte-1n6ht58{display:flex;gap:8px}.var-add.svelte-1n6ht58 .btn{flex:none}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.term.svelte-ui3nvu{background:var(--bg);box-sizing:border-box;width:100%;height:100%;padding:6px 8px}.term.svelte-ui3nvu .xterm,.term.svelte-ui3nvu .xterm-viewport{height:100%}.overlay.svelte-ui3nvu{position:absolute;display:flex;pointer-events:none;align-items: center;gap:8px;top:6px;right:8px}.status.svelte-ui3nvu{font-family:var(--font-mono);letter-spacing:.05em;text-transform:uppercase;color:var(--ink-faint);font-size:11px}.overlay.svelte-ui3nvu button{pointer-events:auto;appearance:none;display:inline-flex;cursor:pointer;background:var(--bg);border:1px solid var(--rule);color:var(--ink);opacity:.3;justify-content:center;align-items: center;width:24px;height:24px;padding:0;transition:opacity .15s}.overlay.svelte-ui3nvu button:hover,.overlay.svelte-ui3nvu button[aria-pressed=true]{opacity:1;background:var(--ink);color:var(--bg)}.overlay.svelte-ui3nvu button:focus-visible{opacity:1;background:var(--ink);color:var(--bg)}@media (prefers-reduced-motion:reduce){.overlay.svelte-ui3nvu button{transition:none}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.split.svelte-1ysvu88{display:flex;width:100%;height:100%}.side.svelte-1ysvu88{position:relative;flex:0 auto;min-width:0}.side.hidden.svelte-1ysvu88{display:none}.divider.svelte-1ysvu88{cursor:col-resize;background:var(--rule);z-index:1;background-clip:content-box;border-left:3px solid #0000;border-right:3px solid #0000;flex:none;width:7px;margin:0 -3px}.divider.hidden.svelte-1ysvu88{display:none}.divider.svelte-1ysvu88:focus-visible{outline:2px solid var(--ink);outline-offset:-2px}
|
package/README.md
CHANGED
|
@@ -18,6 +18,8 @@ The UI opens at `http://localhost:6969`. State (SQLite DB + per-instance workspa
|
|
|
18
18
|
- `DATA_DIR` — where state lives (default `~/.codebay`)
|
|
19
19
|
- `DOCKER_HOST` — Docker socket/URL (defaults to your active Docker context)
|
|
20
20
|
- `HOST` — bind address (default `127.0.0.1`). Set `0.0.0.0` to reach codebay from other machines; your instances' forwarded app ports are then published on all interfaces too. Each container's code-server port stays loopback-only regardless — it runs without a password of its own and is reached through the Basic-Auth-gated `/p/:id/` proxy instead. Existing instances need a **Restart** to pick up the new binding.
|
|
21
|
+
- `PUBLIC_ORIGIN` — the origin you actually load codebay from (default `http://localhost:<PORT>`). Every form POST — creating an instance, restarting one, saving settings — is checked against it, so reaching the UI at any other address (a LAN IP, a hostname, or a reverse proxy / tunnel that terminates TLS) makes those actions fail with a `403 Cross-site POST form submissions are forbidden` until you set this. Give the exact scheme + host + port your browser shows, with no trailing slash — `PUBLIC_ORIGIN=http://192.168.1.50:6969` or `PUBLIC_ORIGIN=https://codebay.example.com`. Behind a TLS-terminating proxy this is the public `https://` URL, not the loopback address the proxy forwards to.
|
|
22
|
+
- `TRUSTED_ORIGINS` — comma-separated extra origins accepted alongside `PUBLIC_ORIGIN`, for when the UI is legitimately reachable at more than one address (e.g. `http://localhost:6969,http://192.168.1.50:6969`)
|
|
21
23
|
- `BASIC_AUTH_PASSWORD` — enables HTTP Basic Auth over the whole UI (disabled when unset); required if you bind beyond loopback with `HOST=0.0.0.0`
|
|
22
24
|
- `CODEBAY_CLAUDE_CODE_TOKEN` — Claude Code token to inject into every container (e.g. from `claude setup-token`) instead of discovering the host's credentials
|
|
23
25
|
- `CODEBAY_GITHUB_TOKEN` — GitHub token to inject instead of reading `gh auth token` from the host
|
|
@@ -25,6 +27,7 @@ The UI opens at `http://localhost:6969`. State (SQLite DB + per-instance workspa
|
|
|
25
27
|
|
|
26
28
|
## Troubleshooting
|
|
27
29
|
|
|
30
|
+
- `403 Cross-site POST form submissions are forbidden` when clicking anything that saves — codebay is being reached at an origin other than `http://localhost:<PORT>`. Set `PUBLIC_ORIGIN` to the URL in your browser's address bar and restart (see Configuration).
|
|
28
31
|
- `warn: incorrect peer dependency "svelte@5.56.8"` on startup — harmless. It comes from `svelte-french-toast`, whose published peer range predates Svelte 5; the library works correctly on Svelte 5. Nothing to fix.
|
|
29
32
|
|
|
30
33
|
## Development
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE instances ADD COLUMN mode TEXT NOT NULL DEFAULT 'ide';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE instances ADD COLUMN terminal_split INTEGER NOT NULL DEFAULT 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codebay",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Web UI for spinning up isolated devcontainer instances from any local folder, each with a browser-based VS Code (code-server) — built for parallel Claude Code workflows.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -63,6 +63,8 @@
|
|
|
63
63
|
"@fontsource-variable/doto": "^5.3.0",
|
|
64
64
|
"@fontsource-variable/jetbrains-mono": "^5.3.0",
|
|
65
65
|
"@lucide/svelte": "^1.28.0",
|
|
66
|
+
"@xterm/addon-fit": "^0.11.0",
|
|
67
|
+
"@xterm/xterm": "^6.0.0",
|
|
66
68
|
"@zihaolam/bun-sqlite-migrations": "^1.0.3",
|
|
67
69
|
"dockerode": "^5.0.1",
|
|
68
70
|
"mochi-framework": "^0.9.1",
|
package/src/avatars/serialize.ts
CHANGED
|
@@ -4,6 +4,10 @@ import { type AvatarArt, ROWS, COLS, ON, GRAY } from './types.ts';
|
|
|
4
4
|
|
|
5
5
|
export const REPO_URL = 'https://github.com/khromov/codebay';
|
|
6
6
|
|
|
7
|
+
// A brand-new sprite vs. a hand-redraw of an existing (AI-generated) one — the two
|
|
8
|
+
// produce differently-shaped issues so the contribution skill can tell them apart.
|
|
9
|
+
export type ContributionMode = 'create' | 'edit';
|
|
10
|
+
|
|
7
11
|
// The name doubles as a filename and a catalog key, hence the aggressive squash.
|
|
8
12
|
export function normalizeName(raw: string): string {
|
|
9
13
|
return raw
|
|
@@ -27,7 +31,8 @@ export function cellsToPixels(cells: number[]): string[] {
|
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
// Byte-identical to the hand-written sprites (tabs, single quotes, trailing newline),
|
|
30
|
-
// so a maintainer can drop it in and prettier won't touch it.
|
|
34
|
+
// so a maintainer can drop it in and prettier won't touch it. It never emits `robot`,
|
|
35
|
+
// so a redraw of an AI sprite lands as a plain hand-drawn module — human-made by construction.
|
|
31
36
|
export function toModuleSource(art: AvatarArt): string {
|
|
32
37
|
const rows = art.pixels.map((row) => `\t\t'${row}'`).join(',\n');
|
|
33
38
|
return `import type { AvatarArt } from './types.ts';
|
|
@@ -44,9 +49,15 @@ export default art;
|
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
// The art is ~64 chars, far below any URL length limit.
|
|
47
|
-
export function toIssueUrl(art: AvatarArt): string {
|
|
48
|
-
const
|
|
49
|
-
const
|
|
52
|
+
export function toIssueUrl(art: AvatarArt, mode: ContributionMode = 'create'): string {
|
|
53
|
+
const body = mode === 'edit' ? editIssueBody(art) : createIssueBody(art);
|
|
54
|
+
const title = mode === 'edit' ? `Avatar edit: ${art.name}` : `Avatar contribution: ${art.name}`;
|
|
55
|
+
const params = new URLSearchParams({ title, body });
|
|
56
|
+
return `${REPO_URL}/issues/new?${params}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function createIssueBody(art: AvatarArt): string {
|
|
60
|
+
return [
|
|
50
61
|
"Hi! I drew this avatar in the editor easter egg and I'd love to see it in the official set.",
|
|
51
62
|
'',
|
|
52
63
|
'```',
|
|
@@ -61,6 +72,24 @@ export function toIssueUrl(art: AvatarArt): string {
|
|
|
61
72
|
'',
|
|
62
73
|
'_It should satisfy `src/avatars/avatars.test.ts`: exactly 8×8, only `#`/`+`/`.` pixels, unique name._'
|
|
63
74
|
].join('\n');
|
|
64
|
-
|
|
65
|
-
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// A redraw replaces an existing AI-generated sprite in place; the module carries no
|
|
78
|
+
// `robot` flag, so landing it promotes the sprite from robot-drawn to hand-drawn.
|
|
79
|
+
function editIssueBody(art: AvatarArt): string {
|
|
80
|
+
return [
|
|
81
|
+
`Hi! I redrew the existing \`${art.name}\` sprite by hand in the editor easter egg — it started as an AI-generated placeholder and this replaces it.`,
|
|
82
|
+
'',
|
|
83
|
+
'```',
|
|
84
|
+
...art.pixels,
|
|
85
|
+
'```',
|
|
86
|
+
'',
|
|
87
|
+
`Ready-to-paste replacement for \`src/avatars/${art.name}.ts\` (the \`robot\` flag is intentionally dropped — it's hand-drawn now):`,
|
|
88
|
+
'',
|
|
89
|
+
'```ts',
|
|
90
|
+
toModuleSource(art).trimEnd(),
|
|
91
|
+
'```',
|
|
92
|
+
'',
|
|
93
|
+
'_It should satisfy `src/avatars/avatars.test.ts`: exactly 8×8, only `#`/`+`/`.` pixels._'
|
|
94
|
+
].join('\n');
|
|
66
95
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { ideUrl, type Instance, type Preflight } from '../types.ts';
|
|
2
|
+
import { ideUrl, type Instance, type InstanceFilter, type Preflight } from '../types.ts';
|
|
3
3
|
import { findAvatar, type AvatarArt } from '../avatars/index.ts';
|
|
4
4
|
import { SvelteSet } from 'svelte/reactivity';
|
|
5
5
|
import DashboardView from './DashboardView.svelte';
|
|
6
6
|
import IdeBar from './IdeBar.svelte';
|
|
7
7
|
import IdeLoader from './IdeLoader.svelte';
|
|
8
|
+
import TerminalSplit from './TerminalSplit.svelte';
|
|
8
9
|
import { playChime, unlockAudio } from '../sound.ts';
|
|
9
10
|
import { liveStream } from '../live.ts';
|
|
10
11
|
import { apiPost } from '../api.ts';
|
|
@@ -15,12 +16,14 @@
|
|
|
15
16
|
preflight,
|
|
16
17
|
initialPath,
|
|
17
18
|
snapshot,
|
|
18
|
-
pet
|
|
19
|
+
pet,
|
|
20
|
+
filter
|
|
19
21
|
}: {
|
|
20
22
|
preflight: Preflight;
|
|
21
23
|
initialPath: string;
|
|
22
24
|
snapshot: Instance[];
|
|
23
25
|
pet?: AvatarArt;
|
|
26
|
+
filter: InstanceFilter;
|
|
24
27
|
} = $props();
|
|
25
28
|
|
|
26
29
|
// Seeding from the SSR snapshot is intentional — the live stream overwrites it.
|
|
@@ -32,6 +35,9 @@
|
|
|
32
35
|
// Seeded from SSR, then updated live so changing the pet in settings swaps the header without a reload.
|
|
33
36
|
// svelte-ignore state_referenced_locally
|
|
34
37
|
let livePet = $state<AvatarArt | undefined>(pet);
|
|
38
|
+
// Owned here (not in DashboardView) so it survives the dashboard↔IDE tab switches that remount the view.
|
|
39
|
+
// svelte-ignore state_referenced_locally
|
|
40
|
+
let liveFilter = $state<InstanceFilter>(filter);
|
|
35
41
|
// svelte-ignore state_referenced_locally
|
|
36
42
|
let loaded = $state(snapshot.length > 0);
|
|
37
43
|
const running = $derived(instances.filter((i) => i.status === 'running'));
|
|
@@ -70,6 +76,14 @@
|
|
|
70
76
|
}
|
|
71
77
|
}
|
|
72
78
|
|
|
79
|
+
// Optimistic local update + best-effort persist; the broadcast echo reconciles every open tab.
|
|
80
|
+
function setFilter(v: InstanceFilter) {
|
|
81
|
+
liveFilter = v;
|
|
82
|
+
void apiPost('/api/settings/filter', { value: v }).catch(() => {
|
|
83
|
+
/* best-effort — the in-memory value already reflects the choice */
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
73
87
|
// Mochi has no client router, and a document reload would tear down the code-server iframes.
|
|
74
88
|
// svelte-ignore state_referenced_locally
|
|
75
89
|
let path = $state(initialPath);
|
|
@@ -174,6 +188,14 @@
|
|
|
174
188
|
livePet = findAvatar(msg.data.name ?? undefined);
|
|
175
189
|
return;
|
|
176
190
|
}
|
|
191
|
+
if (msg.type === 'filter') {
|
|
192
|
+
liveFilter = msg.data.value;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (msg.type === 'default-mode') {
|
|
196
|
+
livePreflight = { ...livePreflight, defaultMode: msg.data.mode };
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
177
199
|
if (msg.type === 'health') {
|
|
178
200
|
// A tick in flight when a rebuild starts probes the *old* container and
|
|
179
201
|
// reports it accessible, which would mount the iframe against the replacement too early.
|
|
@@ -250,7 +272,14 @@
|
|
|
250
272
|
oncancelrename={cancelRename}
|
|
251
273
|
/>
|
|
252
274
|
{:else}
|
|
253
|
-
<DashboardView
|
|
275
|
+
<DashboardView
|
|
276
|
+
preflight={livePreflight}
|
|
277
|
+
{instances}
|
|
278
|
+
{loaded}
|
|
279
|
+
pet={livePet}
|
|
280
|
+
filter={liveFilter}
|
|
281
|
+
onFilter={setFilter}
|
|
282
|
+
/>
|
|
254
283
|
{/if}
|
|
255
284
|
|
|
256
285
|
<!-- Always mounted, only hidden, so the iframes survive navigation. -->
|
|
@@ -259,14 +288,23 @@
|
|
|
259
288
|
{#if visited.has(inst.id)}
|
|
260
289
|
<div class="pane" class:active={inst.id === active}>
|
|
261
290
|
{#if mountable(inst.id)}
|
|
262
|
-
|
|
263
|
-
|
|
291
|
+
{#if inst.mode === 'terminal'}
|
|
292
|
+
<TerminalSplit
|
|
293
|
+
id={inst.id}
|
|
294
|
+
active={inst.id === active}
|
|
295
|
+
initialOpen={inst.terminal_split === 1}
|
|
296
|
+
/>
|
|
297
|
+
{:else}
|
|
298
|
+
<iframe src={ideUrl(inst)} title={inst.name} onload={() => loadedFrames.add(inst.id)}
|
|
299
|
+
></iframe>
|
|
300
|
+
{/if}
|
|
264
301
|
{/if}
|
|
265
|
-
<!-- Two distinct waits: the health probe is unbounded and gets the override,
|
|
266
|
-
|
|
302
|
+
<!-- Two distinct waits: the health probe is unbounded and gets the override, the
|
|
303
|
+
iframe's own `load` is bounded and gets a plain loader. The terminal has no
|
|
304
|
+
`load` event, so once it's mountable its own "connecting…" covers the gap. -->
|
|
267
305
|
{#if !mountable(inst.id)}
|
|
268
306
|
<IdeLoader stalledAfterMs={STALLED_AFTER_MS} onoverride={() => forced.add(inst.id)} />
|
|
269
|
-
{:else if !loadedFrames.has(inst.id)}
|
|
307
|
+
{:else if inst.mode !== 'terminal' && !loadedFrames.has(inst.id)}
|
|
270
308
|
<IdeLoader />
|
|
271
309
|
{/if}
|
|
272
310
|
</div>
|
|
@@ -67,7 +67,8 @@
|
|
|
67
67
|
// 0.5px is a crisp single device-pixel at 2×; at 1× the cells can't spare any gutter.
|
|
68
68
|
const gap = $derived(s >= 2 ? 0.5 : 0);
|
|
69
69
|
|
|
70
|
-
const
|
|
70
|
+
const resolved = $derived(art ?? pickAvatar(id));
|
|
71
|
+
const cells = $derived(decode(resolved));
|
|
71
72
|
// The 10×10 outer ring is unlit LED cells — a bezel of real dots, not blank padding.
|
|
72
73
|
const grid = $derived(
|
|
73
74
|
Array.from({ length: 100 }, (_, i) => {
|
|
@@ -86,7 +87,7 @@
|
|
|
86
87
|
class:ghosting
|
|
87
88
|
role="img"
|
|
88
89
|
aria-label={name}
|
|
89
|
-
title={name}
|
|
90
|
+
title={resolved.name}
|
|
90
91
|
style="width:{10 * s}px;height:{10 *
|
|
91
92
|
s}px;--gap:{gap}px;--bx:{bx};--by:{by};--spin:{spin}deg;--hue:{hue}deg"
|
|
92
93
|
{onpointerdown}
|