# v0.2.2 釋出說明

v0.2.1 之上的修補版，**無破壞性變更**。三條主線：

1. **Prompt registry**——SDK 內建的每一支 LLM system prompt 現在都可以從單一 API 依 locale 覆蓋。想做日文／西文／法文，不用再翻子系統。
2. **`autoInstall()` 一行安裝**——零設定啟動點，回傳完全構造好的 `DotDotDuck`，內建 locale 偵測、demo LLM stub、預設鴨子精靈。
3. **吉祥物升級**——新的 HERO 打招呼膠囊（FAB 展開成橫向長條）、8 條吉祥物動畫改為 CSS variable 可調、SDK 內建鴨子精靈出貨（基本安裝不再需要接 `--dddk-*-url`）。

另外修好觸控筆電下 palette footer 消失、InlineAgent 多餘的「處理中」提示、以及 Dwell 游泳動畫在子目錄 host 上 404 的 URL scope bug。

## TL;DR

- **`dddk.prompts`**——每個 `DotDotDuck` instance 上多了 `PromptRegistry`。`dddk.prompts.list()` 列出 SDK 內建每一支 prompt id；`dddk.prompts.override(id, locale, render)` 依 locale 換一支；`dddk.prompts.append(id, extra)` 尾接站台級的補充規則。
- **`autoInstall(overrides?)`**——新工廠函式。`import { autoInstall } from '@perhapxin/dddk'; const dddk = autoInstall();` 就啟動起來，內含 demo LLM、瀏覽器 locale 偵測、預設鴨子精靈。傳 overrides 就能替換成 host 實際的接線。
- **HERO 打招呼膠囊**——第一次造訪，圓形 FAB 展開成橫向的黃色半透明膠囊，鴨子留在右邊（原本 FAB 的位置），打招呼文字填滿左邊。12+ 個視覺屬性都是 `--dddk-fab-hero-*` 變數，host 在 `:root` 上宣告就能主題化。
- **8 條吉祥物動畫可調**——`--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`，加上原本的 `--dddk-fab-hero-transition-ms` / `--dddk-swim-duration`。想放慢配合品牌節奏、或壓低給 reduced motion，都改一行完事。
- **7 張鴨子精靈內建**——SDK `dist/duck/` 出貨 `neutral.png`、`swim-side.png`、`hero-greet.png`、`chill-shades.png`、`swim-cycle.png`、`logo.png`、`cursor.png`，`tokens.css` 用 `--dddk-*-url` 預設綁好。想用預設就不用自己 host PNG，想換就覆蓋。
- **新 WebAgent cursor 精靈**——原本 SVG 的三角箭頭 + 鴨頭換成 bitmap 精靈：**一隻鴨子騎在紙飛機上**、尖角朝左上作為點擊起點。透過 `--dddk-cursor-url` 換掉整張。Scroll + reading mode 仍保留 SVG（仍可用 `--webagent-cursor-{fill,stroke}` 主題化）。
- **Palette「Powered by dotdotduck」footer**——右邊 brand mark 用 `--dddk-brand-mark-url`（預設指到內建 logo），左邊依輸入模式切換：鍵盤環境顯示 `↑ ↓ ⏎ esc`，`(hover: none) and (pointer: coarse)` 觸控環境顯示「點列項選擇 / 點外側關閉」。修好「觸控筆電上 footer 整條消失」的 bug。
- **InlineAgent 不再重複提示**——「處理中」的 subtitle indicator 拿掉了；串流 diff overlay 本身就是 loading 狀態，畫兩次除了雜訊還會有 race（早於 diff 收尾就消失、或晚於才收）。錯誤仍走 `subtitle.show({ type: 'info' })`。
- **Dwell 游泳 URL 修好**——游泳 overlay 的 spritesheet 改走 `--dddk-swim-cycle-url`（root-absolute-safe），不再硬編 `/duck/swim-cycle.png`。子目錄 host 上動畫回來了。

## 詳細變更

### Prompt registry（多語言產品團隊最有感的一條）

之前 prompts 散落在約 10 個子系統模組裡（webagent narrator、planner、InlineAgent、markdown-edit、翻譯、STT 清理、Dwell 分類器）。host 想「日文版 prompt」或「拿掉 persona 段」得一個檔一個檔翻。

