title: "The Context tab" description: "What is open, what is on each page, the set algebra over tabs, and exporting them." nodes: 17 progress: 94% generated: ohm spec editor#
The Context tab#
What is open, what is on each page, the set algebra over tabs, and exporting them.
Contents#
☑ Overview#
☑ Scope#
note
packages/core/src/page-inspect.ts, packages/core/src/context-export.ts, the
context.* protocol methods, apps/ui/src/components/ContextPane.tsx, and the
tab.inspect / tab.markdown / context.* host API.
☑ One extraction, two consumers#
decision
The panel's per-tab breakdown and the tab.inspect() scripts call are the same
code: a single expression evaluated in the page, returning the outline, links,
inputs, buttons, forms and counts.
Two implementations of "what is on this page" would disagree within a week, and the panel's job is partly to teach the script API — it can only do that if what it shows is what a script gets.
☑ Reading a page#
☑ A password value is never read#
requirement #security #regression
Inputs of type password are listed — type, name, label, selector — with a null
value. The field is useful to know about; its contents are not Ohm's to copy out of
the page, and a feature that quietly harvested them would be indefensible whatever
it enabled.
☑ Everything is capped in the page, not after#
requirement #regression
300 links, 200 inputs, 200 buttons, 50 forms, 100 headings, and every string clipped to 200 characters — all applied inside the evaluated expression, before anything crosses the debugger. A shopping page with ten thousand links must not become a ten megabyte CDP message.
What was cut is named in truncated, which the panel and the markdown both show.
Silently returning the first 300 of 9000 links would be worse than refusing.
- node:
one-extraction
☑ Every control carries a selector that still finds it#
requirement
#id when there is one, then tag[name=…], then tag:nth-of-type(n) scoped to
an identified parent. The point is that reading and acting are the same surface:
what inspect() lists, tab.click() and tab.type() can use.
☑ One unreadable tab does not lose the others#
requirement #safety
context.inspectAll and context.export report a per-tab error — discarded,
crashed, navigated mid-read — and return everything else. The panel says how many
failed and why.
☑ Tabs as a set#
☑ Checked tabs, a stored set, and the operators between them#
component
The panel holds two collections: the checked tabs and a stored set.
Set ← checked fills one from the other; ∪ ∩ − ∖ combine them; shuffle, rotate
and reverse reorder, matching S.Shuffle, S.Rotate and S.Reverse exactly.
Results keep tab order rather than click order, so the same two clicks always give the same set.
- spec:
02-ohm-language
☑ Every action shows the Ohm that would do it#
decision
The panel renders the current set as \Tab \times urls, with Copy and Run it.
Clicking is meant to teach the notation, not to substitute for it: the shortest path
from "I did this by hand" to "this runs every morning" is seeing the expression that
matches what you just clicked.
☑ context.selection() in a script#
api
The UI pushes its selection to core (context.setSelection), which a script reads
as context.selection() — a matrix of tabs, so \times and every S.*
operator apply — plus context.inspect() and context.expression().
Core owns the selection; the panel and the script host read the same value, so the two can never disagree.
- spec:
03-scripts
☑ Export#
☑ Four formats, two destinations#
api
html (the page's own outerHTML), text (innerText), markdown (the
inspection: outline, forms, buttons, links) and json (the inspection as data).
To a chosen folder, one file per tab named from the page title with an index prefix
so two identical titles cannot overwrite each other — or returned for the clipboard.
☑ Review findings#
☐ Export into a project needs the active project threading through#
task
context.export accepts a project and will write into its files/, but the
shell does not yet pass the active project down to the panel, so the button always
asks for a folder. Thread it from ProjectsPanel through App.
☑ The Context view had to opt out of the .view layout#
note #regression
.view is a column flex box that sizes to its content, which left the panel 499px
wide inside an 887px slot — the tab list squeezed to 151px while the console showed
through beside it. .context-view sets width: 100% explicitly.
Measured, not guessed: the layout looked plausible in the screenshot until the element widths were read out of the running app.
Links between nodes#
graph LR
everything_is_capped_in_the_page_not_after["Everything is capped in the page, not after"] -.-> one_extraction["One extraction, two consumers"]