|
| 1 | +# a2ui Icon — render real Material Symbols (not the name as text) — Design |
| 2 | + |
| 3 | +**Status:** Approved direction (2026-06-20, brainstormed). |
| 4 | + |
| 5 | +## Problem |
| 6 | +The a2ui catalog `Icon` component (`libs/chat/src/lib/a2ui/catalog/icon.component.ts`) renders the icon **name as literal text** — a spec with `Icon { name: "trending_up" }` shows the string "trending_up" instead of a glyph. |
| 7 | + |
| 8 | +## Alignment |
| 9 | +The canonical A2UI protocol/renderer uses **Material Symbols**: the Icon `name` is a Material Symbols identifier (`check`, `trending_up`, `star`, …), rendered via the Material Symbols font where the name is a *ligature*. Aligning the Angular renderer means doing the same — render the name through the Material Symbols Outlined font. |
| 10 | + |
| 11 | +## Decisions (settled via brainstorm) |
| 12 | +- **Render Material Symbols** (protocol-aligned) — the component outputs the name into a `material-symbols-outlined`-classed span so the font renders the glyph. `currentColor` (theme-aware, matches the repo's inline-SVG convention); size via `font-size`. |
| 13 | +- **Demos load the font; document for consumers.** `@threadplane/chat` does **not** inject any CDN `<link>` at runtime. The a2ui demo apps load the Material Symbols Outlined stylesheet in their `index.html`; the lib README documents the requirement (standard for icon fonts). |
| 14 | +- **No backend prompt change.** The LLM keeps emitting Material Symbols names. Valid names render; unknown / not-yet-loaded names fall back gracefully (browser default) — acceptable per the "keep open, rely on fallback" choice. |
| 15 | + |
| 16 | +## Changes |
| 17 | + |
| 18 | +### 1. `libs/chat/src/lib/a2ui/catalog/icon.component.ts` |
| 19 | +- Add `material-symbols-outlined` to the span's class list (alongside `a2ui-icon`); keep rendering `{{ effectiveName() }}` (the ligature). The global Material Symbols stylesheet (loaded by the host) styles `.material-symbols-outlined`; the component's own `.a2ui-icon` style sets `color: currentColor`, line-height, and the `font-size` from `size()` (default 1.125rem). Render nothing when there is no name. |
| 20 | +- Keep all existing inputs (`name`/`icon` alias, `size`, framework inputs) unchanged. |
| 21 | + |
| 22 | +### 2. Demo apps — load the font |
| 23 | +Add to the `<head>` of each a2ui demo's `index.html`: |
| 24 | +```html |
| 25 | +<link rel="preconnect" href="https://fonts.googleapis.com" /> |
| 26 | +<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> |
| 27 | +<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" /> |
| 28 | +``` |
| 29 | +Files: `examples/chat/angular/src/index.html`, `examples/ag-ui/angular/src/index.html`, `cockpit/chat/a2ui/angular/src/index.html`, `cockpit/ag-ui/a2ui/angular/src/index.html`. |
| 30 | + |
| 31 | +### 3. Docs |
| 32 | +- `libs/chat/README.md` (and/or the a2ui getting-started doc): a short "Icons" note — the a2ui `Icon` component renders Material Symbols; include the Material Symbols Outlined stylesheet in your app's `<head>` for glyphs to render; names are Material Symbols identifiers. |
| 33 | + |
| 34 | +### 4. Tests |
| 35 | +- `icon.component.spec.ts`: assert the rendered span carries the `material-symbols-outlined` class and the icon name as text content (the ligature), and applies `font-size` from `size`. (Glyph rendering itself needs the font + a browser; covered by the visual smoke.) |
| 36 | + |
| 37 | +## Verification |
| 38 | +- `nx run-many -t test lint build --projects=chat` — green. |
| 39 | +- Build the 4 a2ui demo apps — green. |
| 40 | +- **Visual smoke**: serve one a2ui demo (e.g. cockpit/ag-ui/a2ui or examples/chat a2ui mode) and confirm a spec with icons renders **glyphs** (not the raw names); screenshot. (Live LLM optional — a static spec/fixture with an `Icon` suffices.) |
| 41 | +- PR + auto-merge + watcher. |
| 42 | + |
| 43 | +## Risks |
| 44 | +- **FOUC / fallback:** before the font loads, the ligature text shows briefly; for an unknown name the browser shows tofu/nothing. Acceptable per the agreed fallback behavior; the `a2ui-icon` style can `overflow:hidden`/fixed-box to limit layout shift. |
| 45 | +- **No e2e text assertions on icon names** were found, so switching to glyphs won't break existing e2e. |
| 46 | +- Library stays CDN-free; only demos add the font link. |
0 commit comments