Public Access
Draw what authenticated, and over what, on the session status bar
The v5b design's own row: the negotiated cipher, then the host key's algorithm and the name of the key or credential that authenticated, as one mono run beside CONNECTED — on both surfaces, off MainWindowViewModel's surface-aware SessionCipher and SessionIdentityText, the same shape SessionAddress set. The identity's name comes out of TryBuildAuthentication, the one resolution point that always had it in scope and always threw it away; it rides HostAuthentication to the tab and to the SFTP connect alike. Three deviations, recorded in the gaps doc: the algorithm prints as negotiated rather than shortened, the run is plain text because no pin-details modal exists for an open session, and a typed password shows the algorithm alone — there is no item behind the dot. A dead terminal tab keeps its facts for the scrollback still on screen; an SFTP disconnect, with no scrollback, clears them.
This commit is contained in:
@@ -3400,7 +3400,13 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
// No tab was opened for this attempt, which means the user closed the connecting tab while the
|
||||
// handshake was still running. The session is real and has to be adopted rather than dropped:
|
||||
// dropping it would leave a shell running with nothing in the window naming it.
|
||||
var adopted = new TerminalTabViewModel(e.SessionId, e.Label, e.Address) { StartedAt = clock.GetUtcNow() };
|
||||
var adopted = new TerminalTabViewModel(e.SessionId, e.Label, e.Address)
|
||||
{
|
||||
StartedAt = clock.GetUtcNow(),
|
||||
Cipher = NullIfEmpty(e.Cipher),
|
||||
HostKeyAlgorithm = NullIfEmpty(e.HostKeyAlgorithm),
|
||||
IdentityLabel = e.IdentityLabel,
|
||||
};
|
||||
AdoptTab(adopted);
|
||||
RefreshConnectedHosts();
|
||||
return;
|
||||
@@ -3411,6 +3417,9 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
// From this moment, not from when the tab first appeared — connecting is not open, and the session
|
||||
// shell's elapsed timer is about a shell that is actually running.
|
||||
tab.StartedAt = clock.GetUtcNow();
|
||||
tab.Cipher = NullIfEmpty(e.Cipher);
|
||||
tab.HostKeyAlgorithm = NullIfEmpty(e.HostKeyAlgorithm);
|
||||
tab.IdentityLabel = e.IdentityLabel;
|
||||
|
||||
// The pane exists from this moment, so what the rectangle should hold has changed — the card goes and
|
||||
// the WebView comes back. Only for the tab being looked at, which is what these flags already ask.
|
||||
@@ -3914,6 +3923,69 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
_ => null,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The negotiated server-to-client cipher the status bar shows beside CONNECTED, or null when neither
|
||||
/// surface has a session — or when the one it has has nothing to report.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// One property reading whichever surface is showing, for the same reason <see cref="SessionAddress"/>
|
||||
/// is: <c>SessionStatusBar.axaml</c> is the same markup on both surfaces. The terminal's is
|
||||
/// <see cref="TerminalTabViewModel.Cipher"/>; SFTP's is <see cref="TransfersViewModel.ConnectedCipher"/>.
|
||||
/// </remarks>
|
||||
internal string? SessionCipher => Surface switch
|
||||
{
|
||||
ShellSurface.Terminal => NullIfEmpty(SelectedTab?.Cipher),
|
||||
_ when IsTransfersShowing => NullIfEmpty(Transfers.ConnectedCipher),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
/// <summary>The accepted host key's algorithm, e.g. <c>ssh-ed25519</c> — printed as negotiated, not shortened.</summary>
|
||||
/// <remarks>See <see cref="SessionCipher"/>; resolved the same way, off <see cref="TerminalTabViewModel.HostKeyAlgorithm"/> or <see cref="TransfersViewModel.ConnectedHostKeyAlgorithm"/>.</remarks>
|
||||
internal string? SessionHostKeyAlgorithm => Surface switch
|
||||
{
|
||||
ShellSurface.Terminal => NullIfEmpty(SelectedTab?.HostKeyAlgorithm),
|
||||
_ when IsTransfersShowing => NullIfEmpty(Transfers.ConnectedHostKeyAlgorithm),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The display name of the key or credential that authenticated, or null when a typed password did or
|
||||
/// nothing is connected.
|
||||
/// </summary>
|
||||
/// <remarks>See <see cref="SessionCipher"/>; resolved the same way, off <see cref="TerminalTabViewModel.IdentityLabel"/> or <see cref="TransfersViewModel.ConnectedIdentityLabel"/>.</remarks>
|
||||
internal string? SessionIdentityLabel => Surface switch
|
||||
{
|
||||
ShellSurface.Terminal => SelectedTab?.IdentityLabel,
|
||||
_ when IsTransfersShowing => Transfers.ConnectedIdentityLabel,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The status bar's one run of text for the host key and the identity that authenticated — the design's
|
||||
/// <c>ed25519 · acme-deploy-key</c> — or null while <see cref="SessionHostKeyAlgorithm"/> is.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Composed here rather than in the view, so <c>SessionStatusBar.axaml</c> binds one <c>TextBlock</c> to
|
||||
/// one string instead of assembling a separator between two bindings that can each be absent on their own.
|
||||
/// The algorithm prints exactly as negotiated — <c>ssh-ed25519</c>, not the design's shortened
|
||||
/// <c>ed25519</c> — because trimming it would be a cosmetic claim about a string this shell has no
|
||||
/// business editing. A typed-password session has no item to name, so it shows the algorithm alone with
|
||||
/// no <c>·</c> — there being nothing after the dot would be a punctuation mark standing in for the fact
|
||||
/// that was never real.
|
||||
/// </remarks>
|
||||
internal string? SessionIdentityText => SessionHostKeyAlgorithm is { } algorithm
|
||||
? SessionIdentityLabel is { } label ? $"{algorithm} · {label}" : algorithm
|
||||
: null;
|
||||
|
||||
/// <summary>Null for an empty string, unchanged otherwise.</summary>
|
||||
/// <remarks>
|
||||
/// <see cref="ISshConnection.Cipher"/> and <see cref="HostKeyPresentation.Algorithm"/> are non-nullable
|
||||
/// strings that this shell nonetheless treats as absent when empty — a session whose facts genuinely
|
||||
/// could not be read back (see <c>TerminalWorkspace.GetSessionFacts</c>) hands over <see cref="string.Empty"/>
|
||||
/// rather than null, and the status bar's collapse bindings only know how to ask about null.
|
||||
/// </remarks>
|
||||
private static string? NullIfEmpty(string? value) => string.IsNullOrEmpty(value) ? null : value;
|
||||
|
||||
/// <summary>Whether the session the header and status bar are describing is actually open.</summary>
|
||||
/// <remarks>
|
||||
/// Not the same question as <see cref="SessionAddress"/> being non-null on the terminal surface: a tab
|
||||
@@ -3981,6 +4053,10 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
OnPropertyChanged(nameof(SessionAddress));
|
||||
OnPropertyChanged(nameof(IsSessionConnected));
|
||||
OnPropertyChanged(nameof(SessionElapsedText));
|
||||
OnPropertyChanged(nameof(SessionCipher));
|
||||
OnPropertyChanged(nameof(SessionHostKeyAlgorithm));
|
||||
OnPropertyChanged(nameof(SessionIdentityLabel));
|
||||
OnPropertyChanged(nameof(SessionIdentityText));
|
||||
OnPropertyChanged(nameof(ShowsQuickAccessSidebar));
|
||||
}
|
||||
|
||||
@@ -3996,7 +4072,10 @@ internal sealed partial class MainWindowViewModel : ObservableObject, IAsyncDisp
|
||||
if (e.PropertyName is nameof(TransfersViewModel.SelectedHost)
|
||||
or nameof(TransfersViewModel.IsConnected)
|
||||
or nameof(TransfersViewModel.Remote)
|
||||
or nameof(TransfersViewModel.ConnectedTo))
|
||||
or nameof(TransfersViewModel.ConnectedTo)
|
||||
or nameof(TransfersViewModel.ConnectedCipher)
|
||||
or nameof(TransfersViewModel.ConnectedHostKeyAlgorithm)
|
||||
or nameof(TransfersViewModel.ConnectedIdentityLabel))
|
||||
{
|
||||
RaiseSessionState();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user