Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions apps/web/app/gen2/workspace.css
Original file line number Diff line number Diff line change
Expand Up @@ -1098,7 +1098,10 @@
--ss-muted: #2a2827;
--ss-muted-foreground: #a8a5a3;
--ss-border: #2a2827;
min-height: 100dvh;
display: flex;
flex-direction: column;
height: 100dvh;
min-height: 0;
background: var(--ss-background);
color: var(--ss-foreground);
font-family: var(--font-geist-sans), Arial, sans-serif;
Expand Down Expand Up @@ -1203,12 +1206,20 @@
font-size: 0.8125rem;
}

.gen2-superset-workspace-content {
flex: 1;
min-height: 0;
}

.gen2-superset-files-panel .gen2-superset-file-pane {
min-height: calc(100dvh - 48px);
height: 100%;
min-height: 0;
}

.gen2-superset-tool-panel {
min-height: calc(100dvh - 48px);
box-sizing: border-box;
height: 100%;
min-height: 0;
padding: 16px;
background: var(--ss-background);
}
Expand Down
66 changes: 34 additions & 32 deletions apps/web/components/gen2/superset-workspace-shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -341,42 +341,44 @@ export function SupersetWorkspaceShell({
{notice}
</p>
) : null}
<div
id="superset-panel-files"
role="tabpanel"
aria-labelledby="superset-tab-files"
hidden={tab !== "files"}
className="gen2-superset-files-panel"
>
<SupersetFilePane
key={worktreeId}
<div className="gen2-superset-workspace-content">
<div
id="superset-panel-files"
role="tabpanel"
aria-labelledby="superset-tab-files"
hidden={tab !== "files"}
className="gen2-superset-files-panel"
>
<SupersetFilePane
key={worktreeId}
workspaceId={workspaceId}
canEdit={canEdit}
worktreeId={worktreeId}
onDirtyChange={setDirty}
/>
</div>
<SupersetChangesPane
workspaceId={workspaceId}
canEdit={canEdit}
worktreeId={worktreeId}
onDirtyChange={setDirty}
visible={tab === "changes"}
/>
<section
id="superset-panel-terminal"
role="tabpanel"
aria-labelledby="superset-tab-terminal"
hidden={tab !== "terminal"}
className="gen2-superset-tool-panel"
>
<Gen2TerminalPane
key={worktreeId}
workspaceId={workspaceId}
worktreeId={worktreeId}
visible={tab === "terminal"}
canStart
onExit={() => undefined}
/>
</section>
</div>
<SupersetChangesPane
workspaceId={workspaceId}
worktreeId={worktreeId}
visible={tab === "changes"}
/>
<section
id="superset-panel-terminal"
role="tabpanel"
aria-labelledby="superset-tab-terminal"
hidden={tab !== "terminal"}
className="gen2-superset-tool-panel"
>
<Gen2TerminalPane
key={worktreeId}
workspaceId={workspaceId}
worktreeId={worktreeId}
visible={tab === "terminal"}
canStart
onExit={() => undefined}
/>
</section>
</main>
);
}
11 changes: 11 additions & 0 deletions apps/web/components/gen2/terminal-pane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export function Gen2TerminalPane({
const fitRef = useRef<FitAddon | null>(null);
const sessionRef = useRef<string | null>(null);
const afterRef = useRef(0);
const dimensionsRef = useRef("");
const [status, setStatus] = useState<"idle" | "starting" | "live" | "ended">(
"idle",
);
Expand Down Expand Up @@ -80,6 +81,7 @@ export function Gen2TerminalPane({
term.loadAddon(fit);
term.open(host);
if (host.clientWidth > 0 && host.clientHeight > 0) fit.fit();
dimensionsRef.current = `${term.rows}:${term.cols}`;
termRef.current = term;
fitRef.current = fit;

Expand Down Expand Up @@ -170,6 +172,12 @@ export function Gen2TerminalPane({
onExit();
return;
}
// The legacy guest endpoint parks this request, while the Superset
// bridge returns an immediate snapshot. Yield between idle snapshots
// so an open shell cannot turn into a tight browser request loop.
if (result.chunks.length === 0) {
await new Promise((resolve) => setTimeout(resolve, 150));
}
} catch {
if (cancelled) return;
networkFailures += 1;
Expand Down Expand Up @@ -203,6 +211,9 @@ export function Gen2TerminalPane({
// A hidden pane measures zero; fitting against that throws.
if (host.clientWidth === 0 || host.clientHeight === 0) return;
fitRef.current?.fit();
const dimensions = `${term.rows}:${term.cols}`;
if (dimensions === dimensionsRef.current) return;
dimensionsRef.current = dimensions;
void post({
action: "resize",
sessionId,
Expand Down
10 changes: 10 additions & 0 deletions infra/runtime/scripts/bootstrap-host.sh
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,16 @@ if ! grep -q '^codev-shell:' "${work_dir}/rootfs/etc/shadow"; then
echo 'codev-shell:!:20000::::::' >>"${work_dir}/rootfs/etc/shadow"
fi

# Interactive terminals intentionally run as codev-shell while the checkout is
# assembled by root. Trust only this workspace and its managed worktrees at the
# protected system-config scope; a shell user must not have to weaken Git's
# ownership protection with a global wildcard before `git status` can work.
cat >>"${work_dir}/rootfs/etc/gitconfig" <<'GITCONFIG'
[safe]
directory = /workspace
directory = /workspace/*
GITCONFIG

cat >"${work_dir}/rootfs/etc/systemd/system/workspace.mount" <<'UNIT'
[Unit]
Description=CoDev workspace disk
Expand Down
9 changes: 9 additions & 0 deletions infra/runtime/scripts/provision-host-image.sh
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,15 @@ cp -a "/usr/lib/${guest_lib_dir}/." \
install -d -m 0755 "${work_dir}/rootfs/workspace"
install -d -m 0755 "${work_dir}/rootfs/etc/systemd/system/multi-user.target.wants"

# The interactive shell is unprivileged while CoDev assembles the checkout as
# root. Trust this checkout and its managed worktrees without making every
# path trusted for a terminal user.
cat >>"${work_dir}/rootfs/etc/gitconfig" <<'GITCONFIG'
[safe]
directory = /workspace
directory = /workspace/*
GITCONFIG

cat >"${work_dir}/rootfs/etc/systemd/system/workspace.mount" <<'UNIT'
[Unit]
Description=CoDev workspace disk
Expand Down
Loading