Skip to content

feat(sdk): a Data App's structure, read from its markup, in the inspect panel - #31

Merged
nkhdo merged 8 commits into
mainfrom
feat/1-element-binding
Oct 12, 2026
Merged

nkhdo merged 8 commits into
mainfrom
feat/1-element-binding

Conversation

@nkhdo

@nkhdo nkhdo commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

A Data App can mark its parts in its own HTML. The inspect panel in anfra serve then shows them as a tree, like a browser's element inspector: hover a node to see it on the page, click to scroll to it, pick one from the page, and copy a handle for an agent.

Markup

Five attributes. data-anfra-container="id" and data-anfra-block="id" name the parts, with an optional data-anfra-label. data-anfra-query and data-anfra-control go on the elements an entity draws into, as name or app/name. Ids are unique per kind. A block holds no container or block, and a control lives in a block the way a query does.

The markup is optional. A wrong name, a duplicate id or a block inside a block shows up as a sentence on that node in the tree. Nothing throws.

SDK

While a host inspects, the app runtime reads the attributes into a tree and posts it with the snapshot, along with how many places draw each entity and in which blocks. A mutation observer keeps the tree current; it watches elements coming and going and the five attributes, and nothing else. A frame nobody inspects does no work.

Declaring a query, control or mapping now notifies subscribers, so an app that adds charts while it runs shows them in the panel right away instead of after their first run.

The host sends three new messages, anfra:highlight, anfra:locate and anfra:pick-watch, and the frame sends anfra:picked back. MountedDataApp gains highlight, locate, setPicking and an onPick callback.

The frame draws one overlay over the marked element: a tinted box and a label with the kind, the id or name in bold, the author's label, a note such as "1 of 3" when the entity is drawn in several places, and the size. query.locate() and control.locate() find their marked elements and flash the same overlay. Hidden elements get the overlay whenever they have a box, which is what devtools does.

appserve

The inspect panel has two tabs. Structure has a toolbar with Pick and Copy handle, the tree under it, and a detail pane for the selected node with its handle and a copy button on that row. Pick ends on a click on the page, on Escape, or on a click in the tree, and clears the overlay. Copy handle gives something like [Revenue over time](data-anfra-block="trend"). Data has the cards from before; each one says in how many places and which blocks its entity is drawn, and has a Locate button.

Checks

make check and make test pass. New SDK tests cover parsing, the tree and its problems, usage, the overlay, pick mode and declaring. I tried it by hand in appserve on three Data Apps: hover, locate, pick, copy, the Data tab, elements added after load, and an app's own locate button.

Left for later: reading the structure from the file without running it, exposing it to author code, and doing anything special for hidden content.

🤖 Generated with Claude Code

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces element bindings to the Anfra SDK, allowing queries and controls to be bound to DOM elements via CSS selectors. It also implements a 'locate' feature that enables the host to request the frame to scroll to and highlight these bound elements. The UI has been updated with 'Locate' buttons in the inspect panel, and comprehensive tests have been added. The review feedback highlights two important improvements in 'elements.ts': ensuring the 'unbind' method trims input selectors to match 'bind' and avoid silent failures, and replacing the unsafe '(element as HTMLElement).offsetWidth' cast with 'element.getBoundingClientRect()' to safely trigger reflows on both HTML and SVG elements.

Comment thread web/sdk/src/app/elements.ts Outdated
Comment thread web/sdk/src/app/elements.ts Outdated
@nkhdo
nkhdo force-pushed the feat/1-element-binding branch from c2f50fe to e143d74 Compare October 10, 2026 02:47
@nkhdo nkhdo changed the title feat(sdk): element bindings and locate for Data App queries and controls feat(sdk): a Data App's structure, read from its markup, in the inspect panel Oct 10, 2026
nkhdo and others added 3 commits October 10, 2026 02:50
…te it

A definition marks its semantic parts on its own elements:
data-anfra-container and data-anfra-block (ids, unique per kind, with an
optional data-anfra-label), and data-anfra-query / data-anfra-control on
the elements an entity draws into, as "name" or "app/name". The app
runtime reads them into a tree of nodes, with problems (an unknown name,
a duplicate id, a block inside a block) reported on the node and never
thrown, and posts it in the inspect snapshot beside the apps, with where
each entity is drawn. Only while a host inspects: then a mutation
observer limited to elements and the five attributes keeps it current,
and nothing runs otherwise.

The host asks the frame to highlight a node, locate one (or an entity
wherever it is drawn), and to start or stop pick mode; the frame draws
one overlay (a tinted box and a label) over the marked element, and in
pick mode reports the node under a click without letting the click
through. `query.locate()` and `control.locate()` find their marked
elements the same way, for a button of the app's own.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The panel becomes a devtool with two tabs. Structure shows the page's
marked parts as a tree: hovering a node highlights it on the page,
clicking scrolls to it and opens a detail pane (id, label, problems, and
the entity's card for a query or control), Pick chooses a node from the
page, and Copy handle puts `[Label](data-anfra-block="id")` on the
clipboard for an agent. Data holds the cards, each saying in how many
places and which blocks its entity is drawn, with a Locate button.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@nkhdo
nkhdo force-pushed the feat/1-element-binding branch from e143d74 to 8fb3ec1 Compare October 10, 2026 02:50
nkhdo and others added 2 commits October 10, 2026 02:57
The label read as one run of text joined by dots. It is now parts the eye
can tell apart, as a browser's element inspector does: the kind muted,
the id or name bold, the author's label beside it, a note such as
"1 of 3", and the size at the right.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The problems the tree reports, the cards' "drawn" line, the pick hint and
the empty state now say what is wrong or what to do in one plain sentence
each, instead of a clause joined to a rule.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@nkhdo
nkhdo force-pushed the feat/1-element-binding branch 2 times, most recently from d9a19a8 to 5e40369 Compare October 10, 2026 03:08
Choosing a node in the tree ends pick mode, as a click on the page does,
and a pick clears the overlay it left: the overlay shows only while the
reader hovers the tree. The Structure tab's toolbar (Pick, Copy handle)
is its own row between the tab header and the tree, outside what
scrolls. The handle row in the detail pane has a copy button of its own,
shown on hover.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@nkhdo
nkhdo force-pushed the feat/1-element-binding branch from 5e40369 to 8745c5f Compare October 10, 2026 03:10
nkhdo and others added 2 commits October 10, 2026 07:43
A dynamic app declares queries and controls while the page runs. A host
inspecting it saw the new entity only on the next change, usually its
first run, and a node marked with its name was flagged as unknown until
then. Declaring, and mapping, now notify like any other change; a render
subscribed to the app runs once more, which it handles already for an
entity with no result.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@nkhdo
nkhdo merged commit 9da5ecb into main Oct 12, 2026
7 checks passed
@nkhdo
nkhdo deleted the feat/1-element-binding branch October 12, 2026 03:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants