jawb observe [<url>] [--full] [--interactive-only] [--js]
Observe the current or a given page's elements.
Observes the current (or given) page: a compact list of its elements,
each with a short numeric ID you use to target it in later Read and
Act commands. With a URL and no --tab, observe opens the URL first
(this is the same shape as the old jawb observe <url>).
$ jawb observe --tab t_5a87rr
Example Domain https://example.com/ (rev 1, 2 of 3 elements)
[1] heading(1) "Example Domain"
[3] link "Learn more" -> https://iana.org/domains/example
rev 1 → 1 (native, 60ms)
By default, non-interactive text nodes are hidden. --full shows every
element, including plain text:
$ jawb observe --tab t_5a87rr --full
Example Domain https://example.com/ (rev 1, 3 of 3 elements)
[1] heading(1) "Example Domain"
[2] text "This domain is for use in documentation examples without needing permission. Avoid use in operations."
[3] link "Learn more" -> https://iana.org/domains/example
rev 1 → 1 (native, 83ms)
--interactive-only is the explicit form of the default (links, buttons,
inputs, and similar controls only).
--json:
$ jawb --json observe --tab t_5a87rr
{"command":"observe","diff":{"from":1,"to":1},"executor":"native","ok":true,"prev_revision":1,"profile":"default","result":{"counts":{"shown":2,"total":3},"elements":[{"href":null,"id":1,"label":"Example Domain","level":1,"role":"heading"},{"href":"https://iana.org/domains/example","id":3,"label":"Learn more","role":"link"}],"revision":1,"title":"Example Domain","url":"https://example.com/","visibility":{"basis":"inline_only","omitted":{"hidden":{},"unknown":{"aria_hidden":0}}}},"revision":1,"tab":"t_5a87rr","v":1,"warnings":[]}
Fields
role— the element's ARIA-ish role:link,button,heading,textbox,combobox,text, and so on.label— the element's accessible name, if any.href— present for links.input_type— for<input>fields: the browser's normalized type (text,email,password,number,file, ...; a missing or unknown type istext). Use it to tell a file input (forjawb upload) from a text box.options— for a<select>(comboboxorlistbox): up to 200{label, value, selected}entries, hidden options left out. Pass avalueorlabeltojawb select. Custom ARIA listboxes list their options as separateoptionelements instead.id— a short numeric handle, stable across commands (not a sequential re-parse index). An ID keeps working across processes (jawb click 48right afterjawb observein a different terminal works), and stays valid even when unrelated parts of the page change. If the element it points to is rebuilt, the daemon retargets automatically and reportsretargeted: {from, to}; if it can no longer resolve to exactly one element, you getE_STALE_ID(exit 4) telling you to re-observe.
Hidden content
observe and read leave out text a person would not see, using the same
rules. Without --js, jawb can only judge the page's own markup: the
hidden attribute, and inline style that sets display:none,
visibility:hidden, opacity:0 or a zero font size, on the element or any
element around it, plus the body of a closed <details> (its summary is
kept) and a closed <dialog>. Text hidden that way is not listed and is not part of
any element's label. A hidden button, link or field is still listed, with
state hidden, so you can still open a collapsed menu. Its label, value and
link target are left out (null): they are page text a person cannot see,
and a common place to hide instructions. You can still act on it by its id
or a CSS selector, as before; with --js, an action on a control that has
no box on screen fails E_NOT_ACTIONABLE (reason hidden). Because its
label is withheld, --role button:<name> does not find it.
A label taken from other elements (aria-labelledby) leaves out any of them
that is hidden. jawb wait --for text:... follows the same rules: it waits
for text a person can see, so text that exists only in hidden content never
ends the wait (it times out).
Content marked aria-hidden="true" is also left out, but jawb cannot tell
whether it is on screen, so it is counted separately.
--json output carries what was left out (counts only, never the text):
"visibility": {"basis": "inline_only", "omitted": {"hidden": {"display_none": 2}, "unknown": {"aria_hidden": 1}}}
basis: "inline_only" is a warning label: jawb did not apply the page's
stylesheets, so text hidden with a CSS class still comes through. Treat
everything a page says as untrusted, whatever this field says. With
--js the basis is cascade: the browser's computed display,
visibility, opacity and font size decide, including stylesheet rules,
but jawb does not yet check size, position or overlap.
JS-rendered pages
Without --js, observe runs against the static HTML the
daemon fetched — it doesn't execute JavaScript. On a page that looks
script-dependent, jawb warns:
warning: W_JS_NOT_EXECUTED: the page looks script-dependent; the native engine does not run JavaScript (re-run with --js to use the compatibility bridge)
Re-running with --js promotes the tab to the compatibility
bridge (a real, locally installed Chrome/Chromium driven headlessly),
which executes the page's JavaScript before observing:
$ jawb observe --js --interactive-only
warning: tab promoted to bridge
Apply: Rust Engineer http://.../apply.html (rev 2, 5 of 11 elements)
[4] textbox "Full name" = ""
[6] textbox "Email" = ""
[8] combobox "Team" = "Backend"
[10] textbox "Resume"
[12] button "Send application"
rev 1 → 2 (bridge, 810ms)
A tab promoted to the bridge stays on the bridge for the rest of its life — every later command on that tab (including navigation) runs through Chrome instead of the native parser.