typebulb 0.45.1 → 0.46.0

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.
package/README.md CHANGED
@@ -46,6 +46,7 @@ typebulb send <file> tb:snapshot Print the live page's rendered outline (roles,
46
46
  typebulb send <file> tb:rect … Print a named control's rect ('tb:rect button "Pass"' → {x,y,width,height} + viewport)
47
47
  typebulb send <file> tb:click … Click a control by role+name ('tb:click button "Pass"'); the reply is a fresh snapshot
48
48
  typebulb send <file> tb:set … Set a form control ('tb:set combobox "level" = hard'), firing input+change
49
+ typebulb send <file> tb:png … Save the live canvas as PNG, print its path (sole canvas needs no name; 'tb:png "<name>"' among several)
49
50
  typebulb get <file> <kind> Print one block's content (data, insight, code, …) to stdout
50
51
  typebulb put <file> <k>=<src> Write a file's (or stdin's) content into a block, surgically
51
52
  typebulb pull <url|file> Fetch a bulb from typebulb.com into typebulbs/u/<user>/<slug>.bulb.md
@@ -272,6 +273,7 @@ That one launch *is* the loop: the server watches the file, so every save recomp
272
273
  - **Acting on the page** — `typebulb send <file> 'tb:click button "Pass"'` clicks the one control matching that role and name (exact, else a unique case-insensitive substring) and replies with a fresh snapshot; `tb:set combobox "strength" = hard` is the same for form controls (checkboxes and radios take `tb:click`). A disabled, readonly, or covered target is an error naming it — that silence is the bug class these verbs catch. Needs exactly one page open, and the reply is the immediate frame (slow work: follow up with `tb:snapshot`). Only what the outline names is targetable: real `<button>`s and labeled controls, not an `onClick` `<div>`.
273
274
  - **Poking state** — for state beyond what a form control expresses (`tb:set` covers those), author a set-handler up front: a `tb.onMessage` branch that takes a data payload (JSON arrives parsed), applies it to your state — committing the change if your framework needs an explicit step — and returns the new state: `typebulb send <file> '{"set":"speed","value":2}' --wait` prints it. In React, register it in an effect so it closes over the setters (the returned unsubscribe is the cleanup).
