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
defineToolbarand the re-exports insrc/api/index.ts.- Svelte widgets,
WidgetMount.svelte, and thetheme.cssand widget globs. - The context's
model,openWindow(name, …),page.readTextandpage.readSelection. - The render shapes scripts cannot produce:
apiVersion, thestandardlayout,customprops, and thecta,iconandfake-popupitems.
Moving a toolbar to a script
- Start from the contributor template:
a metadata header, then one component that returns
<Toolbar>and its parts. - Put widgets and windows in the same file as components, and state in stores.
- Replace
theme.csswith the toolbar'sstylesand itstheme(an ink and a plate shape; see the theme). - Run
npm run checkandnpm 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:
| Before | Now |
|---|---|
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.checked | checked={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), nothing | value={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, era | Ignored since 0.4: theme.ink and brandShape |