@ghostystudio/pointer
v0.8.0
Published
«Señalar»: point at an element of an HTML page, let an agent read it just-in-time and change ONLY that node — picker, in-iframe runtime (Penpal) with double-click text editing, byte-exact node splice, scope heuristics and write guards.
Maintainers
Readme
@ghostystudio/pointer
The «Señalar» pattern — for any canvas with an agent next to it: the person points, the agent understands what it is, changes ONLY that, and the person sees it change.
Used by Ghosty Studio pages and by Deník landings.
import { readPickMessage, pickedElementLine, parsePickedLine } from "@ghostystudio/pointer/picked";
import { readNode, swapNodeHtml } from "@ghostystudio/pointer/html";
import { reachesBeyondNode, wantsSiblings } from "@ghostystudio/pointer/scope";
import { assertSafeRefine } from "@ghostystudio/pointer/guards";| Step | Module | Contract |
|---|---|---|
| 1. Point | picked | ELEMENT_PICKER_SCRIPT sleeps inside the iframe; readPickMessage sanitizes {tag, id, idAttr, selector, text}. Never HTML. |
| 2. Send | picked | pickedElementLine goes in the user's turn, before their text; consumed on send. parsePickedLine reads it back. |
| 3. Read | html | readNode(html, id) → exact node bytes + parent's open tag, loaded just-in-time by a tool. |
| 4. Scope | scope | wantsSiblings, quotedTargets, reachesBeyondNode: does the request reach beyond the node? |
| 5. Write | guards + html | assertSafeRefine (empty, truncated, lost assets/prices, wrong node) → swapNodeHtml (byte compare-and-swap, never re-serializes). |
Ids are opaque and stable (data-id, or data-eb-id): stamped once server-side, preserved by every write.
Release
One source of truth for Ghosty Studio, Deník and EasyBits. Bump version in package.json and merge
to main: .github/workflows/publish-pointer.yml tests, builds and publishes it (skips if that
version is already on npm). Apps depend with ^ and Dependabot merges the update when CI passes.
Never copy this code into an app — import it.
MIT
