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
48 changes: 48 additions & 0 deletions libs/chat/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,51 @@ Each runtime adapter extracts citations into the `Message.citations` array:
- **AG-UI** — reads from STATE_DELTA at JSON Pointer `/citations/{messageId}`

The `CitationsResolverService` is provided to query citations in message-first or markdown-fallback order.

## A2UI surface theming

`<a2ui-surface>` declares ~50 internal `--a2ui-*` tokens at `:host` with dark-theme defaults (spacing, typography, shape radius, focus ring, motion, elevation, color). Catalog components consume them via `var(--a2ui-*)`. Override at `:root` to retheme.

### Agent-driven theming (v1 wire format)

Agents control exactly two knobs per the canonical A2UI v1 spec, set via `beginRendering.styles`:

- `font` — primary font family
- `primaryColor` — hex `#RRGGBB`

Both flow through to the rendered surface as inline styles on `<a2ui-surface>` (highest specificity), overriding any consumer `:root` defaults for the lifetime of that surface.

### Built-in theme presets

Four CSS files ship in the package, each declaring `:root` overrides for the relevant tokens:

```css
/* In your global stylesheet */
@import '@ngaf/chat/themes/default-dark.css'; /* lib defaults, explicit */
@import '@ngaf/chat/themes/default-light.css'; /* neutral light, blue accent */
@import '@ngaf/chat/themes/material-dark.css'; /* Material Design 3 dark */
@import '@ngaf/chat/themes/material-light.css'; /* Material Design 3 light */
```

