Measure the explorer, and cap edit mode at what it can carry

Three numbers, taken on the emulator through the app's own render report
and written into EXPLORER.md; the fixture tree the sandbox now builds is
what they were taken against.

The viewer's scan was on the main thread. Decision 8 said off it, and the
first version did it in a `remember` inside the composition, which is not
that -- 460ms of frozen screen on a 1 MiB file, long enough that the
accessibility tree cannot be read, which is exactly what "the app has
stopped" looks like from outside. It runs on Dispatchers.Default now, with
a spinner where the file will be. Reading a megabyte is otherwise fine:
the viewer is a row per line, and it opens and scrolls 28,660 of them.

Edit mode needed a cap, and not the one the plan expected. The cost that
matters is not the highlighting -- 40ms a keystroke at 128 kB, which is
survivable -- it is Compose laying out one enormous text in the field:
2,027ms per frame at 128 kB, with typed characters dropped, and no
response at all at 1 MiB. Switching highlighting off would have saved
nothing, since every arrangement of a single text field pays it. So
EDIT_LIMIT is 32 kB, the largest size actually measured as usable, and
above it the pencil is disabled with the reason in words beside it: a
disabled control teaches what the thing can do but cannot say why it is
off, and a reader who cannot edit a file they can plainly read would
otherwise conclude the app is broken.

`FileLines.of` is timed like everything else here, so the figure lands in
the render report rather than needing a harness to ask for it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Opus 5 committed 2026-09-04 00:16:57 -04:00
1 parent 9c4d33273b
commit 2c12274285
6 files changed
+147 -24

No files matched your search

+51 -11
View File
@@ -393,18 +393,51 @@ pure functions with tests.
`~/repos/emulator-tools`, `ui-trace`'s tap-by-label action; then the
two bench scripts onto it, with no coordinate tap left in `app/*.sh`.
## Numbers to measure, before deciding
## What the measurements said (2026-09-04)
- Scan time for a 1 MiB source file on the emulator, and on the phone
through the render report. That decides whether `FILE_LIMIT` is right
and whether edit mode highlights every keystroke or only below a size.
- Time to first line for a 1 MiB file over the tunnel: the read, the
transfer, the scan, the first composition. If the transfer dominates,
the route gains nothing from streaming; if the scan does, it moves to
a worker with the plain text drawn first.
- The `BasicTextField` at 20,000 lines: whether typing stays responsive.
If not, edit mode gets a lower cap than the viewer, stated in the
editor rather than discovered by a stuck keyboard.
Taken on the emulator in a **debug** build, which runs Compose at a
fraction of release speed and renders in software -- so these rank
correctly against each other and are pessimistic in absolute terms.
Generated Rust, through the app's own render report.
| file | lines | scan + cut | scan per keystroke | worst frame record |
|--------|--------|------------|--------------------|--------------------|
| 32 kB | 917 | 11ms | 10ms | 183ms |
| 128 kB | 3,633 | -- | 40ms | 2,027ms |
| 1 MB | 28,660 | 460ms | -- | -- |
Three things followed.
**The viewer's scan had to leave the main thread.** Decision 8 said "off
the main thread" and the first version did it in a `remember` inside the
composition, which is not that: 460ms of frozen screen at the size the
server is willing to send, long enough that the accessibility tree cannot
be read -- which is exactly what "the app has stopped" looks like from
outside. It now runs on `Dispatchers.Default` with a spinner where the file
will be.
**`FILE_LIMIT` at 1 MiB is right for reading.** Time to first line for a
1 MiB file, tap to text on screen, was **2.4s** against the sandbox --
1.2s of which is that server's deliberate `--delay`, and 460ms the scan.
The transfer is not what dominates, so the route gains nothing from
streaming.
**Edit mode needed a cap, and not the one that was expected.** The plan
expected to be deciding a size below which highlighting stays on. That is
not the cost that matters: highlighting 128 kB costs 40ms a keystroke,
which is survivable, while laying the same text out in one
`BasicTextField` costs two seconds -- characters typed into it were
dropped, and a 1 MiB file stopped the app responding altogether. Since
every arrangement of a single text field pays that, switching highlighting
off would have saved nothing. So `EDIT_LIMIT` is **32 kB**, the largest
size measured as usable, and above it the pencil is disabled with the
reason said in words beside it -- a disabled control teaches what the thing
can do but cannot say why it is off, and a reader who cannot edit a file
they can plainly read would otherwise conclude the app is broken.
Reading is unaffected: the viewer opens and scrolls the 1 MiB file fine,
because it is a `LazyColumn` of lines rather than one text object. That
difference is the whole of decision 8.
## Later, deliberately not now
@@ -419,3 +452,10 @@ pure functions with tests.
- Uploading from the phone into a directory. Attachments already do the
upload half; this would be the same route with a chosen destination.
- Search within a file, and find-in-files.
- **A line-by-line editor**, which is the way past `EDIT_LIMIT`. The
viewer already draws a file as rows and stays fast on a megabyte; an
editor built the same way -- a field per line, or a field over the lines
on screen -- would not pay Compose's cost of laying out one enormous
text. It is a good deal more than this feature needed, and 32 kB covers
the config files, notes and ordinary source files anybody edits from a
phone.