Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Dark mode: add the `dark` class to `<html>` (`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 |
Expand Down
2 changes: 1 addition & 1 deletion docs/checking.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<img>`/`<svg>` markup; found in review |
| HarnessIcon | no rule — inline logos overlap ordinary `<img>`/`<svg>` 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) |
Expand Down
103 changes: 103 additions & 0 deletions docs/components/harness-icon.md
Original file line number Diff line number Diff line change
@@ -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
<script lang="ts">
import { HARNESS_ICONS, HarnessIcon, isHarnessIconId } from "@kenn-io/kit-ui";
</script>

<HarnessIcon harness="claude" />
<HarnessIcon harness="openai" size={20} />

{#if isHarnessIconId(session.agent)}
<HarnessIcon harness={session.agent} size={14} decorative />
{/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 `<symbol>` 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.
88 changes: 0 additions & 88 deletions docs/components/harness-mark.md

This file was deleted.

4 changes: 2 additions & 2 deletions src/demo/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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 },
Expand Down
131 changes: 131 additions & 0 deletions src/demo/pages/HarnessIconDemo.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<script lang="ts">
import { HARNESS_ICONS, HarnessIcon } from "../../lib/index.js";
import DemoSection from "../DemoSection.svelte";

let size = $state(24);
</script>

<DemoSection
title="Every harness"
description="One monochrome glyph per coding-agent harness, painted in currentColor so it follows the theme like a lucide icon."
code={`<HarnessIcon harness="claude" />
<HarnessIcon harness="openai" size={20} />

{#each HARNESS_ICONS as { id } (id)}
<HarnessIcon harness={id} />
{/each}`}
>
<div class="gallery" data-demo="harness-icon-gallery">
{#each HARNESS_ICONS as { id } (id)}
<div class="gallery__cell">
<HarnessIcon harness={id} size={24} />
<code class="gallery__id">{id}</code>
</div>
{/each}
</div>
</DemoSection>

<DemoSection
title="Size"
description="size sets both dimensions in px; every glyph is square."
code={`<HarnessIcon harness="gemini" size={32} />`}
>
<div class="controls">
<label class="controls__field">
size
<input
type="range"
min="12"
max="48"
step="1"
bind:value={size}
data-demo="harness-icon-size"
/>
<span>{size}px</span>
</label>
</div>
<div class="row" data-demo="harness-icon-sized">
<HarnessIcon harness="claude" {size} />
<HarnessIcon harness="openai" {size} />
<HarnessIcon harness="gemini" {size} />
<HarnessIcon harness="opencode" {size} />
<HarnessIcon harness="hermes" {size} />
<HarnessIcon harness="forge" {size} />
</div>
</DemoSection>

<DemoSection
title="Inline and muted"
description="Icons sit on the text baseline and inherit colour, so a muted row dims them with it. Pass decorative when the surrounding text already names the harness."
code={`<span class="muted">
Session ran in <HarnessIcon harness="cursor" size={14} decorative /> Cursor
</span>`}
>
<p class="inline" data-demo="harness-icon-inline">
Session ran in <HarnessIcon harness="cursor" size={14} decorative /> Cursor, then handed off to
<HarnessIcon harness="openai" size={14} decorative /> Codex.
</p>
<p class="inline inline--muted">
<HarnessIcon harness="claude" size={14} /> ·
<HarnessIcon harness="copilot" size={14} /> ·
<HarnessIcon harness="kimi" size={14} /> ·
<HarnessIcon harness="gemini" size={14} />
</p>
</DemoSection>

<style>
.gallery {
display: flex;
flex-wrap: wrap;
gap: var(--space-4);
}

.gallery__cell {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: var(--space-2);
min-width: 120px;
padding: var(--space-3);
border: 1px solid var(--border-muted);
border-radius: var(--radius-md);
}

.gallery__id {
font-size: var(--font-size-xs);
color: var(--text-muted);
}

.controls {
display: flex;
flex-wrap: wrap;
gap: var(--space-4);
margin-bottom: var(--space-4);
}

.controls__field {
display: inline-flex;
align-items: center;
gap: var(--space-2);
font-size: var(--font-size-sm);
}

.row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--space-5);
}

.inline {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--space-2);
margin: 0 0 var(--space-3);
}

.inline--muted {
color: var(--text-muted);
}
</style>
Loading
Loading