Public Access
Record v5c in the design-import log, and true up the manual checks
This commit is contained in:
@@ -287,9 +287,9 @@ the chrome, hosts and terminals, file transfer, the vault, teams, and preference
|
|||||||
> | The tab and status dots' third, amber state | ◆ **Still two states**, for the reason the hosts screen's own dot has stayed two states since v3: green means a shell is open (or a session is connected), grey means it is not, and nothing here pings a host to justify a third colour meaning "reachable but not connected". |
|
> | The tab and status dots' third, amber state | ◆ **Still two states**, for the reason the hosts screen's own dot has stayed two states since v3: green means a shell is open (or a session is connected), grey means it is not, and nothing here pings a host to justify a third colour meaning "reachable but not connected". |
|
||||||
> | The status bar's negotiated cipher, host-key algorithm and key/credential name | Omitted, and recorded rather than silently dropped. None of the three is reachable from this shell today: `SshNetConnection` holds the negotiated algorithms but nothing reads them off `ISshConnection`; `HostKeyPresentation` exists only for the trust questions a connection asks on the way in, not for a session already open; and a key's display name is never threaded past the connect call into anything the session keeps. What is real and bound is CONNECTED plus a dot, the session's own elapsed timer, and — terminal surface only — `UTF-8`, worded as a fact about this client's own renderer rather than a claim the remote agreed to. Candidates for future plumbing, not a defect closed here. |
|
> | The status bar's negotiated cipher, host-key algorithm and key/credential name | Omitted, and recorded rather than silently dropped. None of the three is reachable from this shell today: `SshNetConnection` holds the negotiated algorithms but nothing reads them off `ISshConnection`; `HostKeyPresentation` exists only for the trust questions a connection asks on the way in, not for a session already open; and a key's display name is never threaded past the connect call into anything the session keeps. What is real and bound is CONNECTED plus a dot, the session's own elapsed timer, and — terminal surface only — `UTF-8`, worded as a fact about this client's own renderer rather than a claim the remote agreed to. Candidates for future plumbing, not a defect closed here. |
|
||||||
> | S3 dimmed in the design's own switcher | **Enabled.** The mock leaves S3 as future work; this application already has bucket browsing, so SSH, SFTP and S3 are a true three-way segment, wired to `IsSshShowing`, `IsTransfersShowing` and `IsBucketsShowing` exactly alike. |
|
> | S3 dimmed in the design's own switcher | **Enabled.** The mock leaves S3 as future work; this application already has bucket browsing, so SSH, SFTP and S3 are a true three-way segment, wired to `IsSshShowing`, `IsTransfersShowing` and `IsBucketsShowing` exactly alike. |
|
||||||
> | The S3/Buckets screen | **Did not get the session shell this pass.** `TransfersScreen` serves both SFTP and S3 today and only the SFTP usage in `MainWindow.axaml` sits inside the new tab row/header/status bar/sidebar; the S3 usage is unchanged. |
|
> | The S3/Buckets screen | **Did not get the session shell in v5b.** `TransfersScreen` serves both SFTP and S3 today and only the SFTP usage in `MainWindow.axaml` sat inside the new tab row/header/status bar/sidebar; the S3 usage was unchanged at the time. **v5c gives it the shell's own look without the machinery** — a 26-pixel padded, bordered, radius-12 container and nothing past that, since a bucket has no tab to close, no host to head a card with and no pin for a sidebar to show; see the v5c section, below. |
|
||||||
> | No pins destination in the design at all | **Kept anyway.** The rail still carries Pins — `KnownHostsScreen` — because the mock has no screen for approved host keys and this application's has to stay reachable. |
|
> | No pins destination in the design at all | **Kept anyway.** The rail still carries Pins — `KnownHostsScreen` — because the mock has no screen for approved host keys and this application's has to stay reachable. |
|
||||||
> | The popover's Settings and Preferences rows, and the design's own Settings-* family of screens | Both land on the one screen this application actually has, `Preferences` — the design's own Settings area is future work, so this is two doors to one room rather than a door removed. |
|
> | The popover's Settings and Preferences rows, and the design's own Settings-* family of screens | **Landed in v5c.** What was two doors to one room in v5b — Settings and Preferences both opening the same bare `Preferences` screen — is now two of three doors onto their own settings pages: Settings opens General, Preferences opens Preferences, and a third row, Vaults, opens Vaults. All three are real, distinct pages inside one settings mode; see the v5c section, below. |
|
||||||
> | `· Org` after the user chip's name, and a `Primary` tag on a vault row in the popover | Neither. There is no organisation concept behind a vault — only the vault itself — and no vault is distinguished as primary; the popover's vault rows are the existing shown-vaults toggles, restyled. |
|
> | `· Org` after the user chip's name, and a `Primary` tag on a vault row in the popover | Neither. There is no organisation concept behind a vault — only the vault itself — and no vault is distinguished as primary; the popover's vault rows are the existing shown-vaults toggles, restyled. |
|
||||||
> | The design's titlebar, which has nowhere for a sync indicator | `SYNCED` stays, on the titlebar's right side, ahead of the window's own minimise/maximise/close buttons — the one thing this titlebar keeps that the design's own does not draw at all. |
|
> | The design's titlebar, which has nowhere for a sync indicator | `SYNCED` stays, on the titlebar's right side, ahead of the window's own minimise/maximise/close buttons — the one thing this titlebar keeps that the design's own does not draw at all. |
|
||||||
> | Per-tab SFTP sessions, implied by a tab row shared between the terminal and the SFTP surface | **Not built, and not what shipped instead.** A click on the SFTP tab row runs `MainWindowViewModel.SelectFilesHostAsync`, which opens (or reuses) a second, SFTP-specific connection through the same "Browse files" plumbing a pin click already used — an honest second login, not a channel multiplexed onto the terminal's. `SelectedTab` moves with the click, which is also what keeps the sidebar's QUICK ACCESS in step — that list is keyed to `SelectedTab` on both surfaces, so selecting a different terminal tab afterwards can leave QUICK ACCESS naming a host that is not the one the remote pane is actually browsing. |
|
> | Per-tab SFTP sessions, implied by a tab row shared between the terminal and the SFTP surface | **Not built, and not what shipped instead.** A click on the SFTP tab row runs `MainWindowViewModel.SelectFilesHostAsync`, which opens (or reuses) a second, SFTP-specific connection through the same "Browse files" plumbing a pin click already used — an honest second login, not a channel multiplexed onto the terminal's. `SelectedTab` moves with the click, which is also what keeps the sidebar's QUICK ACCESS in step — that list is keyed to `SelectedTab` on both surfaces, so selecting a different terminal tab afterwards can leave QUICK ACCESS naming a host that is not the one the remote pane is actually browsing. |
|
||||||
@@ -307,6 +307,63 @@ the chrome, hosts and terminals, file transfer, the vault, teams, and preference
|
|||||||
> | The design's shorter captions — "Type into terminal", the rationale-only sentence for "runs on insert" | The screen's own longer, more actionable wording kept instead: the insert button still names the destination tab (`TYPE INTO {tab}` / `NO TERMINAL OPEN`), and the "runs on insert" caption still states the operational consequence rather than only the reason the setting exists. Both of the design's sentences are true; the ones already here say more. |
|
> | The design's shorter captions — "Type into terminal", the rationale-only sentence for "runs on insert" | The screen's own longer, more actionable wording kept instead: the insert button still names the destination tab (`TYPE INTO {tab}` / `NO TERMINAL OPEN`), and the "runs on insert" caption still states the operational consequence rather than only the reason the setting exists. Both of the design's sentences are true; the ones already here say more. |
|
||||||
> | A delete confirmation for a snippet | **Built, matching the mock.** `SnippetsViewModel.RequestDelete`/`ConfirmDelete`/`CancelDelete` say the same vault-wide-reach, tombstone, no-undo sentence `VaultViewModel.HowFarADeletionGoes` already says for every other item kind — additive beside the existing uncounted `DeleteCommand`, which the phone's own DELETE row still calls. |
|
> | A delete confirmation for a snippet | **Built, matching the mock.** `SnippetsViewModel.RequestDelete`/`ConfirmDelete`/`CancelDelete` say the same vault-wide-reach, tombstone, no-undo sentence `VaultViewModel.HowFarADeletionGoes` already says for every other item kind — additive beside the existing uncounted `DeleteCommand`, which the phone's own DELETE row still calls. |
|
||||||
> | The Logs screen's own footer sentence about encryption | Adopted verbatim — verified against this screen's own header remark and ADR 0001 before shipping it, both true, so it is drawn as literal text rather than reworded. |
|
> | The Logs screen's own footer sentence about encryption | Adopted verbatim — verified against this screen's own header remark and ADR 0001 before shipping it, both true, so it is drawn as literal text rather than reworded. |
|
||||||
|
>
|
||||||
|
> ## The desktop's v5c — a settings area, at last
|
||||||
|
>
|
||||||
|
> An eighth pass, and the first to give the design's own Settings area a home of its own rather than folding
|
||||||
|
> its two real screens into the rail's popover. Settings is a full-window **mode** now, swapped in wholesale
|
||||||
|
> rather than laid over anything: its own 53px titlebar reading "Back to application", a 340px `SettingsNav`
|
||||||
|
> rail — SETTINGS (General, Vaults, Account) above CUSTOMIZE (Security, Preferences, Groups, Tags), Logout
|
||||||
|
> pinned below both — and a content column capped at the design's own 1100 pixels, which is 741 beside the
|
||||||
|
> rail at the window's own minimum rather than the full 1100 (`LayoutHarness.SettingsContentWidth`). Seven
|
||||||
|
> pages, not the design's eight: the ORGANISATION section and its one page are refused outright, below. Two
|
||||||
|
> of the seven are wholly new — Account and Security did not exist as screens before this pass — and the
|
||||||
|
> other five are the old `PreferencesScreen.axaml` and `VaultsScreen.axaml` split apart and restyled into the
|
||||||
|
> design's own card idiom; both files are deleted, and every command either one offered is reachable here
|
||||||
|
> exactly once. The popover's own Settings, Vaults and Preferences rows, and everywhere else in the codebase
|
||||||
|
> that used to navigate to the bare `Preferences` or `Vaults` screen, now land in this mode instead —
|
||||||
|
> `MainWindowViewModel.ShowScreen` redirects at that one point rather than at every caller that used to reach
|
||||||
|
> either screen directly.
|
||||||
|
>
|
||||||
|
> The importer moved inside this same chrome rather than staying a screen of its own: it draws over the
|
||||||
|
> Preferences page as a boolean overlay — `SettingsNav` stays lit on Preferences the whole time it is open,
|
||||||
|
> and the titlebar's own back button reads "Back to preferences" instead of "Back to application" — restyled
|
||||||
|
> to the design's own table (ALIAS/HOSTNAME/USER/PORT/WHAT THIS MEANS, one header tick-all button in place of
|
||||||
|
> the old TICK ALL/TICK NONE pair). Known Hosts was restyled the same pass but kept its own place: it stays a
|
||||||
|
> main-chrome screen, reached from the rail's Keys entry and now also from Security's own "Approved host
|
||||||
|
> keys" row, because the design draws no page for it at all. And the S3/Buckets surface — which got none of
|
||||||
|
> v5b's session shell — picked up that shell's *look* this time without its machinery: a 26-pixel padded,
|
||||||
|
> bordered, radius-12 container and nothing else, since a bucket has no tab row, no host to head a card with,
|
||||||
|
> no status-bar fact to print and no pin for a sidebar to show.
|
||||||
|
>
|
||||||
|
> | v5c element | What ships instead |
|
||||||
|
> | --- | --- |
|
||||||
|
> | The Settings-Organisation page, and the rail's own ORGANISATION section | Refused outright. No organisation entity exists anywhere in this product — a team is the membership list behind a shared vault, and there is exactly one tenant per deployment — so the rail simply has no third section; see `SettingsNav.axaml`'s own remark. |
|
||||||
|
> | General: the update-channel switcher, Launch at login, Reopen tabs, the theme control and the language picker | Refused, and carried on the page's own NOT BUILT YET card rather than left silently missing: which channel a copy follows is fixed when it is built, nothing registers this application with Windows' own startup list or remembers a tab list across a launch, only the one dark theme exists, and there is no i18n anywhere in this client. |
|
||||||
|
> | Vaults: the VAULT DEFAULTS card (auto-lock, require-password-on-unlock, relay) and the RECOVERY card (kit, export) | Both refused outright — none of the three settings exists, sync is always on, and there is no recovery kit and no export. |
|
||||||
|
> | Vaults: "Manage devices", and the "3 devices" count on the sync line | Refused. There is no list-devices endpoint anywhere in this client. |
|
||||||
|
> | Vaults: per-vault UNLOCKED/LOCKED chips, and the "Unlock ⟨vault⟩" modal | Refused. This application locks the keychain as a whole, not one vault at a time, so there is no per-vault state for a chip or a modal to act on; the keychain-level fact is the sync card's own dot instead. |
|
||||||
|
> | Vaults: the magenta "Default" badge | Dropped rather than faked. The app does track a "new items go to" vault, but that lives on a different view model than the row being drawn here, and cross-referencing the two per card would be more machinery than the badge is worth. |
|
||||||
|
> | Vaults: "Sorted by name ▾" | Decorative in the mock, and not drawn — `VaultsViewModel.Vaults` is already ordered personal-first-then-name, and there is no second order to switch to. |
|
||||||
|
> | Vaults: member avatar stacks on every card | Refused on an unselected card — `VaultRowViewModel` carries a member *count*, not the members themselves, and only the selected vault's own member list is actually loaded. A count stands in on the card; avatars are real in the members panel, where the data is. |
|
||||||
|
> | Account: Edit profile, Change passphrase, the DEVICES list, Sign out everywhere | All four refused — no endpoint exists for any of them, and the passphrase is a one-time enrollment choice with no change flow. What is new and real on this page instead: the SIGN-IN card's issuer sentence, `MainWindowViewModel.Issuer` off `MeResponse.Issuer`, cached the same turn the name and email are and surfaced here for the first time — "Signing in proves who you are — it never decrypts a vault." |
|
||||||
|
> | Security: the strict-host-key toggle, the allowed-algorithms chips, the auto-lock link row | Refused — the app always asks on a changed key, there is no algorithm allow-list anywhere in the SSH stack, and there is no auto-lock setting for the link to point at. |
|
||||||
|
> | Security: RECENT SECURITY EVENTS as a list of rows | Shipped as the link alone, "See all activity in Logs." A real list would mean a filtered read over `LogsViewModel`'s two logs, and which entries count as "security" is a judgement call the design does not resolve; the honest link is complete on its own. |
|
||||||
|
> | Preferences: the device-name editor, Font/Cursor/Scrollback/Copy on select/Terminal bell, the clipboard-clear delay, confirm-run-on-insert | All refused, on the page's own carried-over NOT-BUILT idiom — none of the seven has anything behind it. Windows Hello's register/"Stop unlocking here" pair moved off this page to Security instead, one home rather than two. |
|
||||||
|
> | Groups: drag-to-reorder, and the mock's "the order here is the order there" sentence | Refused. `HostGroupRowViewModel` orders by the vault new items go into, then by vault name, then by label — there is no manual order to drag into. The page prints the true sentence instead and draws no drag handles. |
|
||||||
|
> | Tags: the LAST APPLIED column | Refused — no timestamp of when a tag was last put on a host exists anywhere in this client. |
|
||||||
|
> | Tags: a per-tag vault chip | Refused — `TagRowViewModel` carries a label and a host count and nothing else; there is no vault id on the row for a chip to read. |
|
||||||
|
> | Import: the "saving to ⟨vault⟩ ▾" picker | Refused as a control. Every import writes through the same `session.ActiveVaultId` every other bulk write does, so there is no per-import target to choose — the footer prints the vault's name as a fact, `ImportViewModel.SelectionSummary`, rather than as a `▾`. |
|
||||||
|
> | Import: the old ADDRESS/AUTHENTICATION/STATE columns | Folded into the design's own column set — ALIAS/HOSTNAME/USER/PORT — with the authentication text moved onto the alias cell's own tooltip rather than kept as a column of its own. |
|
||||||
|
> | Import: TICK ALL / TICK NONE | Replaced by the design's own header tick-all button, one press that ticks or unticks every row through `ImportRowViewModel.ToggleAllCommand`. |
|
||||||
|
> | Import as a nav-rail destination | Refused. It is a boolean overlay over the Preferences settings page (`MainWindowViewModel.IsImportOpen`), not a page of its own — `SettingsNav` stays lit on Preferences throughout, exactly as `Import.dc.html` draws it. |
|
||||||
|
> | The design's fixed 1100px content column | `MaxWidth="1100"` rather than a fixed width — the column is 741 pixels beside the 340px rail at the window's own minimum, where a fixed 1100 would not fit. |
|
||||||
|
> | The status bar and the update banner, while in settings mode | Both hidden — `MainWindow.axaml`'s bottom two rows read `!IsSettingsMode`. The design's own settings titlebar has no room for either, and there is nowhere honest to draw them instead. |
|
||||||
|
> | The design's sentence-case button copy | Not adopted. Every button on every settings page keeps this application's own ALL-CAPS mono convention — `CHECK NOW`, `SIGN OUT`, `OPEN IMPORTER` — over the mock's own "Check now". |
|
||||||
|
> | The design's assumption that Settings is the only thing on screen | Not followed. The quick-connect palette and an unapproved host key's decision card both still draw over settings mode exactly as they interrupt every other screen — a connection question does not stop mattering because the window happens to be showing Settings; see `MainWindow.axaml`'s own remark on the ordering of its Panel. |
|
||||||
|
> | The importer's own sentence, "This is the only control in DodoSSH that opens key material from a directory you did not point at file by file — nothing is read until Import is pressed." | Adopted verbatim — verified against `ImportViewModel.ScanAsync` and `ImportAsync`: scanning never calls `SshConfigLocator.ReadIdentity`, and `ImportAsync` is the only path that ever does. |
|
||||||
|
> | The tags page's own sentence, "Renaming here is one write and every host follows." | Adopted verbatim — verified against `SaveTagAsync`: a host names a tag by id, never by label, so a rename touches nothing but the tag item itself. |
|
||||||
|
> | The known-hosts screen's own intro, "Every pin is a decision recorded at the moment of connecting. Fingerprints are never trimmed — compare them character by character against what the operator published." | Adopted verbatim, matching the stance the rest of this application already takes on a pin. |
|
||||||
|
|
||||||
Most of it landed. This file is the rest: every element of that design with nothing behind it, which
|
Most of it landed. This file is the rest: every element of that design with nothing behind it, which
|
||||||
project each piece would have to land in, and **what the shipped interface does instead**. That last
|
project each piece would have to land in, and **what the shipped interface does instead**. That last
|
||||||
|
|||||||
+126
-50
@@ -115,6 +115,61 @@ vault that authenticates with a key filed in the switched-off one still connects
|
|||||||
`VaultViewModel.IsVaultShown` for the list, and `VaultVisibilityTests` for the same assertions made against
|
`VaultViewModel.IsVaultShown` for the list, and `VaultVisibilityTests` for the same assertions made against
|
||||||
view models — this check is the version with a real connection on the end of it.
|
view models — this check is the version with a real connection on the end of it.
|
||||||
|
|
||||||
|
### 1.8 Entering and leaving settings mode
|
||||||
|
|
||||||
|
From HOSTS, press the user chip at the foot of the nav rail and choose **Settings**. Then repeat from a
|
||||||
|
fresh open, choosing **Vaults**, and again choosing **Preferences**.
|
||||||
|
|
||||||
|
**Pass:** each of the three swaps the whole window's chrome — the titlebar becomes the 53px "Back to
|
||||||
|
application" bar, the ordinary nav rail is replaced by the 340px SettingsNav, and the status bar and the
|
||||||
|
update banner (if either was showing) are both gone. **Settings** lands on General; **Vaults** and
|
||||||
|
**Preferences** land on their own pages, each lit in SettingsNav's own list — General/Vaults/Account under
|
||||||
|
SETTINGS, Security/Preferences/Groups/Tags under CUSTOMIZE, Logout pinned below both. Click every one of the
|
||||||
|
seven rows in turn: each swaps the content column without leaving settings mode or touching the titlebar.
|
||||||
|
|
||||||
|
Now open a terminal, leave it showing, and open Settings from the user chip again. Press **Back to
|
||||||
|
application** — or, without touching anything else, press Esc.
|
||||||
|
|
||||||
|
**Pass:** both return to exactly the screen that was showing before Settings was opened — the terminal,
|
||||||
|
still running, rather than HOSTS or anywhere else. Switching between settings pages first (General → Vaults
|
||||||
|
→ Preferences) does not change what "back" goes back to; only the screen open the moment Settings was
|
||||||
|
**first** entered does.
|
||||||
|
|
||||||
|
**Failure means:** a return to HOSTS regardless of where Settings was opened from is
|
||||||
|
`MainWindowViewModel.settingsReturnScreen` being recaptured on every `EnterSettings` call rather than only on
|
||||||
|
the way in from outside settings mode — see that field's own remark. A status bar or update banner still
|
||||||
|
visible in settings mode is `MainWindow.axaml`'s two bottom rows no longer reading `!IsSettingsMode`. Esc
|
||||||
|
doing nothing is `MainWindow.axaml.cs`'s `OnKeyDown` no longer checking `IsSettingsMode` after the palette's
|
||||||
|
own branch.
|
||||||
|
|
||||||
|
### 1.9 The importer's own home, and what still reaches through settings mode
|
||||||
|
|
||||||
|
From Settings → Preferences, press **OPEN IMPORTER**.
|
||||||
|
|
||||||
|
**Pass:** the titlebar's back button relabels itself "Back to preferences" — not "Back to application" — and
|
||||||
|
SettingsNav stays lit on **Preferences** the whole time; the importer is drawn over the Preferences page
|
||||||
|
rather than being a destination of its own. Press SCAN AGAIN and let it read your `~/.ssh/config`.
|
||||||
|
|
||||||
|
Press **Back to preferences** (or Esc). **Pass:** the Preferences page is back, still inside settings mode,
|
||||||
|
and the titlebar's label reverts. Press Esc a second time (or **Back to application**): now settings mode
|
||||||
|
itself closes, back to whatever screen was open before Settings was entered — the same two-deep
|
||||||
|
"closest thing first" order the titlebar's own two buttons and `MainWindowViewModel.OpenImport`/`CloseImport`
|
||||||
|
follow.
|
||||||
|
|
||||||
|
**Then, with the importer open, press Ctrl+K** and connect to a host from the palette.
|
||||||
|
|
||||||
|
**Pass:** it works exactly as it does anywhere else in the application — the palette opens over the
|
||||||
|
importer, and connecting leaves settings mode outright for the new terminal, the same way choosing any
|
||||||
|
ordinary nav rail destination does through `MainWindowViewModel.ShowScreen`. An unapproved host's key card
|
||||||
|
comes up over settings mode the same way it would over any other screen.
|
||||||
|
|
||||||
|
**Failure means:** a back button that never changes label is `SettingsTitleBar.axaml`'s two buttons both
|
||||||
|
bound to the same side of `IsImportOpen`. SettingsNav lighting a different row while the importer is up is
|
||||||
|
`MainWindowViewModel.IsSettingsPreferencesPage` reading `IsImportOpen` when it must not —
|
||||||
|
`Import.dc.html` draws the rail unmoved on purpose; see the remark on `IsImportOpen`. Ctrl+K doing nothing
|
||||||
|
over the importer would be a guard added to `OnKeyDown` for `IsSettingsMode` that the design never asked for
|
||||||
|
and this application's own quick-connect card was built to reach past.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 2 — Known Hosts as its own page
|
## Phase 2 — Known Hosts as its own page
|
||||||
@@ -200,7 +255,7 @@ The parser has 22 cases over the shapes a real file contains, and the end-to-end
|
|||||||
|
|
||||||
### 2.7 Scan your own `~/.ssh/config` and read the preview against the file
|
### 2.7 Scan your own `~/.ssh/config` and read the preview against the file
|
||||||
|
|
||||||
Preferences → IMPORT HOSTS → SCAN. Do not press import yet.
|
Settings → Preferences → OPEN IMPORTER, then SCAN AGAIN. Do not press Import N hosts yet.
|
||||||
|
|
||||||
**Pass:** every entry you would expect is listed, with the address and port you expect, and the warnings
|
**Pass:** every entry you would expect is listed, with the address and port you expect, and the warnings
|
||||||
above the table account for anything missing.
|
above the table account for anything missing.
|
||||||
@@ -364,61 +419,81 @@ card, nothing saying the hosts are unfiled.
|
|||||||
**Failure means:** the "invisible until used" property is gone, and every existing user gets a section they
|
**Failure means:** the "invisible until used" property is gone, and every existing user gets a section they
|
||||||
did not ask for. `HasGroups` is what hides the row of group cards; `HostRowViewModel.HasGroup` hides the chip.
|
did not ask for. `HasGroups` is what hides the row of group cards; `HostRowViewModel.HasGroup` hides the chip.
|
||||||
|
|
||||||
### 3.2 Filing hosts, and the grid being one level at a time
|
### 3.2 Filing hosts, and the board drawing every group at once
|
||||||
|
|
||||||
Make two groups and file some hosts into each through the host editor, leaving at least one host unfiled.
|
Make two groups — the Group ▾ flyout's "New group…" — and file some hosts into each, once through a host's
|
||||||
|
own editor and its GROUP picker, and once by ticking several hosts and choosing **Change group…** from the
|
||||||
|
right-click menu. Leave at least one host unfiled.
|
||||||
|
|
||||||
**Pass:** the filed hosts **leave the grid** as they are filed — a group is a place, not a label, and the
|
**Pass:** every group is a heading on the board **at once** — in label order, "No group" first where it is
|
||||||
grid holds one level of it the way a directory pane holds one directory. What is left is the unfiled hosts.
|
occupied — with its own hosts underneath. There is no double-click, no card to open a group into and no
|
||||||
**There is no heading and no fold on the desktop** — the headings, their chevrons and UNGROUPED are the
|
breadcrumb trail: v3's one-level-at-a-time grid is gone outright, and every section is on screen from the
|
||||||
phone's, whose list has no room for a row of group cards and draws the whole tree flat instead. With every
|
moment the board is. A host filed under `production` is drawn once, under that heading, and nowhere else on
|
||||||
host filed, the grid says so in a sentence rather than sitting empty.
|
the board.
|
||||||
|
|
||||||
**Then press a group card once.** It is marked as chosen and **nothing else happens** — the grid is still the
|
**Then press the chevron beside one heading.** It folds — the heading stays, with its live count, and its
|
||||||
level it was, and no buttons appear beside the GROUPS heading: editing and deleting a group are on the card's
|
cards go — and pressing it again brings the cards back. **Then press "Collapse all"**, drawn on the first
|
||||||
own right-click menu, which is 7.9. **Then double-press it.** The group opens: its hosts
|
heading only. It folds every section at once and its own label swaps to "Expand all"; opening any one heading
|
||||||
are the grid, the trail above the cards reads `ALL HOSTS › <name> ›`, each card carrying the group's name as
|
by hand swaps the label back to "Collapse all".
|
||||||
an accent chip, and the card grid shows what is *inside* that group rather than every group in the keychain.
|
|
||||||
Pressing ALL HOSTS goes back to the outermost level.
|
|
||||||
|
|
||||||
**Then type a filed host's name into the find box at the top.** It is found from the outermost level,
|
**Then type a filed host's name into the find box at the top, with its heading folded.** **Pass:** the
|
||||||
wherever it was filed and however deep, with the chip on its card saying which group it came out of. Clearing
|
heading's own count narrows to match — the count is read off the set the find box has already narrowed,
|
||||||
the box puts it away again. Inside a group the same box reaches that group and everything under it, and the
|
before the fold is looked at — but the cards themselves stay hidden until that heading is reopened; open it
|
||||||
empty-grid sentence there offers ALL HOSTS as the way to widen it.
|
and the narrowed set is there. Clear the box and every count returns.
|
||||||
|
|
||||||
**Failure means:** if one press still narrows the grid, the card `ListBox` is bound to the wrong property —
|
**Failure means:** a host drawn under a heading that is not its own, or under two at once, is
|
||||||
`SelectedItem` is `SelectedGroup`, and only `OpenGroupCommand` writes `GroupFilter`. Filed hosts still on the
|
`VaultViewModel.AddFlatSection`'s membership test reading the wrong field for that section —
|
||||||
outermost level is `Matches` no longer comparing the host's group to the open one. A search that cannot find
|
`host.Host.GroupId == group.EntityId` for a named heading, everything else for "No group". A chevron that
|
||||||
a filed host is worse than either: it is the box answering "no host matches that" about a machine the
|
folds every section rather than the one it sits beside is `ToggleGroupCommand` being handed the board rather
|
||||||
keychain has got. A full-width bar with a chevron between the cards is the old grouping coming back through
|
than the single `SidebarGroupHeader` it was pressed on. A count that does not move with the find box is
|
||||||
`SidebarRows`; the desktop grid binds `VisibleHosts`. See `HostsScreen.axaml`.
|
`MatchesHostBoardFilters` not running before `HostSections` is rebuilt — see
|
||||||
|
`VaultViewModel.RebuildHostSections` and `OnHostFilterChanged`.
|
||||||
|
|
||||||
### 3.2a A group inside a group, and the way back out
|
### 3.2a A group nested under another, and what a host filed under it inherits
|
||||||
|
|
||||||
Make two groups and file one under the other with the parent picker in the group editor.
|
Make an outer group with a default port and no default username, then make a second group whose editor's
|
||||||
|
PARENT picker points at the outer one, and give this inner group no default port of its own. File a host
|
||||||
|
under the inner group with nothing set on the host itself.
|
||||||
|
|
||||||
**Pass:** only the outer group has a card to start with. Double-press it and the inner one is the only card
|
**Pass:** both groups are their own headings on the board, side by side — nesting is never drawn on the board
|
||||||
shown, with the trail reading `ALL HOSTS › <outer> ›`. Double-press that, and the cards disappear entirely —
|
itself, only carried in the group editor's own PARENT field. The inner heading's count is only the hosts
|
||||||
it has nothing inside it — while the trail stays. Pressing the **middle** crumb goes back one level rather
|
filed directly under it, and the outer's is only the hosts filed directly under the outer one; the host filed
|
||||||
than all the way out, which is also how a group with nothing inside it is renamed: back out to the level
|
under the inner group is not counted on the outer heading. Connect the host anyway: it dials the **outer**
|
||||||
where it has a card, and right-click that.
|
group's port. `HostInheritance.Chain` walks past the inner group's own silence on that field rather than
|
||||||
|
stopping at the nearest group regardless of whether it answered. Give the inner group its own default port
|
||||||
|
and reconnect: the host now dials that one instead, because the nearer group's answer wins once there is one.
|
||||||
|
|
||||||
**Failure means:** cards for groups that are not at this level is `VisibleGroups` having been bound past —
|
**Then open the inner group's own editor and look at its PARENT picker.** The outer group is offered. **Then
|
||||||
the flat `Groups` is the phone's and the lookups'. A group that cannot be reached at all is worse and is the
|
open the outer group's own editor and look at its PARENT picker.** The inner group is **not** offered — a
|
||||||
case `EffectiveParents` promotes: see 3.4a.
|
group found by walking down from the outer one is refused as its own parent, which is what keeps this one
|
||||||
|
machine, acting alone, from building a cycle. (Two machines can still build one offline between them; that is
|
||||||
|
3.4a, below.)
|
||||||
|
|
||||||
### 3.2b Making something while standing inside a group
|
**Failure means:** a host that dials 22 with the outer group's port set is the chain stopping at the first
|
||||||
|
group above the host rather than reading each field independently from the nearest group that states it — see
|
||||||
|
`HostInheritance.Resolve`'s own remarks. A parent picker offering a group's own descendant is
|
||||||
|
`VaultViewModel.BuildGroupParentChoices` walking the wrong direction: each candidate has to be walked
|
||||||
|
*upward*, through `HostInheritance.Chain`, to see whether it passes through the group being edited — a
|
||||||
|
downward index of children is what the alternative would need, and this view model keeps none.
|
||||||
|
|
||||||
Open a group, then press **+ NEW HOST**, and afterwards **+ NEW GROUP**.
|
### 3.2b Making a host or a group inherits nothing from where the board is scrolled
|
||||||
|
|
||||||
**Pass:** the host editor opens with that group already chosen in its group picker, and the saved host is on
|
Scroll the board so one group's section fills the window, or fold every other section away, then press
|
||||||
the screen it was made on rather than somewhere the trail is not. The group editor likewise opens with that
|
**+ New host**. Afterwards, open the Group ▾ flyout and press **New group…**.
|
||||||
group as its parent, so the new group is a card inside the one that is open.
|
|
||||||
|
|
||||||
**Failure means:** anything created inside a group disappearing the moment it is saved. That is the papercut
|
**Pass:** the host editor opens with its GROUP picker on **"No group"**, and the group editor opens with its
|
||||||
a level-at-a-time grid comes with, and `NewHost` / `NewGroup` are where it is answered. Note the deliberate
|
PARENT on **"No group"** too — neither reads anything from which heading happens to be on screen or scrolled
|
||||||
difference between them: the host editor also takes a merely *selected* card as its group, the group editor
|
to. This is deliberate: the level-at-a-time grid these two commands used to inherit an "open group" from is
|
||||||
takes only the group that is open.
|
gone, and nothing on the flat board replaced that context. Pick a group by hand in either picker and it
|
||||||
|
stays picked — the host or the new group lands there once saved.
|
||||||
|
|
||||||
|
**Failure means:** an editor that opens already filed under whichever section happened to be on screen is
|
||||||
|
`VaultViewModel.GroupTarget` — the fallback both `NewHost` and `NewGroup` still read — having been wired to a
|
||||||
|
live selection again. Read that property's own remarks before treating this as a regression: it is an alias
|
||||||
|
for the single-select `GroupFilter`, which nothing in the current toolbar ever assigns any more — the Group ▾
|
||||||
|
flyout ticks a set, `checkedGroupFilterIds`, a different field entirely — so today the fallback is dead code
|
||||||
|
rather than a path either `+` button takes. Restoring it as live board context would make both commands
|
||||||
|
context-sensitive in a way nothing on this board signals before the fact.
|
||||||
|
|
||||||
### 3.3 Deleting a group with hosts in it
|
### 3.3 Deleting a group with hosts in it
|
||||||
|
|
||||||
@@ -1618,7 +1693,7 @@ newcomer **must not have signed in to this deployment before** — 12.1 is about
|
|||||||
|
|
||||||
### 12.1 An address with no account is refused, and joins nothing when it later signs in · **the one worth the most care**
|
### 12.1 An address with no account is refused, and joins nothing when it later signs in · **the one worth the most care**
|
||||||
|
|
||||||
1. Sign in as `alice`, make a vault on the VAULTS screen, and select it.
|
1. Sign in as `alice`, go to Settings → Vaults, make a vault there, and select it.
|
||||||
2. Add `bob@example.com` as a Member, with Bob having never signed in here.
|
2. Add `bob@example.com` as a Member, with Bob having never signed in here.
|
||||||
3. **Pass:** it is refused. The status line names the address and says to ask them to sign in to this
|
3. **Pass:** it is refused. The status line names the address and says to ask them to sign in to this
|
||||||
server once and then add them. **Nothing on the screen should suggest anything is pending** — no
|
server once and then add them. **Nothing on the screen should suggest anything is pending** — no
|
||||||
@@ -1716,7 +1791,7 @@ vault is now flagged for rekey, the transfer is removing the outgoing owner rath
|
|||||||
|
|
||||||
Look for a way to remove a vault, on both heads.
|
Look for a way to remove a vault, on both heads.
|
||||||
|
|
||||||
**Pass:** there is none, and the VAULTS screen says why in a sentence: nothing in this product removes a
|
**Pass:** there is none, and Settings → Vaults says why in a sentence: nothing in this product removes a
|
||||||
vault, and the server refuses to archive the membership list behind one while it exists. Archiving that
|
vault, and the server refuses to archive the membership list behind one while it exists. Archiving that
|
||||||
list is still reachable over the API, and the endpoint suite drives both its refusal and its success — what
|
list is still reachable over the API, and the endpoint suite drives both its refusal and its success — what
|
||||||
is being checked here is that no button offers it.
|
is being checked here is that no button offers it.
|
||||||
@@ -1727,7 +1802,7 @@ button that always refuses is the milder failure and is still worth removing.
|
|||||||
|
|
||||||
### 12.8 Renaming a vault reaches every place its name is drawn · **needs two accounts**
|
### 12.8 Renaming a vault reaches every place its name is drawn · **needs two accounts**
|
||||||
|
|
||||||
Rename a shared vault from the VAULTS screen.
|
Rename a shared vault from Settings → Vaults.
|
||||||
|
|
||||||
**Pass:** the new name is on the vault list, on the badge of every host card in that vault, in the keychain
|
**Pass:** the new name is on the vault list, on the badge of every host card in that vault, in the keychain
|
||||||
screen's "new items file to" picker, in the host editor's vault picker, and in the nav rail's user-chip
|
screen's "new items file to" picker, in the host editor's vault picker, and in the nav rail's user-chip
|
||||||
@@ -2149,10 +2224,11 @@ fifteen seconds comes from.
|
|||||||
|
|
||||||
### 16.5 The version on screen is the version that was built
|
### 16.5 The version on screen is the version that was built
|
||||||
|
|
||||||
Right-click `DodoSSH.exe` → Properties → Details, and open PREFERENCES → UPDATES.
|
Right-click `DodoSSH.exe` → Properties → Details, and open Settings → General.
|
||||||
|
|
||||||
**Pass:** File version reads the tag (`0.1.0.0`), product **DodoSSH**, company **DodoTech**, and the
|
**Pass:** File version reads the tag (`0.1.0.0`), product **DodoSSH**, company **DodoTech**, and the
|
||||||
preferences screen prints the same number.
|
General page's UPDATES card prints the same number — the block moved there from Preferences in v5c, and
|
||||||
|
Preferences itself carries no version line any more.
|
||||||
|
|
||||||
**Failure means:** `0.0.0.0` is MinVer never seeing a tag — a shallow clone, or `fetch-depth` having been
|
**Failure means:** `0.0.0.0` is MinVer never seeing a tag — a shallow clone, or `fetch-depth` having been
|
||||||
dropped from a checkout. `1.0.0.0` is somebody having wired the app manifest's inert `assemblyIdentity`
|
dropped from a checkout. `1.0.0.0` is somebody having wired the app manifest's inert `assemblyIdentity`
|
||||||
@@ -2174,7 +2250,7 @@ script warns rather than failing when that is legitimate, which is the first rel
|
|||||||
### 16.7 The update arrives, and the restart lands in it · **the whole point of the work**
|
### 16.7 The update arrives, and the restart lands in it · **the whole point of the work**
|
||||||
|
|
||||||
With v0.1.0 installed and running, a vault unlocked, a host change made, and **a terminal open**, publish
|
With v0.1.0 installed and running, a vault unlocked, a host change made, and **a terminal open**, publish
|
||||||
v0.1.1 (`-Upload`). Then press CHECK NOW on PREFERENCES rather than waiting six hours.
|
v0.1.1 (`-Upload`). Then press CHECK NOW on Settings → General rather than waiting six hours.
|
||||||
|
|
||||||
**Pass:** the progress bar moves, the banner appears above the status bar, and — the part to actually watch
|
**Pass:** the progress bar moves, the banner appears above the status bar, and — the part to actually watch
|
||||||
— the terminal **reflows cleanly rather than being sliced**, with the remote seeing the smaller row count.
|
— the terminal **reflows cleanly rather than being sliced**, with the remote seeing the smaller row count.
|
||||||
@@ -2228,7 +2304,7 @@ there is one install directory, the pack ids collide and the nightly has replace
|
|||||||
outright — which is the thing ADR 0013 decision 9 is constructed to make impossible, so it means one of the
|
outright — which is the thing ADR 0013 decision 9 is constructed to make impossible, so it means one of the
|
||||||
four separations has been undone.
|
four separations has been undone.
|
||||||
|
|
||||||
**And the direction that matters most:** on the release build, PREFERENCES → UPDATES → CHECK NOW must not
|
**And the direction that matters most:** on the release build, Settings → General → CHECK NOW must not
|
||||||
offer a nightly, ever, however many have been published since. It reads a different index and refuses
|
offer a nightly, ever, however many have been published since. It reads a different index and refuses
|
||||||
prereleases; if a nightly version is ever offered there, stop and treat it as a release-channel incident
|
prereleases; if a nightly version is ever offered there, stop and treat it as a release-channel incident
|
||||||
rather than as a bug in the nightly.
|
rather than as a bug in the nightly.
|
||||||
|
|||||||
Reference in New Issue
Block a user