diff --git a/packages/opencode/src/cli/ui.ts b/packages/opencode/src/cli/ui.ts index 0eb6be187e..05f0454b8f 100644 --- a/packages/opencode/src/cli/ui.ts +++ b/packages/opencode/src/cli/ui.ts @@ -1,9 +1,8 @@ import { EOL } from "os" import { Schema } from "effect" -import { banner } from "./logo" +import { ansiWordmark, wordmark } from "./logo" -// Fork: hub-style CODEV banner (see @opencode-ai/tui/logo). -const wordmark = banner +// Fork: lowercase "codev" wordmark (see @opencode-ai/tui/logo). export class CancelledError extends Schema.TaggedErrorClass()("UICancelledError", {}) {} @@ -45,8 +44,8 @@ export function logo(pad?: string) { if (!process.stdout.isTTY && !process.stderr.isTTY) { return wordmark.map((row) => (pad ?? "") + row).join(EOL) } - // Bold bright cyan, matching the codev hub CLI banner. - return wordmark.map((row) => (pad ?? "") + "\x1b[1m\x1b[96m" + row + "\x1b[0m").join(EOL) + // Bold codev brand teal with the drop shadow, matching the TUI home banner. + return ansiWordmark(pad).join(EOL) } export async function input(prompt: string): Promise { diff --git a/packages/tui/src/component/logo.tsx b/packages/tui/src/component/logo.tsx index 5ade5401a8..07e9598d80 100644 --- a/packages/tui/src/component/logo.tsx +++ b/packages/tui/src/component/logo.tsx @@ -1,19 +1,62 @@ -import { RGBA, TextAttributes } from "@opentui/core" -import { For } from "solid-js" -import { banner } from "../logo" - -// Fork: hub-style CODEV banner in uniform bold teal (Tailwind cyan-600), -// the codev brand color. -const BRAND = RGBA.fromHex("#0891b2") +import { TextAttributes } from "@opentui/core" +import { For, type JSX } from "solid-js" +import { tint, useTheme } from "../context/theme" +import { logo } from "../logo" +// Fork: lowercase "codev" wordmark (half the height of the block banner) in the +// theme's brand secondary (teal in dark, purple in light), with shadow marks. export function Logo() { + const { theme } = useTheme() + + const renderLine = (line: string, bold: boolean): JSX.Element[] => { + const brand = theme.secondary + const shadow = tint(theme.background, brand, 0.25) + const attrs = bold ? TextAttributes.BOLD : undefined + return Array.from(line).map((char) => { + if (char === "_") { + return ( + + {" "} + + ) + } + if (char === "^") { + return ( + + ▀ + + ) + } + if (char === "~") { + return ( + + ▀ + + ) + } + if (char === ",") { + return ( + + ▄ + + ) + } + return ( + + {char} + + ) + }) + } + return ( - - {(line) => ( - - {line} - + + {(line, index) => ( + + {renderLine(line, true)} + {renderLine(logo.right[index()], true)} + )} diff --git a/packages/tui/src/logo.ts b/packages/tui/src/logo.ts index 6b76e302c8..085f59e0eb 100644 --- a/packages/tui/src/logo.ts +++ b/packages/tui/src/logo.ts @@ -5,16 +5,35 @@ export const logo = { right: [" ▄ ", "█▀▀█ █▀▀█ █__█", "█__█ █^^^ █__█", "▀▀▀▀ ▀▀▀▀ ▀▀ "], } -// Fork: hub-style CODEV banner (same ANSI-Shadow art as the codev hub's -// src/components/Banner.tsx). Used by the TUI home banner and CLI help. -export const banner = [ - " ██████╗ ██████╗ ██████╗ ███████╗██╗ ██╗", - "██╔════╝██╔═══██╗██╔══██╗██╔════╝██║ ██║", - "██║ ██║ ██║██║ ██║█████╗ ██║ ██║", - "██║ ██║ ██║██║ ██║██╔══╝ ╚██╗ ██╔╝", - "╚██████╗╚██████╔╝██████╔╝███████╗ ╚████╔╝ ", - " ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═══╝ ", -] +// Fork: the lowercase "codev" wordmark composed into plain lines (shadow marks +// substituted to glyphs) for CLI banners that color each line uniformly. +const substituteMarks = (line: string) => + Array.from(line, (char) => + char === "_" ? " " : char === "^" || char === "~" ? "▀" : char === "," ? "▄" : char, + ).join("") +export const wordmark = logo.left.map( + (line, index) => `${substituteMarks(line)} ${substituteMarks(logo.right[index] ?? "")}`, +) + +// Fork: ANSI-colored lowercase wordmark for CLI banners, matching the TUI home +// banner: bold brand teal with a drop shadow behind the mark cells. The shadow +// is tint(theme dark background #0a0a0a, teal, 0.25) = #0a2c34. +export function ansiWordmark(pad = "") { + const reset = "\x1b[0m" + const teal = "\x1b[1m\x1b[38;2;8;145;178m" + const shadowFg = "\x1b[38;2;10;44;52m" + const shadowBg = "\x1b[48;2;10;44;52m" + const draw = (line: string) => + Array.from(line, (char) => { + if (char === "_") return `${shadowBg} ${reset}` + if (char === "^") return `${teal}${shadowBg}▀${reset}` + if (char === "~") return `${shadowFg}▀${reset}` + if (char === ",") return `${shadowFg}▄${reset}` + if (char === " ") return " " + return `${teal}${char}${reset}` + }).join("") + return logo.left.map((line, index) => `${pad}${draw(line)} ${draw(logo.right[index] ?? "")}`) +} export const go = { left: [" ", "█▀▀▀", "█_^█", "▀▀▀▀"], diff --git a/packages/tui/src/util/presentation.ts b/packages/tui/src/util/presentation.ts index 5ce0fbd398..409ad8007f 100644 --- a/packages/tui/src/util/presentation.ts +++ b/packages/tui/src/util/presentation.ts @@ -1,18 +1,13 @@ -import { banner } from "../logo" +import { ansiWordmark } from "../logo" const reset = "\x1b[0m" const bold = "\x1b[1m" const dim = "\x1b[90m" -function wordmark(pad = "") { - // Fork: hub-style CODEV banner in bold bright cyan. - return banner.map((line) => `${pad}${bold}\x1b[96m${line}${reset}`) -} - export function sessionEpilogue(input: { title: string; sessionID?: string }) { const weak = (text: string) => `${dim}${text.padEnd(10, " ")}${reset}` return [ - ...wordmark(" "), + ...ansiWordmark(" "), "", ` ${weak("Session")}${bold}${input.title}${reset}`, ` ${weak("Continue")}${bold}codev -s ${input.sessionID}${reset}`,