Public Access
Give the phone the + it was promised, and the editors behind it
Steps 5 and 6 of docs/adding-hosts-on-the-phone.md, which finishes it. The phone can now put things in a keychain rather than only read one. The floating + arrives three designs after it was first asked for. It was refused twice on the honest grounds that it would open an editor this head had not got — and that stopped being true when steps 1 to 4 landed. It raises a sheet rather than a form, because "add" on this screen has been two operations since groups existed: a host, or a group to file hosts under. Both editors are cards in the list's own row, swapped for the list rather than stacked over it, following SnippetsScreen and for the reason written there: a form on top of the list hides what it is about. There is still no dialog and no editor screen anywhere on this head. The port and username boxes may be left empty, and the greyed text inside each is what the host will actually use — following the group picker as it moves, so the form says what leaving a box blank will do rather than making it a guess. The back gesture gains a guard above the switch rather than a case inside it. An editor is not a screen and has no entry there, and it is strictly nearer: the sheet sits over the editor's own screen, so back lowers whatever is topmost before it considers moving between screens. A group editor needs a way in, and the phone draws no groups panel — so the heading in the host list gains a pencil. A + that adds groups with no way to correct one is the same strange thing to ship as one that adds hosts with no way to correct one, and worse: a group's defaults are what every host beneath it falls back to. Scouting step 6 turned up a bug worth more than the prose it was looking for. ReloadGroupsAsync read the active vault alone, which was a cosmetic limitation while a group carried only a name — a teammate's host showed under UNGROUPED and nothing else was lost. Since a group began lending a port, a username and a binding, the same omission silently dropped all three: that host would dial 22 as nobody while the machine is on 2222 as deploy, with nothing on screen saying why. The resolution map now spans every readable vault. The editable list stays narrow, because a row shown across vaults needs a vault id for rename and delete and two vaults may hold groups with one name — but the map needs none of that, since it is only ever asked what an id says. An adversarial review of this change found something I had written a comment denying. The + was a child of the outer Panel rather than of the list's row, so it anchored to the bottom of the screen and sat squarely over the new EDIT button and the lower edge of CONNECT — and because a Panel hit-tests its last-declared child first, tapping the right end of EDIT would have raised the add sheet. The comment beside it claimed it was in the list's row precisely so that could not happen, and manual-checks 8.1 names that exact failure. It is in Grid.Row 2 now. The same pass caught a connect bar that was disabled rather than hidden while its own comment said hidden, a scrim that would have flashed its default pressed chrome across the screen, a fab comment claiming an accent fill no other button had when Button.primary has it, and a back-gesture remark naming two view-model properties that do not exist anywhere in the repo. That last one came from this plan, which invented IsVaultsSurface and IsConnectionsSurface, and pointed step 6 at a ConnectionsScreen.axaml that has never existed. Both are corrected in the plan rather than quietly worked around, along with a fourth "groups are flat" site in README.md that step 1 missed. The phone's rectangles are not measurable and structurally never will be: the layout suite is net10.0, the Android head is net10.0-android, and Avalonia's application is a one-shot process global — so a second head cannot share the process even if the reference were possible. Everything the sheet and both editors bind to is shared and is tested headlessly here; the pixels go to docs/manual-checks.md phase 8, nine checks, which is where this project already sends what it cannot assert. Tags remain the one thing asked for that did not ship. Tag is a full item kind and TagIds merges, encodes and resolves, but no screen draws a chip or offers to add one, so the tags a client can store are ones nothing here can see. Both editors carry the set through a save untouched so a client that can set them does not lose them. Recorded as half-shipped in docs/design-import-gaps.md rather than quietly left out. Verified by the whole suite: 1402 tests over nineteen projects, none failing. Both heads build. Nothing seen on a display — phase 8 is what that costs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -691,3 +691,101 @@ add a bucket — then come back.
|
||||
|
||||
**Failure means:** the screen has gone back to copying the vault's lists once at unlock. Covered by
|
||||
`TheTransfersScreen_FollowsTheVaultsHostList`; this is the version of it with a real picker in front of it.
|
||||
|
||||
## Phase 8 — Adding hosts on the phone
|
||||
|
||||
Nine checks, and the reason there are nine rather than none is worth stating: **the layout suite cannot see
|
||||
any of this and structurally never will.** `DodoSSH.Client.App.Layout.Tests` targets `net10.0` and
|
||||
`DodoSSH.Client.Android` targets `net10.0-android`, so a project reference is impossible; Avalonia's
|
||||
application, dispatcher and platform are one-shot process globals, so a second head cannot share the
|
||||
process either; and the phone's numbers only mean anything measured under the phone's own style graph.
|
||||
Every property and command below is covered headlessly in `ShellFlowTests` — the flow is shared, and only
|
||||
the rectangles are not. So what follows is the rectangles.
|
||||
|
||||
Run on a 360dp-wide device or emulator, unlocked, on HOSTS.
|
||||
|
||||
### 8.1 The + is reachable and is not covering anything
|
||||
|
||||
Look at the bottom right of the host list with no host selected.
|
||||
|
||||
**Pass:** a filled accent circle, 56×56, clear of the bottom edge and of the last row's text. Then select a
|
||||
host so the connect bar rises.
|
||||
|
||||
**Pass:** the + has moved up with the bar and is not over the CONNECT button.
|
||||
|
||||
**Failure means:** the + was anchored to the screen rather than to the list's row. Nothing in the layout
|
||||
harness would catch it if it could run — its only fit rule is containment inside the window, never overlap
|
||||
between two controls, so a button sitting squarely on top of CONNECT reports clean.
|
||||
|
||||
### 8.2 The sheet says what each choice does
|
||||
|
||||
Tap the +.
|
||||
|
||||
**Pass:** a sheet rises from the bottom with a grab handle, two rows — **New host** and **New group** — each
|
||||
with a sentence under it, and CANCEL. The scrim darkens the list.
|
||||
|
||||
**Note:** the four nav destinations underneath stay lit and tappable. That is deliberate — this is a menu,
|
||||
not a decision, and navigating away is a fine way to answer it. Compare the changed-host-key screen, which
|
||||
must offer no way out but BACK.
|
||||
|
||||
### 8.3 The sheet is dismissible three ways
|
||||
|
||||
Tap the scrim. Reopen, tap CANCEL. Reopen, use the system back gesture.
|
||||
|
||||
**Pass:** all three lower it, and none of them opens an editor.
|
||||
|
||||
**Failure means:** back is being handled by the screen switch rather than by the guard above it, and the
|
||||
gesture has left HOSTS instead of closing the sheet.
|
||||
|
||||
### 8.4 An empty port box says what it will dial
|
||||
|
||||
Add a group with a default port of 2222, then tap + → New host and choose that group in the GROUP picker.
|
||||
|
||||
**Pass:** the port box is **empty**, and the greyed text inside it changes to `2222` the moment the group is
|
||||
chosen. The username box behaves the same way.
|
||||
|
||||
**Failure means:** the editor loaded a resolved value into the box instead of a placeholder behind it —
|
||||
saving would then pin 2222 onto the host, and changing the group later would leave that host behind.
|
||||
|
||||
### 8.5 A host created that way inherits rather than copies
|
||||
|
||||
Save that host, then edit the group's default port to 2200 and look at the host's row.
|
||||
|
||||
**Pass:** the address now reads `…:2200`. Connect: the session goes to 2200.
|
||||
|
||||
**Failure means:** the port was copied at creation and the group is a one-shot template.
|
||||
|
||||
### 8.6 Filing a host into a group does not silently pin its password
|
||||
|
||||
Add a group with a default SSH key. Add a host with no group and leave AUTHENTICATION on **Password (ask
|
||||
each time)**. Save. Now edit it and choose the group.
|
||||
|
||||
**Pass:** the picker gains an **Inherit from group** entry and moves to it. Save, and the host row reads
|
||||
`key`, and the connect bar shows a note naming the group rather than a password box.
|
||||
|
||||
**Failure means:** the picker's "Password (ask each time)" was written as a decision when it was only the
|
||||
absence of one, and this host will never use the fleet's key.
|
||||
|
||||
### 8.7 A host can still refuse its group's key
|
||||
|
||||
On that same host, set AUTHENTICATION back to **Password (ask each time)** and save.
|
||||
|
||||
**Pass:** the connect bar shows a password box again, and it survives a lock and unlock.
|
||||
|
||||
**Failure means:** the fourth state has no storage and the host is inheriting whatever the group says.
|
||||
|
||||
### 8.8 Back closes the topmost thing, one at a time
|
||||
|
||||
Tap + → New host, then use the back gesture.
|
||||
|
||||
**Pass:** the editor closes and the list is back, with the boxes cleared. Back again leaves the application
|
||||
— HOSTS is the screen the app opens on and back from it should exit, as it always did.
|
||||
|
||||
### 8.9 Every target is a thumb's size
|
||||
|
||||
Walk the sheet's two rows, the editor's fields, both pickers, the checkbox and the four buttons.
|
||||
|
||||
**Pass:** nothing is under 44 effective pixels tall.
|
||||
|
||||
**Failure means:** a control took its size from its glyph. The desktop suite's only tap-target assertion has
|
||||
a floor of 20 pixels — set for a mouse — so copying it here would certify a target no thumb can hit.
|
||||
|
||||
Reference in New Issue
Block a user