# v0.2.2 release notes

Patch release on top of v0.2.1. **No breaking changes.** Three themes:

1. **Prompt registry** — every LLM system prompt the SDK ships is now overridable per-locale from one API. Host localises Japanese/Spanish/French without patching any subsystem.
2. **`autoInstall()` one-line install** — a zero-config on-ramp that returns a fully-constructed `DotDotDuck` with sensible defaults (locale detection, demo LLM stub, bundled duck sprites).
3. **Mascot upgrade** — new HERO greeting capsule (FAB morphs into a horizontal pill), 8 mascot animations tunable via CSS variables, and SDK ships duck sprites out of the box (no more `--dddk-*-url` wiring for basic install).

Plus fixes for the palette footer disappearing on touch-capable laptops, InlineAgent's redundant "processing" indicator, and the Dwell swim overlay URL scoping bug.

## TL;DR

- **`dddk.prompts`** — new `PromptRegistry` on every `DotDotDuck` instance. `dddk.prompts.list()` enumerates every SDK-shipped prompt id; `dddk.prompts.override(id, locale, render)` swaps one per-locale; `dddk.prompts.append(id, extra)` stitches site-wide reminders on the end.
- **`autoInstall(overrides?)`** — new factory. `import { autoInstall } from '@perhapxin/dddk'; const dddk = autoInstall();` boots everything with a demo LLM, browser-locale detection, and default duck sprites. Pass overrides to fill in real host wiring.
- **HERO greeting capsule** — first-visit FAB morphs into a horizontal yellow-translucent pill with the mascot on the right and greeting text on the left. All 12+ visual props are `--dddk-fab-hero-*` variables; hosts theme by declaring on `:root`.
- **8 mascot animations var-tunable** — `--dddk-avatar-swim-duration`, `--dddk-avatar-bob-duration`, `--dddk-indicator-swim-duration`, `--dddk-indicator-wave-duration`, `--dddk-brand-mark-bob-duration`, `--dddk-dwell-avatar-bob-duration`, `--dddk-dwell-chill-breath-duration`, plus the existing `--dddk-fab-hero-transition-ms` / `--dddk-swim-duration`. Hosts throttle for slower brands or hush for reduced motion.
- **7 duck sprites bundled** — SDK now ships `neutral.png`, `swim-side.png`, `hero-greet.png`, `chill-shades.png`, `swim-cycle.png`, `logo.png`, and `cursor.png` under `dist/duck/`, wired via `--dddk-*-url` defaults in `tokens.css`. Hosts don't need to serve their own PNGs unless they want to swap the mascot.
- **New WebAgent cursor sprite** — the SVG pointer glyph (small yellow triangle + duck head) is replaced by a bitmap sprite: a duck riding a paper airplane, sharp origami tip at the top-left as the click origin. Swap via `--dddk-cursor-url`. Scroll + reading modes keep the SVG glyphs (still themeable via `--webagent-cursor-{fill,stroke}`).
- **Palette "Powered by dotdotduck" footer** — brand mark on the right (uses `--dddk-brand-mark-url`, defaults to the shipped logo); left side flips between keyboard hints (`↑ ↓ ⏎ esc`) and touch hints (`tap to pick / tap outside to close`) based on `(hover: none) and (pointer: coarse)`. Fixes the "footer vanished on my touchscreen laptop" bug.
- **InlineAgent no longer double-signals** — the redundant "processing" pill is gone; the streaming diff overlay IS the loading state, showing it twice added noise and (worse) sometimes lingered after the diff finished due to a race between clear-on-first-delta and diff-render.
- **Dwell swim URL fixed** — swim overlay's spritesheet URL now goes through `--dddk-swim-cycle-url` (root-absolute-safe) instead of a hard-coded `/duck/swim-cycle.png`. Hosts serving from a subdirectory no longer lose the animation.

## What changed

### Prompt registry (the headline for teams doing multi-language product)

Prompts previously lived inline in ~10 subsystem modules (webagent narrator, planner, InlineAgent, markdown-edit, translator, STT cleanup, Dwell classifier). A host that wanted "Japanese prompts" or "no persona line" had to spelunk each module.

v0.2.2 consolidates every default behind stable IDs on `dddk.prompts`:

