fix: restore brand integrity of TUI wordmark (#8584)
This commit is contained in:
@@ -1,24 +1,75 @@
|
|||||||
import { TextAttributes } from "@opentui/core"
|
import { TextAttributes, RGBA } from "@opentui/core"
|
||||||
import { For } from "solid-js"
|
import { For, type JSX } from "solid-js"
|
||||||
import { useTheme } from "@tui/context/theme"
|
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() {
|
export function Logo() {
|
||||||
const { theme } = useTheme()
|
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 (
|
return (
|
||||||
<box>
|
<box>
|
||||||
<For each={LOGO_LEFT}>
|
<For each={LOGO_LEFT}>
|
||||||
{(line, index) => (
|
{(line, index) => (
|
||||||
<box flexDirection="row" gap={1}>
|
<box flexDirection="row" gap={1}>
|
||||||
<text fg={theme.textMuted} selectable={false}>
|
<box flexDirection="row">{renderLine(line, theme.textMuted, false)}</box>
|
||||||
{line}
|
<box flexDirection="row">{renderLine(LOGO_RIGHT[index()], theme.text, true)}</box>
|
||||||
</text>
|
|
||||||
<text fg={theme.text} attributes={TextAttributes.BOLD} selectable={false}>
|
|
||||||
{LOGO_RIGHT[index()]}
|
|
||||||
</text>
|
|
||||||
</box>
|
</box>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
|
|||||||
@@ -417,6 +417,13 @@ async function getCustomThemes() {
|
|||||||
return result
|
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 {
|
function generateSystem(colors: TerminalColors, mode: "dark" | "light"): ThemeJson {
|
||||||
const bg = RGBA.fromHex(colors.defaultBackground ?? colors.palette[0]!)
|
const bg = RGBA.fromHex(colors.defaultBackground ?? colors.palette[0]!)
|
||||||
const fg = RGBA.fromHex(colors.defaultForeground ?? colors.palette[7]!)
|
const fg = RGBA.fromHex(colors.defaultForeground ?? colors.palette[7]!)
|
||||||
@@ -428,13 +435,6 @@ function generateSystem(colors: TerminalColors, mode: "dark" | "light"): ThemeJs
|
|||||||
return ansiToRgba(i)
|
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
|
// Generate gray scale based on terminal background
|
||||||
const grays = generateGrayScale(bg, isDark)
|
const grays = generateGrayScale(bg, isDark)
|
||||||
const textMuted = generateMutedTextColor(bg, isDark)
|
const textMuted = generateMutedTextColor(bg, isDark)
|
||||||
|
|||||||
Reference in New Issue
Block a user