The other half of EXPLORER.md: a folder button on the session header opens the machine's filesystem, starting where the session works. It draws **over** the session in the same `Box`, so the session under it stays composed -- its event stream keeps flowing, its draft and scroll position stay where they were, and coming back from a file costs nothing. Back steps one level inside it (editor, viewer, directory, parent) and only closes from where it opened; the platform gesture, the button and the swipe all go through the one function, so they cannot mean different things. The viewer is a `LazyColumn` of lines rather than one `Text`, because text layout is linear in the text and a twenty-thousand-line file in a single `Text` measures all of it to draw a screenful. Lines do not wrap and share one horizontal scroll, so a logical line is a visual line and the gutter cannot come to number the wrong text; the gutter's width is measured from the digit count of the line count in the style it is drawn in. The editor is a `BasicTextField` with a `VisualTransformation` carrying the scanner's spans, which is the one Compose API that colours a field's own text rather than replacing the field. `fileLanguage` reads the same table `fenceLanguage` does, so a language added for fences is a language added for files. A file that changed on the machine while it was open here refuses to be overwritten and asks, with what each of the three answers costs. That is the ordinary case, not the exotic one: an agent editing the file somebody is reading is what this whole feature is for. The speedometer moves off the header into the session settings dialog, where the session's other about-the-session controls are, and the folder takes a place between the usage chart and the cog -- widest scope to narrowest, cog at the end, as Iris asked. Both benchmark scripts move onto `ui-trace`'s new tap-by-label action in the same change, so the render report is never unavailable and never pressed at a coordinate that has stopped meaning anything; `app/bench-lib.sh` is what they share, and `grep -n "tap [0-9]" app/*.sh` is the check. Exercised on the emulator against the sandbox's new fixture tree, with a screenshot or a ui-trace for each: the listing (dotfiles, directories first, a symlink to a directory sorted with them, a name with a tab in it), a highlighted file, binary, too big, a permission error, editing and saving, the 409 and its Overwrite, back with unsaved edits, creating a name that exists, creating one that does not and landing in the editor, an empty directory, and `..` above the directory the session opened in. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
58 lines
2.6 KiB
Bash
58 lines
2.6 KiB
Bash
# Shared by transcript-bench.sh and stream-bench.sh: the parts of driving
|
|
# this app that both need and that neither should describe twice.
|
|
#
|
|
# Sourced, not run. It assumes android-env.sh has already been sourced, so
|
|
# `ui-trace` and `adb` are this checkout's.
|
|
#
|
|
# **Nothing here presses a coordinate.** Every control is found by the name
|
|
# it already carries for assistive technology, resolved from the screen at
|
|
# the moment of the gesture (`ui-trace record --do "tap 'Save'"`). A
|
|
# coordinate is a position measured once by hand, and anything that moves
|
|
# the control -- a button added to the row, a font size, another device --
|
|
# makes the tap land on whatever now sits there; the bench then reports a
|
|
# number that was never measured, which reads exactly like a result. That
|
|
# is not hypothetical: both scripts pressed the render-report button at
|
|
# `tap 723 205` until 2026-09-03, when it moved into the session settings
|
|
# dialog. The check that none has crept back:
|
|
#
|
|
# grep -n "tap [0-9]" app/*.sh
|
|
|
|
# The title of the session these benches open by default: whichever one the
|
|
# server lists first, which is the most recently active.
|
|
#
|
|
# Asked of the server rather than taken from the screen, because a row has
|
|
# no stable name of its own and "the first one" is a fact about the list
|
|
# rather than about a position on it. Passing -s overrides it, and that is
|
|
# the honest way to measure a particular session: the list is ordered by
|
|
# last activity, so "the first row" is a different session at different
|
|
# times of day.
|
|
first_session_title() {
|
|
./ui-sandbox.sh api /sessions |
|
|
python3 -c 'import json,sys; d=json.load(sys.stdin); print(d[0]["title"] if d else "")'
|
|
}
|
|
|
|
# Restarts the app and opens the session called $1.
|
|
open_session() {
|
|
adb shell am force-stop com.example.aiapp
|
|
adb shell am start -n com.example.aiapp/.MainActivity >/dev/null
|
|
sleep 5
|
|
ui-trace record -d 6000 --do "tap '$1'" --do 'wait 3000' \
|
|
-o /tmp/bench-open.txt >/dev/null
|
|
sleep 2
|
|
}
|
|
|
|
# Copies the app's render report to the clipboard and the log, through the
|
|
# session settings dialog it lives in.
|
|
#
|
|
# Pressed twice per run: once to empty the counters, and once at the end,
|
|
# so what comes back covers exactly the work between the two. Closing the
|
|
# dialog is part of it -- it covers the transcript, which is the thing the
|
|
# gestures in between are meant to reach.
|
|
copy_render_report() {
|
|
ui-trace record -d 7000 \
|
|
--do "tap 'Session settings'" --do 'wait 1500' \
|
|
--do "tap 'Copy'" --do 'wait 800' \
|
|
--do "tap 'Close'" --do 'wait 800' \
|
|
-o "${1:-/tmp/bench-report.txt}" >/dev/null
|
|
}
|