```ts
dddk.prompts.list();
// → ['dwell-classify.system', 'inline-edit.system', 'markdown-edit.system',
//    'planner.system', 'translate.system', 'voice-cleanup.system',
//    'webagent-cot.system', 'webagent-narrator.system', 'task.system']

// Override one prompt for one locale
dddk.prompts.override('inline-edit.system', 'ja', () => `あなたはインライン編集アシスタントです…`);

// Append a site-wide reminder to the narrator (runs for every locale)
dddk.prompts.append('webagent-narrator.system', () => 'Never mention pricing. Always cite sources.');

// Reset a prompt back to the SDK default
dddk.prompts.reset('inline-edit.system');
```

Fallback order: `override(id, locale)` → `override(id, 'en')` → SDK default. Hosts localise EN + zh-TW + ja without touching the defaults for locales they don't care about.

Full guide: [prompts.md](./prompts.md).

### `autoInstall()`

Zero-config on-ramp:

```ts
import { autoInstall } from '@perhapxin/dddk';
import '@perhapxin/dddk/styles.css';

const dddk = autoInstall();
// Mounts palette + subtitle + Dwell + FAB with SDK-default duck sprites.
// Locale auto-detected from navigator.language.
// LLM defaults to a "wire your real provider" demo stub — swap in when ready.
```

Real integration passes overrides on top:

```ts
const dddk = autoInstall({
  llm: myOpenAIProvider,
  siteName: 'Acme',
  agentName: 'Rex',
  paletteCommands: myCommands,
  brand: { voice: 'friendly' },
});
```

`autoInstall(overrides)` and `new DotDotDuck({ ...defaults, ...overrides })` are functionally equivalent — the factory just picks the defaults. See [auto-install.md](./auto-install.md).

### HERO greeting capsule (`showHeroGreeting`)

New greeting shape: on first visit the small circular FAB **morphs horizontally into a yellow-translucent capsule** — mascot stays on the right (its original corner), greeting text fills the space that opens up on the left, all-CSS transition (no separate bubble element). Every visual prop is a variable: width, height, radius, background, shadow, transition, face size, padding — full list in [mascot.md](./mascot.md).

Hosts fire it from wherever their onboarding flow lives:

```ts
await dddk.mobile.showHeroGreeting(
  "Hi! I'm Rex. Tap me or press Ctrl+K to open the palette.",
  { autoDismissMs: 20000 },
);
```

Reverses on dismiss (capsule contracts back to circle, face reverts to the previous state).

### 8 mascot animations tunable

Previously only 2 mascot animation timings were overridable via CSS variables (`--dddk-fab-hero-transition-ms`, `--dddk-swim-duration`). v0.2.2 promotes 8 more that hosts actually want to tune for brand cadence:

```css
:root {
  /* Bar-attached avatar */
  --dddk-avatar-swim-duration: 620ms;
  --dddk-avatar-bob-duration: 2.6s;
  /* Thinking indicator */
  --dddk-indicator-swim-duration: 1.5s;
  --dddk-indicator-swim-pip-duration: 1s;
  --dddk-indicator-wave-duration: 0.9s;
  /* Palette brand mark */
  --dddk-brand-mark-bob-duration: 3.2s;
  /* Dwell corner mascot */
  --dddk-dwell-avatar-bob-duration: 2s;
  --dddk-dwell-chill-breath-duration: 3s;
}
```

To kill an animation entirely, target the selector directly (`[data-dddk-ui="palette-footer-brand-mark"] { animation: none; }`). Under `prefers-reduced-motion: reduce` the SDK already switches every mascot loop off — the variables above are for brand tuning, not accessibility.

### Bundled duck sprites

The SDK now ships six PNGs under `dist/duck/`, wired via `--dddk-*-url` defaults on `:root`:

| Variable | Default | Where it's used |
|---|---|---|
| `--dddk-avatar-url` | `./duck/neutral.png` | Subtitle bar avatar, Dwell chip |
| `--dddk-swim-url` | `./duck/swim-side.png` | Proactive swim indicator |
| `--dddk-hero-url` | `./duck/hero-greet.png` | HERO greeting (optional big-reveal sprite) |
| `--dddk-chill-url` | `./duck/chill-shades.png` | Dwell frame corner mascot |
| `--dddk-swim-cycle-url` | `./duck/swim-cycle.png` | Dwell top-edge swim spritesheet (8 frames) |
| `--dddk-brand-mark-url` | `./duck/logo.png` | Palette "Powered by dotdotduck" mark |
| `--dddk-cursor-url` | `./duck/cursor.png` | WebAgent synthetic cursor (paper-airplane + duck) |

