docs: Update documentation for 145 files

Generated by AurionDocs
Job ID: c99fff50-67a3-4294-b4df-3e73f4f12de9
Source commit: 4dcb480
This commit is contained in:
Hue
2026-07-23 08:10:35 +02:00
parent 4dcb480d1d
commit f8f4e03ddd
145 changed files with 22779 additions and 0 deletions
@@ -0,0 +1,19 @@
# ChatColors
> **File:** `src/EchoHub.Client/UI/Chat/ChatColors.cs`
> **Kind:** class
```csharp
public static partial class ChatColors
```
Shared color attributes and a small parsing helper for chat rendering. Use this class when rendering chat UI elements (timestamps, system messages, mentions, channel references, embeds, attachments, etc.) so all parts of the UI use a consistent set of Attribute values. Call SplitMentions when you need to break a message into colored segments so mentions (@user) and channel references (#channel) can be rendered with their accent colors while non-special text uses a supplied default.
## Remarks
ChatColors centralizes the visual styling for chat components and includes a utility to split text into ChatSegment pieces that carry color information. SplitMentions performs a two-pass parse: first it finds @mentions (avoiding emails by requiring no preceding word character) and marks them with MentionTextAttr; then it examines the remaining, non-mention segments to find #channel references (the regex requires at least one letter to avoid matching hex colors or numeric issue references) and marks those with ChannelRefAttr. All Attribute instances are readonly and intended as shared, immutable style tokens that renderers can reuse.
## Notes
- The mention regex uses a negative lookbehind (?<!\w) so strings like "me@domain" are not treated as @mentions.
- The channel regex requires at least one ASCII letter to avoid matching plain hex colors or purely numeric tokens.
- SplitMentions accepts a nullable defaultColor; callers should handle null when rendering (null means "no explicit attribute supplied").
@@ -0,0 +1,120 @@
# ChatLine.cs
> **Source:** `src/EchoHub.Client/UI/Chat/ChatLine.cs`
## Contents
- [ChatLine](#chatline)
- [AttachmentActionSpan](#attachmentactionspan)
- [AttachmentAction](#attachmentaction)
---
## ChatLine
> **File:** `src/EchoHub.Client/UI/Chat/ChatLine.cs`
> **Kind:** class
```csharp
public partial class ChatLine
```
Represents a single rendered chat line made up of colored ChatSegment pieces and associated display metadata. Use ChatLine when preparing or manipulating a line for rendering in the chat view (layout, wrapping, attachment actions, separators, mention/highlight state) rather than working with raw strings or segments directly.
## Remarks
ChatLine is the view-level unit for a message or a rule separator: it aggregates ChatSegment instances (text + color), stores metadata such as MessageId, sender, attachment info and clickable action spans, and exposes logic to break the line into multiple display lines that fit a viewport width. It centralizes presentation concerns (continuation indentation, colored continuation prefixes, non-wrapping rule lines, and unread-marker behavior) so the chat rendering layer can ask a ChatLine to produce the wrapped pieces it needs rather than implementing wrapping and metadata handling itself.
## Example
```csharp
// Construct from plain text
var line = new ChatLine("Hello, world!");
// Optional metadata
line.MessageId = Guid.NewGuid();
line.SenderUsername = "alice";
// Wrap to a viewport width of 40 columns, with a 4-space continuation indent
var wrapped = line.Wrap(40, continuationIndent: 4);
// Construct from explicit segments (preserves per-segment color attributes)
var segments = new List<ChatSegment>
{
new ChatSegment("[alice] ", ChatColors.RailAttr),
new ChatSegment("This is a message", null)
};
var coloredLine = new ChatLine(segments);
```
## Notes
- RuleLabel makes the line a separator rule; such lines are not word-wrapped and are regenerated to the viewport width by the view.
- If ContinuationPrefixSegments is set, it overrides ContinuationIndent: continuation lines use the prefix segments' column width instead of plain-space indentation.
- ActionSpans (when present) are column positions relative to the unwrapped line; only the first wrapped line preserves those spans — subsequent wrapped continuation lines do not.
- Wrapping respects grapheme clusters and column widths (uses GetGraphemes and GetColumns), so wide characters and combining sequences are handled when measuring width. If width <= 0 or the line already fits, Wrap returns the original line in a single-element list.
---
## AttachmentActionSpan
> **File:** `src/EchoHub.Client/UI/Chat/ChatLine.cs`
> **Kind:** record
```csharp
public readonly record struct AttachmentActionSpan(int StartCol, int EndCol, AttachmentAction Action)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `StartCol` | `int` | — |
| `EndCol` | `int` | — |
| `Action` | `AttachmentAction` | — |
It encodes an inclusive horizontal span on a chat line that maps to an AttachmentAction when clicked. This readonly record struct pairs StartCol and EndCol (both inclusive) with an Action to designate a specific clickable region that triggers an attachment operation.
## Remarks
Because it's a value type with immutable fields, AttachmentActionSpan is cheap to copy and compare, which helps with hit-testing and rendering across frames. It expresses the intent of interactive regions alongside their coordinates and associated action, keeping the UI layer decoupled from how actions are executed. This symbol complements other line-rendering data structures that describe clickable spans, enabling straightforward collection, filtering, and application during rendering.
## Notes
- EndCol is inclusive; ensure range checks treat EndCol as inclusive to avoid off-by-one errors.
- Overlapping spans may require careful resolution logic at render or hit-test time to determine which action should fire.
---
## AttachmentAction
> **File:** `src/EchoHub.Client/UI/Chat/ChatLine.cs`
> **Kind:** enum
```csharp
public enum AttachmentAction
{
OpenImage,
SaveImage,
}
```
An enum that represents the action a click on an attachment line can trigger in the chat UI. It lets the click handler distinguish between opening the image for viewing and saving the image to disk, promoting explicit, testable logic rather than ad-hoc behavior.
## Remarks
By codifying the possible outcomes as an enum, AttachmentAction defines a clear contract for how attachment clicks should be handled. It decouples the UI event from the concrete actions, making it easy to extend with new options (for example, ShareImage) without changing call sites. This abstraction supports consistent behavior across different chat lines and simplifies testing by allowing mocks or verifications based on the enum value.
## Example
```csharp
AttachmentAction action = /* determined by UI context */;
switch (action)
{
case AttachmentAction.OpenImage:
// Open the image in a viewer
break;
case AttachmentAction.SaveImage:
// Persist the image to disk
break;
}
```
## Notes
- If you later add actions to the enum, remember to handle them in all switch expressions and tests.
- Prefer explicit enum-based logic over string-based representations to avoid misinterpretation.
- Ensure UI-to-action mappings are consistent across chat lines to prevent user confusion.
---
@@ -0,0 +1,19 @@
# ChatListSource
> **File:** `src/EchoHub.Client/UI/Chat/ChatListSource.cs`
> **Kind:** class
```csharp
public class ChatListSource : IListDataSource
```
A list-backed data source for chat messages that implements IListDataSource and performs grapheme-aware rendering with per-segment coloring, mention-background highlighting, and a focus-based full-row highlight. Use this when supplying chat messages to a ListView-like control that expects the data source to manage items, raise collection-change notifications, and draw each row with segment-level attributes and correct column clipping.
## Remarks
ChatListSource maintains an internal `List<ChatLine>`, tracks the longest item via MaxItemLength, and raises a CollectionChanged (Reset) event whenever the collection is modified unless SuspendCollectionChangedEvent is set. Its Render implementation is grapheme-aware (uses GraphemeHelper.GetGraphemes and each grapheme's column width) and applies attributes per ChatSegment: a Focus attribute (when the row is selected and the list has focus) or the segment's color with fallbacks for missing backgrounds. If a ChatLine.IsMention is true the renderer uses ChatColors.MentionHighlightAttr.Background to override segment backgrounds and to fill the remainder of the row.
## Notes
- GetLine returns null for out-of-range indices; callers should validate the index first.
- IsMarked/SetMark are intentionally no-ops in this implementation and Dispose is a no-op — no per-item mark state or unmanaged cleanup is performed.
- MaxItemLength is updated only when lines are added/inserted; mutating a ChatLine.TextLength after insertion will not update MaxItemLength automatically. Use SuspendCollectionChangedEvent to batch updates and suppress the Reset event during bulk changes.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,26 @@
# ChatSegment
> **File:** `src/EchoHub.Client/UI/Chat/ChatSegment.cs`
> **Kind:** record
```csharp
public record ChatSegment(string Text, Attribute? Color)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Text` | `string` | — |
| `Color` | `Attribute?` | — |
ChatSegment represents a colored fragment of text within a chat line. It pairs the displayed text with an optional color attribute, enabling the UI to render parts of a message with varying styling without altering the textual content. As a record, ChatSegment is immutable and supports value-based equality, making it convenient to compose a full line by aggregating multiple segments in a deterministic way.
## Remarks
ChatSegment exists to separate content from presentation. By modeling a line as a sequence of segments, the rendering layer can apply different colors or styles to each piece while preserving the original order. The record-like semantics also ease comparisons, caching, and deduplication of segments across messages.
## Notes
- Color is stored as a nullable Attribute; a null Color means no special styling is requested for this segment.
- `Attribute` is a general metadata type; downstream renderers interpret it to apply styling. The exact meaning of the Color value depends on the consuming UI.
- Because ChatSegment is a two-property record, equality includes both Text and Color; changes to either produce a distinct segment, which is important when deduplicating or comparing segments.
@@ -0,0 +1,11 @@
# RenderHelpers
> **File:** `src/EchoHub.Client/UI/Chat/RenderHelpers.cs`
> **Kind:** class
```csharp
static class RenderHelpers
```
RenderHelpers is a small, shared utility for rendering IListDataSource content. Its WriteText method writes text to a ListView grapheme-by-grapheme while respecting a maximum width, returning the updated count of drawn columns. It iterates over grapheme clusters obtained from GraphemeHelper.GetGraphemes(text); for each grapheme, it computes the display width with GetColumns() (falling back to 1 if necessary). If adding the grapheme would exceed maxWidth, rendering stops. Otherwise, it appends the grapheme to the ListView via lv.AddStr(grapheme) and increments the drawn count. This centralizes grapheme-aware rendering logic so multiple IListDataSource implementations share consistent width handling and avoid duplicating rendering concerns.
@@ -0,0 +1,24 @@
# WelcomeBanner
> **File:** `src/EchoHub.Client/UI/Chat/WelcomeBanner.cs`
> **Kind:** class
```csharp
internal static class WelcomeBanner
```
Renders a MOTD-style splash in the chat pane when no channel is selected — a gold-gradient ASCII logo accompanied by a version tagline and quick usage hints, evoking classic IRC greetings. Use WelcomeBanner.Build to generate the banner lines for a given viewport width and version string, then feed those lines into the chat UI.
## Remarks
WelcomeBanner encapsulates the presentation of the welcome banner: centering, padding, colorization, and the two-logo strategy are all handled here so the rest of the chat UI can simply render a sequence of lines. It selects between a full-width BigLogo and a compact SmallLogo based on the viewport width, scales the gradient across the chosen logo, and appends a version tagline plus a set of user hints. This keeps branding consistent across sizes and isolates banner-specific formatting from the broader rendering pipeline.
## Example
```csharp
var lines = WelcomeBanner.Build(80, "1.2.3");
// integrate 'lines' into the chat pane
```
## Notes
- The logo variant is chosen based on the provided width; very small panes will display SmallLogo to preserve legibility.
- The color attributes (Attributes on ChatSegment) require UI support in the chat renderer; without color support the banner falls back to plain text.
@@ -0,0 +1,23 @@
# AudioPlayerDialog
> **File:** `src/EchoHub.Client/UI/Dialogs/AudioPlayerDialog.cs`
> **Kind:** class
```csharp
public sealed class AudioPlayerDialog
```
AudioPlayerDialog is a sealed class that presents a modal Audio Player UI within the application's terminal UI. It assembles a compact layout with the current file name, a wave-like block visualization, playback status, and simple volume and playback controls, all exposed via a single Show method that binds an IApplication and an AudioPlaybackService to the dialog's lifecycle.
## Remarks
By encapsulating layout, colors, and animation in one place, it provides a reusable, cohesive UX for audio playback that can be dropped into different screens without duplicating UI code. The class relies on themed attributes (e.g. WaveActiveAttr, WaveIdleAttr, FileNameAttr, Status*Attr) to ensure consistent appearance, and uses a timer-driven animation loop to render the wave pattern while playback is active.
## Example
```csharp
AudioPlayerDialog.Show(app, audioService, "/path/to/song.mp3", "song.mp3");
```
## Notes
- The waveform visualization uses Unicode block characters; ensure your terminal font supports these glyphs for correct rendering.
- The dialog starts a background animation timer; dispose the dialog or stop the timer to avoid leaks when closing.
@@ -0,0 +1,23 @@
# ChannelPasswordDialog
> **File:** `src/EchoHub.Client/UI/Dialogs/ChannelPasswordDialog.cs`
> **Kind:** class
```csharp
public sealed class ChannelPasswordDialog
```
Prompts for a channel password when joining a protected channel and returns the entered password, or null if the user cancels. Use this helper whenever you need a consistent, modal password prompt instead of duplicating dialog boilerplate across join flows.
## Remarks
This class centralizes the user flow for joining password-protected channels. It presents a modal dialog titled Join #<channel>, collects the password, and returns it to the caller, ensuring a single, predictable contract. The UI avoids displaying the actual password text by using a redacted caption and automatically focusing the password field, while the dialog lifecycle is orchestrated through the application (app.Run and app.RequestStop).
## Example
```csharp
string? password = ChannelPasswordDialog.Show(app, "mychannel", "Enter password to join #mychannel.");
```
## Notes
- The method is synchronous and modal; it blocks the caller until the user completes the interaction.
- A null return value indicates the user canceled the operation. If the user submits an empty password, a brief error dialog is shown and the prompt remains active until a non-empty password is provided.
@@ -0,0 +1,80 @@
# ConnectDialog.cs
> **Source:** `src/EchoHub.Client/UI/Dialogs/ConnectDialog.cs`
## Contents
- [ConnectDialog](#connectdialog)
- [ConnectDialogResult](#connectdialogresult)
---
## ConnectDialog
> **File:** `src/EchoHub.Client/UI/Dialogs/ConnectDialog.cs`
> **Kind:** class
```csharp
public sealed class ConnectDialog
```
ConnectDialog is a Terminal.Gui-based dialog that collects server connection details and authentication information for the application. When shown, it can display a list of SavedServer entries at the top if any saved servers are provided; in that case a Saved Servers section is rendered with a ListView of display names that indicate whether a session exists (the code appends a [session] marker when a RefreshToken is present). Below (or in place of it, when there are no saved servers), the dialog presents manual entry fields for Server URL (default http://localhost:5000), Username, and Password, along with UI hints such as a hidden password placeholder and a Remember me option. Additional fields include Display Name and, when relevant, an Invite Code for invite-gated registrations. The static Show method returns a ConnectDialogResult when the user completes the dialog, or null if the dialog is cancelled; the dialog height is adjusted depending on whether saved servers are shown.
---
## ConnectDialogResult
> **File:** `src/EchoHub.Client/UI/Dialogs/ConnectDialog.cs`
> **Kind:** record
```csharp
public record ConnectDialogResult(
string ServerUrl, string Username, string Password,
bool IsRegister, bool RememberMe, string? SavedRefreshToken,
string? DisplayName = null, string? InviteCode = null)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `ServerUrl` | `string` | — |
| `Username` | `string` | — |
| `Password` | `string` | — |
| `IsRegister` | `bool` | — |
| `RememberMe` | `bool` | — |
| `SavedRefreshToken` | `string?` | — |
| `DisplayName` | `string?` | `null` |
| [`InviteCode`](../../../EchoHub.Core/Models/InviteCode.cs.md) | `string?` | `null` |
ConnectDialogResult encapsulates all user input gathered from the connect dialog as a single, immutable value. It is produced when the dialog completes and is consumed by the rest of the application to initiate a connection flow, passing the server URL, credentials, and onboarding flags as a single, strongly-typed package.
## Remarks
By collecting all related fields into a single record, this abstraction reduces coupling between the UI layer and the connection logic. It clearly expresses the intent of the user's action (login vs register) and whether credentials should be remembered, while allowing optional data (DisplayName, InviteCode) to participate in specialized flows without forcing callers to thread every field separately.
## Example
```csharp
// Common usage: construct a result from values collected in UI
var result = new ConnectDialogResult(
ServerUrl: "https://example.server/api",
Username: "alice",
Password: "P@ssw0rd",
IsRegister: false,
RememberMe: true,
SavedRefreshToken: null,
DisplayName: "Alice",
InviteCode: "INVITE-2024-ABCD"
);
```
## Notes
- DisplayName and InviteCode are nullable; omit them or pass null if not applicable.
- Password should be treated as sensitive data: avoid logging it or persisting it longer than necessary, and ensure proper disposal or clearing after use.
- SavedRefreshToken may be null; handle accordingly in login/refresh flows.
- This record is intended for in-memory transfer between UI and authentication/connection logic; when persisting or transmitting, apply appropriate security measures and avoid leaking confidential fields.
---
@@ -0,0 +1,74 @@
# CreateChannelDialog.cs
> **Source:** `src/EchoHub.Client/UI/Dialogs/CreateChannelDialog.cs`
## Contents
- [CreateChannelDialog](#createchanneldialog)
- [CreateChannelResult](#createchannelresult)
---
## CreateChannelDialog
> **File:** `src/EchoHub.Client/UI/Dialogs/CreateChannelDialog.cs`
> **Kind:** class
```csharp
public sealed class CreateChannelDialog
```
Displays a modal Create Channel dialog that collects the details needed to create a new channel: a name, an optional topic, a password, and a public visibility setting. The name is trimmed and normalized to lower case; if it is empty, the dialog reports an error and stays open. On Create, it builds a CreateChannelResult containing the name, topic (nullable), isPublic, and the password; on Cancel it returns null. The dialog runs via the provided IApplication instance and returns after the user makes a choice.
## Remarks
Encapsulates all UI logic for channel creation into a single entry point, enabling consistent behavior across the app and isolating rendering from business logic. The class acts as a small, self-contained UX widget that constructs the result object, ensuring callers need only handle the CreateChannelResult or null.
## Notes
- Name validation is minimal in code: the name is trimmed and lowercased, and non-empty; there is no explicit enforcement of length or allowed character patterns at runtime beyond what the UI hints suggest.
- Password handling appears behind-the-scenes (the UI labels redact the password, yet the password value is captured and returned as part of the result); ensure secure handling and minimize exposure of the plaintext password.
- The snippet references passwordField and publicCheckbox, which must exist in the full class scope; if you modify the UI composition, ensure these controls are present and wired consistently with the password retrieval and public visibility logic.
---
## CreateChannelResult
> **File:** `src/EchoHub.Client/UI/Dialogs/CreateChannelDialog.cs`
> **Kind:** record
```csharp
public record CreateChannelResult(string Name, string? Topic, bool IsPublic, string? Password)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Name` | `string` | — |
| `Topic` | `string?` | — |
| `IsPublic` | `bool` | — |
| `Password` | `string?` | — |
CreateChannelResult is an immutable data carrier that represents the outcome of creating a channel in the EchoHub client UI. It carries the channel's Name, an optional Topic, a flag IsPublic indicating whether the channel is public, and an optional Password.
## Remarks
As a record, CreateChannelResult participates in value-based equality, making comparisons straightforward without manual field checks. The positional constructor provides a concise, immutable payload that is easy to pass through layers (UI, services, or view models). You can deconstruct a result into its components, or derive a modified copy with a with-expression if you need a slightly different result without mutating the original. This type is intended to be produced by the channel-creation flow and consumed by UI code and downstream components.
## Example
```csharp
// Common case: create a public channel with a topic and password
var result = new CreateChannelResult("General", "Team discussions", true, "s3cr3t");
// Access fields
string name = result.Name;
string? topic = result.Topic;
bool isPublic = result.IsPublic;
string? password = result.Password;
// Deconstruct for convenience
var (n, t, pub, pwd) = result;
// Create a modified copy
var updated = result with { Topic = "New topic" };
```
---
@@ -0,0 +1,64 @@
# ProfileEditDialog.cs
> **Source:** `src/EchoHub.Client/UI/Dialogs/ProfileEditDialog.cs`
## Contents
- [ProfileEditDialog](#profileeditdialog)
- [ProfileEditResult](#profileeditresult)
---
## ProfileEditDialog
> **File:** `src/EchoHub.Client/UI/Dialogs/ProfileEditDialog.cs`
> **Kind:** class
```csharp
public sealed class ProfileEditDialog
```
ProfileEditDialog provides a Terminal.Gui dialog for editing the user's profile. Its Show method presents a modal dialog titled "Edit Profile" with fields for Display Name, Bio, Nickname Color (with a hex input and a live color preview) and Avatar selection, plus notification preferences, returning a ProfileEditResult when the user accepts or null if cancelled.
## Remarks
ProfileEditDialog centralizes profile-edit UI in one reusable component, ensuring a consistent look and behavior whenever the user updates their profile. It wires up real-time color previews by updating the color swatch whenever the hex input changes, and it delegates color parsing to HexColorHelper to translate user input into a Color value. The Avatar field demonstrates integration with a file picker (OpenDialog) within a Terminal.Gui workflow, keeping file selection cohesive with the rest of the dialog.
## Notes
- The Show method accepts optional parameters for notificationSoundEnabled and notificationVolume, defaulting to false and 30 respectively.
- If no avatar is selected, avatarField.Text remains empty.
- The return type is ProfileEditResult?; callers should handle null to cover the cancel path.
- This implementation relies on Terminal.Gui primitives (Label, TextField, Button, CheckBox, OpenDialog) and collaborator types (ProfileEditResult, HexColorHelper); ensure these types are available in the consuming project.
---
## ProfileEditResult
> **File:** `src/EchoHub.Client/UI/Dialogs/ProfileEditDialog.cs`
> **Kind:** record
```csharp
public record ProfileEditResult(string? DisplayName, string? Bio, string? NicknameColor, string? AvatarPath, bool? NotificationSoundEnabled, byte? NotificationVolume)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `DisplayName` | `string?` | — |
| `Bio` | `string?` | — |
| `NicknameColor` | `string?` | — |
| `AvatarPath` | `string?` | — |
| `NotificationSoundEnabled` | `bool?` | — |
| `NotificationVolume` | `byte?` | — |
Represents the data returned from the profile edit dialog. It encapsulates the user\'s optional inputs for DisplayName, Bio, NicknameColor, AvatarPath, NotificationSoundEnabled, and NotificationVolume so the caller can apply changes in a single operation. Each property is nullable: a null value means no change for that field; a non-null value provides a new value to persist.
## Remarks
ProfileEditResult is an immutable value object used as the dialog\'s return type. Its nullable fields express a delta: non-null values indicate updates, while null indicates no change. As a record, it benefits from value-based equality, making comparisons and tests straightforward, and it cleanly separates UI input from downstream update logic.
## Notes
- Null values indicate no change; apply only non-null fields when updating the profile.
- The type is immutable; to derive modifications, use a with-expression to create a new instance.
---
@@ -0,0 +1,51 @@
# ProfileViewDialog.cs
> **Source:** `src/EchoHub.Client/UI/Dialogs/ProfileViewDialog.cs`
## Contents
- [ProfileViewDialog](#profileviewdialog)
- [ProfileAction](#profileaction)
---
## ProfileViewDialog
> **File:** `src/EchoHub.Client/UI/Dialogs/ProfileViewDialog.cs`
> **Kind:** class
```csharp
public sealed class ProfileViewDialog
```
ProfileViewDialog renders a dialog to view a user's server profile; when showing the current user's profile it also exposes action buttons (Edit Profile / Set Status) and returns the chosen ProfileAction, while viewing another user yields a read-only presentation.
## Remarks
ProfileViewDialog encapsulates all the layout and formatting decisions for a user profile in a single place. It dynamically switches between a read-only view and an ownership-aware view that surfaces actions, and it applies color theming to the status and nickname fields. By centralizing this UI behavior, the dialog remains consistent across the application and reduces duplication by isolating profile presentation from business logic. The component gracefully handles a missing profile by showing an error message and returning a Close action, which defines a clear contract for callers.
## Notes
- If invoked with a null profile, the dialog shows an error and returns ProfileAction.Close; callers should guard against null input or handle the Close result accordingly.
- The dialog title differentiates ownership with "My Profile" for the current user and "Profile — {username}" for others, and it uses color-coding helpers to reflect status and nickname color for quick visual cues.
---
## ProfileAction
> **File:** `src/EchoHub.Client/UI/Dialogs/ProfileViewDialog.cs`
> **Kind:** enum
```csharp
public enum ProfileAction
{
Close,
EditProfile,
SetStatus
}
```
ProfileAction defines the set of actions a user can select from their profile dialog: Close, EditProfile, and SetStatus. It provides a typed representation of user intent that downstream UI logic can handle in a deterministic way, rather than relying on magic strings or numeric codes.
## Remarks
ProfileAction represents the users chosen action from the profile dialog, allowing the UI layer to dispatch the appropriate workflow in a type-safe way. By enumerating possible intents, the code can exhaustively handle all cases in a switch or pattern-match, reducing errors from invalid values. The Close action also clarifies that the action is about dialog lifecycle control as opposed to in-dialog tasks such as editing or setting status. If new actions are required in the future, they should be added here with clear naming that maps to corresponding UI behaviors.
---
@@ -0,0 +1,94 @@
# SearchDialog.cs
> **Source:** `src/EchoHub.Client/UI/Dialogs/SearchDialog.cs`
## Contents
- [SearchDialog](#searchdialog)
- [SearchResult](#searchresult)
- [SearchResultType](#searchresulttype)
---
## SearchDialog
> **File:** `src/EchoHub.Client/UI/Dialogs/SearchDialog.cs`
> **Kind:** class
```csharp
public static class SearchDialog
```
SearchDialog is a command-palette style search dialog used to quickly navigate channels and trigger common app actions from a single, keyboard-driven interface. Use it when you want fast, non-mouse access to channels and actions by filtering a combined list and selecting with Enter.
## Remarks
SearchDialog composes a modal dialog that presents both channel names and a predefined set of actions, merged into a single searchable list via a SearchListSource. It returns the selected SearchResult and signals completion to the hosting application by invoking RequestStop on IApplication, keeping the dialog logic decoupled from the rest of the UI. This abstraction enables a reusable, consistent navigation surface across different parts of the app.
## Example
```csharp
// Example
IApplication app = /* obtain your app instance */;
IReadOnlyList<string> channels = new[] { "general", "engineering" };
var result = SearchDialog.Show(app, channels);
if (result != null)
{
// Handle the selected item (channel or action) here.
}
```
## Notes
- The dialog includes a hint, a text field for filtering, a list of results, and a Cancel button; selection is returned as a SearchResult, or null if cancelled.
- Ctrl+K handling in both the dialog and the search field cancels the operation by requesting stop from the application, so be aware that this combo acts as a cancel gesture rather than an open/search trigger.
- When items exist, the first item is pre-selected; filtering updates the source and may reset the selection.
---
## SearchResult
> **File:** `src/EchoHub.Client/UI/Dialogs/SearchDialog.cs`
> **Kind:** record
```csharp
public record SearchResult(SearchResultType Type, string Key, string Label)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Type` | `SearchResultType` | — |
| `Key` | `string` | — |
| `Label` | `string` | — |
Represents a single entry in search results, encapsulating the result's category (Type), a key (Key), and a user-facing label (Label). As a positional-record, it is immutable and compared by value, which makes it convenient to pass around and render in the search UI.
## Remarks
Use SearchResult to model a single outcome returned by the search feature. Type communicates the kind of item (as defined by SearchResultType), Key is the stable identifier for navigation or lookup, and Label is the display text shown in the results list. Because it is a deconstructible record, you can conveniently extract its fields with deconstruction or pattern matching, and equality checks are based on the content rather than the instance identity.
## Notes
- Immutability: SearchResult uses a primary constructor; properties are read-only and a modified instance must be created with a with-expression or a new constructor.
- Deconstruction: The positional constructor enables deconstruction: var (t, k, l) = result; or access via result.Type, result.Key, result.Label.
- Type relies on the SearchResultType enum; when consuming code, prefer switching on Type rather than comparing display strings.
---
## SearchResultType
> **File:** `src/EchoHub.Client/UI/Dialogs/SearchDialog.cs`
> **Kind:** enum
```csharp
public enum SearchResultType
{
Channel,
Action
}
```
Represents the category of a search result in the EchoHub client UI, distinguishing Channel results from Action results. Developers reach for this enum to branch rendering or navigation logic based on the result type, instead of using boolean flags or string comparisons.
## Remarks
Because it is a small discriminant, SearchResultType is typically consumed alongside a broader SearchResult structure. It enables simple pattern matching in switch expressions or if statements, guiding UI decisions such as which view to open or which icon to display when a user selects a result.
---
@@ -0,0 +1,69 @@
# StatusDialog.cs
> **Source:** `src/EchoHub.Client/UI/Dialogs/StatusDialog.cs`
## Contents
- [StatusDialog](#statusdialog)
- [StatusDialogResult](#statusdialogresult)
---
## StatusDialog
> **File:** `src/EchoHub.Client/UI/Dialogs/StatusDialog.cs`
> **Kind:** class
```csharp
public sealed class StatusDialog
```
StatusDialog is a terminal-based UI component that presents a compact dialog for updating the current user's status and an optional status message. Its Show method renders the dialog initialized with the provided current status and message, and returns a StatusDialogResult when the user saves, or null if the user cancels.
The dialog consists of a title 'Set Status', a status option selector pre-populated with the current status, a text field for the status message, and Save/Cancel actions. On Save, the selected status is captured (defaulting to Online if nothing is selected) and the message is trimmed; an empty message becomes null. The method returns a new StatusDialogResult with those values and stops the application loop via app.RequestStop(); Cancel returns null and stops the loop.
Callers use the returned result to apply the updated status and message; otherwise, no changes are made.
## Remarks
StatusDialog encapsulates the presentation logic for updating user status, isolating UI concerns from business logic. It is a small, reusable piece that orchestrates Terminal.Gui controls (Dialog, Label, OptionSelector, TextField, Button) and relies on IApplication to drive the modal flow. The use of a default Online and trimming of the message ensures sane behavior even when fields are left blank.
## Example
```csharp
var result = StatusDialog.Show(app, currentStatus, currentMessage);
if (result != null)
{
// Apply updates to the user's status and message
currentStatus = result.Status;
currentMessage = result.Message;
}
```
## Notes
- A null result indicates the user cancelled the dialog; callers should guard against applying changes in this case.
- If the user leaves the Message field blank or whitespace, the message is stored as null.
- The Save action is wired as the default action (IsDefault = true), and both Save and Cancel terminate the modal interaction by invoking app.RequestStop().
---
## StatusDialogResult
> **File:** `src/EchoHub.Client/UI/Dialogs/StatusDialog.cs`
> **Kind:** record
```csharp
public record StatusDialogResult(UserStatus Status, string? StatusMessage)
```
**Parameters:**
| Parameter | Type | Default |
|-----------|------|---------|
| `Status` | [`UserStatus`](../../../EchoHub.Core/Models/UserStatus.cs.md) | — |
| `StatusMessage` | `string?` | — |
StatusDialogResult is a minimal, immutable data carrier returned when the status dialog completes. It groups the chosen user status (Status) with an optional message (StatusMessage) into a single value that downstream logic can consume without inspecting the dialog UI directly. As a C# record, it benefits from value-based equality and straightforward deconstruction.
## Remarks
StatusDialogResult encapsulates the outcome of a UI interaction into a single semantic unit that can be passed through the application flow or stored for auditing. It separates presentation concerns from business logic: callers reason about the user's status and optional message rather than UI details. The nullable StatusMessage signals that extra context is optional; consumer code should handle the absence gracefully, typically by pattern matching on Status and checking for a non-null message. The record type also supports structural equality, making tests and comparisons concise.
---
@@ -0,0 +1,14 @@
# UpdateConfirmDialog
> **File:** `src/EchoHub.Client/UI/Dialogs/UpdateConfirmDialog.cs`
> **Kind:** class
```csharp
public sealed class UpdateConfirmDialog
```
UpdateConfirmDialog is a sealed utility with a single static Show method that prompts the user to confirm an available update. It builds a small modal dialog titled “Update Available” showing the current and latest versions and offers two actions: Update (default) and Cancel; it returns true if the user chooses Update and false otherwise. The method runs the provided IApplication until the user makes a choice, using RequestStop to close the dialog and return the result.
## Remarks
It encapsulates the update-confirmation interaction as a reusable, modal prompt that coordinates with the host application's event loop, avoiding duplication of dialog boilerplate across the codebase.
@@ -0,0 +1,28 @@
# DroppedFileParser
> **File:** `src/EchoHub.Client/UI/Helpers/DroppedFileParser.cs`
> **Kind:** class
```csharp
public static class DroppedFileParser
```
DroppedFileParser is a small utility that interprets terminal-dropped input as potential file paths and resolves them to existing files. Use it when you need to convert user-typed or pasted text into concrete file paths without scattering filesystem checks across callers.
## Remarks
This abstraction centralizes the logic for recognizing path-like input and for extracting one or more existing file paths from either a single path or a space-separated list of paths. It exposes a fast pre-check (LooksLikePath) to avoid expensive filesystem calls for clearly non-path input, and a test-friendly parser (TryGetFiles) that can inject a custom file existence predicate. The design favors explicit handling of both Windows (drive letters and UNC) and POSIX-style absolute paths, including quoted components and spaces.
## Example
```csharp
var input = "\"C:\\Temp\\report.pdf\" C:\\Data\\log.txt";
if (DroppedFileParser.TryGetFiles(input, out var files))
{
// files contains: ["C:\\Temp\\report.pdf", "C:\\Data\\log.txt"]
}
```
## Notes
- LookSLikePath may return true for strings that resemble paths (e.g., starting with a quote, a slash, UNC prefix, or a drive letter), so TryGetFiles should be used to confirm actual file existence.
- TryGetFiles enforces that all tokens are fully-qualified paths and that each path exists (via the injectable fileExists predicate, which defaults to File.Exists). This reduces accidental assumptions about the input.
- The tokenization logic respects quoted segments so that spaces within a single path do not split tokens unintentionally.
@@ -0,0 +1,19 @@
# EmojiHelper
> **File:** `src/EchoHub.Client/UI/Helpers/EmojiHelper.cs`
> **Kind:** class
```csharp
public static class EmojiHelper
```
EmojiHelper converts emoji grapheme clusters to text shortcodes for safe TUI rendering. It replaces emoji with fixed-width ASCII shortcodes when available, falling back to a generic [emoji] placeholder for unknown symbols; non-emoji text passes through unchanged.
## Remarks
This utility uses grapheme-aware processing to handle complex emoji sequences (including ZWJ-joined glyphs and modifier-bearing emojis) by iterating over text elements rather than individual code points. It first attempts a full-grapheme shortcode lookup, then falls back to the base emoji (the first rune of the grapheme) if necessary, and finally uses the [emoji] placeholder when no mapping exists. An initial pass quickly determines whether any emoji exist in the input to avoid unnecessary work. The implementation relies on StringBuilder for efficient string construction, StringInfo for grapheme segmentation, and the EmojiShortcodes mapping as the source of truth for replacements.
## Notes
- Unknown or unmapped emoji are replaced with [emoji], which can reduce expressiveness if the shortcode dictionary is incomplete. Ensure EmojiShortcodes covers the emoji you expect to render in your UI.
@@ -0,0 +1,18 @@
# HexColorHelper
> **File:** `src/EchoHub.Client/UI/Helpers/HexColorHelper.cs`
> **Kind:** class
```csharp
public static class HexColorHelper
```
HexColorHelper is a small utility that converts hex color strings into Terminal.Gui coloring primitives. Use ParseHexColor to obtain an Attribute suitable for styling a control's foreground, and ParseHexToColor when you need a Color value with a safe fallback for invalid input.
## Remarks
By centralizing hex parsing, HexColorHelper avoids duplicating color-conversion logic and provides predictable fallbacks for malformed input. It interprets a hex string as an RGB triplet and applies it as the foreground color (with no explicit background). This keeps styling decisions consistent across the UI while keeping the parsing logic isolated in one place.
## Notes
- Invalid input yields null (for ParseHexColor) or the provided fallback (for ParseHexToColor); no exceptions are thrown.
- A 6-digit hex value is required after an optional leading '#'. Non-hex characters or incorrect length return fallback/null.
@@ -0,0 +1,25 @@
# NickColorHelper
> **File:** `src/EchoHub.Client/UI/Helpers/NickColorHelper.cs`
> **Kind:** class
```csharp
public static class NickColorHelper
```
NickColorHelper deterministically assigns a stable color to every nickname, ensuring the same nick always maps to the same palette entry. This mirrors classic IRC behavior and lets busy channels stay readable without per-user configuration.
## Remarks
NickColorHelper isolates color selection from rendering logic by exposing a pure function GetPaletteIndex and GetAttribute. The palette itself is a fixed sequence of medium-saturation colors designed for legibility on both dark and light backgrounds; changing the palette order would re-color every nick and break visual consistency across sessions.
## Example
```csharp
var color = NickColorHelper.GetAttribute("Alice");
// Use `color` when rendering Alice's username in the UI
```
## Notes
- Null nick will throw; ensure non-null before calling GetPaletteIndex.
- The palette order is fixed; reordering or removing entries changes every nickname's color.
- The mapping uses a case-insensitive FNV-1a hash; changing the hash or its normalization will alter which nick gets which color.
@@ -0,0 +1,21 @@
# ChannelListSource
> **File:** `src/EchoHub.Client/UI/ListSources/ChannelListSource.cs`
> **Kind:** class
```csharp
public class ChannelListSource : IListDataSource
```
A colored, list-backed IListDataSource that presents channel names with visual affordances: an active-channel indicator, unread count badges, and markers for protected, private and system channels. Use this when you need a ListView-compatible data source that maintains channel ordering, per-channel unread counts and simple visual state (active, mention, protected/private, system) instead of hand-rendering each row.
## Remarks
ChannelListSource centralizes the channel-list state required by a ListView: the ordered channel names, a per-channel unread count map and several role sets (protected, mention, private and system). It exposes a single Update method that replaces the in-memory collections in one operation and (unless suspended) raises a Reset collection-changed event so consumers can re-layout or refresh. The class also provides MaxItemLength to help the host compute layout and ToList to produce a display-friendly list of channel strings (each prefixed with '#'). Rendering is delegated to the ListView via the Render method; the class supplies attributes (ActiveAttr, UnreadAttr, NormalAttr, BadgeAttr, MentionAttr, SystemAttr) and simple prefix/marker rules so the view paints active items, unread badges and visual separation for system channels.
## Notes
- Update clears and replaces all internal collections; call it with the full desired state rather than trying to patch individual entries.
- The Count/MaxItemLength values are derived from the current channel list. MaxItemLength computes name.Length + 6 (reserved space for prefixes/badges), so layout logic should consider that padding when sizing the list column.
- CollectionChanged will be invoked with a NotifyCollectionChangedAction.Reset at the end of Update unless SuspendCollectionChangedEvent is true. SuspendCollectionChangedEvent is a simple in-memory flag — using it prevents the Reset event from being raised during an Update.
- IsMarked and SetMark are intentionally inert (IsMarked always returns false and SetMark is a no-op), so callers should not rely on marking support from this source.
- Render moves the ListView cursor using Math.Max(col - viewportX, 0) to account for horizontal scrolling (viewportX). Hosts should provide correct viewportX and width values so rendering and clipping behave as intended.
@@ -0,0 +1,22 @@
# SearchListSource
> **File:** `src/EchoHub.Client/UI/ListSources/SearchListSource.cs`
> **Kind:** class
```csharp
public class SearchListSource(List<SearchResult> items) : IListDataSource
```
List data source that feeds a search dialog's ListView: it maintains an original item list, supports case-insensitive filtering by label or key, raises a Reset collection-changed notification when the filter changes (unless suspended), and renders each row with color-coding depending on the SearchResultType.
## Remarks
This class combines two responsibilities commonly needed by a search dialog: fast, in-memory filtering of a fixed set of SearchResult records and rendering of those results into a ListView with per-type coloring. Consumers attach to CollectionChanged to refresh the UI when Filter(string) updates the visible set. Render uses RenderHelpers.WriteText to draw the label and then fills the remainder of the column; it chooses a highlight (selected) attribute from the ListView or a per-result attribute (channel/action) and preserves the list's background when a per-result attribute leaves the background as Color.None.
## Notes
- Filter is case-insensitive and matches either SearchResult.Label or SearchResult.Key.
- When Filter receives a null/whitespace query the visible list is reset to all items and a Reset event is raised (unless SuspendCollectionChangedEvent is true).
- IsMarked and SetMark are no-ops; this data source does not track per-item marks.
- Dispose is a no-op; there are no unmanaged resources to release.
- MaxItemLength returns 0 when there are no filtered items.
- This class does not provide internal synchronization; callers should ensure thread-safety when mutating the source list or calling Filter from multiple threads.
@@ -0,0 +1,21 @@
# UserListSource
> **File:** `src/EchoHub.Client/UI/ListSources/UserListSource.cs`
> **Kind:** class
```csharp
public class UserListSource : IListDataSource
```
A data source implementation for a list view that presents online users with per-user nickname colors. Use this when you need a ready-made IListDataSource that holds tuples of display text, an optional nickname color (Attribute), and the username; it supplies item count, a maximal item width, batch updates via Update, and a Render implementation that paints a status/prefix in the normal role and the username portion in the configured nickname color while respecting selection and a fixed column width.
## Remarks
UserListSource is a UI-focused data source: it couples a small in-memory collection of user display tuples with a Render method tailored for a ListView consumer. It delegates grapheme-aware splitting to GraphemeHelper so prefix characters (status icon and optional role badge) are drawn in the list's normal attribute while the visible username text is drawn in the per-user nickname color unless the item is selected (selection forces the normal/Focus attribute). MaxItemLength is maintained as a convenience for layout calculations and is updated by Update.
## Notes
- Update replaces the entire contents; after calling Update the class raises NotifyCollectionChangedAction.Reset unless SuspendCollectionChangedEvent is true. If you set SuspendCollectionChangedEvent to batch multiple updates you are responsible for raising/triggering an appropriate collection changed notification afterward.
- MaxItemLength is computed using each entry's Text.GetColumns(), so wide characters and grapheme clusters affect reported width — MaxItemLength is a column/terminal-width measure, not a character count.
- Rendering is grapheme-aware and respects the provided width: text drawing stops when the accumulated column width reaches the requested width. This prevents partial grapheme rendering but means long names will be truncated to fit.
- Several IListDataSource members are intentionally trivial: IsMarked and SetMark are no-ops, ToList returns the visible Text values as objects, and Dispose is a no-op. Callers should not rely on any persistent marking or disposal behavior from this class.
- The implementation contains no internal synchronization; it is not inherently thread-safe. Ensure all access (especially Update and Render) is serialized by the caller when used from multiple threads.
File diff suppressed because it is too large Load Diff