Skip to content

@askable-ui/svelte

Svelte 4 bindings for askable-ui.

Install

bash
npm install @askable-ui/svelte @askable-ui/core

<Askable>

Renders a wrapper element with data-askable managed reactively.

svelte
<script>
  import Askable from '@askable-ui/svelte/Askable.svelte';
</script>

<Askable meta={{ metric: 'revenue', delta: '-12%' }}>
  <RevenueChart />
</Askable>

<Askable meta={{ metric: 'revenue' }} scope="analytics">
  <RevenueChart />
</Askable>

<Askable meta="main navigation" as="nav">
  <NavLinks />
</Askable>

Props:

PropTypeDefaultDescription
metaRecord<string, unknown> | stringValue for data-askable attribute
scopestringOptional category written to data-askable-scope for scoped prompt/history queries
asstring"div"HTML element to render

createAskableStore(options?)

Factory that returns Svelte stores backed by an AskableContext. SSR-safe — observe() is guarded and only runs in the browser.

ts
import { createAskableStore } from '@askable-ui/svelte';

const { focus, promptContext, ctx, destroy } = createAskableStore();

Always call destroy() in onDestroy:

ts
import { onDestroy } from 'svelte';
const { destroy } = createAskableStore();
onDestroy(destroy);

Options:

OptionTypeDescription
eventsAskableEvent[]Trigger events. Default: ['click', 'hover', 'focus']

Returns:

ValueTypeDescription
focusReadable<AskableFocus | null>Reactive focus store
promptContextReadable<string>Reactive prompt-ready context store
ctxAskableContextFull context instance
destroy() => voidTears down the context

Examples:

ts
// Subscribe with $ auto-subscription in Svelte
$: console.log($focus?.meta);
$: console.log($promptContext);

// Click-only
const store = createAskableStore({ events: ['click'] });

Inspector

Svelte can mount the inspector through createAskableStore({ inspector: ... }).

ts
import { createAskableStore } from '@askable-ui/svelte';

const askable = createAskableStore({
  events: ['click'],
  inspector: {
    position: 'bottom-left',
  },
});

Because createAskableStore() creates a private context by default, the inspector automatically follows that store's context. If you want multiple components or stores to share one inspector/context, pass the same explicit ctx to each store:

ts
import { createAskableContext } from '@askable-ui/core';

const sharedCtx = createAskableContext();
sharedCtx.observe(document, { events: ['hover'] });

const chartStore = createAskableStore({ ctx: sharedCtx, inspector: true });
const chatStore = createAskableStore({ ctx: sharedCtx });

Remember to call destroy() so the inspector panel is removed when the component/store is torn down.

Shared vs private/custom contexts

Svelte differs from the React/Vue adapters: createAskableStore() creates a private AskableContext per store by default.

  • Private default — every createAskableStore() call creates its own context and observer lifecycle.
  • Custom provided context — pass ctx when multiple components/stores should share one context.
  • Per-surface isolation — create separate stores or separate explicit contexts when different panels should not share focus/history.

That means if two components both call createAskableStore() with no ctx, they will not automatically share focus.

ts
import { createAskableContext } from '@askable-ui/core';
import { createAskableStore } from '@askable-ui/svelte';

// Private stores: isolated from each other
const left = createAskableStore({ events: ['click'] });
const right = createAskableStore({ events: ['click'] });

// Shared explicit context across stores/components
const sharedCtx = createAskableContext();
sharedCtx.observe(document, { events: ['hover'] });

const chartStore = createAskableStore({ ctx: sharedCtx });
const chatStore = createAskableStore({ ctx: sharedCtx });

Use the private default for isolated widgets. Pass a shared ctx when multiple Svelte components need to agree on one Askable focus/history stream.


createAskableSourceStore(id, source, options?)

Lifecycle-managed registration for app-owned context sources. Use this when the assistant needs data that is not fully rendered in the DOM: paginated tables, virtualized lists, documents, charts, maps, calendars, canvases, file trees, or custom state.

The store registers the source immediately and unregisters it when destroy() is called.

