diff --git a/CHANGELOG.md b/CHANGELOG.md index b4cda3e3f7..21e2397190 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,33 @@ file is the human-readable summary of what changed and why. ## Unreleased +### Desktop + +- **The app icon is the CoDev mark** — the Dock, taskbar and installer icons + were still upstream's black "o" square (and the blueprint variants for the + dev/beta channels). They are now the brand-red "C" — the same glyph the + `codev --mini` splash badges with — on a white tile, generated for every size, + `.icns` and `.ico` from a single source of record + (`packages/desktop/icons/icon.svg`) by `bun ./scripts/generate-icons.ts`. The + macOS `.icns` and the Dock PNG use Apple's Big Sur grid (inset plus drop + shadow) so the icon sits correctly next to native apps; all three channels + ship the same mark for now. + +### Desktop / Web UI + +- **No wordmark on the new-session screen, and the composer is centred** — the + screen led with a large watermark of the product wordmark and anchored the prompt + box a quarter of the way down the pane to make room for it. The watermark is gone + and the prompt box now sits in the middle of the pane. + +- **The banner wordmark reads "codev"** — the large wordmark on the home screen, + the error page and shared-session pages still spelled "opencode". It is now the + lowercase codev wordmark, drawn from the same pixel glyphs as the TUI banner and + the landing page: "co" in the brand navy on light themes and the theme foreground + on dark, "dev" always in the brand red, with no drop shadow. The new-session + watermark was rebranded the same way before being dropped, so it comes back + branded if it is ever reinstated. + ## 1.18.4-8 ### Agent diff --git a/DIVERGENCES.md b/DIVERGENCES.md index 4c4b5dbd66..a5b4695817 100644 --- a/DIVERGENCES.md +++ b/DIVERGENCES.md @@ -92,6 +92,11 @@ from the squash message first, or use a plain merge. | `packages/opencode/script/postinstall.mjs` | `base` prefix `opencode-` → `codev-code-`; source/target binary names `opencode`/`opencode.exe` → `codev`/`codev.exe` (matches build.ts's `bin/codev` and publish.ts's `bin/codev.exe` stub); temp-dir prefix and failure message rebranded | Install-time platform-binary fetch must match the published package and binary names. | Keep our prefix and binary names. | | `packages/tui/src/logo.ts` | Pixel glyphs "open"+"code" → "co"+"dev"; added a `brand` export (`navy` `#19224c` / `red` `#ee0033`, the landing page's `--color-brand-navy`/`--color-brand-red`), a `wordmarkHalves` export (each row's "co" and "dev" halves with the shadow marks substituted to glyphs, so each half can take its own brand color), the composed `wordmark` (plain lines, built from `wordmarkHalves`) and `ansiWordmark` (bold, no shadow: "co" in the terminal's default foreground, "dev" in brand red truecolor) exports for CLI banners; added a `badge` export (the "c" of the wordmark — `logo.left` rows 1-3 sliced to the first glyph, with the `_` shadow-fill marks stripped to spaces so it renders as a flat outline) for the `--mini` splashes | Branded wordmark art; `ansiWordmark` feeds the CLI help banner and session epilogue. | Keep our glyphs + exports; take upstream's surrounding changes. | | `packages/tui/src/component/logo.tsx` | Renders the lowercase codev wordmark from `wordmarkHalves` — one bold `` per half, no per-char shadow cells — with "co" in `brand.navy` on light / `theme.text` on dark and "dev" in `brand.red`, instead of upstream's two-tone "open"+"code" | Banner is pixel-identical to the landing page wordmark (`codev-landing-page` `app/components/PixelWordmark.tsx`: `text-brand-navy dark:text-foreground` + `text-brand-red`), so the marketing site and the TUI read as one brand. Deliberately fixed brand colors rather than the theme's `secondary`, and no drop shadow — the landing wordmark has neither. | Keep our version wholesale. If upstream restructures the component, re-apply the two-half brand coloring rather than reverting to a theme color. | +| `packages/ui/src/components/logo.tsx`, `packages/ui/src/components/logo.css` | `Logo` renders the lowercase "codev" wordmark instead of upstream's "opencode": the `wordmarkHalves` pixel glyphs of `packages/tui/src/logo.ts` resolved to a 24x6 grid at one unit per pixel (viewBox `0 0 24 6`, was `0 0 234 42`), as two ``s — `data-slot="logo-wordmark-co"` and `…-dev` — with no drop-shadow paths. Colors come from two custom properties defined in logo.css on `[data-component="logo-wordmark"]`: `--logo-wordmark-dev` is always brand red `#ee0033`, `--logo-wordmark-co` is brand navy `#19224c` on light and `var(--icon-strong-base)` on dark. The dark swap is declared three times — under `prefers-color-scheme: dark` for surfaces that never stamp a theme, then under both `[data-color-scheme="light"]` and `[data-color-scheme="dark"]` so the app's explicit choice (`packages/ui/src/theme/context.tsx` sets it on ``) beats the OS preference in both directions. Brand hexes are literals, not theme tokens — same reasoning as the TUI component row. `Mark` and `Splash` (the square "o") are untouched. | The web/desktop banner is the same art the TUI home banner and the landing page wordmark draw, so every surface reads as one brand. | Keep our version wholesale. If upstream restructures the component, re-apply the two-half brand coloring rather than reverting to `--icon-*` tokens. | +| `packages/ui/src/v2/components/wordmark-v2.tsx` (+ fork-only `wordmark-v2.css`) | `WordmarkV2` — the watermark behind the v2 new-session composer (`packages/app/src/components/session/session-new-design-view.tsx`) — draws the codev wordmark from the same 24x6 pixel grid as `Logo` instead of upstream's 720x129 "opencode", as two ``s (`data-slot="wordmark-v2-co"` / `…-dev`). Upstream's watermark treatment is kept verbatim: the `0.6` outer opacity and the alpha mask whose gradient fades the mark out over its bottom half (converted to the new viewBox: gradient `y1` 68/129 → 3.163/6). "co" keeps `fill="currentColor"` so it still follows the caller's `text-*` class; "dev" takes brand red `#ee0033`. Per-half opacities live in the new `wordmark-v2.css`, following the one-CSS-file-per-v2-component convention: `co` is `0.112` (upstream's own `0.16 × 0.7` nesting, flattened), `dev` is boosted to `0.224` on light and `0.336` on dark via `--wordmark-v2-dev-opacity`, declared under `prefers-color-scheme` and both `[data-color-scheme]` values like components/logo.css. The boost is deliberate and measured: at upstream's weight a saturated red composites to within a few levels of the grey "co" half against the dark background and the two-tone disappears entirely, while the same value already reads pink against the light one. | The component is currently **not rendered** — the new-session view drops it (see the row below) — but it stays rebranded and exported so reinstating the watermark does not bring the upstream mark back with it. | Keep our version wholesale, including the per-theme `dev` opacity. If upstream retunes the watermark opacities, re-derive the `dev` boost from the new base rather than adopting a single flat value. | +| `packages/app/src/components/session/session-new-design-view.tsx` | Drops the `WordmarkV2` watermark from the v2 new-session screen and centres the composer. Upstream absolutely positions the content block at `top-[25.375%]` to leave room for the wordmark above it; with the mark gone that offset just parks the composer high in an empty pane, so the outer element becomes `flex items-center justify-center` over `size-full` (carrying the `px-6` inset across) and the `mt-8` spacer under the wordmark goes away. | The wordmark reads as clutter on the desktop new-session screen; without it the pane should be balanced, not top-weighted. | Keep our centred layout. If upstream restyles this view, re-apply the drop + centring rather than restoring `top-[25.375%]`. Reinstating the watermark means restoring the offset too, or it will sit centred with the composer. | +| `packages/app/src/pages/home.tsx`, `packages/app/src/pages/error.tsx`, `packages/enterprise/src/routes/share/[shareID].tsx` | `Logo` width utilities retuned for the codev wordmark's 4:1 box (upstream's is 5.57:1): home `md:w-xl` → `md:w-104`, error `w-58.5` → `w-42`, share (both call sites) `w-58.5` → `w-42` | Each value is the width at which the new wordmark renders at exactly the height the upstream one did, so no surrounding layout shifts. | Keep our widths; take upstream's surrounding changes. Recompute rather than restore if upstream changes the size. | +| `packages/desktop/icons/{dev,beta,prod}/` (all PNGs, `icon.icns`, `icon.ico`, `dock.png`), `packages/desktop/icons/README.md` | Regenerated from the fork-only `packages/desktop/icons/icon.svg` by the fork-only `packages/desktop/scripts/generate-icons.ts`: the brand-red `#ee0033` CoDev "C" — the `--mini` splash badge glyph, at the VS Code extension icon's proportions (unit = 120/1024, mark 480x600 centered) — on a white tile, replacing upstream's black/blueprint "o" mark. All three channels are byte-identical for now (chosen deliberately over upstream's per-channel treatments); the generator still writes them separately so a channel can diverge later. `icon.icns` members and `dock.png` use the macOS Big Sur grid (824/1024 inset + drop shadow); Windows, Linux and Store assets stay full-bleed. README rewritten from the Tauri-CLI + Image2Icon recipe to the generator. The stale Tauri-era `android/` and `ios/` subfolders are left untouched — nothing in the Electron build reads them. | Closes the "desktop app icons need design assets" rebrand gap: the Dock/taskbar icon was still upstream's mark. | Keep our assets and README (binary: take ours wholesale). Regenerate via the script rather than hand-editing; if upstream adds an icon size, add it to `FLAT_PNGS`/`ICONSET` and rerun. | | `packages/tui/src/theme/assets/codev.json` (new file), `packages/tui/src/theme/index.ts`, `packages/tui/src/context/theme.tsx` | Added the `codev` theme — upstream's `opencode.json` palette with `darkSecondary` → `#0891b2` (brand teal) — registered it in the theme index, and made it the default/fallback theme (`"opencode"` → `"codev"` in the store default, kv fallback, and load-failure fallbacks). `theme/assets/opencode.json` itself matches upstream again. | Brand accent lives in a fork-owned theme instead of edits to upstream's palette; the default theme carries the brand. | Keep our theme file, its index registration, and the fallback strings; when upstream changes the opencode palette, mirror it into `codev.json` (keeping our `darkSecondary`). | | `packages/opencode/src/cli/ui.ts` | `wordmark`/`logo()` render the lowercase codev wordmark via `ansiWordmark` (bold, no shadow, "dev" in brand red on TTY) | Help banner matches the TUI home banner. | Keep our wordmark + logo(); take upstream's surrounding changes. | | `packages/tui/src/util/presentation.ts` | `sessionEpilogue` returns only the one-line `Continue codev -s ` hint — dropped the `ansiWordmark` banner and the `Session ` line (and the now-unused `title` input) | The exit transcript (see the exit-transcript row) already prints the conversation on quit; the wordmark + title banner was redundant noise above the resume hint. | Keep the single Continue line; if upstream re-adds a banner/title here, keep it removed. | @@ -196,10 +201,13 @@ from the squash message first, or use a plain merge. crash dialogs in `main/windows.ts` — the fork doesn't ship the desktop app; revisit before shipping it), `favicon-v3.ico` + `social-share.png` (binary formats we can't author well — need design assets; note the docs site serves the upstream-branded `social-share.png` as its - `og:image`), desktop app icons (tracked sources in `packages/desktop/icons/{dev,beta,prod}/`, - copied to the gitignored `resources/icons` at build — need design assets), and the - `ai.opencode.desktop` appId / `opencode://` scheme (identity plumbing wired into - deep-link handling). + `og:image`), the OAuth callback pages (`packages/core/src/oauth/page.ts` — its `WORDMARK` + constant is a second copy of the opencode wordmark geometry, and the page's headline/message/ + footnote strings and `<title>` all say "OpenCode"; the file asks to be kept in sync with + `packages/ui/src/components/logo.tsx`, which has since moved to the codev wordmark, so + rebranding it means the wordmark *and* the strings together or the page reads inconsistently), + and the `ai.opencode.desktop` appId / `opencode://` scheme (identity plumbing wired into + deep-link handling). Desktop app icons are no longer a gap — see the icons row in the table. - **URLs**: links the app opens in the browser point at the fork — docs at the base in `DOCS_URL` (`packages/codev-brand/src/index.ts`, base64-encoded — the site is locale-routed and the Code docs live under `/code`; no trailing diff --git a/packages/app/src/components/session/session-new-design-view.tsx b/packages/app/src/components/session/session-new-design-view.tsx index a324c64fa6..669820febd 100644 --- a/packages/app/src/components/session/session-new-design-view.tsx +++ b/packages/app/src/components/session/session-new-design-view.tsx @@ -1,16 +1,17 @@ import type { JSX } from "solid-js" -import { WordmarkV2 } from "@opencode-ai/ui/v2/wordmark-v2" import { NEW_SESSION_CONTENT_WIDTH } from "@/pages/session/new-session-layout" +// Fork: no wordmark watermark. Upstream anchors the composer at 25.375% from the +// top to leave room for a `WordmarkV2` above it; with the mark gone that offset +// just parks the composer high in an empty pane, so the content centers instead. +// `WordmarkV2` itself is still exported (and rebranded) if it is reinstated. export function NewSessionDesignView(props: { children: JSX.Element }) { return ( - <div data-component="session-new-design" class="relative size-full overflow-hidden bg-v2-background-bg-deep "> - <div class="absolute inset-x-0 top-[25.375%] flex justify-center px-6"> - <div class={NEW_SESSION_CONTENT_WIDTH}> - <WordmarkV2 class="h-auto w-full text-v2-background-bg-inverse" /> - <div class="mt-8">{props.children}</div> - </div> - </div> + <div + data-component="session-new-design" + class="relative flex size-full items-center justify-center overflow-hidden bg-v2-background-bg-deep px-6" + > + <div class={NEW_SESSION_CONTENT_WIDTH}>{props.children}</div> </div> ) } diff --git a/packages/app/src/pages/error.tsx b/packages/app/src/pages/error.tsx index a28b9764eb..6508bcd73e 100644 --- a/packages/app/src/pages/error.tsx +++ b/packages/app/src/pages/error.tsx @@ -281,7 +281,8 @@ export const ErrorPage: Component<ErrorPageProps> = (props) => { data-tauri-drag-region > <div class="w-2/3 max-w-3xl flex flex-col items-center justify-center gap-8"> - <Logo class="w-58.5 opacity-12 shrink-0" /> + {/* Fork: `w-42` keeps the codev wordmark's 4:1 box at upstream's 42px height. */} + <Logo class="w-42 opacity-12 shrink-0" /> <div class="flex flex-col items-center gap-2 text-center"> <h1 class="text-lg font-medium text-text-strong">{language.t("error.page.title")}</h1> <p class="text-sm text-text-weak">{language.t(errorDescriptionKey(props.error))}</p> diff --git a/packages/app/src/pages/home.tsx b/packages/app/src/pages/home.tsx index 302bb4c2e9..65819b65a0 100644 --- a/packages/app/src/pages/home.tsx +++ b/packages/app/src/pages/home.tsx @@ -1749,7 +1749,9 @@ export function LegacyHome() { return ( <div class="mx-auto mt-55 w-full md:w-auto px-4"> - <Logo class="md:w-xl opacity-12" /> + {/* Fork: narrower than upstream's `md:w-xl` so the chunkier 24x6 codev + wordmark renders at the same height the 234x42 opencode one did. */} + <Logo class="md:w-104 opacity-12" /> <Button size="large" variant="ghost" diff --git a/packages/desktop/icons/README.md b/packages/desktop/icons/README.md index cf2f8e24c5..a11c05fe3b 100644 --- a/packages/desktop/icons/README.md +++ b/packages/desktop/icons/README.md @@ -1,14 +1,30 @@ -# Tauri Icons +# App Icons -Here's the process I've been using to create icons: +`icon.svg` is the source of record: the brand-red CoDev "C" mark on a white tile. +Everything under `dev/`, `beta/` and `prod/` is generated from that geometry by -- Save source image as `app-icon.png` in `packages/desktop` -- `cd` to `packages/desktop` -- Run `bun tauri icon -o src-tauri/icons/{environment}` -- Use [Image2Icon](https://img2icnsapp.com/)'s 'Big Sur Icon' preset to generate an `icon.icns` file and place it in the appropriate icons folder +```sh +cd packages/desktop +bun ./scripts/generate-icons.ts +``` -The Image2Icon step is necessary as the `icon.icns` generated by `app-icon.png` does not apply the shadow/padding expected by macOS, -so app icons appear larger than expected. +The three channels are currently identical — the same red C on white ships as the +dev, beta and prod icon. `generate-icons.ts` still writes each channel separately, +so giving a channel its own treatment later is a change to that script alone. -For unpackaged Electron on macOS, `app.dock.setIcon()` should use a PNG. Keep `dock.png` in each channel folder synced with the -extracted `icon_128x128@2x.png` from that channel's `icon.icns` so the dev Dock icon matches the packaged app inset. +The generator rasterizes, encodes the PNGs and builds the `.ico` itself, so the only +external tool it needs is `iconutil` for `.icns` — which means it has to run on macOS. +This replaces the old Tauri-CLI + [Image2Icon](https://img2icnsapp.com/) workflow; +the Big Sur inset and drop shadow that step used to add are now applied directly to +the `.icns` members and to `dock.png` (824/1024 rounded square), while the Windows, +Linux and Store assets stay full-bleed. + +`dock.png` exists because unpackaged Electron on macOS sets its Dock icon from a PNG +via `app.dock.setIcon()` — it is the macOS-inset render, so the dev Dock icon matches +the packaged app. + +`build` copies the channel folder to the gitignored `resources/icons` (see +`scripts/copy-icons.ts`), which is what `electron-builder.config.ts` points at. + +The `android/` and `ios/` folders in each channel are leftovers from the Tauri era. +Nothing in the Electron build reads them and the generator does not touch them. diff --git a/packages/desktop/icons/beta/128x128.png b/packages/desktop/icons/beta/128x128.png index 751e80f1fd..141161e078 100644 Binary files a/packages/desktop/icons/beta/128x128.png and b/packages/desktop/icons/beta/128x128.png differ diff --git a/packages/desktop/icons/beta/128x128@2x.png b/packages/desktop/icons/beta/128x128@2x.png index fe330df419..80dd4cdc42 100644 Binary files a/packages/desktop/icons/beta/128x128@2x.png and b/packages/desktop/icons/beta/128x128@2x.png differ diff --git a/packages/desktop/icons/beta/32x32.png b/packages/desktop/icons/beta/32x32.png index 2703048eed..45cd48bb51 100644 Binary files a/packages/desktop/icons/beta/32x32.png and b/packages/desktop/icons/beta/32x32.png differ diff --git a/packages/desktop/icons/beta/64x64.png b/packages/desktop/icons/beta/64x64.png index ecd7fe3142..55bc7b0b34 100644 Binary files a/packages/desktop/icons/beta/64x64.png and b/packages/desktop/icons/beta/64x64.png differ diff --git a/packages/desktop/icons/beta/Square107x107Logo.png b/packages/desktop/icons/beta/Square107x107Logo.png index e6ea73f4da..0054fc0b72 100644 Binary files a/packages/desktop/icons/beta/Square107x107Logo.png and b/packages/desktop/icons/beta/Square107x107Logo.png differ diff --git a/packages/desktop/icons/beta/Square142x142Logo.png b/packages/desktop/icons/beta/Square142x142Logo.png index 74ae729c42..6003015b59 100644 Binary files a/packages/desktop/icons/beta/Square142x142Logo.png and b/packages/desktop/icons/beta/Square142x142Logo.png differ diff --git a/packages/desktop/icons/beta/Square150x150Logo.png b/packages/desktop/icons/beta/Square150x150Logo.png index 0b109b8f4a..15a87a9ada 100644 Binary files a/packages/desktop/icons/beta/Square150x150Logo.png and b/packages/desktop/icons/beta/Square150x150Logo.png differ diff --git a/packages/desktop/icons/beta/Square284x284Logo.png b/packages/desktop/icons/beta/Square284x284Logo.png index 0261ded42c..eb3392e3bd 100644 Binary files a/packages/desktop/icons/beta/Square284x284Logo.png and b/packages/desktop/icons/beta/Square284x284Logo.png differ diff --git a/packages/desktop/icons/beta/Square30x30Logo.png b/packages/desktop/icons/beta/Square30x30Logo.png index 34158f10a4..31d391bcc3 100644 Binary files a/packages/desktop/icons/beta/Square30x30Logo.png and b/packages/desktop/icons/beta/Square30x30Logo.png differ diff --git a/packages/desktop/icons/beta/Square310x310Logo.png b/packages/desktop/icons/beta/Square310x310Logo.png index f18bfada4c..de8f332e41 100644 Binary files a/packages/desktop/icons/beta/Square310x310Logo.png and b/packages/desktop/icons/beta/Square310x310Logo.png differ diff --git a/packages/desktop/icons/beta/Square44x44Logo.png b/packages/desktop/icons/beta/Square44x44Logo.png index 6d1cc06c08..4b335b6e57 100644 Binary files a/packages/desktop/icons/beta/Square44x44Logo.png and b/packages/desktop/icons/beta/Square44x44Logo.png differ diff --git a/packages/desktop/icons/beta/Square71x71Logo.png b/packages/desktop/icons/beta/Square71x71Logo.png index a26084dc2f..f0dc8cebe9 100644 Binary files a/packages/desktop/icons/beta/Square71x71Logo.png and b/packages/desktop/icons/beta/Square71x71Logo.png differ diff --git a/packages/desktop/icons/beta/Square89x89Logo.png b/packages/desktop/icons/beta/Square89x89Logo.png index 58b0eb6053..4cce23ba5e 100644 Binary files a/packages/desktop/icons/beta/Square89x89Logo.png and b/packages/desktop/icons/beta/Square89x89Logo.png differ diff --git a/packages/desktop/icons/beta/StoreLogo.png b/packages/desktop/icons/beta/StoreLogo.png index 648fd2114d..c7d7f9f62e 100644 Binary files a/packages/desktop/icons/beta/StoreLogo.png and b/packages/desktop/icons/beta/StoreLogo.png differ diff --git a/packages/desktop/icons/beta/dock.png b/packages/desktop/icons/beta/dock.png index f274ef6459..8ba09fba58 100644 Binary files a/packages/desktop/icons/beta/dock.png and b/packages/desktop/icons/beta/dock.png differ diff --git a/packages/desktop/icons/beta/icon.icns b/packages/desktop/icons/beta/icon.icns index f98de5da88..c5454fbeb1 100644 Binary files a/packages/desktop/icons/beta/icon.icns and b/packages/desktop/icons/beta/icon.icns differ diff --git a/packages/desktop/icons/beta/icon.ico b/packages/desktop/icons/beta/icon.ico index df8588c8e4..07e53246bc 100644 Binary files a/packages/desktop/icons/beta/icon.ico and b/packages/desktop/icons/beta/icon.ico differ diff --git a/packages/desktop/icons/beta/icon.png b/packages/desktop/icons/beta/icon.png index 5313049562..61df476419 100644 Binary files a/packages/desktop/icons/beta/icon.png and b/packages/desktop/icons/beta/icon.png differ diff --git a/packages/desktop/icons/dev/128x128.png b/packages/desktop/icons/dev/128x128.png index d7fc4db149..141161e078 100644 Binary files a/packages/desktop/icons/dev/128x128.png and b/packages/desktop/icons/dev/128x128.png differ diff --git a/packages/desktop/icons/dev/128x128@2x.png b/packages/desktop/icons/dev/128x128@2x.png index 5918823064..80dd4cdc42 100644 Binary files a/packages/desktop/icons/dev/128x128@2x.png and b/packages/desktop/icons/dev/128x128@2x.png differ diff --git a/packages/desktop/icons/dev/32x32.png b/packages/desktop/icons/dev/32x32.png index 53925cc4f5..45cd48bb51 100644 Binary files a/packages/desktop/icons/dev/32x32.png and b/packages/desktop/icons/dev/32x32.png differ diff --git a/packages/desktop/icons/dev/64x64.png b/packages/desktop/icons/dev/64x64.png index a88ef15c64..55bc7b0b34 100644 Binary files a/packages/desktop/icons/dev/64x64.png and b/packages/desktop/icons/dev/64x64.png differ diff --git a/packages/desktop/icons/dev/Square107x107Logo.png b/packages/desktop/icons/dev/Square107x107Logo.png index 0de29ec828..0054fc0b72 100644 Binary files a/packages/desktop/icons/dev/Square107x107Logo.png and b/packages/desktop/icons/dev/Square107x107Logo.png differ diff --git a/packages/desktop/icons/dev/Square142x142Logo.png b/packages/desktop/icons/dev/Square142x142Logo.png index af62e8e1e9..6003015b59 100644 Binary files a/packages/desktop/icons/dev/Square142x142Logo.png and b/packages/desktop/icons/dev/Square142x142Logo.png differ diff --git a/packages/desktop/icons/dev/Square150x150Logo.png b/packages/desktop/icons/dev/Square150x150Logo.png index 2b19dc39cc..15a87a9ada 100644 Binary files a/packages/desktop/icons/dev/Square150x150Logo.png and b/packages/desktop/icons/dev/Square150x150Logo.png differ diff --git a/packages/desktop/icons/dev/Square284x284Logo.png b/packages/desktop/icons/dev/Square284x284Logo.png index eda6d9901f..eb3392e3bd 100644 Binary files a/packages/desktop/icons/dev/Square284x284Logo.png and b/packages/desktop/icons/dev/Square284x284Logo.png differ diff --git a/packages/desktop/icons/dev/Square30x30Logo.png b/packages/desktop/icons/dev/Square30x30Logo.png index dad821ba84..31d391bcc3 100644 Binary files a/packages/desktop/icons/dev/Square30x30Logo.png and b/packages/desktop/icons/dev/Square30x30Logo.png differ diff --git a/packages/desktop/icons/dev/Square310x310Logo.png b/packages/desktop/icons/dev/Square310x310Logo.png index 555b3b1979..de8f332e41 100644 Binary files a/packages/desktop/icons/dev/Square310x310Logo.png and b/packages/desktop/icons/dev/Square310x310Logo.png differ diff --git a/packages/desktop/icons/dev/Square44x44Logo.png b/packages/desktop/icons/dev/Square44x44Logo.png index 9f8ad001f7..4b335b6e57 100644 Binary files a/packages/desktop/icons/dev/Square44x44Logo.png and b/packages/desktop/icons/dev/Square44x44Logo.png differ diff --git a/packages/desktop/icons/dev/Square71x71Logo.png b/packages/desktop/icons/dev/Square71x71Logo.png index 43feb78488..f0dc8cebe9 100644 Binary files a/packages/desktop/icons/dev/Square71x71Logo.png and b/packages/desktop/icons/dev/Square71x71Logo.png differ diff --git a/packages/desktop/icons/dev/Square89x89Logo.png b/packages/desktop/icons/dev/Square89x89Logo.png index 628cc597f0..4cce23ba5e 100644 Binary files a/packages/desktop/icons/dev/Square89x89Logo.png and b/packages/desktop/icons/dev/Square89x89Logo.png differ diff --git a/packages/desktop/icons/dev/StoreLogo.png b/packages/desktop/icons/dev/StoreLogo.png index 8d3aa53cff..c7d7f9f62e 100644 Binary files a/packages/desktop/icons/dev/StoreLogo.png and b/packages/desktop/icons/dev/StoreLogo.png differ diff --git a/packages/desktop/icons/dev/dock.png b/packages/desktop/icons/dev/dock.png index 4953d5531e..8ba09fba58 100644 Binary files a/packages/desktop/icons/dev/dock.png and b/packages/desktop/icons/dev/dock.png differ diff --git a/packages/desktop/icons/dev/icon.icns b/packages/desktop/icons/dev/icon.icns index d73a94904a..c5454fbeb1 100644 Binary files a/packages/desktop/icons/dev/icon.icns and b/packages/desktop/icons/dev/icon.icns differ diff --git a/packages/desktop/icons/dev/icon.ico b/packages/desktop/icons/dev/icon.ico index bec385d9aa..07e53246bc 100644 Binary files a/packages/desktop/icons/dev/icon.ico and b/packages/desktop/icons/dev/icon.ico differ diff --git a/packages/desktop/icons/dev/icon.png b/packages/desktop/icons/dev/icon.png index 6de37ea294..61df476419 100644 Binary files a/packages/desktop/icons/dev/icon.png and b/packages/desktop/icons/dev/icon.png differ diff --git a/packages/desktop/icons/icon.svg b/packages/desktop/icons/icon.svg new file mode 100644 index 0000000000..0ed7187a70 --- /dev/null +++ b/packages/desktop/icons/icon.svg @@ -0,0 +1,22 @@ +<!-- Source of record for every generated icon in icons/{dev,beta,prod}/. --> +<!-- Regenerate with `bun ./scripts/generate-icons.ts` from packages/desktop. --> +<!-- + The CoDev "C" mark in brand red #ee0033 (the "dev" half of the wordmark, see + packages/tui/src/logo.ts) on a white tile. The glyph is the block art + `█▀▀▀ / █ / ▀▀▀▀` resolved to a 4x5 pixel grid with a one-unit stroke, drawn + at the same proportions as the VS Code extension icon (sdks/vscode/images/ + icon.svg): unit = 120 of 1024, so the mark is 480x600 centered on the tile. + + This is the flat, full-bleed variant used for Windows, Linux and the Store + tiles. The generator renders the same geometry inset to 824/1024 with a drop + shadow for icon.icns and dock.png, which is the macOS Big Sur icon grid. +--> +<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg"> + <rect width="1024" height="1024" rx="229" fill="#ffffff"/> + <!-- top bar --> + <rect x="272" y="212" width="480" height="120" fill="#ee0033"/> + <!-- left column --> + <rect x="272" y="212" width="120" height="600" fill="#ee0033"/> + <!-- bottom bar --> + <rect x="272" y="692" width="480" height="120" fill="#ee0033"/> +</svg> diff --git a/packages/desktop/icons/prod/128x128.png b/packages/desktop/icons/prod/128x128.png index caf7b02eb3..141161e078 100644 Binary files a/packages/desktop/icons/prod/128x128.png and b/packages/desktop/icons/prod/128x128.png differ diff --git a/packages/desktop/icons/prod/128x128@2x.png b/packages/desktop/icons/prod/128x128@2x.png index 47fe4c61ea..80dd4cdc42 100644 Binary files a/packages/desktop/icons/prod/128x128@2x.png and b/packages/desktop/icons/prod/128x128@2x.png differ diff --git a/packages/desktop/icons/prod/32x32.png b/packages/desktop/icons/prod/32x32.png index 5868bcc933..45cd48bb51 100644 Binary files a/packages/desktop/icons/prod/32x32.png and b/packages/desktop/icons/prod/32x32.png differ diff --git a/packages/desktop/icons/prod/64x64.png b/packages/desktop/icons/prod/64x64.png index 1ed7425d85..55bc7b0b34 100644 Binary files a/packages/desktop/icons/prod/64x64.png and b/packages/desktop/icons/prod/64x64.png differ diff --git a/packages/desktop/icons/prod/Square107x107Logo.png b/packages/desktop/icons/prod/Square107x107Logo.png index 1db249bf72..0054fc0b72 100644 Binary files a/packages/desktop/icons/prod/Square107x107Logo.png and b/packages/desktop/icons/prod/Square107x107Logo.png differ diff --git a/packages/desktop/icons/prod/Square142x142Logo.png b/packages/desktop/icons/prod/Square142x142Logo.png index 1961c34081..6003015b59 100644 Binary files a/packages/desktop/icons/prod/Square142x142Logo.png and b/packages/desktop/icons/prod/Square142x142Logo.png differ diff --git a/packages/desktop/icons/prod/Square150x150Logo.png b/packages/desktop/icons/prod/Square150x150Logo.png index abc507347e..15a87a9ada 100644 Binary files a/packages/desktop/icons/prod/Square150x150Logo.png and b/packages/desktop/icons/prod/Square150x150Logo.png differ diff --git a/packages/desktop/icons/prod/Square284x284Logo.png b/packages/desktop/icons/prod/Square284x284Logo.png index 51e2a1b9fe..eb3392e3bd 100644 Binary files a/packages/desktop/icons/prod/Square284x284Logo.png and b/packages/desktop/icons/prod/Square284x284Logo.png differ diff --git a/packages/desktop/icons/prod/Square30x30Logo.png b/packages/desktop/icons/prod/Square30x30Logo.png index 066a1fd0c8..31d391bcc3 100644 Binary files a/packages/desktop/icons/prod/Square30x30Logo.png and b/packages/desktop/icons/prod/Square30x30Logo.png differ diff --git a/packages/desktop/icons/prod/Square310x310Logo.png b/packages/desktop/icons/prod/Square310x310Logo.png index 2a85c8e952..de8f332e41 100644 Binary files a/packages/desktop/icons/prod/Square310x310Logo.png and b/packages/desktop/icons/prod/Square310x310Logo.png differ diff --git a/packages/desktop/icons/prod/Square44x44Logo.png b/packages/desktop/icons/prod/Square44x44Logo.png index c855b80632..4b335b6e57 100644 Binary files a/packages/desktop/icons/prod/Square44x44Logo.png and b/packages/desktop/icons/prod/Square44x44Logo.png differ diff --git a/packages/desktop/icons/prod/Square71x71Logo.png b/packages/desktop/icons/prod/Square71x71Logo.png index c8168f7111..f0dc8cebe9 100644 Binary files a/packages/desktop/icons/prod/Square71x71Logo.png and b/packages/desktop/icons/prod/Square71x71Logo.png differ diff --git a/packages/desktop/icons/prod/Square89x89Logo.png b/packages/desktop/icons/prod/Square89x89Logo.png index 19ec1777de..4cce23ba5e 100644 Binary files a/packages/desktop/icons/prod/Square89x89Logo.png and b/packages/desktop/icons/prod/Square89x89Logo.png differ diff --git a/packages/desktop/icons/prod/StoreLogo.png b/packages/desktop/icons/prod/StoreLogo.png index 3fd053d349..c7d7f9f62e 100644 Binary files a/packages/desktop/icons/prod/StoreLogo.png and b/packages/desktop/icons/prod/StoreLogo.png differ diff --git a/packages/desktop/icons/prod/dock.png b/packages/desktop/icons/prod/dock.png index f2ab694e97..8ba09fba58 100644 Binary files a/packages/desktop/icons/prod/dock.png and b/packages/desktop/icons/prod/dock.png differ diff --git a/packages/desktop/icons/prod/icon.icns b/packages/desktop/icons/prod/icon.icns index be910ad5f9..c5454fbeb1 100644 Binary files a/packages/desktop/icons/prod/icon.icns and b/packages/desktop/icons/prod/icon.icns differ diff --git a/packages/desktop/icons/prod/icon.ico b/packages/desktop/icons/prod/icon.ico index ff88d21e4c..07e53246bc 100644 Binary files a/packages/desktop/icons/prod/icon.ico and b/packages/desktop/icons/prod/icon.ico differ diff --git a/packages/desktop/icons/prod/icon.png b/packages/desktop/icons/prod/icon.png index 0ecbb6d5f8..61df476419 100644 Binary files a/packages/desktop/icons/prod/icon.png and b/packages/desktop/icons/prod/icon.png differ diff --git a/packages/desktop/scripts/generate-icons.ts b/packages/desktop/scripts/generate-icons.ts new file mode 100644 index 0000000000..ba4183b7a8 --- /dev/null +++ b/packages/desktop/scripts/generate-icons.ts @@ -0,0 +1,261 @@ +// Fork: regenerates every app icon in icons/{dev,beta,prod}/ from the geometry +// in icons/icon.svg — the brand-red CoDev "C" on a white tile. +// +// Replaces the manual Tauri-CLI + Image2Icon workflow the old icons/README.md +// described. Everything here is self-contained (raw rasterizer, PNG encoder, ICO +// container) except `iconutil`, which is macOS-only and only needed for .icns. +// +// Run from packages/desktop: `bun ./scripts/generate-icons.ts` +import { deflateSync } from "node:zlib" +import { mkdir, rm } from "node:fs/promises" +import { $ } from "bun" + +const RED = [238, 0, 51] as const +const WHITE = [255, 255, 255] as const + +// Fractions of the tile, taken from icons/icon.svg (and matching +// sdks/vscode/images/icon.svg): a 4x5 pixel grid with a one-unit stroke. +const UNIT = 120 / 1024 +const CORNER = 229 / 1024 +// macOS Big Sur draws app icons in an 824/1024 rounded square with a drop +// shadow; anything full-bleed reads as oversized next to native icons. +const MACOS_TILE = 824 / 1024 + +type Style = "flat" | "macos" + +// Coverage of the rounded rect and of the "C" for one pixel, supersampled. +function coverage(size: number, style: Style) { + const samples = size <= 256 ? 8 : 4 + const tile = size * (style === "macos" ? MACOS_TILE : 1) + const origin = (size - tile) / 2 + const radius = tile * CORNER + const unit = tile * UNIT + const mark = { + x: origin + (tile - unit * 4) / 2, + y: origin + (tile - unit * 5) / 2, + } + + // The glyph is three axis-aligned bars: top, left column, bottom. + const bars = [ + { x: mark.x, y: mark.y, w: unit * 4, h: unit }, + { x: mark.x, y: mark.y, w: unit, h: unit * 5 }, + { x: mark.x, y: mark.y + unit * 4, w: unit * 4, h: unit }, + ] + + const inTile = (x: number, y: number) => { + if (x < origin || y < origin || x > origin + tile || y > origin + tile) return false + // Only the four corner boxes need the radius test; the cross in the middle + // of the square is always inside. + const dx = Math.max(origin + radius - x, x - (origin + tile - radius), 0) + const dy = Math.max(origin + radius - y, y - (origin + tile - radius), 0) + return dx * dx + dy * dy <= radius * radius + } + + const inMark = (x: number, y: number) => + bars.some((bar) => x >= bar.x && x < bar.x + bar.w && y >= bar.y && y < bar.y + bar.h) + + const tileAlpha = new Float32Array(size * size) + const markAlpha = new Float32Array(size * size) + const step = 1 / samples + const total = samples * samples + + for (let py = 0; py < size; py += 1) { + for (let px = 0; px < size; px += 1) { + let hitTile = 0 + let hitMark = 0 + for (let sy = 0; sy < samples; sy += 1) { + for (let sx = 0; sx < samples; sx += 1) { + const x = px + (sx + 0.5) * step + const y = py + (sy + 0.5) * step + if (inTile(x, y)) hitTile += 1 + if (inMark(x, y)) hitMark += 1 + } + } + tileAlpha[py * size + px] = hitTile / total + markAlpha[py * size + px] = hitMark / total + } + } + + return { tileAlpha, markAlpha } +} + +// Three box-blur passes approximate a gaussian closely enough for a drop shadow. +function blur(source: Float32Array, size: number, radius: number) { + const span = Math.max(1, Math.round(radius)) + const pass = (input: Float32Array, horizontal: boolean) => { + const out = new Float32Array(input.length) + for (let a = 0; a < size; a += 1) { + for (let b = 0; b < size; b += 1) { + let sum = 0 + let count = 0 + for (let d = -span; d <= span; d += 1) { + const at = b + d + if (at < 0 || at >= size) continue + sum += horizontal ? input[a * size + at] : input[at * size + a] + count += 1 + } + const value = sum / count + if (horizontal) out[a * size + b] = value + else out[b * size + a] = value + } + } + return out + } + + return [0, 1, 2].reduce((acc) => pass(pass(acc, true), false), source) +} + +function render(size: number, style: Style) { + const { tileAlpha, markAlpha } = coverage(size, style) + const shadow = + style === "macos" + ? blur( + // Offset down by 2% of the canvas so the tile appears to sit on the surface. + Float32Array.from({ length: size * size }, (_, index) => { + const y = Math.floor(index / size) - Math.round(size * 0.02) + return y < 0 ? 0 : tileAlpha[y * size + (index % size)] + }), + size, + size * 0.018, + ) + : undefined + + const out = new Uint8Array(size * size * 4) + for (let index = 0; index < size * size; index += 1) { + // Bottom-up "over" compositing in straight alpha: shadow, tile, then mark. + const layers: Array<{ rgb: readonly number[]; a: number }> = [ + { rgb: WHITE, a: tileAlpha[index] }, + { rgb: RED, a: markAlpha[index] * tileAlpha[index] }, + ] + if (shadow) layers.unshift({ rgb: [0, 0, 0], a: shadow[index] * 0.3 }) + + const pixel = layers.reduce( + (dst, src) => { + const a = src.a + dst.a * (1 - src.a) + if (a === 0) return { rgb: [0, 0, 0], a: 0 } + return { + rgb: [0, 1, 2].map((c) => (src.rgb[c] * src.a + dst.rgb[c] * dst.a * (1 - src.a)) / a), + a, + } + }, + { rgb: [0, 0, 0] as readonly number[], a: 0 }, + ) + + out[index * 4] = Math.round(pixel.rgb[0]) + out[index * 4 + 1] = Math.round(pixel.rgb[1]) + out[index * 4 + 2] = Math.round(pixel.rgb[2]) + out[index * 4 + 3] = Math.round(pixel.a * 255) + } + + return out +} + +const CRC_TABLE = Int32Array.from({ length: 256 }, (_, index) => + Array.from({ length: 8 }).reduce((c) => (c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1), index), +) + +function crc32(bytes: Uint8Array) { + return (bytes.reduce((c, byte) => CRC_TABLE[(c ^ byte) & 0xff] ^ (c >>> 8), 0xffffffff) ^ 0xffffffff) >>> 0 +} + +function chunk(type: string, data: Uint8Array) { + const body = new Uint8Array(type.length + data.length) + body.set(Uint8Array.from(type, (char) => char.charCodeAt(0))) + body.set(data, type.length) + const out = new Uint8Array(body.length + 8) + const view = new DataView(out.buffer) + view.setUint32(0, data.length) + out.set(body, 4) + view.setUint32(out.length - 4, crc32(body)) + return out +} + +function png(rgba: Uint8Array, size: number) { + const header = new Uint8Array(13) + const view = new DataView(header.buffer) + view.setUint32(0, size) + view.setUint32(4, size) + header.set([8, 6, 0, 0, 0], 8) + + // Filter type 0 (none) on every scanline; the art is flat enough that + // predictors buy little and this keeps the encoder trivial. + const raw = new Uint8Array(size * (size * 4 + 1)) + for (let y = 0; y < size; y += 1) { + raw.set(rgba.subarray(y * size * 4, (y + 1) * size * 4), y * (size * 4 + 1) + 1) + } + + return Buffer.concat([ + Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]), + chunk("IHDR", header), + chunk("IDAT", deflateSync(raw, { level: 9 })), + chunk("IEND", new Uint8Array(0)), + ]) +} + +// PNG-compressed ICO entries, supported by Windows Vista and later. +function ico(entries: Array<{ size: number; data: Buffer }>) { + const dir = Buffer.alloc(6 + entries.length * 16) + dir.writeUInt16LE(0, 0) + dir.writeUInt16LE(1, 2) + dir.writeUInt16LE(entries.length, 4) + + entries.reduce((offset, entry, index) => { + const at = 6 + index * 16 + dir.writeUInt8(entry.size >= 256 ? 0 : entry.size, at) + dir.writeUInt8(entry.size >= 256 ? 0 : entry.size, at + 1) + dir.writeUInt8(0, at + 2) + dir.writeUInt8(0, at + 3) + dir.writeUInt16LE(1, at + 4) + dir.writeUInt16LE(32, at + 6) + dir.writeUInt32LE(entry.data.length, at + 8) + dir.writeUInt32LE(offset, at + 12) + return offset + entry.data.length + }, dir.length) + + return Buffer.concat([dir, ...entries.map((entry) => entry.data)]) +} + +const FLAT_PNGS = [ + ["32x32.png", 32], + ["64x64.png", 64], + ["128x128.png", 128], + ["128x128@2x.png", 256], + ["icon.png", 512], + ["StoreLogo.png", 50], + ...[30, 44, 71, 89, 107, 142, 150, 284, 310].map((n) => [`Square${n}x${n}Logo.png`, n] as const), +] as const + +const ICO_SIZES = [16, 24, 32, 48, 64, 128, 256] + +// Apple's required iconset members: {logical size, @2x?}. +const ICONSET = [16, 32, 128, 256, 512].flatMap((n) => [ + { name: `icon_${n}x${n}.png`, size: n }, + { name: `icon_${n}x${n}@2x.png`, size: n * 2 }, +]) + +// One render per distinct size, reused across every channel. +const flat = new Map( + [...new Set([...FLAT_PNGS.map(([, size]) => size), ...ICO_SIZES])].map((size) => [ + size, + png(render(size, "flat"), size), + ]), +) +const macos = new Map( + [...new Set([256, ...ICONSET.map((entry) => entry.size)])].map((size) => [size, png(render(size, "macos"), size)]), +) + +for (const channel of ["dev", "beta", "prod"]) { + const dir = `icons/${channel}` + await Promise.all(FLAT_PNGS.map(([name, size]) => Bun.write(`${dir}/${name}`, flat.get(size)!))) + await Bun.write(`${dir}/icon.ico`, ico(ICO_SIZES.map((size) => ({ size, data: flat.get(size)! })))) + await Bun.write(`${dir}/dock.png`, macos.get(256)!) + + const iconset = `${dir}/icon.iconset` + await rm(iconset, { recursive: true, force: true }) + await mkdir(iconset, { recursive: true }) + await Promise.all(ICONSET.map((entry) => Bun.write(`${iconset}/${entry.name}`, macos.get(entry.size)!))) + await $`iconutil -c icns -o ${dir}/icon.icns ${iconset}` + await rm(iconset, { recursive: true, force: true }) + + console.log(`Generated ${channel} icons in ${dir}`) +} diff --git a/packages/enterprise/src/routes/share/[shareID].tsx b/packages/enterprise/src/routes/share/[shareID].tsx index 8c8ac59f7a..6267f6f098 100644 --- a/packages/enterprise/src/routes/share/[shareID].tsx +++ b/packages/enterprise/src/routes/share/[shareID].tsx @@ -263,7 +263,7 @@ export default function () { </For> </div> <div class="px-4 flex items-center justify-center pt-20 pb-8 shrink-0"> - <Logo class="w-58.5 opacity-12" /> + <Logo class="w-42 opacity-12" /> </div> </div> ) @@ -341,7 +341,7 @@ export default function () { }} > <div classList={{ "w-full flex items-center justify-center pb-8 shrink-0": true }}> - <Logo class="w-58.5 opacity-12" /> + <Logo class="w-42 opacity-12" /> </div> </SessionTurn> </div> diff --git a/packages/ui/src/components/logo.css b/packages/ui/src/components/logo.css index a909782b77..d948f34e25 100644 --- a/packages/ui/src/components/logo.css +++ b/packages/ui/src/components/logo.css @@ -2,3 +2,30 @@ width: 16px; aspect-ratio: 4/5; } + +/* Fork: the codev wordmark's two halves. Literal hex rather than theme tokens — + these are the landing page's brand colors (codev-landing-page app/globals.css + --color-brand-navy / --color-brand-red, mirrored in packages/tui/src/logo.ts + `brand`), and the wordmark is supposed to read the same everywhere. Only the + "co" half swaps, to the theme foreground on dark where navy goes muddy. */ +[data-component="logo-wordmark"] { + --logo-wordmark-co: #19224c; + --logo-wordmark-dev: #ee0033; +} + +@media (prefers-color-scheme: dark) { + [data-component="logo-wordmark"] { + --logo-wordmark-co: var(--icon-strong-base); + } +} + +/* An explicit theme choice beats the OS preference: the app always stamps + data-color-scheme on <html> (packages/ui/src/theme/context.tsx), so both + directions need an override or a light theme on a dark OS renders navy-on-dark. */ +[data-color-scheme="light"] [data-component="logo-wordmark"] { + --logo-wordmark-co: #19224c; +} + +[data-color-scheme="dark"] [data-component="logo-wordmark"] { + --logo-wordmark-co: var(--icon-strong-base); +} diff --git a/packages/ui/src/components/logo.tsx b/packages/ui/src/components/logo.tsx index 35b4e44427..0fe06f6f97 100644 --- a/packages/ui/src/components/logo.tsx +++ b/packages/ui/src/components/logo.tsx @@ -31,32 +31,31 @@ export const Splash = (props: Pick<ComponentProps<"svg">, "ref" | "class">) => { ) } +// Fork: lowercase "codev" wordmark instead of upstream's "opencode". The paths +// are the pixel glyphs of `packages/tui/src/logo.ts` (`wordmarkHalves`) resolved +// to a 24x6 grid at one unit per pixel, so the banner is the same art the TUI +// and the landing page draw. Colors come from logo.css: "co" is brand navy on +// light and the theme foreground on dark, "dev" is always brand red, and neither +// half takes upstream's drop shadow. export const Logo = (props: { class?: string }) => { return ( <svg + data-component="logo-wordmark" xmlns="http://www.w3.org/2000/svg" - viewBox="0 0 234 42" + viewBox="0 0 24 6" fill="none" classList={{ [props.class ?? ""]: !!props.class }} > - <g> - <path d="M18 30H6V18H18V30Z" fill="var(--icon-weak-base)" /> - <path d="M18 12H6V30H18V12ZM24 36H0V6H24V36Z" fill="var(--icon-base)" /> - <path d="M48 30H36V18H48V30Z" fill="var(--icon-weak-base)" /> - <path d="M36 30H48V12H36V30ZM54 36H36V42H30V6H54V36Z" fill="var(--icon-base)" /> - <path d="M84 24V30H66V24H84Z" fill="var(--icon-weak-base)" /> - <path d="M84 24H66V30H84V36H60V6H84V24ZM66 18H78V12H66V18Z" fill="var(--icon-base)" /> - <path d="M108 36H96V18H108V36Z" fill="var(--icon-weak-base)" /> - <path d="M108 12H96V36H90V6H108V12ZM114 36H108V12H114V36Z" fill="var(--icon-base)" /> - <path d="M144 30H126V18H144V30Z" fill="var(--icon-weak-base)" /> - <path d="M144 12H126V30H144V36H120V6H144V12Z" fill="var(--icon-strong-base)" /> - <path d="M168 30H156V18H168V30Z" fill="var(--icon-weak-base)" /> - <path d="M168 12H156V30H168V12ZM174 36H150V6H174V36Z" fill="var(--icon-strong-base)" /> - <path d="M198 30H186V18H198V30Z" fill="var(--icon-weak-base)" /> - <path d="M198 12H186V30H198V12ZM204 36H180V6H198V0H204V36Z" fill="var(--icon-strong-base)" /> - <path d="M234 24V30H216V24H234Z" fill="var(--icon-weak-base)" /> - <path d="M216 12V18H228V12H216ZM234 24H216V30H234V36H210V6H234V24Z" fill="var(--icon-strong-base)" /> - </g> + <path + data-slot="logo-wordmark-co" + d="M0 1h4v1h-4zM5 1h4v1h-4zM0 2h1v3h-1zM5 2h1v3h-1zM8 2h1v3h-1zM0 5h4v1h-4zM5 5h4v1h-4z" + fill="var(--logo-wordmark-co)" + /> + <path + data-slot="logo-wordmark-dev" + d="M13 0h1v1h-1zM10 1h4v1h-4zM15 1h4v1h-4zM20 1h1v4h-1zM23 1h1v4h-1zM10 2h1v3h-1zM13 2h1v3h-1zM15 2h1v1h-1zM18 2h1v1h-1zM15 3h4v1h-4zM15 4h1v1h-1zM10 5h4v1h-4zM15 5h4v1h-4zM21 5h2v1h-2z" + fill="var(--logo-wordmark-dev)" + /> </svg> ) } diff --git a/packages/ui/src/v2/components/wordmark-v2.css b/packages/ui/src/v2/components/wordmark-v2.css new file mode 100644 index 0000000000..74beaab6d8 --- /dev/null +++ b/packages/ui/src/v2/components/wordmark-v2.css @@ -0,0 +1,31 @@ +/* Fork: per-half opacities for the codev wordmark. 0.112 is upstream's own + watermark weight (it nested opacity 0.16 over 0.7 on every glyph path). + + The brand red needs more than that to read AS red at this weight: against the + dark background it lands within a few levels of the grey "co" half and the + two-tone disappears, while against the light one the same value already reads + pink. So "dev" is boosted, and by different amounts per theme. */ +[data-slot="wordmark-v2-co"] { + opacity: 0.112; +} + +[data-slot="wordmark-v2-dev"] { + opacity: var(--wordmark-v2-dev-opacity, 0.224); +} + +@media (prefers-color-scheme: dark) { + [data-component="wordmark-v2"] { + --wordmark-v2-dev-opacity: 0.336; + } +} + +/* An explicit theme choice beats the OS preference: the app always stamps + data-color-scheme on <html> (packages/ui/src/theme/context.tsx), so both + directions need an override. Same pattern as components/logo.css. */ +[data-color-scheme="light"] [data-component="wordmark-v2"] { + --wordmark-v2-dev-opacity: 0.224; +} + +[data-color-scheme="dark"] [data-component="wordmark-v2"] { + --wordmark-v2-dev-opacity: 0.336; +} diff --git a/packages/ui/src/v2/components/wordmark-v2.tsx b/packages/ui/src/v2/components/wordmark-v2.tsx index 1abe1cd828..94db74135b 100644 --- a/packages/ui/src/v2/components/wordmark-v2.tsx +++ b/packages/ui/src/v2/components/wordmark-v2.tsx @@ -1,67 +1,45 @@ import { createUniqueId, type ComponentProps } from "solid-js" +import "./wordmark-v2.css" +// Fork: lowercase "codev" wordmark instead of upstream's "opencode". The paths +// are the pixel glyphs of `packages/tui/src/logo.ts` (`wordmarkHalves`) resolved +// to a 24x6 grid at one unit per pixel, the same art `Logo` draws. Upstream's +// watermark treatment is kept: the 0.6 outer opacity and the gradient mask that +// fades the wordmark out into the background over its bottom half. "co" keeps +// `currentColor` so it still follows whatever the caller sets (the new-session +// view passes the inverse background); only "dev" takes a fixed brand color, in +// logo.css's palette. export function WordmarkV2(props: Pick<ComponentProps<"svg">, "class">) { const mask = createUniqueId() const maskGradient = createUniqueId() return ( <svg + data-component="wordmark-v2" xmlns="http://www.w3.org/2000/svg" - viewBox="0 0 720 129" + viewBox="0 0 24 6" fill="none" classList={{ [props.class ?? ""]: !!props.class }} > <g opacity="0.6"> <g mask={`url(#${mask})`}> - <g opacity="0.16"> - <path - opacity="0.7" - d="M55.3846 36.4286H18.4615V91.7143H55.3846V36.4286ZM73.8462 110.143H0V18H73.8462V110.143Z" - fill="currentColor" - /> - <path - opacity="0.7" - d="M110.462 91.7143H147.385V36.4286H110.462V91.7143ZM165.846 110.143H110.462V128.571H92V18H165.846V110.143Z" - fill="currentColor" - /> - <path - opacity="0.7" - d="M258.846 73.2857H203.462V91.7143H258.846V110.143H185V18H258.846V73.2857ZM203.462 54.8571H240.385V36.4286H203.462V54.8571Z" - fill="currentColor" - /> - <path - opacity="0.7" - d="M332.385 36.4286H295.462V110.143H277V18H332.385V36.4286ZM350.846 110.143H332.385V36.4286H350.846V110.143Z" - fill="currentColor" - /> - <path - opacity="0.7" - d="M442.846 36.4286H387.462V91.7143H442.846V110.143H369V18H442.846V36.4286Z" - fill="currentColor" - /> - <path - opacity="0.7" - d="M517.385 36.4286H480.462V91.7143H517.385V36.4286ZM535.846 110.143H462V18H535.846V110.143Z" - fill="currentColor" - /> - <path - opacity="0.7" - d="M609.385 36.8571H572.462V92.1429H609.385V36.8571ZM627.846 110.571H554V18.4286H609.385V0H627.846V110.571Z" - fill="currentColor" - /> - <path - opacity="0.7" - d="M664.462 36.4286V54.8571H701.385V36.4286H664.462ZM719.846 73.2857H664.462V91.7143H719.846V110.143H646V18H719.846V73.2857Z" - fill="currentColor" - /> - </g> + <path + data-slot="wordmark-v2-co" + d="M0 1h4v1h-4zM5 1h4v1h-4zM0 2h1v3h-1zM5 2h1v3h-1zM8 2h1v3h-1zM0 5h4v1h-4zM5 5h4v1h-4z" + fill="currentColor" + /> + <path + data-slot="wordmark-v2-dev" + d="M13 0h1v1h-1zM10 1h4v1h-4zM15 1h4v1h-4zM20 1h1v4h-1zM23 1h1v4h-1zM10 2h1v3h-1zM13 2h1v3h-1zM15 2h1v1h-1zM18 2h1v1h-1zM15 3h4v1h-4zM15 4h1v1h-1zM10 5h4v1h-4zM15 5h4v1h-4zM21 5h2v1h-2z" + fill="#ee0033" + /> </g> </g> <defs> - <mask id={mask} style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="720" height="129"> - <rect width="720" height="129" fill={`url(#${maskGradient})`} /> + <mask id={mask} style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="24" height="6"> + <rect width="24" height="6" fill={`url(#${maskGradient})`} /> </mask> - <linearGradient id={maskGradient} x1="360" y1="68" x2="360" y2="129" gradientUnits="userSpaceOnUse"> + <linearGradient id={maskGradient} x1="12" y1="3.163" x2="12" y2="6" gradientUnits="userSpaceOnUse"> <stop stop-color="white" stop-opacity="0.7" /> <stop offset="1" stop-color="white" stop-opacity="0" /> </linearGradient>