From f5f200a4a96ac153c19d553e57154484804d0d9f Mon Sep 17 00:00:00 2001 From: Marius van Niekerk Date: Tue, 1 Sep 2026 23:47:21 -0400 Subject: [PATCH] Replace harness wordmarks with the AgentsView icon glyphs `HarnessMark` shipped the wide brand wordmarks from the AgentsView docs matrix. Consumers wanted an icon: something square that sits in a 16px icon slot next to the agent's own label, the way a lucide glyph does. A wordmark three to seven times wider than tall cannot do that, and rendering "Claude Code" beside a button already labelled "Claude" reads as noise. AgentsView's landing page also carries a sprite of 49 monochrome 24x24 glyphs, one per harness, which is the artwork consumers actually need. `HarnessIcon` bundles those glyphs and draws them in `currentColor`, so they follow the theme and dim with muted text without a `mono` switch. Ids follow the sprite's brand names rather than agent products: Codex draws `openai`, Cortex Code draws `snowflake`, Positron draws `posit`. Each entry lists the agent products the docs draw with it so a consumer can build its own mapping. The wordmark component, its assets, demo, docs, and spec are removed rather than kept alongside; nothing consumes them yet and the API is free to break. Generated with Claude Code (claude-fable-5-1) Co-authored-by: Claude Fable 5.1 --- README.md | 2 +- docs/checking.md | 2 +- docs/components/harness-icon.md | 103 +++++++++++ docs/components/harness-mark.md | 88 ---------- src/demo/App.svelte | 4 +- src/demo/pages/HarnessIconDemo.svelte | 131 ++++++++++++++ src/demo/pages/HarnessMarkDemo.svelte | 129 -------------- src/lib/assets/harness-icons/aider.svg | 1 + src/lib/assets/harness-icons/antigravity.svg | 1 + src/lib/assets/harness-icons/claude.svg | 1 + src/lib/assets/harness-icons/codebuff.svg | 1 + src/lib/assets/harness-icons/commandcode.svg | 1 + src/lib/assets/harness-icons/copilot.svg | 1 + src/lib/assets/harness-icons/cursor.svg | 1 + src/lib/assets/harness-icons/deepseek.svg | 1 + src/lib/assets/harness-icons/devin.svg | 1 + src/lib/assets/harness-icons/forge.svg | 1 + src/lib/assets/harness-icons/gemini.svg | 1 + src/lib/assets/harness-icons/goose.svg | 1 + src/lib/assets/harness-icons/gptme.svg | 1 + src/lib/assets/harness-icons/grok.svg | 1 + src/lib/assets/harness-icons/hermes.svg | 1 + src/lib/assets/harness-icons/iflow.svg | 1 + src/lib/assets/harness-icons/kilo.svg | 1 + src/lib/assets/harness-icons/kimi.svg | 1 + src/lib/assets/harness-icons/kiro.svg | 1 + src/lib/assets/harness-icons/mistral.svg | 1 + src/lib/assets/harness-icons/omnigent.svg | 1 + src/lib/assets/harness-icons/omp.svg | 1 + src/lib/assets/harness-icons/openai.svg | 1 + src/lib/assets/harness-icons/openclaw.svg | 1 + src/lib/assets/harness-icons/opencode.svg | 1 + src/lib/assets/harness-icons/openhands.svg | 1 + src/lib/assets/harness-icons/pi.svg | 1 + src/lib/assets/harness-icons/piebald.svg | 1 + src/lib/assets/harness-icons/poolside.svg | 1 + src/lib/assets/harness-icons/posit.svg | 1 + src/lib/assets/harness-icons/prime.svg | 1 + src/lib/assets/harness-icons/qoder.svg | 1 + src/lib/assets/harness-icons/qwen.svg | 1 + src/lib/assets/harness-icons/reasonix.svg | 1 + src/lib/assets/harness-icons/roocode.svg | 1 + src/lib/assets/harness-icons/shelley.svg | 1 + src/lib/assets/harness-icons/snowflake.svg | 1 + src/lib/assets/harness-icons/sourcegraph.svg | 1 + src/lib/assets/harness-icons/tencent.svg | 1 + src/lib/assets/harness-icons/trae.svg | 1 + src/lib/assets/harness-icons/visualstudio.svg | 1 + src/lib/assets/harness-icons/vscode.svg | 1 + src/lib/assets/harness-icons/warp.svg | 1 + src/lib/assets/harness-icons/windsurf.svg | 1 + src/lib/assets/harness-icons/workbuddy.svg | 1 + src/lib/assets/harness-icons/xiaomi.svg | 1 + src/lib/assets/harness-icons/zai.svg | 1 + src/lib/assets/harness-icons/zed.svg | 1 + src/lib/assets/harness-icons/zencoder.svg | 1 + src/lib/assets/harness/claude-code.svg | 13 -- src/lib/assets/harness/codex.svg | 8 - src/lib/assets/harness/copilot.svg | 4 - src/lib/assets/harness/cursor.svg | 9 - src/lib/assets/harness/forge.png | Bin 13896 -> 0 bytes src/lib/assets/harness/gemini.svg | 17 -- src/lib/assets/harness/opencode.svg | 1 - src/lib/components/HarnessIcon.svelte | 165 ++++++++++++++++++ src/lib/components/HarnessMark.svelte | 122 ------------- src/lib/components/harness-icon.ts | 131 ++++++++++++++ src/lib/components/harness-mark.ts | 82 --------- src/lib/index.ts | 14 +- tests/browser/harness-icon.spec.ts | 55 ++++++ tests/browser/harness-mark.spec.ts | 80 --------- 70 files changed, 645 insertions(+), 564 deletions(-) create mode 100644 docs/components/harness-icon.md delete mode 100644 docs/components/harness-mark.md create mode 100644 src/demo/pages/HarnessIconDemo.svelte delete mode 100644 src/demo/pages/HarnessMarkDemo.svelte create mode 100644 src/lib/assets/harness-icons/aider.svg create mode 100644 src/lib/assets/harness-icons/antigravity.svg create mode 100644 src/lib/assets/harness-icons/claude.svg create mode 100644 src/lib/assets/harness-icons/codebuff.svg create mode 100644 src/lib/assets/harness-icons/commandcode.svg create mode 100644 src/lib/assets/harness-icons/copilot.svg create mode 100644 src/lib/assets/harness-icons/cursor.svg create mode 100644 src/lib/assets/harness-icons/deepseek.svg create mode 100644 src/lib/assets/harness-icons/devin.svg create mode 100644 src/lib/assets/harness-icons/forge.svg create mode 100644 src/lib/assets/harness-icons/gemini.svg create mode 100644 src/lib/assets/harness-icons/goose.svg create mode 100644 src/lib/assets/harness-icons/gptme.svg create mode 100644 src/lib/assets/harness-icons/grok.svg create mode 100644 src/lib/assets/harness-icons/hermes.svg create mode 100644 src/lib/assets/harness-icons/iflow.svg create mode 100644 src/lib/assets/harness-icons/kilo.svg create mode 100644 src/lib/assets/harness-icons/kimi.svg create mode 100644 src/lib/assets/harness-icons/kiro.svg create mode 100644 src/lib/assets/harness-icons/mistral.svg create mode 100644 src/lib/assets/harness-icons/omnigent.svg create mode 100644 src/lib/assets/harness-icons/omp.svg create mode 100644 src/lib/assets/harness-icons/openai.svg create mode 100644 src/lib/assets/harness-icons/openclaw.svg create mode 100644 src/lib/assets/harness-icons/opencode.svg create mode 100644 src/lib/assets/harness-icons/openhands.svg create mode 100644 src/lib/assets/harness-icons/pi.svg create mode 100644 src/lib/assets/harness-icons/piebald.svg create mode 100644 src/lib/assets/harness-icons/poolside.svg create mode 100644 src/lib/assets/harness-icons/posit.svg create mode 100644 src/lib/assets/harness-icons/prime.svg create mode 100644 src/lib/assets/harness-icons/qoder.svg create mode 100644 src/lib/assets/harness-icons/qwen.svg create mode 100644 src/lib/assets/harness-icons/reasonix.svg create mode 100644 src/lib/assets/harness-icons/roocode.svg create mode 100644 src/lib/assets/harness-icons/shelley.svg create mode 100644 src/lib/assets/harness-icons/snowflake.svg create mode 100644 src/lib/assets/harness-icons/sourcegraph.svg create mode 100644 src/lib/assets/harness-icons/tencent.svg create mode 100644 src/lib/assets/harness-icons/trae.svg create mode 100644 src/lib/assets/harness-icons/visualstudio.svg create mode 100644 src/lib/assets/harness-icons/vscode.svg create mode 100644 src/lib/assets/harness-icons/warp.svg create mode 100644 src/lib/assets/harness-icons/windsurf.svg create mode 100644 src/lib/assets/harness-icons/workbuddy.svg create mode 100644 src/lib/assets/harness-icons/xiaomi.svg create mode 100644 src/lib/assets/harness-icons/zai.svg create mode 100644 src/lib/assets/harness-icons/zed.svg create mode 100644 src/lib/assets/harness-icons/zencoder.svg delete mode 100644 src/lib/assets/harness/claude-code.svg delete mode 100644 src/lib/assets/harness/codex.svg delete mode 100644 src/lib/assets/harness/copilot.svg delete mode 100644 src/lib/assets/harness/cursor.svg delete mode 100644 src/lib/assets/harness/forge.png delete mode 100644 src/lib/assets/harness/gemini.svg delete mode 100644 src/lib/assets/harness/opencode.svg create mode 100644 src/lib/components/HarnessIcon.svelte delete mode 100644 src/lib/components/HarnessMark.svelte create mode 100644 src/lib/components/harness-icon.ts delete mode 100644 src/lib/components/harness-mark.ts create mode 100644 tests/browser/harness-icon.spec.ts delete mode 100644 tests/browser/harness-mark.spec.ts diff --git a/README.md b/README.md index 226df88..4f692e8 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ Dark mode: add the `dark` class to `` (`document.documentElement.classList | [FindBar](docs/components/find-bar.md) | agentsview `SessionFindBar` | In-content find bar with N-of-M counter and match navigation | | [FitStages](docs/components/fit-stages.md) | new (TopBar collapse pattern) | Multi-breakpoint wrapper: renders the richest of N stage snippets that fits, by measurement | | [FlashBanner](docs/components/flash-banner.md) | consumer app | Transient toast banner driven by the `showFlash` store | -| [HarnessMark](docs/components/harness-mark.md) | agentsview docs site | Coding-agent harness wordmarks (Claude Code, Codex, Gemini, …), themeable and mono-capable | +| [HarnessIcon](docs/components/harness-icon.md) | agentsview docs site | Coding-agent harness glyphs (Claude, OpenAI, Gemini, …), square and monochrome | | [IconButton](docs/components/icon-button.md) | both (hand-rolled `.icon-btn`) | Square ghost button for a lone icon, required aria-label | | [KbdBadge](docs/components/kbd-badge.md) | consumer app | Keyboard-shortcut badge, hidden on touch devices | | [Markdown](docs/components/code-block.md) | both (consolidated) | Sanitized GFM rendering with highlighted fences and app-extension hooks | diff --git a/docs/checking.md b/docs/checking.md index 4fccac0..f6d1378 100644 --- a/docs/checking.md +++ b/docs/checking.md @@ -121,7 +121,7 @@ everything and aren't repeated per row. | Button | no rule — generic button markup has no reliable signature; state-recipe rules catch disabled and pressed drift | | ChipStack | no rule — generic badge markup has no reliable signature; found in review | | ProviderBrandMark, ProviderButton | no rule — provider images and action buttons overlap ordinary image/button markup; class or label heuristics would false-positive | -| HarnessMark | no rule — inline logos overlap ordinary ``/`` markup; found in review | +| HarnessIcon | no rule — inline logos overlap ordinary ``/`` markup; found in review | | Chip | `chip-label-override` (CSS reaching into the internal label span); hand-rolled badge markup itself has no reliable signature | | StatusDot | `hand-rolled-status-dot` | | Checkbox | `hand-rolled-checkbox` (bare native checkboxes and `accent-color` styling) | diff --git a/docs/components/harness-icon.md b/docs/components/harness-icon.md new file mode 100644 index 0000000..0bff7a3 --- /dev/null +++ b/docs/components/harness-icon.md @@ -0,0 +1,103 @@ +# HarnessIcon + +`HarnessIcon` renders the glyph of a coding-agent harness (Claude, Codex via +the OpenAI mark, Gemini, …) as a square, monochrome icon. The artwork is the +icon sprite from the AgentsView documentation landing page, bundled into the +component so an icon never costs an extra asset request. + +```svelte + + + + + +{#if isHarnessIconId(session.agent)} + +{/if} +{session.agentLabel} +``` + +## Props + +| Prop | Type | Default | Notes | +| ------------ | --------------- | ---------- | --------------------------------------------------------------------- | +| `harness` | `HarnessIconId` | required | Which glyph to draw | +| `label` | `string` | brand name | Accessible name (`role="img"`) | +| `decorative` | `boolean` | `false` | Hides the icon from assistive tech when nearby text names the harness | +| `size` | `number` | `16` | Width and height in px | +| `class` | `string` | — | Additional class on the icon root | + +The root is an inline-flex square sized by `--harness-icon-size`, so icons sit +on the text baseline like a lucide icon and inherit the surrounding `color`. A +muted row dims its icons along with its text. Every glyph paints in +`currentColor`; there are no brand colours to flatten. + +## Harnesses + +`HARNESS_ICONS` lists every id with its brand name and the agent products the +AgentsView docs draw with it, in the order the docs present them. +`isHarnessIconId(value)` narrows an arbitrary string to `HarnessIconId`, and +`harnessIconInfo(id)` returns the entry. Ids are brand glyphs, not agent +products: Codex draws `openai`, Cortex Code draws `snowflake`, and Positron +draws `posit`. Map product names on the consumer side. + +| Id | Brand | Agents | +| -------------- | ------------- | -------------------------------------- | +| `claude` | Claude | Claude Code, OpenClaude, Claude Cowork | +| `openai` | OpenAI | Codex | +| `gemini` | Gemini | Gemini | +| `copilot` | Copilot | Copilot | +| `vscode` | VS Code | VS Code Copilot | +| `visualstudio` | Visual Studio | Visual Studio Copilot | +| `cursor` | Cursor | Cursor, Cursor IDE | +| `warp` | Warp | Warp | +| `qwen` | Qwen | Qwen Code, QwenPaw | +| `deepseek` | DeepSeek | DeepSeek TUI, DeepSeek Harness | +| `mistral` | Mistral | Mistral Vibe | +| `zed` | Zed | Zed | +| `posit` | Posit | Positron, Posit Assistant | +| `sourcegraph` | Sourcegraph | Amp (historical) | +| `opencode` | OpenCode | OpenCode | +| `antigravity` | Antigravity | Antigravity | +| `kilo` | Kilo | Kilo, Kilo (legacy) | +| `kimi` | Kimi | Kimi, Kimi Work | +| `kiro` | Kiro | Kiro | +| `openhands` | OpenHands | OpenHands | +| `zencoder` | Zencoder | Zencoder | +| `snowflake` | Snowflake | Cortex Code | +| `goose` | Goose | Goose | +| `grok` | Grok | Grok | +| `hermes` | Hermes | Hermes | +| `xiaomi` | Xiaomi | MiMoCode | +| `openclaw` | OpenClaw | OpenClaw | +| `pi` | Pi | Pi | +| `poolside` | Poolside | Poolside | +| `qoder` | Qoder | Qoder | +| `roocode` | RooCode | RooCode | +| `trae` | Trae | Trae, TraeX | +| `aider` | Aider | Aider | +| `gptme` | gptme | gptme | +| `omnigent` | Omnigent | Omnigent | +| `zai` | Z.ai | ZCode | +| `commandcode` | Command Code | Command Code | +| `codebuff` | Codebuff | Codebuff, Freebuff | +| `forge` | Forge | Forge | +| `iflow` | iFlow | iFlow | +| `omp` | OhMyPi | OhMyPi | +| `prime` | Prime Agent | Prime Agent | +| `piebald` | Piebald | Piebald | +| `tencent` | Tencent | QClaw | +| `reasonix` | Reasonix | Reasonix | +| `shelley` | Shelley | Shelley | +| `workbuddy` | WorkBuddy | WorkBuddy | +| `windsurf` | Windsurf | Windsurf | +| `devin` | Devin | Devin CLI | + +## Provenance + +Sources are the `` definitions in the AgentsView documentation landing +page sprite, split into one 24×24 file each with the geometry unchanged. Each +logo remains a trademark of its owner. Use the icons to identify the harness a +session ran in, not to imply endorsement. diff --git a/docs/components/harness-mark.md b/docs/components/harness-mark.md deleted file mode 100644 index f61ec22..0000000 --- a/docs/components/harness-mark.md +++ /dev/null @@ -1,88 +0,0 @@ -# HarnessMark - -`HarnessMark` renders the wordmark of a coding-agent harness (Claude Code, -Codex, Gemini, …) at an icon-like height. The artwork was consolidated from -the agentsview docs landing page and is bundled into the component, so a mark -never costs an extra asset request. - -```svelte - - - - - -{#if isHarnessId(session.agent)} - -{/if} -{session.agentLabel} -``` - -## Props - -| Prop | Type | Default | Notes | -| ------------ | ----------- | ------------ | --------------------------------------------------------------------- | -| `harness` | `HarnessId` | required | Which mark to draw | -| `label` | `string` | display name | Accessible name (`role="img"`) | -| `decorative` | `boolean` | `false` | Hides the mark from assistive tech when nearby text names the harness | -| `size` | `number` | `20` | Root height in px; width follows the artwork's aspect ratio | -| `mono` | `boolean` | `false` | Flattens brand colours to `currentColor` | -| `class` | `string` | — | Additional class on the mark root | - -The root is an inline-flex span sized by `--harness-mark-size`, so marks sit -on the text baseline and inherit the surrounding `color`. A muted row dims its -marks along with its text. - -Two marks carry an optical correction (`HarnessInfo.opticalScale`): Gemini -and Forge are drawn taller than `size` and centred on the root box, mirroring -the agentsview docs matrix where they are shown at 30 and 28 px against a 22 px -baseline. The root box itself stays at `size`, so line boxes and row heights do -not change. The docs matrix also doubles Codex, which suits its wide grid cells -but overwhelms inline text; kit-ui leaves that mark at 1×. - -## Harnesses - -`HARNESSES` lists every id with its display name, in the order the agentsview -docs present them. `isHarnessId(value)` narrows an arbitrary string (a -session's agent name, say) to `HarnessId`, and `harnessInfo(id)` returns the -entry. - -| Id | Artwork | Colour | -| ---------------- | ---------------------------------------------------- | --------------------------------------- | -| `claude-code` | vector | orange asterisk, text in `currentColor` | -| `codex` | vector (the OpenAI wordmark, as agentsview ships it) | `currentColor` | -| `gemini` | vector | brand gradient | -| `copilot` | vector | `currentColor` | -| `cursor` | vector | `currentColor` | -| `opencode` | vector | `currentColor` at three opacities | -| `forge` | raster (PNG, transparent) | orange; `mono` has no effect | -| `openhands` | typeset | brand colour | -| `amp` | typeset | brand colour | -| `vscode-copilot` | typeset | brand colour | -| `positron` | typeset | brand colour | -| `openclaw` | typeset | brand colour | -| `pi` | typeset | brand colour | -| `iflow` | typeset | brand colour | -| `zencoder` | typeset | brand colour | -| `kimi` | typeset | brand colour | -| `warp` | typeset | brand colour | -| `hermes` | typeset | brand colour | -| `cortex-code` | typeset | brand colour | -| `kiro` | typeset | brand colour | - -Typeset marks are bold text in the inherited font at the mark height. The -agentsview docs ship these as `` SVGs in the system font; rendering them -as real text keeps them crisp and lets the font fall back cleanly. - -## Provenance and theming - -Sources are the agent wordmarks on the AgentsView documentation site's landing page. The vector marks -were adjusted for a themeable library: `width`/`height` attributes dropped so -CSS sizes them, `fill="white"` and Claude's cream text changed to -`currentColor`, OpenCode's three greys mapped to `currentColor` at 100 / 72 / -30 % opacity, and no-op clip paths and masks removed. Gemini keeps its gradient -and mask under namespaced ids. Brand geometry is unchanged. - -Each logo remains a trademark of its owner. Use the marks to identify the -harness a session ran in, not to imply endorsement. diff --git a/src/demo/App.svelte b/src/demo/App.svelte index e44960a..8ac0699 100644 --- a/src/demo/App.svelte +++ b/src/demo/App.svelte @@ -32,7 +32,7 @@ import FitStagesDemo from "./pages/FitStagesDemo.svelte"; import FlashBannerDemo from "./pages/FlashBannerDemo.svelte"; import FormFieldDemo from "./pages/FormFieldDemo.svelte"; - import HarnessMarkDemo from "./pages/HarnessMarkDemo.svelte"; + import HarnessIconDemo from "./pages/HarnessIconDemo.svelte"; import IconButtonDemo from "./pages/IconButtonDemo.svelte"; import ImagePreviewDemo from "./pages/ImagePreviewDemo.svelte"; import KbdBadgeDemo from "./pages/KbdBadgeDemo.svelte"; @@ -113,7 +113,7 @@ { id: "fit-stages", label: "FitStages", component: FitStagesDemo }, { id: "flash-banner", label: "FlashBanner", component: FlashBannerDemo }, { id: "form-field", label: "FormField", component: FormFieldDemo }, - { id: "harness-mark", label: "HarnessMark", component: HarnessMarkDemo }, + { id: "harness-icon", label: "HarnessIcon", component: HarnessIconDemo }, { id: "icon-button", label: "IconButton", component: IconButtonDemo }, { id: "image-preview", label: "ImagePreview", component: ImagePreviewDemo }, { id: "kbd-badge", label: "KbdBadge", component: KbdBadgeDemo }, diff --git a/src/demo/pages/HarnessIconDemo.svelte b/src/demo/pages/HarnessIconDemo.svelte new file mode 100644 index 0000000..c7a56af --- /dev/null +++ b/src/demo/pages/HarnessIconDemo.svelte @@ -0,0 +1,131 @@ + + + + + +{#each HARNESS_ICONS as { id } (id)} + +{/each}`} +> + + + +`} +> +
+ +
+
+ + + + + + +
+
+ + + Session ran in Cursor +`} +> +

+ Session ran in Cursor, then handed off to + Codex. +

+

+ · + · + · + +

+
+ + diff --git a/src/demo/pages/HarnessMarkDemo.svelte b/src/demo/pages/HarnessMarkDemo.svelte deleted file mode 100644 index bbf6e31..0000000 --- a/src/demo/pages/HarnessMarkDemo.svelte +++ /dev/null @@ -1,129 +0,0 @@ - - - - - -{#each HARNESSES as { id } (id)} - -{/each}`} -> - - - - -`} -> -
- - - - -
-
- - - - - - -
-
- - - Session ran in Cursor -`} -> -

