codebay 0.9.1 → 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 +213 -146
- 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-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 +129 -42
- package/src/lib/health.server.ts +55 -11
- package/src/lib/injections.server.ts +45 -38
- package/src/lib/instances.server.ts +181 -19
- package/src/lib/log-capture.server.ts +262 -0
- package/src/lib/ports.server.ts +31 -0
- package/src/lib/proxy.server.ts +2 -2
- 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,7 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { type Instance } from '../types.ts';
|
|
3
|
+
import { findAvatar } from '../avatars/index.ts';
|
|
3
4
|
import Settings from '@lucide/svelte/icons/settings';
|
|
5
|
+
import RotateCw from '@lucide/svelte/icons/rotate-cw';
|
|
4
6
|
import Terminal from '@lucide/svelte/icons/terminal';
|
|
7
|
+
import LayoutTemplate from '@lucide/svelte/icons/layout-template';
|
|
5
8
|
import Avatar from './Avatar.svelte';
|
|
6
9
|
import AppBar from './AppBar.svelte';
|
|
7
10
|
import { withPopupMarker } from '../lib/popup-nav.ts';
|
|
@@ -12,6 +15,7 @@
|
|
|
12
15
|
attention,
|
|
13
16
|
editingId,
|
|
14
17
|
editingName = $bindable(),
|
|
18
|
+
onreload,
|
|
15
19
|
onselect,
|
|
16
20
|
onstartrename,
|
|
17
21
|
oncommitrename,
|
|
@@ -22,6 +26,8 @@
|
|
|
22
26
|
attention: Record<string, 'done' | 'waiting' | null>;
|
|
23
27
|
editingId: string | null;
|
|
24
28
|
editingName: string;
|
|
29
|
+
/** Absent for terminal tabs, which carry their own reload, and until the iframe exists. */
|
|
30
|
+
onreload?: () => void;
|
|
25
31
|
onselect: (id: string) => void;
|
|
26
32
|
onstartrename: (instance: Instance) => void;
|
|
27
33
|
oncommitrename: (id: string) => void;
|
|
@@ -41,7 +47,11 @@
|
|
|
41
47
|
>
|
|
42
48
|
{#if editingId === inst.id}
|
|
43
49
|
<div class="tab-label editing">
|
|
44
|
-
<Avatar
|
|
50
|
+
<Avatar
|
|
51
|
+
name={inst.name}
|
|
52
|
+
art={findAvatar(inst.avatar ?? undefined) ?? null}
|
|
53
|
+
scale={4}
|
|
54
|
+
/>
|
|
45
55
|
<!-- svelte-ignore a11y_autofocus -->
|
|
46
56
|
<input
|
|
47
57
|
class="tab-name-edit"
|
|
@@ -63,10 +73,16 @@
|
|
|
63
73
|
ondblclick={() => onstartrename(inst)}
|
|
64
74
|
title={inst.name}
|
|
65
75
|
>
|
|
66
|
-
<Avatar
|
|
76
|
+
<Avatar
|
|
77
|
+
name={inst.name}
|
|
78
|
+
art={findAvatar(inst.avatar ?? undefined) ?? null}
|
|
79
|
+
scale={4}
|
|
80
|
+
/>
|
|
67
81
|
<span class="tab-name">{inst.name}</span>
|
|
68
82
|
{#if inst.mode === 'terminal'}
|
|
69
83
|
<span class="tab-mode" title="Terminal-only instance"><Terminal size={13} /></span>
|
|
84
|
+
{:else}
|
|
85
|
+
<span class="tab-mode" title="Full IDE instance"><LayoutTemplate size={13} /></span>
|
|
70
86
|
{/if}
|
|
71
87
|
</button>
|
|
72
88
|
{/if}
|
|
@@ -74,28 +90,55 @@
|
|
|
74
90
|
{/each}
|
|
75
91
|
</nav>
|
|
76
92
|
{/if}
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
93
|
+
<div class="right">
|
|
94
|
+
{#if onreload}
|
|
95
|
+
<button
|
|
96
|
+
type="button"
|
|
97
|
+
class="reload"
|
|
98
|
+
onclick={onreload}
|
|
99
|
+
title="Reload editor"
|
|
100
|
+
aria-label="Reload editor"><RotateCw size={18} /></button
|
|
101
|
+
>
|
|
102
|
+
{/if}
|
|
103
|
+
<a
|
|
104
|
+
class="cog"
|
|
105
|
+
href={withPopupMarker('/settings')}
|
|
106
|
+
target="_blank"
|
|
107
|
+
rel="noopener noreferrer"
|
|
108
|
+
title="Settings"
|
|
109
|
+
aria-label="Settings"><Settings size={18} /></a
|
|
110
|
+
>
|
|
111
|
+
</div>
|
|
85
112
|
</AppBar>
|
|
86
113
|
|
|
87
114
|
<style>
|
|
88
|
-
.
|
|
115
|
+
/* Grouped so the auto-margin doesn't have to move when the reload button comes and goes. */
|
|
116
|
+
.right {
|
|
117
|
+
display: flex;
|
|
118
|
+
align-items: stretch;
|
|
119
|
+
margin-left: auto;
|
|
120
|
+
}
|
|
121
|
+
.cog,
|
|
122
|
+
.reload {
|
|
89
123
|
display: inline-flex;
|
|
90
124
|
align-items: center;
|
|
91
125
|
justify-content: center;
|
|
92
126
|
width: 48px;
|
|
93
127
|
flex: none;
|
|
94
|
-
margin-left: auto;
|
|
95
128
|
color: var(--ink);
|
|
96
129
|
border-left: 1px solid var(--rule);
|
|
97
130
|
}
|
|
98
|
-
.
|
|
131
|
+
.reload {
|
|
132
|
+
appearance: none;
|
|
133
|
+
background: transparent;
|
|
134
|
+
border-top: 0;
|
|
135
|
+
border-right: 0;
|
|
136
|
+
border-bottom: 0;
|
|
137
|
+
padding: 0;
|
|
138
|
+
cursor: pointer;
|
|
139
|
+
}
|
|
140
|
+
.cog:hover,
|
|
141
|
+
.reload:hover {
|
|
99
142
|
background: var(--ink);
|
|
100
143
|
color: var(--bg);
|
|
101
144
|
}
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import Button from './Button.svelte';
|
|
3
|
+
import type { InstanceMode } from '../types.ts';
|
|
3
4
|
|
|
4
5
|
// A fixed flip count per tick, rather than a per-bit probability, is what keeps
|
|
5
6
|
// the cadence visually even; `speed` scales only the interval, never the churn.
|
|
6
7
|
let {
|
|
7
8
|
speed = 1,
|
|
9
|
+
mode = 'ide',
|
|
8
10
|
// Omit both unless the wait could in principle never end.
|
|
9
11
|
stalledAfterMs,
|
|
10
12
|
onoverride
|
|
11
|
-
}: {
|
|
13
|
+
}: {
|
|
14
|
+
speed?: number;
|
|
15
|
+
mode?: InstanceMode;
|
|
16
|
+
stalledAfterMs?: number;
|
|
17
|
+
onoverride?: () => void;
|
|
18
|
+
} = $props();
|
|
19
|
+
|
|
20
|
+
const isTerminal = $derived(mode === 'terminal');
|
|
12
21
|
|
|
13
22
|
let stalled = $state(false);
|
|
14
23
|
$effect(() => {
|
|
@@ -52,11 +61,14 @@
|
|
|
52
61
|
{/each}
|
|
53
62
|
</div>
|
|
54
63
|
<div class="label">
|
|
55
|
-
Loading editor<span class="dot">.</span><span class="dot">.</span
|
|
64
|
+
Loading {isTerminal ? 'terminal' : 'editor'}<span class="dot">.</span><span class="dot">.</span
|
|
65
|
+
><span class="dot">.</span>
|
|
56
66
|
</div>
|
|
57
67
|
{#if stalled}
|
|
58
68
|
<div class="stalled">
|
|
59
|
-
<p>
|
|
69
|
+
<p>
|
|
70
|
+
Waiting for {isTerminal ? 'the terminal' : 'code-server'} to answer inside the container.
|
|
71
|
+
</p>
|
|
60
72
|
{#if onoverride}
|
|
61
73
|
<Button size="sm" onclick={onoverride}>Open anyway</Button>
|
|
62
74
|
{/if}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { type Instance } from '../types.ts';
|
|
3
|
+
import { findAvatar } from '../avatars/index.ts';
|
|
3
4
|
import Avatar from './Avatar.svelte';
|
|
4
5
|
import BranchBox from './BranchBox.svelte';
|
|
5
6
|
import PortsBox from './PortsBox.svelte';
|
|
@@ -52,7 +53,11 @@
|
|
|
52
53
|
</div>
|
|
53
54
|
{/if}
|
|
54
55
|
<div class="card-head">
|
|
55
|
-
<Avatar
|
|
56
|
+
<Avatar
|
|
57
|
+
name={instance.name}
|
|
58
|
+
art={findAvatar(instance.avatar ?? undefined) ?? null}
|
|
59
|
+
interactive
|
|
60
|
+
/>
|
|
56
61
|
{#if editing}
|
|
57
62
|
<!-- svelte-ignore a11y_autofocus -->
|
|
58
63
|
<input
|
|
@@ -22,26 +22,24 @@
|
|
|
22
22
|
let logEl = $state<HTMLDivElement | null>(null);
|
|
23
23
|
let following = $state(true);
|
|
24
24
|
|
|
25
|
+
// tick() lets the <pre> text node grow before we measure, so we reach the true bottom.
|
|
26
|
+
async function scrollToBottom() {
|
|
27
|
+
await tick();
|
|
28
|
+
logEl?.scrollTo(0, logEl.scrollHeight);
|
|
29
|
+
}
|
|
30
|
+
|
|
25
31
|
// Pin to the newest output whenever logs grow — but only while following.
|
|
26
|
-
// tick() lets the <pre> text node grow before we scroll, so we reach the true bottom.
|
|
27
32
|
$effect(() => {
|
|
28
33
|
if (!following || logs.length === 0) return; // reading logs.length tracks appended chunks
|
|
29
|
-
|
|
30
|
-
if (following) logEl?.scrollTo(0, logEl.scrollHeight);
|
|
31
|
-
});
|
|
34
|
+
scrollToBottom();
|
|
32
35
|
});
|
|
33
36
|
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
if (!logEl) return;
|
|
38
|
-
const gap = logEl.scrollHeight - logEl.clientHeight - logEl.scrollTop;
|
|
39
|
-
if (gap > 40) following = false;
|
|
40
|
-
}
|
|
41
|
-
|
|
37
|
+
// Following is deliberately sticky: only this button changes it. Inferring it from
|
|
38
|
+
// scroll events misfired constantly, since streaming chunks and reflow above the log
|
|
39
|
+
// both move scrollTop without the user touching anything.
|
|
42
40
|
function toggleFollow() {
|
|
43
41
|
following = !following;
|
|
44
|
-
if (following)
|
|
42
|
+
if (following) scrollToBottom();
|
|
45
43
|
}
|
|
46
44
|
|
|
47
45
|
$effect(() => installPopupBackTrap());
|
|
@@ -255,7 +253,14 @@
|
|
|
255
253
|
<div class="log-bar panel-bar">
|
|
256
254
|
<span>Boot log</span>
|
|
257
255
|
<div class="log-actions">
|
|
258
|
-
<button
|
|
256
|
+
<button
|
|
257
|
+
class="copy"
|
|
258
|
+
class:active={following}
|
|
259
|
+
aria-pressed={following}
|
|
260
|
+
title={following ? 'Following — click to pause' : 'Paused — click to follow new output'}
|
|
261
|
+
onclick={toggleFollow}
|
|
262
|
+
>
|
|
263
|
+
{#if following}<span class="dot"></span>{/if}
|
|
259
264
|
{following ? 'Following' : 'Follow log'}
|
|
260
265
|
</button>
|
|
261
266
|
<button class="copy" onclick={copyLogs} disabled={!logs}>
|
|
@@ -263,7 +268,7 @@
|
|
|
263
268
|
</button>
|
|
264
269
|
</div>
|
|
265
270
|
</div>
|
|
266
|
-
<div class="logs" bind:this={logEl}
|
|
271
|
+
<div class="logs" bind:this={logEl}>
|
|
267
272
|
<pre>{logs || 'Waiting for output…'}<span class="caret"></span></pre>
|
|
268
273
|
</div>
|
|
269
274
|
{#if instance?.status === 'error' && instance.error}
|
|
@@ -535,6 +540,9 @@
|
|
|
535
540
|
align-items: center;
|
|
536
541
|
}
|
|
537
542
|
.copy {
|
|
543
|
+
display: inline-flex;
|
|
544
|
+
align-items: center;
|
|
545
|
+
gap: 5px;
|
|
538
546
|
font-family: var(--font-mono);
|
|
539
547
|
font-weight: 600;
|
|
540
548
|
font-size: 10px;
|
|
@@ -554,6 +562,14 @@
|
|
|
554
562
|
background: transparent;
|
|
555
563
|
color: var(--bg);
|
|
556
564
|
}
|
|
565
|
+
/* The dot, not the inverted fill, is what separates "following" from a mere hover. */
|
|
566
|
+
.dot {
|
|
567
|
+
flex: none;
|
|
568
|
+
width: 6px;
|
|
569
|
+
height: 6px;
|
|
570
|
+
border-radius: 50%;
|
|
571
|
+
background: var(--attn-done);
|
|
572
|
+
}
|
|
557
573
|
.copy:disabled {
|
|
558
574
|
opacity: 0.5;
|
|
559
575
|
cursor: not-allowed;
|
|
@@ -568,6 +584,8 @@
|
|
|
568
584
|
height: calc(100vh - 320px);
|
|
569
585
|
min-height: 360px;
|
|
570
586
|
overflow: auto;
|
|
587
|
+
/* Scroll anchoring would shift scrollTop as the <pre> grows; a paused log must stay put. */
|
|
588
|
+
overflow-anchor: none;
|
|
571
589
|
}
|
|
572
590
|
.logs pre {
|
|
573
591
|
margin: 0;
|