autoInstall() — one-line install

Zero-config on-ramp. Returns a fully-constructed DotDotDuck with sensible defaults so you can see the SDK working before deciding what to configure.

Minimum

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

const dddk = autoInstall();

That's it. You get:

  • Command palette wired to Ctrl+K / ⌘K.
  • Subtitle bar mounted center-bottom.
  • Dwell long-press detector.
  • FAB mascot in the bottom-right corner on desktop and mobile (v0.2.3+ — fab.alwaysVisible: true is the autoInstall default). A MobileTrigger is auto-attached.
  • HERO greeting on first visit — the FAB morphs into a horizontal capsule with a bilingual title + "Tap me → palette" and "Hold me → talk" action rows + keyboard hint (desktop only). Session-storage-gated so returning visitors aren't nagged.
  • All seven default duck sprites loaded — sprite URLs resolved via import.meta.url at construction time (v0.2.3+), so Dwell swim / cursor / palette footer icon all show correctly on subdirectory hosts + non-Vite setups too.
  • Locale auto-detected: zh / zh-*'zh-TW', everything else → 'en'.
  • SDK-shipped prompt defaults registered on dddk.prompts.
  • A demo LLM that responds to any completion with a "wire your real provider" nudge — so agent / InlineAgent / voice-cleanup don't silently no-op; the user sees why nothing is happening.

Passing overrides

Every field of DotDotDuckConfig is accepted verbatim, plus one factory-specific flag (demoLLM).

const dddk = autoInstall({
  llm: myOpenAIProvider,          // real LLM replaces the demo stub
  siteName: 'Acme',
  agentName: 'Rex',
  paletteCommands: myCommands,
  brand: { voice: 'friendly' },
  webAgent: {
    contextProvider: () => ({ tenant: currentTenant() }),
  },
});

autoInstall(overrides) and new DotDotDuck({ ...factoryDefaults, ...overrides }) are functionally equivalent — the factory just picks the defaults. Anything you don't override keeps the demo default.

Disabling the demo LLM

By default, if you don't pass llm, autoInstall wires a demo stub. To keep the old "silent no-op if no LLM" behaviour (matches raw new DotDotDuck({})), set demoLLM: false:

const dddk = autoInstall({ demoLLM: false });
// Palette + Dwell + FAB still mount; agent-side features silently
// no-op until an `llm` gets wired at runtime.

Useful when your LLM provider is async-loaded (e.g. behind auth) and you want the SDK up before it's ready.

Full option type

interface AutoInstallOptions extends Partial<DotDotDuckConfig> {
  /**
   * When true and no `llm` is provided, wire a demo LLM. Default: true.
   */
  demoLLM?: boolean;

  /**
   * MobileTrigger config. v0.2.3+ auto-attaches a MobileTrigger with
   * `fab.alwaysVisible: true` (desktop shows the FAB too) + the built-in
   * bilingual HERO greeting. Pass `false` to skip auto-attach entirely;
   * pass a partial config to override any field.
   */
  mobile?: MobileTriggerConfig | false;

  /**
   * Sprite URL overrides. v0.2.3+ resolves default sprite URLs from
   * `import.meta.url` at install time. Pass a per-key override to swap
   * one for your brand.
   */
  sprites?: {
    avatar?: string;    swim?: string;      hero?: string;
    chill?: string;     swimCycle?: string; brandMark?: string;
    cursor?: string;
  };

  /**
   * HERO greeting override. Convenience alias for
   * `mobile.heroGreeting`. `mobile.heroGreeting` takes precedence if
   * both are set.
   */
  heroGreeting?: HeroGreetingConfig | false;
}

Return type: DotDotDuck. Same instance you'd get from new DotDotDuck(...). All methods (startAgent, runSkill, submitSurface, etc.) are available.

Examples

// Disable the FAB entirely (host mounts their own trigger):
autoInstall({ mobile: false });

// Position the FAB bottom-left instead of bottom-right:
autoInstall({ mobile: { fab: { position: 'bottom-left' } } });

// Swap the mascot for your brand character:
autoInstall({
  sprites: {
    avatar: '/my-brand/mascot.png',
    swim:   '/my-brand/mascot-side.png',
    // brand-mark left as default → keeps the dotdotduck logo on the
    // palette footer's "Powered by" strip.
  },
});

// Custom greeting title, keep everything else default:
autoInstall({
  heroGreeting: {
    title: { en: "Hi! I'm Rex", 'zh-TW': '嗨!我是 Rex' },
  },
});

// Disable the greeting auto-fire on first visit:
autoInstall({ heroGreeting: false });

When NOT to use autoInstall

  • You already wire every config field explicitly. Migrating to autoInstall({...}) would just move code around — no benefit.
  • You need SSR-safe initialisation. autoInstall reads navigator.language at construction time, so it can't run on the server. Call it inside onMount (Svelte) / useEffect (React) / mounted (Vue).
  • You want the demo LLM to be permanent. The demo LLM is intentionally hostile-to-real-use — its response tells the user to wire a real provider. If your product legitimately doesn't need an LLM, pass demoLLM: false and skip the pill.

Common integration patterns

Vanilla HTML + <script>

<script type="module">
  import { autoInstall } from 'https://esm.sh/@perhapxin/dddk@0.2.2';
  import 'https://esm.sh/@perhapxin/dddk@0.2.2/styles.css';

  const dddk = autoInstall();
  window.dddk = dddk; // for DevTools poking
</script>

SvelteKit

<script lang="ts">
  import { onMount } from 'svelte';
  import { autoInstall } from '@perhapxin/dddk';
  import '@perhapxin/dddk/styles.css';

  onMount(() => {
    const dddk = autoInstall({
      llm: myProvider,
      paletteCommands: buildCommands(),
    });
    return () => dddk.destroy?.();
  });
</script>

React / Next.js

'use client';
import { useEffect } from 'react';
import { autoInstall } from '@perhapxin/dddk';
import '@perhapxin/dddk/styles.css';

export function DddkBoot() {
  useEffect(() => {
    const dddk = autoInstall({ llm: getLLM() });
    return () => dddk.destroy?.();
  }, []);
  return null;
}