Bundleware, Toolbar Script Reference

Last site update 29th October 2026

API 1 (retired)

API 1 was the first way to write a Bundleware toolbar: a trusted TypeScript module in this repository, registered with defineToolbar from ~/api, with Svelte widgets, a theme.css file and a build step. It was retired on 2026-10-19. Every toolbar is now a toolbar script, bundled or your own, and nothing in Bundleware reads an API 1 module.

What was removed

Moving a toolbar to a script

  1. Start from the contributor template: a metadata header, then one component that returns <Toolbar> and its parts.
  2. Put widgets and windows in the same file as components, and state in stores.
  3. Replace theme.css with the toolbar's styles and its theme (an ink and a plate shape; see the theme).
  4. Run npm run check and npm run test:contributor.

From the first API 2 drafts

API 2 changed twice before its release: from Lit templates to JSX and hooks, then to a toolbar composed of parts in Raycast's style. A draft written against an earlier version needs these edits:

BeforeNow
defineToolbar({ iconSvg, theme, layout, items, rightItems, styles, setup, widgets, windows })defineToolbar(<MyToolbar />), where MyToolbar returns <Toolbar icon theme layout styles> and its parts
{ kind: 'button', label, href }<Toolbar.Button title action={<Action.OpenInBrowser url />} />
{ kind: 'button', label, action: 'go' } with context.action('go', fn) or useAction<Toolbar.Button title onAction={fn} />
{ kind: 'button', label, popup }, context.openPopup(spec)action={<Action.ShowPopup popup />}, context.showPopup(spec)
{ kind: 'dropdown', label, items }, { kind: 'split-button', … }<Toolbar.Menu title> and <Toolbar.SplitButton title …> with <Toolbar.Menu.Item> children
{ kind: 'search', placeholder, urlTemplate, buttonLabel }<Toolbar.Search placeholder url buttonTitle />
{ kind: 'text', content }, { kind: 'status', value, label, dot }, { kind: 'separator' }<Toolbar.Text text />, <Toolbar.Status text title dot />, <Toolbar.Separator />
rightItems: […]<Toolbar.Right>…</Toolbar.Right>
{ kind: 'widget', name, props } and widgets: { Name }<Toolbar.Widget><Name …props /></Toolbar.Widget>; props can be any values and update live
windows: { Name } and openWindow('Name', { title, props })<Action.OpenWindow title target={<Name …props />} />, or context.openWindow(<Name …props />, { title })
checked toggled by the host and read as event.checkedchecked={state} with onAction changing your state
setup(context)Hooks and effects in the toolbar component
useModel(key, initial), context.model(key, initial)const x = store(initial) at the top level, then useStore(x) or x.get()/x.set()
iconSvg: icons.search, <Icon name="search" />icon={Icon.Search}, <Icon name={Icon.Search} /> (the strings still work)
Name(root, context, props) { context.view(root, () => html`…`) }Name(props) { return <…>; }, with const context = useToolbar()
html`<b @click=${fn} ?disabled=${x}>${text}</b>`<b onClick={fn} disabled={x}>{text}</b>
context.state({…}), context.effect(fn)useState(…), useEffect(fn, deps)
.value=${live(x)}, ref(fn), nothingvalue={x} with onInput, useRef(), null
unsafeSVG(icons.x)<Icon name={Icon.X} />; your own markup with dangerouslySetInnerHTML
context.selection(), page.readSelection(), page.readText(o)page.read().selection, page.read(o).text
context.ai, context.openAiSetup()context.aiSite, context.openLocalAiSetup()
generate(task, text, () => fallback, signal)generate({ task, text, fallback, signal }) with a fallback string, or useGenerate()
Bundleware.createSpeechController(fn)useSpeech(), or context.speech(fn), with available instead of unavailable
:scope.bundle-bar--<id>:scope.bundle-bar (the old form still works)
theme.palette, appearanceOverrides, font, bevel, eraIgnored since 0.4: theme.ink and brandShape