274
275
  - **A page must be open** — the CLI runs no browser of its own, so every client-side check waits on a real window (and `--no-open` means there isn't one). `send` says which case it is: nobody has ever connected (share the link), or a page dropped and hasn't returned (it's stale — reload it). Never open a window at the user: the server logs `[page] connected` when a page attaches, so end your turn with the link, arming `typebulb wait <file> --match "[page] connected"` in the background first — the user opening the page is your wake-up.
276
+ - **Reading a canvas** — `typebulb send <file> tb:png` writes the page's canvas to a PNG (a stable per-bulb path under `~/.typebulb/`, overwritten each read) and prints the path — rendered truth for a bulb whose output is drawn, with no probe handler and nothing disturbed. One canvas needs no name; several take `tb:png "<name>"` (an `aria-label` on the canvas names it). A WebGL/WebGPU canvas without `preserveDrawingBuffer` reads back blank outside its own frame — capture during the draw instead.
275
277
 
276
278
  ### Emitting a server-only bulb
277
279
 
@@ -302,7 +304,7 @@ The host owns a bulb's **width**; you own its **height**.
302
304
  - **Self-testing a local bulb** — To confirm a bulb works, run it, instrument with `tb.log(...)`, and read it back with `typebulb logs`. That's the loop to verify behaviour without asking the user to copy-paste console output. `tb.fs.write(...)` is handy for dumping large outputs.
303
305
  - **Self-testing client code** — gate checks behind `tb.onMessage(m => { if (m === 'selftest') return run() })`, trigger with `typebulb send <file> selftest --wait`, and assert on the JSON reply — see [Interrogating the live page](#interrogating-the-live-page).
304
306
  - **Probe handlers go in the first draft** — the selftest, readback, and set handlers you'll want later: adding one is an edit, and that hot reload destroys the very state you meant to inspect.
305
- - **Read a canvas back as an image** — return a bare `canvas.toDataURL()` string from a handler; `typebulb send <file> png --wait` prints it raw, and decoding the base64 after the `data:image/png;base64,` prefix yields a PNG you can view. That's the visual-verification loop for canvas/WebGPU bulbs.
307
+ - **Read a canvas back as an image** — `typebulb send <file> tb:png`: the visual-verification loop for canvas/WebGPU bulbs (see [Interrogating the live page](#interrogating-the-live-page)). The authored form — return a bare `canvas.toDataURL()` string from a handler — remains for what the verb can't reach, like capturing a WebGL frame during its own draw.
306
308
  - **Testing a `server.ts` export directly** — `typebulb call <file> <fn> [arg…]` boots `server.ts`, invokes one export, and prints its return as JSON to stdout (logs/errors to stderr, so `… | jq` works). Args after `<fn>` are JSON-or-string; `--args '<json-array>'` (or `--args -` for stdin) escapes tricky quoting. Needs `--trust`.
307
309
  - **Mount to the container your `index.html` declares.** The corpus convention is `<div id="root"></div>` with `createRoot(document.getElementById("root")!)`.
308
310
  - **All imports at the top of `code.tsx`, and every bare import declared in `config.json` `dependencies`.** Bare imports (`react`, `d3`, `three`, …) resolve from a CDN — no install step — but declaring them is **required, not optional**: an import missing from `dependencies` is a lint error that fails `npx typebulb check` *and* refuses to run. Declaring is also what pins versions and lets `check` fetch type defs (without it you get errors like `TS2875: react/jsx-runtime`). So a bulb with imports must carry a `config.json` with a matching `dependencies` entry for each.
package/SKILL.md CHANGED
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: typebulb
3
3
  description: "Author and run Typebulb bulbs — single-file markdown apps (TypeScript/TSX) that run locally via `npx typebulb` (full power: filesystem, database, `server.ts`, `tb.ai`) or render live inline in your coding agent's session through Typebulb's agent mirror (embedded, client-only). A bulb can be a visual widget (chart, simulation, diagram, calculator, UI), a full-stack tool with a Node backend, or an AI app that calls models at runtime. Covers the bulb format, the `tb.*` API, trust, and the local run/embed workflow. Use when the user wants a bulb, a quick local tool (visual, backend-backed, or AI-powered), or something visual rendered inline in the conversation."
4
- version: 0.45.1
4
+ version: 0.46.0
5
5
  ---
6
6
 
7
- > Generated from typebulb v0.45.1. `npx typebulb agent` prints the running version alongside the path to its packaged SKILL.md: if that version is newer than this one, replace this file with that one.
7
+ > Generated from typebulb v0.46.0. `npx typebulb agent` prints the running version alongside the path to its packaged SKILL.md: if that version is newer than this one, replace this file with that one.
8
8
 
9
9
  # typebulb
10
10
 
@@ -54,6 +54,7 @@ typebulb send <file> tb:snapshot Print the live page's rendered outline (roles,
54
54
  typebulb send <file> tb:rect … Print a named control's rect ('tb:rect button "Pass"' → {x,y,width,height} + viewport)
55
55
  typebulb send <file> tb:click … Click a control by role+name ('tb:click button "Pass"'); the reply is a fresh snapshot
56
56
  typebulb send <file> tb:set … Set a form control ('tb:set combobox "level" = hard'), firing input+change
57
+ typebulb send <file> tb:png … Save the live canvas as PNG, print its path (sole canvas needs no name; 'tb:png "<name>"' among several)
57
58
  typebulb get <file> <kind> Print one block's content (data, insight, code, …) to stdout
58
59
  typebulb put <file> <k>=<src> Write a file's (or stdin's) content into a block, surgically
59
60
  typebulb pull <url|file> Fetch a bulb from typebulb.com into typebulbs/u/<user>/<slug>.bulb.md
@@ -280,6 +281,7 @@ That one launch *is* the loop: the server watches the file, so every save recomp
280
281
  - **Acting on the page** — `typebulb send <file> 'tb:click button "Pass"'` clicks the one control matching that role and name (exact, else a unique case-insensitive substring) and replies with a fresh snapshot; `tb:set combobox "strength" = hard` is the same for form controls (checkboxes and radios take `tb:click`). A disabled, readonly, or covered target is an error naming it — that silence is the bug class these verbs catch. Needs exactly one page open, and the reply is the immediate frame (slow work: follow up with `tb:snapshot`). Only what the outline names is targetable: real `<button>`s and labeled controls, not an `onClick` `<div>`.
281
282
  - **Poking state** — for state beyond what a form control expresses (`tb:set` covers those), author a set-handler up front: a `tb.onMessage` branch that takes a data payload (JSON arrives parsed), applies it to your state — committing the change if your framework needs an explicit step — and returns the new state: `typebulb send <file> '{"set":"speed","value":2}' --wait` prints it. In React, register it in an effect so it closes over the setters (the returned unsubscribe is the cleanup).
282
283
  - **A page must be open** — the CLI runs no browser of its own, so every client-side check waits on a real window (and `--no-open` means there isn't one). `send` says which case it is: nobody has ever connected (share the link), or a page dropped and hasn't returned (it's stale — reload it). Never open a window at the user: the server logs `[page] connected` when a page attaches, so end your turn with the link, arming `typebulb wait <file> --match "[page] connected"` in the background first — the user opening the page is your wake-up.
284
+ - **Reading a canvas** — `typebulb send <file> tb:png` writes the page's canvas to a PNG (a stable per-bulb path under `~/.typebulb/`, overwritten each read) and prints the path — rendered truth for a bulb whose output is drawn, with no probe handler and nothing disturbed. One canvas needs no name; several take `tb:png "<name>"` (an `aria-label` on the canvas names it). A WebGL/WebGPU canvas without `preserveDrawingBuffer` reads back blank outside its own frame — capture during the draw instead.
283
285
 
284
286
  ### Emitting a server-only bulb
285
287
 
@@ -310,7 +312,7 @@ The host owns a bulb's **width**; you own its **height**.
310
312
  - **Self-testing a local bulb** — To confirm a bulb works, run it, instrument with `tb.log(...)`, and read it back with `typebulb logs`. That's the loop to verify behaviour without asking the user to copy-paste console output. `tb.fs.write(...)` is handy for dumping large outputs.
311
313
  - **Self-testing client code** — gate checks behind `tb.onMessage(m => { if (m === 'selftest') return run() })`, trigger with `typebulb send <file> selftest --wait`, and assert on the JSON reply — see [Interrogating the live page](#interrogating-the-live-page).
312
314
  - **Probe handlers go in the first draft** — the selftest, readback, and set handlers you'll want later: adding one is an edit, and that hot reload destroys the very state you meant to inspect.
313
- - **Read a canvas back as an image** — return a bare `canvas.toDataURL()` string from a handler; `typebulb send <file> png --wait` prints it raw, and decoding the base64 after the `data:image/png;base64,` prefix yields a PNG you can view. That's the visual-verification loop for canvas/WebGPU bulbs.
315
+ - **Read a canvas back as an image** — `typebulb send <file> tb:png`: the visual-verification loop for canvas/WebGPU bulbs (see [Interrogating the live page](#interrogating-the-live-page)). The authored form — return a bare `canvas.toDataURL()` string from a handler — remains for what the verb can't reach, like capturing a WebGL frame during its own draw.
314
316
  - **Testing a `server.ts` export directly** — `typebulb call <file> <fn> [arg…]` boots `server.ts`, invokes one export, and prints its return as JSON to stdout (logs/errors to stderr, so `… | jq` works). Args after `<fn>` are JSON-or-string; `--args '<json-array>'` (or `--args -` for stdin) escapes tricky quoting. Needs `--trust`.
315
317
  - **Mount to the container your `index.html` declares.** The corpus convention is `<div id="root"></div>` with `createRoot(document.getElementById("root")!)`.
316
318
  - **All imports at the top of `code.tsx`, and every bare import declared in `config.json` `dependencies`.** Bare imports (`react`, `d3`, `three`, …) resolve from a CDN — no install step — but declaring them is **required, not optional**: an import missing from `dependencies` is a lint error that fails `npx typebulb check` *and* refuses to run. Declaring is also what pins versions and lets `check` fetch type defs (without it you get errors like `TS2875: react/jsx-runtime`). So a bulb with imports must carry a `config.json` with a matching `dependencies` entry for each.