Просмотр исходного кода

feat(app): refine session UI styling (#33860)

Co-authored-by: Brendan Allan <14191578+Brendonovich@users.noreply.github.com>
usrnk1 2 месяцев назад
Родитель
Сommit
78a5a030ce

+ 3 - 1
packages/app/src/app.tsx

@@ -16,6 +16,7 @@ import {
   type Component,
   createEffect,
   createMemo,
+  createRenderEffect,
   createResource,
   createSignal,
   ErrorBoundary,
@@ -278,10 +279,11 @@ function QueryProvider(props: ParentProps) {
 function BodyDesignClass() {
   const settings = useSettings()
 
-  createEffect(() => {
+  createRenderEffect(() => {
     if (typeof document === "undefined") return
 
     const enabled = settings.general.newLayoutDesigns()
+    document.body.toggleAttribute("data-new-layout", enabled)
     document.body.classList.toggle("text-12-regular", !enabled)
     document.body.classList.toggle("font-(family-name:--font-family-text)", enabled)
     document.body.classList.toggle("text-[13px]", enabled)

+ 10 - 2
packages/app/src/pages/session.tsx

@@ -908,7 +908,13 @@ export default function Page() {
   )
 
   const reviewPanel = () => (
-    <div class="flex flex-col h-full overflow-hidden bg-background-stronger contain-strict">
+    <div
+      classList={{
+        "flex flex-col h-full overflow-hidden contain-strict": true,
+        "bg-v2-background-bg-base": settings.general.newLayoutDesigns(),
+        "bg-background-stronger": !settings.general.newLayoutDesigns(),
+      }}
+    >
       <div class="relative pt-2 flex-1 min-h-0 overflow-hidden">
         {reviewContent({
           diffStyle: layout.review.diffStyle(),
@@ -1713,7 +1719,9 @@ export default function Page() {
         >
           <div
             classList={{
-              "flex-1 min-h-0 flex flex-col bg-background-stronger": true,
+              "flex-1 min-h-0 flex flex-col": true,
+              "bg-v2-background-bg-base": settings.general.newLayoutDesigns(),
+              "bg-background-stronger": !settings.general.newLayoutDesigns(),
               "rounded-[10px] overflow-hidden": settings.general.newLayoutDesigns(),
               "shadow-[var(--v2-elevation-raised)]": settings.general.newLayoutDesigns() && !!params.id,
             }}

+ 7 - 1
packages/app/src/pages/session/composer/session-composer-region.tsx

@@ -1,5 +1,6 @@
 import { Show, type JSX } from "solid-js"
 import { useLanguage } from "@/context/language"
+import { useSettings } from "@/context/settings"
 import { SessionPermissionDock } from "@/pages/session/composer/session-permission-dock"
 import { SessionQuestionDock } from "@/pages/session/composer/session-question-dock"
 import { SessionFollowupDock } from "@/pages/session/composer/session-followup-dock"
@@ -13,6 +14,7 @@ export function SessionComposerRegion(props: {
 }) {
   const language = useLanguage()
   const controller = props.controller
+  const settings = useSettings()
   const rolled = () => {
     const revert = controller.revert()
     return revert?.items.length ? revert : undefined
@@ -22,7 +24,11 @@ export function SessionComposerRegion(props: {
     <div
       ref={controller.setDockRef}
       data-component="session-prompt-dock"
-      class="w-full shrink-0 flex flex-col justify-center items-center pb-3 bg-background-stronger pointer-events-none"
+      classList={{
+        "w-full shrink-0 flex flex-col justify-center items-center pb-3 pointer-events-none": true,
+        "bg-v2-background-bg-base": settings.general.newLayoutDesigns(),
+        "bg-background-stronger": !settings.general.newLayoutDesigns(),
+      }}
     >
       <div
         classList={{

+ 17 - 3
packages/app/src/pages/session/timeline/message-timeline.tsx

@@ -1019,7 +1019,13 @@ export function MessageTimeline(props: {
                 <div class="flex w-max min-w-full justify-end gap-2">
                   <Index each={comments()}>
                     {(comment) => (
-                      <div class="shrink-0 max-w-[260px] rounded-[6px] border border-border-weak-base bg-background-stronger px-2.5 py-2">
+                      <div
+                        classList={{
+                          "shrink-0 max-w-[260px] rounded-[6px] border-border-weak-base bg-background-stronger px-2.5 py-2": true,
+                          "border-[0.5px]": settings.general.newLayoutDesigns(),
+                          border: !settings.general.newLayoutDesigns(),
+                        }}
+                      >
                         <div class="flex items-center gap-1.5 min-w-0 text-11-medium text-text-strong">
                           <FileIcon node={{ path: comment().path, type: "file" }} class="size-3.5 shrink-0" />
                           <span class="truncate">{getFilename(comment().path)}</span>
@@ -1288,7 +1294,11 @@ export function MessageTimeline(props: {
           <div
             data-session-title
             classList={{
-              "sticky top-0 z-30 bg-[linear-gradient(to_bottom,var(--background-stronger)_48px,transparent)]": true,
+              "sticky top-0 z-30": true,
+              "bg-[linear-gradient(to_bottom,var(--v2-background-bg-base)_48px,transparent)]":
+                settings.general.newLayoutDesigns(),
+              "bg-[linear-gradient(to_bottom,var(--background-stronger)_48px,transparent)]":
+                !settings.general.newLayoutDesigns(),
               "w-full": true,
               "pb-4": true,
               "pr-3": true,
@@ -1509,7 +1519,11 @@ export function MessageTimeline(props: {
                                       <Button
                                         size="large"
                                         variant="secondary"
-                                        class="w-full shadow-none border border-border-weak-base"
+                                        class={
+                                          settings.general.newLayoutDesigns()
+                                            ? "w-full shadow-none border-[0.5px] border-border-weak-base"
+                                            : "w-full shadow-none border border-border-weak-base"
+                                        }
                                         onClick={unshareSession}
                                         disabled={unshareMutation.isPending}
                                       >

+ 40 - 9
packages/session-ui/src/components/basic-tool.css

@@ -43,7 +43,7 @@
     align-items: center;
     justify-content: center;
     flex-shrink: 0;
-    color: var(--text-weak);
+    color: var(--v2-text-text-faint);
 
     [data-component="spinner"] {
       width: 16px;
@@ -89,14 +89,14 @@
     font-weight: var(--font-weight-medium);
     line-height: var(--line-height-large);
     letter-spacing: var(--letter-spacing-normal);
-    color: var(--text-strong);
+    color: var(--v2-text-text-base);
 
     &.capitalize {
       text-transform: capitalize;
     }
 
     &.agent-title {
-      color: var(--text-strong);
+      color: var(--v2-text-text-base);
       font-weight: var(--font-weight-medium);
     }
   }
@@ -114,7 +114,7 @@
     font-weight: var(--font-weight-regular);
     line-height: var(--line-height-large);
     letter-spacing: var(--letter-spacing-normal);
-    color: var(--text-base);
+    color: var(--v2-text-text-muted);
 
     &.clickable {
       cursor: pointer;
@@ -122,7 +122,7 @@
       transition: color 0.15s ease;
 
       &:hover {
-        color: var(--text-base);
+        color: var(--v2-text-text-muted);
       }
     }
 
@@ -160,7 +160,7 @@
     font-weight: var(--font-weight-regular);
     line-height: var(--line-height-large);
     letter-spacing: var(--letter-spacing-normal);
-    color: var(--text-base);
+    color: var(--v2-text-text-muted);
   }
 
   [data-slot="basic-tool-tool-action"] {
@@ -178,7 +178,7 @@
   gap: 8px;
   padding: 8px 12px;
   border-radius: 6px;
-  border: 1px solid var(--border-weak-base, rgba(255, 255, 255, 0.08));
+  border: 0.5px solid var(--border-weak-base, rgba(255, 255, 255, 0.08));
   background: color-mix(in srgb, var(--background-base) 92%, transparent);
   transition:
     border-color 0.15s ease,
@@ -213,7 +213,7 @@
     align-items: center;
     justify-content: center;
     flex-shrink: 0;
-    color: var(--icon-weak);
+    color: var(--v2-text-text-faint);
     margin-left: auto;
     opacity: 0;
     transition:
@@ -233,7 +233,7 @@
   }
 
   [data-slot="basic-tool-tool-subtitle"] {
-    color: var(--text-strong);
+    color: var(--v2-text-text-base);
   }
 
   &:hover,
@@ -246,3 +246,34 @@
     }
   }
 }
+
+body:not([data-new-layout]) {
+  [data-component="tool-trigger"] {
+    [data-slot="basic-tool-tool-spinner"] {
+      color: var(--text-weak);
+    }
+
+    [data-slot="basic-tool-tool-title"],
+    [data-slot="basic-tool-tool-title"].agent-title {
+      color: var(--text-strong);
+    }
+
+    [data-slot="basic-tool-tool-subtitle"],
+    [data-slot="basic-tool-tool-subtitle"].clickable:hover,
+    [data-slot="basic-tool-tool-arg"] {
+      color: var(--text-base);
+    }
+  }
+
+  [data-component="task-tool-card"] {
+    border-width: 1px;
+
+    [data-component="task-tool-action"] {
+      color: var(--icon-weak);
+    }
+
+    [data-slot="basic-tool-tool-subtitle"] {
+      color: var(--text-strong);
+    }
+  }
+}

+ 30 - 0
packages/session-ui/src/components/markdown-inline-code-kind.test.ts

@@ -0,0 +1,30 @@
+import { describe, expect, test } from "bun:test"
+import { inlineCodeKind } from "./markdown-inline-code-kind"
+
+describe("inlineCodeKind", () => {
+  test("leaves code expressions as normal inline code", () => {
+    expect(
+      inlineCodeKind(`case "question.asked": ... input.setStore("question", question.sessionID, [question]) / splice/insert`),
+    ).toBeUndefined()
+    expect(inlineCodeKind(`<SessionQuestionDock request={request} ... />`)).toBeUndefined()
+    expect(inlineCodeKind(`from sync.data.question + sync.data.session.`)).toBeUndefined()
+    expect(inlineCodeKind(`@opencode-ai/app <StatusPopover />)`)).toBeUndefined()
+    expect(inlineCodeKind(`sync.data.session`)).toBeUndefined()
+    expect(inlineCodeKind(`window.api`)).toBeUndefined()
+  })
+
+  test("detects file and directory paths", () => {
+    expect(inlineCodeKind(`app.tsx`)).toBe("path")
+    expect(inlineCodeKind(`packages/desktop-electron`)).toBe("path")
+    expect(inlineCodeKind(`~/.config/opencode`)).toBe("path")
+    expect(inlineCodeKind(`@opencode-ai/app`)).toBe("path")
+    expect(inlineCodeKind(`session/status`)).toBe("path")
+  })
+
+  test("detects urls", () => {
+    expect(inlineCodeKind(`https://opencode.ai/docs`)).toBe("url")
+    expect(inlineCodeKind(`http://localhost:4444`)).toBe("url")
+    expect(inlineCodeKind(`file:///tmp/opencode`)).toBeUndefined()
+    expect(inlineCodeKind(`ftp://opencode.ai/docs`)).toBeUndefined()
+  })
+})

+ 13 - 0
packages/session-ui/src/components/markdown-inline-code-kind.ts

@@ -0,0 +1,13 @@
+export function inlineCodeKind(text: string): "path" | "url" | undefined {
+  if (/^https?:\/\//i.test(text)) return "url"
+  if (/^[a-z][a-z0-9+.-]*:\/\//i.test(text)) return
+  if (/^\/[a-z][a-z0-9-]*$/i.test(text)) return
+  if (/\s/.test(text)) return
+  if (/[()\[\]{}*+=<>|&^"';]/.test(text)) return
+  if (
+    /[/\\]/.test(text) ||
+    /^\.\.?[/\\]/.test(text) ||
+    /\.(tsx?|jsx?|json|py|go|rs|rb|php|css|html|md|sh|ya?ml|toml|sql|c|cpp|h|java|kt|swift|scala|xml|png|jpe?g|gif|svg|ico)$/i.test(text)
+  )
+    return "path"
+}

+ 137 - 24
packages/session-ui/src/components/markdown.css

@@ -1,9 +1,17 @@
 [data-component="markdown"] {
+  --markdown-inline-code-path-color: var(--v2-text-text-code-accent);
+  --markdown-inline-code-color: var(--v2-text-text-code-accent);
+  --markdown-inline-code-bg-mix: 8%;
+  --markdown-shell-code-background: var(--v2-background-bg-layer-02);
+  --markdown-shell-code-border: var(--v2-border-border-muted);
+  --markdown-shell-code-color: var(--v2-text-text-base);
+  --markdown-shell-code-token-color: currentColor;
+
   /* Reset & Base Typography */
   min-width: 0;
   max-width: 100%;
   overflow-wrap: break-word;
-  color: var(--text-strong);
+  color: var(--v2-text-text-base);
   font-family: var(--font-family-sans);
   font-size: var(--font-size-base); /* 14px */
   line-height: 160%;
@@ -22,15 +30,31 @@
     margin-bottom: 0;
   }
 
-  /* Headings: Same size, distinguished by color and spacing */
-  h1,
-  h2,
+  /* Headings: Sized by level, distinguished by color and spacing */
+  h1 {
+    font-size: 17px;
+    color: var(--v2-text-text-base);
+    font-weight: 600;
+    margin-top: 0px;
+    margin-bottom: 24px;
+    line-height: var(--line-height-large);
+  }
+
+  h2 {
+    font-size: 15px;
+    color: var(--v2-text-text-base);
+    font-weight: 600;
+    margin-top: 0px;
+    margin-bottom: 24px;
+    line-height: var(--line-height-large);
+  }
+
   h3,
   h4,
   h5,
   h6 {
-    font-size: 14px;
-    color: var(--text-strong);
+    font-size: 13px;
+    color: var(--v2-text-text-base);
     font-weight: var(--font-weight-medium);
     margin-top: 0px;
     margin-bottom: 24px;
@@ -40,7 +64,7 @@
   /* Emphasis & Strong: Neutral strong color */
   strong,
   b {
-    color: var(--text-strong);
+    color: var(--v2-text-text-base);
     font-weight: var(--font-weight-medium);
   }
 
@@ -95,7 +119,7 @@
   }
 
   li::marker {
-    color: var(--text-weak);
+    color: var(--v2-text-text-muted);
   }
 
   /* Nested lists spacing */
@@ -112,10 +136,10 @@
 
   /* Blockquotes */
   blockquote {
-    border-left: 2px solid var(--border-weak-base);
+    border-left: 0.5px solid var(--v2-border-border-base);
     margin: 1.5rem 0;
     padding-left: 0.5rem;
-    color: var(--text-weak);
+    color: var(--v2-text-text-muted);
     font-style: normal;
   }
 
@@ -132,13 +156,27 @@
     font-size: 13px;
     padding: 12px;
     border-radius: 6px;
-    border: 0.5px solid var(--border-weak-base);
+    border: 0.5px solid var(--v2-border-border-base);
   }
 
   [data-component="markdown-code"] {
     position: relative;
   }
 
+  [data-component="markdown-code"][data-code-kind="shell"] .shiki {
+    background: var(--markdown-shell-code-background);
+    border-color: var(--markdown-shell-code-border);
+    color: var(--markdown-shell-code-color);
+  }
+
+  [data-component="markdown-code"][data-code-kind="shell"] code {
+    color: var(--markdown-shell-code-color);
+  }
+
+  [data-component="markdown-code"][data-code-kind="shell"] code span {
+    color: var(--markdown-shell-code-token-color) !important;
+  }
+
   [data-slot="markdown-copy-button"] {
     position: absolute;
     top: 4px;
@@ -160,7 +198,7 @@
       background: var(--surface-float-base);
       color: var(--text-invert-strong);
       padding: 2px 8px;
-      border: 1px solid var(--border-weak-base, rgba(0, 0, 0, 0.07));
+      border: 0.5px solid var(--v2-border-border-base, rgba(0, 0, 0, 0.07));
       box-shadow: var(--shadow-md);
 
       pointer-events: none;
@@ -185,11 +223,11 @@
 
   [data-slot="markdown-copy-button"][data-variant="secondary"] {
     box-shadow: none;
-    border: 1px solid var(--border-weak-base);
+    border: 0.5px solid var(--v2-border-border-base);
   }
 
   [data-slot="markdown-copy-button"][data-variant="secondary"] [data-slot="icon-svg"] {
-    color: var(--icon-base);
+    color: var(--v2-icon-icon-base);
   }
 
   [data-component="markdown-code"]:hover [data-slot="markdown-copy-button"] {
@@ -222,15 +260,28 @@
   :not(pre) > code {
     font-family: var(--font-family-mono);
     font-feature-settings: var(--font-family-mono--font-feature-settings);
-    color: var(--syntax-string);
+    color: var(--v2-text-text-base);
     font-weight: var(--font-weight-medium);
-    /* font-size: 13px; */
+    padding: 2px 4px;
+    border-radius: 4px;
+    background: color-mix(in oklch, var(--v2-text-text-base) var(--markdown-inline-code-bg-mix), transparent);
+  }
 
-    /* padding: 2px 2px; */
-    /* margin: 0 1.5px; */
-    /* border-radius: 2px; */
-    /* background: var(--surface-base); */
-    /* box-shadow: 0 0 0 0.5px var(--border-weak-base); */
+  :not(pre) > code[data-inline-code-kind="path"] {
+    color: var(--markdown-inline-code-path-color);
+    background: color-mix(
+      in oklch,
+      var(--markdown-inline-code-path-color) var(--markdown-inline-code-bg-mix),
+      transparent
+    );
+  }
+
+  a.external-link > code[data-inline-code-kind="url"] {
+    color: var(--markdown-inline-code-color);
+    background: none;
+    text-decoration: underline;
+    text-decoration-color: color-mix(in oklch, var(--markdown-inline-code-color) 25%, transparent);
+    text-underline-offset: 2px;
   }
 
   /* Tables */
@@ -246,16 +297,16 @@
   th,
   td {
     /* Minimal borders for structure, matching TUI "lines" roughly but keeping it web-clean */
-    border-bottom: 1px solid var(--border-weaker-base);
+    border-bottom: 0.5px solid var(--v2-border-border-muted);
     padding: 12px;
     text-align: left;
     vertical-align: top;
   }
 
   th {
-    color: var(--text-strong);
+    color: var(--v2-text-text-base);
     font-weight: var(--font-weight-medium);
-    border-bottom: 1px solid var(--border-weak-base);
+    border-bottom: 0.5px solid var(--v2-border-border-base);
   }
 
   /* Images */
@@ -268,6 +319,68 @@
   }
 }
 
+body:not([data-new-layout]) [data-component="markdown"] {
+  color: var(--text-strong);
+
+  h1,
+  h2,
+  h3,
+  h4,
+  h5,
+  h6 {
+    font-size: 14px;
+    color: var(--text-strong);
+    font-weight: var(--font-weight-medium);
+  }
+
+  strong,
+  b {
+    color: var(--text-strong);
+  }
+
+  li::marker {
+    color: var(--text-weak);
+  }
+
+  blockquote {
+    border-left: 2px solid var(--border-weak-base);
+    color: var(--text-weak);
+  }
+
+  .shiki {
+    border-color: var(--border-weak-base);
+  }
+
+  :not(pre) > code {
+    color: var(--syntax-string);
+    padding: 0;
+    border-radius: 0;
+    background: none;
+  }
+
+  [data-slot="markdown-copy-button"]::after {
+    border: 1px solid var(--border-weak-base, rgba(0, 0, 0, 0.07));
+  }
+
+  [data-slot="markdown-copy-button"][data-variant="secondary"] {
+    border: 1px solid var(--border-weak-base);
+  }
+
+  [data-slot="markdown-copy-button"][data-variant="secondary"] [data-slot="icon-svg"] {
+    color: var(--icon-base);
+  }
+
+  th,
+  td {
+    border-bottom: 1px solid var(--border-weaker-base);
+  }
+
+  th {
+    color: var(--text-strong);
+    border-bottom-color: var(--border-weak-base);
+  }
+}
+
 [data-component="markdown"] a.external-link:hover > code {
   text-decoration: underline;
   text-underline-offset: 2px;

+ 48 - 0
packages/session-ui/src/components/markdown.tsx

@@ -25,6 +25,7 @@ import {
 import { markdownBlockKey, type MarkdownToken } from "./markdown-worker-protocol"
 import { shouldResetCodeTokens, type RenderedCodeState } from "./markdown-code-state"
 import { getCachedMarkdown, sanitizeMarkdown, touchCachedMarkdown, type MarkdownCacheEntry } from "./markdown-cache"
+import { inlineCodeKind } from "./markdown-inline-code-kind"
 
 type RenderedBlock =
   | (MarkdownCacheEntry & { key: string; mode: Exclude<Block["mode"], "code"> })
@@ -140,6 +141,35 @@ function setCopyState(button: HTMLButtonElement, labels: CopyLabels, copied: boo
   button.setAttribute("data-tooltip", labels.copy)
 }
 
+const shellLanguages = new Set(["bash", "sh", "shell", "zsh", "fish", "console", "terminal"])
+
+function codeKind(language: string | undefined) {
+  const value = language?.toLowerCase()
+  if (!value) return
+  if (shellLanguages.has(value)) return "shell"
+}
+
+function codeLanguage(block: HTMLPreElement) {
+  const code = block.querySelector("code")
+  if (!(code instanceof HTMLElement)) return
+  return code.className.match(/(?:^|\s)language-([^\s]+)/)?.[1]
+}
+
+function applyCodeMetadata(wrapper: HTMLElement, language: string | undefined) {
+  if (!document.body.hasAttribute("data-new-layout")) {
+    delete wrapper.dataset.language
+    delete wrapper.dataset.codeKind
+    return
+  }
+
+  if (language) wrapper.dataset.language = language
+  else delete wrapper.dataset.language
+
+  const kind = codeKind(language)
+  if (kind) wrapper.dataset.codeKind = kind
+  else delete wrapper.dataset.codeKind
+}
+
 function ensureCodeWrapper(block: HTMLPreElement, labels: CopyLabels) {
   const parent = block.parentElement
   if (!parent) return
@@ -147,12 +177,15 @@ function ensureCodeWrapper(block: HTMLPreElement, labels: CopyLabels) {
   if (!wrapped) {
     const wrapper = document.createElement("div")
     wrapper.setAttribute("data-component", "markdown-code")
+    applyCodeMetadata(wrapper, codeLanguage(block))
     parent.replaceChild(wrapper, block)
     wrapper.appendChild(block)
     wrapper.appendChild(createCopyButton(labels))
     return
   }
 
+  applyCodeMetadata(parent, codeLanguage(block))
+
   const buttons = Array.from(parent.querySelectorAll('[data-slot="markdown-copy-button"]')).filter(
     (el): el is HTMLButtonElement => el instanceof HTMLButtonElement,
   )
@@ -196,11 +229,23 @@ function markCodeLinks(root: HTMLDivElement) {
   }
 }
 
+function markInlineCode(root: HTMLDivElement) {
+  const codeNodes = Array.from(root.querySelectorAll(":not(pre) > code"))
+  for (const code of codeNodes) {
+    if (!(code instanceof HTMLElement)) continue
+    delete code.dataset.inlineCodeKind
+    const kind = inlineCodeKind(code.textContent ?? "")
+    if (kind) code.dataset.inlineCodeKind = kind
+  }
+}
+
 function decorate(root: HTMLDivElement, labels: CopyLabels) {
   const blocks = Array.from(root.querySelectorAll("pre"))
   for (const block of blocks) {
     ensureCodeWrapper(block, labels)
   }
+  if (!document.body.hasAttribute("data-new-layout")) return
+  markInlineCode(root)
   markCodeLinks(root)
 }
 
@@ -522,6 +567,8 @@ function updateCodeBlock(
 
   const code = existing?.querySelector("code")
   if (code instanceof HTMLElement) {
+    const wrapper = code.closest('[data-component="markdown-code"]')
+    if (wrapper instanceof HTMLElement) applyCodeMetadata(wrapper, block.language)
     code.className = `language-${block.language}`
     const previous = renderedCodeTokens.get(next)
     const reset = shouldResetCodeTokens(previous, {
@@ -552,6 +599,7 @@ function updateCodeBlock(
 
   const wrapper = document.createElement("div")
   wrapper.setAttribute("data-component", "markdown-code")
+  applyCodeMetadata(wrapper, block.language)
   const pre = document.createElement("pre")
   pre.className = "shiki OpenCode"
   const codeElement = document.createElement("code")

+ 244 - 55
packages/session-ui/src/components/message-part.css

@@ -14,7 +14,7 @@
   font-weight: var(--font-weight-regular);
   line-height: var(--line-height-large);
   letter-spacing: var(--letter-spacing-normal);
-  color: var(--text-strong);
+  color: var(--v2-text-text-base);
   display: flex;
   flex-direction: column;
   align-items: flex-end;
@@ -41,15 +41,15 @@
     min-width: 0;
     border-radius: 6px;
     overflow: hidden;
-    background: var(--surface-weak);
-    border: 1px solid var(--border-weak-base);
+    background: var(--v2-background-bg-layer-02);
+    border: 0.5px solid var(--v2-border-border-base);
     cursor: default;
     transition:
       border-color 0.15s ease,
       opacity 0.3s ease;
 
     &:hover {
-      border-color: var(--border-strong-base);
+      border-color: var(--v2-border-border-strong);
     }
 
     &[data-clickable] {
@@ -107,7 +107,7 @@
     overflow: hidden;
     text-overflow: ellipsis;
     white-space: nowrap;
-    color: var(--text-base);
+    color: var(--v2-text-text-muted);
     font-size: var(--font-size-small);
     line-height: var(--line-height-large);
   }
@@ -130,10 +130,10 @@
     white-space: pre-wrap;
     word-break: break-word;
     overflow: hidden;
-    background: var(--surface-base);
-    border: 1px solid var(--border-weak-base);
+    background: var(--v2-background-bg-layer-02);
+    border: none;
     padding: 8px 12px;
-    border-radius: 6px;
+    border-radius: 10px;
 
     [data-highlight="file"] {
       color: var(--syntax-property);
@@ -199,7 +199,7 @@
   }
 
   .text-text-strong {
-    color: var(--text-strong);
+    color: var(--v2-text-text-base);
   }
 
   .font-medium {
@@ -267,7 +267,7 @@
   [data-slot="compaction-part-line"] {
     flex: 1 1 auto;
     height: 1px;
-    background: var(--border-weak-base);
+    background: var(--v2-border-border-base);
   }
 
   [data-slot="compaction-part-label"] {
@@ -279,18 +279,18 @@
 
 [data-component="reasoning-part"] {
   width: 100%;
-  color: var(--text-base);
+  color: var(--v2-text-text-muted);
   line-height: var(--line-height-normal);
 
   [data-component="markdown"] {
     margin-top: 16px;
     font-style: normal;
     font-size: 13px;
-    color: var(--text-weak);
+    color: var(--v2-text-text-muted);
 
     strong,
     b {
-      color: var(--text-weak);
+      color: var(--v2-text-text-muted);
     }
 
     p:has(strong) {
@@ -357,7 +357,7 @@
 
 [data-component="bash-output"] {
   width: 100%;
-  border: 1px solid var(--border-weak-base);
+  border: 0.5px solid var(--v2-border-border-base);
   border-radius: 6px;
   background: transparent;
   position: relative;
@@ -380,7 +380,7 @@
 
   [data-slot="bash-copy"] [data-component="icon-button"][data-variant="secondary"] {
     box-shadow: none;
-    border: 1px solid var(--border-weak-base);
+    border: 0.5px solid var(--v2-border-border-base);
   }
 
   [data-slot="bash-copy"] [data-component="icon-button"][data-variant="secondary"] [data-slot="icon-svg"] {
@@ -442,7 +442,7 @@
     font-weight: var(--font-weight-medium);
     line-height: var(--line-height-large);
     letter-spacing: var(--letter-spacing-normal);
-    color: var(--text-base);
+    color: var(--v2-text-text-muted);
   }
 
   [data-slot="message-part-title-spinner"] {
@@ -453,7 +453,7 @@
     align-items: center;
     justify-content: center;
     flex-shrink: 0;
-    color: var(--text-weak);
+    color: var(--v2-text-text-muted);
 
     [data-component="spinner"] {
       width: 16px;
@@ -464,7 +464,7 @@
   [data-slot="message-part-title-text"] {
     flex-shrink: 0;
     text-transform: capitalize;
-    color: var(--text-strong);
+    color: var(--v2-text-text-base);
   }
 
   [data-slot="message-part-title-filename"] {
@@ -484,7 +484,7 @@
   }
 
   [data-slot="message-part-directory"] {
-    color: var(--text-weak);
+    color: var(--v2-text-text-muted);
     text-overflow: ellipsis;
     overflow: hidden;
     white-space: nowrap;
@@ -493,7 +493,7 @@
   }
 
   [data-slot="message-part-filename"] {
-    color: var(--text-strong);
+    color: var(--v2-text-text-base);
     flex-shrink: 0;
   }
 
@@ -508,7 +508,7 @@
 
 [data-component="edit-content"] {
   border-radius: inherit;
-  border-top: 1px solid var(--border-weaker-base);
+  border-top: 0.5px solid var(--v2-border-border-muted);
   overflow-x: hidden;
   overflow-y: visible;
 
@@ -527,7 +527,7 @@
 
 [data-component="write-content"] {
   border-radius: inherit;
-  border-top: 1px solid var(--border-weaker-base);
+  border-top: 0.5px solid var(--v2-border-border-muted);
   overflow-x: hidden;
   overflow-y: visible;
 
@@ -561,7 +561,7 @@
   font-family: var(--font-family-sans);
   font-size: var(--font-size-base);
   line-height: var(--line-height-large);
-  color: var(--text-base);
+  color: var(--v2-text-text-muted);
 }
 
 [data-slot="basic-tool-tool-subtitle"].exa-tool-query {
@@ -583,7 +583,7 @@
   max-width: 100%;
   font: inherit;
   line-height: inherit;
-  color: var(--text-interactive-base);
+  color: var(--v2-text-text-accent);
   text-decoration: underline;
   text-underline-offset: 2px;
   overflow: hidden;
@@ -591,11 +591,11 @@
   white-space: nowrap;
 
   &:hover {
-    color: var(--text-interactive-base);
+    color: var(--v2-text-text-accent);
   }
 
   &:visited {
-    color: var(--text-interactive-base);
+    color: var(--v2-text-text-accent);
   }
 }
 
@@ -615,7 +615,7 @@
 
     &[data-completed="completed"] {
       text-decoration: line-through;
-      color: var(--text-weaker);
+      color: var(--v2-text-text-faint);
     }
   }
 }
@@ -660,8 +660,8 @@
   flex-direction: column;
   gap: 4px;
   padding: 8px 12px;
-  background-color: var(--surface-critical-weak);
-  border-top: 1px solid var(--border-critical-base);
+  background-color: var(--v2-state-bg-danger);
+  border-top: 0.5px solid var(--v2-state-border-danger);
 
   [data-slot="diagnostic"] {
     display: flex;
@@ -673,7 +673,7 @@
   }
 
   [data-slot="diagnostic-label"] {
-    color: var(--text-on-critical-base);
+    color: var(--v2-state-fg-danger);
     font-weight: var(--font-weight-medium);
     text-transform: uppercase;
     letter-spacing: -0.5px;
@@ -681,12 +681,12 @@
   }
 
   [data-slot="diagnostic-location"] {
-    color: var(--text-on-critical-weak);
+    color: var(--v2-state-fg-danger);
     flex-shrink: 0;
   }
 
   [data-slot="diagnostic-message"] {
-    color: var(--text-on-critical-base);
+    color: var(--v2-state-fg-danger);
     word-break: break-word;
     display: -webkit-box;
     -webkit-box-orient: vertical;
@@ -761,7 +761,7 @@
     font-size: 14px;
     font-weight: var(--font-weight-medium);
     line-height: var(--line-height-large);
-    color: var(--text-strong);
+    color: var(--v2-text-text-base);
     min-width: 0;
   }
 
@@ -778,7 +778,7 @@
     font-size: 14px;
     font-weight: var(--font-weight-regular);
     line-height: var(--line-height-large);
-    color: var(--text-weak);
+    color: var(--v2-text-text-muted);
     padding: 0;
   }
 
@@ -856,7 +856,7 @@
     font-size: 14px;
     font-weight: var(--font-weight-medium);
     line-height: var(--line-height-large);
-    color: var(--text-strong);
+    color: var(--v2-text-text-base);
     min-width: 0;
   }
 
@@ -916,7 +916,7 @@
     font-size: 14px;
     font-weight: var(--font-weight-medium);
     line-height: var(--line-height-large);
-    color: var(--text-strong);
+    color: var(--v2-text-text-base);
     padding: 0 10px;
     -webkit-user-select: text;
     user-select: text;
@@ -927,7 +927,7 @@
     font-size: 13px;
     font-weight: var(--font-weight-regular);
     line-height: var(--line-height-large);
-    color: var(--text-weak);
+    color: var(--v2-text-text-muted);
     padding: 0 10px;
   }
 
@@ -953,8 +953,8 @@
     align-items: flex-start;
     gap: 12px;
     padding: 8px 8px 8px 10px;
-    background-color: var(--surface-raised-stronger-non-alpha);
-    border: 1px solid var(--border-weak-base);
+    background-color: var(--v2-background-bg-layer-01);
+    border: 0.5px solid var(--v2-border-border-base);
     border-radius: 6px;
     box-shadow: none;
     text-align: left;
@@ -966,11 +966,11 @@
       box-shadow 0.15s ease;
 
     &:hover:not([data-picked="true"]) {
-      background-color: var(--background-base);
+      background-color: var(--v2-background-bg-base);
     }
 
     &[data-picked="true"] {
-      background-color: var(--surface-interactive-weak);
+      background-color: var(--v2-state-bg-info);
       border-color: transparent;
       box-shadow: var(--shadow-xs-border-hover);
     }
@@ -991,7 +991,7 @@
     height: 16px;
     padding: 2px;
     border-radius: var(--radius-sm);
-    border: 1px solid var(--border-weak-base);
+    border: 0.5px solid var(--v2-border-border-base);
     display: inline-flex;
     align-items: center;
     justify-content: center;
@@ -1014,7 +1014,7 @@
       width: 6px;
       height: 6px;
       border-radius: 999px;
-      background-color: var(--background-stronger);
+      background-color: var(--v2-background-bg-layer-01);
       opacity: 0;
     }
 
@@ -1052,7 +1052,7 @@
     font-size: 14px;
     font-weight: var(--font-weight-medium);
     line-height: var(--line-height-large);
-    color: var(--text-strong);
+    color: var(--v2-text-text-base);
   }
 
   [data-slot="option-description"] {
@@ -1060,7 +1060,7 @@
     font-size: 14px;
     font-weight: var(--font-weight-regular);
     line-height: var(--line-height-large);
-    color: var(--text-base);
+    color: var(--v2-text-text-muted);
     min-width: 0;
     overflow-wrap: anywhere;
     white-space: normal;
@@ -1088,7 +1088,7 @@
     font-size: 14px;
     font-weight: var(--font-weight-regular);
     line-height: var(--line-height-large);
-    color: var(--text-base);
+    color: var(--v2-text-text-muted);
     min-width: 0;
     cursor: text;
     resize: none;
@@ -1096,11 +1096,11 @@
     overflow-wrap: anywhere;
 
     &::placeholder {
-      color: var(--text-weak);
+      color: var(--v2-text-text-muted);
     }
 
     &:focus-visible {
-      outline: 1px solid var(--border-interactive-base);
+      outline: 0.5px solid var(--v2-border-border-focus);
       outline-offset: 2px;
       border-radius: var(--radius-xs);
     }
@@ -1139,11 +1139,11 @@
     font-size: 13px;
 
     [data-slot="question-text"] {
-      color: var(--text-weak);
+      color: var(--v2-text-text-muted);
     }
 
     [data-slot="answer-text"] {
-      color: var(--text-strong);
+      color: var(--v2-text-text-base);
     }
   }
 }
@@ -1166,13 +1166,13 @@
     z-index: 20;
     height: calc(32px + var(--tool-content-gap));
     padding-bottom: var(--tool-content-gap);
-    background-color: var(--background-stronger);
+    background-color: var(--v2-background-bg-base);
   }
 }
 
 [data-component="accordion"][data-scope="apply-patch"] {
   [data-slot="accordion-trigger"] {
-    background-color: var(--background-stronger) !important;
+    background-color: var(--v2-background-bg-base) !important;
   }
 
   [data-slot="apply-patch-trigger-content"] {
@@ -1199,7 +1199,7 @@
   }
 
   [data-slot="apply-patch-directory"] {
-    color: var(--text-base);
+    color: var(--v2-text-text-muted);
     text-overflow: ellipsis;
     overflow: hidden;
     white-space: nowrap;
@@ -1208,7 +1208,7 @@
   }
 
   [data-slot="apply-patch-filename"] {
-    color: var(--text-strong);
+    color: var(--v2-text-text-base);
     min-width: 0;
     overflow: hidden;
     text-overflow: ellipsis;
@@ -1268,10 +1268,199 @@
   font-size: var(--font-size-small);
   font-weight: var(--font-weight-regular);
   line-height: var(--line-height-large);
-  color: var(--text-weak);
+  color: var(--v2-text-text-muted);
 
   [data-component="icon"] {
     flex-shrink: 0;
     color: var(--icon-weak);
   }
 }
+
+body:not([data-new-layout]) {
+  [data-component="user-message"] {
+    color: var(--text-strong);
+
+    [data-slot="user-message-attachment"] {
+      background: var(--surface-weak);
+      border: 1px solid var(--border-weak-base);
+
+      &:hover {
+        border-color: var(--border-strong-base);
+      }
+    }
+
+    [data-slot="user-message-attachment-name"] {
+      color: var(--text-base);
+    }
+
+    [data-slot="user-message-text"] {
+      background: var(--surface-base);
+      border: 1px solid var(--border-weak-base);
+      border-radius: 6px;
+    }
+
+    .text-text-strong {
+      color: var(--text-strong);
+    }
+  }
+
+  [data-slot="compaction-part-line"] {
+    background: var(--border-weak-base);
+  }
+
+  [data-component="reasoning-part"] {
+    color: var(--text-base);
+
+    [data-component="markdown"],
+    [data-component="markdown"] :is(strong, b) {
+      color: var(--text-weak);
+    }
+  }
+
+  [data-component="bash-output"] {
+    border: 1px solid var(--border-weak-base);
+
+    [data-slot="bash-copy"] [data-component="icon-button"][data-variant="secondary"] {
+      border: 1px solid var(--border-weak-base);
+    }
+  }
+
+  [data-component="edit-trigger"],
+  [data-component="write-trigger"] {
+    [data-slot="message-part-title"] {
+      color: var(--text-base);
+    }
+
+    [data-slot="message-part-title-spinner"],
+    [data-slot="message-part-directory"] {
+      color: var(--text-weak);
+    }
+
+    [data-slot="message-part-title-text"],
+    [data-slot="message-part-filename"] {
+      color: var(--text-strong);
+    }
+  }
+
+  [data-component="edit-content"],
+  [data-component="write-content"] {
+    border-top: 1px solid var(--border-weaker-base);
+  }
+
+  [data-component="exa-tool-output"] {
+    color: var(--text-base);
+  }
+
+  [data-slot="exa-tool-link"],
+  [data-slot="exa-tool-link"]:hover,
+  [data-slot="exa-tool-link"]:visited {
+    color: var(--text-interactive-base);
+  }
+
+  [data-slot="message-part-todo-content"][data-completed="completed"] {
+    color: var(--text-weaker);
+  }
+
+  [data-component="diagnostics"] {
+    background-color: var(--surface-critical-weak);
+    border-top: 1px solid var(--border-critical-base);
+
+    [data-slot="diagnostic-label"],
+    [data-slot="diagnostic-message"] {
+      color: var(--text-on-critical-base);
+    }
+
+    [data-slot="diagnostic-location"] {
+      color: var(--text-on-critical-weak);
+    }
+  }
+
+  [data-component="dock-prompt"][data-kind="permission"] {
+    [data-slot="permission-header-title"] {
+      color: var(--text-strong);
+    }
+
+    [data-slot="permission-hint"] {
+      color: var(--text-weak);
+    }
+  }
+
+  [data-component="dock-prompt"][data-kind="question"] {
+    [data-slot="question-header-title"],
+    [data-slot="question-text"],
+    [data-slot="option-label"] {
+      color: var(--text-strong);
+    }
+
+    [data-slot="question-hint"] {
+      color: var(--text-weak);
+    }
+
+    [data-slot="question-option"] {
+      background-color: var(--surface-raised-stronger-non-alpha);
+      border: 1px solid var(--border-weak-base);
+
+      &:hover:not([data-picked="true"]) {
+        background-color: var(--background-base);
+      }
+
+      &[data-picked="true"] {
+        background-color: var(--surface-interactive-weak);
+      }
+    }
+
+    [data-slot="question-option-box"] {
+      border: 1px solid var(--border-weak-base);
+
+      [data-slot="question-option-radio-dot"] {
+        background-color: var(--background-stronger);
+      }
+    }
+
+    [data-slot="option-description"],
+    [data-slot="question-custom-input"] {
+      color: var(--text-base);
+    }
+
+    [data-slot="question-custom-input"] {
+      &::placeholder {
+        color: var(--text-weak);
+      }
+
+      &:focus-visible {
+        outline: 1px solid var(--border-interactive-base);
+      }
+    }
+  }
+
+  [data-component="question-answers"] {
+    [data-slot="question-text"] {
+      color: var(--text-weak);
+    }
+
+    [data-slot="answer-text"] {
+      color: var(--text-strong);
+    }
+  }
+
+  :is([data-component="edit-tool"], [data-component="write-tool"], [data-component="apply-patch-tool"])
+    > [data-component="collapsible"]
+    > [data-slot="collapsible-trigger"][aria-expanded="true"],
+  [data-component="accordion"][data-scope="apply-patch"] [data-slot="accordion-trigger"] {
+    background-color: var(--background-stronger) !important;
+  }
+
+  [data-component="accordion"][data-scope="apply-patch"] {
+    [data-slot="apply-patch-directory"] {
+      color: var(--text-base);
+    }
+
+    [data-slot="apply-patch-filename"] {
+      color: var(--text-strong);
+    }
+  }
+
+  [data-component="tool-loaded-file"] {
+    color: var(--text-weak);
+  }
+}

+ 12 - 2
packages/session-ui/src/components/session-review.css

@@ -21,7 +21,7 @@
 
   [data-slot="session-review-header"] {
     z-index: 120;
-    background-color: var(--background-stronger);
+    background-color: var(--v2-background-bg-base);
     height: 40px;
     padding-bottom: 8px;
     flex-shrink: 0;
@@ -211,7 +211,7 @@
 
   [data-slot="session-review-large-diff"] {
     padding: 12px;
-    background: var(--background-stronger);
+    background: var(--v2-background-bg-base);
   }
 
   [data-slot="session-review-large-diff-title"] {
@@ -235,3 +235,13 @@
     margin-top: 10px;
   }
 }
+
+body:not([data-new-layout]) [data-component="session-review"] {
+  [data-slot="session-review-header"] {
+    background-color: var(--background-stronger);
+  }
+
+  [data-slot="session-review-large-diff"] {
+    background: var(--background-stronger);
+  }
+}

+ 12 - 2
packages/session-ui/src/components/session-turn.css

@@ -71,7 +71,7 @@
   }
 
   .error-card {
-    color: var(--text-on-critical-base);
+    color: var(--v2-text-text-base);
     max-height: 240px;
     white-space: pre-wrap;
     overflow-wrap: anywhere;
@@ -102,7 +102,7 @@
     position: sticky;
     top: var(--sticky-accordion-top, 0px);
     z-index: 20;
-    background-color: var(--background-stronger);
+    background-color: var(--v2-background-bg-base);
     height: 44px;
   }
 
@@ -226,6 +226,16 @@
   }
 }
 
+body:not([data-new-layout]) [data-component="session-turn"] {
+  .error-card {
+    color: var(--text-on-critical-base);
+  }
+
+  [data-slot="session-turn-diffs-header"] {
+    background-color: var(--background-stronger);
+  }
+}
+
 [data-slot="session-turn-list"] {
   gap: 24px;
 }

+ 39 - 2
packages/session-ui/src/components/tool-error-card.css

@@ -2,6 +2,19 @@
   --card-pad-y: 8px;
   --card-line-pad: 12px;
 
+  [data-slot="basic-tool-tool-title"] {
+    color: var(--v2-text-text-base);
+  }
+
+  [data-slot="basic-tool-tool-subtitle"] {
+    color: var(--v2-text-text-muted);
+  }
+
+  [data-slot="collapsible-arrow"],
+  [data-slot="collapsible-arrow-icon"] {
+    color: var(--v2-text-text-faint);
+  }
+
   > [data-component="collapsible"].tool-collapsible {
     gap: 0px;
   }
@@ -43,10 +56,34 @@
   }
 
   [data-slot="tool-error-card-content"] :where(*)::selection {
-    background: var(--surface-critical-base);
-    color: var(--text-on-critical-base);
+    background: var(--v2-state-bg-danger);
+    color: var(--v2-text-text-base);
+  }
+
+  [data-slot="tool-error-card-content"] :where(*)::-moz-selection {
+    background: var(--v2-state-bg-danger);
+    color: var(--v2-text-text-base);
+  }
+}
+
+body:not([data-new-layout]) [data-component="card"][data-kind="tool-error-card"] {
+  [data-slot="basic-tool-tool-title"] {
+    color: var(--text-strong);
+  }
+
+  [data-slot="basic-tool-tool-subtitle"] {
+    color: var(--text-base);
+  }
+
+  [data-slot="collapsible-arrow"] {
+    color: var(--text-base);
+  }
+
+  [data-slot="collapsible-arrow-icon"] {
+    color: var(--icon-weaker);
   }
 
+  [data-slot="tool-error-card-content"] :where(*)::selection,
   [data-slot="tool-error-card-content"] :where(*)::-moz-selection {
     background: var(--surface-critical-base);
     color: var(--text-on-critical-base);

+ 1 - 0
packages/ui/script/colors.txt

@@ -246,6 +246,7 @@
 --v2-text-text-contrast: var(--v2-grey-50);
 --v2-text-text-accent: var(--v2-blue-600);
 --v2-text-text-accent-hover: var(--v2-blue-700);
+--v2-text-text-code-accent: var(--v2-blue-900);
 
 --v2-icon-icon-base: var(--v2-grey-800);
 --v2-icon-icon-muted: var(--v2-grey-600);

+ 31 - 7
packages/ui/src/components/accordion.css

@@ -35,12 +35,12 @@
         cursor: default;
         user-select: none;
 
-        background-color: var(--background-stronger);
-        border: 1px solid var(--border-weak-base);
+        background-color: var(--v2-background-bg-base);
+        border: 0.5px solid var(--v2-border-border-base);
         border-radius: 0;
         box-shadow: none;
         overflow: clip;
-        color: var(--text-strong);
+        color: var(--v2-text-text-base);
         transition: background-color 0.15s ease;
 
         /* text-12-regular */
@@ -52,10 +52,10 @@
         letter-spacing: var(--letter-spacing-normal);
 
         &:hover:not([data-disabled]) {
-          background-color: var(--surface-base-hover);
+          background-color: var(--v2-overlay-simple-overlay-hover);
         }
         &:active:not([data-disabled]) {
-          background-color: var(--surface-base-active);
+          background-color: var(--v2-overlay-simple-overlay-pressed);
         }
         &:focus-visible {
           outline: none;
@@ -82,9 +82,9 @@
 
     &[data-expanded] {
       [data-slot="accordion-content"] {
-        border: 1px solid var(--border-weak-base);
+        border: 0.5px solid var(--v2-border-border-base);
         border-top: 0;
-        background-color: var(--background-stronger);
+        background-color: var(--v2-background-bg-layer-01);
       }
     }
 
@@ -104,6 +104,30 @@
   }
 }
 
+body:not([data-new-layout]) [data-component="accordion"] {
+  [data-slot="accordion-item"] [data-slot="accordion-header"] [data-slot="accordion-trigger"] {
+    background-color: var(--background-stronger);
+    border-width: 1px;
+    border-color: var(--border-weak-base);
+    color: var(--text-strong);
+
+    &:hover:not([data-disabled]) {
+      background-color: var(--surface-base-hover);
+    }
+
+    &:active:not([data-disabled]) {
+      background-color: var(--surface-base-active);
+    }
+  }
+
+  [data-slot="accordion-item"][data-expanded] [data-slot="accordion-content"] {
+    border-width: 1px;
+    border-color: var(--border-weak-base);
+    border-top: 0;
+    background-color: var(--background-stronger);
+  }
+}
+
 @keyframes slideDown {
   from {
     height: 0;

+ 26 - 5
packages/ui/src/components/card.css

@@ -19,14 +19,14 @@
   font-weight: var(--font-weight-regular);
   line-height: var(--line-height-large);
   letter-spacing: var(--letter-spacing-normal);
-  color: var(--text-strong);
+  color: var(--v2-text-text-base);
 
   --card-gap: 8px;
   --card-icon: 16px;
   --card-indent: 0px;
   --card-line-pad: 8px;
 
-  --card-accent: var(--icon-active);
+  --card-accent: var(--v2-icon-icon-base);
 
   &:has([data-slot="card-title"]) {
     gap: 8px;
@@ -48,7 +48,7 @@
   }
 
   :where([data-card="title"], [data-slot="card-title"]) {
-    color: var(--text-strong);
+    color: var(--v2-text-text-base);
     font-weight: var(--font-weight-medium);
   }
 
@@ -72,7 +72,7 @@
   }
 
   :where([data-slot="card-title-icon"][data-placeholder]) [data-component="icon"] {
-    color: var(--text-weak);
+    color: var(--v2-text-text-faint);
   }
 
   :where([data-slot="card-title-icon"])
@@ -82,7 +82,7 @@
   }
 
   :where([data-card="description"], [data-slot="card-description"]) {
-    color: var(--text-base);
+    color: var(--v2-text-text-muted);
     white-space: pre-wrap;
     overflow-wrap: anywhere;
     word-break: break-word;
@@ -92,3 +92,24 @@
     padding-left: var(--card-indent);
   }
 }
+
+body:not([data-new-layout]) [data-component="card"] {
+  color: var(--text-strong);
+  --card-accent: var(--icon-active);
+
+  :where([data-card="title"], [data-slot="card-title"]) {
+    color: var(--text-strong);
+  }
+
+  :where([data-slot="card-title-icon"][data-placeholder]) [data-component="icon"] {
+    color: var(--text-weak);
+  }
+
+  :where([data-card="description"], [data-slot="card-description"]) {
+    color: var(--text-base);
+  }
+
+  &[data-variant="error"] {
+    --card-accent: var(--icon-critical-base) !important;
+  }
+}

+ 1 - 1
packages/ui/src/components/card.tsx

@@ -40,7 +40,7 @@ export function Card(props: CardProps) {
   const variant = () => split.variant ?? "normal"
   const accent = () => {
     const v = variant()
-    if (v === "error") return "var(--icon-critical-base)"
+    if (v === "error") return "var(--v2-state-fg-danger)"
     if (v === "warning") return "var(--icon-warning-active)"
     if (v === "success") return "var(--icon-success-active)"
     if (v === "info") return "var(--icon-info-active)"

+ 2 - 1
packages/ui/src/styles/tailwind/colors.css

@@ -249,6 +249,7 @@
   --color-v2-text-text-contrast: var(--v2-text-text-contrast);
   --color-v2-text-text-accent: var(--v2-text-text-accent);
   --color-v2-text-text-accent-hover: var(--v2-text-text-accent-hover);
+  --color-v2-text-text-code-accent: var(--v2-text-text-code-accent);
   --color-v2-icon-icon-base: var(--v2-icon-icon-base);
   --color-v2-icon-icon-muted: var(--v2-icon-icon-muted);
   --color-v2-icon-icon-inverse: var(--v2-icon-icon-inverse);
@@ -282,4 +283,4 @@
   --color-v2-state-bg-info: var(--v2-state-bg-info);
   --color-v2-state-fg-info: var(--v2-state-fg-info);
   --color-v2-state-border-info: var(--v2-state-border-info);
-}
+}

+ 20 - 14
packages/ui/src/theme/themes/oc-2.json

@@ -20,6 +20,8 @@
       "text-base": "#6F6F6F",
       "text-weak": "#8F8F8F",
       "text-weaker": "#C7C7C7",
+      "text-diff-add-base": "var(--v2-state-fg-success)",
+      "text-diff-delete-base": "var(--v2-state-fg-danger)",
       "border-weak-base": "#DBDBDB",
       "border-weaker-base": "#E8E8E8",
       "icon-base": "#8F8F8F",
@@ -31,14 +33,14 @@
       "surface-interactive-weak": "#F5FAFF",
       "icon-success-base": "#0ABE00",
       "surface-success-base": "#E6FFE5",
-      "syntax-comment": "#7a7a7a",
-      "syntax-keyword": "#a753ae",
-      "syntax-string": "#00ceb9",
-      "syntax-primitive": "#034cff",
-      "syntax-property": "#a753ae",
-      "syntax-type": "#8a6f00",
+      "syntax-comment": "var(--v2-text-text-muted)",
+      "syntax-keyword": "var(--v2-pink-800)",
+      "syntax-string": "var(--v2-green-800)",
+      "syntax-primitive": "var(--v2-pink-800)",
+      "syntax-property": "var(--v2-orange-800)",
+      "syntax-type": "var(--v2-purple-800)",
       "syntax-constant": "#007b80",
-      "syntax-critical": "#ff8c00",
+      "syntax-critical": "var(--v2-red-800)",
       "syntax-diff-delete": "#ff8c00",
       "syntax-diff-unknown": "#a753ae",
       "surface-critical-base": "#FFF2F0"
@@ -170,6 +172,7 @@
       "v2-text-text-contrast": "var(--v2-grey-50)",
       "v2-text-text-accent": "var(--v2-blue-600)",
       "v2-text-text-accent-hover": "var(--v2-blue-700)",
+      "v2-text-text-code-accent": "var(--v2-blue-900)",
       "v2-icon-icon-base": "var(--v2-grey-800)",
       "v2-icon-icon-muted": "var(--v2-grey-600)",
       "v2-icon-icon-inverse": "var(--v2-grey-50)",
@@ -252,6 +255,8 @@
       "text-base": "#A0A0A0",
       "text-weak": "#707070",
       "text-weaker": "#505050",
+      "text-diff-add-base": "var(--v2-state-fg-success)",
+      "text-diff-delete-base": "var(--v2-state-fg-danger)",
       "border-weak-base": "#282828",
       "border-weaker-base": "#232323",
       "icon-base": "#7E7E7E",
@@ -262,14 +267,14 @@
       "surface-base-hover": "#FFFFFF0D",
       "surface-interactive-weak": "#0D172B",
       "surface-success-base": "#022B00",
-      "syntax-comment": "#8f8f8f",
-      "syntax-keyword": "#edb2f1",
-      "syntax-string": "#00ceb9",
-      "syntax-primitive": "#8cb0ff",
-      "syntax-property": "#fab283",
-      "syntax-type": "#fcd53a",
+      "syntax-comment": "var(--v2-text-text-muted)",
+      "syntax-keyword": "var(--v2-pink-400)",
+      "syntax-string": "var(--v2-green-400)",
+      "syntax-primitive": "var(--v2-pink-400)",
+      "syntax-property": "var(--v2-orange-400)",
+      "syntax-type": "var(--v2-purple-400)",
       "syntax-constant": "#93e9f6",
-      "syntax-critical": "#fab283",
+      "syntax-critical": "var(--v2-red-400)",
       "syntax-diff-delete": "#fab283",
       "syntax-diff-unknown": "#edb2f1",
       "surface-critical-base": "#1F0603"
@@ -401,6 +406,7 @@
       "v2-text-text-contrast": "var(--v2-grey-50)",
       "v2-text-text-accent": "var(--v2-blue-400)",
       "v2-text-text-accent-hover": "var(--v2-blue-300)",
+      "v2-text-text-code-accent": "var(--v2-blue-400)",
       "v2-icon-icon-base": "var(--v2-grey-400)",
       "v2-icon-icon-muted": "var(--v2-grey-600)",
       "v2-icon-icon-inverse": "var(--v2-grey-1100)",

+ 2 - 0
packages/ui/src/theme/v2/mapping.ts

@@ -18,6 +18,7 @@ const light: Record<string, V2ColorValue> = {
   "v2-text-text-contrast": ref("v2-grey-100"),
   "v2-text-text-accent": ref("v2-blue-600"),
   "v2-text-text-accent-hover": ref("v2-blue-700"),
+  "v2-text-text-code-accent": ref("v2-blue-900"),
   "v2-border-border-muted": ref("v2-alpha-dark-8"),
   "v2-border-border-base": ref("v2-alpha-dark-10"),
   "v2-border-border-strong": ref("v2-alpha-dark-20"),
@@ -81,6 +82,7 @@ const dark: Record<string, V2ColorValue> = {
   "v2-text-text-contrast": ref("v2-grey-100"),
   "v2-text-text-accent": ref("v2-blue-400"),
   "v2-text-text-accent-hover": ref("v2-blue-300"),
+  "v2-text-text-code-accent": ref("v2-blue-400"),
   "v2-border-border-muted": ref("v2-alpha-light-8"),
   "v2-border-border-base": ref("v2-alpha-light-10"),
   "v2-border-border-strong": ref("v2-alpha-light-20"),

+ 16 - 0
packages/ui/src/v2/styles/theme.css

@@ -1,4 +1,16 @@
 @layer theme {
+  html[data-theme="oc-2"] body:not([data-new-layout]) {
+    --text-diff-add-base: light-dark(#167517, #c4ffc0);
+    --text-diff-delete-base: light-dark(#fa3012, #ec2f14);
+    --syntax-comment: light-dark(#7a7a7a, #8f8f8f);
+    --syntax-keyword: light-dark(#a753ae, #edb2f1);
+    --syntax-string: #00ceb9;
+    --syntax-primitive: light-dark(#034cff, #8cb0ff);
+    --syntax-property: light-dark(#a753ae, #fab283);
+    --syntax-type: light-dark(#8a6f00, #fcd53a);
+    --syntax-critical: light-dark(#ff8c00, #fab283);
+  }
+
   :root {
     color-scheme: light;
 
@@ -22,6 +34,7 @@
     --v2-text-text-contrast: var(--v2-grey-50);
     --v2-text-text-accent: var(--v2-blue-600);
     --v2-text-text-accent-hover: var(--v2-blue-700);
+    --v2-text-text-code-accent: var(--v2-blue-900);
 
     /* ── Icon ── */
     --v2-icon-icon-base: var(--v2-grey-800);
@@ -141,6 +154,7 @@
       --v2-text-text-contrast: var(--v2-grey-100);
       --v2-text-text-accent: var(--v2-blue-400);
       --v2-text-text-accent-hover: var(--v2-blue-300);
+      --v2-text-text-code-accent: var(--v2-blue-400);
 
       --v2-icon-icon-base: var(--v2-grey-400);
       --v2-icon-icon-muted: var(--v2-grey-600);
@@ -243,6 +257,7 @@
     --v2-text-text-contrast: var(--v2-grey-50);
     --v2-text-text-accent: var(--v2-blue-600);
     --v2-text-text-accent-hover: var(--v2-blue-700);
+    --v2-text-text-code-accent: var(--v2-blue-900);
 
     --v2-icon-icon-base: var(--v2-grey-800);
     --v2-icon-icon-muted: var(--v2-grey-600);
@@ -352,6 +367,7 @@
     --v2-text-text-contrast: var(--v2-grey-50);
     --v2-text-text-accent: var(--v2-blue-400);
     --v2-text-text-accent-hover: var(--v2-blue-300);
+    --v2-text-text-code-accent: var(--v2-blue-400);
 
     --v2-icon-icon-base: var(--v2-grey-400);
     --v2-icon-icon-muted: var(--v2-grey-600);