` 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}`}
+>
+
+ {#each HARNESS_ICONS as { id } (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}`}
->
-
- {#each HARNESSES as { id } (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 934144d..0000000
Binary files a/src/lib/assets/harness/forge.png and /dev/null differ
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");
- }
-});