@askable-ui/qwik
v0.17.1
Published
Qwik hooks and components to give AI assistants real-time context about what users see and select
Maintainers
Readme
@askable-ui/qwik
Qwik hooks and components for askable-ui. Turn explicitly selected or focused UI into structured context for AI assistants.
npm install @askable-ui/qwik @askable-ui/coreUsage
import { component$ } from '@builder.io/qwik';
import { Askable, useAskable } from '@askable-ui/qwik';
export default component$(() => {
const { promptContext } = useAskable();
return (
<>
<Askable meta={{ metric: 'revenue', value: '$2.34M' }}>
<article>Revenue: $2.34M</article>
</Askable>
<pre>{promptContext.value}</pre>
</>
);
});Use useAskableAgent() when a question should be sent with the current UI
context:
import { $ } from '@builder.io/qwik';
const agent = useAskableAgent();
const handler = $((request) =>
fetch('/api/ai', {
method: 'POST',
body: JSON.stringify(request),
}).then((response) => response.json()),
);
await agent.send('Explain this metric', handler);Imperative hook actions are QRLs and can be captured directly by onClick$.
Handlers and callback options passed to those actions must also be created with
$(). Synchronous source and context callbacks such as textExtractor,
sanitizeText, and router getters use Qwik's sync$(); asynchronous callbacks
such as sanitizeSource use $(). sync$() requires Qwik 1.6 or newer and
cannot capture lexical state. QRL invocation is asynchronous;
await mutation actions before immediately reading their updated signals.
streamFrom() is a runtime-only QRL action: create its ReadableStream or
AsyncIterable in the browser event that invokes it. Streams themselves are
not serialized into the SSR snapshot.
API
| Export | Purpose |
| --- | --- |
| useAskable(options?) | Reactive focus and prompt context signals |
| useAskableAgent(options?) | Package questions with current UI context |
| <Askable meta={...}> | Annotate rendered UI with data-askable |
| asMeta<T>(focus) | Read typed focus metadata |
Context sharing and isolation
Default hooks share a context by name + events + viewport. An unnamed hook that supplies maxHistory, sanitizeMeta, sanitizeText, sanitizeSource, or textExtractor receives a private context so capture and privacy configuration cannot affect unrelated consumers. Supplying name explicitly opts into sharing for the same events + viewport configuration, and that configuration's first mounted consumer supplies its creation options.
Context lifecycle
Qwik initializes the DOM-backed context in a visible task. ctxRef is the stable
signal for lifecycle-aware integrations; its value is undefined during SSR and
is populated after the component mounts. Read ctx only from browser actions or
visible tasks—do not destructure it during component render:
const askable = useAskable();
// Safe in an event handler after the component is visible.
const readPrompt = $(() => askable.ctx.toPromptContext());
// For reactive lifecycle code:
const ctx = askable.ctxRef.value;useAskableStream, useAskableChat, useAskableHistory,
useAskableSource, and useAskableAgent resolve this signal at call/task time,
so they do not capture the pre-mount context value.
For SSR-to-browser resume, use a QRL factory to create a hook-owned context or reconstruct a custom source in the browser:
import { $ } from '@builder.io/qwik';
import { createAskableContext } from '@askable-ui/core';
const askable = useAskable({ ctx$: $(() => createAskableContext()) });
const source = useAskableSource('stats', $(() => ({
resolve: () => ({ count: 2 }),
})));The hook observes and destroys contexts created by ctx$. Passing ctx or a
source object directly remains supported for client-only mounts; runtime objects
wrapped with noSerialize are intentionally unavailable after SSR resume.
