Public Access
Merge branch 'claude/sleepy-chebyshev-cda68d'
This commit is contained in:
@@ -142,6 +142,17 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
/// <summary>Where the embedded browser should navigate.</summary>
|
||||
internal Uri TerminalPageUrl => workspace.PageUrl;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when a terminal session opens, so the view can hand the terminal the keyboard.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Forwarded from <see cref="VaultViewModel.SessionOpened"/> rather than exposed there directly,
|
||||
/// because <see cref="Vault"/> is replaced on every unlock and the view would have to re-subscribe
|
||||
/// each time. This shell is the window's data context for the life of the process, so one
|
||||
/// subscription is enough.
|
||||
/// </remarks>
|
||||
internal event EventHandler? TerminalSessionOpened;
|
||||
|
||||
internal bool IsStarting => State == ShellState.Starting;
|
||||
|
||||
internal bool IsNeedingServer => State == ShellState.NeedsServer;
|
||||
@@ -469,6 +480,26 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
return exception.Message;
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// One place for the subscription, so unlocking, locking and disposing all route through it rather
|
||||
/// than each remembering to detach.
|
||||
/// </remarks>
|
||||
partial void OnVaultChanged(VaultViewModel? oldValue, VaultViewModel? newValue)
|
||||
{
|
||||
if (oldValue is not null)
|
||||
{
|
||||
oldValue.SessionOpened -= OnVaultSessionOpened;
|
||||
}
|
||||
|
||||
if (newValue is not null)
|
||||
{
|
||||
newValue.SessionOpened += OnVaultSessionOpened;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnVaultSessionOpened(object? sender, EventArgs e) =>
|
||||
TerminalSessionOpened?.Invoke(this, e);
|
||||
|
||||
partial void OnStateChanged(ShellState value)
|
||||
{
|
||||
OnPropertyChanged(nameof(IsStarting));
|
||||
|
||||
@@ -179,6 +179,18 @@ internal sealed partial class VaultViewModel(
|
||||
[ObservableProperty]
|
||||
private string? hostKeyMismatch;
|
||||
|
||||
/// <summary>
|
||||
/// Raised once a terminal session is open and its renderer has it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// An event rather than a property because handing the terminal the keyboard is something that
|
||||
/// happens, not something that is true: connecting a second host while one is already open has to
|
||||
/// move focus again, and no state change describes that. Raised on the UI thread — every await on
|
||||
/// the path from the command to here uses <c>ConfigureAwait(true)</c> — so a handler may touch
|
||||
/// controls directly.
|
||||
/// </remarks>
|
||||
internal event EventHandler? SessionOpened;
|
||||
|
||||
internal bool HasPendingHostKey => PendingHostKey is not null;
|
||||
|
||||
internal bool HasHostKeyMismatch => HostKeyMismatch is not null;
|
||||
@@ -611,6 +623,12 @@ internal sealed partial class VaultViewModel(
|
||||
.ConfigureAwait(true);
|
||||
|
||||
Status = $"Connected to {row.Label}.";
|
||||
|
||||
// Only now, and only on success. The page's own term.focus() focuses the textarea inside
|
||||
// the document, which does nothing while the window's keyboard focus is still on the
|
||||
// Connect button — so without this the first keystrokes of the session go to the shell's
|
||||
// UI instead of the remote shell.
|
||||
SessionOpened?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
catch (SshHostKeyUnknownException exception)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user