v0.2.3 release notes

Patch release on top of v0.2.2. No breaking changes. One consistent theme: autoInstall() alone gives you the complete mascot experience — no host wiring, no CSS overrides, no bodyHtml to write.

TL;DR

  • HERO greeting body defaults ship in the SDK — every install shows a coherent bilingual first-visit greeting without the host writing any HTML. Override title / rows / kbdHint per-field, override the whole HTML via bodyHtml, or disable via heroGreeting: false.
  • Sprite URL runtime resolver — SDK reads its own JS location via import.meta.url at construction time + inline-sets --dddk-*-url on :root with reachable absolute URLs. Fixes the "Dwell swim / cursor / palette footer icon are all missing on subdirectory hosts / non-Vite setups" bug that only dddk-frontend had worked around by hand-overriding paths.
  • autoInstall() auto-attaches MobileTrigger — with fab.alwaysVisible: true (desktop shows the FAB too) + the new heroGreeting default. Pass mobile: false to opt out.
  • New Dwell config showCornerMascots: boolean — enterprise / business-app hosts (accounting, ERP, medical) can render just the Dwell frame outline without the playful side-view swim duck + sunglasses "chill" duck corners. Default true (existing behaviour preserved).
  • New sprites option on autoInstall(...) — swap any bundled sprite for your brand character by passing an override map. avatar / swim / hero / chill / swimCycle / brandMark / cursor — each optional, unspecified keys fall back to the SDK bundle.

What changed

HERO greeting body defaults

Before v0.2.3 the greeting body was 100% the host's responsibility — mobile.showHeroGreeting('Hi!') showed just a bare text line, or the host had to hand-write 60+ lines of bodyHtml (title + action rows + keyboard hint) themselves. Every host looked different; dddk-frontend's rich greeting was custom code that other installs didn't inherit.

v0.2.3 ships a bilingual template inside the SDK. New config on MobileTriggerConfig:

export interface HeroGreetingConfig {
  autoShow?: boolean;         // default true; auto-fires on first mount
  autoDismissMs?: number;     // default 20000
  storageKey?: string;        // default 'dddk_hero_greeting_seen_v1'
  delayMs?: number;           // default 1200
  title?: string | Partial<Record<string, string>>;   // bilingual
  rows?: HeroGreetingRow[];                           // action list
  kbdHint?: string | Partial<Record<string, string>> | false;
  ariaLabel?: string | Partial<Record<string, string>>;
  bodyHtml?: string | ((locale: string) => string);   // full escape hatch
}

Three overrides you'll actually use:

// 1. Just tweak the title, keep everything else default
new MobileTrigger({
  heroGreeting: {
    title: { en: "Hi! I'm Rex", 'zh-TW': '嗨!我是 Rex' },
  },
});

// 2. Full replacement — pass raw HTML
new MobileTrigger({
  heroGreeting: {
    bodyHtml: (locale) => locale === 'zh-TW'
      ? '<div>你好</div>'
      : '<div>Hello</div>',
  },
});

// 3. Disable it entirely
new MobileTrigger({ heroGreeting: false });

Every visual axis of the template is a CSS variable declared in tokens.css:

  • --dddk-fab-hero-title-size / -title-weight
  • --dddk-fab-hero-row-size / -row-gap / -row-margin / -icon-size
  • --dddk-fab-hero-kbd-size / -kbd-margin / -kbd-opacity

Override on :root for site-wide tuning without touching the config.

Sprite URL runtime resolver

The SDK ships seven duck sprites in dist/duck/. tokens.css declares --dddk-avatar-url: url('./duck/neutral.png') etc. as defaults.

The catch: three SDK subsystems (Dwell, palette footer, WebAgent cursor) inject their own <style> blocks at runtime. Chrome resolves url() inside runtime-injected styles against the document URL — not the source CSS file. So the SDK's relative ./duck/… breaks on any host served from a subdirectory, or on setups that aren't standard Vite production.

initSpriteDefaults() runs at autoInstall() time — computes each sprite URL via new URL('./duck/x.png', import.meta.url) and inline-sets on :root.style:

  • Vite / Webpack / esbuild recognise this pattern as an asset reference — process the PNG at build time + rewrite the string.
  • Non-bundler setups (esm.sh, raw <script type="module">) resolve the URL against the actual JS location, which is where the sprites also live.

Either way, sprite URLs point at something reachable. Host overrides still win — pass autoInstall({ sprites: { avatar: '/my-brand/mascot.png' } }) and the resolver honours it.

autoInstall() auto-attaches MobileTrigger

Before v0.2.3, calling autoInstall() gave you a palette + subtitle bar but no FAB. You had to also call new MobileTrigger({ ... }).attachTo(dddk) separately. That mismatched the "just install and it works" promise.

v0.2.3 has autoInstall() mount a MobileTrigger by default with fab.alwaysVisible: true (desktop shows the FAB too), plus the new HERO greeting on first visit. New top-level options:

autoInstall({
  mobile: false,                    // disable FAB entirely
  // or:
  mobile: { fab: { position: 'bottom-left' } }, // override any MobileTriggerConfig field

  sprites: {                        // swap sprites
    avatar: '/my-brand/mascot.png',
    swim:   '/my-brand/swim.png',
  },

  heroGreeting: {                   // convenience alias for mobile.heroGreeting
    title: { en: "Hi! I'm Rex", 'zh-TW': '嗨!我是 Rex' },
  },
});

Dwell showCornerMascots opt-out

The two decorative Dwell corner mascots (side-view swim duck riding the top edge + sunglasses "chill" duck at the frame corner) were reported as out-of-context on serious business UIs (accounting, ERP, medical). New config:

new Dwell({
  llm: myLLM,
  showCornerMascots: false,   // frame outline only; no mascots
});

Long-press-to-activate + Escape-to-clear + the frame outline itself work identically. Only the mascots are suppressed.

Bug fixes

  • showHeroGreeting() documentation clarified — the greeting body is now SDK-provided when no bodyHtml is passed, matching the auto-fire default.
  • HERO greeting session-storage key renamed from host-specific dddk:palette_hint_shown to SDK-owned dddk_hero_greeting_seen_v1. Hosts that previously wired the greeting via layout code no longer need to manage session-storage themselves.

Migrating from v0.2.2

Purely additive — every field, function, and CSS variable that existed in v0.2.2 still works. Two optional adoptions:

  1. Drop your hand-written HERO greeting body — pass a per-field override on MobileTriggerConfig.heroGreeting instead. Frontend just dropped 60 lines this way.
  2. Delete --dddk-*-url overrides in your app.css if you were using them to work around the "sprites don't render on subdirectory hosts" bug — the runtime resolver now handles it.

Full guide: migrating.md.

What v0.2.3 does NOT change

  • All existing hosts using new DotDotDuck({...}) + manual new MobileTrigger({...}) continue to work unchanged.
  • No CSS variable names or renderer contracts changed.
  • The showHeroGreeting(text, opts) public API signature is unchanged.
  • No new peer dependencies.