pocket is the app’s API for a browser module: the argument of the browser module’s default export. With it, a browser module adds rail buttons, menu rows, launcher actions, pages, and sheets, and sets how the calls of its tools show. It also calls its module’s routes and gets its module’s events. Browser modules tells you how to write one, and what happens when it fails.
The types are Pocket and the types that it uses, in types/pocket.d.ts. A browser module is JavaScript: Types for your editor tells you how to use the types in JSDoc.
The browser module
Section titled “The browser module”default
Section titled “default”default(pocket: Pocket): void | (() => void) | Promise<void | (() => void)>;The browser module’s default export. The app imports the module and calls this function with pocket. When the function returns a function, that function runs when this version goes, as with pocket.onDispose.
pocketApi
Section titled “pocketApi”pocketApi?: number;The version of the extension API that the browser module needs. An app with an older version does not set it up. The app reads it after it imports the module, so keep the module’s top level to declarations. API versions tells you more.
About the module
Section titled “About the module”version
Section titled “version”readonly version: number;The version of the extension API. It is the same number as web.api on the server.
module
Section titled “module”readonly module: string;The module’s name: the name of its file without .ts.
readonly title: string;The module’s title, as the Extensions sheet shows it.
Drawing
Section titled “Drawing”These are the app’s own Preact and htm. Use them, and not a copy of your own, so that your components draw as the app’s components do.
readonly h: typeof h;Preact’s h.
html(strings: TemplateStringsArray, ...values: unknown[]): VNode | VNode[];htm, bound to the app’s Preact: write components as tagged templates, such as html`<p>${text}</p>`.
Fragment
Section titled “Fragment”readonly Fragment: typeof Fragment;Preact’s Fragment.
useState
Section titled “useState”readonly useState: typeof useState;Preact’s useState hook.
useEffect
Section titled “useEffect”readonly useEffect: typeof useEffect;Preact’s useEffect hook.
useLayoutEffect
Section titled “useLayoutEffect”readonly useLayoutEffect: typeof useLayoutEffect;Preact’s useLayoutEffect hook.
useRef
Section titled “useRef”readonly useRef: typeof useRef;Preact’s useRef hook.
useMemo
Section titled “useMemo”readonly useMemo: typeof useMemo;Preact’s useMemo hook.
useCallback
Section titled “useCallback”readonly useCallback: typeof useCallback;Preact’s useCallback hook.
useReducer
Section titled “useReducer”readonly useReducer: typeof useReducer;Preact’s useReducer hook.
Pieces
Section titled “Pieces”The app’s own components, so that a module looks like the rest of the app.
readonly Icon: FunctionComponent<IconProps>;One of the app’s icons by name, or an icon of your own by path. See IconProps.
Spinner
Section titled “Spinner”readonly Spinner: FunctionComponent<LoaderProps>;The app’s spinner. Screen readers read its label. See LoaderProps.
Loader
Section titled “Loader”readonly Loader: FunctionComponent<LoaderProps>;The app’s spinner with its label beside it, for a page or sheet that loads. See LoaderProps.
Markdown
Section titled “Markdown”readonly Markdown: FunctionComponent<MarkdownProps>;Shows Markdown. It cleans the HTML that it makes, as the app does for Pi’s replies. See MarkdownProps.
Switch
Section titled “Switch”readonly Switch: FunctionComponent<SwitchProps>;The app’s on and off switch. See SwitchProps.
Who and where
Section titled “Who and where”me(): Person | null;The person who uses this tab, as { id, name, role }, or null before sign-in.
conversationId
Section titled “conversationId”conversationId(): number | null;The id of the session that this tab shows, a number as in its address (/s/12), or null when it shows none.
Routes and events
Section titled “Routes and events”api<T = unknown>( path: string, body?: object, options?: { method?: WebMethod; signal?: AbortSignal },): Promise<T>;Calls one of the module’s own routes, and gives the JSON that it returns.
- Without
body, it sends a GET. Withbody, it sends a POST withbodyas JSON.methodsets another method:api("items/3", undefined, { method: "DELETE" }). - A query string in
pathgoes to the handler’squery:api("usage?days=7"). signalstops the call.- When the server answers with an error, the promise rejects with an error that has the server’s words and its
status. - It rejects at once for a path out of the module’s routes: a path with a
.or..part, a leading//, or a scheme such ashttps:.
on<T = unknown>(event: string, run: (data: T) => void): () => void;Calls run with the data of each event that the module sends with web.send. It returns a function: call it to stop. The app keeps no events for a page that is closed. Thus a page that opens must first get the data that is current, and then listen. Events tells you more.
Adding to the app
Section titled “Adding to the app”Each thing that a module adds has an id of its own. To add one with the same id again replaces it. Names gives the rules for ids.
What a module adds draws at the next frame, one time for all its changes since the last frame. Change what you added from an event, a timer, or an effect, and not while a component draws. A module that changes what it added each time that it draws is stopped in that browser. Browser modules tells you about these rules.
rail(spec: RailSpec): Added<RailSpec>;Adds a button on the rail, after the app’s own tools. It shows in the sidebar, in the drawer, and on a phone’s home screen. See RailSpec.
menu(spec: MenuSpec): Added<MenuSpec>;Adds a row in the App group of the menu. See MenuSpec.
launcher
Section titled “launcher”launcher(spec: LauncherSpec): Added<LauncherSpec>;Adds an action in the launcher (Ctrl+K, or ⌘+K). See LauncherSpec.
page(spec: PageSpec): Added<PageSpec>;Adds a page. A page shows in the place of the session, as the subagents board does. See PageSpec.
sheet<P extends object = object>(spec: SheetSpec<P>): Added<SheetSpec<P>>;Adds a sheet, as the app’s own sheets. See SheetSpec.
toolCall
Section titled “toolCall”toolCall<A = Record<string, unknown>>(spec: ToolCallSpec<A>): { remove(): void };Sets how the calls of one of the module’s own tools show in transcripts and peek tiles. Without it, a call shows as the tool’s name, with its arguments as JSON. It goes with the version that added it. See ToolCallSpec. How tool calls show tells you more.
Opening and closing
Section titled “Opening and closing”A module opens and closes only its own pages and sheets. A version that is gone, or that failed, opens and closes nothing.
openPage
Section titled “openPage”openPage(id: string): void;Opens the module’s page with this id. A sheet or the launcher over the page closes.
closePage
Section titled “closePage”closePage(): void;Closes the module’s page that shows, whichever page it is.
openSheet
Section titled “openSheet”openSheet(id: string, props?: object): void;Opens the module’s sheet with this id. Its component gets the fields of props.
closeSheet
Section titled “closeSheet”closeSheet(): void;Closes the module’s sheet that shows.
Notices, styles, and cleanups
Section titled “Notices, styles, and cleanups”notify
Section titled “notify”notify(level: NoticeLevel, message: string): void;Shows a notice, as the app’s own notices: "info", "warning", or "error". A module can show only a few notices in a short time, for all its versions together. Its actions that fail and its stylesheets that do not load count too. The app drops the rest. Limits gives the numbers. A version that is gone, or that failed, shows none.
style(path: string): void;Loads a stylesheet from the browser module’s folder, such as pocket.style("look.css"), into the app’s own cascade layer (a theme’s rules win over it). The stylesheet goes with this version. A path out of the folder throws. When the stylesheet does not load, a notice says so. A browser module that is one file has no folder, so it cannot load a stylesheet.
onDispose
Section titled “onDispose”onDispose(dispose: () => void): void;Runs dispose when this version goes: an edit replaces it, or the owner turns the module off. When the version is already gone, or failed, it runs dispose at once. The app does not wait for a browser cleanup: do at once what must be done.
The objects you give
Section titled “The objects you give”Action
Section titled “Action”type Action = | { page: string; sheet?: never; run?: never } | { sheet: string; page?: never; run?: never } | { run: () => unknown; page?: never; sheet?: never };What a rail button, a menu row, or a launcher action does when a person uses it. Give exactly one of these fields:
page: the id of a page that the module adds. The button, row, or action opens it.sheet: the id of a sheet that the module adds. It opens it.run: a function that it calls. When the function throws, or its promise rejects, a notice says so.
A rail button that opens a page shows when the page is open. A second tap closes the page.
Buttons, rows, and actions of modules show in the order in which the server lists the modules. An edit keeps the module’s place.
RailSpec
Section titled “RailSpec”type RailSpec = Action & { id: string; title: string; icon?: string; iconPath?: string; badge?: number | string | null | false;};A button on the rail.
id: the button’s id in the module.title: the button’s label.icon: the name of one of the app’s icons, such aspulse,grid,globe,key,chat,folder, orsearch. An unknown name showsgrid.iconPath: an icon of your own, as thedof a stroked SVG path in a 24 × 24 box.badge: a short count or word on the button.0,NaN,-Infinity,"","0",null, andfalseshow no badge, as the app’s own counts do at 0. A count that is too large shows a maximum with+. Long text is cut, also in the button’s label, without cutting a character in two. Limits gives the numbers.
Modules’ rail buttons have a fixed height together. When they need more, they scroll.
MenuSpec
Section titled “MenuSpec”type MenuSpec = Action & { id: string; label: string; hint?: string;};A row in the App group of the menu.
id: the row’s id in the module.label: the row’s words.hint: short words on the right of the row.
LauncherSpec
Section titled “LauncherSpec”type LauncherSpec = Action & { id: string; label: string; detail?: string; icon?: string; iconPath?: string; keywords?: string[];};An action in the launcher.
id: the action’s id in the module.label: the action’s words.detail: a line under the label. The default is the module’s title.icon,iconPath: the action’s icon, as for a rail button.keywords: more words that find the action when a person types them.
PageSpec
Section titled “PageSpec”interface PageSpec { id: string; title: string; component: ComponentType<PageProps>; escape?: boolean; swipe?: boolean;}A page in the place of the session. It has its title and a close button. On a phone, it fills the screen.
id: the page’s id in the module.title: the page’s title.component: what the page shows. It getsPageProps.escape: withfalse, the page keeps Esc for itself. Esc still closes a sheet, the launcher, or PiP over the page. The close button and back still close the page.swipe: withfalse, no swipe on the page goes back, also at the screen’s edge.
Esc and back close a page. Esc does not close it while an input method composes a character. On a narrow touch screen, a swipe to the right on the page goes back. This is not so in a field, or in an area that scrolls sideways. In an app added to the home screen of an iPhone or iPad, a swipe in from the left edge also goes back. A data-no-swipe attribute on an element stops the swipe on it and in it, for example on a slider. Limits gives the width.
SheetSpec
Section titled “SheetSpec”interface SheetSpec<P extends object = object> { id: string; title: string; component: ComponentType<SheetProps<P>>; wide?: boolean;}A sheet, as the app’s own sheets.
id: the sheet’s id in the module.title: the sheet’s title.component: what the sheet shows. It getsSheetProps.wide: withtrue, the sheet is wider.
A page or sheet can open with an id that the module never added. Then it says that the module no longer has it.
PageProps
Section titled “PageProps”interface PageProps { pocket: Pocket; close(): void;}What the component of a page or sheet gets.
pocket: thepocketof the version that added the page or sheet.close: closes this page or sheet, of this version, while it shows. When you call it later, it closes nothing else. To close whichever page of the module shows, usepocket.closePage().
SheetProps
Section titled “SheetProps”type SheetProps<P extends object = object> = P & PageProps;What a sheet’s component gets: the fields of the props that openSheet gave, and pocket and close.
ToolCallSpec
Section titled “ToolCallSpec”interface ToolCallSpec<A = Record<string, unknown>> { tool: string; icon?: string; iconPath?: string; label: (args: A) => string; subject?: (args: A) => string; summary?: (args: A) => CallSummary;}How the calls of one tool show, from their arguments.
tool: the tool’s name, exactly as Pi calls it. The tool must be one that the module’s own extensions install. The app refuses any other tool, so no module changes how the calls of another module show. With a server that is too old to list the module’s tools,toolCalldoes nothing.label: the words of the call’s line, such as"start session".subject: what the call acts on.summary: how a group of calls counts this call. SeeCallSummary.icon,iconPath: the call’s icon in peek tiles, as for a rail button.
Each function gets a copy of the call’s arguments. What it returns shows as one line of text, never as HTML. When a function throws, or returns something that is not text, the call shows the default way. The browser’s console has the error.
CallSummary
Section titled “CallSummary”interface CallSummary { verb: string; noun?: string; by?: string;}How a group of calls counts a call. The calls count by the values of their argument by. For example, { verb: "messaged", noun: "session", by: "to" } gives “messaged 2 sessions” for calls to two addresses, however many calls there are. Without noun, the group shows only the verb, such as “checked linked sessions”.
interface Added<S> { update(patch: Partial<S>): void; remove(): void;}What rail, menu, launcher, page, and sheet return.
update: changes the fields inpatch. An update that changes nothing draws nothing again. Values count as the same only when they are the very same value: a new array or function is a change.remove: removes what was added.
The props of the pieces
Section titled “The props of the pieces”IconProps
Section titled “IconProps”interface IconProps { name?: string; path?: string; size?: number;}name: the name of one of the app’s icons.path: an icon of your own, as foriconPath.size: its width and height, in pixels.
LoaderProps
Section titled “LoaderProps”interface LoaderProps { label?: string;}The props of Loader and Spinner. label says what loads, in words.
MarkdownProps
Section titled “MarkdownProps”interface MarkdownProps { text: string;}text is the Markdown to show.
SwitchProps
Section titled “SwitchProps”interface SwitchProps { on: boolean; label: string; onChange: () => void; disabled?: boolean;}on: whether the switch is on.label: the switch’s name, for screen readers.onChange: runs when a person turns the switch. Setonagain from it.disabled: withtrue, nobody can turn the switch.
CSS names Pi Pocket keeps
Section titled “CSS names Pi Pocket keeps”To look like the app, use its colors and classes. The colors follow the theme. Pi Pocket keeps these names. The rest of its CSS can change. These names are a subset of the theme surface, the one full list of the tokens, parts, and states Pi Pocket keeps.
- Colors:
var(--text),var(--muted),var(--accent),var(--bg),var(--card),var(--line),var(--err),var(--warn), andvar(--ok). - Classes:
button(withprimary),error-box,muted,small,mono, andsetting(a row with a label and a control).
These pages describe Pi Pocket 0.14.0. Your copy has the pages of its own version in its docs/ folder.