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> | string—Value for data-askable attribute
scopestring—Optional 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
onSelectionChange(state) => voidCalled when pinned selected context changes or is cleared
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
selectionStateReadable<AskableRegionCaptureState | null>Reactive pinned packet, selection, and selected-state affordance element for rendering confirmation UI
start(overrides?) => voidStarts capture, optionally overriding options for one capture
cancel() => voidCancels the active overlay
clearSelection() => voidRemoves the current persisted selected-state UI
getSelection() => AskableRegionCaptureState | nullReads the currently pinned packet, selection, and affordance element
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.

Use selectionState when a Svelte component should visibly confirm selected context or render an inline question input. It mirrors getSelection() and updates when capture pins, clears, or dismisses selected context.


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, onSelectionChange, and onCancel.

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

Use selectionState for app-rendered selected-text confirmation and inline chat inputs. It mirrors getSelection() and updates when text context is pinned, cleared, dismissed, cancelled, or destroyed.

Released under the MIT License.