Material presets map [Material Design 3 color tokens](https://m3.material.io/styles/color/the-color-system/tokens) to the `--a2ui-*` vocabulary — no `@angular/material` runtime dep, just CSS custom-property declarations.

### Custom themes

Override any subset of the ~50 tokens at `:root`:

```css
:root {
--a2ui-primary: #FF6B35; /* brand orange */
--a2ui-shape-medium: 4px; /* sharper corners */
--a2ui-spacing-3: 16px; /* roomier layouts */
}
```

The token surface:
- **Color** — `--a2ui-primary`, `--a2ui-on-primary`, `--a2ui-secondary`, `--a2ui-surface`, `--a2ui-on-surface`, `--a2ui-surface-variant`, `--a2ui-on-surface-variant`, `--a2ui-outline`, `--a2ui-outline-variant`, `--a2ui-error`, `--a2ui-on-error`, `--a2ui-scrim`
- **Spacing** — `--a2ui-spacing-1` (4px) through `--a2ui-spacing-7` (40px)
- **Typography** — `--a2ui-typography-{h1..h5,body,caption,label}-{size,weight,line-height}`
- **Shape** — `--a2ui-shape-{extra-small,small,medium,large,extra-large}`
- **Focus ring** — `--a2ui-focus-ring-color`, `--a2ui-focus-ring-width`
- **Motion** — `--a2ui-motion-duration-{short,medium,long}`, `--a2ui-motion-easing-{standard,emphasized}`
- **Elevation** — `--a2ui-elevation-{0,1,2,3,4,5}` (box-shadow tokens)
3 changes: 2 additions & 1 deletion libs/chat/ng-package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
},
"allowedNonPeerDependencies": ["@cacheplane/partial-json", "@cacheplane/partial-markdown"],
"assets": [
{ "input": "src/lib/styles", "glob": "chat.css", "output": "." }
{ "input": "src/lib/styles", "glob": "chat.css", "output": "." },
{ "input": "src/themes", "glob": "*.css", "output": "themes" }
]
}
6 changes: 5 additions & 1 deletion libs/chat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@
"types": "./testing.d.ts",
"default": "./fesm2022/ngaf-chat-testing.mjs"
},
"./chat.css": "./chat.css"
"./chat.css": "./chat.css",
"./themes/default-dark.css": "./themes/default-dark.css",
"./themes/default-light.css": "./themes/default-light.css",
"./themes/material-dark.css": "./themes/material-dark.css",
"./themes/material-light.css": "./themes/material-light.css"
},
"dependencies": {
"@cacheplane/partial-json": ">=0.1.1 <0.3.0",
Expand Down
32 changes: 32 additions & 0 deletions libs/chat/src/themes/default-dark.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* @ngaf/chat — default dark theme
* SPDX-License-Identifier: MIT
*
* The lib's `:host` defaults on `<a2ui-surface>` already declare these
* exact values — this preset is a no-op @import for symmetry/docs and
* for consumers who want their `:root` to make the choice explicit.
*
* Usage in your global stylesheet:
* @import '@ngaf/chat/themes/default-dark.css';
*
* The agent's `beginRendering.styles.primaryColor` continues to override
* `--a2ui-primary` per surface (host inline-style binding, highest
* specificity).
*/
:root {
/* === Color (dark, blue accent) === */
--a2ui-primary: #4f8df5;
--a2ui-on-primary: #ffffff;
--a2ui-primary-hover: #6699f7;
--a2ui-secondary: #8a92a3;
--a2ui-on-secondary: #ffffff;
--a2ui-surface: #1a1d23;
--a2ui-on-surface: #ffffff;
--a2ui-surface-variant: rgba(255, 255, 255, 0.05);
--a2ui-on-surface-variant: rgba(255, 255, 255, 0.7);
--a2ui-outline: rgba(255, 255, 255, 0.1);
--a2ui-outline-variant: rgba(255, 255, 255, 0.05);
--a2ui-error: #f5524f;
--a2ui-on-error: #ffffff;
--a2ui-scrim: rgba(0, 0, 0, 0.6);
}
36 changes: 36 additions & 0 deletions libs/chat/src/themes/default-light.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* @ngaf/chat — default light theme
* SPDX-License-Identifier: MIT
*
* Neutral light palette with blue accent. Off-white surface (#FAFAFA)
* for friendlier contrast vs. pure white. Same blue accent (#4f8df5)
* as default-dark — the only theming decision is foreground/background
* inversion + outline lightening + softer elevation.
*
* Usage in your global stylesheet:
* @import '@ngaf/chat/themes/default-light.css';
*/
:root {
/* === Color (light, blue accent) === */
--a2ui-primary: #4f8df5;
--a2ui-on-primary: #ffffff;
--a2ui-primary-hover: #3b7be3;
--a2ui-secondary: #5b6275;
--a2ui-on-secondary: #ffffff;
--a2ui-surface: #fafafa;
--a2ui-on-surface: #1a1d23;
--a2ui-surface-variant: rgba(0, 0, 0, 0.04);
--a2ui-on-surface-variant: rgba(0, 0, 0, 0.6);
--a2ui-outline: rgba(0, 0, 0, 0.12);
--a2ui-outline-variant: rgba(0, 0, 0, 0.06);
--a2ui-error: #d33533;
--a2ui-on-error: #ffffff;
--a2ui-scrim: rgba(0, 0, 0, 0.4);

/* === Elevation (softer shadows on light backgrounds) === */
--a2ui-elevation-1: 0 1px 2px rgba(0, 0, 0, 0.08);
--a2ui-elevation-2: 0 2px 4px rgba(0, 0, 0, 0.10);
--a2ui-elevation-3: 0 4px 8px rgba(0, 0, 0, 0.12);
--a2ui-elevation-4: 0 8px 16px rgba(0, 0, 0, 0.14);
--a2ui-elevation-5: 0 16px 32px rgba(0, 0, 0, 0.16);
}
38 changes: 38 additions & 0 deletions libs/chat/src/themes/material-dark.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* @ngaf/chat — Material Design 3 dark theme
* SPDX-License-Identifier: MIT
*
* Maps Google's Material Design 3 dark color tokens (m3.material.io)
* into the @ngaf/chat token vocabulary. No `@angular/material` runtime
* dependency — pure CSS custom-property declarations.
*
* Color values from the Material Design 3 spec
* (https://m3.material.io/styles/color/the-color-system/tokens).
*
* Usage in your global stylesheet:
* @import '@ngaf/chat/themes/material-dark.css';
*/
:root {
/* === Color (M3 dark) === */
--a2ui-primary: #D0BCFF;
--a2ui-on-primary: #381E72;
--a2ui-primary-hover: #B69DF8;
--a2ui-secondary: #CCC2DC;
--a2ui-on-secondary: #332D41;
--a2ui-surface: #1C1B1F;
--a2ui-on-surface: #E6E1E5;
--a2ui-surface-variant: #49454F;
--a2ui-on-surface-variant: #CAC4D0;
--a2ui-outline: #938F99;
--a2ui-outline-variant: #49454F;
--a2ui-error: #F2B8B5;
--a2ui-on-error: #601410;
--a2ui-scrim: rgba(0, 0, 0, 0.6);

/* === Shape (M3 favors slightly larger radii) === */
--a2ui-shape-extra-small: 4px;
--a2ui-shape-small: 8px;
--a2ui-shape-medium: 12px;
--a2ui-shape-large: 16px;
--a2ui-shape-extra-large: 28px;
}
45 changes: 45 additions & 0 deletions libs/chat/src/themes/material-light.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* @ngaf/chat — Material Design 3 light theme
* SPDX-License-Identifier: MIT
*
* Maps Google's Material Design 3 light color tokens (m3.material.io)
* into the @ngaf/chat token vocabulary. No `@angular/material` runtime
* dependency — pure CSS custom-property declarations.
*
* Color values from the Material Design 3 spec
* (https://m3.material.io/styles/color/the-color-system/tokens).
*
* Usage in your global stylesheet:
* @import '@ngaf/chat/themes/material-light.css';
*/
:root {
/* === Color (M3 light) === */
--a2ui-primary: #6750A4;
--a2ui-on-primary: #FFFFFF;
--a2ui-primary-hover: #533F8E;
--a2ui-secondary: #625B71;
--a2ui-on-secondary: #FFFFFF;
--a2ui-surface: #FFFBFE;
--a2ui-on-surface: #1C1B1F;
--a2ui-surface-variant: #E7E0EC;
--a2ui-on-surface-variant: #49454F;
--a2ui-outline: #79747E;
--a2ui-outline-variant: #CAC4D0;
--a2ui-error: #B3261E;
--a2ui-on-error: #FFFFFF;
--a2ui-scrim: rgba(0, 0, 0, 0.4);

/* === Shape (M3 favors slightly larger radii) === */
--a2ui-shape-extra-small: 4px;
--a2ui-shape-small: 8px;
--a2ui-shape-medium: 12px;
--a2ui-shape-large: 16px;
--a2ui-shape-extra-large: 28px;

/* === Elevation (softer shadows on light backgrounds) === */
--a2ui-elevation-1: 0 1px 2px rgba(0, 0, 0, 0.08);
--a2ui-elevation-2: 0 2px 4px rgba(0, 0, 0, 0.10);
--a2ui-elevation-3: 0 4px 8px rgba(0, 0, 0, 0.12);
--a2ui-elevation-4: 0 8px 16px rgba(0, 0, 0, 0.14);
--a2ui-elevation-5: 0 16px 32px rgba(0, 0, 0, 0.16);
}
Loading