Bundleware, Toolbar Script Reference

Last site update 29th October 2026

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.tsx

Change 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:

FieldMeaning
idThe toolbar's @id. It is kept in storage, so it is never renamed.
orderPosition in the stack and in every list: the popup, Setup and the Dashboard.
requiredSetup 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.
removalThe challenge that removes it: 'pong', 'faces' or 'computer-trivia'.
summary, tileThe 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 fieldMeaning
@idUnique lowercase kebab-case.
@nameThe 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.
PartsRendered 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

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.