Public Access
Give the phone the rest of its screens, and a way in
All seven screens of the design, plus the two it does not draw because it starts at an enrolled phone: naming a server, and choosing a passphrase. The five states docs/android-port.md worried about losing at 360dp are all here and none of them softened. The changed-key refusal is a full-screen panel rather than a bottom sheet, because a sheet is swipe-to-dismiss by convention and that screen must have no way forward. The recovery code raises FLAG_SECURE for its own state and lowers it afterwards, so the sentence about screenshots is true rather than decorative. The delete confirmations keep their counts and replace the row in place. Signing in works, and the seam it needed is worth more than the implementation: IAuthorizationCallback now sits between OidcClient and the loopback listener, so the two heads differ in where the response arrives and in nothing else. PKCE, the state check, discovery, the token exchange and the key binding stay one implementation — a second OIDC client would be a second place for a security bug to live. The phone registers a private-use scheme with the system rather than binding a loopback port, which on a shared device any other app can do first. The accessory key row needed TerminalWorkspace.SendInputAsync: ordinary typing goes from the renderer straight down the socket, and there was no way in for the keys a software keyboard does not have. Ctrl latches, because one thumb cannot chord, and the latch is drawn — a modifier that is on and does not look on is how somebody sends ^L to a database prompt believing they typed an l. 597 client tests green, including two new ones for the input path and one for the terminal surface command. Nothing has run on a device.
This commit is contained in:
@@ -411,6 +411,24 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
/// <summary>Where the embedded browser should navigate.</summary>
|
||||
internal Uri TerminalPageUrl => workspace.PageUrl;
|
||||
|
||||
/// <summary>
|
||||
/// Types into a terminal on behalf of something that is not the keyboard.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Exposed on the shell rather than reached through the workspace directly, because the workspace is a
|
||||
/// composition-root object and a view has no business holding one — the same reason tabs go through
|
||||
/// here rather than through <c>TerminalWorkspace.CloseSessionAsync</c>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The Android head's accessory key row is what needs it: a software keyboard has no Ctrl, Esc, Tab or
|
||||
/// arrows, so those keys are drawn and their bytes sent from here. Ordinary typing never comes this
|
||||
/// way — it goes from the renderer straight down the socket.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
internal ValueTask SendTerminalInputAsync(uint sessionId, ReadOnlyMemory<byte> data) =>
|
||||
workspace.SendInputAsync(sessionId, data, CancellationToken.None);
|
||||
|
||||
/// <summary>
|
||||
/// Raised when a terminal session opens, so the view can hand the terminal the keyboard.
|
||||
/// </summary>
|
||||
@@ -620,6 +638,24 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
Surface = ShellSurface.Page;
|
||||
}
|
||||
|
||||
/// <summary>Switches to the terminal surface.</summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The other half of <see cref="ShowScreenCommand"/>, and it exists because the phone's bottom bar
|
||||
/// names the terminal beside the pages. The desktop reaches this surface only implicitly — opening a
|
||||
/// session or clicking a tab — because its tab strip is always on screen and is itself the way back.
|
||||
/// A phone has no room for a permanent strip beside a full-height screen, so the destination needs a
|
||||
/// button, and a button needs a command.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Not gated on there being a tab, for the same reason <see cref="IsTerminalShowing"/> is not: closing
|
||||
/// the last tab returns the surface to a page, so the empty case does not arise here — and the terminal
|
||||
/// screen carries an empty state anyway, which is worth being able to reach deliberately.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[RelayCommand]
|
||||
private void ShowTerminal() => Surface = ShellSurface.Terminal;
|
||||
|
||||
// ---- Open terminals ----
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user