v0.2.2 把每一支預設都收攏到 `dddk.prompts` 上的穩定 ID：

```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']

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

// 為 narrator 尾接全站規則（每個 locale 都會套用）
dddk.prompts.append('webagent-narrator.system', () => '絕不主動提及價格。所有事實都要引用來源。');

// 重置回 SDK 預設
dddk.prompts.reset('inline-edit.system');
```

Fallback 順序：`override(id, locale)` → `override(id, 'en')` → SDK 預設。要 EN + zh-TW + ja 三語就寫三次，不動的 locale 自動吃 fallback。

完整介紹：[prompts.md](./prompts.md)。

### `autoInstall()`

零設定啟動點：

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

const dddk = autoInstall();
// Palette + subtitle + Dwell + FAB 都掛上、鴨子精靈用內建版。
// Locale 自動吃 navigator.language。
// LLM 預設是「換掉我以啟用真正的 AI」demo stub，準備好再換。
```

要真正接線就傳 overrides：

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

`autoInstall(overrides)` 跟 `new DotDotDuck({ ...defaults, ...overrides })` 等價，工廠函式只是幫你挑好預設值。詳見 [auto-install.md](./auto-install.md)。

### HERO 打招呼膠囊（`showHeroGreeting`）

新的打招呼形狀：第一次造訪時，圓形小 FAB **橫向展開成黃色半透明膠囊**——鴨子留在右邊（原本的角落），打招呼文字填滿左半邊，全 CSS 過渡（不再是額外的對話泡泡元素）。每個視覺屬性都是變數：寬、高、圓角、背景、陰影、過渡、鴨頭大小、內距——完整列表在 [mascot.md](./mascot.md)。

從 host 的 onboarding 流程觸發：

```ts
await dddk.mobile.showHeroGreeting(
  '嗨！我是 Rex。點我或按 Ctrl+K 打開指令面板。',
  { autoDismissMs: 20000 },
);
```

收起時反向動畫（膠囊縮回圓形、表情復原）。

### 8 條吉祥物動畫可調

之前只有 2 條時間可以吃 CSS variable (`--dddk-fab-hero-transition-ms`、`--dddk-swim-duration`)。v0.2.2 把 8 條 host 實務上會想調的都提升成變數：

```css
:root {
  /* 字幕條頭像 */
  --dddk-avatar-swim-duration: 620ms;
  --dddk-avatar-bob-duration: 2.6s;
  /* 思考中 indicator */
  --dddk-indicator-swim-duration: 1.5s;
  --dddk-indicator-swim-pip-duration: 1s;
  --dddk-indicator-wave-duration: 0.9s;
  /* Palette 品牌 mark */
  --dddk-brand-mark-bob-duration: 3.2s;
  /* Dwell 角落吉祥物 */
  --dddk-dwell-avatar-bob-duration: 2s;
  --dddk-dwell-chill-breath-duration: 3s;
}
```

想完全關掉某一條動畫，對選擇器直接下 `animation: none`（例：`[data-dddk-ui="palette-footer-brand-mark"] { animation: none; }`）。`prefers-reduced-motion: reduce` 環境 SDK 已經自動把吉祥物動畫關掉——上面的變數是給品牌調速用，不是給無障礙。

### 內建鴨子精靈

SDK `dist/duck/` 出貨六張 PNG，`tokens.css` 用 `--dddk-*-url` 綁在 `:root`：

| 變數 | 預設 | 使用位置 |
|---|---|---|
| `--dddk-avatar-url` | `./duck/neutral.png` | 字幕條頭像、Dwell chip |
| `--dddk-swim-url` | `./duck/swim-side.png` | Proactive 游泳 indicator |
| `--dddk-hero-url` | `./duck/hero-greet.png` | HERO 打招呼（選用的大顯身手精靈） |
| `--dddk-chill-url` | `./duck/chill-shades.png` | Dwell 框角落吉祥物 |
| `--dddk-swim-cycle-url` | `./duck/swim-cycle.png` | Dwell 頂邊游泳 spritesheet（8 幀） |
| `--dddk-brand-mark-url` | `./duck/logo.png` | Palette「Powered by dotdotduck」品牌 mark |
| `--dddk-cursor-url` | `./duck/cursor.png` | WebAgent 合成 cursor（紙飛機 + 鴨） |

想換自家品牌角色，`:root` 上重新宣告即可：

```css
:root {
  --dddk-avatar-url: url('/my-brand/mascot.png');
  --dddk-swim-url: url('/my-brand/swim.png');
  /* 想保留 palette 上的 dotdotduck 品牌歸屬，別動 --dddk-brand-mark-url。 */
}
```

**子目錄注意**——Chrome 對 CSS custom property 裡的 `url()`，若透過 JS-injected `<style>` 套用（Dwell / palette 就是這樣載 style 的），會用 **文件 URL** 當基準解析、不是 CSS 檔位置。你的 app 若掛在 `/tools/dddk/` 這類子目錄，SDK 預設的 `./duck/...` 會失效。修法：在你 app 的 `:root` 覆蓋成 root-absolute 路徑——`--dddk-swim-cycle-url: url('/my-app/duck/swim-cycle.png');`。細節寫在 [theming.md](./theming.md)。

### Palette footer

兩處變更：

1. **右邊 brand mark** 用新的 `--dddk-brand-mark-url`（預設 `./duck/logo.png` 內建），host 覆蓋掉 `--dddk-avatar-url` 也不會意外拿掉 palette 上的品牌 mark（有 fallback）。
2. **左邊 hint 依輸入模式切換**：鍵盤環境 `↑ ↓ 上下移動 · ↵ 選取 · esc 關閉`；`(hover: none) and (pointer: coarse)` 觸控環境 `點列項選擇 · 點外側關閉`。v0.2.1 用 `(pointer: coarse)` 單一條件，會誤傷有真觸控板的觸控筆電（footer 整條被藏），這次收斂。

### InlineAgent 停止重複提示

`runInstruction` 跟 prefix-submit 兩條路徑上的「處理中」subtitle indicator 拔掉了。原因：streaming diff overlay 本身就是 loading 狀態——文字一段段串進來、原文有刪除線、新文有 diff 樣式；再多一個底部提示除了雜訊還有 race（clear-on-first-delta 早於或晚於 diff 完成）。錯誤仍走 catch 分支的 `subtitle.show({ type: 'info' })`。

## Bug fixes

- **觸控筆電上 palette footer 消失**——media query 是 `(pointer: coarse)` 單條件，會命中觸控筆電的觸控板。收斂成 `(hover: none) and (pointer: coarse)`，只有真正觸控主導的裝置才切換 hint。
- **Dwell 游泳 overlay 在子目錄 host 上 404**——之前硬編 `url('/duck/swim-cycle.png')`。改走 `--dddk-swim-cycle-url` 並附內建預設，子目錄 host 覆蓋即可。
- **切換 locale 後 palette footer 文字空白**——`refreshFooter()` 用 innerHTML 蓋整條，把初始 mount 分開加的 brand mark 沖掉。兩條路徑改為共用 `buildFooterHTML()`。
- **12 個未宣告的 CSS 變數**——`--dddk-fab-hero-*`、`--dddk-indicator-duck-url`、`--dddk-dwell-swim-*` 等，SDK 內 `var()` 讀但 `tokens.css` 沒宣告（host 找不到能調哪些）。全補上合理的預設值。

## Contributor 變更

- 新 `src/prompts/` 目錄（`registry.ts` + `defaults.ts`）作為 prompt ID 的單一來源。新增 prompt 的流程：加到 `PROMPT_IDS` enum → 在 `defaults.ts` 註冊預設 → callsite 走 `promptRegistry.render(id, ctx, locale)`。
- 三個 inline prompt const 抽成獨立檔（`modules/immersive-translate/prompt.ts`、`modules/voice/prompt.ts`、`triggers/dwell/prompt.ts`），registry 可以 import 而不用把子系統 runtime 一起拉進來。
- `tsup.config.ts` 的 `onSuccess` 現在會把 `src/duck/*.png` 複製到 `dist/duck/`，並為 sub-path 的 `dist/styles/tokens.css` 把 `url('./duck/...')` 改寫成 `url('../duck/...')`，讓 bundled `dist/styles.css` 跟 sub-path import 兩條路徑都解析對。

## 從 v0.2.1 升級

純新增，不用改任何 callsite。想採用新 API 的話看 [migrating.md](./migrating.md)。
