@askable-ui/svelte
Svelte 4 bindings for askable-ui.
Install
npm install @askable-ui/svelte @askable-ui/core<Askable>
Renders a wrapper element with data-askable managed reactively.
<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:
| Prop | Type | Default | Description |
|---|---|---|---|
meta | Record<string, unknown> | string | — | Value for data-askable attribute |
scope | string | — | Optional category written to data-askable-scope for scoped prompt/history queries |
as | string | "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.
import { createAskableStore } from '@askable-ui/svelte';
const { focus, promptContext, ctx, destroy } = createAskableStore();Always call destroy() in onDestroy:
import { onDestroy } from 'svelte';
const { destroy } = createAskableStore();
onDestroy(destroy);Options:
| Option | Type | Description |
|---|---|---|
events | AskableEvent[] | Trigger events. Default: ['click', 'hover', 'focus'] |
Returns:
| Value | Type | Description |
|---|---|---|
focus | Readable<AskableFocus | null> | Reactive focus store |
promptContext | Readable<string> | Reactive prompt-ready context store |
ctx | AskableContext | Full context instance |
destroy | () => void | Tears down the context |
Examples:
// 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: ... }).
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:
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
ctxwhen 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.
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.
<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:
| Option | Type | Description |
|---|---|---|
enabled | boolean | Register while true. Defaults to true |
ctx | AskableContext | Optional context to share with other Svelte stores/components |
events | AskableEvent[] | Observation events for the underlying store context |
Returns:
| Value | Type | Description |
|---|---|---|
ctx | AskableContext | Context instance that owns the source |
sourceId | string | Trimmed registered source id |
resolve(request?) | Promise<AskableResolvedContextSource> | Resolve this source directly |
toPromptContext(options?) | Promise<string> | Serialize focus plus this source |
notifyChanged() | () => void | Re-resolve matching async subscribers after source data changes |
unregister() | () => void | Manually unregister before destroy when needed |
destroy() | () => void | Unregisters 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.
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:
import { onDestroy } from 'svelte';
onDestroy(capture.destroy);Options:
| Option | Type | Description |
|---|---|---|
shape | 'region' | 'circle' | 'lasso' | Initial capture shape. Default: 'region' |
includeViewport | boolean | Include viewport metadata in the emitted Context packet |
source | WebContextSource | App/page source metadata attached to the packet |
intent | string | User intent attached to the capture |
ctx | AskableContext | Optional context to share with other Svelte stores/components |
events | AskableEvent[] | Observation events for the underlying store context |
onCapture | (packet, selection) => void | Called after a region, circle, or lasso is accepted |
onCancel | () => void | Called after active capture is cancelled |
Returns:
| Value | Type | Description |
|---|---|---|
active | Readable<boolean> | Whether the overlay is active |
lastPacket | Readable<WebContextPacket | null> | Last captured Context packet |
lastSelection | Readable<AskableRegionCaptureSelection | null> | Last raw region/circle/lasso selection geometry |
start | (overrides?) => void | Starts capture, optionally overriding options for one capture |
cancel | () => void | Cancels the active overlay |
destroy | () => void | Cancels capture and destroys the underlying store context |
isActive | () => boolean | Reads the current overlay state |
ctx | AskableContext | Store 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.
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.