Bladeren bron

fix: restore brand integrity of TUI wordmark (#8584)

Kit Langton 1 maand geleden
bovenliggende
commit
3a9fd1bb36

+ 62 - 11
packages/opencode/src/cli/cmd/tui/component/logo.tsx

@@ -1,24 +1,75 @@
-import { TextAttributes } from "@opentui/core"
-import { For } from "solid-js"
-import { useTheme } from "@tui/context/theme"
+import { TextAttributes, RGBA } from "@opentui/core"
+import { For, type JSX } from "solid-js"
+import { useTheme, tint } from "@tui/context/theme"
 
-const LOGO_LEFT = [`                   `, `█▀▀█ █▀▀█ █▀▀█ █▀▀▄`, `█░░█ █░░█ █▀▀▀ █░░█`, `▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀  ▀`]
+// Shadow markers (rendered chars in parens):
+// _ = full shadow cell (space with bg=shadow)
+// ^ = letter top, shadow bottom (▀ with fg=letter, bg=shadow)
+// ~ = shadow top only (▀ with fg=shadow)
+const SHADOW_MARKER = /[_^~]/
 
-const LOGO_RIGHT = [`             ▄     `, `█▀▀▀ █▀▀█ █▀▀█ █▀▀█`, `█░░░ █░░█ █░░█ █▀▀▀`, `▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀`]
+const LOGO_LEFT = [
+  `                   `,
+  `█▀▀█ █▀▀█ █▀▀█ █▀▀▄`,
+  `█__█ █__█ █^^^ █__█`,
+  `▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀~~▀`,
+]
+
+const LOGO_RIGHT = [
+  `             ▄     `,
+  `█▀▀▀ █▀▀█ █▀▀█ █▀▀█`,
+  `█___ █__█ █__█ █^^^`,
+  `▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀`,
+]
 
 export function Logo() {
   const { theme } = useTheme()
+
+  const renderLine = (line: string, fg: RGBA, bold: boolean): JSX.Element[] => {
+    const shadow = tint(theme.background, fg, 0.25)
+    const attrs = bold ? TextAttributes.BOLD : undefined
+    const elements: JSX.Element[] = []
+    let i = 0
+
+    while (i < line.length) {
+      const rest = line.slice(i)
+      const markerIndex = rest.search(SHADOW_MARKER)
+
+      if (markerIndex === -1) {
+        elements.push(<text fg={fg} attributes={attrs} selectable={false}>{rest}</text>)
+        break
+      }
+
+      if (markerIndex > 0) {
+        elements.push(<text fg={fg} attributes={attrs} selectable={false}>{rest.slice(0, markerIndex)}</text>)
+      }
+
+      const marker = rest[markerIndex]
+      switch (marker) {
+        case "_":
+          elements.push(<text fg={fg} bg={shadow} attributes={attrs} selectable={false}> </text>)
+          break
+        case "^":
+          elements.push(<text fg={fg} bg={shadow} attributes={attrs} selectable={false}>▀</text>)
+          break
+        case "~":
+          elements.push(<text fg={shadow} attributes={attrs} selectable={false}>▀</text>)
+          break
+      }
+
+      i += markerIndex + 1
+    }
+
+    return elements
+  }
+
   return (
     <box>
       <For each={LOGO_LEFT}>
         {(line, index) => (
           <box flexDirection="row" gap={1}>
-            <text fg={theme.textMuted} selectable={false}>
-              {line}
-            </text>
-            <text fg={theme.text} attributes={TextAttributes.BOLD} selectable={false}>
-              {LOGO_RIGHT[index()]}
-            </text>
+            <box flexDirection="row">{renderLine(line, theme.textMuted, false)}</box>
+            <box flexDirection="row">{renderLine(LOGO_RIGHT[index()], theme.text, true)}</box>
           </box>
         )}
       </For>

+ 7 - 7
packages/opencode/src/cli/cmd/tui/context/theme.tsx

@@ -417,6 +417,13 @@ async function getCustomThemes() {
   return result
 }
 
+export function tint(base: RGBA, overlay: RGBA, alpha: number): RGBA {
+  const r = base.r + (overlay.r - base.r) * alpha
+  const g = base.g + (overlay.g - base.g) * alpha
+  const b = base.b + (overlay.b - base.b) * alpha
+  return RGBA.fromInts(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255))
+}
+
 function generateSystem(colors: TerminalColors, mode: "dark" | "light"): ThemeJson {
   const bg = RGBA.fromHex(colors.defaultBackground ?? colors.palette[0]!)
   const fg = RGBA.fromHex(colors.defaultForeground ?? colors.palette[7]!)
@@ -428,13 +435,6 @@ function generateSystem(colors: TerminalColors, mode: "dark" | "light"): ThemeJs
     return ansiToRgba(i)
   }
 
-  const tint = (base: RGBA, overlay: RGBA, alpha: number) => {
-    const r = base.r + (overlay.r - base.r) * alpha
-    const g = base.g + (overlay.g - base.g) * alpha
-    const b = base.b + (overlay.b - base.b) * alpha
-    return RGBA.fromInts(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255))
-  }
-
   // Generate gray scale based on terminal background
   const grays = generateGrayScale(bg, isDark)
   const textMuted = generateMutedTextColor(bg, isDark)