Hosts wanting to swap the mascot for their own brand character just redeclare on `:root`:

```css
:root {
  --dddk-avatar-url: url('/my-brand/mascot.png');
  --dddk-swim-url: url('/my-brand/swim.png');
  /* Keep --dddk-brand-mark-url as-is if you want to keep the dotdotduck
     attribution on the palette footer. */
}
```

**Sub-path caveat** — Chrome resolves `url()` in CSS custom properties relative to the DOCUMENT (not the CSS file) when the variable is applied via a JS-injected `<style>` (which is how Dwell / palette style shells load). If your app is served from a subdirectory like `/tools/dddk/`, the SDK's default `./duck/...` breaks. Fix: override with root-absolute paths on your app's `:root` — `--dddk-swim-cycle-url: url('/my-app/duck/swim-cycle.png');`. Detailed writeup in [theming.md](./theming.md).

### Palette footer

Two changes:

1. **Brand mark** on the right shows the shipped logo icon (via new `--dddk-brand-mark-url`, defaults to `./duck/logo.png` bundled in `dist/duck/`). Falls back to `--dddk-avatar-url` if the brand mark is unset — hosts overriding the avatar don't accidentally lose the palette attribution.
2. **Left-side hints adapt to input mode**: keyboard hints (`↑ ↓ navigate · ↵ select · esc close`) on hover-capable devices; touch hints (`Tap a row to pick · Tap outside to close`) on `(hover: none) and (pointer: coarse)`. Previous v0.2.1 media query used `(pointer: coarse)` alone, which caught touchscreen laptops with real trackpads and hid the footer entirely.

### InlineAgent stops double-signalling

The "processing" subtitle indicator that fired on `runInstruction` and prefix-submit paths is removed. Rationale: the streaming diff overlay itself is the loading state — text streams in chunk by chunk with visible strikeout / new-text — and a redundant pill at bottom-of-screen sometimes lingered after the diff completed because clear-on-first-delta could race the diff finish. Errors still surface via `subtitle.show({ type: 'info' })` in the catch branch.

## Bug fixes

- **Palette footer disappearing on touch-capable laptops** — media query was `(pointer: coarse)` alone, which matches trackpads on modern touch laptops. Tightened to `(hover: none) and (pointer: coarse)` so only truly touch-primary devices flip the hints.
- **Dwell swim overlay 404 on subdirectory hosts** — previously hard-coded `url('/duck/swim-cycle.png')`. Now goes through `--dddk-swim-cycle-url` with a shipped default; hosts override for subdirectory paths.
- **Palette footer text vanishing after locale change** — `refreshFooter()` innerHTML-replaced the strip, wiping the brand mark that the initial mount had appended separately. Both paths now converge on `buildFooterHTML()`.
- **12 undeclared CSS variables** — `--dddk-fab-hero-*`, `--dddk-indicator-duck-url`, `--dddk-dwell-swim-*`, etc. were read via `var()` throughout the SDK but never declared in `tokens.css` (no discoverable tuning surface for hosts). All declared with sensible defaults.

## Contributor changes

- New `src/prompts/` directory (`registry.ts` + `defaults.ts`) as the single source of truth for prompt IDs. Adding a new prompt: add to `PROMPT_IDS` enum → register in `defaults.ts` → resolve at callsite via `promptRegistry.render(id, ctx, locale)`.
- Three inline prompt consts extracted to their own files (`modules/immersive-translate/prompt.ts`, `modules/voice/prompt.ts`, `triggers/dwell/prompt.ts`) so the registry can import them without pulling in the subsystem runtime.
- `tsup.config.ts` `onSuccess` hook now copies `src/duck/*.png` → `dist/duck/`, and rewrites `url('./duck/...')` → `url('../duck/...')` for the sub-path `dist/styles/tokens.css` variant so both the bundled `dist/styles.css` and sub-path imports resolve correctly.

## Migrating from v0.2.1

Purely additive — no callsite changes required. See [migrating.md](./migrating.md) for optional adoption of the new APIs.
