Repository navigation
0.7.0
Accessibility checks you can gate on: lint and tabwalk agree with Orca
and don't edit what they walk, and contrast says what it can't measure
(GTK 4) rather than guess. Scrolling; names clicked as they are; an MCP
server that tells agents how to use it, answers while it works and
stops what's cancelled; and --clock that works on Ubuntu 24.04.
Changes to check when upgrading
- GTK 4 apps' contrast isn't checked: GTK 4 says where its widgets
are without their containers' padding and borders, and a stack's
child with its place in the stack counted twice, so what was measured
was often a button's fill or a neighbor's text.a11y contrastsays
each GTK 4 node is "not measured", and why on stderr; with nothing
measured at all, as in a GTK 4 window, it exits 2, not 0 (or 1), and
Screen.contrast()raises. GTK 3, Qt and browsers are measured as
before.SHOTBOX_TRUST_GTK4_BOUNDS=1measures GTK 4 at its bounds, for
a GTK whose bounds you've checked. docs/accessibility.md, "GTK 4's
positions". - A name is taken as it is before as a regex:
a11y click, MCP's
click,find()andclick_node()pick the nodes named exactly NAME
over those NAME matches as a regex, among those showing when they
look, so a calculator's.is its button rather than every
one-character one, and(Draft)is itself. A window's title, a role
and a focus wait match a name as it is too, or as a regex:Foo (1)
now finds the window titled that. - MCP's
lintandcontrastanswer with each node asaccessible_tree
has it, with its bounds, rather than its path. An--ignore(ignore)
regex is found in either.
Added
a11y contrastfinds large text, 18pt or 14pt bold all through, by
its font as GTK and Chromium say it, and holds it to WCAG's 3:1 rather
than 4.5:1;--large RATIOsets that ratio.Screen.contrast(large=..., notes=True), the second for what wasn't measured; MCP'scontrast
lists that after the failures, and why.- Scrolling:
shotbox scroll X Y DY [DX] [--window RE]turns the
mouse wheel at a point, DY clicks down and DX right (negative, up and
left), over XTEST's buttons 4 to 7 on X11 and the virtual pointer's
axis_discreteon Wayland;shotbox a11y scroll NAME DY [DX]over a
node found by its name, asa11y clickfinds it. A step inplay
scripts too;Screen.scrollandScreen.scroll_node, and MCP's
scroll, by a node's name or a point. - MCP's server instructions: as it connects,
shotbox mcptells the
agent how its tools go together; clients pass it on, and directories
show it. record --until-line RE FILE [--tail SECS]: a recording that ends on
an event, once a line matching RE is written to FILE, aswait line
waits for one, and SECS after it;--secondsis then the most it
lasts. It prints the line, or says it was stopped or ran its
--secondsfirst.Screen.record(until=(re, path), tail=...), whose
Recording says thelineand whether it wasstopped; MCP'srecord
takesuntil_line,fileandtail, its file in the--allowfile's
directory, aswait_for's line is.SHOTBOX_SANDBOX=offin the environment:--no-sandboxfor every
session, from Python and MCP too, and for one started inside a session,
for a Dockerfile or CI job to set once (falseand0too).shotbox doctorsays whether sessions have a sandbox, and why not.shotbox key shift: a modifier alone.a11y tabwalksays what Tab never reached (not reached by Tab): the
showing, sensitive controls and fields, and tables and lists whose
items take the focus; the last line counts them.--reach-allfails
on them; without it they don't fail the walk, since arrows or
shortcuts may reach them and a window's own buttons are out of the
Tab order.--ignore REleaves out those it's found in.
Screen.tab_walk(reach_all=True, ignore=...); MCP'stab_walksays
them too, and takesignore.
Changed
- A session that can't make a sandbox says so only where it would have
hidden something: not in a container with no sound, input, HID or USB
devices and nothing in/run/user/UIDorXDG_RUNTIME_DIR. Where it
says so, it says how to say it's meant.
Fixed
- shotbox's own commands inside a
--clocksession (wait,key --hold,record...) run on the real clock, rather than under the
session's libfaketime, which in 0.9.10 (Ubuntu 24.04's) makes the
time.sleepof Python 3.11 and later fail; the programs they start for the session, as
term's, keep the session's clock. a11y contrastleaves out a disabled control's label, as the control
is; says text too small to have a core of its color (a ".") is too
small to measure, and passes it; and takes text as the third pixel in
order of contrast, not the strongest color three pixels share, which
a thin stroke seldom draws.- A name, role, window title, announcement or
--ignorethat isn't a
regex (+) says so in a sentence, rather than with a traceback; a name
that isn't one is taken only as it is. a11y clickand MCP'sclick, when more than one node matches, list
them as tree lines with where they are, and how to narrow it, rather
than by their paths; when none does, the nearest names. MCP'sclick
takes atimeout.- MCP's
wait_forwith awhatthere isn't names the choices, rather
than asking for a value. shotbox mcpanswerspingat once during a long call, takes
notifications/cancelled, calling off the request's waits, click,
check or recording and sending no answer to it, calls off the call
running when its stdin closes, rather than finishing it, and sendsnotifications/progressevery 5 seconds to
a call with aprogressToken. docs/mcp.md says which calls run long,
and how to raise a client's timeout.a11y tabwalkno longer presses Tab in a text view, where it typed a
tab into the document: it tries ctrl+Tab, shift+Tab in Qt (GTK's
types a tab), Escape (not in a dialog, which it would close) and F6 to
leave it, and fails as a trap only if none does. One it begins in,
nothing leaving, or leaves backward, it leaves by giving the focus to
what's after it, so the rest is walked. It presses Shift first, as
GTK 4 on Wayland says where the focus is only after a key; with no
node focused even then, it presses nothing, and says so; focus gone
after a press is said as that, not as Tab leaving it where it was.
The docs no longer say ctrl+Tab always gets out: an editor's own
shortcuts can take it. MCP'stab_walkanswers intabwalk's lines
(round again after 6, where it saidRound again after 6.).a11y lintandtreetake a field's label as its name, as a screen
reader does: a GTK 3 field with a mnemonic label passes, and is
text "Name:"in the tree;a11y click Name:(andscroll) takes the
field, not its label. They leave out what isn't showing, as a
revealer's unrevealed bar (gedit's search bar), not only what's
hidden; and lint, what says it has no size (gedit's one document's
hidden tab). Thanks to @jay23606, whose #141 found the same fix.wait linewith a pattern that isn't a regex says so, rather than
with a traceback.record --sound --seconds Nwhose session's sound server stops first,
as a lasting session's does when it's stopped: the picture ends with
the sound, rather than running on for up to a second past it.--audiowith--clock: the sound server runs on the real clock, so
sound plays and records. libfaketime moved the timer it plays by (late
by the offset, or at once, as TIME was behind or ahead of now), and
nothing in the session could read it.recordwhose ffmpeg is still opening what it records 10 seconds in
kills it and fails saying so, rather than waiting on it for good,
Ctrl-C and all.