codebay 0.9.0 → 0.10.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/manifest.json +216 -149
- package/.mochi/svelte-client/HydratableIsland-aebn1fa1.js +1 -0
- package/.mochi/svelte-client/_hydrate-AppShell_2rtnxn6pgp2rh-rx6arrv5.js +2 -0
- package/.mochi/svelte-client/{_hydrate-AvatarsView_257s9yq1wwr4f-92hepzby.js → _hydrate-AvatarsView_257s9yq1wwr4f-jpjdmgpe.js} +1 -1
- package/.mochi/svelte-client/_hydrate-InstanceView_2ma45l9bybfhl-c9y47dat.js +1 -0
- package/.mochi/svelte-client/_hydrate-SettingsView_8hmarwmf8onq-d61feq89.js +38 -0
- package/.mochi/svelte-client/{chunk-2j95sv83.js → chunk-0fjx3z3r.js} +1 -1
- package/.mochi/svelte-client/chunk-8xvr02wh.js +2 -0
- package/.mochi/svelte-client/{chunk-m497q88s.js → chunk-9143wy3j.js} +2 -2
- package/.mochi/svelte-client/{chunk-v65n8th6.js → chunk-bn731rda.js} +1 -1
- package/.mochi/svelte-client/{chunk-a2psjmrm.js → chunk-j72r8pkk.js} +2 -2
- package/.mochi/svelte-client/chunk-k37wj1jm.js +2 -0
- package/.mochi/svelte-client/{chunk-mpv3qx28.js → chunk-p20tkpde.js} +1 -1
- package/.mochi/svelte-client/chunk-pc0fm37c.js +2 -0
- package/.mochi/svelte-client/{chunk-6rbs0mhx.js → chunk-t0ap7q89.js} +1 -1
- package/.mochi/svelte-client/{chunk-rs41by6w.js → chunk-wvtkwfsh.js} +1 -1
- package/.mochi/svelte-compile/{App-7vjcp38v.server.js → App-59cdj86f.server.js} +167 -103
- package/.mochi/svelte-compile/{Avatars-e2cfc95t.server.js → Avatars-fmagfyj2.server.js} +1 -1
- package/.mochi/svelte-compile/{Instance-h78n2dsv.server.js → Instance-s1gqwzmx.server.js} +16 -10
- package/.mochi/svelte-compile/{Settings-tav2asn7.server.js → Settings-k447mt0x.server.js} +540 -88
- package/.mochi/svelte-compile/{chunk-brjv9pyd.js → chunk-3t7v4hk0.js} +1 -7
- package/.mochi/svelte-compile/chunk-gkjgybb2.js +15 -0
- package/.mochi/svelte-compile/{chunk-m8ygpyhr.js → chunk-th2dbqvc.js} +20 -32
- package/.mochi/svelte-css/Button-oharop2xz6am.css +1 -0
- package/.mochi/svelte-css/IdeBar-3bgr3m5z0lpeg.css +1 -0
- package/.mochi/svelte-css/{InstanceView-3vzow3ttxzlh3.css → InstanceView-1pd7j16bd2b16.css} +1 -1
- package/.mochi/svelte-css/SettingsView-n3oy75uonzp2.css +1 -0
- package/.mochi/svelte-css/{TopBar-2q87li6yt8ids.css → TopBar-2y76zxwehrfos.css} +1 -1
- package/migrations/0007_instance_avatar.sql +1 -0
- package/package.json +2 -2
- package/src/avatars/diamond.ts +5 -6
- package/src/avatars/index.ts +1 -16
- package/src/avatars/pick.server.ts +25 -0
- package/src/avatars/whale.ts +7 -8
- package/src/components/AppShell.svelte +29 -4
- package/src/components/Avatar.svelte +8 -8
- package/src/components/Button.svelte +22 -1
- package/src/components/DashboardView.svelte +14 -2
- package/src/components/FolderBrowser.svelte +6 -2
- package/src/components/IdeBar.svelte +56 -13
- package/src/components/IdeLoader.svelte +15 -3
- package/src/components/InstanceCard.svelte +6 -1
- package/src/components/InstanceView.svelte +33 -15
- package/src/components/SettingsView.svelte +548 -1
- package/src/components/TopBar.svelte +37 -10
- package/src/components/UiShowcase.svelte +5 -10
- package/src/container-injections/claude-aliases.ts +1 -1
- package/src/container-injections/claude-code-custom.ts +2 -1
- package/src/container-injections/claude-code-ide-extension.ts +5 -0
- package/src/container-injections/claude-code-install.ts +67 -0
- package/src/container-injections/claude-code-models.ts +2 -1
- package/src/container-injections/claude-code-update.ts +62 -1
- package/src/container-injections/claude-effort-level.ts +32 -0
- package/src/container-injections/claude-permission-mode.ts +40 -0
- package/src/container-injections/custom-env-vars.ts +82 -0
- package/src/container-injections/host-env-vars.ts +2 -1
- package/src/lib/config.server.ts +3 -0
- package/src/lib/db.server.ts +8 -3
- package/src/lib/devcontainer.server.ts +182 -47
- package/src/lib/health.server.ts +55 -11
- package/src/lib/injections.server.ts +46 -36
- package/src/lib/instances.server.ts +185 -19
- package/src/lib/log-capture.server.ts +262 -0
- package/src/lib/ports.server.ts +31 -0
- package/src/lib/proxy.server.ts +21 -76
- package/src/pages/Settings.svelte +22 -1
- package/src/routes.ts +98 -2
- package/src/types.ts +36 -0
- package/.mochi/svelte-client/HydratableIsland-k6q8esj4.js +0 -1
- package/.mochi/svelte-client/_hydrate-AppShell_2rtnxn6pgp2rh-sg96rp17.js +0 -2
- package/.mochi/svelte-client/_hydrate-InstanceView_2ma45l9bybfhl-p3f649gk.js +0 -1
- package/.mochi/svelte-client/_hydrate-SettingsView_8hmarwmf8onq-dzmgmv78.js +0 -29
- package/.mochi/svelte-client/chunk-7wkvn2vx.js +0 -2
- package/.mochi/svelte-client/chunk-m4fzsd5b.js +0 -2
- package/.mochi/svelte-css/Button-2vtbrzb5kgu25.css +0 -1
- package/.mochi/svelte-css/IdeBar-10xodudcx57qr.css +0 -1
- package/.mochi/svelte-css/SettingsView-2qfauz68axro1.css +0 -1
- package/src/container-injections/claude-skip-permissions.ts +0 -28
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// src/types.ts
|
|
3
|
-
function ideUrl(instance) {
|
|
4
|
-
const base = `/p/${instance.id}/`;
|
|
5
|
-
return instance.remote_workspace_folder ? `${base}?folder=${encodeURIComponent(instance.remote_workspace_folder)}` : base;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
2
|
// src/api.ts
|
|
9
3
|
var APP_REQUEST_HEADER = "X-Codebay-Request";
|
|
10
4
|
async function apiFetch(url, init, fallbackMessage = "Request failed") {
|
|
@@ -55,4 +49,4 @@ function StatusBadge($$renderer, $$props) {
|
|
|
55
49
|
$$renderer.push(`<span${$.attr_class(`status ${$.stringify(status)}`, "svelte-1hqr9xd")}>${$.escape(label[status])}</span>`);
|
|
56
50
|
}
|
|
57
51
|
|
|
58
|
-
export {
|
|
52
|
+
export { apiFetch, apiPost, apiDelete, forwardedPortUrl, StatusBadge };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/types.ts
|
|
3
|
+
var CLAUDE_PERMISSION_MODES = [
|
|
4
|
+
"default",
|
|
5
|
+
"manual",
|
|
6
|
+
"auto",
|
|
7
|
+
"plan"
|
|
8
|
+
];
|
|
9
|
+
var CLAUDE_EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
10
|
+
function ideUrl(instance) {
|
|
11
|
+
const base = `/p/${instance.id}/`;
|
|
12
|
+
return instance.remote_workspace_folder ? `${base}?folder=${encodeURIComponent(instance.remote_workspace_folder)}` : base;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export { CLAUDE_PERMISSION_MODES, CLAUDE_EFFORT_LEVELS, ideUrl };
|
|
@@ -285,15 +285,14 @@ var crown_default = art17;
|
|
|
285
285
|
// src/avatars/diamond.ts
|
|
286
286
|
var art18 = {
|
|
287
287
|
name: "diamond",
|
|
288
|
-
robot: true,
|
|
289
288
|
pixels: [
|
|
290
289
|
".######.",
|
|
291
|
-
"
|
|
290
|
+
"##.#.#.#",
|
|
291
|
+
"#.#.#.#.",
|
|
292
292
|
"########",
|
|
293
|
-
"
|
|
294
|
-
"
|
|
295
|
-
"
|
|
296
|
-
"..####..",
|
|
293
|
+
"#.######",
|
|
294
|
+
".#.####.",
|
|
295
|
+
"..#.##..",
|
|
297
296
|
"...##..."
|
|
298
297
|
]
|
|
299
298
|
};
|
|
@@ -963,16 +962,15 @@ var wasp_default = art58;
|
|
|
963
962
|
// src/avatars/whale.ts
|
|
964
963
|
var art59 = {
|
|
965
964
|
name: "whale",
|
|
966
|
-
robot: true,
|
|
967
965
|
pixels: [
|
|
968
|
-
"
|
|
969
|
-
"
|
|
970
|
-
"
|
|
971
|
-
"
|
|
966
|
+
"#.#.#...",
|
|
967
|
+
".#.#....",
|
|
968
|
+
"..#.....",
|
|
969
|
+
"..#.....",
|
|
970
|
+
".####...",
|
|
972
971
|
"######.#",
|
|
973
|
-
"
|
|
974
|
-
"
|
|
975
|
-
"...##..."
|
|
972
|
+
"#.####.#",
|
|
973
|
+
"########"
|
|
976
974
|
]
|
|
977
975
|
};
|
|
978
976
|
var whale_default = art59;
|
|
@@ -1106,17 +1104,6 @@ var avatars = [
|
|
|
1106
1104
|
windmill_default,
|
|
1107
1105
|
yinyan_default
|
|
1108
1106
|
];
|
|
1109
|
-
function fnv1a(s) {
|
|
1110
|
-
let h = 2166136261;
|
|
1111
|
-
for (let i = 0;i < s.length; i++) {
|
|
1112
|
-
h ^= s.charCodeAt(i);
|
|
1113
|
-
h = Math.imul(h, 16777619);
|
|
1114
|
-
}
|
|
1115
|
-
return h >>> 0;
|
|
1116
|
-
}
|
|
1117
|
-
function pickAvatar(id) {
|
|
1118
|
-
return avatars[fnv1a(id) % avatars.length];
|
|
1119
|
-
}
|
|
1120
1107
|
function findAvatar(name) {
|
|
1121
1108
|
return name ? avatars.find((a) => a.name === name) : undefined;
|
|
1122
1109
|
}
|
|
@@ -1126,7 +1113,7 @@ import"svelte/internal/flags/async";
|
|
|
1126
1113
|
import * as $ from "svelte/internal/server";
|
|
1127
1114
|
function Avatar($$renderer, $$props) {
|
|
1128
1115
|
$$renderer.component(($$renderer2) => {
|
|
1129
|
-
let {
|
|
1116
|
+
let { name, scale = 6, art: art63, interactive = false } = $$props;
|
|
1130
1117
|
let pressed = false;
|
|
1131
1118
|
let ghosting = false;
|
|
1132
1119
|
let bx = "50%";
|
|
@@ -1166,19 +1153,19 @@ function Avatar($$renderer, $$props) {
|
|
|
1166
1153
|
}
|
|
1167
1154
|
const s = $.derived(() => Math.max(1, Math.round(scale)));
|
|
1168
1155
|
const gap = $.derived(() => s() >= 2 ? 0.5 : 0);
|
|
1169
|
-
const resolved = $.derived(() => art63 ??
|
|
1170
|
-
const cells = $.derived(() => decode(resolved()));
|
|
1156
|
+
const resolved = $.derived(() => art63 ?? null);
|
|
1157
|
+
const cells = $.derived(() => resolved() ? decode(resolved()) : []);
|
|
1171
1158
|
const grid = $.derived(() => Array.from({ length: 100 }, (_, i) => {
|
|
1172
1159
|
const r = Math.floor(i / 10);
|
|
1173
1160
|
const c = i % 10;
|
|
1174
1161
|
const inner = r >= 1 && r <= 8 && c >= 1 && c <= 8;
|
|
1175
|
-
return inner ? cells()[(r - 1) * 8 + (c - 1)] : 0;
|
|
1162
|
+
return inner ? cells()[(r - 1) * 8 + (c - 1)] ?? 0 : 0;
|
|
1176
1163
|
}));
|
|
1177
1164
|
$$renderer2.push(`<span${$.attr_class("avatar svelte-1pre9qb", undefined, {
|
|
1178
1165
|
interactive,
|
|
1179
1166
|
pressed,
|
|
1180
1167
|
ghosting
|
|
1181
|
-
})} role="img"${$.attr("aria-label", name)}${$.attr("title", resolved()
|
|
1168
|
+
})} 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`)}><!--[-->`);
|
|
1182
1169
|
const each_array = $.ensure_array_like(grid());
|
|
1183
1170
|
for (let i = 0, $$length = each_array.length;i < $$length; i++) {
|
|
1184
1171
|
let cell = each_array[i];
|
|
@@ -1203,6 +1190,7 @@ function Button($$renderer, $$props) {
|
|
|
1203
1190
|
variant = "default",
|
|
1204
1191
|
size = "md",
|
|
1205
1192
|
ghost = false,
|
|
1193
|
+
square = false,
|
|
1206
1194
|
icon,
|
|
1207
1195
|
iconSize,
|
|
1208
1196
|
href,
|
|
@@ -1223,7 +1211,7 @@ function Button($$renderer, $$props) {
|
|
|
1223
1211
|
"aria-disabled": disabled || undefined,
|
|
1224
1212
|
tabindex: disabled ? -1 : undefined,
|
|
1225
1213
|
...rest
|
|
1226
|
-
}, "svelte-1klcfz0", { ghost })}>`);
|
|
1214
|
+
}, "svelte-1klcfz0", { ghost, square })}>`);
|
|
1227
1215
|
if (Icon2()) {
|
|
1228
1216
|
$$renderer.push("<!--[0-->");
|
|
1229
1217
|
if (Icon2()) {
|
|
@@ -1246,7 +1234,7 @@ function Button($$renderer, $$props) {
|
|
|
1246
1234
|
class: `btn ${$2.stringify(variant)} ${$2.stringify(size)}`,
|
|
1247
1235
|
disabled,
|
|
1248
1236
|
...rest
|
|
1249
|
-
}, "svelte-1klcfz0", { ghost })}>`);
|
|
1237
|
+
}, "svelte-1klcfz0", { ghost, square })}>`);
|
|
1250
1238
|
if (Icon2()) {
|
|
1251
1239
|
$$renderer.push("<!--[0-->");
|
|
1252
1240
|
if (Icon2()) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.btn.svelte-1klcfz0{display:inline-flex;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;border:1px solid var(--ink);background:var(--bg-card);color:var(--ink);cursor:pointer;text-decoration:none;justify-content:center;align-items: center;gap:7px;font-weight:600}.btn.svelte-1klcfz0:disabled{opacity:.4;cursor:not-allowed}.btn[aria-disabled=true].svelte-1klcfz0{opacity:.4;cursor:not-allowed;pointer-events:none}.btn.sm.svelte-1klcfz0{padding:7px 12px;font-size:12px}.btn.md.svelte-1klcfz0{padding:9px 16px;font-size:13px}.btn.lg.svelte-1klcfz0{padding:12px 22px;font-size:15px}.btn.square.svelte-1klcfz0{padding:0}.btn.square.sm.svelte-1klcfz0{width:30px;height:30px}.btn.square.md.svelte-1klcfz0{width:35px;height:35px}.btn.square.lg.svelte-1klcfz0{width:44px;height:44px}.btn.default.svelte-1klcfz0:hover:not(:disabled){background:var(--ink);color:var(--bg)}.btn.primary.svelte-1klcfz0{background:var(--ink);color:var(--bg);font-weight:700}.btn.primary.svelte-1klcfz0:hover:not(:disabled){background:var(--bg-card);color:var(--ink)}.btn.ghost.svelte-1klcfz0{background:0 0;border-style:dashed}.btn.ghost.svelte-1klcfz0:hover:not(:disabled){background:var(--ink);color:var(--bg);border-style:solid}.btn.danger.svelte-1klcfz0{color:var(--danger);border-color:var(--danger)}.btn.danger.svelte-1klcfz0:hover:not(:disabled){color:var(--danger);background-color:var(--danger-soft);background-image:linear-gradient(90deg,var(--danger)50%,transparent 0),linear-gradient(90deg,var(--danger)50%,transparent 0),linear-gradient(0deg,var(--danger)50%,transparent 0),linear-gradient(0deg,var(--danger)50%,transparent 0);animation:svelte-1klcfz0-marching-ants 2s linear infinite;background-position:0 0,0 100%,0 0,100% 0;background-repeat:repeat-x,repeat-x,repeat-y,repeat-y;background-size:14px 2px,14px 2px,2px 14px,2px 14px;border-color:#0000}@keyframes svelte-1klcfz0-marching-ants{to{background-position:28px 0,-28px 100%,0 -28px,100% 28px}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.right.svelte-r39acv{display:flex;align-items:stretch;margin-left:auto}.cog.svelte-r39acv,.reload.svelte-r39acv{display:inline-flex;color:var(--ink);border-left:1px solid var(--rule);flex:none;justify-content:center;align-items: center;width:48px}.reload.svelte-r39acv{appearance:none;cursor:pointer;background:0 0;border-top:0;border-bottom:0;border-right:0;padding:0}.cog.svelte-r39acv:hover,.reload.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
|
-
.topbar.svelte-15fxkvm{display:flex;border-bottom:1px solid var(--rule);align-items: center;gap:18px;padding:16px 24px}.back.svelte-15fxkvm{display:inline-flex;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;color:var(--ink-soft);text-decoration:none;align-items: center;gap:5px;font-size:12px}.back.svelte-15fxkvm:hover{color:var(--ink)}.title.svelte-15fxkvm{display:flex;flex:1;align-items: center;gap:12px}.name.svelte-15fxkvm{font-family:var(--font-display);text-transform:uppercase;letter-spacing:.03em;font-size:20px;font-weight:700}.open.svelte-15fxkvm{display:inline-flex;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;color:var(--ink);text-decoration:none;border:1px solid var(--ink);align-items: center;gap:6px;padding:7px 12px;font-size:12px;font-weight:600}.open.svelte-15fxkvm:hover{background:var(--ink);color:var(--bg)}.restart.svelte-15fxkvm{display:inline-flex;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;color:var(--ink);background:var(--bg);border:1px solid var(--ink);cursor:pointer;align-items: center;gap:6px;padding:7px 12px;font-size:12px;font-weight:600}.restart.svelte-15fxkvm:hover:not(:disabled){background:var(--ink);color:var(--bg)}.restart.svelte-15fxkvm:disabled{opacity:.5;cursor:not-allowed}.restart-err.svelte-15fxkvm{font-family:var(--font-mono);color:var(--danger);margin:0;padding:8px 24px 0;font-size:12px}.stage.svelte-15fxkvm{max-width:1200px;margin:0 auto;padding:20px 24px 40px}.meta.svelte-15fxkvm{display:grid;grid-template-columns:max-content 1fr;align-items: center;gap:6px 14px;margin-bottom:18px}.meta.svelte-15fxkvm .k:where(.svelte-15fxkvm){font-family:var(--font-display);letter-spacing:.14em;text-transform:uppercase;color:var(--ink-faint);font-size:12px;font-weight:700}.meta.svelte-15fxkvm code:where(.svelte-15fxkvm){font-family:var(--font-mono);color:var(--ink-soft);font-size:13px}.meta.svelte-15fxkvm code.muted:where(.svelte-15fxkvm){color:var(--ink-faint)}.healthslot.svelte-15fxkvm,.ports.svelte-15fxkvm{margin-bottom:18px}.ports-bar.svelte-15fxkvm{display:flex;justify-content:space-between;align-items: center}.rebuild.svelte-15fxkvm{font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.08em;background:var(--bg);color:var(--ink);border:1px solid var(--bg);cursor:pointer;padding:4px 9px;font-size:10px;font-weight:600}.rebuild.svelte-15fxkvm:hover:not(:disabled){color:var(--bg);background:0 0}.rebuild.svelte-15fxkvm:disabled{opacity:.5;cursor:not-allowed}.ports-body.svelte-15fxkvm{background:var(--bg-card);padding:12px 14px 14px}.port-list.svelte-15fxkvm{list-style:none;display:flex;flex-direction:column;gap:6px;margin:0 0 12px;padding:0}.port-list.svelte-15fxkvm li:where(.svelte-15fxkvm){display:flex;font-family:var(--font-mono);align-items: center;gap:8px;font-size:13px}.port-list.svelte-15fxkvm .cp:where(.svelte-15fxkvm){color:var(--ink);font-weight:600}.port-list.svelte-15fxkvm .arr:where(.svelte-15fxkvm){color:var(--ink-faint)}.port-list.svelte-15fxkvm a:where(.svelte-15fxkvm){display:inline-flex;color:var(--ink);text-decoration:none;border-bottom:1px solid var(--ink-faint);align-items: center;gap:4px}.port-list.svelte-15fxkvm a:where(.svelte-15fxkvm):hover{border-bottom-color:var(--ink)}.rm.svelte-15fxkvm{border:1px solid var(--ink-faint);color:var(--ink-soft);cursor:pointer;background:0 0;width:20px;height:20px;margin-left:auto;font-size:15px;line-height:1}.rm.svelte-15fxkvm:hover{border-color:var(--danger);color:var(--danger)}.empty.svelte-15fxkvm{font-family:var(--font-mono);color:var(--ink-faint);margin:0 0 12px;font-size:12px}.add.svelte-15fxkvm{display:flex;gap:8px}.add.svelte-15fxkvm input:where(.svelte-15fxkvm){font-family:var(--font-mono);border:1px solid var(--ink);background:var(--bg);color:var(--ink);outline:none;flex:1;min-width:0;padding:6px 9px;font-size:13px}.add.svelte-15fxkvm button:where(.svelte-15fxkvm){font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;border:1px solid var(--ink);background:var(--bg-card);color:var(--ink);cursor:pointer;padding:6px 14px;font-size:12px;font-weight:600}.add.svelte-15fxkvm button:where(.svelte-15fxkvm):hover{background:var(--ink);color:var(--bg)}.port-err.svelte-15fxkvm{font-family:var(--font-mono);color:var(--danger);margin:10px 0 0;font-size:12px}.hint.svelte-15fxkvm{font-family:var(--font-mono);color:var(--ink-faint);margin:10px 0 0;font-size:11px}.hint.pending.svelte-15fxkvm{color:var(--ink-soft)}.hint.svelte-15fxkvm code:where(.svelte-15fxkvm){color:var(--ink)}.logwrap.svelte-15fxkvm{overflow:hidden}.log-bar.svelte-15fxkvm{display:flex;justify-content:space-between;align-items: center}.log-actions.svelte-15fxkvm{display:flex;align-items: center;gap:6px}.copy.svelte-15fxkvm{font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.08em;background:var(--bg);color:var(--ink);border:1px solid var(--bg);cursor:pointer;padding:4px 9px;font-size:10px;font-weight:600}.copy.svelte-15fxkvm:hover:not(:disabled),.copy.active.svelte-15fxkvm{color:var(--bg);background:0 0}.copy.svelte-15fxkvm:disabled{opacity:.5;cursor:not-allowed}.logs.svelte-15fxkvm{position:relative;background:repeating-linear-gradient(0deg,var(--screen-line)0 1px,transparent 1px 3px),var(--screen-bg);color:var(--screen-ink);overflow:auto;height:calc(100vh - 320px);min-height:360px;padding:14px}.logs.svelte-15fxkvm pre:where(.svelte-15fxkvm){font-family:var(--font-mono);white-space:pre-wrap;word-break:break-word;margin:0;font-size:12.5px;line-height:1.5}@keyframes svelte-15fxkvm-lcd-blink{50%{background:0 0}}.caret.svelte-15fxkvm{display:inline-block;background:var(--screen-ink);vertical-align:text-bottom;animation:svelte-15fxkvm-lcd-blink 1.05s steps(1)infinite;width:.6em;height:1.05em;margin-left:2px}.err.svelte-15fxkvm{background:var(--bg-card);color:var(--ink);border-top:2px solid var(--ink);font-family:var(--font-mono);padding:12px 14px;font-size:13px}
|
|
1
|
+
.topbar.svelte-15fxkvm{display:flex;border-bottom:1px solid var(--rule);align-items: center;gap:18px;padding:16px 24px}.back.svelte-15fxkvm{display:inline-flex;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;color:var(--ink-soft);text-decoration:none;align-items: center;gap:5px;font-size:12px}.back.svelte-15fxkvm:hover{color:var(--ink)}.title.svelte-15fxkvm{display:flex;flex:1;align-items: center;gap:12px}.name.svelte-15fxkvm{font-family:var(--font-display);text-transform:uppercase;letter-spacing:.03em;font-size:20px;font-weight:700}.open.svelte-15fxkvm{display:inline-flex;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;color:var(--ink);text-decoration:none;border:1px solid var(--ink);align-items: center;gap:6px;padding:7px 12px;font-size:12px;font-weight:600}.open.svelte-15fxkvm:hover{background:var(--ink);color:var(--bg)}.restart.svelte-15fxkvm{display:inline-flex;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;color:var(--ink);background:var(--bg);border:1px solid var(--ink);cursor:pointer;align-items: center;gap:6px;padding:7px 12px;font-size:12px;font-weight:600}.restart.svelte-15fxkvm:hover:not(:disabled){background:var(--ink);color:var(--bg)}.restart.svelte-15fxkvm:disabled{opacity:.5;cursor:not-allowed}.restart-err.svelte-15fxkvm{font-family:var(--font-mono);color:var(--danger);margin:0;padding:8px 24px 0;font-size:12px}.stage.svelte-15fxkvm{max-width:1200px;margin:0 auto;padding:20px 24px 40px}.meta.svelte-15fxkvm{display:grid;grid-template-columns:max-content 1fr;align-items: center;gap:6px 14px;margin-bottom:18px}.meta.svelte-15fxkvm .k:where(.svelte-15fxkvm){font-family:var(--font-display);letter-spacing:.14em;text-transform:uppercase;color:var(--ink-faint);font-size:12px;font-weight:700}.meta.svelte-15fxkvm code:where(.svelte-15fxkvm){font-family:var(--font-mono);color:var(--ink-soft);font-size:13px}.meta.svelte-15fxkvm code.muted:where(.svelte-15fxkvm){color:var(--ink-faint)}.healthslot.svelte-15fxkvm,.ports.svelte-15fxkvm{margin-bottom:18px}.ports-bar.svelte-15fxkvm{display:flex;justify-content:space-between;align-items: center}.rebuild.svelte-15fxkvm{font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.08em;background:var(--bg);color:var(--ink);border:1px solid var(--bg);cursor:pointer;padding:4px 9px;font-size:10px;font-weight:600}.rebuild.svelte-15fxkvm:hover:not(:disabled){color:var(--bg);background:0 0}.rebuild.svelte-15fxkvm:disabled{opacity:.5;cursor:not-allowed}.ports-body.svelte-15fxkvm{background:var(--bg-card);padding:12px 14px 14px}.port-list.svelte-15fxkvm{list-style:none;display:flex;flex-direction:column;gap:6px;margin:0 0 12px;padding:0}.port-list.svelte-15fxkvm li:where(.svelte-15fxkvm){display:flex;font-family:var(--font-mono);align-items: center;gap:8px;font-size:13px}.port-list.svelte-15fxkvm .cp:where(.svelte-15fxkvm){color:var(--ink);font-weight:600}.port-list.svelte-15fxkvm .arr:where(.svelte-15fxkvm){color:var(--ink-faint)}.port-list.svelte-15fxkvm a:where(.svelte-15fxkvm){display:inline-flex;color:var(--ink);text-decoration:none;border-bottom:1px solid var(--ink-faint);align-items: center;gap:4px}.port-list.svelte-15fxkvm a:where(.svelte-15fxkvm):hover{border-bottom-color:var(--ink)}.rm.svelte-15fxkvm{border:1px solid var(--ink-faint);color:var(--ink-soft);cursor:pointer;background:0 0;width:20px;height:20px;margin-left:auto;font-size:15px;line-height:1}.rm.svelte-15fxkvm:hover{border-color:var(--danger);color:var(--danger)}.empty.svelte-15fxkvm{font-family:var(--font-mono);color:var(--ink-faint);margin:0 0 12px;font-size:12px}.add.svelte-15fxkvm{display:flex;gap:8px}.add.svelte-15fxkvm input:where(.svelte-15fxkvm){font-family:var(--font-mono);border:1px solid var(--ink);background:var(--bg);color:var(--ink);outline:none;flex:1;min-width:0;padding:6px 9px;font-size:13px}.add.svelte-15fxkvm button:where(.svelte-15fxkvm){font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.06em;border:1px solid var(--ink);background:var(--bg-card);color:var(--ink);cursor:pointer;padding:6px 14px;font-size:12px;font-weight:600}.add.svelte-15fxkvm button:where(.svelte-15fxkvm):hover{background:var(--ink);color:var(--bg)}.port-err.svelte-15fxkvm{font-family:var(--font-mono);color:var(--danger);margin:10px 0 0;font-size:12px}.hint.svelte-15fxkvm{font-family:var(--font-mono);color:var(--ink-faint);margin:10px 0 0;font-size:11px}.hint.pending.svelte-15fxkvm{color:var(--ink-soft)}.hint.svelte-15fxkvm code:where(.svelte-15fxkvm){color:var(--ink)}.logwrap.svelte-15fxkvm{overflow:hidden}.log-bar.svelte-15fxkvm{display:flex;justify-content:space-between;align-items: center}.log-actions.svelte-15fxkvm{display:flex;align-items: center;gap:6px}.copy.svelte-15fxkvm{display:inline-flex;font-family:var(--font-mono);text-transform:uppercase;letter-spacing:.08em;background:var(--bg);color:var(--ink);border:1px solid var(--bg);cursor:pointer;align-items: center;gap:5px;padding:4px 9px;font-size:10px;font-weight:600}.copy.svelte-15fxkvm:hover:not(:disabled),.copy.active.svelte-15fxkvm{color:var(--bg);background:0 0}.dot.svelte-15fxkvm{background:var(--attn-done);border-radius:50%;flex:none;width:6px;height:6px}.copy.svelte-15fxkvm:disabled{opacity:.5;cursor:not-allowed}.logs.svelte-15fxkvm{position:relative;background:repeating-linear-gradient(0deg,var(--screen-line)0 1px,transparent 1px 3px),var(--screen-bg);color:var(--screen-ink);overflow:auto;overflow-anchor:none;height:calc(100vh - 320px);min-height:360px;padding:14px}.logs.svelte-15fxkvm pre:where(.svelte-15fxkvm){font-family:var(--font-mono);white-space:pre-wrap;word-break:break-word;margin:0;font-size:12.5px;line-height:1.5}@keyframes svelte-15fxkvm-lcd-blink{50%{background:0 0}}.caret.svelte-15fxkvm{display:inline-block;background:var(--screen-ink);vertical-align:text-bottom;animation:svelte-15fxkvm-lcd-blink 1.05s steps(1)infinite;width:.6em;height:1.05em;margin-left:2px}.err.svelte-15fxkvm{background:var(--bg-card);color:var(--ink);border-top:2px solid var(--ink);font-family:var(--font-mono);padding:12px 14px;font-size:13px}
|
|
@@ -0,0 +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)}details.advanced.svelte-1n6ht58>summary:where(.svelte-1n6ht58){display:flex;cursor:pointer;list-style:none;font-family:var(--font-mono);align-items: center;gap:10px;padding:14px 16px}details.advanced.svelte-1n6ht58>summary:where(.svelte-1n6ht58)::-webkit-details-marker{display:none}details.advanced.svelte-1n6ht58>summary:where(.svelte-1n6ht58) .name:where(.svelte-1n6ht58){font-size:13px}details.advanced.svelte-1n6ht58>summary:where(.svelte-1n6ht58) .hint:where(.svelte-1n6ht58){color:var(--ink-soft);font-size:12px}details.advanced.svelte-1n6ht58>summary:where(.svelte-1n6ht58):after{content:"▸";color:var(--ink-soft);margin-left:auto}details.advanced[open].svelte-1n6ht58>summary:where(.svelte-1n6ht58):after{content:"▾"}.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}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
.topbar.svelte-1h259us{display:flex;border-bottom:1px solid var(--rule);justify-content:space-between;align-items: center;padding:18px 28px}.topbar-actions.svelte-1h259us{display:inline-flex;align-items: center;gap:14px}
|
|
1
|
+
.topbar.svelte-1h259us{display:flex;border-bottom:1px solid var(--rule);justify-content:space-between;align-items: center;padding:18px 28px}.topbar-actions.svelte-1h259us{display:inline-flex;align-items: center;gap:14px}.new-group.svelte-1h259us{display:inline-flex;align-items: center;gap:6px}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE instances ADD COLUMN avatar TEXT;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codebay",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.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",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"build": "mochi-framework build",
|
|
48
48
|
"start": "bun src/index.ts",
|
|
49
49
|
"prod": "bun run build && bun run start",
|
|
50
|
-
"dev": "MODE=development DISABLE_OPEN_BROWSER=1 DATA_DIR=./.codebay bun src/index.ts",
|
|
50
|
+
"dev": "MODE=development DISABLE_OPEN_BROWSER=1 DATA_DIR=./.codebay HOST=0.0.0.0 bun src/index.ts",
|
|
51
51
|
"typecheck": "svelte-check --tsconfig ./tsconfig.json --compiler-warnings 'attribute_illegal_colon:ignore' && tsc -p tsconfig.json --noEmit",
|
|
52
52
|
"test": "bun test",
|
|
53
53
|
"lint": "prettier --check . && eslint .",
|
package/src/avatars/diamond.ts
CHANGED
|
@@ -2,15 +2,14 @@ import type { AvatarArt } from './types.ts';
|
|
|
2
2
|
|
|
3
3
|
const art: AvatarArt = {
|
|
4
4
|
name: 'diamond',
|
|
5
|
-
robot: true,
|
|
6
5
|
pixels: [
|
|
7
6
|
'.######.',
|
|
8
|
-
'
|
|
7
|
+
'##.#.#.#',
|
|
8
|
+
'#.#.#.#.',
|
|
9
9
|
'########',
|
|
10
|
-
'
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
'..####..',
|
|
10
|
+
'#.######',
|
|
11
|
+
'.#.####.',
|
|
12
|
+
'..#.##..',
|
|
14
13
|
'...##...'
|
|
15
14
|
]
|
|
16
15
|
};
|
package/src/avatars/index.ts
CHANGED
|
@@ -129,22 +129,7 @@ export const avatars: AvatarArt[] = [
|
|
|
129
129
|
yinyan
|
|
130
130
|
];
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
function fnv1a(s: string): number {
|
|
134
|
-
let h = 2166136261;
|
|
135
|
-
for (let i = 0; i < s.length; i++) {
|
|
136
|
-
h ^= s.charCodeAt(i);
|
|
137
|
-
h = Math.imul(h, 16777619);
|
|
138
|
-
}
|
|
139
|
-
return h >>> 0;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Duplicates are possible but rare, since the hash spreads ids evenly across the catalog.
|
|
143
|
-
export function pickAvatar(id: string): AvatarArt {
|
|
144
|
-
return avatars[fnv1a(id) % avatars.length]!;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/** Lookup by name, for sprites chosen by hand rather than hashed from an id. */
|
|
132
|
+
/** Lookup by name; the server (`pick.server.ts`) resolves an instance's stored sprite name to its art. */
|
|
148
133
|
export function findAvatar(name: string | undefined): AvatarArt | undefined {
|
|
149
134
|
return name ? avatars.find((a) => a.name === name) : undefined;
|
|
150
135
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { avatars } from './index.ts';
|
|
2
|
+
import type { AvatarArt } from './types.ts';
|
|
3
|
+
|
|
4
|
+
// Server-only: uses Bun.hash (a native, non-crypto hash) so we don't hand-roll one. crc32 returns a
|
|
5
|
+
// plain uint32 to bucket directly. Stable across processes/versions, so an id maps to the same sprite.
|
|
6
|
+
function bucket(id: string): number {
|
|
7
|
+
return Bun.hash.crc32(id) % avatars.length;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** A deterministic sprite for an id — the default before a unique one is chosen and persisted. */
|
|
11
|
+
export function pickAvatar(id: string): AvatarArt {
|
|
12
|
+
return avatars[bucket(id)]!;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Prefer the id's deterministic sprite; if it's taken, probe forward so only colliding instances
|
|
16
|
+
// deviate from what `pickAvatar` would give. Falls back to the hashed pick once the catalog is full.
|
|
17
|
+
export function pickUniqueAvatar(id: string, taken: Iterable<string>): AvatarArt {
|
|
18
|
+
const used = new Set(taken);
|
|
19
|
+
const base = bucket(id);
|
|
20
|
+
for (let k = 0; k < avatars.length; k++) {
|
|
21
|
+
const art = avatars[(base + k) % avatars.length]!;
|
|
22
|
+
if (!used.has(art.name)) return art;
|
|
23
|
+
}
|
|
24
|
+
return avatars[base]!;
|
|
25
|
+
}
|
package/src/avatars/whale.ts
CHANGED
|
@@ -2,16 +2,15 @@ import type { AvatarArt } from './types.ts';
|
|
|
2
2
|
|
|
3
3
|
const art: AvatarArt = {
|
|
4
4
|
name: 'whale',
|
|
5
|
-
robot: true,
|
|
6
5
|
pixels: [
|
|
7
|
-
'
|
|
8
|
-
'
|
|
9
|
-
'
|
|
10
|
-
'
|
|
6
|
+
'#.#.#...',
|
|
7
|
+
'.#.#....',
|
|
8
|
+
'..#.....',
|
|
9
|
+
'..#.....',
|
|
10
|
+
'.####...',
|
|
11
11
|
'######.#',
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'...##...'
|
|
12
|
+
'#.####.#',
|
|
13
|
+
'########'
|
|
15
14
|
]
|
|
16
15
|
};
|
|
17
16
|
|
|
@@ -160,9 +160,23 @@
|
|
|
160
160
|
|
|
161
161
|
const STALLED_AFTER_MS = 10_000;
|
|
162
162
|
|
|
163
|
+
const activeInstance = $derived(running.find((i) => i.id === active));
|
|
164
|
+
|
|
165
|
+
// A pane opened with "Open anyway" before code-server was up is stuck on the proxy's 503
|
|
166
|
+
// forever, and a document reload would tear down every other tab's editor.
|
|
167
|
+
const frames: Record<string, HTMLIFrameElement | null> = {};
|
|
168
|
+
|
|
169
|
+
function reloadActive() {
|
|
170
|
+
const inst = activeInstance;
|
|
171
|
+
if (!inst || inst.mode === 'terminal') return;
|
|
172
|
+
loadedFrames.delete(inst.id);
|
|
173
|
+
// `location.replace`, not `src =`: assigning src pushes a parent session-history entry,
|
|
174
|
+
// which would break the shell's pushState back button.
|
|
175
|
+
frames[inst.id]?.contentWindow?.location.replace(ideUrl(inst));
|
|
176
|
+
}
|
|
177
|
+
|
|
163
178
|
$effect(() => {
|
|
164
|
-
|
|
165
|
-
document.title = onIde && inst ? `${inst.name} — Codebay` : 'Codebay';
|
|
179
|
+
document.title = onIde && activeInstance ? `${activeInstance.name} — Codebay` : 'Codebay';
|
|
166
180
|
});
|
|
167
181
|
|
|
168
182
|
// Browsers block audio until the page has been interacted with.
|
|
@@ -266,6 +280,9 @@
|
|
|
266
280
|
{attention}
|
|
267
281
|
{editingId}
|
|
268
282
|
bind:editingName
|
|
283
|
+
onreload={activeInstance && activeInstance.mode !== 'terminal' && mountable(active)
|
|
284
|
+
? reloadActive
|
|
285
|
+
: undefined}
|
|
269
286
|
onselect={(id) => navigate(`/ide/${id}`)}
|
|
270
287
|
onstartrename={startRename}
|
|
271
288
|
oncommitrename={commitRename}
|
|
@@ -295,7 +312,11 @@
|
|
|
295
312
|
initialOpen={inst.terminal_split === 1}
|
|
296
313
|
/>
|
|
297
314
|
{:else}
|
|
298
|
-
<iframe
|
|
315
|
+
<iframe
|
|
316
|
+
bind:this={frames[inst.id]}
|
|
317
|
+
src={ideUrl(inst)}
|
|
318
|
+
title={inst.name}
|
|
319
|
+
onload={() => loadedFrames.add(inst.id)}
|
|
299
320
|
></iframe>
|
|
300
321
|
{/if}
|
|
301
322
|
{/if}
|
|
@@ -303,7 +324,11 @@
|
|
|
303
324
|
iframe's own `load` is bounded and gets a plain loader. The terminal has no
|
|
304
325
|
`load` event, so once it's mountable its own "connecting…" covers the gap. -->
|
|
305
326
|
{#if !mountable(inst.id)}
|
|
306
|
-
<IdeLoader
|
|
327
|
+
<IdeLoader
|
|
328
|
+
mode={inst.mode}
|
|
329
|
+
stalledAfterMs={STALLED_AFTER_MS}
|
|
330
|
+
onoverride={() => forced.add(inst.id)}
|
|
331
|
+
/>
|
|
307
332
|
{:else if inst.mode !== 'terminal' && !loadedFrames.has(inst.id)}
|
|
308
333
|
<IdeLoader />
|
|
309
334
|
{/if}
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { decode, type AvatarArt } from '../avatars/index.ts';
|
|
3
3
|
|
|
4
4
|
// `scale` is device-pixels per LED cell; the panel is always an integer multiple
|
|
5
5
|
// of it (10 × scale px) so every pixel lands on a crisp boundary.
|
|
6
6
|
let {
|
|
7
|
-
id = '',
|
|
8
7
|
name,
|
|
9
8
|
scale = 6,
|
|
10
9
|
art,
|
|
11
10
|
interactive = false
|
|
12
11
|
}: {
|
|
13
|
-
id?: string;
|
|
14
12
|
name: string;
|
|
15
13
|
scale?: number;
|
|
16
|
-
art?: AvatarArt;
|
|
14
|
+
art?: AvatarArt | null;
|
|
17
15
|
interactive?: boolean;
|
|
18
16
|
} = $props();
|
|
19
17
|
|
|
@@ -67,15 +65,17 @@
|
|
|
67
65
|
// 0.5px is a crisp single device-pixel at 2×; at 1× the cells can't spare any gutter.
|
|
68
66
|
const gap = $derived(s >= 2 ? 0.5 : 0);
|
|
69
67
|
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
// Instance sprites are resolved server-side and handed in as `art`; a missing or unknown one
|
|
69
|
+
// (null/unset) renders an empty panel rather than a colliding fallback.
|
|
70
|
+
const resolved = $derived(art ?? null);
|
|
71
|
+
const cells = $derived(resolved ? decode(resolved) : []);
|
|
72
72
|
// The 10×10 outer ring is unlit LED cells — a bezel of real dots, not blank padding.
|
|
73
73
|
const grid = $derived(
|
|
74
74
|
Array.from({ length: 100 }, (_, i) => {
|
|
75
75
|
const r = Math.floor(i / 10);
|
|
76
76
|
const c = i % 10;
|
|
77
77
|
const inner = r >= 1 && r <= 8 && c >= 1 && c <= 8;
|
|
78
|
-
return inner ? cells[(r - 1) * 8 + (c - 1)] : 0;
|
|
78
|
+
return inner ? (cells[(r - 1) * 8 + (c - 1)] ?? 0) : 0;
|
|
79
79
|
})
|
|
80
80
|
);
|
|
81
81
|
</script>
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
class:ghosting
|
|
88
88
|
role="img"
|
|
89
89
|
aria-label={name}
|
|
90
|
-
title={resolved
|
|
90
|
+
title={resolved?.name}
|
|
91
91
|
style="width:{10 * s}px;height:{10 *
|
|
92
92
|
s}px;--gap:{gap}px;--bx:{bx};--by:{by};--spin:{spin}deg;--hue:{hue}deg"
|
|
93
93
|
{onpointerdown}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
variant = 'default',
|
|
9
9
|
size = 'md',
|
|
10
10
|
ghost = false,
|
|
11
|
+
square = false,
|
|
11
12
|
icon,
|
|
12
13
|
iconSize,
|
|
13
14
|
href,
|
|
@@ -20,6 +21,8 @@
|
|
|
20
21
|
size?: Size;
|
|
21
22
|
/** Dashed-outline treatment that fills on hover — used for secondary links. */
|
|
22
23
|
ghost?: boolean;
|
|
24
|
+
/** Icon-only: drops the padding for a square that matches the row's button height. */
|
|
25
|
+
square?: boolean;
|
|
23
26
|
/** Optional leading icon component, e.g. a lucide icon like `Plus`. */
|
|
24
27
|
icon?: Component<{ size?: number }>;
|
|
25
28
|
/** Override the icon size; defaults to a size that scales with `size`. */
|
|
@@ -40,6 +43,7 @@
|
|
|
40
43
|
<a
|
|
41
44
|
class="btn {variant} {size}"
|
|
42
45
|
class:ghost
|
|
46
|
+
class:square
|
|
43
47
|
href={disabled ? undefined : href}
|
|
44
48
|
aria-disabled={disabled || undefined}
|
|
45
49
|
tabindex={disabled ? -1 : undefined}
|
|
@@ -49,7 +53,7 @@
|
|
|
49
53
|
{@render children?.()}
|
|
50
54
|
</a>
|
|
51
55
|
{:else}
|
|
52
|
-
<button class="btn {variant} {size}" class:ghost {onclick} {disabled} {...rest}>
|
|
56
|
+
<button class="btn {variant} {size}" class:ghost class:square {onclick} {disabled} {...rest}>
|
|
53
57
|
{#if Icon}<Icon size={resolvedIconSize} />{/if}
|
|
54
58
|
{@render children?.()}
|
|
55
59
|
</button>
|
|
@@ -97,6 +101,23 @@
|
|
|
97
101
|
font-size: 15px;
|
|
98
102
|
}
|
|
99
103
|
|
|
104
|
+
/* Sized to match the text button of the same `size`, so the two sit flush in a row. */
|
|
105
|
+
.btn.square {
|
|
106
|
+
padding: 0;
|
|
107
|
+
}
|
|
108
|
+
.btn.square.sm {
|
|
109
|
+
width: 30px;
|
|
110
|
+
height: 30px;
|
|
111
|
+
}
|
|
112
|
+
.btn.square.md {
|
|
113
|
+
width: 35px;
|
|
114
|
+
height: 35px;
|
|
115
|
+
}
|
|
116
|
+
.btn.square.lg {
|
|
117
|
+
width: 44px;
|
|
118
|
+
height: 44px;
|
|
119
|
+
}
|
|
120
|
+
|
|
100
121
|
.btn.default:hover:not(:disabled) {
|
|
101
122
|
background: var(--ink);
|
|
102
123
|
color: var(--bg);
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
type Instance,
|
|
4
|
+
type InstanceFilter,
|
|
5
|
+
type InstanceMode,
|
|
6
|
+
type Preflight
|
|
7
|
+
} from '../types.ts';
|
|
3
8
|
import type { AvatarArt } from '../avatars/index.ts';
|
|
4
9
|
import FolderBrowser from './FolderBrowser.svelte';
|
|
5
10
|
import InstanceCard from './InstanceCard.svelte';
|
|
@@ -28,6 +33,8 @@
|
|
|
28
33
|
type Action = 'start' | 'stop' | 'delete' | 'rebuild';
|
|
29
34
|
|
|
30
35
|
let browserOpen = $state(false);
|
|
36
|
+
// Set only by the mode shortcut button; null lets the picker follow the global default.
|
|
37
|
+
let browserMode = $state<InstanceMode | null>(null);
|
|
31
38
|
let creating = $state(false);
|
|
32
39
|
let actionError = $state<string | null>(null);
|
|
33
40
|
let editingId = $state<string | null>(null);
|
|
@@ -148,7 +155,11 @@
|
|
|
148
155
|
{creating}
|
|
149
156
|
{filter}
|
|
150
157
|
{onFilter}
|
|
151
|
-
|
|
158
|
+
defaultMode={preflight.defaultMode}
|
|
159
|
+
onNew={(mode) => {
|
|
160
|
+
browserMode = mode ?? null;
|
|
161
|
+
browserOpen = true;
|
|
162
|
+
}}
|
|
152
163
|
onDeleteAll={deleteAll}
|
|
153
164
|
/>
|
|
154
165
|
|
|
@@ -199,6 +210,7 @@
|
|
|
199
210
|
<FolderBrowser
|
|
200
211
|
onpick={createFrom}
|
|
201
212
|
defaultMode={preflight.defaultMode}
|
|
213
|
+
initialMode={browserMode}
|
|
202
214
|
onclose={() => (browserOpen = false)}
|
|
203
215
|
/>
|
|
204
216
|
{/if}
|
|
@@ -15,16 +15,20 @@
|
|
|
15
15
|
let {
|
|
16
16
|
onpick,
|
|
17
17
|
onclose,
|
|
18
|
-
defaultMode = 'ide'
|
|
18
|
+
defaultMode = 'ide',
|
|
19
|
+
initialMode = null
|
|
19
20
|
}: {
|
|
20
21
|
onpick: (source: string, opts?: { branch?: string; mode?: InstanceMode }) => void;
|
|
21
22
|
onclose: () => void;
|
|
22
23
|
defaultMode?: InstanceMode;
|
|
24
|
+
/** Opens pinned to this mode — set by the dashboard's mode shortcut button. */
|
|
25
|
+
initialMode?: InstanceMode | null;
|
|
23
26
|
} = $props();
|
|
24
27
|
|
|
25
28
|
// Follows the global default live — settings opens in its own popup, so the toggle there can
|
|
26
29
|
// move while this picker is open. An explicit choice here wins and is never yanked back.
|
|
27
|
-
|
|
30
|
+
// svelte-ignore state_referenced_locally
|
|
31
|
+
let modeOverride = $state<InstanceMode | null>(initialMode);
|
|
28
32
|
const mode = $derived(modeOverride ?? defaultMode);
|
|
29
33
|
|
|
30
34
|
let result = $state<BrowseResult | null>(null);
|