Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,10 @@ new tokens valued per theme, because no pair of the existing ones keeps the
selected segment lighter than its track in both. Rejected: two rows of six for
the console's eleven pages (every page pays for a launcher grid at the top) and
a tab bar fixed at the bottom (a fixed bar fights the composer and the
keyboard, and the operator's other installed board took its own out).
keyboard, and the operator's other installed board took its own out). The
second was reversed on 2026-11-12
(`2026-11-12-the-boards-are-apps-on-a-phone.md`): the bar steps aside while a
field is typed in, and that other board has a bottom tab bar again.

Measured headless at 375 and 414 in both languages, both themes: no cell off
the screen, nothing overflowing, no name cut, one row on the pipe's and the
Expand Down
100 changes: 100 additions & 0 deletions .agents/notes/implemented/2026-11-12-the-boards-are-apps-on-a-phone.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
title: On a phone the three boards are laid out as apps, with a tab bar at the bottom
status: implemented
date: 2026-11-12
scope: stack
---

## Decision

On a phone (640px and narrower) the three boards take an app's frame; on a
desk the layout is what it was, refined.

- **The tab strip is a tab bar** pinned to the bottom edge, frosted, one row of
equal cells with the icon over the name. The page on screen is marked by a
soft pill behind its icon; a count that needs somebody (`.count.warn`,
`.count.bad`) is a filled badge on the icon, any other count a quiet one. The
console's two groups stay one cell each, and a group's own pages are a
segmented control under the large title.
- **The bar steps aside while a field is typed in**, and comes back on the blur.
- **A large title** names the page on screen; once it has scrolled under the
header, the header shows the name in small, centred, and draws its bottom line.
- **The header keeps what a thumb needs:** the name, a search button that opens
the search field under it, the gear, and the live dot, with the word shown
only when the board is not live. Language, light or dark and ↻ are left to the
settings drawer, where all three boards already offered them.
- **A dialog is a sheet** rising from the bottom edge, its answers full width
and 46px tall; **a toast is a capsule** dropping from under the status bar.
- **An installed board refreshes when pulled** down from the top. It presses the
same ↻ a desk does, and it is wired only where nothing else answers a pull: an
installed board, on a touch screen, with no drawer or dialog open.
- **Depth, glass and motion are tokens** valued per mode: `--elev`/`--elev-up`
for cards, `--glass`/`--hairline` for the bars, `--ease`/`--spring` for what
slides, and `--num` (the system's rounded face, figures of one width) for
numbers a person counts. The overview's one sentence glows from its card's
corner in the colour of what it says, and its badge sends out a slow ring when
something needs somebody. A shown page fades and rises 8px into place. Under
`prefers-reduced-motion` all of it stands still.
- **The home-screen icons** are drawn on a surface lit from above with a faint
glow of the accent behind the glyph, and iOS is handed the full-bleed icon.

The new script is one pinned block, `hookstack app shell`, the twelfth in
`scripts/assert_design.py`.

## Why

The operator called the boards crude as apps and asked for taste ("这个 ui
太简陋了 … 做的有 taste 一点,给人惊喜"). Rendered at 414px before the change,
the header held seven controls in two rows (the name, the language, the gear,
light or dark, ↻, the live pill, and the search on its own row), and the tabs
sat under it, out of a thumb's reach, in a strip that read as a web page's.

**The tab bar was turned down once**, on 2026-10-06
(`2026-10-06-three-boards-install-on-a-phone.md`), for two reasons, and both
were checked before this reversed it:

- *A fixed bar fights the composer and the keyboard.* It did, the way any bar
does: the keyboard lifts a fixed bottom bar over the field it opened for. So
the bar now leaves while a text field, a textarea or a select has focus, the
way the phone's own apps hide theirs, and the console's session panes end above
the bar (`--tabbar-h`), so the composer is never under it. Measured headless:
the bar is `display: none` with the session composer focused and back on blur.
- *The operator's other installed board took its own out.* It has one again: its
design language (read 2026-11-12) lays a phone out as a fixed header and a
bottom tab bar for the places an on-call person opens most.

That other board also paid for a lesson this keeps: the View Transitions API,
used there for a change of page, flashed its fixed tab bar and swallowed the taps
of a whole transition, by design. A change of page here is a plain CSS animation
on the view being shown, which captures nothing and blocks nothing, and a live
refetch, which redraws inside a view without showing it again, never replays it.

Everything else follows from the bar: once the strip is at the bottom, the name
of the page is no longer on screen, which is what the large title is for; the
second row of a group's pages had nowhere to go but under that title; and with
the tabs gone from the top, the header could shed what a phone reaches through
the gear anyway. A browser tab keeps its own pull to refresh, so the board's
pull is wired only in an installed board, which has none.

## Consequences

- Measured headless on the stack smoke's data, every page of the three boards
(126 renders: 375px and 414px, both languages, light, and dark at 375px): no
page wider than the screen, no tab name cut, one row in the bar on all three
boards, every field at 16px; the pull arms at 64px of travel
and presses ↻ once; the header's small title follows the scroll both ways; the
group's segmented control presses the strip's own tabs, so the page's router
is still the only one, and it follows a language switch.
- On a desk the layout and every control are where they were; the cards gained a
shadow on white and a top edge in the dark, and numbers moved from `--mono` to
`--num`.
- The same sweep found the console's Approvals squeezing a report's title into
a column one word wide beside its buttons, on a phone; its two tables are one
block per row there now, like the flight recorder. And the desk's margin under
the strip had lifted the tab bar 20px off the bottom edge; the phone zeroes it.
- A home-screen icon on a phone keeps the image it was added with. The new icons
reach a phone only when the icon is added again, like the status-bar style.
- The tab shell's phone rules and the app shell together decide what a phone
shows; a fourth board gets both by copying the blocks.
- Not done: swipe-back from the edge to close a drawer, and a count-up of the
overview's numbers. Each is a gesture or a timer the boards do not have yet.
18 changes: 10 additions & 8 deletions OVERVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,16 +206,18 @@ investigated, approved by a named person, the condition ending, the fix held.

## On a phone

![the three boards at a phone's width: the pipe's attention entry with its six tabs in one row, the investigator's actions page under its runtime group with the procedure a person approved and what each command printed, and the judge's board](docs/img/hookstack-phones.png)
![the three boards at a phone's width, each with its tab bar along the bottom edge: the pipe's attention entry under its large title, its sentence glowing amber, the investigator's actions page under its runtime group with the procedure a person approved and what each command printed, and the judge's board](docs/img/hookstack-phones.png)

The same three boards at a phone's width, from a run of the rehearsal above.
Each installs as an app and runs under the status bar in its own colour; its
tabs fit one row, and the investigator's two groups each take one cell, the open
one unfolding its pages beneath as a segmented control. Wide tables become one
block per row, and every field types at a size the phone does not zoom into. A
press from a phone, on a card or in the console, approves the version of the
procedure that was on the screen; one that changed since is refused and the
console shows it again.
Each installs as an app and is laid out as one: a frosted tab bar along the
bottom edge, the page's name as a large title that the header takes over once it
scrolls away, the overview's one sentence glowing in the colour of its answer.
The investigator's two groups each take one cell in the bar, the open one's pages
a segmented control under the title. A dialog rises as a sheet, an installed
board refreshes when pulled down, wide tables become one block per row, and
every field types at a size the phone does not zoom into. A press from a phone,
on a card or in the console, approves the version of the procedure that was on
the screen; one that changed since is refused and the console shows it again.

## Running it locally

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ The investigator is the product. The pipe and the judge are the smallest alertin
- **Half a day, for real:** [STACK.md](STACK.md) runs all three from source; [docs/containment.md](docs/containment.md) is the boundary map you read *before* pointing it at a cluster.
- **What it does, in full:** [docs/what-you-get.md](docs/what-you-get.md).

![hookprobe's console: a real investigation, conclusion first, the root cause it could not establish said so, and the bill under it](docs/img/hookprobe-sessions.png)
![the three boards on a phone, each laid out as an app with a frosted tab bar along the bottom: the pipe's alerts under a large title, its one sentence glowing amber because two things wait on you; the investigator's runtime page with the procedure a person approved and what each command printed; the judge's board](docs/img/hookstack-phones.png)

The three boards read in Chinese or English and follow the system's light or dark; [the full tour](OVERVIEW.md) has every screenshot, from local runs and not mockups.
The three boards install on a phone as apps, read in Chinese or English and follow the system's light or dark; [the full tour](OVERVIEW.md) has every screenshot, from local runs and not mockups.

## How it fits together

Expand Down
4 changes: 2 additions & 2 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
- **半天,真上:**[STACK.md](STACK.md) 从源码一步步跑起三个服务;[docs/containment.md](docs/containment.md) 是指向真实集群**之前**要读的边界地图。
- **完整功能清单:**[docs/what-you-get.md](docs/what-you-get.md)(英文)。

![hookprobe 的控制台:一次真实的调查,结论在最前面,查不到的根因明说查不到,下面是这次的花费](docs/img/zh/hookprobe-sessions.png)
![三块看板装在手机上,都像一个 App,底部是毛玻璃标签栏:hookrelay 的告警页,大标题下那句话泛着琥珀色的光,因为有两件事等你处理;hookprobe 的运行页,一个有人批准过的处置步骤和每条命令的输出;hookjudge 的概览](docs/img/zh/hookstack-phones.png)

三块看板都能切换中英文,并跟随系统的浅色或深色;[完整介绍](OVERVIEW.md)(英文)里有全部截图,取自本地真实运行,不是效果图。
三块看板都能装到手机上当 App 用,能切换中英文,并跟随系统的浅色或深色;[完整介绍](OVERVIEW.md)(英文)里有全部截图,取自本地真实运行,不是效果图。

## 怎么串起来

Expand Down
Loading
Loading