building-the-toolbar
Skill727 starsChanged yesterdayMITView on GitHub
Builds, watches, checks and verifies the PostHog toolbar in both of its forms, the floating toolbar that posthog-js loads on customer pages and the browser extension that shows the toolbar in a Chrome side panel or Firefox sidebar. Use when changing anything under frontend/src/toolbar/ (floating toolbar or extension), frontend/toolbar-config.mjs, or the toolbar build and guard scripts, or when asked to build, watch, load unpacked, or test the toolbar or the toolbar extension in a browser. Covers `pnpm build:toolbar-all` and `pnpm watch:toolbar-all`, the check-toolbar-size, check-toolbar-graph and check-toolbar-csp-eval guards, why both forms must be built and kept in sync, and how to check each one in a real browser.
We split this skill into the rules it gives an agent
10 rules. About 0 are common, 5 are distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 33
Put logic in shared/. A form folder configures shared code for its form: it mounts logics, binds them to its UI, and renders shared …
- Very specific: line 34
When a component is the same in both forms, it lives in shared/ and takes its data and callbacks as props.
- Very specific: line 36
Keep each kea logic's path([...]) as it is when you move its file.
- Distinctive: line 46
Do not add an extension-only branch to shared code when a fix that serves both forms exists.
- Distinctive: line 47
When a branch is unavoidable, keep it in one place and say why in a comment.
- Very specific: line 78
When check-toolbar-graph fails, cut the new import rather than add it to bin/toolbar-graph-baseline.json.
- Distinctive: line 96
After a rebuild, reload the extension, then close and open the panel.
- Distinctive: line 97
Check both browsers when you change the extension, and walk the README's manual checks for the tools you touched.
- Very specific: line 98
Floating toolbar: Start the dev stack (see /run-posthog), open the Toolbar page at http://localhost:8010/toolbar, add a test site that …
- Distinctive: line 101
Check the forms you didn't mean to change as well.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Building the toolbar
One source tree, frontend/src/toolbar/, ships in two forms.
Both builds live in frontend/toolbar-config.mjs and share its toolbar module plugin, which swaps app modules for the toolbar's shims and keeps denylisted packages out.
| Form | Entry | Output (under frontend/dist/) | Runs in |
| Floating toolbar | src/toolbar/floating/index.tsx | toolbar.js loader, toolbar/ app chunks | Customer pages, loaded by posthog-js |
| Browser extension | src/toolbar/extension/panel.tsx, content.tsx | toolbar-extension/<browser>/, one zip per browser | Chrome side panel, Firefox sidebar (Manifest V3) |
Where code goes
| Folder | Holds |
toolbar/shared/ | The kea logics, the API and auth helpers, the shims, utils, and every component both forms render |
toolbar/floating/ | The floating toolbar: its entry and loader, the bar, and the menus that bind shared logic to its UI |
toolbar/extension/ | The extension: the panel, the content script, the page agent, and the panel's own tool components |
Inside floating/ and extension/, only the entry points the build names sit at the top. Everything else goes in a folder:
| Folder | Holds |
components/<feature>/ | React components, their stories and tests. The feature folders use the names shared/ uses, such as flags |
logics/ | The form's kea logics and the pure helpers they test. The extension splits them into panel/ and page/ by where they run |
messaging/ | Extension only: the port, page agent and form sync protocol between the panel, the content script and the page |
browser/ | Extension only: the wrappers around the extension APIs |
- Put logic in
shared/. A form folder configures shared code for its form: it mounts logics, binds them to its UI, and renders shared components with its own data. - When a component is the same in both forms, it lives in
shared/and takes its data and callbacks as props.WebExperimentVariantis the pattern:floating/components/experiments/ExperimentsTabEditorProvider.tsxandextension/components/experiments/ExtensionExperimentEditorProvider.tsxeach bind it to their own state. shared/never imports fromfloating/orextension/, and the two forms never import each other.toolbar/toolbarLayout.test.tsfails on such an import.- Keep each kea logic's
path([...])as it is when you move its file. The path is the logic's identity and storage key, not its location.
Build both, every time
The extension's content script, extension/content.tsx, runs the shared kea logics and components on the page, such as toolbarConfigLogic, elementsLogic and ElementOverlays.
The panel imports shared modules too, and both go through the same module plugin.
So any change under toolbar/shared/, or in toolbar-config.mjs, can break either form, even when you only meant to touch one.
- Build and check both forms for every toolbar change.
- Fix shared toolbar code in the general way. Do not add an extension-only branch to shared code when a fix that serves both forms exists. When a branch is unavoidable, keep it in one place and say why in a comment.
From frontend/:
pnpm build:toolbar-all # production floating toolbar, then the extension
pnpm watch:toolbar-all # rebuild both on changeTo build one form only:
| Form | Build | Watch |
| Floating toolbar | pnpm build:toolbar-floating | pnpm watch:toolbar-floating |
| Browser extension | pnpm build:toolbar-extension | pnpm watch:toolbar-extension |
The dev stack (hogli start) already runs pnpm watch:toolbar-floating through start-vite.
While it runs, start pnpm watch:toolbar-extension alone, so two watchers don't write the same files.
Run the guards
The guards read toolbar-esbuild-meta.json and dist/toolbar*, which the floating toolbar build writes.
CI runs them on a production build, so run them after pnpm build:toolbar-all, not after a watch build:
pnpm check-toolbar-size # loader size, eager-set budget, every file under the CloudFront gzip limit
pnpm check-toolbar-graph # no new toolbar-to-app import edges, no denylisted packages
pnpm check-toolbar-csp-eval # no eval, new Function or string timers in shipped codeEach script's header comment explains its limits.
When check-toolbar-graph fails, cut the new import rather than add it to bin/toolbar-graph-baseline.json.
These guards cover the floating toolbar bundle only.
The extension has no guard script, but Manifest V3 refuses eval and remote scripts, so a break there shows when you load the extension.
Also run the toolbar tests and the logic types:
pnpm exec jest src/toolbar
pnpm typegen:write # then check that git diff shows no drift in logic typesCheck it in a real browser
Tests and builds don't show a broken overlay, a missing style, or a CSP refusal. Load the build.
- Extension. Load
dist/toolbar-extension/chromeunpacked in Chrome anddist/toolbar-extension/firefoxas a temporary add-on in Firefox. The steps are in the extension README; read it before you load the extension the first time. After a rebuild, reload the extension, then close and open the panel. Check both browsers when you change the extension, and walk the README's manual checks for the tools you touched. - Floating toolbar. Start the dev stack (see
/run-posthog), open the Toolbar page athttp://localhost:8010/toolbar, add a test site that loads posthog-js to the authorized URLs, and launch the toolbar on it.Toolbar.stories.tsxrenders the floating toolbar in Storybook when you only need to look at a component.
Check the forms you didn't mean to change as well. A change to a shared logic shows up in both.
In this repository124 other files it gives its agents.
- AGENTS.mdAGENTS.md
- common/AGENTS.mdAGENTS.md
- hog/AGENTS.mdAGENTS.md
- proto/AGENTS.mdAGENTS.md
- rust/AGENTS.mdAGENTS.md
- .cursor/rules/django-python.mdcCursor rule
- .cursor/rules/react-typescript.mdcCursor rule
- .cursor/rules/rust.mdcCursor rule
- adding-activity-loggingSkill
- adding-api-scopesSkill
- adding-inbound-webhooksSkill
- adding-inbox-sourcesSkill
- adding-ingestion-warningsSkill
- adding-mcp-store-serversSkill
- adding-personhog-rpcSkill
- adding-product-alertingSkill
- adding-project-secret-api-key-authSkill
- adding-today-highlightsSkill
- adopting-generated-api-typesSkill
- analyzing-experiment-precompute-canarySkill
- analyzing-experiment-query-performanceSkill
- analyzing-insights-across-teamsSkill
- announcing-behavior-changesSkill
- auditing-llm-gateway-paritySkill
- auditing-warehouse-source-coverageSkill
- authenticating-to-clickhouseSkill
- authoring-ci-workflowsSkill
- building-product-empty-statesSkill
- clickhouse-migrationsSkill
- debugging-ci-failuresSkill
- debugging-local-replaySkill
- debugging-local-task-agent-runsSkill
- debugging-mcp-analyticsSkill
- debugging-signals-pipelineSkill
- debugging-table-access-deniedSkill
- depot-ciSkill
- depot-container-buildsSkill
- depot-github-runnersSkill
- developing-subscriptionsSkill
- django-migrationsSkill
- django-startup-timeSkill
- documenting-warehouse-sourcesSkill
- editing-agents-mdSkill
- establishing-code-ownershipSkill
- extending-hobby-smoke-testsSkill
- extending-personhog-test-harnessSkill
- finding-llm-gateway-migration-candidatesSkill
- fixing-flaky-testsSkill
- formatting-slack-messagesSkill
- gating-production-deploysSkill
- gating-sensitive-actionsSkill
- generating-clickhouse-query-performance-reportsSkill
- hogliSkill
- implementing-mcp-toolsSkill
- implementing-mcp-ui-appsSkill
- implementing-warehouse-sourcesSkill
- improving-drf-endpointsSkill
- improving-mcp-toolsSkill
- ingestion-pipeline-doctor-nodejsSkill
- instrumenting-first-party-metricsSkill
- The first 60 shown.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

