A tree input plugin for Tweakpane that allows users to select items from a hierarchical tree structure.
- Hierarchical Tree Structure: Support for arbitrarily nested tree nodes
- Native HTML Elements: Uses
<details>and<summary>elements for native expand/collapse behavior - Type-Safe: Fully typed TypeScript implementation
- Path Tracking: Returns complete path information including indices, values, and leaf value
- Dynamic trees & DOM diffing: The view attempts to reuse existing DOM elements when rebuilding the tree to reduce reflows and preserve element identity — see
docs/dynamic-trees.mdfor details.
<script type="module">
import {Pane} from 'tweakpane';
import * as TweakpaneTreePlugin from 'tweakpane-plugin-tree';
const pane = new Pane();
pane.registerPlugin(TweakpaneTreePlugin);
</script>import {Pane} from 'tweakpane';
import * as TreePlugin from 'tweakpane-plugin-tree';
const pane = new Pane();
pane.registerPlugin(TreePlugin);const params = {
selectedItem: {
treePathIndices: [],
treePathValues: [],
leafValue: undefined
}
};
pane.addBinding(params, 'selectedItem', {
view: 'tree',
children: [
{
label: 'Colors',
children: [
{ label: 'Red', value: '#ff0000' },
{ label: 'Green', value: '#00ff00' },
{ label: 'Blue', value: '#0000ff' }
]
},
{
label: 'Sizes',
children: [
{ label: 'Small', value: 10 },
{ label: 'Medium', value: 20 },
{ label: 'Large', value: 30 }
]
}
]
}).on('change', (ev) => {
console.log(ev.value);
// {
// treePathIndices: [0, 1],
// treePathValues: [null, '#00ff00'],
// leafValue: '#00ff00'
// }
});Leaf nodes represent selectable items:
{
label: string; // Display text
value?: unknown; // Optional value associated with this item
}Branch nodes contain children and can optionally be selectable themselves:
{
label: string; // Display text
children: TreeChildren; // Array of child options or nodes
value?: unknown; // Optional value associated with this node
}When you bind a tree input, the external value has the following structure:
{
treePathIndices: number[]; // Array of indices representing the path from root to selected item
treePathValues: unknown[]; // Array of values at each level of the path
leafValue: unknown; // The value of the selected leaf item
}- Reading: Only the
treePathIndicesproperty is read from the bound object - Writing: All three properties (
treePathIndices,treePathValues,leafValue) are written to the bound object
See the test page for a complete working example.
-
Install deps:
npm install(repository uses npm; devDependencies are listed inpackage.json). -
Dev server:
npm run devornpm start— runs the Vite dev server and openstest/browser.htmlfor quick visual debugging. -
Build (dev):
npm run build:dev— Vite build (unminified output). -
Build (prod):
npm run build:prod— Vite build in production mode (minified). The convenience scriptnpm run buildruns bothbuild:dtsand Vite builds in parallel. -
Type definitions:
npm run build:dtsrunstsc --project src/tsconfig-dts.jsonto emit.d.tsfiles intodist/types. -
Lint:
npm run lint— runseslint(fast check; use this for CI and local validation). -
Test:
npm test— runsts-node test/visual-test.ts, a visual-regression test that:- starts a Vite dev server rooted at
test/(default port 7357) - launches Puppeteer to open
test/browser.html - captures screenshots into
test/__screenshots__and compares them against baselines usinglooks-same - writes diff images to
test/__screenshots__when mismatches are found and exits non-zero on failure
Use
npm test -- --acceptto accept and overwrite baseline screenshots, ornpm test -- --only-serverto start the server without running the browser checks.Note: The repository also contains experimental Vitest tests (
npm run test-vitest) that were added as an experiment; they are incomplete and flaky and should be ignored for CI/publishing. - starts a Vite dev server rooted at
-
Packaging helpers: after build,
npm run assetswill append versions and create zip artifacts.
If npm test fails or behaves unexpectedly, try these steps:
-
Quick checks:
- Make sure you ran
npm installand that thepuppeteerChromium binary exists (look fornode_modules/puppeteer/.local-chromium). - Run
npm test -- --only-serverand openhttp://localhost:7357/test/browser.htmlto inspect the page manually.
- Make sure you ran
-
CI / headless environments:
- Some CI runners need additional system libraries for Chromium. On Linux, install deps like
libnss3,libx11-6,libxcomposite1,libxrandr2,libasound2, and fonts. - On GitHub Actions,
ubuntu-latestusually includes Chrome. Otherwise install Chrome/Chromium and setPUPPETEER_EXECUTABLE_PATHto the binary path. - If the runner blocks sandboxing, run Chromium with
--no-sandbox --disable-setuid-sandbox(configure your CI container or use a Docker image that allows this).
- Some CI runners need additional system libraries for Chromium. On Linux, install deps like
-
Troubleshooting diffs:
- Baseline screenshots live in
test/__screenshots__/s-<slug>.png. New runs creates-<slug>-new.pngand diff imagess-<slug>-diff.pngwhen mismatches occur. - Use
npm test -- --acceptto accept intentional UI changes and overwrite baselines.
- Baseline screenshots live in
-
Flakiness & rendering differences:
- Visual tests can fail due to fonts, OS-level rendering differences, or network timing. Use
--only-serverto inspect and stabilize resources, or add small waits in the test page.
- Visual tests can fail due to fonts, OS-level rendering differences, or network timing. Use
Note: The Vitest experiments remain in the repo (npm run test-vitest), but they are experimental and not used for CI/publishing.
Notes: The CSS is compiled from src/sass/plugin.scss and inlined into the css export by the bundler at build time (see vite.config.ts). CI and prepublish steps call npm test (see prepublishOnly), so ensure lint passes before publishing.
MIT