- Session ran in Cursor, then handed off to - Codex. -

-

- · - · - · - -

-
- - diff --git a/src/lib/assets/harness-icons/aider.svg b/src/lib/assets/harness-icons/aider.svg new file mode 100644 index 0000000..c0f37fc --- /dev/null +++ b/src/lib/assets/harness-icons/aider.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/antigravity.svg b/src/lib/assets/harness-icons/antigravity.svg new file mode 100644 index 0000000..fb7f0d3 --- /dev/null +++ b/src/lib/assets/harness-icons/antigravity.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/claude.svg b/src/lib/assets/harness-icons/claude.svg new file mode 100644 index 0000000..30a26eb --- /dev/null +++ b/src/lib/assets/harness-icons/claude.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/codebuff.svg b/src/lib/assets/harness-icons/codebuff.svg new file mode 100644 index 0000000..259e642 --- /dev/null +++ b/src/lib/assets/harness-icons/codebuff.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/commandcode.svg b/src/lib/assets/harness-icons/commandcode.svg new file mode 100644 index 0000000..ada9018 --- /dev/null +++ b/src/lib/assets/harness-icons/commandcode.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/copilot.svg b/src/lib/assets/harness-icons/copilot.svg new file mode 100644 index 0000000..f3e9abc --- /dev/null +++ b/src/lib/assets/harness-icons/copilot.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/cursor.svg b/src/lib/assets/harness-icons/cursor.svg new file mode 100644 index 0000000..369a695 --- /dev/null +++ b/src/lib/assets/harness-icons/cursor.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/deepseek.svg b/src/lib/assets/harness-icons/deepseek.svg new file mode 100644 index 0000000..8b10bef --- /dev/null +++ b/src/lib/assets/harness-icons/deepseek.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/devin.svg b/src/lib/assets/harness-icons/devin.svg new file mode 100644 index 0000000..abe018f --- /dev/null +++ b/src/lib/assets/harness-icons/devin.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/forge.svg b/src/lib/assets/harness-icons/forge.svg new file mode 100644 index 0000000..876c183 --- /dev/null +++ b/src/lib/assets/harness-icons/forge.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/gemini.svg b/src/lib/assets/harness-icons/gemini.svg new file mode 100644 index 0000000..a9222e5 --- /dev/null +++ b/src/lib/assets/harness-icons/gemini.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/goose.svg b/src/lib/assets/harness-icons/goose.svg new file mode 100644 index 0000000..81ce04e --- /dev/null +++ b/src/lib/assets/harness-icons/goose.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/gptme.svg b/src/lib/assets/harness-icons/gptme.svg new file mode 100644 index 0000000..d0b985e --- /dev/null +++ b/src/lib/assets/harness-icons/gptme.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/grok.svg b/src/lib/assets/harness-icons/grok.svg new file mode 100644 index 0000000..cdc11b1 --- /dev/null +++ b/src/lib/assets/harness-icons/grok.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/hermes.svg b/src/lib/assets/harness-icons/hermes.svg new file mode 100644 index 0000000..2c42383 --- /dev/null +++ b/src/lib/assets/harness-icons/hermes.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/iflow.svg b/src/lib/assets/harness-icons/iflow.svg new file mode 100644 index 0000000..75febb1 --- /dev/null +++ b/src/lib/assets/harness-icons/iflow.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/kilo.svg b/src/lib/assets/harness-icons/kilo.svg new file mode 100644 index 0000000..732644b --- /dev/null +++ b/src/lib/assets/harness-icons/kilo.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/kimi.svg b/src/lib/assets/harness-icons/kimi.svg new file mode 100644 index 0000000..76f19f5 --- /dev/null +++ b/src/lib/assets/harness-icons/kimi.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/kiro.svg b/src/lib/assets/harness-icons/kiro.svg new file mode 100644 index 0000000..f2d97dd --- /dev/null +++ b/src/lib/assets/harness-icons/kiro.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/mistral.svg b/src/lib/assets/harness-icons/mistral.svg new file mode 100644 index 0000000..ab7690b --- /dev/null +++ b/src/lib/assets/harness-icons/mistral.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/omnigent.svg b/src/lib/assets/harness-icons/omnigent.svg new file mode 100644 index 0000000..b26348f --- /dev/null +++ b/src/lib/assets/harness-icons/omnigent.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/omp.svg b/src/lib/assets/harness-icons/omp.svg new file mode 100644 index 0000000..a64994e --- /dev/null +++ b/src/lib/assets/harness-icons/omp.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/openai.svg b/src/lib/assets/harness-icons/openai.svg new file mode 100644 index 0000000..ccafc8c --- /dev/null +++ b/src/lib/assets/harness-icons/openai.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/openclaw.svg b/src/lib/assets/harness-icons/openclaw.svg new file mode 100644 index 0000000..5e4815b --- /dev/null +++ b/src/lib/assets/harness-icons/openclaw.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/opencode.svg b/src/lib/assets/harness-icons/opencode.svg new file mode 100644 index 0000000..daae80a --- /dev/null +++ b/src/lib/assets/harness-icons/opencode.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/openhands.svg b/src/lib/assets/harness-icons/openhands.svg new file mode 100644 index 0000000..6c9514f --- /dev/null +++ b/src/lib/assets/harness-icons/openhands.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/pi.svg b/src/lib/assets/harness-icons/pi.svg new file mode 100644 index 0000000..fe68dc4 --- /dev/null +++ b/src/lib/assets/harness-icons/pi.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/piebald.svg b/src/lib/assets/harness-icons/piebald.svg new file mode 100644 index 0000000..83ec0ea --- /dev/null +++ b/src/lib/assets/harness-icons/piebald.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/poolside.svg b/src/lib/assets/harness-icons/poolside.svg new file mode 100644 index 0000000..05985f6 --- /dev/null +++ b/src/lib/assets/harness-icons/poolside.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/posit.svg b/src/lib/assets/harness-icons/posit.svg new file mode 100644 index 0000000..3844be6 --- /dev/null +++ b/src/lib/assets/harness-icons/posit.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/prime.svg b/src/lib/assets/harness-icons/prime.svg new file mode 100644 index 0000000..49ee7e2 --- /dev/null +++ b/src/lib/assets/harness-icons/prime.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/qoder.svg b/src/lib/assets/harness-icons/qoder.svg new file mode 100644 index 0000000..d82cc1b --- /dev/null +++ b/src/lib/assets/harness-icons/qoder.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/qwen.svg b/src/lib/assets/harness-icons/qwen.svg new file mode 100644 index 0000000..9b91539 --- /dev/null +++ b/src/lib/assets/harness-icons/qwen.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/reasonix.svg b/src/lib/assets/harness-icons/reasonix.svg new file mode 100644 index 0000000..772bc2c --- /dev/null +++ b/src/lib/assets/harness-icons/reasonix.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/roocode.svg b/src/lib/assets/harness-icons/roocode.svg new file mode 100644 index 0000000..cc7a1c1 --- /dev/null +++ b/src/lib/assets/harness-icons/roocode.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/shelley.svg b/src/lib/assets/harness-icons/shelley.svg new file mode 100644 index 0000000..f9f80f6 --- /dev/null +++ b/src/lib/assets/harness-icons/shelley.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/snowflake.svg b/src/lib/assets/harness-icons/snowflake.svg new file mode 100644 index 0000000..f55d66a --- /dev/null +++ b/src/lib/assets/harness-icons/snowflake.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/sourcegraph.svg b/src/lib/assets/harness-icons/sourcegraph.svg new file mode 100644 index 0000000..e17669d --- /dev/null +++ b/src/lib/assets/harness-icons/sourcegraph.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/tencent.svg b/src/lib/assets/harness-icons/tencent.svg new file mode 100644 index 0000000..fbf8327 --- /dev/null +++ b/src/lib/assets/harness-icons/tencent.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/trae.svg b/src/lib/assets/harness-icons/trae.svg new file mode 100644 index 0000000..fbea2b6 --- /dev/null +++ b/src/lib/assets/harness-icons/trae.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/visualstudio.svg b/src/lib/assets/harness-icons/visualstudio.svg new file mode 100644 index 0000000..45278cb --- /dev/null +++ b/src/lib/assets/harness-icons/visualstudio.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/vscode.svg b/src/lib/assets/harness-icons/vscode.svg new file mode 100644 index 0000000..9e2ef54 --- /dev/null +++ b/src/lib/assets/harness-icons/vscode.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/warp.svg b/src/lib/assets/harness-icons/warp.svg new file mode 100644 index 0000000..40ce883 --- /dev/null +++ b/src/lib/assets/harness-icons/warp.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/windsurf.svg b/src/lib/assets/harness-icons/windsurf.svg new file mode 100644 index 0000000..4f518ab --- /dev/null +++ b/src/lib/assets/harness-icons/windsurf.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/workbuddy.svg b/src/lib/assets/harness-icons/workbuddy.svg new file mode 100644 index 0000000..76ab763 --- /dev/null +++ b/src/lib/assets/harness-icons/workbuddy.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/xiaomi.svg b/src/lib/assets/harness-icons/xiaomi.svg new file mode 100644 index 0000000..59cf68f --- /dev/null +++ b/src/lib/assets/harness-icons/xiaomi.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/zai.svg b/src/lib/assets/harness-icons/zai.svg new file mode 100644 index 0000000..c575da1 --- /dev/null +++ b/src/lib/assets/harness-icons/zai.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/zed.svg b/src/lib/assets/harness-icons/zed.svg new file mode 100644 index 0000000..9d2cfc1 --- /dev/null +++ b/src/lib/assets/harness-icons/zed.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness-icons/zencoder.svg b/src/lib/assets/harness-icons/zencoder.svg new file mode 100644 index 0000000..2db5fa0 --- /dev/null +++ b/src/lib/assets/harness-icons/zencoder.svg @@ -0,0 +1 @@ + diff --git a/src/lib/assets/harness/claude-code.svg b/src/lib/assets/harness/claude-code.svg deleted file mode 100644 index 166692e..0000000 --- a/src/lib/assets/harness/claude-code.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/src/lib/assets/harness/codex.svg b/src/lib/assets/harness/codex.svg deleted file mode 100644 index 3f25c51..0000000 --- a/src/lib/assets/harness/codex.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/src/lib/assets/harness/copilot.svg b/src/lib/assets/harness/copilot.svg deleted file mode 100644 index 078815c..0000000 --- a/src/lib/assets/harness/copilot.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/lib/assets/harness/cursor.svg b/src/lib/assets/harness/cursor.svg deleted file mode 100644 index f18635a..0000000 --- a/src/lib/assets/harness/cursor.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/src/lib/assets/harness/forge.png b/src/lib/assets/harness/forge.png deleted file mode 100644 index 934144d8dd486558243e7772c0b813b43630397c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13896 zcmb{ZbyQnl)CLG6#ogWAEd;mXPAO8{Luhe_;*?@36ljYTOK~XfUZ6-Jg%l_*1&TI6 ziZLtlW#N19^E`q7vf?z+t*D^aSqY9uq3 zIQHUHErtYT+JqfO_y&SBp|a%O(iEvGq$$dzPlTvVg{k3|ILXSSUeXj#g{l336rKPI zBS9K-5$Ysm(n3ulLqVEEWzy#WiU1jMOHu0AN~8fm@^5Ci9stT98S(@rQm_!b zss1=(?b>5U$dg6MlR1i0IZ9AP%99;BV2wP*`{#;z<%&7~4CllN>(~j)Uxp%Gl_X7- z#1}{bQ6VuFqzREFe*vI)E=6G@MwO^UY9~hJE=B1kNg1a|7OOypaKthbrrx*5uGS?C zlOz9OiStZ^3h9iMsYa5aM)FpbN1gcHW8z*D{30!)3m42~>qoPeI7la~VlAQq zO`?w)#NEdDJNDS^M))h%kA6PGfoc<#Xc2wZC)~EfuF@qubH+Sy!0I%@->`Y~*9mjW z4!cx`=+qhWmo4_DEq1vMVgD2S27Q83ZK6tD!dgAT1uL9-eS&5Kg3o${!=`xchWJee z1QTYsC|69B8|HU&+(~oXaWmY}r+8BqxG;0vJ`?<}rg&Y(_=&;@A2iE>fu=@J(Z~N6 zKk{FOkI-Uxfx3p8cn1Vnm;!(fPPuvXDV{*hXMwIxfo}3H{%+_4hNQS8P()l#L|oEL zTtZ$-R$g32NL*ZAT)Z_!Q}X{J;N|1$0Sfzn78sttO+yRt|NnXj^zd>E2z2uD{lCco zB;=(f|ECjMz|b-VhNO_LhMHOE5~AQ=w2fu>8e0s8+8?IIW2O*Fb^>q4ZxSq^$K$*& zo^UF?)u?*s_6^Eus>j{LmHPY#pvhGaT2|9?X({$E2ZO z_|T8G;XnGy(%)5+xumLpaqgk19vV0uAbKNPJ9za-7!N5EO6By2eXf6RG=mlB#HTAOqq-t4#E6ht^jvHiZlLg%kKfrkH{#%h*?k_~yaK$H6G>7(w#GzR zALykZ1h;QmO*_%LkY0TFG9LU=tu5QLJ)65*v)%qC(~EcUFyV1`fndw>pLGk6)Wc2o zzm^QzvOL@ATwjg{ce5|TE>GtgbDuP!#U{{K-y(RH5eH%?p1jNYZObpB6W#xl^41AH z{T6JKf%rgY5j0j66x#Cr?Gj__^0`IpHCSvsZXvT!#?kx0>+V3 zHh$d6a~v8|@CZnS7I)Ma_NKeEAJ8zFgcZdfiF-&fdB6I62f%RoMR~w&bxlGu*}OzKN|FlKwWH zVU(K}Q7>5-6IGM+&K@wzTcVfrk;igpO1dzT22c}u&PavJTB3#+_?QV|gV$W-<3 zR_-w8L1xj?F%!CHuz`@wH0=#)Ylf_gd3rLAVQGCi&44N<1Xlw~@$G8Emi(4fpdwa# zalC{_|DaG@DcIi6v6H0Wg~@seO23;Lrq2oi7Q0idshi#BAmuqZwHv-)A=?7{?heGYeUYfHpFnK&o>JXoz_47-MchUghxX2gFT=KTG7j*=BjQIDpz}R6+?U z=4S+}4|}^aacwQN3l@=8StCh(Emn6h_qZ>Iqr%hFCDqjGki?1@zcT78bBuF^qc}E zbao~4QPyHgEN@gN!&V!`IM+`;vijjM>-(#(&uL)g^WeKJ(#Z?QBl*XcJj%;f12Fv? zsP2+idga|ayvy$C@LUIgZa0!+uAli!eDIm5PK26B6Dn?)sCW7M^ET2>fnj&u1TNSc z*@?qsYFIPvwnd4G+Bw;6$J7<8TuvjQ3q<#tYb1U;`f%Utbtge-`t%K*-LvVET#51M z_egF7P8+{}Y=X@UbL-;8D)D_JU|smFc^xcAIi~E9b;Yl4!%;3@%3kzCq(`uQ@2!J8 zLBo1fT|g&A-Ro}GQU0wNG}IxVvLrmBd-UNEo+IHz;abJYs^=k51@2Nmjs~2W7^-LqvGK zTn`YhlZ5i`p>1A^)~12qovs^@sJn8_n{%`q_C0i=b*z;EZc`uuZCBtlD0+}$vls<= zcmMoz@@n$6d`W-IH^G1yh`Rz2Vg)*WmZsR7V1u^gm!kk#W@Xb1?4Ogg%c*{Aa&F@` z{UNcJjDCEnxrTeB$W+h{`CR|;Xf83XR&f_MGS(H-XHx$rwgv*#O^cpL$zRQ})mW8A zV?Eq8Y)s6l?ZYpUr+p|POL+U@>}nUQV|7q0uid7hEAGs^I$8-bM1rFjWVaboZ4T&W z`*Aa+;M-2+l1s~PvAqC_%I=X}PVKv7!>hjN1!zN z6b*Ktd5+Qa&Ajcil#=&ouoIB%EoiWB^S^m&oo5q2gO`LbEBja6a-bdiej)j{@Dd-n zycQa5ToyFi93xr2_ZAOmI}<8lo3NGM7BsrNL3+uv#^z5CffenJt$ z^QE6VEL44S@u7(7lm|BBoRPk<)JI9Zy(2C)DrXq|E8Fz-0c>;tcMNAa_B)c%?dI_O z3sx)_|5xuQIJVmp@a--1g5t!FOVmwVGX0Nq6x_h}6UyfrDVtm(J?yO$!EZhwbx0X) zY6RyV?RWp08*Y$3=>LAlrKgb-d2N)jMfaW-HY1YLleVWGeiMQvyEq`0$HyeTMs|x+ z9W4a;q=cgwYPH#pFc-`_uK?f8E6Fazu7q8!y}QzRiJ88-i-wyuJo&&!)3=spsQEuv z7c;$OvR^qIS|La-l1cH5u-~K#SpHt^kiH|gcde`W5X$veM)RYDHQ_RlGq>vg?a`nS zqHV_!zZOYeL^?zIm$USuHP9YR?PcLk|JGm=Ap#xA11PUG+MLb&;M^!FT;Y-F#uRda zzw5u3AO~c!jpgd|>?_S^*L8=F=d=`o6iJtjEk{9G5bkLp!MWYix*-XWf$h^4g={kI zNa-zqJ$@!MY&NZg`aotw>G64cbUxa-y+;hFx+;Fj8Jy!WkPUD&VPiwcycH_6EGNB5 z{?+E?)HK45eLMoIq7p;$)Uu!FuN94oKwx2rb4aTN?9kXps>bF8$zdFK^xN&`93Jni zMecxU^ULlBey(a9H!KJtt&V&0W^`cX5o~=) z1esY$DitFca>Q&|S3#e~M&@~x+irvU z>a<@-{g_8b_4z)E3AAJV<`3U>w1%Prq9BsInwO+ovP(2rhxPuiGs&7 z+=?Mpcl28%vKqmT@yJFN_xBP*u5n7(ff#nL?itoW8uf-lg>TQSa=Ymj{w^u-{OmD* z4T}f|X9B|9xdK^}b?*ub{v;ley<39pyRg1G!>_rwLGG%=(S|qo>%apQcBx5j(oUhJCu3NyFP zR`cwZD)0Y19v6rod++z1+?P=VobnK3q++X`DWO)~DpUtAv)SX@J>{|JEm(ioGd)_f#pG?+=4)@6Pdz%7JZbc2sG7esa<8J)? z+%pm9O4p-4K-5ib=$0|V1&_tQ47lRdJXJKl_aC1hlrXLz1!D{aO}7pT`_l&meLdTe zUh2COyV7iCzteesv(N#743*H#fRbb>@5 zt57cHT4_Hbe~#xDJ4&CX(W-(BNj=GSTn_`tg)3Q*fW_6;MVhI+cnj~0g`A`!iLjm) zNi}E-Kt#FxV((QyndQ6Lsw%BS9oz?GL8>%i9MK+hE*^#!;`oDy-aAsB+>@0ibr?#1DorA97_Ds zaA$1I+9Tki$(Nbu96zYmC(HyEYt3=z^9`fI9S_gUUT!^$w#OTm4**VnVj z`YEZr@M!9hl5f;7I&h!QaR59*EG`T7j7Rg7mSyVNJ|l99W-jX@KOjfmGy|%OdO7hr zzQ_`gz2Co5WcdR$@C-lMyE3(hr}oTK3%8$s_#@e3c&Kj1P81zqG;pGg{qY@2)NBA$ z%2-PAB4!Kk!3;8c=k)$iOEP`F2Q$=(mA4zuT;!%u!$R=}i~4WZ`8!K^+A14huhqu@ zYIeiR@7ov{?jy)yv!IMQJI^d1DHb@DjdFDo%-S%i^$T(+vTc#OmsQH>i$#!9Ln^bX zwqL&2^hNBqg<7It`%?_hK7)Uz>=aVNq$Qa!U^IO=djVYeuiD4>a^DxCt(zFP$J35s znDjQQr*aw;XWxMn`WXv|z0}$;J2ZKl6HMlfiMxNk;0qOc6UqdT4597Ep|3z>(RAV@ zU9c-6JJq{MYu{di-bWQvJZ@q=m_xI{OKYT`b+~~xmo}6yI83z>(QC!Tsc|o8T)zN< zdF+L6{JLv!Bum)=^s?p~{la`*o!WOvDI2o}mgtR)X|*f`|7=x14YT2xXPx@VI(0O@ zwYZdM_{mVUW+GAr(H`2$`}HtJrE`J{lC2)vb@&+lbv)?^@odk zOOP7}X<_-$C8F~OJ8j^_?=_Z)5@`#aJS%~bPYoEPpbF6R1`~1Q#oT5^wlV+$&ce5e zE9V~hEB~C9eg6x1`x29R zFZo0C=E~I^r;8z5#cjWv{co1AzHd8&0Tj&pLkk2qhZRblyEGTno}MSy%Z%|7M^mxy zzjAmR*S-B&Hjc|=Y}Nt(_HUKfGG}}DX9*i(+CAwCL*%sEWawH@_Lo__Hv;Y)>WaXwNlJC2;u#VR{x{6ipcQQWa`XYcRd zQ_()W!rtQD6Pvg+Nl@UBHLensxIdkGWfyF36L}Z{;WIgY}U` zEGH^|16F_~F+gY^(AU{AS3wq%bD)3DpZ?h?U1^Uqe@Lu?nWo&5M zp5r{#-bxJeGF1CW4c=quaG%v*cjg-W+BAQ4+!;*OH99AjSM-+i_u#%+YYOl9eH*6C z;d@#`DEc)B(RRz9SFYW1A9B$N?o*}1DNt~@g))%x62CSOpLE!KW_98PvRIxR%pAV$ zp>|uC{1cRqjr8@+&A!)OKK~v%&}x5|9PlXzcEgyp$V{+Uuu^|yu?j-pUiWIQ3dK{# z*DUpp&d>?-LTe@Zxw_9+QO$r*@^I7AI}tHWInCy5LLTNsY48O!#?kt%F;RWUts{JuIPu;;A45pUNtF?f0|MOw+?XdBH z-^6&U_?1Sfq?BLaBJO#v?`UCLtSiT=>%R}^3={Qf@SoX|BKn2yr?e2ix%gryn&*Kf~EvgvQ%Fe|*fl(m$@?j}BLIC{#c@ z?$!ew5GNPL>KQZ~#$yHMaW9<6JY)+xuZ zfAuO@kA>25+c{ZbTgB&%<;mTXSHv9ku{N{c8gi$Ex6|oCq5q=a)w?g(`L}z0R;6yX zng$bJ|DkA)y(U3cFHd?+hhw2)hR&M*qqJDwVEKd`X{%%}^8D^?C(aSONCH~`k! zgGo2N%dIuDwpF|bl1qO;bSA3Ez(UfBS?UXgb zv=k5hC+}NB(O!fq3>FF&$cvXs%HljP`VWn#`GbYKR|58TH&B72useru|Eq$#jd*+7 zwDCz=Z4|s^wYdKNlSAlI)>CgO3Dnek7LgQr$-kxsf_h&_q{X5$T77#T;tVJ(eh!)T zr}}MfaQcw(OMfmx1@iWiXM+WNAEzgljfByENy{d~hE31Yma`f}G|3NxtnIb4(UCBD z*`)Gu&kb4Ieu5Xjvv=X$O-~PE*u`TxxnQM1&_|sDyuq_;62-XOU)`UP7Uv@FQEEc3cMP34YZAk_XHhD!Z3D92v) zMs^^w{Djcv*dbo3U?Eg>{{EhIOVmK|?FDvdHZpRrlWp<$eaumy!pIcoZ7TZx=4#;% z)8}61A$7}`m?#lUd^BHm*V!*P2bw4aUvTseMi-Yjl)S_ycqy?DUnZUjN}m7pHM+uz zesE1+O?af{Ts3}M#p7#_<~}xw(=iXWmf4L%L3-tMRy+c8jxESp%3@$}%@E>)M}2p& z5?Y98rLG&mek17_Je;=wD3AB5U!D(9_%g6(baFw5Q#v`*z4;ZxK44O}52mPFT;M&G zNU7YC`ndzm#DVl zXi&EjZ3&i|6jRf#Te+{ygq4~z-CLKy#7{ATO80J!>tK8TqH4bsh#IU{)JR5+bzn1H z{x)Z1C`u^2PEEOihLzJ%@x9%N$_+$EpMby{HuTC*MSSyu-d~$Me9*$}-MOAuTaK2t zg25qrg&JgpGWNCr29nMhV{iRdgvM}7k>%J*VT4MD^n-G~ zv|RE05PZ1Zs+3?4+nOokFMfEyC9O~^!wetSToc3i`l0q<%`8Y`os~Ph4@PMZJ|y?f zIAf4bRInxVqV=XazkbmyDijFpFpof|e%;Afd@5oOT#TOr`i*wJXxXlmf$h09|H_MG zYTP@K*GojvTsaVWy_Plwj2CR&*P#uFQ`r&Hw4AaNHrtEu%QoEqc__Eoc0_$xDRQ*K6ycUs1H5vP<>{Gxdp(evq;k*y%9%= zFyQ9iQG9srfjdb~tY|1>&zM{6MCuT<*5hPwP^cUU{i8jHT`El1r{H%8TaQQd94aCO3${CkUS}Cz}bMz za1qQvy>v5!b0!IsYDu0VGfe(-Adm}Xr2?$B0O;N|iyG{`{|?fKUQa>tM+2GCp;a^o zn$;>k1CBj0Pa`Q+Ol$%3RZ&hwsi0{3PWq;y2W9dt_d>G)WBYGe(8?U14MHE6rus-p z=WdV2e$hG$eNqwUo;s)JrFB;G25e$E0ph+lBliN9p}Vyu7XkLctUagQosQkY>8w{0 zJ}x>Z0Grz-ZhZ{F)PAUW<6ybIoQ8fPTZ`8VyXB&3cJ^X=4hs zL}KdnK=4;&=On(y`igbt4+O_n88Q5i;1i;?f@sLreZIacRLM8o&-fD)l68<>JEln_ z3l~KO#SbnDOAelWN3;9G5Ph5K{OAL_BQw%M%;M!}oMB~Cpx)=hfhQM#y+D7;UYZ%m zKnwPolUnpvoXpUXVVynPrqvUu+Fr63h~_)om^?Dcr};birf$w|WzC@W{v{L4L)_Xu2+x(0R-?X%ll)V|9`TtqXd~j^;eW@rkny<2>!L>WkQJZHnn^8Cq z|03zhtCg314=Pz3o+%P;JXN-D;$(_#|9keW57X<~&jYEJ!Cd5fc1#EF7m`-C2(^>% zG5NZI7;shLOJR}IO)>h^0DCp)h%(|@{Q$MxFtX1cp>)_#vu|=lB8hR~8^BZ2KTm&l zHoU`s)U}iedUo{V;WzoqI(P(8ii)RoDQW_<_9w?;;#l}TkZbf3@9k3#IPp~F0MD|gfG~6aa-v~a zILV0fbmZNg!$*?~0o4q06`U&*5ADqg42}C=JVMHcGU^qzF3#c!cTy*?T$+;pj z$CbEQW=^59WeD>Yeb|%Mi46_enTQ>U7|qpZ+b;+;5l09w^w3(f?}3 zxBctM%j~216Ss&kmNizt5Z1MaEXnz?SUZ7Hz~pUL#mn`S_!@K7c^}wKu;M+OWba|D z5j~v_UJPsjUn|K(1G$(|BCC@)e z7~i%DJyp_AaduYEg4_}Glk6p)1i38#|M7F~t6GxY6?aZ}S7T8Rm#O>>{z zeFwQJ+NrtrzR!UTo&y{zHlYIhB~Q7`5>@rl`pWf8r3dLo}MO<&yf z<$_uvLF^E|+;wOw^3^k#RF++M4%)&8RS^-Mi%SE6vmdQF04DN?2#o(l&Viq&tB@cGsxW~;bJq))(^h4*vRw>hKS5b*3E)K%F3(@Iiv6+LM{_3 zQigB%?LrT_^%RzD)&6kIMXx95pVOmm(OHgiF)pK{h<$LNxO?@4 zut2GD4i{k+jpmo}r-ml$NoKQ!tm4aSm$c$3>RH#DSDni$=p}=zqKI1m^K7@XcR{Q= zD5ivnIx2KQ>fq2WN}n4%b6udTGLIP9+^TO!9rm1Ey?cB7+0^T&*ZF9#n#*jxFOcf& zpjh5M@aM0QOjcOH^kud^pdKUr_F1wJI)0JiG}oF_5!>L@nT zPHU5?G)a2%ThWK-Ql53{myD~;Hde%MID<0|RDyq*$bdRRw9v~&CjF#7Lf>{nQ(J&> zuyX|F{<=9xYO_JKwlHyDw)f5_*Dt>t6b^^V1+gvBEf(&xGXK#$GS+Klcq}%3=W|Q5 zJK&{x8de=$u?^Mb^&KF1=I~F~i261&csfA~Tj`mV=YGQ74SwJFVN=EM%oUM+6NbB@ zVGni@Kf+#?Ez{>+_8*;+HFk%4vM_Dh-U6|XLYYv3l&H^-5AJO~V&5B6L?+jhOn&$Q z-Nxh996XAd!_C=KX_ZQEZOE1_Q>Iz{<4NOTz{ZQRuk%8V%~c*6K0kWn>oYf2$>VK(KmQinFLVn8CZ%0wun>uK{S{MdfE3c^Rv^7+zwV5<^(r_~q@p`8j zr)6g>djLe?4luY(BYc5ad#lFeSN9>BAmyv&chHtpQV|CqK@W%LR>~Yla#6bl6m0Zn zx;MXYD}9s5S<30S{K>02II^&5D@y${*rFQT$2e@oGO|!U>w`r{Z~XW^dP2p}6u@Tf zryRROvPR#>4*awmtt!KKqzgDZTlVo<*x(%?D`kAxGZ{EedQ*mN%VUk~Zh7*K^&OjEdePg*9!txwW&g=RRMEinXkglEAJd(@$x$w)aaeQ=#_YGBDMz1j z>%e=SfBj92>R9K#id%pb-qVm+n1YL95!@&75B9$Lwm7*l*#!uHGxQ?x5%ibi$i}Qh zn;e$d`{=x>=?D*wb+iqgEJqF?RtW2EE=oy-!@;^-$^ZdK{YMyZ$Eg#mhbUrWaG#o& zjwh;yQ!~Js1P8u44}e-*kZ zJ2XOdhtm_c-%)hKptGA}a6^vjJOF|hYdq2a=$E-=!-Z% z9jefh2bi}5aL$6?$e-~v3DAz^O5rgR6*>=}38#*se zDqM18{vXm=Ec88OGpT_+>wX&`pZ}OxNZ{N{$}%)<&r9>}C|2}@KH|RqW`_qGa2jWY2o<`L_cxqo9@X%A^C#C~q9`~xaovRYS=fT6(Jb!K_evc;) zSxpdwz{dWycNU|If11Svdt&#L)T!ucfKfj+r2|68lU4!YumE`RWWA3jco%GiNXxX0 zWy4VM9G>H*)z0YPZ)~8OA+@)u>*@@oLl>diH}!~s*~Q^joBCcjiUC%e=^r_5j06z- zCSXZ!a#HWfxPC zR$XncC*%3Jqt>~aE?AO%E@)0>?UGgayGA%XE>|;`d}O#n3^v@E8|$sY)9QMwAd1)cEmU;}U@Cbj zb@a;Gm9CsxY?n`DHqAA=Wm4lMHE>+`NUQJYJ6c3vHAGO#n08v z9<)Ld3CrK}DU1rLW$s34DD!QEqw04e)-Rygk`Q-2I1 z@;G}$!sn*py?Ns5?OPPNh_BoY8Arco^_DAYe40s_%osvZ;Ap0(g zl(Gf$%3nKhVha>oKNrn%dAuL_pOT}ReQRS?3|+nDs_UR&T510=!(NZ@?=ui{ zEm;BWzs;GvIG2^*UR?yc)OVq4Bg=wSvZXrY>XNLb?7sK{Bc;74Wp$%eSEAXimj2(x z!&HId5Gu&;3k{|o$#j7=uY^5qJ9;3;Ow1TAOw5z>mz$J_)5Mv~xBVJjm6A z34>I4M2&PAMM?fRqAx~CR}`m99A$SBfpV|ur*u$#{ATEVJBnMR3#s=VFO*|`$_kf8 zCLL(gw@)x@)lcf9x7sYpFkC+7YSP*)ST>br%7CdFrjQC;mNpviz*r`iYFIvd4`(J3 z&)k7-_TGnO`SI6B#`qDkX2gTZ1Q5E@-P06t)dYpiFtG+Q^rnzVYv)l>ePP+0sQA6s z%EQ-Iu0ma|Ym+YCvvWhrU(av@^T^!|{u&;+Rt(pep>=wK7K!sC0@P@}Q{EaOJ(85eH$51 z1B)6q(v2u9iAkj8zMBX_yF{Da@~89gmfd}Vy*lIJco8~U%YMW~@gzro77 z6&aR1b+#iRGi=UevoDx-yp6n4N?uP*60ZGmmD{4XWn{0!i@erEZ@!|l^x#Iw?1~3z z*tf#@n>qx!mT5xyc1ge00GBk{dU=pv5YahuOr`=0WB0b@_k;^?N+&;=UoyOo9Ge~L z*1NVLc1}Y&vbu*jC8(Xf5Cy!*@KSTN1yKb`PDy#kyMHX2kWxG6vN$V4*JRaQv8@@5 zzttvI#}*mz9Ln(A1|P**x$=cG_-r?7?r@A0G|-8m)G=GMpJ*>tR7Jh^qXTdz!oQ%S zgY2dxnfQ@v2fk0LM7|@}7XA5j*Q6XFR(FP@!4ln*^Bf|M8IogagpWuPD0iNL%4QsK z3OS@0cSnzkOZ3d6glm-S#|>6Kxr?G;0Fh#M?T*mG+dY}jU>+1uRs&dUxf3+vQMnY{ znj02(kVrhs3(@V%E%>9Mu%%n=ZaezSyZ$h?_iBnG>9hQ^Yuq{XSYByui+8!&yEALr zdKZCK5>AZP?hK$7fEStp#wj}=DRLjp2DnzUH)HnlkcZ~P4FFvk>fs?_b7{&lfdU#M z;OP9QUmsLAm{}kLXCnOrXzu!DO{$?!Ig#H&hL~>kB%xV0qq98s3M@# zWTJ-BYUym)vkQ2f(btmbQU)N)_Js-GN6VFQ!jLWu%+U6;voi~r#m=||Y@;G))2 z=?ZnL9h#wXBzEbOxW9Vy2ABZ-1L4ELR!whO6w0fj3Y8m7BzXU6(T5BQ2h>cW_m71k z@*&-OFY7rQ8B~3!X@4jWoIw=O?L%~j3R?_$4jZxu=*=%2BM8YC+-rALx(K8v0{N#< zvG8-`g4uOL)d3qYLyCZa?M?US{_ZA`Ybk_}AODZKjLVOxP885anqv-Q3m4%dkR&iN zgN{O3Xw$fop(m=hPkSPT;SPK#KxV-S>GTcO;RBT{Z8x=dGwdu*-%Og^Hqp3*yLN3* zEV3iX(P4^uQ8cQ_6HhVUOyywMms);=%+)(leH&wkK3!btm#i~79LaCl=cM-6*pq$g zG{xj4?x+vfk^k;(wU}N~Ei%?_nu*o{qLtuqqhs z?xCpD2xNScoluc|A#+sm66)h(!%C+2hg^=o0iAQgW!hxOxpoca5_oQGX+VIyZmeQ_mi}c!UHj;hW|?I z83j4cJzD6?+R*GGVL0Iw=iH@2MveMwiShQ~4&$Ae;X^aA1|0M(a4WwBlKq)_5vT-_ zpz&fWL(z-`h^N+mX_#z=xJ-{DCLoDUbc>=$$&-6bs~~6G_aA97(f?|Kp{r@6QTNy} G?*9M*DAFYW diff --git a/src/lib/assets/harness/gemini.svg b/src/lib/assets/harness/gemini.svg deleted file mode 100644 index 06306a6..0000000 --- a/src/lib/assets/harness/gemini.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/src/lib/assets/harness/opencode.svg b/src/lib/assets/harness/opencode.svg deleted file mode 100644 index 5386e14..0000000 --- a/src/lib/assets/harness/opencode.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/components/HarnessIcon.svelte b/src/lib/components/HarnessIcon.svelte new file mode 100644 index 0000000..417ca84 --- /dev/null +++ b/src/lib/components/HarnessIcon.svelte @@ -0,0 +1,165 @@ + + + + {@html svg} + + + diff --git a/src/lib/components/HarnessMark.svelte b/src/lib/components/HarnessMark.svelte deleted file mode 100644 index 7ff0181..0000000 --- a/src/lib/components/HarnessMark.svelte +++ /dev/null @@ -1,122 +0,0 @@ - - - - {#if svg} - {@html svg} - {:else if image} - - {:else} - - {info.label} - - {/if} - - - diff --git a/src/lib/components/harness-icon.ts b/src/lib/components/harness-icon.ts new file mode 100644 index 0000000..d362bef --- /dev/null +++ b/src/lib/components/harness-icon.ts @@ -0,0 +1,131 @@ +/** + * Coding-agent harnesses kit-ui ships a glyph for. Ids and artwork mirror the + * icon sprite on the AgentsView documentation landing page, which draws one + * 24×24 monochrome glyph per harness. + */ +export type HarnessIconId = + | "claude" + | "openai" + | "gemini" + | "copilot" + | "vscode" + | "visualstudio" + | "cursor" + | "warp" + | "qwen" + | "deepseek" + | "mistral" + | "zed" + | "posit" + | "sourcegraph" + | "opencode" + | "antigravity" + | "kilo" + | "kimi" + | "kiro" + | "openhands" + | "zencoder" + | "snowflake" + | "goose" + | "grok" + | "hermes" + | "xiaomi" + | "openclaw" + | "pi" + | "poolside" + | "qoder" + | "roocode" + | "trae" + | "aider" + | "gptme" + | "omnigent" + | "zai" + | "commandcode" + | "codebuff" + | "forge" + | "iflow" + | "omp" + | "prime" + | "piebald" + | "tencent" + | "reasonix" + | "shelley" + | "workbuddy" + | "windsurf" + | "devin"; + +export interface HarnessIconInfo { + id: HarnessIconId; + /** Brand name; also the default accessible name of the icon. */ + label: string; + /** Agent products the AgentsView docs draw with this glyph. */ + agents: readonly string[]; +} + +/** Every harness with a glyph, in the order the AgentsView docs present them. */ +export const HARNESS_ICONS: readonly HarnessIconInfo[] = [ + { id: "claude", label: "Claude", agents: ["Claude Code", "OpenClaude", "Claude Cowork"] }, + { id: "openai", label: "OpenAI", agents: ["Codex"] }, + { id: "gemini", label: "Gemini", agents: ["Gemini"] }, + { id: "copilot", label: "Copilot", agents: ["Copilot"] }, + { id: "vscode", label: "VS Code", agents: ["VS Code Copilot"] }, + { id: "visualstudio", label: "Visual Studio", agents: ["Visual Studio Copilot"] }, + { id: "cursor", label: "Cursor", agents: ["Cursor", "Cursor IDE"] }, + { id: "warp", label: "Warp", agents: ["Warp"] }, + { id: "qwen", label: "Qwen", agents: ["Qwen Code", "QwenPaw"] }, + { id: "deepseek", label: "DeepSeek", agents: ["DeepSeek TUI", "DeepSeek Harness"] }, + { id: "mistral", label: "Mistral", agents: ["Mistral Vibe"] }, + { id: "zed", label: "Zed", agents: ["Zed"] }, + { id: "posit", label: "Posit", agents: ["Positron", "Posit Assistant"] }, + { id: "sourcegraph", label: "Sourcegraph", agents: ["Amp (historical)"] }, + { id: "opencode", label: "OpenCode", agents: ["OpenCode"] }, + { id: "antigravity", label: "Antigravity", agents: ["Antigravity"] }, + { id: "kilo", label: "Kilo", agents: ["Kilo", "Kilo (legacy)"] }, + { id: "kimi", label: "Kimi", agents: ["Kimi", "Kimi Work"] }, + { id: "kiro", label: "Kiro", agents: ["Kiro"] }, + { id: "openhands", label: "OpenHands", agents: ["OpenHands"] }, + { id: "zencoder", label: "Zencoder", agents: ["Zencoder"] }, + { id: "snowflake", label: "Snowflake", agents: ["Cortex Code"] }, + { id: "goose", label: "Goose", agents: ["Goose"] }, + { id: "grok", label: "Grok", agents: ["Grok"] }, + { id: "hermes", label: "Hermes", agents: ["Hermes"] }, + { id: "xiaomi", label: "Xiaomi", agents: ["MiMoCode"] }, + { id: "openclaw", label: "OpenClaw", agents: ["OpenClaw"] }, + { id: "pi", label: "Pi", agents: ["Pi"] }, + { id: "poolside", label: "Poolside", agents: ["Poolside"] }, + { id: "qoder", label: "Qoder", agents: ["Qoder"] }, + { id: "roocode", label: "RooCode", agents: ["RooCode"] }, + { id: "trae", label: "Trae", agents: ["Trae", "TraeX"] }, + { id: "aider", label: "Aider", agents: ["Aider"] }, + { id: "gptme", label: "gptme", agents: ["gptme"] }, + { id: "omnigent", label: "Omnigent", agents: ["Omnigent"] }, + { id: "zai", label: "Z.ai", agents: ["ZCode"] }, + { id: "commandcode", label: "Command Code", agents: ["Command Code"] }, + { id: "codebuff", label: "Codebuff", agents: ["Codebuff", "Freebuff"] }, + { id: "forge", label: "Forge", agents: ["Forge"] }, + { id: "iflow", label: "iFlow", agents: ["iFlow"] }, + { id: "omp", label: "OhMyPi", agents: ["OhMyPi"] }, + { id: "prime", label: "Prime Agent", agents: ["Prime Agent"] }, + { id: "piebald", label: "Piebald", agents: ["Piebald"] }, + { id: "tencent", label: "Tencent", agents: ["QClaw"] }, + { id: "reasonix", label: "Reasonix", agents: ["Reasonix"] }, + { id: "shelley", label: "Shelley", agents: ["Shelley"] }, + { id: "workbuddy", label: "WorkBuddy", agents: ["WorkBuddy"] }, + { id: "windsurf", label: "Windsurf", agents: ["Windsurf"] }, + { id: "devin", label: "Devin", agents: ["Devin CLI"] }, +]; + +const BY_ID: ReadonlyMap = new Map( + HARNESS_ICONS.map((h) => [h.id, h]), +); + +/** Narrow an arbitrary string (a session's agent name, say) to a known harness glyph. */ +export function isHarnessIconId(value: string): value is HarnessIconId { + return BY_ID.has(value as HarnessIconId); +} + +export function harnessIconInfo(id: HarnessIconId): HarnessIconInfo { + const info = BY_ID.get(id); + if (!info) throw new Error(`unknown harness "${id}"`); + return info; +} diff --git a/src/lib/components/harness-mark.ts b/src/lib/components/harness-mark.ts deleted file mode 100644 index 2c0ea4d..0000000 --- a/src/lib/components/harness-mark.ts +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Coding-agent harnesses kit-ui ships wordmarks for. Ids mirror the asset - * names on the AgentsView documentation site, where the artwork was - * consolidated from. - */ -export type HarnessId = - | "claude-code" - | "codex" - | "gemini" - | "copilot" - | "cursor" - | "opencode" - | "openhands" - | "amp" - | "vscode-copilot" - | "positron" - | "openclaw" - | "pi" - | "iflow" - | "zencoder" - | "kimi" - | "warp" - | "hermes" - | "cortex-code" - | "kiro" - | "forge"; - -export interface HarnessInfo { - id: HarnessId; - /** Display name; also the default accessible name of the mark. */ - label: string; - /** - * Brand colour of the typeset wordmark. Only harnesses without vector or - * raster artwork carry one — the others keep their colour in the asset. - */ - color?: string; - /** - * Optical correction so every mark reads at the same visual weight for a - * given `size`. Mirrors the agentsview docs matrix, which draws Gemini and - * Forge taller than the rest (30 / 28 px against a 22 px baseline). The - * docs also double Codex, but that only suits its wide grid cells; inline - * the OpenAI wordmark already sits level with the others at 1×. - */ - opticalScale?: number; -} - -/** Every harness with a mark, in the order the agentsview docs present them. */ -export const HARNESSES: readonly HarnessInfo[] = [ - { id: "claude-code", label: "Claude Code" }, - { id: "codex", label: "Codex" }, - { id: "gemini", label: "Gemini", opticalScale: 30 / 22 }, - { id: "copilot", label: "Copilot" }, - { id: "cursor", label: "Cursor" }, - { id: "opencode", label: "OpenCode" }, - { id: "openhands", label: "OpenHands", color: "#E9590C" }, - { id: "amp", label: "amp", color: "#E8604C" }, - { id: "vscode-copilot", label: "VS Code Copilot", color: "#0098B8" }, - { id: "positron", label: "Positron", color: "#447099" }, - { id: "openclaw", label: "OpenClaw", color: "#E68A2E" }, - { id: "pi", label: "Pi", color: "#6366f1" }, - { id: "iflow", label: "iFlow", color: "#0ea5e9" }, - { id: "zencoder", label: "Zencoder", color: "#f87171" }, - { id: "kimi", label: "Kimi", color: "#ec4899" }, - { id: "warp", label: "Warp", color: "#01A4A4" }, - { id: "hermes", label: "Hermes", color: "#7c3aed" }, - { id: "cortex-code", label: "Cortex Code", color: "#29B5E8" }, - { id: "kiro", label: "Kiro", color: "#8845f4" }, - { id: "forge", label: "Forge", opticalScale: 28 / 22 }, -]; - -const BY_ID: ReadonlyMap = new Map(HARNESSES.map((h) => [h.id, h])); - -/** Narrow an arbitrary string (a session's agent name, say) to a known harness. */ -export function isHarnessId(value: string): value is HarnessId { - return BY_ID.has(value as HarnessId); -} - -export function harnessInfo(id: HarnessId): HarnessInfo { - const info = BY_ID.get(id); - if (!info) throw new Error(`unknown harness "${id}"`); - return info; -} diff --git a/src/lib/index.ts b/src/lib/index.ts index 2e82076..68ed0dc 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -39,14 +39,14 @@ export type { FilterDropdownItem, FilterDropdownSection } from "./components/Fil export { default as FindBar } from "./components/FindBar.svelte"; export { default as FitStages } from "./components/FitStages.svelte"; export { default as FlashBanner } from "./components/FlashBanner.svelte"; -export { default as HarnessMark } from "./components/HarnessMark.svelte"; +export { default as HarnessIcon } from "./components/HarnessIcon.svelte"; export { - HARNESSES, - harnessInfo, - isHarnessId, - type HarnessId, - type HarnessInfo, -} from "./components/harness-mark.js"; + HARNESS_ICONS, + harnessIconInfo, + isHarnessIconId, + type HarnessIconId, + type HarnessIconInfo, +} from "./components/harness-icon.js"; export { default as IconButton } from "./components/IconButton.svelte"; export type { IconButtonSize, IconButtonTone } from "./components/IconButton.svelte"; export { default as ImagePreview } from "./components/ImagePreview.svelte"; diff --git a/tests/browser/harness-icon.spec.ts b/tests/browser/harness-icon.spec.ts new file mode 100644 index 0000000..e8ee490 --- /dev/null +++ b/tests/browser/harness-icon.spec.ts @@ -0,0 +1,55 @@ +import { expect, test } from "@playwright/test"; +import { gotoPage, setSlider, setTheme } from "./helpers"; + +const GALLERY = '[data-demo="harness-icon-gallery"]'; + +test("every registered harness renders a glyph", async ({ page }) => { + await gotoPage(page, "harness-icon"); + const gallery = page.locator(GALLERY); + + await expect(gallery.locator(".kit-harness-icon")).toHaveCount(49); + await expect(gallery.locator(".kit-harness-icon > svg")).toHaveCount(49); + + await expect(gallery.getByRole("img", { name: "Claude" })).toBeVisible(); + await expect(gallery.getByRole("img", { name: "OpenAI" })).toBeVisible(); + await expect(gallery.getByRole("img", { name: "Forge" })).toBeVisible(); +}); + +test("glyphs follow the current text colour in both themes", async ({ page }) => { + await gotoPage(page, "harness-icon"); + const openai = page.locator(`${GALLERY} .kit-harness-icon--openai`); + + for (const dark of [false, true]) { + await setTheme(page, { dark }); + const { fill, color } = await openai.evaluate((el) => ({ + fill: getComputedStyle(el.querySelector("path")!).fill, + color: getComputedStyle(el).color, + })); + expect(fill).toBe(color); + } +}); + +test("size sets a square box", async ({ page }) => { + await gotoPage(page, "harness-icon"); + const claude = page.locator('[data-demo="harness-icon-sized"] .kit-harness-icon--claude'); + const hermes = page.locator('[data-demo="harness-icon-sized"] .kit-harness-icon--hermes'); + + await setSlider(page.locator('[data-demo="harness-icon-size"]'), 32); + + for (const icon of [claude, hermes]) { + const box = (await icon.boundingBox())!; + expect(Math.round(box.height)).toBe(32); + expect(Math.round(box.width)).toBe(32); + } +}); + +test("decorative icons are hidden from assistive tech", async ({ page }) => { + await gotoPage(page, "harness-icon"); + const inline = page.locator('[data-demo="harness-icon-inline"] .kit-harness-icon'); + + await expect(inline).toHaveCount(2); + for (const icon of await inline.all()) { + await expect(icon).toHaveAttribute("aria-hidden", "true"); + await expect(icon).not.toHaveAttribute("role", "img"); + } +}); diff --git a/tests/browser/harness-mark.spec.ts b/tests/browser/harness-mark.spec.ts deleted file mode 100644 index c337241..0000000 --- a/tests/browser/harness-mark.spec.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { expect, test } from "@playwright/test"; -import { gotoPage, setSlider, setTheme } from "./helpers"; - -const GALLERY = '[data-demo="harness-gallery"]'; - -test("every registered harness renders a mark", async ({ page }) => { - await gotoPage(page, "harness-mark"); - const gallery = page.locator(GALLERY); - - await expect(gallery.locator(".kit-harness-mark")).toHaveCount(20); - await expect(gallery.locator(".kit-harness-mark > svg")).toHaveCount(6); - await expect(gallery.locator(".kit-harness-mark > img")).toHaveCount(1); - await expect(gallery.locator(".kit-harness-mark__text")).toHaveCount(13); - - await expect(gallery.getByRole("img", { name: "Claude Code" })).toBeVisible(); - await expect(gallery.getByRole("img", { name: "Forge" }).locator("img")).toHaveAttribute( - "src", - /^data:image\/png/, - ); - await expect(gallery.getByRole("img", { name: "Hermes" })).toHaveText("Hermes"); -}); - -test("vector marks follow the current text colour in both themes", async ({ page }) => { - await gotoPage(page, "harness-mark"); - const codex = page.locator(`${GALLERY} .kit-harness-mark--codex`); - - for (const dark of [false, true]) { - await setTheme(page, { dark }); - const { fill, color } = await codex.evaluate((el) => ({ - fill: getComputedStyle(el.querySelector("path")!).fill, - color: getComputedStyle(el).color, - })); - expect(fill).toBe(color); - } -}); - -test("size sets the height and width follows the artwork", async ({ page }) => { - await gotoPage(page, "harness-mark"); - const codex = page.locator('[data-demo="harness-sized"] .kit-harness-mark--codex'); - const hermes = page.locator('[data-demo="harness-sized"] .kit-harness-mark--hermes'); - - await setSlider(page.locator('[data-demo="harness-size"]'), 32); - - for (const mark of [codex, hermes]) { - const box = (await mark.boundingBox())!; - expect(Math.round(box.height)).toBe(32); - expect(box.width).toBeGreaterThan(box.height); - } -}); - -test("mono flattens brand colours to currentColor", async ({ page }) => { - await gotoPage(page, "harness-mark"); - const claude = page.locator('[data-demo="harness-sized"] .kit-harness-mark--claude-code'); - const hermes = page.locator('[data-demo="harness-sized"] .kit-harness-mark--hermes'); - - const asteriskFill = (mark: typeof claude) => - mark.evaluate((el) => getComputedStyle(el.querySelector("path")!).fill); - const textColor = (mark: typeof hermes) => - mark.evaluate((el) => getComputedStyle(el.querySelector("span")!).color); - const rootColor = (mark: typeof claude) => mark.evaluate((el) => getComputedStyle(el).color); - - expect(await asteriskFill(claude)).not.toBe(await rootColor(claude)); - expect(await textColor(hermes)).not.toBe(await rootColor(hermes)); - - await page.locator('[data-demo="harness-mono"] input').check(); - - expect(await asteriskFill(claude)).toBe(await rootColor(claude)); - expect(await textColor(hermes)).toBe(await rootColor(hermes)); -}); - -test("decorative marks are hidden from assistive tech", async ({ page }) => { - await gotoPage(page, "harness-mark"); - const inline = page.locator('[data-demo="harness-inline"] .kit-harness-mark'); - - await expect(inline).toHaveCount(2); - for (const mark of await inline.all()) { - await expect(mark).toHaveAttribute("aria-hidden", "true"); - await expect(mark).not.toHaveAttribute("role", "img"); - } -});