Skip to content

0.7.0

Choose a tag to compare

@mishan mishan released this 17 Oct 01:07
· 318 commits to main since this release
a93c482

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 contrast says
    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=1 measures 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() and click_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 lint and contrast answer with each node as accessible_tree
    has it, with its bounds, rather than its path. An --ignore (ignore)
    regex is found in either.

Added

  • a11y contrast finds 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 RATIO sets that ratio. Screen.contrast(large=..., notes=True), the second for what wasn't measured; MCP's contrast
    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_discrete on Wayland; shotbox a11y scroll NAME DY [DX] over a
    node found by its name, as a11y click finds it. A step in play
    scripts too; Screen.scroll and Screen.scroll_node, and MCP's
    scroll, by a node's name or a point.
  • MCP's server instructions: as it connects, shotbox mcp tells 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, as wait line
    waits for one, and SECS after it; --seconds is then the most it
    lasts. It prints the line, or says it was stopped or ran its
    --seconds first. Screen.record(until=(re, path), tail=...), whose
    Recording says the line and whether it was stopped; MCP's record
    takes until_line, file and tail, its file in the --allow file's
    directory, as wait_for's line is.
  • SHOTBOX_SANDBOX=off in the environment: --no-sandbox for every
    session, from Python and MCP too, and for one started inside a session,
    for a Dockerfile or CI job to set once (false and 0 too). shotbox doctor says whether sessions have a sandbox, and why not.
  • shotbox key shift: a modifier alone.
  • a11y tabwalk says 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-all fails
    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 RE leaves out those it's found in.
    Screen.tab_walk(reach_all=True, ignore=...); MCP's tab_walk says
    them too, and takes ignore.

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/UID or XDG_RUNTIME_DIR. Where it
    says so, it says how to say it's meant.

Fixed

  • shotbox's own commands inside a --clock session (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.sleep of Python 3.11 and later fail; the programs they start for the session, as
    term's, keep the session's clock.
  • a11y contrast leaves 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 --ignore that 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 click and MCP's click, 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's click
    takes a timeout.
  • MCP's wait_for with a what there isn't names the choices, rather
    than asking for a value.
  • shotbox mcp answers ping at 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 sends notifications/progress every 5 seconds to
    a call with a progressToken. docs/mcp.md says which calls run long,
    and how to raise a client's timeout.
  • a11y tabwalk no 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's tab_walk answers in tabwalk's lines
    (round again after 6, where it said Round again after 6.).
  • a11y lint and tree take 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: (and scroll) 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 line with a pattern that isn't a regex says so, rather than
    with a traceback.
  • record --sound --seconds N whose 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.
  • --audio with --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.
  • record whose 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.