@pebble-software/bonsai 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +31 -3
  2. package/dist/ast.js +1 -1
  3. package/dist/chunks/{builder.meta-WpXc5VBB.js → builder.meta-B7Y9M7Ab.js} +128 -122
  4. package/dist/chunks/builder.meta-B7Y9M7Ab.js.map +1 -0
  5. package/dist/chunks/create-renderer-C4nV9RV6.js +282 -0
  6. package/dist/chunks/create-renderer-C4nV9RV6.js.map +1 -0
  7. package/dist/chunks/index-CD5pykpk.js +367 -0
  8. package/dist/chunks/index-CD5pykpk.js.map +1 -0
  9. package/dist/chunks/{render-shared-Ci28BpkI.js → render-shared-Cwb527d4.js} +487 -445
  10. package/dist/chunks/render-shared-Cwb527d4.js.map +1 -0
  11. package/dist/components/core/render-shared.d.ts +25 -15
  12. package/dist/components/core/render-tree-client.d.ts +1 -1
  13. package/dist/components/ui/accordion.d.ts +1 -1
  14. package/dist/core.d.ts +1 -1
  15. package/dist/core.js +3 -3
  16. package/dist/core.js.map +1 -1
  17. package/dist/index.css +8 -0
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.js +14 -10
  20. package/dist/index.js.map +1 -1
  21. package/dist/lib/ast/evaluator.d.ts +1 -1
  22. package/dist/lib/ast/helpers.d.ts +3 -1
  23. package/dist/lib/ast/index.d.ts +1 -1
  24. package/dist/lib/ast/types.d.ts +4 -1
  25. package/dist/lib/ast/utils.d.ts +1 -1
  26. package/dist/lib/registry/json-schema-to-zod.d.ts +2 -2
  27. package/dist/lib/renderer/compile-template.d.ts +10 -2
  28. package/dist/lib/renderer/safe-props.d.ts +4 -0
  29. package/dist/library/schemas/src/bonsai.schema.d.ts +1 -2
  30. package/dist/presets/calendar.js.map +1 -1
  31. package/dist/presets/signature.js +1 -1
  32. package/dist/presets/signature.js.map +1 -1
  33. package/dist/renderer/core-registry.d.ts +1 -1
  34. package/dist/schemas.js +1 -1
  35. package/dist/styles/css/theme.css +8 -0
  36. package/dist/types.d.ts +8 -1
  37. package/package.json +7 -12
  38. package/dist/chunks/builder.meta-WpXc5VBB.js.map +0 -1
  39. package/dist/chunks/create-renderer-DHneTPd-.js +0 -280
  40. package/dist/chunks/create-renderer-DHneTPd-.js.map +0 -1
  41. package/dist/chunks/index-DzpRdatr.js +0 -359
  42. package/dist/chunks/index-DzpRdatr.js.map +0 -1
  43. package/dist/chunks/render-shared-Ci28BpkI.js.map +0 -1
  44. package/dist/components/ui/alert-dialog.d.ts +0 -23
  45. package/dist/components/ui/breadcrumb.d.ts +0 -10
  46. package/dist/components/ui/combobox.d.ts +0 -31
  47. package/dist/components/ui/command.d.ts +0 -23
  48. package/dist/components/ui/dialog.d.ts +0 -23
  49. package/dist/components/ui/drawer.d.ts +0 -22
  50. package/dist/components/ui/dropdown-menu.d.ts +0 -29
  51. package/dist/components/ui/form.d.ts +0 -24
  52. package/dist/components/ui/popover.d.ts +0 -9
  53. package/dist/components/ui/scroll-area.d.ts +0 -13
  54. package/dist/components/ui/sidebar.d.ts +0 -70
  55. package/dist/components/ui/toast.d.ts +0 -46
  56. package/dist/components/ui/tooltip.d.ts +0 -15
  57. package/dist/hooks/haptics.d.ts +0 -8
  58. package/dist/hooks/use-mobile.d.ts +0 -1
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/%40pebble-software%2Fbonsai)](https://www.npmjs.com/package/@pebble-software/bonsai)
4
4
  [![status: preview](https://img.shields.io/badge/status-preview-orange)](https://github.com/PebbleSoftwareInc/pebble-bonsai)
5
- [![react](https://img.shields.io/badge/react-18%20%7C%2019-61dafb)](https://react.dev)
5
+ [![react](https://img.shields.io/badge/react-19.2%2B-61dafb)](https://react.dev)
6
6
 
7
7
  > ⚠️ **Active development / preview** — this package is pre-1.0 and evolving quickly. APIs, the configuration schema, and the component set may change between releases without a deprecation cycle. Pin an exact version and read release notes before upgrading. Feedback and issues are very welcome!
8
8
 
@@ -22,6 +22,7 @@ Bonsai is the rendering engine behind [Pebble Bonsai](https://github.com/PebbleS
22
22
  - [Expression language](#expression-language)
23
23
  - [Structural nodes: each & if](#structural-nodes-each--if)
24
24
  - [Controlled inputs & state](#controlled-inputs--state)
25
+ - [Click actions](#click-actions)
25
26
  - [Component reference](#component-reference)
26
27
  - [Styling](#styling)
27
28
  - [Extending Bonsai](#extending-bonsai)
@@ -67,7 +68,7 @@ npm and pnpm don't auto-install optional peers — if you import the root entry
67
68
 
68
69
  Requirements:
69
70
 
70
- - `react` and `react-dom` 18 or 19
71
+ - `react` and `react-dom` 19.2 or newer
71
72
  - A bundler that understands ES modules (Vite, Next.js, Rspack, etc.) — the package is ESM-only.
72
73
  - Tailwind CSS v4 in your build, for the styles — `index.css` ships as Tailwind source (see [Styling](#styling)).
73
74
 
@@ -245,6 +246,7 @@ Submission only happens when the instance renders a `<form>` (see below) and the
245
246
  - `as: "div"` renders the whole instance as a `<div>` instead of a `<form>` (and no submit handler is attached). Use it when embedding an instance inside a host `<form>`, or when rendering sibling/nested instances, where nested `<form>` elements would be invalid HTML.
246
247
  - The two combine predictably: `render: "pdf"` always wins and produces a `<div>`; otherwise `as` decides.
247
248
  - Components read the mode with `useBonsaiRenderMode()`. The built-in `signature` uses it: under `render: "pdf"` it shows the captured image (or an empty signature line) instead of the interactive pad.
249
+ - Templates restyle their PDF output with the `pdf:` variant: `pdf:hidden`, `pdf:text-sm`, `pdf:break-before-page`. It matches only inside a `render: "pdf"` tree and sorts after the responsive and `print:` variants, so `pdf:text-sm` wins over `md:text-xl` (an A4 page, 794px wide, is past the `md` breakpoint). It ships in `theme.css` and in the bundled stylesheet.
248
250
 
249
251
  Each mounted instance owns its own store — there's no shared module-level state and no global `updateData`; `useUpdateData()` is the only way to reach a given instance's data, and only from within its subtree.
250
252
 
@@ -261,6 +263,7 @@ The template format is defined by the Zod schema in [`library/schemas/src/bonsai
261
263
  **The renderer does not validate templates; it renders whatever it is given.** The schema is a discriminated union on `type` with a per-component `args` contract, and the Pebble Bonsai platform validates every template against it before rendering. At render time:
262
264
 
263
265
  - An unknown `type` (one not in the active registry) follows the registry's `fallback`: a plain `<div>` by default (all built-in registries), a component of your choosing, or — with `fallback: "error"` — an error, which the node's error guard logs and renders as nothing.
266
+ - A few `args` never reach the component, whatever the template says: raw-HTML props (`dangerouslySetInnerHTML`, `srcDoc`) and overrides of where a form submits (`formAction`, `formMethod`, `formTarget`, `formEncType`). URL props (`href`, `src`, `action`, `xlinkHref`) keep only `http(s):`, `mailto:`, `tel:`, `data:image/` and relative URLs, bound values included; anything else is dropped, and an `image` without a usable `src` renders nothing. This is a floor, not a replacement for validation: pages that render untrusted templates should also send a Content Security Policy without `'unsafe-inline'` scripts.
264
267
  - A node that throws while rendering is logged with `console.error` and rendered as nothing; the rest of the tree still renders.
265
268
  - The exported `Config` type is `any`, so TypeScript won't catch template mistakes either.
266
269
 
@@ -419,6 +422,29 @@ Allowlisted methods: `map`, `filter`, `some`, `every`, `find`. Anything else —
419
422
  - The store's `data` is deep-copied from the `data` prop *for plain objects and arrays* — an instance never mutates the object you passed in, and two instances seeded from the same plain-object `data` still get fully independent state. **Non-plain values nested inside `data` (class instances, `Map`/`Set`, functions, `Date`, `File`, etc.) are passed through by reference, not cloned.** If `data` nests something like `{ profile: new SomeClass() }`, that nested value is shared between every instance seeded from it, and writes through it are visible to the caller. Keep non-plain values out of `data` if you need per-instance isolation.
420
423
  - **Reactivity model**: every leaf reads the store itself (via `valtio`'s `useSnapshot`) and evaluates its own bindings against that snapshot — the read *is* the subscription. A node with no bindings at all (a plain layout `div`) is detected and skipped from this entirely, so it never re-renders due to an unrelated store write. This is what keeps a keystroke in one field from re-rendering the rest of the form.
421
424
 
425
+ ## Click actions
426
+
427
+ Any component can take an `onClick` arg: a list of store writes applied in order when it's clicked. Templates carry data, never code: each action names a store path (`set`) and the value to write there (`to`).
428
+
429
+ ```json
430
+ {
431
+ "type": "button",
432
+ "args": {
433
+ "type": "button",
434
+ "className": "{{pizzaChoice == 'pizza1' ? 'bg-accent-orange' : 'bg-background'}}",
435
+ "onClick": [{ "set": "pizzaChoice", "to": "pizza1" }]
436
+ },
437
+ "children": ["Classic Margherita"]
438
+ }
439
+ ```
440
+
441
+ - `set` is a plain path using the same grammar as bindings (`"order.size"`, `"items[0].done"`), not a `{{binding}}`. Missing objects along the path are created.
442
+ - `to` is required. It's either a literal (`"pizza1"`, `true`, `null` to clear, an object) or a `{{...}}` expression such as `"{{!open}}"` or `"{{step + 1}}"`. Every `to` in one click is evaluated against the state from before the click, so `[{ "set": "step", "to": "{{step + 1}}" }, { "set": "previous", "to": "{{step}}" }]` records the old step. Object values are copied into the store, so later edits never change the template.
443
+ - Inside an `each`, a path that starts with the row alias (`"row.selected"`) writes to that row's real store path, just like a two-way binding. Rows of a computed `items` expression have no store path, so clicks there write nothing.
444
+ - Use a `button` with `"type": "button"` for anything that looks clickable. It gets keyboard and focus support for free, and inside the form wrapper a button without a type submits the form.
445
+ - Handlers only exist after client hydration. The server render emits the same markup with or without `onClick`.
446
+ - In a registry, a component that lists `capabilities` must include `"events"` to receive click actions (see [Bring your own registry](#bring-your-own-registry)). All built-in components include it.
447
+
422
448
  ## Component reference
423
449
 
424
450
  The full list of built-in component types is the `COMPONENT` enum in [`library/constants/src/components.ts`](https://github.com/PebbleSoftwareInc/pebble-bonsai/blob/main/library/constants/src/components.ts); allowed `args` for each are in the [schema](#configuration-schema).
@@ -459,7 +485,7 @@ Bonsai is styled with **Tailwind CSS v4**. Components emit plain Tailwind utilit
459
485
  - **Compile it with your own Tailwind v4 build.** `index.css` ships as uncompiled Tailwind source: it opens with `@import "tailwindcss"` and carries the full theme (an OKLCH color palette with swappable accent colors, typography, shadows) and base styles. Its `@source "./"` points Tailwind at Bonsai's compiled `dist/`, so every class the built-in components use is generated. `tailwindcss` and `tw-animate-css` are optional peers for this reason. None of the JavaScript entry points import it; it is the only side effect the package declares (`"sideEffects": ["**/*.css"]`), so bundlers can tree-shake everything else.
460
486
  - **It styles the whole page, not just Bonsai.** The stylesheet includes Tailwind's Preflight reset plus base rules for `body` (font, background, text color) and `h1`–`h6` (heading font). On a page that has its own design system, load it deliberately — for example, only on the routes that render Bonsai.
461
487
  - **Fonts load from Pebble's CDN.** Sora, Fredoka, and Great Vibes (used by the signature control) are declared with `@font-face` pointing at `https://brand.pebble.software/fonts/`. If you use a Content Security Policy, allow that origin in `font-src`, or override `--font-body`, `--font-heading`, and `--font-signature` with fonts you host.
462
- - **Scoping**: every rendered tree is wrapped in a `.bonsai-root` element carrying a `data-mode` attribute (`web` / `pdf` / `preview`), so hosts can scope their own overrides per render mode.
488
+ - **Scoping**: every rendered tree is wrapped in a `.bonsai-root` element carrying a `data-mode` attribute (`web` / `pdf` / `preview`), so hosts can scope their own overrides per render mode; templates use the `pdf:` variant for the same thing (see [Wrapper element and render mode](#wrapper-element-and-render-mode)).
463
489
  - **Accent colors**: set `data-theme` on any ancestor (`<body data-theme="indigo">`) to switch the accent palette. Available values: `red`, `orange`, `amber`, `yellow`, `lime`, `green`, `emerald` (default), `teal`, `cyan`, `sky`, `blue`, `indigo`, `violet`, `purple`, `fuchsia`, `pink`, `rose`.
464
490
  - **Theming**: the theme is token-driven — redefine the CSS variables (e.g. `--color-main`, `--font-heading`) after the stylesheet to restyle every component at once, or target `.bonsai-root` descendants for finer overrides.
465
491
  - **Utilities in your templates**: a `className` utility that no built-in component uses (say, `grid-cols-5`) is only generated if your build also scans the templates that use it — add their location with `@source`. Inside the Pebble Bonsai platform, CSS is compiled per template so this never comes up.
@@ -543,6 +569,8 @@ const acmeRegistry = defineBonsaiRegistry({
543
569
  export const { Bonsai } = createBonsaiRenderer({ registry: acmeRegistry });
544
570
  ```
545
571
 
572
+ `capabilities` is optional; leaving it out means unrestricted. When present, `validateTemplate` rejects anything the list doesn't allow: children without `"children"`, a render mode without `"ssr"`/`"pdf"`, and [click actions](#click-actions) without `"events"`. The renderer also skips `onClick` on a component without `"events"`. In the example above, `rating` can't take `onClick`; add `"events"` to allow it.
573
+
546
574
  A `registry` prop on an individual `<Bonsai>` replaces the renderer's registry for that instance only. `extendRegistry(base, preset)` layers a preset-shaped `{ components, controlledInputs }` over any registry — `createBonsaiRenderer` uses it for its `components`/`controlledInputs` options.
547
575
 
548
576
  ### Contributing a built-in component
package/dist/ast.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as r, c as i, e as p, i as o, p as x } from "./chunks/index-DzpRdatr.js";
1
+ import { a as r, c as i, e as p, i as o, p as x } from "./chunks/index-CD5pykpk.js";
2
2
  import "jsep";
3
3
  export {
4
4
  r as clearExpressionCache,