Skip to content

Raise every control in the session dock to a phone touch target - #1152

Closed
Birfy wants to merge 1 commit into
mainfrom
dev4/feat/647-phone-dock
Closed

Birfy wants to merge 1 commit into
mainfrom
dev4/feat/647-phone-dock

Conversation

@Birfy

@Birfy Birfy commented Oct 25, 2026 •

Copy link
Copy Markdown
Collaborator

Owner: Dev4
Reviewer: Reviewer2
Base: 75ca7ab
Tip SHA: 9538394
Written-by: Dev4
Fixes #1149
Related #647

The phone pass (#362) raised the held-reply card to 40px and left the rest of the same session dock at its desktop sizes, so on a 390px screen the card answers at 40px and the buttons beside it ask the same finger for half a touch target.

Reproduce. grep -n "max-width:640px" plugins/social/client.js → one block, and its whole text was the held card's two rules. Everything else in the dock keeps .fs-btn 30px (client.js:153) and .fs-btn-sm 26px (:158): Invite by link, Approve all, the notice Accept / Decline, Close; Auto-send from now on is a 12px text run with no box.

What changed. Four rules added inside that same @media (max-width:640px) block, keyed off the dock's own container .bt-flybot-session-dock (core/src/client/flybot-slots.js): its buttons 40px, .fs-link a 32px hit area, and a bottom padding for env(safe-area-inset-bottom) — which is 0 on today's shell, because dsh-web-frontend's viewport has no viewport-fit=cover (Dev3's finding on #647), so that rule waits for a shell that sets it and costs nothing until then. Nothing outside the query moved; no component changed, and wider screens keep the compact sizes.

What I ran (tip 85af6a00, TMPDIR=/data/dsh/bot/workspace/tmp DSH_HARNESS=/opt/flybot TZ=UTC):

  • cd plugins/social && node --import ./test/resolve.mjs --test test/social.test.js → 438 tests, 438 pass, 0 skipped
  • the new case alone → ok 1 - at phone width the rest of the session dock is touch-sized too, not only the held card (#647)
  • node .github/scripts/changelog.mjs check → 40 fragments well formed; node scripts/ds-bot-patches.mjs check → ok 10 patches, ok core/ equals ds-bot/ + patches
  • diff -rq against a clean c0a33696 unpack → only these 4 files

Red without the source change. Put the phone block back to its base text (held card only) → the new case reds on the dock rule, actual being the base block. Restore → green. Counts: 1 fail / 0 pass, then 438/438.

Not run: npm run verify (no esbuild or binary on this host), oxlint, the tree/timeline/core suites (untouched), and no real phone or browser — the assertions are on the CSS the plugin ships, the same shape as core/test/mobile-layout.test.js.

Risk.

The inset rule is inert today (evaluates to 0): it is there so the dock follows a shell that sets viewport-fit=cover, not as a claim that the home indicator is already handled. The rules are keyed on the dock container, so a component rendered outside .bt-flybot-session-dock keeps the desktop metrics — that is deliberate (drawers and dialogs on a phone are a separate pass), and a rule matching nothing costs nothing. The touch height is 40px, matching #362 rather than the 44pt/48dp platform guidance: raising both together is the follow-up, not this half.

@Birfy Birfy added the agent-pr Pull request opened by an agent label Oct 25, 2026
@Birfy
Birfy force-pushed the dev4/feat/647-phone-dock branch from bfb4bb3 to 85af6a0 Compare October 25, 2026 10:13
The phone pass (#362) raised the held-reply card to 40px and left the rest of the same dock at its
desktop sizes: `.fs-btn` 30px, `.fs-btn-sm` 26px, and `.fs-link` a 12px text run. On a 390px screen
the card answers at 40px and the buttons beside it ask the same finger for half a touch target (#1149).

They now sit in the single `@media (max-width:640px)` block the plugin already has, keyed off the
dock's own container `.bt-flybot-session-dock`: buttons 40px, the two links a 32px hit area, and a
bottom padding for `env(safe-area-inset-bottom)` — 0 on today's shell, which has no
`viewport-fit=cover`, so the rule waits for one. Nothing outside that query moved.

Fixes #1149
@Birfy
Birfy force-pushed the dev4/feat/647-phone-dock branch from 85af6a0 to 9538394 Compare October 25, 2026 10:21
@Birfy

Birfy commented Oct 25, 2026

Copy link
Copy Markdown
Collaborator Author

Written-by: Dev4

Withdrawn, superseded by #1150 (Dev3). #1150 puts the same lift on the global rules — .fs-btn,.fs-btn-sm{height:40px}, .fs-link{min-height:40px} — while this branch scoped the same two rules to .bt-flybot-session-dock and gave .fs-link 32px. Keeping both would land two different numbers on one class and write the same buttons twice, which is the duplication we are trying to avoid.

Taking the dock-scoped button and link rules out leaves only a padding-bottom:calc(6px + env(safe-area-inset-bottom,0px)) (that env() is 0 on today run shell, so it changes nothing today) and a 6px .fs-group-strip margin: polish, not the defect #1149 filed. Per AGENTS.md that does not justify a PR of its own.

Nothing here reached main: main 75ca7ab9 still carries the single @media block at plugins/social/client.js:258 without any of these rules.

The dock half of #1149 is therefore #1150 own: its global rules cover the dock buttons, links and inputs too. #647 itself stays open — its install half (manifest name, 192/512 bitmaps, viewport-fit=cover) is needs-human inside @deepseek-ai/dsh-web-frontend.

Branch dev4/fix/647-phone-dock stays on the remote at 95383948 for reference if #1150 is bounced.

@Birfy

Birfy commented Oct 25, 2026

Copy link
Copy Markdown
Collaborator Author

Written-by: Dev4

Closing as superseded per the routing in the group: the two dock-only declarations from this branch (.bt-flybot-session-dock{padding-bottom:calc(6px + env(safe-area-inset-bottom,0px))}, .fs-group-strip{margin:6px 8px}) are handed to #1150 (comment 6396475696), .fs-link lands at a single 40px there, and #1149 closes through #1150 Fixes #1149 — not through this PR, which never reached main. Branch kept at 95383948 only as a diff reference.

Birfy added a commit that referenced this pull request Oct 25, 2026
The dock's phone half moves in with the panel's, so one landing closes both
issues and the same buttons are not written twice:

- `.bt-flybot-session-dock{padding-bottom:calc(6px + env(safe-area-inset-bottom,0px))}`
  and `.fs-group-strip{margin:6px 8px}` are taken over from #1152 (closed as
  superseded). The inset carries a 0px fallback, so today it is a plain 6px —
  correct at inset 0, and it becomes real when the shell gains
  `viewport-fit=cover` (#647);
- `.fs-link` keeps one number everywhere (40px); no dock-scoped 32px override;
- the env() fallback is asserted, so the rule cannot silently become inert.
HugoXK pushed a commit to HugoXK/FlyBot that referenced this pull request Oct 25, 2026
…hinkFlowLab#1150)

Owner: Dev3
Reviewer: Reviewer2
Written-by: Dev3
Base: 15d7e52
Tip SHA: 2060886
Fixes ThinkFlowLab#1147
Fixes ThinkFlowLab#1149

The in-repo half of the phone pass: the surfaces `plugins/social/` draws
itself, below 640 px. The shell half (manifest icons,
`viewport-fit=cover`, `env(safe-area-inset-*)` being 0 on this stack)
stays in ThinkFlowLab#647 as `needs-human`. The one declaration that reads the inset
(`ThinkFlowLab#1152`'s dock padding, taken over here) carries a `0px` fallback, so
it is correct as a plain 6 px today and the test asserts that fallback.

**Review INC @ `7d0e1542` fixed in `f9ce30ae`; ThinkFlowLab#1152's dock half taken
over in `84906de8`; rebased onto `15d7e525` in `20608864`** (the ThinkFlowLab#1139
merge touched `plugins/social/client.js`) — the block was at `:258` with
two later rules of equal specificity redeclaring what it set, and two
footer buttons out-specifying `.fs-btn`; the dock's own phone rules now
land here too, so ThinkFlowLab#1149 closes with this PR instead of a second PR on
the same line.

## Reproduce

On this stack the plugin has exactly one `@media` block and it only
sized ThinkFlowLab#362's held-reply Approve / Reject pair. Everything else keeps its
desk size below 640 px:

```
$ grep -c '@media (max-width:640px)' plugins/social/client.js   # 1
$ grep -o '\.fs-btn{[^}]*height:[0-9]*px' plugins/social/client.js # height:30px
$ grep -o '\.fs-btn-sm{[^}]*height:[0-9]*px' plugins/social/client.js # height:26px
$ grep -o '\.fs-remove{[^}]*width:[0-9]*px' plugins/social/client.js # width:20px
$ grep -o '\.fs-preview-foot \.fs-btn{[^}]*height:[0-9]*px' plugins/social/client.js # height:28px
$ grep -o '\.fs-wait-foot \.fs-btn{[^}]*height:[0-9]*px' plugins/social/client.js # height:36px
```

30 / 26 / 20 / 28 / 36 px controls are below every phone floor (WCAG
2.5.8 asks 24 px; the phone guides ask 44/48), and a 12 px `.fs-link`
word is not a row at all.

**The cascade bug the first tip had**, and why it mattered: the block
sat at `:258`, and `.fs-remove{width:20px;height:20px}` (`:261`) and
`.fs-link-input{height:28px}` (`:300`) come after it with the same
specificity — the browser took the later ones, so the remove button
stayed 20 px and the group-invite input stayed 28 px while the test
passed. `.fs-preview-foot .fs-btn` (28 px) and `.fs-wait-foot .fs-btn`
(36 px) are `(0,2,0)` against the block's `(0,1,0)`, so no amount of
ordering fixed those.

## What changed

- `plugins/social/client.js` — **the whole `@media (max-width:640px)`
block moved to the end of the stylesheet**, after `.fs-fail`, so order
decides where specificity ties; ThinkFlowLab#362's held-reply rule keeps its place
at the head of the block and its numbers. Inside it:
`.fs-btn`/`.fs-btn-sm` 40 px; `.fs-link`/`.fs-hold-more` become
`inline-flex` rows at `min-height:40px` (**one number everywhere — no
dock-scoped 32 px override**); `.fs-icon` 40x40;
`.fs-input`/`.fs-digits`/`.fs-link-input` 40 px tall; `.fs-paste` 40 px;
`.fs-remove` 32x32; `.fs-pending-item`/`.fs-past-head` 40 px rows;
`.fs-hold .fs-draft` `overflow-wrap:anywhere`; `.fs-preview-foot
.fs-btn,.fs-wait-foot .fs-btn` at 40 px (the only way past their
`(0,2,0)`); and **`.bt-flybot-session-dock{padding-bottom:calc(6px +
env(safe-area-inset-bottom,0px))}` plus `.fs-group-strip{margin:6px
8px}`, taken over from ThinkFlowLab#1152** so one landing closes ThinkFlowLab#1149.
- `plugins/social/test/group-phone.test.js` — new, 14 cases, reads the
shipped `client.js` (not a copy). Four are guards: *nothing may follow
the phone block*, *the two footer entries exist*, *the dock half is
present and carries no 32 px link override*, and *every `env()` in the
block has a `0px` fallback* — so the inset rule cannot become inert or
unverifiable.
- `docs/subsystems/social.md` — one line in **Owns**, narrowed to what
the code does: 40 px buttons and inputs, row controls at least 32 px,
the block is the last one in the sheet.
- `changelog.d/1147-group-phone-tap-targets.changed.md` — new fragment,
same wording.

Layout only: no copy, no behaviour, no `core/`, and no rule outside the
media block (the dock padding is the block's only inset read, and it
falls back to `0px`). Desktop sizes are asserted unchanged in the same
test file (30 / 26 / 20 px).

## What I ran

```
rebased tree (main 15d7e52 + these files)
plugins/social$ node --import ./test/resolve.mjs --test "test/*.test.js"
# tests 507  # pass 507  # fail 0  # skipped 0

plugins/social$ node --import ./test/resolve.mjs --test test/group-phone.test.js
# tests 14   # pass 14   # fail 0
```

Each acceptance path has an assertion. They are CSS-string assertions
because that is the only surface this package renders in Node (the panel
is plain JS writing a `<style>` string; there is no DOM harness in
`plugins/social/test/`), and the path cases say so. Path 1 pins the two
surfaces where peer text actually reaches the reader from this plugin
(`.fs-hold .fs-draft`, `.fs-msg`); the group message *list* itself is
core's.

## Red without the source change

**Knife 1 — main's `client.js` (`15d7e525`), nothing else changed:** `#
tests 14 # fail 12` — every case that pins a new rule is red. The two
that stay green are ThinkFlowLab#362's own pair and the single-block shape; both are
keep-assertions by design, and path 2's new behaviour is pinned by case
5, which is red.

**Knife 2b — drop the `0px` fallback**
(`env(safe-area-inset-bottom,0px)` → `env(safe-area-inset-bottom)`),
nothing else changed: `# pass 12 # fail 2`, red are exactly *path 2 …
clears the home indicator at inset 0* and *the dock half lands here too*
— the rule would be inert on this stack, and the test says so.

**Knife 3 — the dock half removed** (both declarations stripped): *the
dock half lands here too* is the only case that turns red.

**Knife 2 — the cascade guard:** moving the block back to `:258` and
changing nothing else → `# tests 14 # pass 13 # fail 1`, red is exactly
*the phone block is the last rule in the sheet*; restoring the file puts
it back to 14/14.

## Not run

- No real viewport. This repository has no browser or DOM harness for
the plugin, so "375 px" is pinned as the rule set the browser loads plus
the cascade order, not as a rendered screenshot.
- No `core/` suite: nothing under `core/` is touched and the `ds-bot`
patches are untouched (no `core/` change, so `ds-bot-patches generate`
is not involved).
- Nothing on a phone against the live server.

## Risk

- **Low, CSS-only, scoped by the media query.** Worst case is a taller
panel on a phone; the desktop numbers are asserted unchanged.
- **Moving the block to the end is the one structural change:** it also
means a future rule appended *after* the block would silently win again.
That is exactly what the new "nothing may follow the block" case turns
red on, so the guard is in the test file, not in a comment.
- **It does not make the app installable and does not fix the notch.**
`env(safe-area-inset-bottom)` is 0 here because the shipped `index.html`
has no `viewport-fit=cover`, so the dock rule reads as a plain 6 px
today and is **not** evidence the inset works; ThinkFlowLab#647 tracks the icon pair
and the viewport meta, and the `0px` fallback is asserted precisely so
nobody later reads that rule as proof.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

At phone width the session dock keeps its desktop 26–30px buttons, so a held reply is approved one-handed but everything beside it is not

1 participant