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
23 changes: 22 additions & 1 deletion apps/web/src/components/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6410,7 +6410,10 @@ function ChatViewContent(props: ChatViewProps) {
composerBannerItems.length > 0 || Boolean(threadSyncPhase && !activeEnvironmentUnavailable);

return (
<div className="relative flex min-h-0 min-w-0 flex-1 overflow-hidden bg-background">
<div
className="terminal-shell relative flex min-h-0 min-w-0 flex-1 overflow-hidden bg-background"
data-terminal-shell="true"
>
{rightPanelOpen && !shouldUseRightPanelSheet ? panelLayoutControls : null}
<div
className={cn(
Expand Down Expand Up @@ -6574,6 +6577,24 @@ function ChatViewContent(props: ChatViewProps) {
className="w-full ps-[calc(env(safe-area-inset-left)+0.75rem)] pe-[calc(env(safe-area-inset-right)+0.75rem)] sm:ps-[calc(env(safe-area-inset-left)+1.25rem)] sm:pe-[calc(env(safe-area-inset-right)+1.25rem)]"
>
<div className="pointer-events-auto relative z-10">
{!isDraftHeroState ? (
<div
aria-live="polite"
className="flex items-center gap-2 px-1 pb-1.5 text-[11px] text-muted-foreground"
data-terminal-shell-status="true"
>
<span data-terminal-shell-status-state={isWorking ? "working" : "ready"}>
{isWorking ? (workingStepLabel ?? "Working") : "Ready"}
</span>
{isWorking ? <span aria-hidden="true">· esc to interrupt</span> : null}
{terminalUiState.terminalIds.length > 0 ? (
<span>
· {terminalUiState.terminalIds.length} terminal
{terminalUiState.terminalIds.length === 1 ? "" : "s"} active
</span>
) : null}
</div>
) : null}
{isDraftHeroState ? (
<div className="absolute inset-x-0 bottom-full z-0">
<div
Expand Down
190 changes: 190 additions & 0 deletions apps/web/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -1667,6 +1667,196 @@ html[data-theme-id="t3-chat"]
}
}

/* Terminal shell
The chat remains the same provider-backed workspace, but this presentation
makes the transcript feel like a live terminal session: the output owns the
canvas, prompts are flat and wide, and status/color are intentionally quiet. */
[data-terminal-shell="true"] {
--terminal-shell-background: #11161d;
--terminal-shell-surface: #1b2230;
--terminal-shell-border: rgb(255 255 255 / 9%);
--terminal-shell-muted: #8f9aa8;
--terminal-shell-foreground: #e6e9ed;
--terminal-shell-accent: #52e7d1;
--terminal-shell-selection: rgb(82 231 209 / 18%);
background: var(--terminal-shell-background);
color: var(--terminal-shell-foreground);
font-family: var(--font-mono);
}

[data-terminal-shell="true"] [data-chat-header] {
min-height: 2.75rem;
border-bottom: 1px solid var(--terminal-shell-border);
background: var(--terminal-shell-background);
color: var(--terminal-shell-muted);
font-family: var(--font-mono);
}

[data-terminal-shell="true"] [data-chat-header] h2,
[data-terminal-shell="true"] [data-chat-header] button,
[data-terminal-shell="true"] [data-chat-header] [data-project-favicon] {
font-family: var(--font-mono);
}

[data-terminal-shell="true"] [data-chat-workspace-drop-target="true"] {
background: var(--terminal-shell-background);
}

[data-terminal-shell="true"] [data-timeline-root="true"] {
max-width: none;
padding-inline: clamp(1rem, 4vw, 4rem);
font-family: var(--font-mono);
font-size: 0.9375rem;
line-height: 1.55;
}

[data-terminal-shell="true"] [data-timeline-row-kind="message"] {
padding-block: 0.35rem;
}

[data-terminal-shell="true"] [data-message-role="assistant"] > div {
padding-inline: 0;
}

[data-terminal-shell="true"] [data-message-role="user"] > .group {
align-items: flex-start;
gap: 0;
}

[data-terminal-shell="true"] [data-message-role="user"] > .group > div:first-child {
max-width: none;
border: 0;
border-radius: 0;
background: transparent;
padding: 0;
color: var(--terminal-shell-foreground);
}

[data-terminal-shell="true"] [data-message-role="user"] > .group > div:first-child::before {
margin-inline-end: 0.75rem;
color: var(--terminal-shell-accent);
content: ">";
line-height: 1.55;
}

[data-terminal-shell="true"] [data-message-role="user"] [data-user-message-footer="true"] {
max-width: none;
justify-content: flex-start;
padding-inline-start: 1.5rem;
color: var(--terminal-shell-muted);
}

[data-terminal-shell="true"] [data-message-role="assistant"] a,
[data-terminal-shell="true"] [data-message-role="assistant"] code,
[data-terminal-shell="true"] [data-message-role="user"] code {
color: var(--terminal-shell-accent);
}

[data-terminal-shell="true"] [data-message-role="assistant"] a {
text-decoration-color: color-mix(in srgb, var(--terminal-shell-accent) 65%, transparent);
}

[data-terminal-shell="true"] [data-message-role="assistant"] pre,
[data-terminal-shell="true"] [data-message-role="user"] pre {
border-color: var(--terminal-shell-border);
border-radius: 2px;
background: rgb(0 0 0 / 22%);
}

[data-terminal-shell="true"] .chat-composer-glass-shell {
--chat-composer-glass-surface: var(--terminal-shell-surface);
--chat-composer-outline: var(--terminal-shell-border);
max-width: none;
}

[data-terminal-shell="true"] .chat-composer-glass-shell::before,
[data-terminal-shell="true"] .chat-composer-glass-host::after {
border-radius: 3px;
}

[data-terminal-shell="true"] .chat-composer-glass-shell::before {
background: var(--terminal-shell-surface);
box-shadow: 0 -14px 34px -28px rgb(0 0 0 / 80%);
}

[data-terminal-shell="true"] .chat-composer-glass-host,
[data-terminal-shell="true"] [data-chat-composer-main-surface="true"],
[data-terminal-shell="true"] [data-chat-composer-surface="true"] {
border-radius: 3px;
}

[data-terminal-shell="true"] [data-chat-composer-form="true"] {
max-width: none;
}

[data-terminal-shell="true"] [data-chat-composer-main-surface="true"] {
border: 1px solid var(--terminal-shell-border);
background: var(--terminal-shell-surface);
box-shadow: none;
}

[data-terminal-shell="true"] [data-chat-composer-surface="true"] {
background: transparent;
}

[data-terminal-shell="true"] [data-testid="composer-editor"] {
min-height: 3.5rem;
padding-block: 0.15rem;
font-family: var(--font-mono);
font-size: 0.9375rem;
line-height: 1.55;
caret-color: var(--terminal-shell-accent);
}

[data-terminal-shell="true"] [data-testid="composer-editor"]::selection {
background: var(--terminal-shell-selection);
}

[data-terminal-shell="true"] [data-chat-composer-main-surface="true"] button:hover {
background: rgb(255 255 255 / 7%);
}

[data-terminal-shell="true"] [data-chat-composer-overlay] {
background: linear-gradient(to top, var(--terminal-shell-background) 0%, transparent 100%);
padding-top: 1.5rem;
}

[data-terminal-shell="true"] [data-terminal-shell-status="true"] {
font-family: var(--font-mono);
letter-spacing: 0.01em;
}

[data-terminal-shell="true"] [data-terminal-shell-status-state] {
color: var(--terminal-shell-muted);
}

[data-terminal-shell="true"] [data-terminal-shell-status-state]::before {
display: inline-block;
width: 0.5rem;
height: 0.5rem;
margin-inline-end: 0.5rem;
border-radius: 999px;
background: var(--terminal-shell-muted);
content: "";
vertical-align: 0.03em;
}

[data-terminal-shell="true"] [data-terminal-shell-status-state="working"] {
color: var(--terminal-shell-accent);
}

[data-terminal-shell="true"] [data-terminal-shell-status-state="working"]::before {
background: var(--terminal-shell-accent);
box-shadow: 0 0 0 3px rgb(82 231 209 / 10%);
}

@media (max-width: 639px) {
[data-terminal-shell="true"] [data-timeline-root="true"] {
padding-inline: 1rem;
font-size: 0.875rem;
}
}

/* Theme-token dependency probes are restored synchronously, before paint. Keep
transitions from observing the temporary sentinel color in between. */
html[data-theme-token-probe],
Expand Down