A starter mod for browser-pane: a tab with a page and a server module. Copy it, rename it, and build your own.

A starter mod for browser-pane: a tab in browser-pane's shared browser window, with a page and a server module. /template <text> shows the text in the tab; the page reads the server module's data and keeps notes.
my-mod and turn the tab into a viewer for …". Its CLAUDE.md points Claude Code to integration.md, which explains how the mod and browser-pane fit together, and lists what to rename.From browser-pane's repository, inside Claude Code (browser-pane comes with it):
/plugin install browser-pane-template --marketplace sandipchitale/browser-pane
This folder is also a marketplace of its own: it lists this mod and browser-pane. So a copy made into a repository of its own installs in one line too:
/plugin install browser-pane-template --marketplace <owner>/<repo>
It needs what browser-pane needs: the Claude Code CLI, Node.js and Google Chrome.
claude plugin validate .
claude plugin test . # the hooks
node --test tab-server/ # the server module
MIT
hooks/register.ts 40 lines1import type { Register } from 'claude-code'
2
3// The names a copy changes first (see integration.md, "Make it yours"): the tab's id and title here,
4// and the command, written as a literal where it is registered and matched (claude plugin validate
5// reads those literals to know the hook answers its own command). The tab id must be unique across
6// mods, so use your mod's name.
7const TAB = 'template'
8const TITLE = 'Template'
9
10export const register: Register = on => {
11 // Register the tab once per session. Cheap: browser-pane starts nothing until the first show.
12 on('session.start', async ($, e, next) => {
13 await $.browserPane.register({
14 id: TAB,
15 title: TITLE,
16 root: `${$.plugin.root}/tab`,
17 server: `${$.plugin.root}/tab-server/server.mjs`,
18 description: 'A starter tab: /template [text] shows the text here.',
19 pinned: true, // first in the Guide and the tab strip, until the person unpins it
20 })
21 // Data the tab's server module needs even when the tab is opened from the Guide, not by a show.
22 await $.browserPane.update({ tab: TAB, data: { session: await $.session.id() } })
23 await $.command.register({ name: 'template', description: `Show text in the ${TITLE} tab: /template [text]` })
24 return next(e)
25 })
26
27 on('command.run', { command: 'template' }, async ($, e) => {
28 const text = e.args.trim() || 'Hello from Claude Code'
29 const r = await $.browserPane.show({
30 tab: TAB,
31 select: text, // reaches the page: browserPane.onSelect
32 data: { session: await $.session.id(), shownAt: new Date().toISOString() }, // reaches the server module: update
33 })
34 if (r.isOpened) return { text: `template: "${text}" is in the ${TITLE} tab (${r.how}).` }
35 // No window here (the desktop app or an IDE has no $.process, or Node is missing): say so, or show
36 // the content another way, say in a Pane.
37 return { text: `template: no browser window (${r.reason}). You said: ${text}` }
38 })
39}
40