Public Access
Merge main into the teams branch
Two conflicts, and both were two people counting the same things differently rather than disagreeing about what the code should do. PhoneShell's header comment. The branch made "the five hub screens" numberless, because TEAMS made it six and a number in that sentence had already gone stale once. Main corrected "three destinations" to "two" in the same sentence, because giving a shell the whole phone took the terminal out of the set the header is drawn on. Both are right and neither noticed the other: the header now stays on the hub's screens and on the two top-level destinations, which is Hosts and Keychain. The manual checks. Both sides appended a Phase 10 — main added the software keyboard and the phone's terminal surface as 10 and 11, the branch added Teams. Nothing about them overlaps, so the resolution is to keep all three in the order they were written and renumber Teams to Phase 12, its subsections and the one cross-reference inside 12.1 with it. Main's two phases keep the numbers they already carry in its history, since renumbering those would move headings somebody may already have linked to. Everything else merged without a conflict, and the two places worth checking afterwards both held: IsMoreSurface and the first case of PhoneShell.OnBackRequested each kept ShellScreen.Team alongside main's edits. Those two are one fact in two places, so a merge that dropped Team from either would have trapped the user on the teams screen with the MORE tab dark. Verified after resolving: solution builds with no errors and no new warnings, the Android head builds, and every suite passes — App 214, Layout 73, Api 162, Infrastructure 34, Contracts 25, Session 54. App gained the three shell-flow tests main brought with it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -418,25 +418,21 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Two defaults, because the two audiences never overlap. A release build is installed by somebody
|
||||
/// signing in to the hosted deployment, and typing its address is the only thing standing between
|
||||
/// them and a working application. A debug build is run from a clone, next to
|
||||
/// <c>dotnet run --project src/DodoSSH.Api</c>, and shipping the hosted address there would point
|
||||
/// every development launch at production — which is worse than an inconvenience, since sign-in is
|
||||
/// the step that enrolls a device.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Note the schemes. <c>http</c> locally is not an oversight: the API's first launch profile — the
|
||||
/// one a plain <c>dotnet run</c> and the README both select — is plaintext on 5233, and pointing an
|
||||
/// HTTPS client at a plaintext port fails as "The SSL connection could not be established", which
|
||||
/// One default for every build. The hosted deployment is what all but a handful of launches are
|
||||
/// aiming at, and typing its address is the only thing standing between an installed application and
|
||||
/// a working one. Running against a clone means replacing this with
|
||||
/// <c>http://localhost:5233</c> by hand — note the scheme, because the API's first launch profile —
|
||||
/// the one a plain <c>dotnet run</c> and the README both select — is plaintext on 5233, and pointing
|
||||
/// an HTTPS client at a plaintext port fails as "The SSL connection could not be established", which
|
||||
/// sends people looking for a certificate problem. See <see cref="ExplainSignInFailure" />.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This was once split on <c>DEBUG</c> so a development launch could not enroll a device against
|
||||
/// production by accident. That protection is gone: a debug build now offers the hosted address like
|
||||
/// any other, and the first sign-in accepted unread lands there.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
#if DEBUG
|
||||
internal const string DefaultServerUrl = "http://localhost:5233";
|
||||
#else
|
||||
internal const string DefaultServerUrl = "https://ssh.dodotech.cloud";
|
||||
#endif
|
||||
|
||||
[ObservableProperty]
|
||||
private string serverUrl = DefaultServerUrl;
|
||||
@@ -803,8 +799,15 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
/// and a child window composites above everything its parent paints — so whatever Avalonia draws in the
|
||||
/// same rectangle is drawn underneath it and its buttons cannot be clicked. Anything that covers the
|
||||
/// terminal's area has to collapse the terminal instead, and that is every one of the conditions here: a
|
||||
/// locked vault (the unlock card), the page area (every screen uses the full width), and the
|
||||
/// quick-connect palette.
|
||||
/// locked vault (the unlock card), the page area (every screen uses the full width), the quick-connect
|
||||
/// palette, and the phone's connect sheet.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b>The sheet is here rather than in <see cref="IsTerminalSurface"/>, and the palette is not.</b> The
|
||||
/// palette replaces the whole surface, so collapsing everything the terminal half draws is right. The
|
||||
/// sheet is raised from the terminal's own top bar and that bar has to stay on screen behind it —
|
||||
/// dropping the surface would take the bar, the tabs and the phone's whole chrome with it and leave the
|
||||
/// sheet floating over the page underneath. So only the renderer's rectangle is given up.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b>The terminal and the pages are exclusive, and that is the whole of the rule.</b> They share one
|
||||
@@ -833,7 +836,8 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
/// safe — that detaches it and destroys the whole WebView2 process tree.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
internal bool IsTerminalShowing => IsTerminalSurface && SelectedTab is { HasSession: true };
|
||||
internal bool IsTerminalShowing =>
|
||||
IsTerminalSurface && !IsConnectSheetOpen && SelectedTab is { HasSession: true };
|
||||
|
||||
/// <summary>
|
||||
/// Whether the terminal half of the window is the half being shown, pane or no pane.
|
||||
@@ -897,6 +901,52 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
[RelayCommand]
|
||||
private void ShowTerminal() => Surface = ShellSurface.Terminal;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the phone's connect menu is open over the terminal.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Drawn by the Android head alone, and shell state rather than something that view could hold on its
|
||||
/// own for the reason <see cref="IsSearching"/> is: it has to collapse the renderer while it is up. See
|
||||
/// <see cref="IsTerminalShowing"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// It exists because the phone gives a terminal the whole screen. The bottom bar and the vault header
|
||||
/// are gone while a shell is showing, so the three things that bar was the way to — a host, a host's
|
||||
/// files, a bucket — need a way back that is not "leave the terminal first and remember what you were
|
||||
/// doing". The menu is that, and every entry on it is one of the two navigation commands above.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[ObservableProperty]
|
||||
private bool isConnectSheetOpen;
|
||||
|
||||
/// <summary>Raises the connect menu over the terminal.</summary>
|
||||
/// <remarks>
|
||||
/// Gated on the terminal surface rather than merely trusting its only button to be off screen otherwise.
|
||||
/// The flag collapses the renderer, so one set while a page was showing would be a sheet nobody can see
|
||||
/// holding a terminal hidden that nothing would put back.
|
||||
/// </remarks>
|
||||
[RelayCommand]
|
||||
private void OpenConnectSheet()
|
||||
{
|
||||
if (!IsTerminalSurface)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IsConnectSheetOpen = true;
|
||||
}
|
||||
|
||||
/// <summary>Lowers the connect menu, leaving the terminal where it was.</summary>
|
||||
/// <remarks>
|
||||
/// The scrim, the CANCEL row and the system back gesture all come here. Choosing an entry does not, and
|
||||
/// does not need to: every entry navigates, and leaving the terminal surface lowers the sheet on its own
|
||||
/// — see <see cref="OnSurfaceChanged"/>, which is what makes "the sheet is only ever up over a terminal"
|
||||
/// true of routes nobody wrote it for.
|
||||
/// </remarks>
|
||||
[RelayCommand]
|
||||
private void CloseConnectSheet() => IsConnectSheetOpen = false;
|
||||
|
||||
// ---- Open terminals ----
|
||||
|
||||
/// <summary>
|
||||
@@ -2541,7 +2591,22 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="OnScreenChanged" />
|
||||
partial void OnSurfaceChanged(ShellSurface value) => RaiseSurfaceState();
|
||||
/// <remarks>
|
||||
/// <b>The one place the connect sheet is lowered by something other than a tap.</b> Every way out of a
|
||||
/// terminal ends here — a rail or bottom-bar destination, the files screen, the palette connecting to a
|
||||
/// host, closing the last tab, a lock — and each of them would otherwise leave the flag set on a shell
|
||||
/// showing a page. That is not merely untidy: the flag collapses the renderer, so the next return to the
|
||||
/// terminal would draw the sheet again over a rectangle held blank by it.
|
||||
/// </remarks>
|
||||
partial void OnSurfaceChanged(ShellSurface value)
|
||||
{
|
||||
if (value is not ShellSurface.Terminal)
|
||||
{
|
||||
IsConnectSheetOpen = false;
|
||||
}
|
||||
|
||||
RaiseSurfaceState();
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Both changes raise the same set, and they have to: <see cref="IsHostsShowing"/> and its four siblings
|
||||
@@ -2604,6 +2669,9 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
|
||||
partial void OnIsSearchingChanged(bool value) => RaiseTerminalState();
|
||||
|
||||
/// <inheritdoc cref="OnIsSearchingChanged" />
|
||||
partial void OnIsConnectSheetOpenChanged(bool value) => RaiseTerminalState();
|
||||
|
||||
/// <remarks>
|
||||
/// The unlock card and the confirmation swap, so arming one has to hide the other — see
|
||||
/// <see cref="IsAskingForThePassphrase"/>.
|
||||
|
||||
Reference in New Issue
Block a user