shell+ai: pristine-style nav groups, mobile fixes for /ai

Sidenav (app-shell.tsx):
- Each NavGroup now carries an icon (Building2 / Database / Plug /
  MessageSquare / Eye / Sparkles) rendered on the LEFT of the group
  header, with the chevron moved to the RIGHT. Header typography
  switched to caption + uppercase + tracking-wider muted, matching
  pristine-ui's main-branch app-shell. Same change applied to the
  mobile sheet's group headers.

/ai mobile fixes (ai.tsx):
- Composer container honors iOS safe-area inset
  (pb-[max(0.75rem,env(safe-area-inset-bottom))]) so the input clears
  the home indicator and stays above the soft keyboard.
- Composer toolbar wraps on narrow viewports (flex-wrap + gap-y-1)
  so the agent / model / reasoning / voice chips don't clip.
- Empty-state card uses px-4 sm:px-8 instead of hard px-8.
- MessageRow's 56px turn-number gutter collapses below sm: prose
  flows full-width on phone, two-column layout returns at sm+.

/ai desktop centering:
- Console wrapper opts out of AppShell's [&>*:first-child]:lg:pr-72
  (the page-header clearance for the floating top-right pill) via
  lg:!pr-0. The /ai surface has no top-right page-header controls,
  so the inherited padding was shifting the chat column ~144px left
  of the visible viewport center.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
jules
2026-05-05 19:08:36 +10:00
parent a286b9cdce
commit a74550d73f
2 changed files with 38 additions and 23 deletions

View File

@@ -630,7 +630,7 @@ export default function AIRoute() {
* toggle still works for them). */}
<div
data-theme="console"
className="-m-6 flex h-full min-h-0 flex-col bg-[var(--console-ink)] text-[var(--console-text)]"
className="-m-6 flex h-full min-h-0 flex-col bg-[var(--console-ink)] text-[var(--console-text)] lg:!pr-0"
>
<ChatSurface
models={availableModels}
@@ -1222,7 +1222,7 @@ function ChatSurface({
{/* Empty state — flight-recorder card with staggered reveal */}
<div
aria-hidden={!isEmpty}
className="pointer-events-none absolute inset-x-0 top-[10%] px-8 transition-opacity duration-300"
className="pointer-events-none absolute inset-x-0 top-[10%] px-4 transition-opacity duration-300 sm:px-8"
style={{ opacity: isEmpty ? 1 : 0 }}
>
<div className="mx-auto flex max-w-3xl flex-col gap-4">
@@ -1363,7 +1363,7 @@ function ChatSurface({
* center when empty, then springs to sticky-bottom on the first message. */}
<div
ref={composerRef}
className="sticky bottom-0 z-20 px-4 pb-3 pt-3 sm:px-6"
className="sticky bottom-0 z-20 px-4 pt-3 pb-[max(0.75rem,env(safe-area-inset-bottom))] sm:px-6"
style={{
transform: isEmpty
? "translateY(calc(-50dvh + 50% + 4rem))"
@@ -1630,8 +1630,8 @@ function MessageRow({
// row hangs from a left gutter showing the turn number.
if (role === "user") {
return (
<div className="grid grid-cols-[3.5rem_1fr] gap-x-3 self-stretch">
<div className="flex flex-col items-end pt-[3px]">
<div className="grid grid-cols-1 gap-x-3 self-stretch sm:grid-cols-[3.5rem_1fr]">
<div className="hidden flex-col items-end pt-[3px] sm:flex">
<span className="console-turn-num">
T{(turnNum ?? 0).toString().padStart(2, "0")}
</span>
@@ -1641,7 +1641,7 @@ function MessageRow({
</span>
) : null}
</div>
<div className="border-l border-[var(--console-rule-soft)] pl-4">
<div className="sm:border-l sm:border-[var(--console-rule-soft)] sm:pl-4">
<div className="console-op-line whitespace-pre-wrap">
<span className="console-op-prompt">&nbsp;</span>
{content}
@@ -1655,8 +1655,8 @@ function MessageRow({
// there's no prose (just tool calls), suppress the row entirely.
if (!content.trim()) return null
return (
<div className="grid grid-cols-[3.5rem_1fr] gap-x-3 self-stretch">
<div className="flex flex-col items-end pt-[2px]">
<div className="grid grid-cols-1 gap-x-3 self-stretch sm:grid-cols-[3.5rem_1fr]">
<div className="hidden flex-col items-end pt-[2px] sm:flex">
<span className="console-turn-num text-[var(--console-cyan)]">
T{(turnNum ?? 0).toString().padStart(2, "0")}
</span>
@@ -1664,7 +1664,7 @@ function MessageRow({
{agentName?.slice(0, 6).toLowerCase() ?? "atlas"}
</span>
</div>
<div className="border-l border-[var(--console-cyan-deep)]/40 pl-4">
<div className="sm:border-l sm:border-[var(--console-cyan-deep)]/40 sm:pl-4">
<div className="console-agent-prose">
<MessageBody content={content} toolCalls={toolCalls} />
</div>
@@ -1781,7 +1781,7 @@ function Composer({
className="min-h-[3.5rem] w-full resize-none bg-transparent outline-none"
/>
</div>
<div className="flex items-center justify-between gap-2">
<div className="flex flex-wrap items-center justify-between gap-x-2 gap-y-1">
<div className="flex items-center gap-1">
<button
type="button"