From a188b7b198da37f00d8502085ed03a1f8562e61c Mon Sep 17 00:00:00 2001 From: Alex Stark Date: Sat, 22 Aug 2026 16:32:22 -0700 Subject: [PATCH] feat: add terminal chat shell --- apps/web/src/components/ChatView.tsx | 23 +++- apps/web/src/index.css | 190 +++++++++++++++++++++++++++ 2 files changed, 212 insertions(+), 1 deletion(-) diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx index 20966deb0c4f..b3e75edd4e7f 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -6410,7 +6410,10 @@ function ChatViewContent(props: ChatViewProps) { composerBannerItems.length > 0 || Boolean(threadSyncPhase && !activeEnvironmentUnavailable); return ( -
+
{rightPanelOpen && !shouldUseRightPanelSheet ? panelLayoutControls : null}
+ {!isDraftHeroState ? ( +
+ + {isWorking ? (workingStepLabel ?? "Working") : "Ready"} + + {isWorking ? : null} + {terminalUiState.terminalIds.length > 0 ? ( + + · {terminalUiState.terminalIds.length} terminal + {terminalUiState.terminalIds.length === 1 ? "" : "s"} active + + ) : null} +
+ ) : null} {isDraftHeroState ? (
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],