svelte
<script lang="ts">
  import { onDestroy } from 'svelte';
  import { createAskableSourceStore } from '@askable-ui/svelte';

  const accounts = createAskableSourceStore('accounts', {
    kind: 'collection',
    describe: 'Customer accounts matching the active filters',
    getState: () => ({
      filters,
      sort,
      page: table.getState().pagination.pageIndex + 1,
      pageSize: table.getState().pagination.pageSize,
      totalCount,
    }),
    resolve: async ({ mode, maxItems }) => {
      if (mode === 'visible') return table.getRowModel().rows.map((row) => row.original);
      return summarizeAccounts({ filters, sort, maxItems });
    },
    sanitize: (source) => ({
      ...source,
      data: redactAccountFields(source.data),
    }),
  });

  onDestroy(accounts.destroy);

  async function ask(question: string) {
    const promptContext = await accounts.toPromptContext({
      source: { mode: 'summary', maxItems: 20, timeoutMs: 750 },
      sourceErrorMode: 'include',
    });

    return sendToAgent({ question, promptContext });
  }

  $: {
    filters;
    sort;
    totalCount;
    accounts.notifyChanged();
  }
</script>

Call notifyChanged() when source data changes without a DOM focus change, such as pagination, filters, selected rows, or query-cache updates. Async subscribers created with ctx.subscribeAsync() re-resolve matching sources.

Options:

OptionTypeDescription
enabledbooleanRegister while true. Defaults to true
ctxAskableContextOptional context to share with other Svelte stores/components
eventsAskableEvent[]Observation events for the underlying store context

Returns:

ValueTypeDescription
ctxAskableContextContext instance that owns the source
sourceIdstringTrimmed registered source id
resolve(request?)Promise<AskableResolvedContextSource>Resolve this source directly
toPromptContext(options?)Promise<string>Serialize focus plus this source
notifyChanged()() => voidRe-resolve matching async subscribers after source data changes
unregister()() => voidManually unregister before destroy when needed
destroy()() => voidUnregisters the source and destroys the underlying store context

createAskableRegionCaptureStore(options?)

Factory that starts an explicit region, circle, or lasso selection overlay and exposes the captured Context packet as Svelte stores.

ts
import { createAskableRegionCaptureStore } from '@askable-ui/svelte';

const capture = createAskableRegionCaptureStore({
  includeViewport: true,
  source: { app: 'dashboard' },
  intent: 'answer with this selected area as context',
});

capture.start();
capture.start({ shape: 'circle' });
capture.start({ shape: 'lasso' });
capture.cancel();

Always call destroy() in onDestroy:

ts
import { onDestroy } from 'svelte';

onDestroy(capture.destroy);

Options:

OptionTypeDescription
shape'region' | 'circle' | 'lasso'Initial capture shape. Default: 'region'
includeViewportbooleanInclude viewport metadata in the emitted Context packet
sourceWebContextSourceApp/page source metadata attached to the packet
intentstringUser intent attached to the capture
ctxAskableContextOptional context to share with other Svelte stores/components
eventsAskableEvent[]Observation events for the underlying store context
onCapture(packet, selection) => voidCalled after a region, circle, or lasso is accepted
onCancel() => voidCalled after active capture is cancelled

Returns:

ValueTypeDescription
activeReadable<boolean>Whether the overlay is active
lastPacketReadable<WebContextPacket | null>Last captured Context packet
lastSelectionReadable<AskableRegionCaptureSelection | null>Last raw region/circle/lasso selection geometry
start(overrides?) => voidStarts capture, optionally overriding options for one capture
cancel() => voidCancels the active overlay
destroy() => voidCancels capture and destroys the underlying store context
isActive() => booleanReads the current overlay state
ctxAskableContextStore context instance

For persistent capture tools, pass once: false. The overlay and active store stay on after each accepted capture until the user cancels or the store is destroyed.


createAskableTextSelectionCaptureStore(options?)

Factory that captures highlighted browser text and exposes the captured Context packet as Svelte stores.

ts
import { createAskableTextSelectionCaptureStore } from '@askable-ui/svelte';

const selection = createAskableTextSelectionCaptureStore({
  includeViewport: true,
  source: { app: 'dashboard' },
  intent: 'answer using this highlighted text',
});

selection.start();
selection.captureNow();
selection.cancel();

Always call destroy() in onDestroy.

Options: root, minLength, debounce, once, dedupe, source, intent, ctx, events, onCapture, and onCancel.

Returns: active, lastPacket, lastSelection, start(overrides?), captureNow(overrides?), cancel(), destroy(), isActive(), and ctx.

Released under the MIT License.