v0.2.2 release notes
Patch release on top of v0.2.1. No breaking changes. Three themes:
- 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.
autoInstall()one-line install — a zero-config on-ramp that returns a fully-constructedDotDotDuckwith sensible defaults (locale detection, demo LLM stub, bundled duck sprites).- 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-*-urlwiring 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— newPromptRegistryon everyDotDotDuckinstance.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, andcursor.pngunderdist/duck/, wired via--dddk-*-urldefaults intokens.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:
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.
autoInstall()
Zero-config on-ramp:
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:
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.
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.
Hosts fire it from wherever their onboarding flow lives:
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:
: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:
: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.
Palette footer
Two changes:
- Brand mark on the right shows the shipped logo icon (via new
--dddk-brand-mark-url, defaults to./duck/logo.pngbundled indist/duck/). Falls back to--dddk-avatar-urlif the brand mark is unset — hosts overriding the avatar don't accidentally lose the palette attribution. - 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-urlwith 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 onbuildFooterHTML(). - 12 undeclared CSS variables —
--dddk-fab-hero-*,--dddk-indicator-duck-url,--dddk-dwell-swim-*, etc. were read viavar()throughout the SDK but never declared intokens.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 toPROMPT_IDSenum → register indefaults.ts→ resolve at callsite viapromptRegistry.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.tsonSuccesshook now copiessrc/duck/*.png→dist/duck/, and rewritesurl('./duck/...')→url('../duck/...')for the sub-pathdist/styles/tokens.cssvariant so both the bundleddist/styles.cssand sub-path imports resolve correctly.
Migrating from v0.2.1
Purely additive — no callsite changes required. See migrating.md for optional adoption of the new APIs.