Contributing a toolbar
A bundled toolbar is one toolbar script in
src/toolbars/<id>/<id>.toolbar.tsx, written like the four beside it: a metadata
header, then components that render its <Toolbar>, with no imports. It is drawn in
Banner 2 like every other toolbar; what makes it its own is its plate, ink, icon,
copy and behavior. Reference material and vendor marks do not acquire a
project-wide license by being included in a contribution; licensing remains undecided.
Copy and validate
Use Node 24 or later. From a clean checkout:
npm ci
mkdir src/toolbars/your-toolbar-id
cp examples/specimen-template/your-specimen-id.toolbar.tsx src/toolbars/your-toolbar-id/your-toolbar-id.toolbar.tsxChange the header's @id and @name to match the folder. The build packages every
src/toolbars/<id>/<id>.toolbar.tsx, so adding the folder registers the toolbar. A
toolbar without a catalog entry counts as installed and follows the catalog's
toolbars in the stack.
Run npm run check, npm run test and npm run build. check type-checks your
script with strict, as it does the bundled toolbars, and validates its first frame.
Update the toolbar count and ID tests when a toolbar is accepted. Reload the unpacked
extension and the webpage. npm run test:contributor makes the same copy in a
temporary workspace, checks and builds it, runs it in Chromium and removes the
workspace, without editing your checkout. CI runs it too.
The catalog
To make the toolbar part of the lineup, add an entry to
src/registry/catalog.ts:
| Field | Meaning |
|---|---|
id | The toolbar's @id. It is kept in storage, so it is never renamed. |
order | Position in the stack and in every list: the popup, Setup and the Dashboard. |
required | Setup always installs it, Locked; it can never be hidden. Only ProSearcher is required. |
setup | 'checked' or 'unchecked': listed in Setup's Toolbars step, checked or not on the first run. |
removal | The challenge that removes it: 'pong', 'faces' or 'computer-trivia'. |
summary, tile | The line lists show, and its list tile's ink, where they differ from the script's own. |
A toolbar needs setup or required, or users have no way to install it.
Definition
The script API and validation
are the executable contract. The build renders each script's first frame through the
same intake as installed scripts, so npm run check:specimens rejects malformed parts,
themes and SVG with their location. Packaged toolbars are trusted build-time source;
the SVG checks supplement code review and are not a general-purpose sanitizer.
| Script field | Meaning |
|---|---|
@id | Unique lowercase kebab-case. |
@name | The plate's text, unless <Toolbar wordmark> gives a shorter one. |
<Toolbar icon> | An Icon constant, or self-contained inline <svg>, drawn at 16 px on the plate. No scripts, event handlers, styles, foreign objects, href/src references, remote fonts or raster traces. |
<Toolbar theme> | An ink (or brandColor), a plate shape and optionally a background; see the theme. |
<Toolbar styles> | CSS scoped to this toolbar; see styling. |
| Parts | Rendered in source order; see the toolbar. Every link destination is an absolute HTTP(S) URL, and every icon follows the brand icon's rules. |
Every bar is 32 px. The generated reference at bundleware.net/doc lists each part's props and status.
<Action.ShowPopup popup> and showPopup(spec) show a canned dialog: variant and
title, with optional body, cta, badge, source and bundles. Variants are
loading, upsell, installer, virus, survey, congrats and unavailable.
Nothing is installed, purchased, scanned or sent. Except unavailable and loading,
a completed primary action has a 50% chance of scheduling one more dialog; Escape and
Close do not. Dialogs are nonmodal and do not trap focus.
Layouts and lifecycle
classic(default): parts on the left,<Toolbar.Right>parts at the trailing edge; commands overflow into the » menu as the bar narrows, with live widgets kept.playerandticker: a track region, or text parts in a scrolling strip, which stops under reduced motion.floating-character: parts in a fixed corner container, with no plate or right edge.
Toolbars have no close button. They are shown and hidden from the popup and the Dashboard; hiding persists across tabs and restarts. Create observers, listeners, timers, speech and delayed work through the context or the toolbar hooks, which end them with the mount. When every toolbar is hidden, activity and observers stop and the page offset is removed.
AI website detector contract
The folder name ai-sidebars is historical: these adapters inspect top-level
webpages, not browser-native panels. A detector has:
import type { Detector } from './types';
export const example: Detector = {
id: 'chatgpt', // Example existing AiServiceId, not a browser identity.
serviceName: 'ChatGPT',
hosts: ['chatgpt.com', 'chat.openai.com'],
region: { label: 'Illustrative region', simulated: true },
match() { return document.querySelector<HTMLElement>('main'); },
promptInput(root) { return root.querySelector<HTMLElement>('#prompt-textarea'); },
navigationLinks(root) {
return [...root.querySelectorAll<HTMLAnchorElement>('[data-message-author-role="assistant"] a[href]')];
},
};For a new service, extend AiServiceId in ai-sidebar.svelte.ts
and the service of AiSiteSnapshot in script.ts, and register the
detector in ai-sidebars/index.ts.
Use verified hostnames, boundary-aware matching, specific interface roots and
response-link selectors. match returns the currently present root or null;
promptInput returns input, textarea or contenteditable, including late/replaced
inputs; navigationLinks selects the links whose observed clicks are reported.
A click is not proof of navigation. Functions must be repeatable and attach no
listeners themselves. Helpers in dom.ts traverse accessible open shadow roots.
Closed roots, cross-origin frames and native panels are out of scope. The controller
coalesces rescans and never observes unrelated hosts. Add fixtures for
prefilled/replaced editors and selected links.
What to include
A pull request with a new toolbar includes the script, its catalog entry if it joins the lineup, a screenshot of the bar at Wide and Narrow, and a short note on what it parodies. Do not copy executable installers or third-party artwork you cannot redistribute.