Files
ai-app/app/build-icon-font.sh
T
irisandClaude Opus 5 61d2c78afe Keep the reader's place: in a group, in a compaction, and in the list
Five things Iris asked for, all about the transcript screen holding still
around whoever is reading it.

A tool call opened on its own stayed open when a second call in the same
run turns it into a group. Watching a Bash call and having the session
make another one used to shut the card being read and fold it behind
"Called 2 tools" -- the reader lost their place because something else
happened. The transition is noticed once, at the moment a run first
becomes a group; after that the group's own toggle owns it, so shutting a
group whose inner call is still expanded does not re-open it.

The compaction clock is taken from the `compacting` status event's own
timestamp rather than from this device noticing one, so it survives
leaving the session and coming back -- it used to disappear, because the
only thing that knew when the compaction started was a screen that had
been disposed. The server timestamps every transcript line, so this is
still a measurement; it is compared against the phone's wall clock, which
is the same comparison a session's "last active" already makes.

Session settings are a dialog over the session instead of a screen below
it. Two controls did not warrant a page transition and a back stack, and
the thing they change was hidden while they were on screen. Captions are
gone -- each control is a labelled noun -- and "Notify me" is
"Notifications" with a bell beside it (`md-bell`, added to the committed
Nerd Fonts subset). Failures keep their words, since those are what a
reader cannot work out by looking.

Tool groups are rounded like every other card, their foot bar is the same
height as their heading (both derived from the heading's own line height,
so the pair cannot drift), and the calls inside are a connected stack:
square where they face a neighbour, rounded on the outside, with a small
gap so the join reads as a join.

Scroll position is persistent on the device, per session, keyed by the
row rather than by an index -- an index means nothing across a reopen,
where the transcript is fetched newest-first. Reopening pages backwards
until that row is loaded *and* has something older behind it, because the
oldest loaded row is a half-row that grows when the page behind it
arrives; anchoring into one landed a screen and a half out. The list
draws nothing until the position lands, so there is no frame in which the
transcript is somewhere other than where it was left.

Two things found on the way. `snapshotFlow`'s first emission is the state
before anybody has touched the list, and reading it as a scroll that had
just ended at the newest end wiped every saved position on the way in.
And backwards pages now ask for 800 events rather than 80: ai-app-2
measured a real transcript at 2,426 events for seven assistant messages,
so a page of eighty is a fifth of one row and filling the lookahead took
about thirty sequential round trips -- seconds of a list that will not
move, over the tunnel.

`/tools [n] [gap]` in the echo driver takes seconds between calls, which
is what makes a run grow slowly enough for somebody to have opened one of
its calls first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 19:06:11 -04:00

78 lines
3.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# Rebuilds androidApp/src/main/res/font/nerd_icons.ttf.
#
# The app draws a handful of icons -- a cog, a refresh arrow, send, stop --
# as text in a Nerd Fonts glyph rather than as vector assets or as ordinary
# Unicode. Unicode has no character for most of these, and the ones it does
# have are not reliably in an Android system font, so they land as tofu
# boxes on somebody's phone. Shipping the subset removes the hope: the
# glyph is in the APK.
#
# The whole symbols font is 3 MB for the handful below, so what is
# committed is a subset. Add a codepoint to GLYPHS below and to NerdIcons.kt
# (the two lists have to agree -- a codepoint in the Kotlin but not here is
# a glyph that silently doesn't exist), then run this and commit the result.
#
# Needs python3 and network access; fontTools is fetched into a temporary
# venv, so nothing has to be installed on the machine.
#
# Copied from dev-updater's script of the same name rather than shared
# through wg-app-link, for the reason Theme.kt gives about the palette: the
# link is the tunnel, the pinned CA and enrollment, and an icon set is a
# preference rather than part of that contract.
set -euo pipefail
# Codepoint, then the Nerd Fonts glyph name it came from. Material Design
# Icons bar one, so they read as one family -- and the first two are
# deliberately the same two dev-updater uses, since a cog and a refresh
# arrow mean the same thing in both apps. The exception is noted on its
# own line, as dev-updater's script does with its two.
GLYPHS=(
U+F0493 # md-cog
U+F0450 # md-refresh
U+F048A # md-send
U+F04DB # md-stop
U+F03E4 # md-pause
U+F040A # md-play
U+F1163 # md-send_clock
U+F0156 # md-close
U+F004D # md-arrow_left
U+F009A # md-bell
U+F201 # fa-line_chart -- Font Awesome's, asked for by name
)
url=https://github.com/ryanoasis/nerd-fonts/releases/latest/download/NerdFontsSymbolsOnly.zip
out="$(cd "$(dirname "$0")" && pwd)/androidApp/src/main/res/font/nerd_icons.ttf"
work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT
echo "Fetching $url"
curl -fsSL -o "$work/nf.zip" "$url"
python3 -c 'import sys,zipfile; zipfile.ZipFile(sys.argv[1]).extractall(sys.argv[2])' "$work/nf.zip" "$work"
python3 -m venv "$work/venv"
"$work/venv/bin/pip" -q install fonttools
unicodes="$(IFS=,; echo "${GLYPHS[*]}")"
mkdir -p "$(dirname "$out")"
# The Mono face rather than the proportional one, which this used until
# 2026-08-30. Every glyph in it is one em wide and one em tall, so two
# icons drawn at the same size are the same size -- which is what makes two
# icon buttons beside each other match without either of them being told a
# width. In the proportional face the advances run from 0.46 em (play) to
# 0.92 em (line chart), so the composer's Send button came out visibly wider
# than the Stop button next to it, and any fix at the call site would have
# been one measurement hardcoded per pair.
#
# The trade is the one the old comment named: an icon inline beside text is
# padded out to a cell. That is worth it, and it is also why GLYPH_SIZE in
# NerdIcons.kt came down when this changed -- a glyph that fills its em
# draws bigger at the same point size than one that does not.
"$work/venv/bin/pyftsubset" "$work/SymbolsNerdFontMono-Regular.ttf" \
--unicodes="$unicodes" \
--layout-features= \
--drop-tables+=DSIG \
--output-file="$out"
echo "Wrote $out ($(stat -c %s "$out") bytes) with ${#GLYPHS[@]} glyphs"