From c7f47c5104b12c2ed084635e09ea561b30584ff4 Mon Sep 17 00:00:00 2001 From: HueByte Date: Thu, 16 Jul 2026 14:53:28 +0000 Subject: [PATCH] deploy: 1f4af9a543704c108644de810c0ad83a1af3bd9c --- .../EchoHub.Client.AppOrchestrator.html | 33 + ...choHub.Client.Commands.CommandHandler.html | 30 + .../EchoHub.Client.Config.SavedServer.html | 66 ++ .../EchoHub.Client.Services.ApiClient.html | 38 + ...EchoHub.Client.Services.UpdateChecker.html | 34 + api/client/EchoHub.Client.Themes.Theme.html | 35 + .../EchoHub.Client.UI.Chat.ChatColors.html | 93 ++ .../EchoHub.Client.UI.Chat.ChatLine.html | 131 +++ ...Hub.Client.UI.Chat.ChatMessageManager.html | 144 ++- api/client/EchoHub.Client.UI.Dialogs.html | 4 - ...ub.Client.UI.Helpers.NickColorHelper.html} | 183 ++-- api/client/EchoHub.Client.UI.Helpers.html | 7 + ...ient.UI.ListSources.ChannelListSource.html | 8 +- ...choHub.Core.Contracts.IChannelService.html | 36 + ...e.Contracts.IMessageEncryptionService.html | 36 + .../EchoHub.Core.DTOs.ChannelMetaDto.html | 878 ++++++++++++++++++ api/core/EchoHub.Core.DTOs.html | 8 + .../EchoHub.Server.Config.UploadLimits.html | 564 +++++++++++ api/server/EchoHub.Server.Config.html | 130 +++ ...Server.Controllers.ChannelsController.html | 50 +- ...ub.Server.Controllers.UsersController.html | 8 +- ...choHub.Server.Services.ChannelService.html | 36 + api/toc.html | 19 +- api/toc.json | 2 +- articles/encrypted-rooms.html | 271 ++++++ articles/encryption.html | 2 +- articles/toc.html | 3 + articles/toc.json | 2 +- changelog/index.html | 1 + changelog/toc.html | 3 + changelog/toc.json | 2 +- changelog/v0.2.12.html | 9 +- changelog/v0.2.13.html | 140 +++ index.json | 77 +- manifest.json | 90 +- xrefmap.yml | 831 +++++++++++++++-- 36 files changed, 3728 insertions(+), 276 deletions(-) rename api/client/{EchoHub.Client.UI.Dialogs.UpdateProgressDialog.html => EchoHub.Client.UI.Helpers.NickColorHelper.html} (60%) create mode 100644 api/core/EchoHub.Core.DTOs.ChannelMetaDto.html create mode 100644 api/server/EchoHub.Server.Config.UploadLimits.html create mode 100644 api/server/EchoHub.Server.Config.html create mode 100644 articles/encrypted-rooms.html create mode 100644 changelog/v0.2.13.html diff --git a/api/client/EchoHub.Client.AppOrchestrator.html b/api/client/EchoHub.Client.AppOrchestrator.html index 9f447a7..688bf5b 100644 --- a/api/client/EchoHub.Client.AppOrchestrator.html +++ b/api/client/EchoHub.Client.AppOrchestrator.html @@ -225,6 +225,39 @@ Wires UI events to service calls and connection events to UI updates.

+ + +

+ PendingUpdate + +

+ +

Set when the user confirms an update. The host must run this after the Terminal.Gui main +loop exits (console restored), so the updater's in-place restart doesn't fight the TUI.

+
+
+ +
+
public Func<Task>? PendingUpdate { get; }
+
+ + + + + +

Property Value

+
+
Func<Task>
+
+
+ + + + + + + +

Methods

diff --git a/api/client/EchoHub.Client.Commands.CommandHandler.html b/api/client/EchoHub.Client.Commands.CommandHandler.html index 7acdbbb..0c1c5b7 100644 --- a/api/client/EchoHub.Client.Commands.CommandHandler.html +++ b/api/client/EchoHub.Client.Commands.CommandHandler.html @@ -679,6 +679,36 @@ Class CommandHandler + OnRoomInfo + + + +
+
+ +
+
public event Func<Task>? OnRoomInfo
+
+ + + + + + +

Event Type

+
+
Func<Task>
+
+
+ + + + + + + +

OnSendFile diff --git a/api/client/EchoHub.Client.Config.SavedServer.html b/api/client/EchoHub.Client.Config.SavedServer.html index c9c5d00..f1fc0f4 100644 --- a/api/client/EchoHub.Client.Config.SavedServer.html +++ b/api/client/EchoHub.Client.Config.SavedServer.html @@ -247,6 +247,72 @@ user's machine — the server never sees them.

+ + +

+ LastReadMessages + +

+ +

Last message the user has read per channel (message id as string), persisted so +unread counts, @mention highlights, and the "new messages" marker survive restarts.

+
+
+ +
+
public Dictionary<string, string> LastReadMessages { get; set; }
+
+ + + + + +

Property Value

+
+
Dictionary<string, string>
+
+
+ + + + + + + + + + +

+ LeftChannels + +

+ +

Channels the user explicitly left with /leave. Excluded from the automatic +join-all-channels pass at connect until the user joins them again.

+
+
+ +
+
public List<string> LeftChannels { get; set; }
+
+ + + + + +

Property Value

+
+
List<string>
+
+
+ + + + + + + +

diff --git a/api/client/EchoHub.Client.Services.ApiClient.html b/api/client/EchoHub.Client.Services.ApiClient.html index eb82300..6285e97 100644 --- a/api/client/EchoHub.Client.Services.ApiClient.html +++ b/api/client/EchoHub.Client.Services.ApiClient.html @@ -583,6 +583,44 @@ key-derivation salt). Returns null when the channel doesn't exist.

+ + +

+ GetChannelMetaAsync(string) + +

+ +

Fetches a channel's human-facing metadata (message count, unique posters, estimated +size, created date, room id) for the /meta command. Returns null if it doesn't exist.

+
+
+ +
+
public Task<ChannelMetaDto?> GetChannelMetaAsync(string channelName)
+
+ +

Parameters

+
+
channelName string
+
+
+ +

Returns

+
+
Task<ChannelMetaDto>
+
+
+ + + + + + + + + + +

diff --git a/api/client/EchoHub.Client.Services.UpdateChecker.html b/api/client/EchoHub.Client.Services.UpdateChecker.html index d9dbc80..cc02760 100644 --- a/api/client/EchoHub.Client.Services.UpdateChecker.html +++ b/api/client/EchoHub.Client.Services.UpdateChecker.html @@ -221,6 +221,40 @@ Class UpdateChecker + +

+ PendingUpdate + +

+ +

Set when the user confirms an update. The host runs this after the Terminal.Gui +main loop has exited and the console is restored, so the library's in-place restart doesn't +deadlock against a TUI that still owns the console.

+
+
+ +
+
public Func<Task>? PendingUpdate { get; }
+
+ + + + + +

Property Value

+
+
Func<Task>
+
+
+ + + + + + + +

Methods

diff --git a/api/client/EchoHub.Client.Themes.Theme.html b/api/client/EchoHub.Client.Themes.Theme.html index 3f0f42e..6e13b66 100644 --- a/api/client/EchoHub.Client.Themes.Theme.html +++ b/api/client/EchoHub.Client.Themes.Theme.html @@ -213,6 +213,41 @@ Class Theme + +

+ Border + +

+ +

Colors for the main-window frame borders (and their titles). Null falls back +to Base. Lets themes tone borders down independently of text — +e.g. the transparent themes use a dim gray for a subtler, glassy look. +Supports hex values ("#6E6E6E") as well as named colors.

+
+
+ +
+
public ThemeColors? Border { get; set; }
+
+ + + + + +

Property Value

+
+
ThemeColors
+
+
+ + + + + + + +

diff --git a/api/client/EchoHub.Client.UI.Chat.ChatColors.html b/api/client/EchoHub.Client.UI.Chat.ChatColors.html index 3df7e9f..7fc2800 100644 --- a/api/client/EchoHub.Client.UI.Chat.ChatColors.html +++ b/api/client/EchoHub.Client.UI.Chat.ChatColors.html @@ -214,6 +214,37 @@ Class ChatColors + DateRuleAttr + +

+ +

Horizontal date-separator rules (── Wed, Jul 16 ──…).

+
+
+ +
+
public static readonly Attribute DateRuleAttr
+
+ + + + +

Field Value

+
+
Attribute
+
+
+ + + + + + + + + +

EmbedBorderAttr @@ -424,6 +455,37 @@ Class ChatColors + RailAttr + +

+ +

The dim vertical rail (│) separating the nick column from message text.

+
+
+ +
+
public static readonly Attribute RailAttr
+
+ + + + +

Field Value

+
+
Attribute
+
+
+ + + + + + + + + +

SystemAttr @@ -483,6 +545,37 @@ Class ChatColors + UnreadMarkerAttr + +

+ +

The irssi-style "new messages" unread marker rule.

+
+
+ +
+
public static readonly Attribute UnreadMarkerAttr
+
+ + + + +

Field Value

+
+
Attribute
+
+
+ + + + + + + + +

Methods

diff --git a/api/client/EchoHub.Client.UI.Chat.ChatLine.html b/api/client/EchoHub.Client.UI.Chat.ChatLine.html index 4fd62a6..4892807 100644 --- a/api/client/EchoHub.Client.UI.Chat.ChatLine.html +++ b/api/client/EchoHub.Client.UI.Chat.ChatLine.html @@ -341,6 +341,40 @@ Class ChatLine + +

+ ContinuationPrefixSegments + +

+ +

Colored segments to prepend on continuation lines instead of plain spaces +(e.g. the nick-column rail " │ "). When set, takes +precedence over ContinuationIndent.

+
+
+ +
+
public List<ChatSegment>? ContinuationPrefixSegments { get; set; }
+
+ + + + + +

Property Value

+
+
List<ChatSegment>
+
+
+ + + + + + + +

@@ -372,6 +406,38 @@ Class ChatLine + +

+ IsUnreadMarker + +

+ +

True for the "new messages" unread-marker rule so it can be removed on channel switch.

+
+
+ +
+
public bool IsUnreadMarker { get; set; }
+
+ + + + + +

Property Value

+
+
bool
+
+
+ + + + + + + +

@@ -403,6 +469,71 @@ Class ChatLine + +

+ RuleAttr + +

+ +

Color for a rule line; null falls back to DateRuleAttr.

+
+
+ +
+
public Attribute? RuleAttr { get; set; }
+
+ + + + + +

Property Value

+
+
Attribute?
+
+
+ + + + + + + + + + +

+ RuleLabel + +

+ +

When set, this line is a horizontal separator rule (date change, unread marker). +The view regenerates it to the current viewport width instead of word-wrapping.

+
+
+ +
+
public string? RuleLabel { get; set; }
+
+ + + + + +

Property Value

+
+
string
+
+
+ + + + + + + +

diff --git a/api/client/EchoHub.Client.UI.Chat.ChatMessageManager.html b/api/client/EchoHub.Client.UI.Chat.ChatMessageManager.html index a266eb2..7fbe773 100644 --- a/api/client/EchoHub.Client.UI.Chat.ChatMessageManager.html +++ b/api/client/EchoHub.Client.UI.Chat.ChatMessageManager.html @@ -178,6 +178,72 @@ so the UI layer can refresh.

+

Fields +

+ + + +

+ ContentIndentCols + +

+ +

Columns before message text starts: "HH:mm " + nick column + " │ ".

+
+
+ +
+
public const int ContentIndentCols = 21
+
+ + + + +

Field Value

+
+
int
+
+
+ + + + + + + + + + +

+ NickColWidth + +

+ +

Columns reserved for the right-aligned nick column (WeeChat-style).

+
+
+ +
+
public const int NickColWidth = 12
+
+ + + + +

Field Value

+
+
int
+
+
+ + + + + + + + +

Properties

@@ -245,6 +311,71 @@ so the UI layer can refresh.

+ + +

+ LastReadIds + +

+ +

Last message the user has read per channel — persisted by the orchestrator so +unread/mention state can be seeded from history on the next connect.

+
+
+ +
+
public IReadOnlyDictionary<string, Guid> LastReadIds { get; }
+
+ + + + + +

Property Value

+
+
IReadOnlyDictionary<string, Guid>
+
+
+ + + + + + + + + + +

+ MentionChannels + +

+ +

Channels with an unread @mention of the current user (cleared by ClearUnread(string)).

+
+
+ +
+
public IReadOnlySet<string> MentionChannels { get; }
+
+ + + + + +

Property Value

+
+
IReadOnlySet<string>
+
+
+ + + + + + + +

Methods

@@ -515,17 +646,20 @@ so the UI layer can refresh.

-

- LoadHistory(string, List<MessageDto>) +

+ LoadHistory(string, List<MessageDto>, Guid?)

-

Load historical messages into a channel, replacing any existing messages.

+

Load historical messages into a channel, replacing any existing messages. +When lastReadId is given (persisted from a previous session), +messages after it seed the unread count, @mention highlight, and the +"new messages" marker — so activity that happened while offline still lights up.

-
public void LoadHistory(string channelName, List<MessageDto> messages)
+
public void LoadHistory(string channelName, List<MessageDto> messages, Guid? lastReadId = null)

Parameters

@@ -534,6 +668,8 @@ so the UI layer can refresh.

messages List<MessageDto>
+
lastReadId Guid?
+
diff --git a/api/client/EchoHub.Client.UI.Dialogs.html b/api/client/EchoHub.Client.UI.Dialogs.html index c8098fa..dafb08b 100644 --- a/api/client/EchoHub.Client.UI.Dialogs.html +++ b/api/client/EchoHub.Client.UI.Dialogs.html @@ -162,10 +162,6 @@ Shows Edit Profile / Set Status buttons when viewing own profile.

UpdateConfirmDialog
-
-
UpdateProgressDialog
-
-

Enums

diff --git a/api/client/EchoHub.Client.UI.Dialogs.UpdateProgressDialog.html b/api/client/EchoHub.Client.UI.Helpers.NickColorHelper.html similarity index 60% rename from api/client/EchoHub.Client.UI.Dialogs.UpdateProgressDialog.html rename to api/client/EchoHub.Client.UI.Helpers.NickColorHelper.html index bbfe54d..93cbe48 100644 --- a/api/client/EchoHub.Client.UI.Dialogs.UpdateProgressDialog.html +++ b/api/client/EchoHub.Client.UI.Helpers.NickColorHelper.html @@ -2,11 +2,11 @@ - Class UpdateProgressDialog | EchoHub Documentation + Class NickColorHelper | EchoHub Documentation - - + + @@ -16,7 +16,7 @@ - + @@ -84,24 +84,27 @@
-
+
-

-Class UpdateProgressDialog +

+Class NickColorHelper

-
Namespace
EchoHub.Client.UI.Dialogs
+
Namespace
EchoHub.Client.UI.Helpers
Assembly
EchoHub.Client.dll
-
+

Deterministic per-nick colors for users who haven't picked a nickname color. +The same nick always maps to the same palette entry (classic IRC client behavior), +so a busy channel stays scannable without any configuration.

+
-
public sealed class UpdateProgressDialog
+
public static class NickColorHelper
@@ -111,7 +114,7 @@ Class UpdateProgressDialog object -
UpdateProgressDialog
+
NickColorHelper
@@ -123,6 +126,9 @@ Class UpdateProgressDialog object.GetType() +
+ object.MemberwiseClone() +
object.ToString()
@@ -145,121 +151,78 @@ Class UpdateProgressDialog Constructors - - - - - -

- UpdateProgressDialog(IApplication, string) - -

- -
-
- -
-
public UpdateProgressDialog(IApplication app, string newVersion)
-
- -

Parameters

-
-
app IApplication
-
-
newVersion string
-
-
- - - - - - - - - - - -

Methods

- + -

- Close() - +

+ GetAttribute(string) +

-
+

The color attribute for a nick. Used as a fallback when the user has no +explicit nickname color set.

+
-
public void Close()
-
- - - - - - - - - - - - - - - -

- Show() - -

- -
-
- -
-
public void Show()
-
- - - - - - - - - - - - - - - -

- UpdateProgress(float, string) - -

- -
-
- -
-
public void UpdateProgress(float fraction, string statusText)
+
public static Attribute GetAttribute(string nick)

Parameters

-
fraction float
-
-
statusText string
+
nick string
+

Returns

+
+
Attribute
+
+
+ + + + + + + + + + + + + +

+ GetPaletteIndex(string, int) + +

+ +

Stable palette index for a nick: case-insensitive FNV-1a over the nick, +reduced modulo paletteSize. Pure function (no Terminal.Gui +types) so it is unit-testable without a display driver.

+
+
+ +
+
public static int GetPaletteIndex(string nick, int paletteSize)
+
+ +

Parameters

+
+
nick string
+
+
paletteSize int
+
+
+ +

Returns

+
+
int
+
+
@@ -275,7 +238,7 @@ Class UpdateProgressDialog - Edit this page + Edit this page diff --git a/api/client/EchoHub.Client.UI.Helpers.html b/api/client/EchoHub.Client.UI.Helpers.html index c9f23d0..c672aa3 100644 --- a/api/client/EchoHub.Client.UI.Helpers.html +++ b/api/client/EchoHub.Client.UI.Helpers.html @@ -111,6 +111,13 @@ so we replace them with fixed-width ASCII shortcodes for display only.

HexColorHelper

Helper to parse hex colors to Terminal.Gui Attributes.

+
+
+
+
NickColorHelper
+

Deterministic per-nick colors for users who haven't picked a nickname color. +The same nick always maps to the same palette entry (classic IRC client behavior), +so a busy channel stays scannable without any configuration.

diff --git a/api/client/EchoHub.Client.UI.ListSources.ChannelListSource.html b/api/client/EchoHub.Client.UI.ListSources.ChannelListSource.html index 91e77d9..8267cfd 100644 --- a/api/client/EchoHub.Client.UI.ListSources.ChannelListSource.html +++ b/api/client/EchoHub.Client.UI.ListSources.ChannelListSource.html @@ -459,8 +459,8 @@ Active channel gets a > indicator, unread channels are bright with a count ba -

- Update(List<string>, Dictionary<string, int>, string, IReadOnlySet<string>?) +

+ Update(List<string>, Dictionary<string, int>, string, IReadOnlySet<string>?, IReadOnlySet<string>?)

@@ -468,7 +468,7 @@ Active channel gets a > indicator, unread channels are bright with a count ba
-
public void Update(List<string> channels, Dictionary<string, int> unread, string activeChannel, IReadOnlySet<string>? protectedChannels = null)
+
public void Update(List<string> channels, Dictionary<string, int> unread, string activeChannel, IReadOnlySet<string>? protectedChannels = null, IReadOnlySet<string>? mentionChannels = null)

Parameters

@@ -481,6 +481,8 @@ Active channel gets a > indicator, unread channels are bright with a count ba
protectedChannels IReadOnlySet<string>
+
mentionChannels IReadOnlySet<string>
+
diff --git a/api/core/EchoHub.Core.Contracts.IChannelService.html b/api/core/EchoHub.Core.Contracts.IChannelService.html index 9eb6fe7..c529e2e 100644 --- a/api/core/EchoHub.Core.Contracts.IChannelService.html +++ b/api/core/EchoHub.Core.Contracts.IChannelService.html @@ -385,6 +385,42 @@ Interface IChannelService + +

+ GetChannelMetaAsync(string) + +

+ +
+
+ +
+
Task<ChannelMetaDto?> GetChannelMetaAsync(string channelName)
+
+ +

Parameters

+
+
channelName string
+
+
+ +

Returns

+
+
Task<ChannelMetaDto>
+
+
+ + + + + + + + + + +

diff --git a/api/core/EchoHub.Core.Contracts.IMessageEncryptionService.html b/api/core/EchoHub.Core.Contracts.IMessageEncryptionService.html index 9075f28..f7ef036 100644 --- a/api/core/EchoHub.Core.Contracts.IMessageEncryptionService.html +++ b/api/core/EchoHub.Core.Contracts.IMessageEncryptionService.html @@ -116,6 +116,42 @@ Interface IMessageEncryptionService Fields +

+ + + +

+ CiphertextPrefix + +

+ +

Prefix marking transport/at-rest encrypted content. Decrypt(string) is a +pass-through for values without it.

+
+
+ +
+
public const string CiphertextPrefix = "$ENC$v1$"
+
+ + + + +

Field Value

+
+
string
+
+
+ + + + + + + + +

Properties

diff --git a/api/core/EchoHub.Core.DTOs.ChannelMetaDto.html b/api/core/EchoHub.Core.DTOs.ChannelMetaDto.html new file mode 100644 index 0000000..f59398d --- /dev/null +++ b/api/core/EchoHub.Core.DTOs.ChannelMetaDto.html @@ -0,0 +1,878 @@ + + + + + Class ChannelMetaDto | EchoHub Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+
Table of Contents
+ +
+
+ +
+
+
+ +
+
+ + + +
+ +
+ + + +

+Class ChannelMetaDto +

+ +
+
Namespace
EchoHub.Core.DTOs
+
Assembly
EchoHub.Core.dll
+
+ +

Human-facing summary of a channel (the /meta command). For encrypted channels the +server still knows these figures — count, timestamps, and stored blob sizes — even though it +cannot read the content itself. EstimatedSizeBytes is the sum of stored +attachment blob sizes plus message text length, so it is an estimate, not an exact on-disk total.

+
+
+ +
+
public record ChannelMetaDto : IEquatable<ChannelMetaDto>
+
+ + + + +
+
Inheritance
+
+ +
ChannelMetaDto
+
+
+ +
+
Implements
+
+ +
+
+ + +
+
Inherited Members
+
+ + + + +
+ + + + + + +

Constructors +

+ + + + +

+ ChannelMetaDto(ChannelMetaDto) + +

+ +
+
+ +
+
protected ChannelMetaDto(ChannelMetaDto original)
+
+ +

Parameters

+
+
original ChannelMetaDto
+
+
+ + + + + + + + + + + + + + +

+ ChannelMetaDto(Guid, string, string?, bool, bool, int, int, long, DateTimeOffset) + +

+ +

Human-facing summary of a channel (the /meta command). For encrypted channels the +server still knows these figures — count, timestamps, and stored blob sizes — even though it +cannot read the content itself. EstimatedSizeBytes is the sum of stored +attachment blob sizes plus message text length, so it is an estimate, not an exact on-disk total.

+
+
+ +
+
public ChannelMetaDto(Guid Id, string Name, string? Topic, bool IsEncrypted, bool IsProtected, int MessageCount, int UniqueUserCount, long EstimatedSizeBytes, DateTimeOffset CreatedAt)
+
+ +

Parameters

+
+
Id Guid
+
+
Name string
+
+
Topic string
+
+
IsEncrypted bool
+
+
IsProtected bool
+
+
MessageCount int
+
+
UniqueUserCount int
+
+
EstimatedSizeBytes long
+
+
CreatedAt DateTimeOffset
+
+
+ + + + + + + + + + + + +

Properties +

+ + + + +

+ CreatedAt + +

+ +
+
+ +
+
public DateTimeOffset CreatedAt { get; init; }
+
+ + + + + +

Property Value

+
+
DateTimeOffset
+
+
+ + + + + + + + + + +

+ EqualityContract + +

+ +
+
+ +
+
protected virtual Type EqualityContract { get; }
+
+ + + + + +

Property Value

+
+
Type
+
+
+ + + + + + + + + + +

+ EstimatedSizeBytes + +

+ +
+
+ +
+
public long EstimatedSizeBytes { get; init; }
+
+ + + + + +

Property Value

+
+
long
+
+
+ + + + + + + + + + +

+ Id + +

+ +
+
+ +
+
public Guid Id { get; init; }
+
+ + + + + +

Property Value

+
+
Guid
+
+
+ + + + + + + + + + +

+ IsEncrypted + +

+ +
+
+ +
+
public bool IsEncrypted { get; init; }
+
+ + + + + +

Property Value

+
+
bool
+
+
+ + + + + + + + + + +

+ IsProtected + +

+ +
+
+ +
+
public bool IsProtected { get; init; }
+
+ + + + + +

Property Value

+
+
bool
+
+
+ + + + + + + + + + +

+ MessageCount + +

+ +
+
+ +
+
public int MessageCount { get; init; }
+
+ + + + + +

Property Value

+
+
int
+
+
+ + + + + + + + + + +

+ Name + +

+ +
+
+ +
+
public string Name { get; init; }
+
+ + + + + +

Property Value

+
+
string
+
+
+ + + + + + + + + + +

+ Topic + +

+ +
+
+ +
+
public string? Topic { get; init; }
+
+ + + + + +

Property Value

+
+
string
+
+
+ + + + + + + + + + +

+ UniqueUserCount + +

+ +
+
+ +
+
public int UniqueUserCount { get; init; }
+
+ + + + + +

Property Value

+
+
int
+
+
+ + + + + + + + +

Methods +

+ + + + +

+ Deconstruct(out Guid, out string, out string?, out bool, out bool, out int, out int, out long, out DateTimeOffset) + +

+ +
+
+ +
+
public void Deconstruct(out Guid Id, out string Name, out string? Topic, out bool IsEncrypted, out bool IsProtected, out int MessageCount, out int UniqueUserCount, out long EstimatedSizeBytes, out DateTimeOffset CreatedAt)
+
+ +

Parameters

+
+
Id Guid
+
+
Name string
+
+
Topic string
+
+
IsEncrypted bool
+
+
IsProtected bool
+
+
MessageCount int
+
+
UniqueUserCount int
+
+
EstimatedSizeBytes long
+
+
CreatedAt DateTimeOffset
+
+
+ + + + + + + + + + + + + + +

+ Equals(ChannelMetaDto?) + +

+ +
+
+ +
+
public virtual bool Equals(ChannelMetaDto? other)
+
+ +

Parameters

+
+
other ChannelMetaDto
+
+
+ +

Returns

+
+
bool
+
+
+ + + + + + + + + + + + + +

+ Equals(object?) + +

+ +
+
+ +
+
public override bool Equals(object? obj)
+
+ +

Parameters

+
+
obj object
+
+
+ +

Returns

+
+
bool
+
+
+ + + + + + + + + + + + + +

+ GetHashCode() + +

+ +
+
+ +
+
public override int GetHashCode()
+
+ + +

Returns

+
+
int
+
+
+ + + + + + + + + + + + + +

+ PrintMembers(StringBuilder) + +

+ +
+
+ +
+
protected virtual bool PrintMembers(StringBuilder builder)
+
+ +

Parameters

+
+
builder StringBuilder
+
+
+ +

Returns

+
+
bool
+
+
+ + + + + + + + + + + + + +

+ ToString() + +

+ +
+
+ +
+
public override string ToString()
+
+ + +

Returns

+
+
string
+
+
+ + + + + + + + + + + +

Operators +

+ + + + +

+ operator ==(ChannelMetaDto?, ChannelMetaDto?) + +

+ +
+
+ +
+
public static bool operator ==(ChannelMetaDto? left, ChannelMetaDto? right)
+
+ +

Parameters

+
+
left ChannelMetaDto
+
+
right ChannelMetaDto
+
+
+ +

Returns

+
+
bool
+
+
+ + + + + + + + + + + + + +

+ operator !=(ChannelMetaDto?, ChannelMetaDto?) + +

+ +
+
+ +
+
public static bool operator !=(ChannelMetaDto? left, ChannelMetaDto? right)
+
+ +

Parameters

+
+
left ChannelMetaDto
+
+
right ChannelMetaDto
+
+
+ +

Returns

+
+
bool
+
+
+ + + + + + + + + + + + +
+ + + + +
+ +
+ +
+
+ +
+ + + + diff --git a/api/core/EchoHub.Core.DTOs.html b/api/core/EchoHub.Core.DTOs.html index 4944bb3..4692b85 100644 --- a/api/core/EchoHub.Core.DTOs.html +++ b/api/core/EchoHub.Core.DTOs.html @@ -131,6 +131,14 @@ credential from a passphrase. Never includes the wrapped room key.

ChannelDto
+
+
ChannelMetaDto
+

Human-facing summary of a channel (the /meta command). For encrypted channels the +server still knows these figures — count, timestamps, and stored blob sizes — even though it +cannot read the content itself. EstimatedSizeBytes is the sum of stored +attachment blob sizes plus message text length, so it is an estimate, not an exact on-disk total.

+
+
ChannelOperationResult
diff --git a/api/server/EchoHub.Server.Config.UploadLimits.html b/api/server/EchoHub.Server.Config.UploadLimits.html new file mode 100644 index 0000000..69864cc --- /dev/null +++ b/api/server/EchoHub.Server.Config.UploadLimits.html @@ -0,0 +1,564 @@ + + + + + Class UploadLimits | EchoHub Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+
Table of Contents
+ +
+
+ +
+
+
+ +
+
+ + + +
+ +
+ + + +

+Class UploadLimits +

+ +
+
Namespace
EchoHub.Server.Config
+
Assembly
EchoHub.Server.dll
+
+ +

Admin-configurable upload limits, bound from the Uploads configuration section. +Sizes are expressed in megabytes in configuration; the *Bytes accessors convert them +for enforcement. Every default mirrors HubConstants so an absent or partial +Uploads section preserves the historical built-in limits.

+
+
+ +
+
public sealed class UploadLimits
+
+ + + + +
+
Inheritance
+
+ +
UploadLimits
+
+
+ + + +
+
Inherited Members
+
+ + + + + + +
+ + + + + + +

Constructors +

+ + + + +

+ UploadLimits() + +

+ +
+
+ +
+
public UploadLimits()
+
+ + + + + + + + + + + + + +

Properties +

+ + + + +

+ MaxAttachmentsPerMessage + +

+ +
+
+ +
+
public int MaxAttachmentsPerMessage { get; init; }
+
+ + + + + +

Property Value

+
+
int
+
+
+ + + + + + + + + + +

+ MaxAudioSizeBytes + +

+ +
+
+ +
+
public long MaxAudioSizeBytes { get; }
+
+ + + + + +

Property Value

+
+
long
+
+
+ + + + + + + + + + +

+ MaxAudioSizeMB + +

+ +
+
+ +
+
public int MaxAudioSizeMB { get; init; }
+
+ + + + + +

Property Value

+
+
int
+
+
+ + + + + + + + + + +

+ MaxAvatarSizeBytes + +

+ +
+
+ +
+
public long MaxAvatarSizeBytes { get; }
+
+ + + + + +

Property Value

+
+
long
+
+
+ + + + + + + + + + +

+ MaxAvatarSizeMB + +

+ +
+
+ +
+
public int MaxAvatarSizeMB { get; init; }
+
+ + + + + +

Property Value

+
+
int
+
+
+ + + + + + + + + + +

+ MaxFileSizeBytes + +

+ +
+
+ +
+
public long MaxFileSizeBytes { get; }
+
+ + + + + +

Property Value

+
+
long
+
+
+ + + + + + + + + + +

+ MaxFileSizeMB + +

+ +
+
+ +
+
public int MaxFileSizeMB { get; init; }
+
+ + + + + +

Property Value

+
+
int
+
+
+ + + + + + + + + + +

+ MaxImageSizeBytes + +

+ +
+
+ +
+
public long MaxImageSizeBytes { get; }
+
+ + + + + +

Property Value

+
+
long
+
+
+ + + + + + + + + + +

+ MaxImageSizeMB + +

+ +
+
+ +
+
public int MaxImageSizeMB { get; init; }
+
+ + + + + +

Property Value

+
+
int
+
+
+ + + + + + + + + + +

+ MaxRequestBodyBytes + +

+ +

Absolute ceiling for one message request body (largest file × the attachment cap). Used to +size the request-body and multipart limits so a configured increase actually takes effect.

+
+
+ +
+
public long MaxRequestBodyBytes { get; }
+
+ + + + + +

Property Value

+
+
long
+
+
+ + + + + + + + +

Methods +

+ + + + +

+ MaxForKind(AttachmentKind) + +

+ +

Maximum accepted size for a single attachment of the given kind.

+
+
+ +
+
public long MaxForKind(AttachmentKind kind)
+
+ +

Parameters

+
+
kind AttachmentKind
+
+
+ +

Returns

+
+
long
+
+
+ + + + + + + + + + + + +
+ + + + +
+ +
+ +
+
+ +
+ + + + diff --git a/api/server/EchoHub.Server.Config.html b/api/server/EchoHub.Server.Config.html new file mode 100644 index 0000000..544abc8 --- /dev/null +++ b/api/server/EchoHub.Server.Config.html @@ -0,0 +1,130 @@ + + + + + Namespace EchoHub.Server.Config | EchoHub Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+
Table of Contents
+ +
+
+ +
+
+
+ +
+
+ + + +
+ +
+ +

Namespace EchoHub.Server.Config

+
+
+
+ +

+Classes +

+
+
UploadLimits
+

Admin-configurable upload limits, bound from the Uploads configuration section. +Sizes are expressed in megabytes in configuration; the *Bytes accessors convert them +for enforcement. Every default mirrors HubConstants so an absent or partial +Uploads section preserves the historical built-in limits.

+
+
+ + +
+ +
+
+ + +
+ +
+ +
+
+ +
+ + + + diff --git a/api/server/EchoHub.Server.Controllers.ChannelsController.html b/api/server/EchoHub.Server.Controllers.ChannelsController.html index b951f2b..4212b3c 100644 --- a/api/server/EchoHub.Server.Controllers.ChannelsController.html +++ b/api/server/EchoHub.Server.Controllers.ChannelsController.html @@ -159,8 +159,8 @@ public class ChannelsController : ControllerBase -

- ChannelsController(IChannelService, EchoHubDbContext, FileStorageService, ImageToAsciiService, IHttpClientFactory, IChatService, IMessageEncryptionService) +

+ ChannelsController(IChannelService, EchoHubDbContext, FileStorageService, ImageToAsciiService, IHttpClientFactory, IChatService, IMessageEncryptionService, UploadLimits)

@@ -168,7 +168,7 @@ public class ChannelsController : ControllerBase
-
public ChannelsController(IChannelService channelService, EchoHubDbContext db, FileStorageService fileStorage, ImageToAsciiService asciiService, IHttpClientFactory httpClientFactory, IChatService chatService, IMessageEncryptionService encryption)
+
public ChannelsController(IChannelService channelService, EchoHubDbContext db, FileStorageService fileStorage, ImageToAsciiService asciiService, IHttpClientFactory httpClientFactory, IChatService chatService, IMessageEncryptionService encryption, UploadLimits uploadLimits)

Parameters

@@ -187,6 +187,8 @@ public class ChannelsController : ControllerBase
encryption IMessageEncryptionService
+
uploadLimits UploadLimits
+
@@ -318,6 +320,46 @@ public Task<IActionResult> GetChannelCrypto(string channel) + + +

+ GetChannelMeta(string) + +

+ +

Human-facing summary of a channel (message count, unique posters, estimated size, +created date, room id). Available for encrypted channels too — these are metadata the +server tracks even though it cannot read the messages themselves.

+
+
+ +
+
[HttpGet("{channel}/meta")]
+public Task<IActionResult> GetChannelMeta(string channel)
+
+ +

Parameters

+
+
channel string
+
+
+ +

Returns

+
+
Task<IActionResult>
+
+
+ + + + + + + + + + +

@@ -417,8 +459,6 @@ room-encrypted preview (preview), aligned by file order — the ser
[HttpPost("{channel}/messages")]
 [EnableRateLimiting("upload")]
-[RequestSizeLimit(1048576000)]
-[RequestFormLimits(MultipartBodyLengthLimit = 1048576000)]
 public Task<IActionResult> SendMessageWithAttachments(string channel, string? size = null)
diff --git a/api/server/EchoHub.Server.Controllers.UsersController.html b/api/server/EchoHub.Server.Controllers.UsersController.html index 969d230..59588f2 100644 --- a/api/server/EchoHub.Server.Controllers.UsersController.html +++ b/api/server/EchoHub.Server.Controllers.UsersController.html @@ -159,8 +159,8 @@ public class UsersController : ControllerBase -

- UsersController(IUserService, ImageToAsciiService) +

+ UsersController(IUserService, ImageToAsciiService, UploadLimits)

@@ -168,7 +168,7 @@ public class UsersController : ControllerBase
-
public UsersController(IUserService userService, ImageToAsciiService asciiService)
+
public UsersController(IUserService userService, ImageToAsciiService asciiService, UploadLimits uploadLimits)

Parameters

@@ -177,6 +177,8 @@ public class UsersController : ControllerBase
asciiService ImageToAsciiService
+
uploadLimits UploadLimits
+
diff --git a/api/server/EchoHub.Server.Services.ChannelService.html b/api/server/EchoHub.Server.Services.ChannelService.html index 3230cbe..f9768dc 100644 --- a/api/server/EchoHub.Server.Services.ChannelService.html +++ b/api/server/EchoHub.Server.Services.ChannelService.html @@ -462,6 +462,42 @@ Class ChannelService + +

+ GetChannelMetaAsync(string) + +

+ +
+
+ +
+
public Task<ChannelMetaDto?> GetChannelMetaAsync(string channelName)
+
+ +

Parameters

+
+
channelName string
+
+
+ +

Returns

+
+
Task<ChannelMetaDto>
+
+
+ + + + + + + + + + +

diff --git a/api/toc.html b/api/toc.html index e3066f2..7dad86e 100644 --- a/api/toc.html +++ b/api/toc.html @@ -229,9 +229,6 @@
  • UpdateConfirmDialog
  • -
  • - UpdateProgressDialog -
  • @@ -248,6 +245,9 @@
  • HexColorHelper
  • +
  • + NickColorHelper +
  • @@ -363,6 +363,9 @@
  • ChannelError
  • +
  • + ChannelMetaDto +
  • ChannelOperationResult
  • @@ -540,6 +543,16 @@ +
  • + + EchoHub.Server.Config + + +
  • EchoHub.Server.Controllers diff --git a/api/toc.json b/api/toc.json index c755f2b..9cce285 100644 --- a/api/toc.json +++ b/api/toc.json @@ -1,2 +1,2 @@ -{"items":[{"name":"Client","items":[{"name":"API Reference","includedFrom":"~/_api_meta/client/toc.yml","items":[{"name":"EchoHub.Client","href":"client/EchoHub.Client.html","topicHref":"client/EchoHub.Client.html","topicUid":"EchoHub.Client","type":"Namespace","items":[{"name":"AppOrchestrator","href":"client/EchoHub.Client.AppOrchestrator.html","topicHref":"client/EchoHub.Client.AppOrchestrator.html","topicUid":"EchoHub.Client.AppOrchestrator","type":"Class"}]},{"name":"EchoHub.Client.Commands","href":"client/EchoHub.Client.Commands.html","topicHref":"client/EchoHub.Client.Commands.html","topicUid":"EchoHub.Client.Commands","type":"Namespace","items":[{"name":"CommandHandler","href":"client/EchoHub.Client.Commands.CommandHandler.html","topicHref":"client/EchoHub.Client.Commands.CommandHandler.html","topicUid":"EchoHub.Client.Commands.CommandHandler","type":"Class"},{"name":"CommandResult","href":"client/EchoHub.Client.Commands.CommandResult.html","topicHref":"client/EchoHub.Client.Commands.CommandResult.html","topicUid":"EchoHub.Client.Commands.CommandResult","type":"Class"}]},{"name":"EchoHub.Client.Config","href":"client/EchoHub.Client.Config.html","topicHref":"client/EchoHub.Client.Config.html","topicUid":"EchoHub.Client.Config","type":"Namespace","items":[{"name":"AccountPreset","href":"client/EchoHub.Client.Config.AccountPreset.html","topicHref":"client/EchoHub.Client.Config.AccountPreset.html","topicUid":"EchoHub.Client.Config.AccountPreset","type":"Class"},{"name":"ClientConfig","href":"client/EchoHub.Client.Config.ClientConfig.html","topicHref":"client/EchoHub.Client.Config.ClientConfig.html","topicUid":"EchoHub.Client.Config.ClientConfig","type":"Class"},{"name":"ConfigManager","href":"client/EchoHub.Client.Config.ConfigManager.html","topicHref":"client/EchoHub.Client.Config.ConfigManager.html","topicUid":"EchoHub.Client.Config.ConfigManager","type":"Class"},{"name":"NotificationConfig","href":"client/EchoHub.Client.Config.NotificationConfig.html","topicHref":"client/EchoHub.Client.Config.NotificationConfig.html","topicUid":"EchoHub.Client.Config.NotificationConfig","type":"Class"},{"name":"SavedServer","href":"client/EchoHub.Client.Config.SavedServer.html","topicHref":"client/EchoHub.Client.Config.SavedServer.html","topicUid":"EchoHub.Client.Config.SavedServer","type":"Class"}]},{"name":"EchoHub.Client.Services","href":"client/EchoHub.Client.Services.html","topicHref":"client/EchoHub.Client.Services.html","topicUid":"EchoHub.Client.Services","type":"Namespace","items":[{"name":"ApiClient","href":"client/EchoHub.Client.Services.ApiClient.html","topicHref":"client/EchoHub.Client.Services.ApiClient.html","topicUid":"EchoHub.Client.Services.ApiClient","type":"Class"},{"name":"AsyncRunner","href":"client/EchoHub.Client.Services.AsyncRunner.html","topicHref":"client/EchoHub.Client.Services.AsyncRunner.html","topicUid":"EchoHub.Client.Services.AsyncRunner","type":"Class"},{"name":"AudioPlaybackService","href":"client/EchoHub.Client.Services.AudioPlaybackService.html","topicHref":"client/EchoHub.Client.Services.AudioPlaybackService.html","topicUid":"EchoHub.Client.Services.AudioPlaybackService","type":"Class"},{"name":"BackupInfo","href":"client/EchoHub.Client.Services.BackupInfo.html","topicHref":"client/EchoHub.Client.Services.BackupInfo.html","topicUid":"EchoHub.Client.Services.BackupInfo","type":"Class"},{"name":"ChannelPasswordRequiredException","href":"client/EchoHub.Client.Services.ChannelPasswordRequiredException.html","topicHref":"client/EchoHub.Client.Services.ChannelPasswordRequiredException.html","topicUid":"EchoHub.Client.Services.ChannelPasswordRequiredException","type":"Class"},{"name":"ClientEncryptionService","href":"client/EchoHub.Client.Services.ClientEncryptionService.html","topicHref":"client/EchoHub.Client.Services.ClientEncryptionService.html","topicUid":"EchoHub.Client.Services.ClientEncryptionService","type":"Class"},{"name":"ClipboardFiles","href":"client/EchoHub.Client.Services.ClipboardFiles.html","topicHref":"client/EchoHub.Client.Services.ClipboardFiles.html","topicUid":"EchoHub.Client.Services.ClipboardFiles","type":"Class"},{"name":"EchoHubConnection","href":"client/EchoHub.Client.Services.EchoHubConnection.html","topicHref":"client/EchoHub.Client.Services.EchoHubConnection.html","topicUid":"EchoHub.Client.Services.EchoHubConnection","type":"Class"},{"name":"FolderPickResult","href":"client/EchoHub.Client.Services.FolderPickResult.html","topicHref":"client/EchoHub.Client.Services.FolderPickResult.html","topicUid":"EchoHub.Client.Services.FolderPickResult","type":"Class"},{"name":"JoinOutcome","href":"client/EchoHub.Client.Services.JoinOutcome.html","topicHref":"client/EchoHub.Client.Services.JoinOutcome.html","topicUid":"EchoHub.Client.Services.JoinOutcome","type":"Class"},{"name":"NativeFolderPicker","href":"client/EchoHub.Client.Services.NativeFolderPicker.html","topicHref":"client/EchoHub.Client.Services.NativeFolderPicker.html","topicUid":"EchoHub.Client.Services.NativeFolderPicker","type":"Class"},{"name":"NotificationSoundService","href":"client/EchoHub.Client.Services.NotificationSoundService.html","topicHref":"client/EchoHub.Client.Services.NotificationSoundService.html","topicUid":"EchoHub.Client.Services.NotificationSoundService","type":"Class"},{"name":"OutgoingAttachment","href":"client/EchoHub.Client.Services.OutgoingAttachment.html","topicHref":"client/EchoHub.Client.Services.OutgoingAttachment.html","topicUid":"EchoHub.Client.Services.OutgoingAttachment","type":"Class"},{"name":"PathSetup","href":"client/EchoHub.Client.Services.PathSetup.html","topicHref":"client/EchoHub.Client.Services.PathSetup.html","topicUid":"EchoHub.Client.Services.PathSetup","type":"Class"},{"name":"PickerOutcome","href":"client/EchoHub.Client.Services.PickerOutcome.html","topicHref":"client/EchoHub.Client.Services.PickerOutcome.html","topicUid":"EchoHub.Client.Services.PickerOutcome","type":"Enum"},{"name":"RoomKeyStore","href":"client/EchoHub.Client.Services.RoomKeyStore.html","topicHref":"client/EchoHub.Client.Services.RoomKeyStore.html","topicUid":"EchoHub.Client.Services.RoomKeyStore","type":"Class"},{"name":"UpdateBackupService","href":"client/EchoHub.Client.Services.UpdateBackupService.html","topicHref":"client/EchoHub.Client.Services.UpdateBackupService.html","topicUid":"EchoHub.Client.Services.UpdateBackupService","type":"Class"},{"name":"UpdateChecker","href":"client/EchoHub.Client.Services.UpdateChecker.html","topicHref":"client/EchoHub.Client.Services.UpdateChecker.html","topicUid":"EchoHub.Client.Services.UpdateChecker","type":"Class"}]},{"name":"EchoHub.Client.Themes","href":"client/EchoHub.Client.Themes.html","topicHref":"client/EchoHub.Client.Themes.html","topicUid":"EchoHub.Client.Themes","type":"Namespace","items":[{"name":"Theme","href":"client/EchoHub.Client.Themes.Theme.html","topicHref":"client/EchoHub.Client.Themes.Theme.html","topicUid":"EchoHub.Client.Themes.Theme","type":"Class"},{"name":"ThemeColors","href":"client/EchoHub.Client.Themes.ThemeColors.html","topicHref":"client/EchoHub.Client.Themes.ThemeColors.html","topicUid":"EchoHub.Client.Themes.ThemeColors","type":"Class"},{"name":"ThemeManager","href":"client/EchoHub.Client.Themes.ThemeManager.html","topicHref":"client/EchoHub.Client.Themes.ThemeManager.html","topicUid":"EchoHub.Client.Themes.ThemeManager","type":"Class"}]},{"name":"EchoHub.Client.UI","href":"client/EchoHub.Client.UI.html","topicHref":"client/EchoHub.Client.UI.html","topicUid":"EchoHub.Client.UI","type":"Namespace","items":[{"name":"MainWindow","href":"client/EchoHub.Client.UI.MainWindow.html","topicHref":"client/EchoHub.Client.UI.MainWindow.html","topicUid":"EchoHub.Client.UI.MainWindow","type":"Class"}]},{"name":"EchoHub.Client.UI.Chat","href":"client/EchoHub.Client.UI.Chat.html","topicHref":"client/EchoHub.Client.UI.Chat.html","topicUid":"EchoHub.Client.UI.Chat","type":"Namespace","items":[{"name":"ChatColors","href":"client/EchoHub.Client.UI.Chat.ChatColors.html","topicHref":"client/EchoHub.Client.UI.Chat.ChatColors.html","topicUid":"EchoHub.Client.UI.Chat.ChatColors","type":"Class"},{"name":"ChatLine","href":"client/EchoHub.Client.UI.Chat.ChatLine.html","topicHref":"client/EchoHub.Client.UI.Chat.ChatLine.html","topicUid":"EchoHub.Client.UI.Chat.ChatLine","type":"Class"},{"name":"ChatListSource","href":"client/EchoHub.Client.UI.Chat.ChatListSource.html","topicHref":"client/EchoHub.Client.UI.Chat.ChatListSource.html","topicUid":"EchoHub.Client.UI.Chat.ChatListSource","type":"Class"},{"name":"ChatMessageManager","href":"client/EchoHub.Client.UI.Chat.ChatMessageManager.html","topicHref":"client/EchoHub.Client.UI.Chat.ChatMessageManager.html","topicUid":"EchoHub.Client.UI.Chat.ChatMessageManager","type":"Class"},{"name":"ChatSegment","href":"client/EchoHub.Client.UI.Chat.ChatSegment.html","topicHref":"client/EchoHub.Client.UI.Chat.ChatSegment.html","topicUid":"EchoHub.Client.UI.Chat.ChatSegment","type":"Class"}]},{"name":"EchoHub.Client.UI.Dialogs","href":"client/EchoHub.Client.UI.Dialogs.html","topicHref":"client/EchoHub.Client.UI.Dialogs.html","topicUid":"EchoHub.Client.UI.Dialogs","type":"Namespace","items":[{"name":"AudioPlayerDialog","href":"client/EchoHub.Client.UI.Dialogs.AudioPlayerDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.AudioPlayerDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.AudioPlayerDialog","type":"Class"},{"name":"ChannelPasswordDialog","href":"client/EchoHub.Client.UI.Dialogs.ChannelPasswordDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ChannelPasswordDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.ChannelPasswordDialog","type":"Class"},{"name":"ConnectDialog","href":"client/EchoHub.Client.UI.Dialogs.ConnectDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ConnectDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.ConnectDialog","type":"Class"},{"name":"ConnectDialogResult","href":"client/EchoHub.Client.UI.Dialogs.ConnectDialogResult.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ConnectDialogResult.html","topicUid":"EchoHub.Client.UI.Dialogs.ConnectDialogResult","type":"Class"},{"name":"CreateChannelDialog","href":"client/EchoHub.Client.UI.Dialogs.CreateChannelDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.CreateChannelDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.CreateChannelDialog","type":"Class"},{"name":"CreateChannelResult","href":"client/EchoHub.Client.UI.Dialogs.CreateChannelResult.html","topicHref":"client/EchoHub.Client.UI.Dialogs.CreateChannelResult.html","topicUid":"EchoHub.Client.UI.Dialogs.CreateChannelResult","type":"Class"},{"name":"ProfileAction","href":"client/EchoHub.Client.UI.Dialogs.ProfileAction.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ProfileAction.html","topicUid":"EchoHub.Client.UI.Dialogs.ProfileAction","type":"Enum"},{"name":"ProfileEditDialog","href":"client/EchoHub.Client.UI.Dialogs.ProfileEditDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ProfileEditDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.ProfileEditDialog","type":"Class"},{"name":"ProfileEditResult","href":"client/EchoHub.Client.UI.Dialogs.ProfileEditResult.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ProfileEditResult.html","topicUid":"EchoHub.Client.UI.Dialogs.ProfileEditResult","type":"Class"},{"name":"ProfileViewDialog","href":"client/EchoHub.Client.UI.Dialogs.ProfileViewDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ProfileViewDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.ProfileViewDialog","type":"Class"},{"name":"SearchDialog","href":"client/EchoHub.Client.UI.Dialogs.SearchDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.SearchDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.SearchDialog","type":"Class"},{"name":"SearchResult","href":"client/EchoHub.Client.UI.Dialogs.SearchResult.html","topicHref":"client/EchoHub.Client.UI.Dialogs.SearchResult.html","topicUid":"EchoHub.Client.UI.Dialogs.SearchResult","type":"Class"},{"name":"SearchResultType","href":"client/EchoHub.Client.UI.Dialogs.SearchResultType.html","topicHref":"client/EchoHub.Client.UI.Dialogs.SearchResultType.html","topicUid":"EchoHub.Client.UI.Dialogs.SearchResultType","type":"Enum"},{"name":"StatusDialog","href":"client/EchoHub.Client.UI.Dialogs.StatusDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.StatusDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.StatusDialog","type":"Class"},{"name":"StatusDialogResult","href":"client/EchoHub.Client.UI.Dialogs.StatusDialogResult.html","topicHref":"client/EchoHub.Client.UI.Dialogs.StatusDialogResult.html","topicUid":"EchoHub.Client.UI.Dialogs.StatusDialogResult","type":"Class"},{"name":"UpdateConfirmDialog","href":"client/EchoHub.Client.UI.Dialogs.UpdateConfirmDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.UpdateConfirmDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.UpdateConfirmDialog","type":"Class"},{"name":"UpdateProgressDialog","href":"client/EchoHub.Client.UI.Dialogs.UpdateProgressDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.UpdateProgressDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.UpdateProgressDialog","type":"Class"}]},{"name":"EchoHub.Client.UI.Helpers","href":"client/EchoHub.Client.UI.Helpers.html","topicHref":"client/EchoHub.Client.UI.Helpers.html","topicUid":"EchoHub.Client.UI.Helpers","type":"Namespace","items":[{"name":"DroppedFileParser","href":"client/EchoHub.Client.UI.Helpers.DroppedFileParser.html","topicHref":"client/EchoHub.Client.UI.Helpers.DroppedFileParser.html","topicUid":"EchoHub.Client.UI.Helpers.DroppedFileParser","type":"Class"},{"name":"EmojiHelper","href":"client/EchoHub.Client.UI.Helpers.EmojiHelper.html","topicHref":"client/EchoHub.Client.UI.Helpers.EmojiHelper.html","topicUid":"EchoHub.Client.UI.Helpers.EmojiHelper","type":"Class"},{"name":"HexColorHelper","href":"client/EchoHub.Client.UI.Helpers.HexColorHelper.html","topicHref":"client/EchoHub.Client.UI.Helpers.HexColorHelper.html","topicUid":"EchoHub.Client.UI.Helpers.HexColorHelper","type":"Class"}]},{"name":"EchoHub.Client.UI.ListSources","href":"client/EchoHub.Client.UI.ListSources.html","topicHref":"client/EchoHub.Client.UI.ListSources.html","topicUid":"EchoHub.Client.UI.ListSources","type":"Namespace","items":[{"name":"ChannelListSource","href":"client/EchoHub.Client.UI.ListSources.ChannelListSource.html","topicHref":"client/EchoHub.Client.UI.ListSources.ChannelListSource.html","topicUid":"EchoHub.Client.UI.ListSources.ChannelListSource","type":"Class"},{"name":"SearchListSource","href":"client/EchoHub.Client.UI.ListSources.SearchListSource.html","topicHref":"client/EchoHub.Client.UI.ListSources.SearchListSource.html","topicUid":"EchoHub.Client.UI.ListSources.SearchListSource","type":"Class"},{"name":"UserListSource","href":"client/EchoHub.Client.UI.ListSources.UserListSource.html","topicHref":"client/EchoHub.Client.UI.ListSources.UserListSource.html","topicUid":"EchoHub.Client.UI.ListSources.UserListSource","type":"Class"}]}]},{"name":"Articles","includedFrom":"~/api/client-articles/toc.yml","items":[{"name":"Overview","href":"client-articles/index.html","topicHref":"client-articles/index.html"}]}]},{"name":"Core","items":[{"name":"API Reference","includedFrom":"~/_api_meta/core/toc.yml","items":[{"name":"EchoHub.Core.Constants","href":"core/EchoHub.Core.Constants.html","topicHref":"core/EchoHub.Core.Constants.html","topicUid":"EchoHub.Core.Constants","type":"Namespace","items":[{"name":"HubConstants","href":"core/EchoHub.Core.Constants.HubConstants.html","topicHref":"core/EchoHub.Core.Constants.HubConstants.html","topicUid":"EchoHub.Core.Constants.HubConstants","type":"Class"},{"name":"ValidationConstants","href":"core/EchoHub.Core.Constants.ValidationConstants.html","topicHref":"core/EchoHub.Core.Constants.ValidationConstants.html","topicUid":"EchoHub.Core.Constants.ValidationConstants","type":"Class"}]},{"name":"EchoHub.Core.Contracts","href":"core/EchoHub.Core.Contracts.html","topicHref":"core/EchoHub.Core.Contracts.html","topicUid":"EchoHub.Core.Contracts","type":"Namespace","items":[{"name":"ChannelListItem","href":"core/EchoHub.Core.Contracts.ChannelListItem.html","topicHref":"core/EchoHub.Core.Contracts.ChannelListItem.html","topicUid":"EchoHub.Core.Contracts.ChannelListItem","type":"Class"},{"name":"IChannelService","href":"core/EchoHub.Core.Contracts.IChannelService.html","topicHref":"core/EchoHub.Core.Contracts.IChannelService.html","topicUid":"EchoHub.Core.Contracts.IChannelService","type":"Interface"},{"name":"IChatBroadcaster","href":"core/EchoHub.Core.Contracts.IChatBroadcaster.html","topicHref":"core/EchoHub.Core.Contracts.IChatBroadcaster.html","topicUid":"EchoHub.Core.Contracts.IChatBroadcaster","type":"Interface"},{"name":"IChatService","href":"core/EchoHub.Core.Contracts.IChatService.html","topicHref":"core/EchoHub.Core.Contracts.IChatService.html","topicUid":"EchoHub.Core.Contracts.IChatService","type":"Interface"},{"name":"IEchoHubClient","href":"core/EchoHub.Core.Contracts.IEchoHubClient.html","topicHref":"core/EchoHub.Core.Contracts.IEchoHubClient.html","topicUid":"EchoHub.Core.Contracts.IEchoHubClient","type":"Interface"},{"name":"IMessageEncryptionService","href":"core/EchoHub.Core.Contracts.IMessageEncryptionService.html","topicHref":"core/EchoHub.Core.Contracts.IMessageEncryptionService.html","topicUid":"EchoHub.Core.Contracts.IMessageEncryptionService","type":"Interface"},{"name":"IUserService","href":"core/EchoHub.Core.Contracts.IUserService.html","topicHref":"core/EchoHub.Core.Contracts.IUserService.html","topicUid":"EchoHub.Core.Contracts.IUserService","type":"Interface"}]},{"name":"EchoHub.Core.DTOs","href":"core/EchoHub.Core.DTOs.html","topicHref":"core/EchoHub.Core.DTOs.html","topicUid":"EchoHub.Core.DTOs","type":"Namespace","items":[{"name":"ApiResponse","href":"core/EchoHub.Core.DTOs.ApiResponse.html","topicHref":"core/EchoHub.Core.DTOs.ApiResponse.html","topicUid":"EchoHub.Core.DTOs.ApiResponse","type":"Class"},{"name":"ApiResponse","href":"core/EchoHub.Core.DTOs.ApiResponse-1.html","topicHref":"core/EchoHub.Core.DTOs.ApiResponse-1.html","topicUid":"EchoHub.Core.DTOs.ApiResponse`1","type":"Class"},{"name":"AssignRoleRequest","href":"core/EchoHub.Core.DTOs.AssignRoleRequest.html","topicHref":"core/EchoHub.Core.DTOs.AssignRoleRequest.html","topicUid":"EchoHub.Core.DTOs.AssignRoleRequest","type":"Class"},{"name":"AttachmentDto","href":"core/EchoHub.Core.DTOs.AttachmentDto.html","topicHref":"core/EchoHub.Core.DTOs.AttachmentDto.html","topicUid":"EchoHub.Core.DTOs.AttachmentDto","type":"Class"},{"name":"AvatarUploadResponse","href":"core/EchoHub.Core.DTOs.AvatarUploadResponse.html","topicHref":"core/EchoHub.Core.DTOs.AvatarUploadResponse.html","topicUid":"EchoHub.Core.DTOs.AvatarUploadResponse","type":"Class"},{"name":"BanRequest","href":"core/EchoHub.Core.DTOs.BanRequest.html","topicHref":"core/EchoHub.Core.DTOs.BanRequest.html","topicUid":"EchoHub.Core.DTOs.BanRequest","type":"Class"},{"name":"ChannelCryptoDto","href":"core/EchoHub.Core.DTOs.ChannelCryptoDto.html","topicHref":"core/EchoHub.Core.DTOs.ChannelCryptoDto.html","topicUid":"EchoHub.Core.DTOs.ChannelCryptoDto","type":"Class"},{"name":"ChannelDto","href":"core/EchoHub.Core.DTOs.ChannelDto.html","topicHref":"core/EchoHub.Core.DTOs.ChannelDto.html","topicUid":"EchoHub.Core.DTOs.ChannelDto","type":"Class"},{"name":"ChannelError","href":"core/EchoHub.Core.DTOs.ChannelError.html","topicHref":"core/EchoHub.Core.DTOs.ChannelError.html","topicUid":"EchoHub.Core.DTOs.ChannelError","type":"Enum"},{"name":"ChannelOperationResult","href":"core/EchoHub.Core.DTOs.ChannelOperationResult.html","topicHref":"core/EchoHub.Core.DTOs.ChannelOperationResult.html","topicUid":"EchoHub.Core.DTOs.ChannelOperationResult","type":"Class"},{"name":"CreateChannelRequest","href":"core/EchoHub.Core.DTOs.CreateChannelRequest.html","topicHref":"core/EchoHub.Core.DTOs.CreateChannelRequest.html","topicUid":"EchoHub.Core.DTOs.CreateChannelRequest","type":"Class"},{"name":"EmbedDto","href":"core/EchoHub.Core.DTOs.EmbedDto.html","topicHref":"core/EchoHub.Core.DTOs.EmbedDto.html","topicUid":"EchoHub.Core.DTOs.EmbedDto","type":"Class"},{"name":"EncryptionKeyResponse","href":"core/EchoHub.Core.DTOs.EncryptionKeyResponse.html","topicHref":"core/EchoHub.Core.DTOs.EncryptionKeyResponse.html","topicUid":"EchoHub.Core.DTOs.EncryptionKeyResponse","type":"Class"},{"name":"ErrorResponse","href":"core/EchoHub.Core.DTOs.ErrorResponse.html","topicHref":"core/EchoHub.Core.DTOs.ErrorResponse.html","topicUid":"EchoHub.Core.DTOs.ErrorResponse","type":"Class"},{"name":"JoinChannelResult","href":"core/EchoHub.Core.DTOs.JoinChannelResult.html","topicHref":"core/EchoHub.Core.DTOs.JoinChannelResult.html","topicUid":"EchoHub.Core.DTOs.JoinChannelResult","type":"Class"},{"name":"KickRequest","href":"core/EchoHub.Core.DTOs.KickRequest.html","topicHref":"core/EchoHub.Core.DTOs.KickRequest.html","topicUid":"EchoHub.Core.DTOs.KickRequest","type":"Class"},{"name":"LoginRequest","href":"core/EchoHub.Core.DTOs.LoginRequest.html","topicHref":"core/EchoHub.Core.DTOs.LoginRequest.html","topicUid":"EchoHub.Core.DTOs.LoginRequest","type":"Class"},{"name":"LoginResponse","href":"core/EchoHub.Core.DTOs.LoginResponse.html","topicHref":"core/EchoHub.Core.DTOs.LoginResponse.html","topicUid":"EchoHub.Core.DTOs.LoginResponse","type":"Class"},{"name":"MessageDto","href":"core/EchoHub.Core.DTOs.MessageDto.html","topicHref":"core/EchoHub.Core.DTOs.MessageDto.html","topicUid":"EchoHub.Core.DTOs.MessageDto","type":"Class"},{"name":"MuteRequest","href":"core/EchoHub.Core.DTOs.MuteRequest.html","topicHref":"core/EchoHub.Core.DTOs.MuteRequest.html","topicUid":"EchoHub.Core.DTOs.MuteRequest","type":"Class"},{"name":"PaginatedResponse","href":"core/EchoHub.Core.DTOs.PaginatedResponse-1.html","topicHref":"core/EchoHub.Core.DTOs.PaginatedResponse-1.html","topicUid":"EchoHub.Core.DTOs.PaginatedResponse`1","type":"Class"},{"name":"RefreshRequest","href":"core/EchoHub.Core.DTOs.RefreshRequest.html","topicHref":"core/EchoHub.Core.DTOs.RefreshRequest.html","topicUid":"EchoHub.Core.DTOs.RefreshRequest","type":"Class"},{"name":"RegisterRequest","href":"core/EchoHub.Core.DTOs.RegisterRequest.html","topicHref":"core/EchoHub.Core.DTOs.RegisterRequest.html","topicUid":"EchoHub.Core.DTOs.RegisterRequest","type":"Class"},{"name":"RekeyChannelRequest","href":"core/EchoHub.Core.DTOs.RekeyChannelRequest.html","topicHref":"core/EchoHub.Core.DTOs.RekeyChannelRequest.html","topicUid":"EchoHub.Core.DTOs.RekeyChannelRequest","type":"Class"},{"name":"SendMessageRequest","href":"core/EchoHub.Core.DTOs.SendMessageRequest.html","topicHref":"core/EchoHub.Core.DTOs.SendMessageRequest.html","topicUid":"EchoHub.Core.DTOs.SendMessageRequest","type":"Class"},{"name":"SendUrlRequest","href":"core/EchoHub.Core.DTOs.SendUrlRequest.html","topicHref":"core/EchoHub.Core.DTOs.SendUrlRequest.html","topicUid":"EchoHub.Core.DTOs.SendUrlRequest","type":"Class"},{"name":"ServerStatusDto","href":"core/EchoHub.Core.DTOs.ServerStatusDto.html","topicHref":"core/EchoHub.Core.DTOs.ServerStatusDto.html","topicUid":"EchoHub.Core.DTOs.ServerStatusDto","type":"Class"},{"name":"UpdateProfileRequest","href":"core/EchoHub.Core.DTOs.UpdateProfileRequest.html","topicHref":"core/EchoHub.Core.DTOs.UpdateProfileRequest.html","topicUid":"EchoHub.Core.DTOs.UpdateProfileRequest","type":"Class"},{"name":"UpdateStatusRequest","href":"core/EchoHub.Core.DTOs.UpdateStatusRequest.html","topicHref":"core/EchoHub.Core.DTOs.UpdateStatusRequest.html","topicUid":"EchoHub.Core.DTOs.UpdateStatusRequest","type":"Class"},{"name":"UpdateTopicRequest","href":"core/EchoHub.Core.DTOs.UpdateTopicRequest.html","topicHref":"core/EchoHub.Core.DTOs.UpdateTopicRequest.html","topicUid":"EchoHub.Core.DTOs.UpdateTopicRequest","type":"Class"},{"name":"UserDto","href":"core/EchoHub.Core.DTOs.UserDto.html","topicHref":"core/EchoHub.Core.DTOs.UserDto.html","topicUid":"EchoHub.Core.DTOs.UserDto","type":"Class"},{"name":"UserError","href":"core/EchoHub.Core.DTOs.UserError.html","topicHref":"core/EchoHub.Core.DTOs.UserError.html","topicUid":"EchoHub.Core.DTOs.UserError","type":"Enum"},{"name":"UserOperationResult","href":"core/EchoHub.Core.DTOs.UserOperationResult.html","topicHref":"core/EchoHub.Core.DTOs.UserOperationResult.html","topicUid":"EchoHub.Core.DTOs.UserOperationResult","type":"Class"},{"name":"UserPresenceDto","href":"core/EchoHub.Core.DTOs.UserPresenceDto.html","topicHref":"core/EchoHub.Core.DTOs.UserPresenceDto.html","topicUid":"EchoHub.Core.DTOs.UserPresenceDto","type":"Class"},{"name":"UserProfileDto","href":"core/EchoHub.Core.DTOs.UserProfileDto.html","topicHref":"core/EchoHub.Core.DTOs.UserProfileDto.html","topicUid":"EchoHub.Core.DTOs.UserProfileDto","type":"Class"}]},{"name":"EchoHub.Core.Models","href":"core/EchoHub.Core.Models.html","topicHref":"core/EchoHub.Core.Models.html","topicUid":"EchoHub.Core.Models","type":"Namespace","items":[{"name":"Attachment","href":"core/EchoHub.Core.Models.Attachment.html","topicHref":"core/EchoHub.Core.Models.Attachment.html","topicUid":"EchoHub.Core.Models.Attachment","type":"Class"},{"name":"AttachmentKind","href":"core/EchoHub.Core.Models.AttachmentKind.html","topicHref":"core/EchoHub.Core.Models.AttachmentKind.html","topicUid":"EchoHub.Core.Models.AttachmentKind","type":"Enum"},{"name":"Channel","href":"core/EchoHub.Core.Models.Channel.html","topicHref":"core/EchoHub.Core.Models.Channel.html","topicUid":"EchoHub.Core.Models.Channel","type":"Class"},{"name":"ChannelMembership","href":"core/EchoHub.Core.Models.ChannelMembership.html","topicHref":"core/EchoHub.Core.Models.ChannelMembership.html","topicUid":"EchoHub.Core.Models.ChannelMembership","type":"Class"},{"name":"Message","href":"core/EchoHub.Core.Models.Message.html","topicHref":"core/EchoHub.Core.Models.Message.html","topicUid":"EchoHub.Core.Models.Message","type":"Class"},{"name":"MessageType","href":"core/EchoHub.Core.Models.MessageType.html","topicHref":"core/EchoHub.Core.Models.MessageType.html","topicUid":"EchoHub.Core.Models.MessageType","type":"Enum"},{"name":"RefreshToken","href":"core/EchoHub.Core.Models.RefreshToken.html","topicHref":"core/EchoHub.Core.Models.RefreshToken.html","topicUid":"EchoHub.Core.Models.RefreshToken","type":"Class"},{"name":"ServerRole","href":"core/EchoHub.Core.Models.ServerRole.html","topicHref":"core/EchoHub.Core.Models.ServerRole.html","topicUid":"EchoHub.Core.Models.ServerRole","type":"Enum"},{"name":"User","href":"core/EchoHub.Core.Models.User.html","topicHref":"core/EchoHub.Core.Models.User.html","topicUid":"EchoHub.Core.Models.User","type":"Class"},{"name":"UserStatus","href":"core/EchoHub.Core.Models.UserStatus.html","topicHref":"core/EchoHub.Core.Models.UserStatus.html","topicUid":"EchoHub.Core.Models.UserStatus","type":"Enum"}]},{"name":"EchoHub.Core.Security","href":"core/EchoHub.Core.Security.html","topicHref":"core/EchoHub.Core.Security.html","topicUid":"EchoHub.Core.Security","type":"Namespace","items":[{"name":"RoomCrypto","href":"core/EchoHub.Core.Security.RoomCrypto.html","topicHref":"core/EchoHub.Core.Security.RoomCrypto.html","topicUid":"EchoHub.Core.Security.RoomCrypto","type":"Class"},{"name":"RoomCrypto.DerivedKeys","href":"core/EchoHub.Core.Security.RoomCrypto.DerivedKeys.html","topicHref":"core/EchoHub.Core.Security.RoomCrypto.DerivedKeys.html","topicUid":"EchoHub.Core.Security.RoomCrypto.DerivedKeys","type":"Class"}]},{"name":"EchoHub.Core.Services","href":"core/EchoHub.Core.Services.html","topicHref":"core/EchoHub.Core.Services.html","topicUid":"EchoHub.Core.Services","type":"Namespace","items":[{"name":"FileValidationHelper","href":"core/EchoHub.Core.Services.FileValidationHelper.html","topicHref":"core/EchoHub.Core.Services.FileValidationHelper.html","topicUid":"EchoHub.Core.Services.FileValidationHelper","type":"Class"},{"name":"ImageToAsciiService","href":"core/EchoHub.Core.Services.ImageToAsciiService.html","topicHref":"core/EchoHub.Core.Services.ImageToAsciiService.html","topicUid":"EchoHub.Core.Services.ImageToAsciiService","type":"Class"}]}]},{"name":"Articles","includedFrom":"~/api/core-articles/toc.yml","items":[{"name":"Overview","href":"core-articles/index.html","topicHref":"core-articles/index.html"}]}]},{"name":"Server","items":[{"name":"API Reference","includedFrom":"~/_api_meta/server/toc.yml","items":[{"name":"EchoHub.Server.Auth","href":"server/EchoHub.Server.Auth.html","topicHref":"server/EchoHub.Server.Auth.html","topicUid":"EchoHub.Server.Auth","type":"Namespace","items":[{"name":"JwtTokenService","href":"server/EchoHub.Server.Auth.JwtTokenService.html","topicHref":"server/EchoHub.Server.Auth.JwtTokenService.html","topicUid":"EchoHub.Server.Auth.JwtTokenService","type":"Class"}]},{"name":"EchoHub.Server.Controllers","href":"server/EchoHub.Server.Controllers.html","topicHref":"server/EchoHub.Server.Controllers.html","topicUid":"EchoHub.Server.Controllers","type":"Namespace","items":[{"name":"AuthController","href":"server/EchoHub.Server.Controllers.AuthController.html","topicHref":"server/EchoHub.Server.Controllers.AuthController.html","topicUid":"EchoHub.Server.Controllers.AuthController","type":"Class"},{"name":"ChannelsController","href":"server/EchoHub.Server.Controllers.ChannelsController.html","topicHref":"server/EchoHub.Server.Controllers.ChannelsController.html","topicUid":"EchoHub.Server.Controllers.ChannelsController","type":"Class"},{"name":"FilesController","href":"server/EchoHub.Server.Controllers.FilesController.html","topicHref":"server/EchoHub.Server.Controllers.FilesController.html","topicUid":"EchoHub.Server.Controllers.FilesController","type":"Class"},{"name":"ModerationController","href":"server/EchoHub.Server.Controllers.ModerationController.html","topicHref":"server/EchoHub.Server.Controllers.ModerationController.html","topicUid":"EchoHub.Server.Controllers.ModerationController","type":"Class"},{"name":"ServerController","href":"server/EchoHub.Server.Controllers.ServerController.html","topicHref":"server/EchoHub.Server.Controllers.ServerController.html","topicUid":"EchoHub.Server.Controllers.ServerController","type":"Class"},{"name":"UsersController","href":"server/EchoHub.Server.Controllers.UsersController.html","topicHref":"server/EchoHub.Server.Controllers.UsersController.html","topicUid":"EchoHub.Server.Controllers.UsersController","type":"Class"}]},{"name":"EchoHub.Server.Data","href":"server/EchoHub.Server.Data.html","topicHref":"server/EchoHub.Server.Data.html","topicUid":"EchoHub.Server.Data","type":"Namespace","items":[{"name":"EchoHubDbContext","href":"server/EchoHub.Server.Data.EchoHubDbContext.html","topicHref":"server/EchoHub.Server.Data.EchoHubDbContext.html","topicUid":"EchoHub.Server.Data.EchoHubDbContext","type":"Class"}]},{"name":"EchoHub.Server.Data.Migrations","href":"server/EchoHub.Server.Data.Migrations.html","topicHref":"server/EchoHub.Server.Data.Migrations.html","topicUid":"EchoHub.Server.Data.Migrations","type":"Namespace","items":[{"name":"AddAttachmentFileSize","href":"server/EchoHub.Server.Data.Migrations.AddAttachmentFileSize.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddAttachmentFileSize.html","topicUid":"EchoHub.Server.Data.Migrations.AddAttachmentFileSize","type":"Class"},{"name":"AddChannelEncryptionEnvelope","href":"server/EchoHub.Server.Data.Migrations.AddChannelEncryptionEnvelope.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddChannelEncryptionEnvelope.html","topicUid":"EchoHub.Server.Data.Migrations.AddChannelEncryptionEnvelope","type":"Class"},{"name":"AddChannelIsPublic","href":"server/EchoHub.Server.Data.Migrations.AddChannelIsPublic.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddChannelIsPublic.html","topicUid":"EchoHub.Server.Data.Migrations.AddChannelIsPublic","type":"Class"},{"name":"AddChannelMembership","href":"server/EchoHub.Server.Data.Migrations.AddChannelMembership.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddChannelMembership.html","topicUid":"EchoHub.Server.Data.Migrations.AddChannelMembership","type":"Class"},{"name":"AddChannelPasswordHash","href":"server/EchoHub.Server.Data.Migrations.AddChannelPasswordHash.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddChannelPasswordHash.html","topicUid":"EchoHub.Server.Data.Migrations.AddChannelPasswordHash","type":"Class"},{"name":"AddEncryptionSupport","href":"server/EchoHub.Server.Data.Migrations.AddEncryptionSupport.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddEncryptionSupport.html","topicUid":"EchoHub.Server.Data.Migrations.AddEncryptionSupport","type":"Class"},{"name":"AddMessageAttachments","href":"server/EchoHub.Server.Data.Migrations.AddMessageAttachments.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddMessageAttachments.html","topicUid":"EchoHub.Server.Data.Migrations.AddMessageAttachments","type":"Class"},{"name":"AddMessageEmbed","href":"server/EchoHub.Server.Data.Migrations.AddMessageEmbed.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddMessageEmbed.html","topicUid":"EchoHub.Server.Data.Migrations.AddMessageEmbed","type":"Class"},{"name":"AddModerationRoles","href":"server/EchoHub.Server.Data.Migrations.AddModerationRoles.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddModerationRoles.html","topicUid":"EchoHub.Server.Data.Migrations.AddModerationRoles","type":"Class"},{"name":"InitialCreate","href":"server/EchoHub.Server.Data.Migrations.InitialCreate.html","topicHref":"server/EchoHub.Server.Data.Migrations.InitialCreate.html","topicUid":"EchoHub.Server.Data.Migrations.InitialCreate","type":"Class"}]},{"name":"EchoHub.Server.Hubs","href":"server/EchoHub.Server.Hubs.html","topicHref":"server/EchoHub.Server.Hubs.html","topicUid":"EchoHub.Server.Hubs","type":"Namespace","items":[{"name":"ChatHub","href":"server/EchoHub.Server.Hubs.ChatHub.html","topicHref":"server/EchoHub.Server.Hubs.ChatHub.html","topicUid":"EchoHub.Server.Hubs.ChatHub","type":"Class"}]},{"name":"EchoHub.Server.Services","href":"server/EchoHub.Server.Services.html","topicHref":"server/EchoHub.Server.Services.html","topicUid":"EchoHub.Server.Services","type":"Namespace","items":[{"name":"ChannelService","href":"server/EchoHub.Server.Services.ChannelService.html","topicHref":"server/EchoHub.Server.Services.ChannelService.html","topicUid":"EchoHub.Server.Services.ChannelService","type":"Class"},{"name":"ChatService","href":"server/EchoHub.Server.Services.ChatService.html","topicHref":"server/EchoHub.Server.Services.ChatService.html","topicUid":"EchoHub.Server.Services.ChatService","type":"Class"},{"name":"DirectoryClaimStore","href":"server/EchoHub.Server.Services.DirectoryClaimStore.html","topicHref":"server/EchoHub.Server.Services.DirectoryClaimStore.html","topicUid":"EchoHub.Server.Services.DirectoryClaimStore","type":"Class"},{"name":"FileCleanupService","href":"server/EchoHub.Server.Services.FileCleanupService.html","topicHref":"server/EchoHub.Server.Services.FileCleanupService.html","topicUid":"EchoHub.Server.Services.FileCleanupService","type":"Class"},{"name":"FileStorageService","href":"server/EchoHub.Server.Services.FileStorageService.html","topicHref":"server/EchoHub.Server.Services.FileStorageService.html","topicUid":"EchoHub.Server.Services.FileStorageService","type":"Class"},{"name":"LinkEmbedService","href":"server/EchoHub.Server.Services.LinkEmbedService.html","topicHref":"server/EchoHub.Server.Services.LinkEmbedService.html","topicUid":"EchoHub.Server.Services.LinkEmbedService","type":"Class"},{"name":"MessageEncryptionService","href":"server/EchoHub.Server.Services.MessageEncryptionService.html","topicHref":"server/EchoHub.Server.Services.MessageEncryptionService.html","topicUid":"EchoHub.Server.Services.MessageEncryptionService","type":"Class"},{"name":"MuteExpirationService","href":"server/EchoHub.Server.Services.MuteExpirationService.html","topicHref":"server/EchoHub.Server.Services.MuteExpirationService.html","topicUid":"EchoHub.Server.Services.MuteExpirationService","type":"Class"},{"name":"PresenceTracker","href":"server/EchoHub.Server.Services.PresenceTracker.html","topicHref":"server/EchoHub.Server.Services.PresenceTracker.html","topicUid":"EchoHub.Server.Services.PresenceTracker","type":"Class"},{"name":"RegistrationStatus","href":"server/EchoHub.Server.Services.RegistrationStatus.html","topicHref":"server/EchoHub.Server.Services.RegistrationStatus.html","topicUid":"EchoHub.Server.Services.RegistrationStatus","type":"Class"},{"name":"ServerDirectoryService","href":"server/EchoHub.Server.Services.ServerDirectoryService.html","topicHref":"server/EchoHub.Server.Services.ServerDirectoryService.html","topicUid":"EchoHub.Server.Services.ServerDirectoryService","type":"Class"},{"name":"SignalRBroadcaster","href":"server/EchoHub.Server.Services.SignalRBroadcaster.html","topicHref":"server/EchoHub.Server.Services.SignalRBroadcaster.html","topicUid":"EchoHub.Server.Services.SignalRBroadcaster","type":"Class"},{"name":"UserService","href":"server/EchoHub.Server.Services.UserService.html","topicHref":"server/EchoHub.Server.Services.UserService.html","topicUid":"EchoHub.Server.Services.UserService","type":"Class"}]},{"name":"EchoHub.Server.Setup","href":"server/EchoHub.Server.Setup.html","topicHref":"server/EchoHub.Server.Setup.html","topicUid":"EchoHub.Server.Setup","type":"Namespace","items":[{"name":"DataMigrationService","href":"server/EchoHub.Server.Setup.DataMigrationService.html","topicHref":"server/EchoHub.Server.Setup.DataMigrationService.html","topicUid":"EchoHub.Server.Setup.DataMigrationService","type":"Class"},{"name":"DatabaseSetup","href":"server/EchoHub.Server.Setup.DatabaseSetup.html","topicHref":"server/EchoHub.Server.Setup.DatabaseSetup.html","topicUid":"EchoHub.Server.Setup.DatabaseSetup","type":"Class"},{"name":"FirstRunSetup","href":"server/EchoHub.Server.Setup.FirstRunSetup.html","topicHref":"server/EchoHub.Server.Setup.FirstRunSetup.html","topicUid":"EchoHub.Server.Setup.FirstRunSetup","type":"Class"}]}]},{"name":"Articles","includedFrom":"~/api/server-articles/toc.yml","items":[{"name":"Overview","href":"server-articles/index.html","topicHref":"server-articles/index.html"}]}]},{"name":"Server.Irc","items":[{"name":"API Reference","includedFrom":"~/_api_meta/server-irc/toc.yml","items":[{"name":"EchoHub.Server.Irc","href":"server-irc/EchoHub.Server.Irc.html","topicHref":"server-irc/EchoHub.Server.Irc.html","topicUid":"EchoHub.Server.Irc","type":"Namespace","items":[{"name":"IrcBroadcaster","href":"server-irc/EchoHub.Server.Irc.IrcBroadcaster.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcBroadcaster.html","topicUid":"EchoHub.Server.Irc.IrcBroadcaster","type":"Class"},{"name":"IrcClientConnection","href":"server-irc/EchoHub.Server.Irc.IrcClientConnection.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcClientConnection.html","topicUid":"EchoHub.Server.Irc.IrcClientConnection","type":"Class"},{"name":"IrcCommandHandler","href":"server-irc/EchoHub.Server.Irc.IrcCommandHandler.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcCommandHandler.html","topicUid":"EchoHub.Server.Irc.IrcCommandHandler","type":"Class"},{"name":"IrcGatewayService","href":"server-irc/EchoHub.Server.Irc.IrcGatewayService.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcGatewayService.html","topicUid":"EchoHub.Server.Irc.IrcGatewayService","type":"Class"},{"name":"IrcMessage","href":"server-irc/EchoHub.Server.Irc.IrcMessage.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcMessage.html","topicUid":"EchoHub.Server.Irc.IrcMessage","type":"Class"},{"name":"IrcMessageFormatter","href":"server-irc/EchoHub.Server.Irc.IrcMessageFormatter.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcMessageFormatter.html","topicUid":"EchoHub.Server.Irc.IrcMessageFormatter","type":"Class"},{"name":"IrcNumericReply","href":"server-irc/EchoHub.Server.Irc.IrcNumericReply.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcNumericReply.html","topicUid":"EchoHub.Server.Irc.IrcNumericReply","type":"Class"},{"name":"IrcOptions","href":"server-irc/EchoHub.Server.Irc.IrcOptions.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcOptions.html","topicUid":"EchoHub.Server.Irc.IrcOptions","type":"Class"},{"name":"IrcServiceExtensions","href":"server-irc/EchoHub.Server.Irc.IrcServiceExtensions.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcServiceExtensions.html","topicUid":"EchoHub.Server.Irc.IrcServiceExtensions","type":"Class"}]}]}]}]} +{"items":[{"name":"Client","items":[{"name":"API Reference","includedFrom":"~/_api_meta/client/toc.yml","items":[{"name":"EchoHub.Client","href":"client/EchoHub.Client.html","topicHref":"client/EchoHub.Client.html","topicUid":"EchoHub.Client","type":"Namespace","items":[{"name":"AppOrchestrator","href":"client/EchoHub.Client.AppOrchestrator.html","topicHref":"client/EchoHub.Client.AppOrchestrator.html","topicUid":"EchoHub.Client.AppOrchestrator","type":"Class"}]},{"name":"EchoHub.Client.Commands","href":"client/EchoHub.Client.Commands.html","topicHref":"client/EchoHub.Client.Commands.html","topicUid":"EchoHub.Client.Commands","type":"Namespace","items":[{"name":"CommandHandler","href":"client/EchoHub.Client.Commands.CommandHandler.html","topicHref":"client/EchoHub.Client.Commands.CommandHandler.html","topicUid":"EchoHub.Client.Commands.CommandHandler","type":"Class"},{"name":"CommandResult","href":"client/EchoHub.Client.Commands.CommandResult.html","topicHref":"client/EchoHub.Client.Commands.CommandResult.html","topicUid":"EchoHub.Client.Commands.CommandResult","type":"Class"}]},{"name":"EchoHub.Client.Config","href":"client/EchoHub.Client.Config.html","topicHref":"client/EchoHub.Client.Config.html","topicUid":"EchoHub.Client.Config","type":"Namespace","items":[{"name":"AccountPreset","href":"client/EchoHub.Client.Config.AccountPreset.html","topicHref":"client/EchoHub.Client.Config.AccountPreset.html","topicUid":"EchoHub.Client.Config.AccountPreset","type":"Class"},{"name":"ClientConfig","href":"client/EchoHub.Client.Config.ClientConfig.html","topicHref":"client/EchoHub.Client.Config.ClientConfig.html","topicUid":"EchoHub.Client.Config.ClientConfig","type":"Class"},{"name":"ConfigManager","href":"client/EchoHub.Client.Config.ConfigManager.html","topicHref":"client/EchoHub.Client.Config.ConfigManager.html","topicUid":"EchoHub.Client.Config.ConfigManager","type":"Class"},{"name":"NotificationConfig","href":"client/EchoHub.Client.Config.NotificationConfig.html","topicHref":"client/EchoHub.Client.Config.NotificationConfig.html","topicUid":"EchoHub.Client.Config.NotificationConfig","type":"Class"},{"name":"SavedServer","href":"client/EchoHub.Client.Config.SavedServer.html","topicHref":"client/EchoHub.Client.Config.SavedServer.html","topicUid":"EchoHub.Client.Config.SavedServer","type":"Class"}]},{"name":"EchoHub.Client.Services","href":"client/EchoHub.Client.Services.html","topicHref":"client/EchoHub.Client.Services.html","topicUid":"EchoHub.Client.Services","type":"Namespace","items":[{"name":"ApiClient","href":"client/EchoHub.Client.Services.ApiClient.html","topicHref":"client/EchoHub.Client.Services.ApiClient.html","topicUid":"EchoHub.Client.Services.ApiClient","type":"Class"},{"name":"AsyncRunner","href":"client/EchoHub.Client.Services.AsyncRunner.html","topicHref":"client/EchoHub.Client.Services.AsyncRunner.html","topicUid":"EchoHub.Client.Services.AsyncRunner","type":"Class"},{"name":"AudioPlaybackService","href":"client/EchoHub.Client.Services.AudioPlaybackService.html","topicHref":"client/EchoHub.Client.Services.AudioPlaybackService.html","topicUid":"EchoHub.Client.Services.AudioPlaybackService","type":"Class"},{"name":"BackupInfo","href":"client/EchoHub.Client.Services.BackupInfo.html","topicHref":"client/EchoHub.Client.Services.BackupInfo.html","topicUid":"EchoHub.Client.Services.BackupInfo","type":"Class"},{"name":"ChannelPasswordRequiredException","href":"client/EchoHub.Client.Services.ChannelPasswordRequiredException.html","topicHref":"client/EchoHub.Client.Services.ChannelPasswordRequiredException.html","topicUid":"EchoHub.Client.Services.ChannelPasswordRequiredException","type":"Class"},{"name":"ClientEncryptionService","href":"client/EchoHub.Client.Services.ClientEncryptionService.html","topicHref":"client/EchoHub.Client.Services.ClientEncryptionService.html","topicUid":"EchoHub.Client.Services.ClientEncryptionService","type":"Class"},{"name":"ClipboardFiles","href":"client/EchoHub.Client.Services.ClipboardFiles.html","topicHref":"client/EchoHub.Client.Services.ClipboardFiles.html","topicUid":"EchoHub.Client.Services.ClipboardFiles","type":"Class"},{"name":"EchoHubConnection","href":"client/EchoHub.Client.Services.EchoHubConnection.html","topicHref":"client/EchoHub.Client.Services.EchoHubConnection.html","topicUid":"EchoHub.Client.Services.EchoHubConnection","type":"Class"},{"name":"FolderPickResult","href":"client/EchoHub.Client.Services.FolderPickResult.html","topicHref":"client/EchoHub.Client.Services.FolderPickResult.html","topicUid":"EchoHub.Client.Services.FolderPickResult","type":"Class"},{"name":"JoinOutcome","href":"client/EchoHub.Client.Services.JoinOutcome.html","topicHref":"client/EchoHub.Client.Services.JoinOutcome.html","topicUid":"EchoHub.Client.Services.JoinOutcome","type":"Class"},{"name":"NativeFolderPicker","href":"client/EchoHub.Client.Services.NativeFolderPicker.html","topicHref":"client/EchoHub.Client.Services.NativeFolderPicker.html","topicUid":"EchoHub.Client.Services.NativeFolderPicker","type":"Class"},{"name":"NotificationSoundService","href":"client/EchoHub.Client.Services.NotificationSoundService.html","topicHref":"client/EchoHub.Client.Services.NotificationSoundService.html","topicUid":"EchoHub.Client.Services.NotificationSoundService","type":"Class"},{"name":"OutgoingAttachment","href":"client/EchoHub.Client.Services.OutgoingAttachment.html","topicHref":"client/EchoHub.Client.Services.OutgoingAttachment.html","topicUid":"EchoHub.Client.Services.OutgoingAttachment","type":"Class"},{"name":"PathSetup","href":"client/EchoHub.Client.Services.PathSetup.html","topicHref":"client/EchoHub.Client.Services.PathSetup.html","topicUid":"EchoHub.Client.Services.PathSetup","type":"Class"},{"name":"PickerOutcome","href":"client/EchoHub.Client.Services.PickerOutcome.html","topicHref":"client/EchoHub.Client.Services.PickerOutcome.html","topicUid":"EchoHub.Client.Services.PickerOutcome","type":"Enum"},{"name":"RoomKeyStore","href":"client/EchoHub.Client.Services.RoomKeyStore.html","topicHref":"client/EchoHub.Client.Services.RoomKeyStore.html","topicUid":"EchoHub.Client.Services.RoomKeyStore","type":"Class"},{"name":"UpdateBackupService","href":"client/EchoHub.Client.Services.UpdateBackupService.html","topicHref":"client/EchoHub.Client.Services.UpdateBackupService.html","topicUid":"EchoHub.Client.Services.UpdateBackupService","type":"Class"},{"name":"UpdateChecker","href":"client/EchoHub.Client.Services.UpdateChecker.html","topicHref":"client/EchoHub.Client.Services.UpdateChecker.html","topicUid":"EchoHub.Client.Services.UpdateChecker","type":"Class"}]},{"name":"EchoHub.Client.Themes","href":"client/EchoHub.Client.Themes.html","topicHref":"client/EchoHub.Client.Themes.html","topicUid":"EchoHub.Client.Themes","type":"Namespace","items":[{"name":"Theme","href":"client/EchoHub.Client.Themes.Theme.html","topicHref":"client/EchoHub.Client.Themes.Theme.html","topicUid":"EchoHub.Client.Themes.Theme","type":"Class"},{"name":"ThemeColors","href":"client/EchoHub.Client.Themes.ThemeColors.html","topicHref":"client/EchoHub.Client.Themes.ThemeColors.html","topicUid":"EchoHub.Client.Themes.ThemeColors","type":"Class"},{"name":"ThemeManager","href":"client/EchoHub.Client.Themes.ThemeManager.html","topicHref":"client/EchoHub.Client.Themes.ThemeManager.html","topicUid":"EchoHub.Client.Themes.ThemeManager","type":"Class"}]},{"name":"EchoHub.Client.UI","href":"client/EchoHub.Client.UI.html","topicHref":"client/EchoHub.Client.UI.html","topicUid":"EchoHub.Client.UI","type":"Namespace","items":[{"name":"MainWindow","href":"client/EchoHub.Client.UI.MainWindow.html","topicHref":"client/EchoHub.Client.UI.MainWindow.html","topicUid":"EchoHub.Client.UI.MainWindow","type":"Class"}]},{"name":"EchoHub.Client.UI.Chat","href":"client/EchoHub.Client.UI.Chat.html","topicHref":"client/EchoHub.Client.UI.Chat.html","topicUid":"EchoHub.Client.UI.Chat","type":"Namespace","items":[{"name":"ChatColors","href":"client/EchoHub.Client.UI.Chat.ChatColors.html","topicHref":"client/EchoHub.Client.UI.Chat.ChatColors.html","topicUid":"EchoHub.Client.UI.Chat.ChatColors","type":"Class"},{"name":"ChatLine","href":"client/EchoHub.Client.UI.Chat.ChatLine.html","topicHref":"client/EchoHub.Client.UI.Chat.ChatLine.html","topicUid":"EchoHub.Client.UI.Chat.ChatLine","type":"Class"},{"name":"ChatListSource","href":"client/EchoHub.Client.UI.Chat.ChatListSource.html","topicHref":"client/EchoHub.Client.UI.Chat.ChatListSource.html","topicUid":"EchoHub.Client.UI.Chat.ChatListSource","type":"Class"},{"name":"ChatMessageManager","href":"client/EchoHub.Client.UI.Chat.ChatMessageManager.html","topicHref":"client/EchoHub.Client.UI.Chat.ChatMessageManager.html","topicUid":"EchoHub.Client.UI.Chat.ChatMessageManager","type":"Class"},{"name":"ChatSegment","href":"client/EchoHub.Client.UI.Chat.ChatSegment.html","topicHref":"client/EchoHub.Client.UI.Chat.ChatSegment.html","topicUid":"EchoHub.Client.UI.Chat.ChatSegment","type":"Class"}]},{"name":"EchoHub.Client.UI.Dialogs","href":"client/EchoHub.Client.UI.Dialogs.html","topicHref":"client/EchoHub.Client.UI.Dialogs.html","topicUid":"EchoHub.Client.UI.Dialogs","type":"Namespace","items":[{"name":"AudioPlayerDialog","href":"client/EchoHub.Client.UI.Dialogs.AudioPlayerDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.AudioPlayerDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.AudioPlayerDialog","type":"Class"},{"name":"ChannelPasswordDialog","href":"client/EchoHub.Client.UI.Dialogs.ChannelPasswordDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ChannelPasswordDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.ChannelPasswordDialog","type":"Class"},{"name":"ConnectDialog","href":"client/EchoHub.Client.UI.Dialogs.ConnectDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ConnectDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.ConnectDialog","type":"Class"},{"name":"ConnectDialogResult","href":"client/EchoHub.Client.UI.Dialogs.ConnectDialogResult.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ConnectDialogResult.html","topicUid":"EchoHub.Client.UI.Dialogs.ConnectDialogResult","type":"Class"},{"name":"CreateChannelDialog","href":"client/EchoHub.Client.UI.Dialogs.CreateChannelDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.CreateChannelDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.CreateChannelDialog","type":"Class"},{"name":"CreateChannelResult","href":"client/EchoHub.Client.UI.Dialogs.CreateChannelResult.html","topicHref":"client/EchoHub.Client.UI.Dialogs.CreateChannelResult.html","topicUid":"EchoHub.Client.UI.Dialogs.CreateChannelResult","type":"Class"},{"name":"ProfileAction","href":"client/EchoHub.Client.UI.Dialogs.ProfileAction.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ProfileAction.html","topicUid":"EchoHub.Client.UI.Dialogs.ProfileAction","type":"Enum"},{"name":"ProfileEditDialog","href":"client/EchoHub.Client.UI.Dialogs.ProfileEditDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ProfileEditDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.ProfileEditDialog","type":"Class"},{"name":"ProfileEditResult","href":"client/EchoHub.Client.UI.Dialogs.ProfileEditResult.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ProfileEditResult.html","topicUid":"EchoHub.Client.UI.Dialogs.ProfileEditResult","type":"Class"},{"name":"ProfileViewDialog","href":"client/EchoHub.Client.UI.Dialogs.ProfileViewDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ProfileViewDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.ProfileViewDialog","type":"Class"},{"name":"SearchDialog","href":"client/EchoHub.Client.UI.Dialogs.SearchDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.SearchDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.SearchDialog","type":"Class"},{"name":"SearchResult","href":"client/EchoHub.Client.UI.Dialogs.SearchResult.html","topicHref":"client/EchoHub.Client.UI.Dialogs.SearchResult.html","topicUid":"EchoHub.Client.UI.Dialogs.SearchResult","type":"Class"},{"name":"SearchResultType","href":"client/EchoHub.Client.UI.Dialogs.SearchResultType.html","topicHref":"client/EchoHub.Client.UI.Dialogs.SearchResultType.html","topicUid":"EchoHub.Client.UI.Dialogs.SearchResultType","type":"Enum"},{"name":"StatusDialog","href":"client/EchoHub.Client.UI.Dialogs.StatusDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.StatusDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.StatusDialog","type":"Class"},{"name":"StatusDialogResult","href":"client/EchoHub.Client.UI.Dialogs.StatusDialogResult.html","topicHref":"client/EchoHub.Client.UI.Dialogs.StatusDialogResult.html","topicUid":"EchoHub.Client.UI.Dialogs.StatusDialogResult","type":"Class"},{"name":"UpdateConfirmDialog","href":"client/EchoHub.Client.UI.Dialogs.UpdateConfirmDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.UpdateConfirmDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.UpdateConfirmDialog","type":"Class"}]},{"name":"EchoHub.Client.UI.Helpers","href":"client/EchoHub.Client.UI.Helpers.html","topicHref":"client/EchoHub.Client.UI.Helpers.html","topicUid":"EchoHub.Client.UI.Helpers","type":"Namespace","items":[{"name":"DroppedFileParser","href":"client/EchoHub.Client.UI.Helpers.DroppedFileParser.html","topicHref":"client/EchoHub.Client.UI.Helpers.DroppedFileParser.html","topicUid":"EchoHub.Client.UI.Helpers.DroppedFileParser","type":"Class"},{"name":"EmojiHelper","href":"client/EchoHub.Client.UI.Helpers.EmojiHelper.html","topicHref":"client/EchoHub.Client.UI.Helpers.EmojiHelper.html","topicUid":"EchoHub.Client.UI.Helpers.EmojiHelper","type":"Class"},{"name":"HexColorHelper","href":"client/EchoHub.Client.UI.Helpers.HexColorHelper.html","topicHref":"client/EchoHub.Client.UI.Helpers.HexColorHelper.html","topicUid":"EchoHub.Client.UI.Helpers.HexColorHelper","type":"Class"},{"name":"NickColorHelper","href":"client/EchoHub.Client.UI.Helpers.NickColorHelper.html","topicHref":"client/EchoHub.Client.UI.Helpers.NickColorHelper.html","topicUid":"EchoHub.Client.UI.Helpers.NickColorHelper","type":"Class"}]},{"name":"EchoHub.Client.UI.ListSources","href":"client/EchoHub.Client.UI.ListSources.html","topicHref":"client/EchoHub.Client.UI.ListSources.html","topicUid":"EchoHub.Client.UI.ListSources","type":"Namespace","items":[{"name":"ChannelListSource","href":"client/EchoHub.Client.UI.ListSources.ChannelListSource.html","topicHref":"client/EchoHub.Client.UI.ListSources.ChannelListSource.html","topicUid":"EchoHub.Client.UI.ListSources.ChannelListSource","type":"Class"},{"name":"SearchListSource","href":"client/EchoHub.Client.UI.ListSources.SearchListSource.html","topicHref":"client/EchoHub.Client.UI.ListSources.SearchListSource.html","topicUid":"EchoHub.Client.UI.ListSources.SearchListSource","type":"Class"},{"name":"UserListSource","href":"client/EchoHub.Client.UI.ListSources.UserListSource.html","topicHref":"client/EchoHub.Client.UI.ListSources.UserListSource.html","topicUid":"EchoHub.Client.UI.ListSources.UserListSource","type":"Class"}]}]},{"name":"Articles","includedFrom":"~/api/client-articles/toc.yml","items":[{"name":"Overview","href":"client-articles/index.html","topicHref":"client-articles/index.html"}]}]},{"name":"Core","items":[{"name":"API Reference","includedFrom":"~/_api_meta/core/toc.yml","items":[{"name":"EchoHub.Core.Constants","href":"core/EchoHub.Core.Constants.html","topicHref":"core/EchoHub.Core.Constants.html","topicUid":"EchoHub.Core.Constants","type":"Namespace","items":[{"name":"HubConstants","href":"core/EchoHub.Core.Constants.HubConstants.html","topicHref":"core/EchoHub.Core.Constants.HubConstants.html","topicUid":"EchoHub.Core.Constants.HubConstants","type":"Class"},{"name":"ValidationConstants","href":"core/EchoHub.Core.Constants.ValidationConstants.html","topicHref":"core/EchoHub.Core.Constants.ValidationConstants.html","topicUid":"EchoHub.Core.Constants.ValidationConstants","type":"Class"}]},{"name":"EchoHub.Core.Contracts","href":"core/EchoHub.Core.Contracts.html","topicHref":"core/EchoHub.Core.Contracts.html","topicUid":"EchoHub.Core.Contracts","type":"Namespace","items":[{"name":"ChannelListItem","href":"core/EchoHub.Core.Contracts.ChannelListItem.html","topicHref":"core/EchoHub.Core.Contracts.ChannelListItem.html","topicUid":"EchoHub.Core.Contracts.ChannelListItem","type":"Class"},{"name":"IChannelService","href":"core/EchoHub.Core.Contracts.IChannelService.html","topicHref":"core/EchoHub.Core.Contracts.IChannelService.html","topicUid":"EchoHub.Core.Contracts.IChannelService","type":"Interface"},{"name":"IChatBroadcaster","href":"core/EchoHub.Core.Contracts.IChatBroadcaster.html","topicHref":"core/EchoHub.Core.Contracts.IChatBroadcaster.html","topicUid":"EchoHub.Core.Contracts.IChatBroadcaster","type":"Interface"},{"name":"IChatService","href":"core/EchoHub.Core.Contracts.IChatService.html","topicHref":"core/EchoHub.Core.Contracts.IChatService.html","topicUid":"EchoHub.Core.Contracts.IChatService","type":"Interface"},{"name":"IEchoHubClient","href":"core/EchoHub.Core.Contracts.IEchoHubClient.html","topicHref":"core/EchoHub.Core.Contracts.IEchoHubClient.html","topicUid":"EchoHub.Core.Contracts.IEchoHubClient","type":"Interface"},{"name":"IMessageEncryptionService","href":"core/EchoHub.Core.Contracts.IMessageEncryptionService.html","topicHref":"core/EchoHub.Core.Contracts.IMessageEncryptionService.html","topicUid":"EchoHub.Core.Contracts.IMessageEncryptionService","type":"Interface"},{"name":"IUserService","href":"core/EchoHub.Core.Contracts.IUserService.html","topicHref":"core/EchoHub.Core.Contracts.IUserService.html","topicUid":"EchoHub.Core.Contracts.IUserService","type":"Interface"}]},{"name":"EchoHub.Core.DTOs","href":"core/EchoHub.Core.DTOs.html","topicHref":"core/EchoHub.Core.DTOs.html","topicUid":"EchoHub.Core.DTOs","type":"Namespace","items":[{"name":"ApiResponse","href":"core/EchoHub.Core.DTOs.ApiResponse.html","topicHref":"core/EchoHub.Core.DTOs.ApiResponse.html","topicUid":"EchoHub.Core.DTOs.ApiResponse","type":"Class"},{"name":"ApiResponse","href":"core/EchoHub.Core.DTOs.ApiResponse-1.html","topicHref":"core/EchoHub.Core.DTOs.ApiResponse-1.html","topicUid":"EchoHub.Core.DTOs.ApiResponse`1","type":"Class"},{"name":"AssignRoleRequest","href":"core/EchoHub.Core.DTOs.AssignRoleRequest.html","topicHref":"core/EchoHub.Core.DTOs.AssignRoleRequest.html","topicUid":"EchoHub.Core.DTOs.AssignRoleRequest","type":"Class"},{"name":"AttachmentDto","href":"core/EchoHub.Core.DTOs.AttachmentDto.html","topicHref":"core/EchoHub.Core.DTOs.AttachmentDto.html","topicUid":"EchoHub.Core.DTOs.AttachmentDto","type":"Class"},{"name":"AvatarUploadResponse","href":"core/EchoHub.Core.DTOs.AvatarUploadResponse.html","topicHref":"core/EchoHub.Core.DTOs.AvatarUploadResponse.html","topicUid":"EchoHub.Core.DTOs.AvatarUploadResponse","type":"Class"},{"name":"BanRequest","href":"core/EchoHub.Core.DTOs.BanRequest.html","topicHref":"core/EchoHub.Core.DTOs.BanRequest.html","topicUid":"EchoHub.Core.DTOs.BanRequest","type":"Class"},{"name":"ChannelCryptoDto","href":"core/EchoHub.Core.DTOs.ChannelCryptoDto.html","topicHref":"core/EchoHub.Core.DTOs.ChannelCryptoDto.html","topicUid":"EchoHub.Core.DTOs.ChannelCryptoDto","type":"Class"},{"name":"ChannelDto","href":"core/EchoHub.Core.DTOs.ChannelDto.html","topicHref":"core/EchoHub.Core.DTOs.ChannelDto.html","topicUid":"EchoHub.Core.DTOs.ChannelDto","type":"Class"},{"name":"ChannelError","href":"core/EchoHub.Core.DTOs.ChannelError.html","topicHref":"core/EchoHub.Core.DTOs.ChannelError.html","topicUid":"EchoHub.Core.DTOs.ChannelError","type":"Enum"},{"name":"ChannelMetaDto","href":"core/EchoHub.Core.DTOs.ChannelMetaDto.html","topicHref":"core/EchoHub.Core.DTOs.ChannelMetaDto.html","topicUid":"EchoHub.Core.DTOs.ChannelMetaDto","type":"Class"},{"name":"ChannelOperationResult","href":"core/EchoHub.Core.DTOs.ChannelOperationResult.html","topicHref":"core/EchoHub.Core.DTOs.ChannelOperationResult.html","topicUid":"EchoHub.Core.DTOs.ChannelOperationResult","type":"Class"},{"name":"CreateChannelRequest","href":"core/EchoHub.Core.DTOs.CreateChannelRequest.html","topicHref":"core/EchoHub.Core.DTOs.CreateChannelRequest.html","topicUid":"EchoHub.Core.DTOs.CreateChannelRequest","type":"Class"},{"name":"EmbedDto","href":"core/EchoHub.Core.DTOs.EmbedDto.html","topicHref":"core/EchoHub.Core.DTOs.EmbedDto.html","topicUid":"EchoHub.Core.DTOs.EmbedDto","type":"Class"},{"name":"EncryptionKeyResponse","href":"core/EchoHub.Core.DTOs.EncryptionKeyResponse.html","topicHref":"core/EchoHub.Core.DTOs.EncryptionKeyResponse.html","topicUid":"EchoHub.Core.DTOs.EncryptionKeyResponse","type":"Class"},{"name":"ErrorResponse","href":"core/EchoHub.Core.DTOs.ErrorResponse.html","topicHref":"core/EchoHub.Core.DTOs.ErrorResponse.html","topicUid":"EchoHub.Core.DTOs.ErrorResponse","type":"Class"},{"name":"JoinChannelResult","href":"core/EchoHub.Core.DTOs.JoinChannelResult.html","topicHref":"core/EchoHub.Core.DTOs.JoinChannelResult.html","topicUid":"EchoHub.Core.DTOs.JoinChannelResult","type":"Class"},{"name":"KickRequest","href":"core/EchoHub.Core.DTOs.KickRequest.html","topicHref":"core/EchoHub.Core.DTOs.KickRequest.html","topicUid":"EchoHub.Core.DTOs.KickRequest","type":"Class"},{"name":"LoginRequest","href":"core/EchoHub.Core.DTOs.LoginRequest.html","topicHref":"core/EchoHub.Core.DTOs.LoginRequest.html","topicUid":"EchoHub.Core.DTOs.LoginRequest","type":"Class"},{"name":"LoginResponse","href":"core/EchoHub.Core.DTOs.LoginResponse.html","topicHref":"core/EchoHub.Core.DTOs.LoginResponse.html","topicUid":"EchoHub.Core.DTOs.LoginResponse","type":"Class"},{"name":"MessageDto","href":"core/EchoHub.Core.DTOs.MessageDto.html","topicHref":"core/EchoHub.Core.DTOs.MessageDto.html","topicUid":"EchoHub.Core.DTOs.MessageDto","type":"Class"},{"name":"MuteRequest","href":"core/EchoHub.Core.DTOs.MuteRequest.html","topicHref":"core/EchoHub.Core.DTOs.MuteRequest.html","topicUid":"EchoHub.Core.DTOs.MuteRequest","type":"Class"},{"name":"PaginatedResponse","href":"core/EchoHub.Core.DTOs.PaginatedResponse-1.html","topicHref":"core/EchoHub.Core.DTOs.PaginatedResponse-1.html","topicUid":"EchoHub.Core.DTOs.PaginatedResponse`1","type":"Class"},{"name":"RefreshRequest","href":"core/EchoHub.Core.DTOs.RefreshRequest.html","topicHref":"core/EchoHub.Core.DTOs.RefreshRequest.html","topicUid":"EchoHub.Core.DTOs.RefreshRequest","type":"Class"},{"name":"RegisterRequest","href":"core/EchoHub.Core.DTOs.RegisterRequest.html","topicHref":"core/EchoHub.Core.DTOs.RegisterRequest.html","topicUid":"EchoHub.Core.DTOs.RegisterRequest","type":"Class"},{"name":"RekeyChannelRequest","href":"core/EchoHub.Core.DTOs.RekeyChannelRequest.html","topicHref":"core/EchoHub.Core.DTOs.RekeyChannelRequest.html","topicUid":"EchoHub.Core.DTOs.RekeyChannelRequest","type":"Class"},{"name":"SendMessageRequest","href":"core/EchoHub.Core.DTOs.SendMessageRequest.html","topicHref":"core/EchoHub.Core.DTOs.SendMessageRequest.html","topicUid":"EchoHub.Core.DTOs.SendMessageRequest","type":"Class"},{"name":"SendUrlRequest","href":"core/EchoHub.Core.DTOs.SendUrlRequest.html","topicHref":"core/EchoHub.Core.DTOs.SendUrlRequest.html","topicUid":"EchoHub.Core.DTOs.SendUrlRequest","type":"Class"},{"name":"ServerStatusDto","href":"core/EchoHub.Core.DTOs.ServerStatusDto.html","topicHref":"core/EchoHub.Core.DTOs.ServerStatusDto.html","topicUid":"EchoHub.Core.DTOs.ServerStatusDto","type":"Class"},{"name":"UpdateProfileRequest","href":"core/EchoHub.Core.DTOs.UpdateProfileRequest.html","topicHref":"core/EchoHub.Core.DTOs.UpdateProfileRequest.html","topicUid":"EchoHub.Core.DTOs.UpdateProfileRequest","type":"Class"},{"name":"UpdateStatusRequest","href":"core/EchoHub.Core.DTOs.UpdateStatusRequest.html","topicHref":"core/EchoHub.Core.DTOs.UpdateStatusRequest.html","topicUid":"EchoHub.Core.DTOs.UpdateStatusRequest","type":"Class"},{"name":"UpdateTopicRequest","href":"core/EchoHub.Core.DTOs.UpdateTopicRequest.html","topicHref":"core/EchoHub.Core.DTOs.UpdateTopicRequest.html","topicUid":"EchoHub.Core.DTOs.UpdateTopicRequest","type":"Class"},{"name":"UserDto","href":"core/EchoHub.Core.DTOs.UserDto.html","topicHref":"core/EchoHub.Core.DTOs.UserDto.html","topicUid":"EchoHub.Core.DTOs.UserDto","type":"Class"},{"name":"UserError","href":"core/EchoHub.Core.DTOs.UserError.html","topicHref":"core/EchoHub.Core.DTOs.UserError.html","topicUid":"EchoHub.Core.DTOs.UserError","type":"Enum"},{"name":"UserOperationResult","href":"core/EchoHub.Core.DTOs.UserOperationResult.html","topicHref":"core/EchoHub.Core.DTOs.UserOperationResult.html","topicUid":"EchoHub.Core.DTOs.UserOperationResult","type":"Class"},{"name":"UserPresenceDto","href":"core/EchoHub.Core.DTOs.UserPresenceDto.html","topicHref":"core/EchoHub.Core.DTOs.UserPresenceDto.html","topicUid":"EchoHub.Core.DTOs.UserPresenceDto","type":"Class"},{"name":"UserProfileDto","href":"core/EchoHub.Core.DTOs.UserProfileDto.html","topicHref":"core/EchoHub.Core.DTOs.UserProfileDto.html","topicUid":"EchoHub.Core.DTOs.UserProfileDto","type":"Class"}]},{"name":"EchoHub.Core.Models","href":"core/EchoHub.Core.Models.html","topicHref":"core/EchoHub.Core.Models.html","topicUid":"EchoHub.Core.Models","type":"Namespace","items":[{"name":"Attachment","href":"core/EchoHub.Core.Models.Attachment.html","topicHref":"core/EchoHub.Core.Models.Attachment.html","topicUid":"EchoHub.Core.Models.Attachment","type":"Class"},{"name":"AttachmentKind","href":"core/EchoHub.Core.Models.AttachmentKind.html","topicHref":"core/EchoHub.Core.Models.AttachmentKind.html","topicUid":"EchoHub.Core.Models.AttachmentKind","type":"Enum"},{"name":"Channel","href":"core/EchoHub.Core.Models.Channel.html","topicHref":"core/EchoHub.Core.Models.Channel.html","topicUid":"EchoHub.Core.Models.Channel","type":"Class"},{"name":"ChannelMembership","href":"core/EchoHub.Core.Models.ChannelMembership.html","topicHref":"core/EchoHub.Core.Models.ChannelMembership.html","topicUid":"EchoHub.Core.Models.ChannelMembership","type":"Class"},{"name":"Message","href":"core/EchoHub.Core.Models.Message.html","topicHref":"core/EchoHub.Core.Models.Message.html","topicUid":"EchoHub.Core.Models.Message","type":"Class"},{"name":"MessageType","href":"core/EchoHub.Core.Models.MessageType.html","topicHref":"core/EchoHub.Core.Models.MessageType.html","topicUid":"EchoHub.Core.Models.MessageType","type":"Enum"},{"name":"RefreshToken","href":"core/EchoHub.Core.Models.RefreshToken.html","topicHref":"core/EchoHub.Core.Models.RefreshToken.html","topicUid":"EchoHub.Core.Models.RefreshToken","type":"Class"},{"name":"ServerRole","href":"core/EchoHub.Core.Models.ServerRole.html","topicHref":"core/EchoHub.Core.Models.ServerRole.html","topicUid":"EchoHub.Core.Models.ServerRole","type":"Enum"},{"name":"User","href":"core/EchoHub.Core.Models.User.html","topicHref":"core/EchoHub.Core.Models.User.html","topicUid":"EchoHub.Core.Models.User","type":"Class"},{"name":"UserStatus","href":"core/EchoHub.Core.Models.UserStatus.html","topicHref":"core/EchoHub.Core.Models.UserStatus.html","topicUid":"EchoHub.Core.Models.UserStatus","type":"Enum"}]},{"name":"EchoHub.Core.Security","href":"core/EchoHub.Core.Security.html","topicHref":"core/EchoHub.Core.Security.html","topicUid":"EchoHub.Core.Security","type":"Namespace","items":[{"name":"RoomCrypto","href":"core/EchoHub.Core.Security.RoomCrypto.html","topicHref":"core/EchoHub.Core.Security.RoomCrypto.html","topicUid":"EchoHub.Core.Security.RoomCrypto","type":"Class"},{"name":"RoomCrypto.DerivedKeys","href":"core/EchoHub.Core.Security.RoomCrypto.DerivedKeys.html","topicHref":"core/EchoHub.Core.Security.RoomCrypto.DerivedKeys.html","topicUid":"EchoHub.Core.Security.RoomCrypto.DerivedKeys","type":"Class"}]},{"name":"EchoHub.Core.Services","href":"core/EchoHub.Core.Services.html","topicHref":"core/EchoHub.Core.Services.html","topicUid":"EchoHub.Core.Services","type":"Namespace","items":[{"name":"FileValidationHelper","href":"core/EchoHub.Core.Services.FileValidationHelper.html","topicHref":"core/EchoHub.Core.Services.FileValidationHelper.html","topicUid":"EchoHub.Core.Services.FileValidationHelper","type":"Class"},{"name":"ImageToAsciiService","href":"core/EchoHub.Core.Services.ImageToAsciiService.html","topicHref":"core/EchoHub.Core.Services.ImageToAsciiService.html","topicUid":"EchoHub.Core.Services.ImageToAsciiService","type":"Class"}]}]},{"name":"Articles","includedFrom":"~/api/core-articles/toc.yml","items":[{"name":"Overview","href":"core-articles/index.html","topicHref":"core-articles/index.html"}]}]},{"name":"Server","items":[{"name":"API Reference","includedFrom":"~/_api_meta/server/toc.yml","items":[{"name":"EchoHub.Server.Auth","href":"server/EchoHub.Server.Auth.html","topicHref":"server/EchoHub.Server.Auth.html","topicUid":"EchoHub.Server.Auth","type":"Namespace","items":[{"name":"JwtTokenService","href":"server/EchoHub.Server.Auth.JwtTokenService.html","topicHref":"server/EchoHub.Server.Auth.JwtTokenService.html","topicUid":"EchoHub.Server.Auth.JwtTokenService","type":"Class"}]},{"name":"EchoHub.Server.Config","href":"server/EchoHub.Server.Config.html","topicHref":"server/EchoHub.Server.Config.html","topicUid":"EchoHub.Server.Config","type":"Namespace","items":[{"name":"UploadLimits","href":"server/EchoHub.Server.Config.UploadLimits.html","topicHref":"server/EchoHub.Server.Config.UploadLimits.html","topicUid":"EchoHub.Server.Config.UploadLimits","type":"Class"}]},{"name":"EchoHub.Server.Controllers","href":"server/EchoHub.Server.Controllers.html","topicHref":"server/EchoHub.Server.Controllers.html","topicUid":"EchoHub.Server.Controllers","type":"Namespace","items":[{"name":"AuthController","href":"server/EchoHub.Server.Controllers.AuthController.html","topicHref":"server/EchoHub.Server.Controllers.AuthController.html","topicUid":"EchoHub.Server.Controllers.AuthController","type":"Class"},{"name":"ChannelsController","href":"server/EchoHub.Server.Controllers.ChannelsController.html","topicHref":"server/EchoHub.Server.Controllers.ChannelsController.html","topicUid":"EchoHub.Server.Controllers.ChannelsController","type":"Class"},{"name":"FilesController","href":"server/EchoHub.Server.Controllers.FilesController.html","topicHref":"server/EchoHub.Server.Controllers.FilesController.html","topicUid":"EchoHub.Server.Controllers.FilesController","type":"Class"},{"name":"ModerationController","href":"server/EchoHub.Server.Controllers.ModerationController.html","topicHref":"server/EchoHub.Server.Controllers.ModerationController.html","topicUid":"EchoHub.Server.Controllers.ModerationController","type":"Class"},{"name":"ServerController","href":"server/EchoHub.Server.Controllers.ServerController.html","topicHref":"server/EchoHub.Server.Controllers.ServerController.html","topicUid":"EchoHub.Server.Controllers.ServerController","type":"Class"},{"name":"UsersController","href":"server/EchoHub.Server.Controllers.UsersController.html","topicHref":"server/EchoHub.Server.Controllers.UsersController.html","topicUid":"EchoHub.Server.Controllers.UsersController","type":"Class"}]},{"name":"EchoHub.Server.Data","href":"server/EchoHub.Server.Data.html","topicHref":"server/EchoHub.Server.Data.html","topicUid":"EchoHub.Server.Data","type":"Namespace","items":[{"name":"EchoHubDbContext","href":"server/EchoHub.Server.Data.EchoHubDbContext.html","topicHref":"server/EchoHub.Server.Data.EchoHubDbContext.html","topicUid":"EchoHub.Server.Data.EchoHubDbContext","type":"Class"}]},{"name":"EchoHub.Server.Data.Migrations","href":"server/EchoHub.Server.Data.Migrations.html","topicHref":"server/EchoHub.Server.Data.Migrations.html","topicUid":"EchoHub.Server.Data.Migrations","type":"Namespace","items":[{"name":"AddAttachmentFileSize","href":"server/EchoHub.Server.Data.Migrations.AddAttachmentFileSize.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddAttachmentFileSize.html","topicUid":"EchoHub.Server.Data.Migrations.AddAttachmentFileSize","type":"Class"},{"name":"AddChannelEncryptionEnvelope","href":"server/EchoHub.Server.Data.Migrations.AddChannelEncryptionEnvelope.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddChannelEncryptionEnvelope.html","topicUid":"EchoHub.Server.Data.Migrations.AddChannelEncryptionEnvelope","type":"Class"},{"name":"AddChannelIsPublic","href":"server/EchoHub.Server.Data.Migrations.AddChannelIsPublic.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddChannelIsPublic.html","topicUid":"EchoHub.Server.Data.Migrations.AddChannelIsPublic","type":"Class"},{"name":"AddChannelMembership","href":"server/EchoHub.Server.Data.Migrations.AddChannelMembership.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddChannelMembership.html","topicUid":"EchoHub.Server.Data.Migrations.AddChannelMembership","type":"Class"},{"name":"AddChannelPasswordHash","href":"server/EchoHub.Server.Data.Migrations.AddChannelPasswordHash.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddChannelPasswordHash.html","topicUid":"EchoHub.Server.Data.Migrations.AddChannelPasswordHash","type":"Class"},{"name":"AddEncryptionSupport","href":"server/EchoHub.Server.Data.Migrations.AddEncryptionSupport.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddEncryptionSupport.html","topicUid":"EchoHub.Server.Data.Migrations.AddEncryptionSupport","type":"Class"},{"name":"AddMessageAttachments","href":"server/EchoHub.Server.Data.Migrations.AddMessageAttachments.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddMessageAttachments.html","topicUid":"EchoHub.Server.Data.Migrations.AddMessageAttachments","type":"Class"},{"name":"AddMessageEmbed","href":"server/EchoHub.Server.Data.Migrations.AddMessageEmbed.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddMessageEmbed.html","topicUid":"EchoHub.Server.Data.Migrations.AddMessageEmbed","type":"Class"},{"name":"AddModerationRoles","href":"server/EchoHub.Server.Data.Migrations.AddModerationRoles.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddModerationRoles.html","topicUid":"EchoHub.Server.Data.Migrations.AddModerationRoles","type":"Class"},{"name":"InitialCreate","href":"server/EchoHub.Server.Data.Migrations.InitialCreate.html","topicHref":"server/EchoHub.Server.Data.Migrations.InitialCreate.html","topicUid":"EchoHub.Server.Data.Migrations.InitialCreate","type":"Class"}]},{"name":"EchoHub.Server.Hubs","href":"server/EchoHub.Server.Hubs.html","topicHref":"server/EchoHub.Server.Hubs.html","topicUid":"EchoHub.Server.Hubs","type":"Namespace","items":[{"name":"ChatHub","href":"server/EchoHub.Server.Hubs.ChatHub.html","topicHref":"server/EchoHub.Server.Hubs.ChatHub.html","topicUid":"EchoHub.Server.Hubs.ChatHub","type":"Class"}]},{"name":"EchoHub.Server.Services","href":"server/EchoHub.Server.Services.html","topicHref":"server/EchoHub.Server.Services.html","topicUid":"EchoHub.Server.Services","type":"Namespace","items":[{"name":"ChannelService","href":"server/EchoHub.Server.Services.ChannelService.html","topicHref":"server/EchoHub.Server.Services.ChannelService.html","topicUid":"EchoHub.Server.Services.ChannelService","type":"Class"},{"name":"ChatService","href":"server/EchoHub.Server.Services.ChatService.html","topicHref":"server/EchoHub.Server.Services.ChatService.html","topicUid":"EchoHub.Server.Services.ChatService","type":"Class"},{"name":"DirectoryClaimStore","href":"server/EchoHub.Server.Services.DirectoryClaimStore.html","topicHref":"server/EchoHub.Server.Services.DirectoryClaimStore.html","topicUid":"EchoHub.Server.Services.DirectoryClaimStore","type":"Class"},{"name":"FileCleanupService","href":"server/EchoHub.Server.Services.FileCleanupService.html","topicHref":"server/EchoHub.Server.Services.FileCleanupService.html","topicUid":"EchoHub.Server.Services.FileCleanupService","type":"Class"},{"name":"FileStorageService","href":"server/EchoHub.Server.Services.FileStorageService.html","topicHref":"server/EchoHub.Server.Services.FileStorageService.html","topicUid":"EchoHub.Server.Services.FileStorageService","type":"Class"},{"name":"LinkEmbedService","href":"server/EchoHub.Server.Services.LinkEmbedService.html","topicHref":"server/EchoHub.Server.Services.LinkEmbedService.html","topicUid":"EchoHub.Server.Services.LinkEmbedService","type":"Class"},{"name":"MessageEncryptionService","href":"server/EchoHub.Server.Services.MessageEncryptionService.html","topicHref":"server/EchoHub.Server.Services.MessageEncryptionService.html","topicUid":"EchoHub.Server.Services.MessageEncryptionService","type":"Class"},{"name":"MuteExpirationService","href":"server/EchoHub.Server.Services.MuteExpirationService.html","topicHref":"server/EchoHub.Server.Services.MuteExpirationService.html","topicUid":"EchoHub.Server.Services.MuteExpirationService","type":"Class"},{"name":"PresenceTracker","href":"server/EchoHub.Server.Services.PresenceTracker.html","topicHref":"server/EchoHub.Server.Services.PresenceTracker.html","topicUid":"EchoHub.Server.Services.PresenceTracker","type":"Class"},{"name":"RegistrationStatus","href":"server/EchoHub.Server.Services.RegistrationStatus.html","topicHref":"server/EchoHub.Server.Services.RegistrationStatus.html","topicUid":"EchoHub.Server.Services.RegistrationStatus","type":"Class"},{"name":"ServerDirectoryService","href":"server/EchoHub.Server.Services.ServerDirectoryService.html","topicHref":"server/EchoHub.Server.Services.ServerDirectoryService.html","topicUid":"EchoHub.Server.Services.ServerDirectoryService","type":"Class"},{"name":"SignalRBroadcaster","href":"server/EchoHub.Server.Services.SignalRBroadcaster.html","topicHref":"server/EchoHub.Server.Services.SignalRBroadcaster.html","topicUid":"EchoHub.Server.Services.SignalRBroadcaster","type":"Class"},{"name":"UserService","href":"server/EchoHub.Server.Services.UserService.html","topicHref":"server/EchoHub.Server.Services.UserService.html","topicUid":"EchoHub.Server.Services.UserService","type":"Class"}]},{"name":"EchoHub.Server.Setup","href":"server/EchoHub.Server.Setup.html","topicHref":"server/EchoHub.Server.Setup.html","topicUid":"EchoHub.Server.Setup","type":"Namespace","items":[{"name":"DataMigrationService","href":"server/EchoHub.Server.Setup.DataMigrationService.html","topicHref":"server/EchoHub.Server.Setup.DataMigrationService.html","topicUid":"EchoHub.Server.Setup.DataMigrationService","type":"Class"},{"name":"DatabaseSetup","href":"server/EchoHub.Server.Setup.DatabaseSetup.html","topicHref":"server/EchoHub.Server.Setup.DatabaseSetup.html","topicUid":"EchoHub.Server.Setup.DatabaseSetup","type":"Class"},{"name":"FirstRunSetup","href":"server/EchoHub.Server.Setup.FirstRunSetup.html","topicHref":"server/EchoHub.Server.Setup.FirstRunSetup.html","topicUid":"EchoHub.Server.Setup.FirstRunSetup","type":"Class"}]}]},{"name":"Articles","includedFrom":"~/api/server-articles/toc.yml","items":[{"name":"Overview","href":"server-articles/index.html","topicHref":"server-articles/index.html"}]}]},{"name":"Server.Irc","items":[{"name":"API Reference","includedFrom":"~/_api_meta/server-irc/toc.yml","items":[{"name":"EchoHub.Server.Irc","href":"server-irc/EchoHub.Server.Irc.html","topicHref":"server-irc/EchoHub.Server.Irc.html","topicUid":"EchoHub.Server.Irc","type":"Namespace","items":[{"name":"IrcBroadcaster","href":"server-irc/EchoHub.Server.Irc.IrcBroadcaster.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcBroadcaster.html","topicUid":"EchoHub.Server.Irc.IrcBroadcaster","type":"Class"},{"name":"IrcClientConnection","href":"server-irc/EchoHub.Server.Irc.IrcClientConnection.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcClientConnection.html","topicUid":"EchoHub.Server.Irc.IrcClientConnection","type":"Class"},{"name":"IrcCommandHandler","href":"server-irc/EchoHub.Server.Irc.IrcCommandHandler.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcCommandHandler.html","topicUid":"EchoHub.Server.Irc.IrcCommandHandler","type":"Class"},{"name":"IrcGatewayService","href":"server-irc/EchoHub.Server.Irc.IrcGatewayService.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcGatewayService.html","topicUid":"EchoHub.Server.Irc.IrcGatewayService","type":"Class"},{"name":"IrcMessage","href":"server-irc/EchoHub.Server.Irc.IrcMessage.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcMessage.html","topicUid":"EchoHub.Server.Irc.IrcMessage","type":"Class"},{"name":"IrcMessageFormatter","href":"server-irc/EchoHub.Server.Irc.IrcMessageFormatter.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcMessageFormatter.html","topicUid":"EchoHub.Server.Irc.IrcMessageFormatter","type":"Class"},{"name":"IrcNumericReply","href":"server-irc/EchoHub.Server.Irc.IrcNumericReply.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcNumericReply.html","topicUid":"EchoHub.Server.Irc.IrcNumericReply","type":"Class"},{"name":"IrcOptions","href":"server-irc/EchoHub.Server.Irc.IrcOptions.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcOptions.html","topicUid":"EchoHub.Server.Irc.IrcOptions","type":"Class"},{"name":"IrcServiceExtensions","href":"server-irc/EchoHub.Server.Irc.IrcServiceExtensions.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcServiceExtensions.html","topicUid":"EchoHub.Server.Irc.IrcServiceExtensions","type":"Class"}]}]}]}]} diff --git a/articles/encrypted-rooms.html b/articles/encrypted-rooms.html new file mode 100644 index 0000000..7f258ff --- /dev/null +++ b/articles/encrypted-rooms.html @@ -0,0 +1,271 @@ + + + + + Encrypted Rooms (Password-Protected Channels) | EchoHub Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +
    +
    +
    +
    +
    Table of Contents
    + +
    +
    + +
    +
    +
    + +
    +
    + + + +
    + +
    +

    Encrypted Rooms (Password-Protected Channels)

    + +

    An encrypted room is a channel whose entire content — every message and every file — is +end-to-end encrypted with a key derived from a shared passphrase. Only people who know the +passphrase can read the room. Not even the server owner can read the content, yet the server +can still gate who joins, and it can count and measure what's stored (message count, file sizes, +timestamps) without ever seeing the plaintext.

    +

    This is a stronger guarantee than the transport and at-rest encryption described +elsewhere, where the server decrypts every message to process it. Here the server is treated as +untrusted for content: it holds only ciphertext and wrapped keys.

    +
    +

    The passphrase is the only key. There is no recovery. If everyone who knows a room's +passphrase forgets it, that room's history is permanently unreadable — by design.

    +
    +

    What the server can and cannot see

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    The server can seeThe server cannot see
    That the channel is encryptedMessage text
    Message count and timestampsFile contents
    Who sent each message (sender identity)Image previews (ASCII art)
    Each attachment's file name and byte sizeThe passphrase, the room key, or the key-encryption key
    The estimated total size (via /meta)Anything that would let it decrypt the above
    +

    File names are stored in plaintext so the file list stays usable — treat a file name itself as +non-secret. Everything inside the file is encrypted.

    +

    Key hierarchy

    +

    Three keys are derived from one passphrase. The passphrase, the key-encryption key, and the room +content key never leave the client.

    +
    flowchart TD
    +    P[Passphrase] -->|PBKDF2-SHA256, 210k iterations, per-room salt| OKM[64-byte output]
    +    OKM --> AK["Auth key (first 32 bytes)"]
    +    OKM --> KEK["Key-encryption key / KEK (last 32 bytes)"]
    +    AK -->|sent as lowercase hex| SRV1["Server: BCrypt-hash as the join gate"]
    +    RCK["Room Content Key (random 256-bit)"] -->|encrypts all content| CONTENT[Messages + files + previews]
    +    RCK -->|AES-256-GCM wrap under KEK| WRAP["Wrapped room key"]
    +    WRAP -->|stored| SRV2["Server: stores wrapped key + salt only"]
    +
    +
      +
    • Auth key — the join credential. Derived from the passphrase, sent to the server as hex, and +stored only as a BCrypt hash. Proving knowledge of it is what lets you join; it reveals +nothing about the content key.
    • +
    • Key-encryption key (KEK) — never sent. Used locally to wrap (encrypt) and unwrap the room +content key.
    • +
    • Room Content Key (RCK) — a random 256-bit key generated once, at room creation. It encrypts +every message and file. The server stores it only in wrapped form, so it can hand the wrapped key +to a joiner but can never unwrap it itself.
    • +
    +

    All content encryption is AES-256-GCM with a random 12-byte nonce and a 16-byte authentication +tag per item, so identical inputs never produce identical ciphertext, and any tampering is detected.

    +

    Room-encrypted text carries a self-describing prefix so clients and the server can tell it apart +from plaintext:

    +
    $RC1$base64(nonce || tag || ciphertext)
    +
    +

    Creating a room

    +

    The client does all the cryptography locally, then hands the server only what it needs to gate joins +and store (but not read) the content.

    +
    sequenceDiagram
    +    participant Client
    +    participant Server
    +
    +    Client->>Client: generate salt + random Room Content Key (RCK)
    +    Client->>Client: DeriveKeys(passphrase, salt) → auth key + KEK
    +    Client->>Client: wrap RCK under KEK
    +    Client->>Server: create channel { authKey(hex), salt, wrappedRoomKey }
    +    Server->>Server: BCrypt-hash auth key, store salt + wrapped key
    +    Note over Server: Server never receives passphrase, KEK, or RCK
    +
    +

    Joining a room

    +
    sequenceDiagram
    +    participant Client
    +    participant Server
    +
    +    Client->>Server: GET /crypto → { isEncrypted, salt }
    +    Client->>Client: DeriveKeys(passphrase, salt) → auth key + KEK
    +    Client->>Server: join { authKey(hex) }
    +    Server->>Server: BCrypt-verify against stored hash
    +    alt correct passphrase
    +        Server->>Client: history (ciphertext) + wrapped room key
    +        Client->>Client: unwrap RCK with KEK, then decrypt everything locally
    +    else wrong passphrase
    +        Server->>Client: rejected (join gate fails)
    +    end
    +
    +

    A wrong passphrase fails the BCrypt gate, so the server never even hands out the wrapped key. Even if +it did, an attacker without the KEK cannot unwrap it.

    +

    What gets encrypted

    +

    When you send a message or attach files to an encrypted room, the client encrypts each part with the +room content key before uploading:

    +
      +
    • Message text$RC1$… ciphertext.
    • +
    • Files (any kind) → the whole blob is AES-256-GCM encrypted client-side; the server stores an +opaque ciphertext blob.
    • +
    • Image ASCII previews → rendered on the client, then room-encrypted. The server never sees the +rendered art.
    • +
    +

    The server records each attachment's kind, file name, and byte size (of the ciphertext +blob) as metadata, and broadcasts the ciphertext to other members, who decrypt locally.

    +

    Changing the passphrase

    +

    /passwd <old> <new> rotates the passphrase. Because only the wrapping of the room content key +changes — not the RCK itself — all existing history stays readable:

    +
      +
    1. The client proves knowledge of the old passphrase (old auth key).
    2. +
    3. It unwraps the RCK with the old KEK, then re-wraps it under the new KEK (new salt).
    4. +
    5. It uploads the new auth key + salt + re-wrapped key. The content is never re-encrypted.
    6. +
    +

    Inspecting a room

    +

    Use /meta in any channel to see what the server knows about it, including encrypted rooms:

    +
    Room info for #private-room:
    +  Room ID       3f2a…-…-…
    +  Created       7/16/2026 2:31 PM
    +  Messages      128
    +  Unique users  4
    +  Est. size     42.5 MB
    +  Protection    end-to-end encrypted
    +
    +

    Est. size is the sum of stored attachment blob sizes plus message text length — an estimate of the +room's footprint, computed entirely from metadata the server holds without reading any content.

    +

    Limitations & security notes

    +
      +
    • No recovery. A lost passphrase means unrecoverable history. Keep it safe; there is no reset.
    • +
    • File names are plaintext. They stay readable so the file list works — don't put secrets in a +file name.
    • +
    • IRC is disabled for encrypted rooms. The IRC gateway forwards plaintext and cannot participate +in the room's key scheme, so encrypted channels are not bridged to IRC.
    • +
    • Metadata is visible. Message counts, timestamps, sender identities, file names, and sizes are +intentionally readable so the server can moderate at the metadata level and report /meta.
    • +
    • Endpoint trust. End-to-end encryption protects content from the server and the network, not +from a compromised client device that already holds the passphrase.
    • +
    + +
      +
    • Message Encryption — transport ($ENC$v1$) and optional at-rest database +encryption, where the server does decrypt content for processing. Encrypted rooms are a separate, +stronger layer that sits on top.
    • +
    + +
    + + + + + +
    + +
    + +
    +
    + +
    + + + + diff --git a/articles/encryption.html b/articles/encryption.html index 14307d9..e678055 100644 --- a/articles/encryption.html +++ b/articles/encryption.html @@ -197,7 +197,7 @@

    Limitations

    • TLS-inspecting proxies — if a corporate proxy terminates TLS with a trusted root CA, it can intercept the key exchange (GET /api/server/encryption-key) and read all traffic. A future upgrade to ECDH key exchange would address this.
    • -
    • Server has full access — the server decrypts all messages for processing. This is not end-to-end encryption between users; it's transport encryption between client and server.
    • +
    • Server has full access — the server decrypts all messages for processing. This is not end-to-end encryption between users; it's transport encryption between client and server. For true end-to-end encryption where the server cannot read content, use encrypted rooms.
    • IRC clients receive plaintext — IRC is an open protocol and third-party clients cannot participate in the encryption scheme.

    Troubleshooting

    diff --git a/articles/toc.html b/articles/toc.html index dc29b5a..25dea6e 100644 --- a/articles/toc.html +++ b/articles/toc.html @@ -32,6 +32,9 @@
  • Encryption
  • +
  • + Encrypted Rooms +
  • Notification Sounds
  • diff --git a/articles/toc.json b/articles/toc.json index 81e02ea..5189fb1 100644 --- a/articles/toc.json +++ b/articles/toc.json @@ -1,2 +1,2 @@ -{"items":[{"name":"Guides","items":[{"name":"Getting Started","href":"getting-started.html","topicHref":"getting-started.html"},{"name":"Docker","href":"docker.html","topicHref":"docker.html"},{"name":"Architecture","href":"architecture.html","topicHref":"architecture.html"},{"name":"Configuration","href":"configuration.html","topicHref":"configuration.html"},{"name":"Encryption","href":"encryption.html","topicHref":"encryption.html"},{"name":"Notification Sounds","href":"notification-sounds.html","topicHref":"notification-sounds.html"}]},{"name":"Flows","includedFrom":"~/flows/toc.yml","items":[{"name":"Authentication","href":"../flows/authentication.html","topicHref":"../flows/authentication.html"},{"name":"Connection","href":"../flows/connection.html","topicHref":"../flows/connection.html"},{"name":"Messaging","href":"../flows/messaging.html","topicHref":"../flows/messaging.html"},{"name":"Channels","href":"../flows/channels.html","topicHref":"../flows/channels.html"},{"name":"Moderation","href":"../flows/moderation.html","topicHref":"../flows/moderation.html"},{"name":"Media & Services","href":"../flows/media.html","topicHref":"../flows/media.html"}]}]} +{"items":[{"name":"Guides","items":[{"name":"Getting Started","href":"getting-started.html","topicHref":"getting-started.html"},{"name":"Docker","href":"docker.html","topicHref":"docker.html"},{"name":"Architecture","href":"architecture.html","topicHref":"architecture.html"},{"name":"Configuration","href":"configuration.html","topicHref":"configuration.html"},{"name":"Encryption","href":"encryption.html","topicHref":"encryption.html"},{"name":"Encrypted Rooms","href":"encrypted-rooms.html","topicHref":"encrypted-rooms.html"},{"name":"Notification Sounds","href":"notification-sounds.html","topicHref":"notification-sounds.html"}]},{"name":"Flows","includedFrom":"~/flows/toc.yml","items":[{"name":"Authentication","href":"../flows/authentication.html","topicHref":"../flows/authentication.html"},{"name":"Connection","href":"../flows/connection.html","topicHref":"../flows/connection.html"},{"name":"Messaging","href":"../flows/messaging.html","topicHref":"../flows/messaging.html"},{"name":"Channels","href":"../flows/channels.html","topicHref":"../flows/channels.html"},{"name":"Moderation","href":"../flows/moderation.html","topicHref":"../flows/moderation.html"},{"name":"Media & Services","href":"../flows/media.html","topicHref":"../flows/media.html"}]}]} diff --git a/changelog/index.html b/changelog/index.html index ca8ee1d..12f86ef 100644 --- a/changelog/index.html +++ b/changelog/index.html @@ -90,6 +90,7 @@

    Release history for EchoHub.

    Releases

      +
    • v0.2.13 - Chat Visual Overhaul, Auto-Join All Channels & Persistent Read Positions
    • v0.2.12 - End-to-End Encrypted Channels, IRC Channel Keys, Image Save & Ctrl+W Crash Fix
    • v0.2.11 - EchoHubSpace Auth, Live Directory Updates & Server Browser Metadata
    • v0.2.10 - Command Palette, Infinite History Scroll & Auto-Updater Fixes
    • diff --git a/changelog/toc.html b/changelog/toc.html index 5522b12..81c7cfb 100644 --- a/changelog/toc.html +++ b/changelog/toc.html @@ -15,6 +15,9 @@
    • Overview
    • +
    • + v0.2.13 +
    • v0.2.12
    • diff --git a/changelog/toc.json b/changelog/toc.json index 26ccc79..08e3dfa 100644 --- a/changelog/toc.json +++ b/changelog/toc.json @@ -1,2 +1,2 @@ -{"items":[{"name":"Overview","href":"index.html","topicHref":"index.html"},{"name":"v0.2.12","href":"v0.2.12.html","topicHref":"v0.2.12.html"},{"name":"v0.2.11","href":"v0.2.11.html","topicHref":"v0.2.11.html"},{"name":"v0.2.10","href":"v0.2.10.html","topicHref":"v0.2.10.html"},{"name":"v0.2.9","href":"v0.2.9.html","topicHref":"v0.2.9.html"},{"name":"v0.2.8","href":"v0.2.8.html","topicHref":"v0.2.8.html"},{"name":"v0.2.7","href":"v0.2.7.html","topicHref":"v0.2.7.html"},{"name":"v0.2.6","href":"v0.2.6.html","topicHref":"v0.2.6.html"},{"name":"v0.2.5","href":"v0.2.5.html","topicHref":"v0.2.5.html"},{"name":"v0.2.4","href":"v0.2.4.html","topicHref":"v0.2.4.html"},{"name":"v0.2.3","href":"v0.2.3.html","topicHref":"v0.2.3.html"},{"name":"v0.2.2","href":"v0.2.2.html","topicHref":"v0.2.2.html"},{"name":"v0.2.1","href":"v0.2.1.html","topicHref":"v0.2.1.html"},{"name":"v0.2.0","href":"v0.2.0.html","topicHref":"v0.2.0.html"},{"name":"v0.1.1","href":"v0.1.1.html","topicHref":"v0.1.1.html"},{"name":"v0.1.0","href":"v0.1.0.html","topicHref":"v0.1.0.html"}]} +{"items":[{"name":"Overview","href":"index.html","topicHref":"index.html"},{"name":"v0.2.13","href":"v0.2.13.html","topicHref":"v0.2.13.html"},{"name":"v0.2.12","href":"v0.2.12.html","topicHref":"v0.2.12.html"},{"name":"v0.2.11","href":"v0.2.11.html","topicHref":"v0.2.11.html"},{"name":"v0.2.10","href":"v0.2.10.html","topicHref":"v0.2.10.html"},{"name":"v0.2.9","href":"v0.2.9.html","topicHref":"v0.2.9.html"},{"name":"v0.2.8","href":"v0.2.8.html","topicHref":"v0.2.8.html"},{"name":"v0.2.7","href":"v0.2.7.html","topicHref":"v0.2.7.html"},{"name":"v0.2.6","href":"v0.2.6.html","topicHref":"v0.2.6.html"},{"name":"v0.2.5","href":"v0.2.5.html","topicHref":"v0.2.5.html"},{"name":"v0.2.4","href":"v0.2.4.html","topicHref":"v0.2.4.html"},{"name":"v0.2.3","href":"v0.2.3.html","topicHref":"v0.2.3.html"},{"name":"v0.2.2","href":"v0.2.2.html","topicHref":"v0.2.2.html"},{"name":"v0.2.1","href":"v0.2.1.html","topicHref":"v0.2.1.html"},{"name":"v0.2.0","href":"v0.2.0.html","topicHref":"v0.2.0.html"},{"name":"v0.1.1","href":"v0.1.1.html","topicHref":"v0.1.1.html"},{"name":"v0.1.0","href":"v0.1.0.html","topicHref":"v0.1.0.html"}]} diff --git a/changelog/v0.2.12.html b/changelog/v0.2.12.html index 1f575e1..2ab0cf7 100644 --- a/changelog/v0.2.12.html +++ b/changelog/v0.2.12.html @@ -120,7 +120,10 @@
    • Attach a file by drag & drop or by pasting — drop a file onto the terminal, or copy a file in your file manager and press Ctrl+V, to stage it as an attachment (the next Enter sends it with your caption). Multiple files at once are supported. Ctrl+V still pastes text when the clipboard holds text; Ctrl+Y is a paste alias. On Windows the copied-file paste reads the clipboard's file list directly (Windows Terminal never pastes copied files as text), with xclip/wl-paste used on Linux
    • Pick ASCII-art size for attached images — /size opens a Small/Medium/Large picker (40×40 / 80×80 / 120×120) with descriptions, /size <s|m|l> sets it directly, and /send <file> -l sets it for that message. The choice is a saved preference and applies to copy-paste/drag-drop images (which have no per-file flag); the current size is shown in the staging tray
    • New TransparentLight theme — dark characters on a transparent background, for light terminal color schemes (/theme transparentlight)
    • -
    • Timestamps in messages are now aware of the current culture and display the short time pattern for today's messages and the short date+time pattern for older messages.
    • +
    • /meta command — shows a summary of the current room: room id, created date, message count, unique participant count, estimated storage size, and protection level (open / password-protected / end-to-end encrypted). Works for encrypted rooms too, since these are all metadata the server tracks without reading content. (/info is an alias.)
    • +
    • Configurable upload limits — a new Uploads section in the server settings sets the maximum size per file, image, audio clip, and avatar, plus the maximum attachments per message. Absent values fall back to the previous built-in limits, so existing servers are unaffected until they opt in.
    • +
    • Updater progress bar — the self-updater now draws a real progress bar on the console (Downloading [████████░░] 62%) for each step, replacing the plain status lines, so a large download shows visible progress.
    • +
    • Timestamps in messages are culture-aware and now always include the date: today's messages show a compact date + short time, and older messages show the general short date/time.

    Bug Fixes

      @@ -128,12 +131,16 @@
    • Attachments whose files have been pruned (retention cleanup deletes blobs older than Storage:RetentionDays but left the message rows) no longer render a dead download/preview. When channel history loads, the server checks which attachment blobs still exist: missing ones are dropped from the message, and an attachment-only message whose files are all gone is removed from the database.
    • Fixed intermittent crash on Ctrl+W — Terminal.Gui binds Ctrl+W to clipboard-cut, and Windows clipboard contention (another app holding the clipboard) threw an unhandled Win32Exception that took the app down. Ctrl+W now deletes the previous word (readline behavior, no clipboard), and all clipboard shortcuts (Ctrl+X/C/V/Y) are guarded so transient clipboard failures log a warning instead of crashing
    • Fixed emoji shortcode replacement permanently disabling itself if a cursor update threw mid-replacement
    • +
    • Fixed new messages showing no date — today's messages rendered time-only, and the "is it today?" check compared the server's UTC date against the local date, so the classification could also be wrong near midnight. Timestamps are now converted to local time first and always include the date.
    • +
    • Fixed the self-updater hanging at "extracting" — the update ran while the Terminal.Gui main loop still owned the console, so the old and new processes deadlocked over it. The update now runs after the TUI shuts down, on a clean console
    • IRC LIST no longer leaks private channels; protected channels are marked [+k]

    API Changes

    • ChannelDto gains isProtected and isEncrypted; CreateChannelRequest gains optional password, encryptionSalt, and wrappedRoomKey; SignalR JoinChannel takes an optional second password argument and JoinChannelResult gains passwordRequired, encryptionSalt, and wrappedRoomKey (older clients must update to join over SignalR)
    • New endpoints: GET /api/channels/{channel}/crypto (public crypto metadata — salt only, never the wrapped key) and POST /api/channels/{channel}/rekey (creator-only passphrase change)
    • +
    • New endpoint GET /api/channels/{channel}/meta returns a ChannelMetaDto (room id, name, topic, encrypted/protected flags, message count, unique user count, estimated size, created date) backing the /meta command
    • +
    • New server Uploads configuration section (MaxFileSizeMB, MaxImageSizeMB, MaxAudioSizeMB, MaxAvatarSizeMB, MaxAttachmentsPerMessage); the message-upload endpoint's request-body ceiling is now derived from these values at runtime rather than from compile-time constants
    • The upload endpoint accepts type and content form fields for encrypted channels, where the client supplies the declared message type and room-encrypted content
    • ImageToAsciiService and FileValidationHelper moved from EchoHub.Server to EchoHub.Core so the client can render ASCII art and detect file types for encrypted uploads
    • Message shape change: MessageDto drops Type/AttachmentUrl/AttachmentFileName/AttachmentFileSize and gains Attachments (a list of AttachmentDto { Kind, Url, FileName, FileSize, AsciiPreview }, null/empty for plain text). New Attachment entity + table with a cascade FK to Message
    • diff --git a/changelog/v0.2.13.html b/changelog/v0.2.13.html new file mode 100644 index 0000000..4768f52 --- /dev/null +++ b/changelog/v0.2.13.html @@ -0,0 +1,140 @@ + + + + + v0.2.13 | EchoHub Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      + +
      + +
      +
      +
      +
      +
      Table of Contents
      + +
      +
      + +
      +
      +
      + +
      +
      + + + +
      + +
      +

      v0.2.13

      + +

      A visual overhaul of the chat client — WeeChat-style column layout, per-user nick colors, date rules, unread markers, an activity status segment, and an ASCII welcome banner — plus reliable cross-channel notifications: the client now joins all your channels at connect so unread counts and @mentions light up everywhere, and read positions persist across restarts so activity that happened while you were offline still shows. Modern polish, old IRC soul.

      +

      New Features

      +
        +
      • Aligned message layout (WeeChat-style) — every message renders as HH:mm nickname │ text: a dim timestamp, the nick right-aligned in a fixed 12-column gutter, and a dim rail separating names from content. Word-wrapped continuation lines, multi-line messages, attachments, ASCII-art image previews, and link embeds all align under the text column and extend the rail, so conversations read as a clean two-column grid. Over-long nicks are truncated with in the gutter only — profiles, mentions, and the users panel always use the full name. System and status lines (joined, is now Away, …) show -- in the nick column, replacing the old ** prefix.
      • +
      • Deterministic nickname colors — users who haven't picked a /color now get an automatic, stable color derived from their nick (12-entry palette chosen to stay readable on dark and light backgrounds). The same user is the same color in message headers and the online-users panel, on every client, every session. An explicitly chosen nickname color still takes priority.
      • +
      • Date separator rules — a full-width ── Wed, Jul 16 2026 ───── rule is inserted whenever the calendar day changes (live, in loaded history, and when scrolling back through older messages). Timestamps are now a compact HH:mm — the date lives in the rules instead of being repeated on every line.
      • +
      • "New messages" marker — the first message that arrives in a channel you're not viewing gets an orange ── new messages ── rule above it (irssi-style). Switch to the channel and the marker shows exactly where you left off; it survives the history reload on channel switch and is consumed when you move away again.
      • +
      • Status bar activity segment — channels with unread messages appear in the status bar as │ Act: #dev,#random (up to 4, +n overflow). Channels where you were @mentioned show in orange, others in cyan.
      • +
      • Connecting spinner — transitional connection states (Connecting, Reconnecting, Authenticating…) now animate a braille spinner in the status bar instead of sitting on static text.
      • +
      • Mention-aware channel list — a channel where you were @mentioned turns orange in the channel list (name and unread badge), escalating above the cyan plain-unread highlight. The highlight clears when you view the channel.
      • +
      • Welcome banner — with no channel selected (fresh start, or after disconnecting) the chat pane shows a gold-gradient ASCII "ECHOHUB" logo with the version and key hints, instead of an empty box. Narrow panes get a compact variant.
      • +
      • Rounded frame borders — the channels, chat, input, and users panels draw with rounded corners.
      • +
      • Auto-join all channels at connect — the client now joins every channel it lists for you (public channels and prior memberships) as part of connecting, so message events flow for all of them: unread badges, @mention highlights, and the status-bar activity segment work without having to open each channel first. Protected channels you've never entered are skipped silently (joining them stays a prompted, manual action), and channels you /leave are remembered and excluded until you join them again.
      • +
      • Read positions survive restarts — the client persists the last message you read per channel (locally, per server). On the next connect it compares that against fetched history, so messages that arrived while you were offline still produce unread counts, mention highlights, and a correctly placed "new messages" marker.
      • +
      • Theme-tinted frame borders — themes can now color the window frame borders independently of text via a new optional border section (hex values supported). The Transparent and TransparentLight themes use it to draw dim gray borders instead of stark white ones, for a subtler, glassier look. Custom theme JSON files without a border section keep their base colors.
      • +
      +

      Bug Fixes

      +
        +
      • The @mention highlight no longer drops off the second and later lines of a long mention message — wrapped continuation lines now inherit the highlight.
      • +
      • The unread-position marker is re-anchored after a channel switch reloads history, instead of being wiped by the reload before you could see it.
      • +
      • Cached room keys for end-to-end encrypted channels no longer disappear from the config on reconnect. Saving the server entry after a successful connect replaced it wholesale, wiping the cached keys (and forcing a passphrase re-entry on the next launch); the entry is now updated in place.
      • +
      • A failed channel rejoin after a reconnect (e.g. a channel deleted while you were away) no longer aborts rejoining the remaining channels.
      • +
      • Client config file access is now serialized across threads (token refresh, room-key cache, and read-position checkpoints all write it), preventing rare config corruption.
      • +
      + +
      + + + + + +
      + +
      + +
      +
      + +
      + + + + diff --git a/index.json b/index.json index eb3a808..a592ef5 100644 --- a/index.json +++ b/index.json @@ -7,12 +7,12 @@ "api/client/EchoHub.Client.AppOrchestrator.html": { "href": "api/client/EchoHub.Client.AppOrchestrator.html", "title": "Class AppOrchestrator | EchoHub Documentation", - "summary": "Class AppOrchestrator Namespace EchoHub.Client Assembly EchoHub.Client.dll Central orchestrator for the EchoHub TUI client. Wires UI events to service calls and connection events to UI updates. public sealed class AppOrchestrator : IDisposable Inheritance object AppOrchestrator Implements IDisposable Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors AppOrchestrator(IApplication, ClientConfig) public AppOrchestrator(IApplication app, ClientConfig config) Parameters app IApplication config ClientConfig Properties MainWindow public MainWindow MainWindow { get; } Property Value MainWindow Methods Dispose() public void Dispose()" + "summary": "Class AppOrchestrator Namespace EchoHub.Client Assembly EchoHub.Client.dll Central orchestrator for the EchoHub TUI client. Wires UI events to service calls and connection events to UI updates. public sealed class AppOrchestrator : IDisposable Inheritance object AppOrchestrator Implements IDisposable Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors AppOrchestrator(IApplication, ClientConfig) public AppOrchestrator(IApplication app, ClientConfig config) Parameters app IApplication config ClientConfig Properties MainWindow public MainWindow MainWindow { get; } Property Value MainWindow PendingUpdate Set when the user confirms an update. The host must run this after the Terminal.Gui main loop exits (console restored), so the updater's in-place restart doesn't fight the TUI. public Func? PendingUpdate { get; } Property Value Func Methods Dispose() public void Dispose()" }, "api/client/EchoHub.Client.Commands.CommandHandler.html": { "href": "api/client/EchoHub.Client.Commands.CommandHandler.html", "title": "Class CommandHandler | EchoHub Documentation", - "summary": "Class CommandHandler Namespace EchoHub.Client.Commands Assembly EchoHub.Client.dll public class CommandHandler Inheritance object CommandHandler Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors CommandHandler() public CommandHandler() Methods HandleAsync(string) public Task HandleAsync(string input) Parameters input string Returns Task IsCommand(string) public bool IsCommand(string input) Parameters input string Returns bool Events OnAssignRole public event Func? OnAssignRole Event Type Func OnBanUser public event Func? OnBanUser Event Type Func OnChangeRoomPassword public event Func? OnChangeRoomPassword Event Type Func OnClearAttachments public event Func? OnClearAttachments Event Type Func OnHelp public event Func? OnHelp Event Type Func OnJoinChannel public event Func? OnJoinChannel Event Type Func OnKickUser public event Func? OnKickUser Event Type Func OnLeaveChannel public event Func? OnLeaveChannel Event Type Func OnListUsers public event Func? OnListUsers Event Type Func OnMuteUser public event Func? OnMuteUser Event Type Func OnNukeChannel public event Func? OnNukeChannel Event Type Func OnOpenProfile public event Func? OnOpenProfile Event Type Func OnOpenServers public event Func? OnOpenServers Event Type Func OnQuit public event Func? OnQuit Event Type Func OnSendFile public event Func? OnSendFile Event Type Func OnSetAsciiSize public event Func? OnSetAsciiSize Event Type Func OnSetAvatar public event Func? OnSetAvatar Event Type Func OnSetColor public event Func? OnSetColor Event Type Func OnSetDownloadPath public event Func? OnSetDownloadPath Event Type Func OnSetNick public event Func? OnSetNick Event Type Func OnSetStatus public event Func? OnSetStatus Event Type Func OnSetTheme public event Func? OnSetTheme Event Type Func OnSetTopic public event Func? OnSetTopic Event Type Func OnTestSound public event Func? OnTestSound Event Type Func OnUnbanUser public event Func? OnUnbanUser Event Type Func OnUnmuteUser public event Func? OnUnmuteUser Event Type Func" + "summary": "Class CommandHandler Namespace EchoHub.Client.Commands Assembly EchoHub.Client.dll public class CommandHandler Inheritance object CommandHandler Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors CommandHandler() public CommandHandler() Methods HandleAsync(string) public Task HandleAsync(string input) Parameters input string Returns Task IsCommand(string) public bool IsCommand(string input) Parameters input string Returns bool Events OnAssignRole public event Func? OnAssignRole Event Type Func OnBanUser public event Func? OnBanUser Event Type Func OnChangeRoomPassword public event Func? OnChangeRoomPassword Event Type Func OnClearAttachments public event Func? OnClearAttachments Event Type Func OnHelp public event Func? OnHelp Event Type Func OnJoinChannel public event Func? OnJoinChannel Event Type Func OnKickUser public event Func? OnKickUser Event Type Func OnLeaveChannel public event Func? OnLeaveChannel Event Type Func OnListUsers public event Func? OnListUsers Event Type Func OnMuteUser public event Func? OnMuteUser Event Type Func OnNukeChannel public event Func? OnNukeChannel Event Type Func OnOpenProfile public event Func? OnOpenProfile Event Type Func OnOpenServers public event Func? OnOpenServers Event Type Func OnQuit public event Func? OnQuit Event Type Func OnRoomInfo public event Func? OnRoomInfo Event Type Func OnSendFile public event Func? OnSendFile Event Type Func OnSetAsciiSize public event Func? OnSetAsciiSize Event Type Func OnSetAvatar public event Func? OnSetAvatar Event Type Func OnSetColor public event Func? OnSetColor Event Type Func OnSetDownloadPath public event Func? OnSetDownloadPath Event Type Func OnSetNick public event Func? OnSetNick Event Type Func OnSetStatus public event Func? OnSetStatus Event Type Func OnSetTheme public event Func? OnSetTheme Event Type Func OnSetTopic public event Func? OnSetTopic Event Type Func OnTestSound public event Func? OnTestSound Event Type Func OnUnbanUser public event Func? OnUnbanUser Event Type Func OnUnmuteUser public event Func? OnUnmuteUser Event Type Func" }, "api/client/EchoHub.Client.Commands.CommandResult.html": { "href": "api/client/EchoHub.Client.Commands.CommandResult.html", @@ -47,7 +47,7 @@ "api/client/EchoHub.Client.Config.SavedServer.html": { "href": "api/client/EchoHub.Client.Config.SavedServer.html", "title": "Class SavedServer | EchoHub Documentation", - "summary": "Class SavedServer Namespace EchoHub.Client.Config Assembly EchoHub.Client.dll public class SavedServer Inheritance object SavedServer Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors SavedServer() public SavedServer() Properties ChannelKeys Cached room content keys for end-to-end encrypted channels on this server, keyed by channel name (base64). Like RefreshToken, these live only on the user's machine — the server never sees them. public Dictionary ChannelKeys { get; set; } Property Value Dictionary LastConnected public DateTimeOffset LastConnected { get; set; } Property Value DateTimeOffset Name public required string Name { get; set; } Property Value string RefreshToken public string? RefreshToken { get; set; } Property Value string RememberMe public bool RememberMe { get; set; } Property Value bool Url public required string Url { get; set; } Property Value string Username public string? Username { get; set; } Property Value string" + "summary": "Class SavedServer Namespace EchoHub.Client.Config Assembly EchoHub.Client.dll public class SavedServer Inheritance object SavedServer Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors SavedServer() public SavedServer() Properties ChannelKeys Cached room content keys for end-to-end encrypted channels on this server, keyed by channel name (base64). Like RefreshToken, these live only on the user's machine — the server never sees them. public Dictionary ChannelKeys { get; set; } Property Value Dictionary LastConnected public DateTimeOffset LastConnected { get; set; } Property Value DateTimeOffset LastReadMessages Last message the user has read per channel (message id as string), persisted so unread counts, @mention highlights, and the \"new messages\" marker survive restarts. public Dictionary LastReadMessages { get; set; } Property Value Dictionary LeftChannels Channels the user explicitly left with /leave. Excluded from the automatic join-all-channels pass at connect until the user joins them again. public List LeftChannels { get; set; } Property Value List Name public required string Name { get; set; } Property Value string RefreshToken public string? RefreshToken { get; set; } Property Value string RememberMe public bool RememberMe { get; set; } Property Value bool Url public required string Url { get; set; } Property Value string Username public string? Username { get; set; } Property Value string" }, "api/client/EchoHub.Client.Config.html": { "href": "api/client/EchoHub.Client.Config.html", @@ -57,7 +57,7 @@ "api/client/EchoHub.Client.Services.ApiClient.html": { "href": "api/client/EchoHub.Client.Services.ApiClient.html", "title": "Class ApiClient | EchoHub Documentation", - "summary": "Class ApiClient Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll public sealed class ApiClient : IDisposable Inheritance object ApiClient Implements IDisposable Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ApiClient(string) public ApiClient(string baseUrl) Parameters baseUrl string Properties BaseUrl public string BaseUrl { get; } Property Value string RefreshToken public string? RefreshToken { get; } Property Value string Token public string? Token { get; } Property Value string Methods AssignRoleAsync(string, ServerRole) public Task AssignRoleAsync(string username, ServerRole role) Parameters username string role ServerRole Returns Task BanUserAsync(string, string?) public Task BanUserAsync(string username, string? reason = null) Parameters username string reason string Returns Task CreateChannelAsync(string, string?, bool, string?, string?, string?) public Task CreateChannelAsync(string name, string? topic = null, bool isPublic = true, string? password = null, string? encryptionSalt = null, string? wrappedRoomKey = null) Parameters name string topic string isPublic bool password string encryptionSalt string wrappedRoomKey string Returns Task DeleteChannelAsync(string) public Task DeleteChannelAsync(string channelName) Parameters channelName string Returns Task DeleteMessageAsync(Guid) public Task DeleteMessageAsync(Guid messageId) Parameters messageId Guid Returns Task Dispose() public void Dispose() DownloadFileToTempAsync(string, string) public Task DownloadFileToTempAsync(string relativeUrl, string fileName) Parameters relativeUrl string fileName string Returns Task GetChannelCryptoAsync(string) Fetches a channel's public crypto metadata (whether it's E2E-encrypted and its key-derivation salt). Returns null when the channel doesn't exist. public Task GetChannelCryptoAsync(string channelName) Parameters channelName string Returns Task GetChannelsAsync() public Task> GetChannelsAsync() Returns Task> GetEncryptionKeyAsync() public Task GetEncryptionKeyAsync() Returns Task GetServerInfoAsync() public Task GetServerInfoAsync() Returns Task GetUserProfileAsync(string) public Task GetUserProfileAsync(string username) Parameters username string Returns Task GetValidTokenAsync() Returns a valid access token, refreshing if expired. Used by EchoHubConnection for SignalR token provider. public Task GetValidTokenAsync() Returns Task KickUserAsync(string, string?) public Task KickUserAsync(string username, string? reason = null) Parameters username string reason string Returns Task LoginAsync(string, string) public Task LoginAsync(string username, string password) Parameters username string password string Returns Task LoginWithRefreshTokenAsync(string) public Task LoginWithRefreshTokenAsync(string refreshToken) Parameters refreshToken string Returns Task LogoutAsync() public Task LogoutAsync() Returns Task MuteUserAsync(string, int?, string?) public Task MuteUserAsync(string username, int? durationMinutes = null, string? reason = null) Parameters username string durationMinutes int? reason string Returns Task NukeChannelAsync(string) public Task NukeChannelAsync(string channelName) Parameters channelName string Returns Task RefreshTokenAsync() public Task RefreshTokenAsync() Returns Task RegisterAsync(string, string, string?) public Task RegisterAsync(string username, string password, string? displayName = null) Parameters username string password string displayName string Returns Task RekeyChannelAsync(string, RekeyChannelRequest) public Task RekeyChannelAsync(string channelName, RekeyChannelRequest request) Parameters channelName string request RekeyChannelRequest Returns Task SendMessageWithAttachmentsAsync(string, string, IReadOnlyList, string?) Sends one message with optional text and one or more file attachments. For end-to-end encrypted channels each attachment carries a declared kind and a room-encrypted preview (empty when none); the caption is likewise room-encrypted. public Task SendMessageWithAttachmentsAsync(string channelName, string content, IReadOnlyList attachments, string? size = null) Parameters channelName string content string attachments IReadOnlyList size string Returns Task SendUrlAsync(string, string, string?) public Task SendUrlAsync(string channelName, string url, string? size = null) Parameters channelName string url string size string Returns Task UnbanUserAsync(string) public Task UnbanUserAsync(string username) Parameters username string Returns Task UnmuteUserAsync(string) public Task UnmuteUserAsync(string username) Parameters username string Returns Task UpdateChannelTopicAsync(string, string?) public Task UpdateChannelTopicAsync(string channelName, string? topic) Parameters channelName string topic string Returns Task UpdateProfileAsync(UpdateProfileRequest) public Task UpdateProfileAsync(UpdateProfileRequest request) Parameters request UpdateProfileRequest Returns Task UploadAvatarAsync(Stream, string) public Task UploadAvatarAsync(Stream imageStream, string fileName) Parameters imageStream Stream fileName string Returns Task Events OnTokensRefreshed public event Action? OnTokensRefreshed Event Type Action" + "summary": "Class ApiClient Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll public sealed class ApiClient : IDisposable Inheritance object ApiClient Implements IDisposable Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ApiClient(string) public ApiClient(string baseUrl) Parameters baseUrl string Properties BaseUrl public string BaseUrl { get; } Property Value string RefreshToken public string? RefreshToken { get; } Property Value string Token public string? Token { get; } Property Value string Methods AssignRoleAsync(string, ServerRole) public Task AssignRoleAsync(string username, ServerRole role) Parameters username string role ServerRole Returns Task BanUserAsync(string, string?) public Task BanUserAsync(string username, string? reason = null) Parameters username string reason string Returns Task CreateChannelAsync(string, string?, bool, string?, string?, string?) public Task CreateChannelAsync(string name, string? topic = null, bool isPublic = true, string? password = null, string? encryptionSalt = null, string? wrappedRoomKey = null) Parameters name string topic string isPublic bool password string encryptionSalt string wrappedRoomKey string Returns Task DeleteChannelAsync(string) public Task DeleteChannelAsync(string channelName) Parameters channelName string Returns Task DeleteMessageAsync(Guid) public Task DeleteMessageAsync(Guid messageId) Parameters messageId Guid Returns Task Dispose() public void Dispose() DownloadFileToTempAsync(string, string) public Task DownloadFileToTempAsync(string relativeUrl, string fileName) Parameters relativeUrl string fileName string Returns Task GetChannelCryptoAsync(string) Fetches a channel's public crypto metadata (whether it's E2E-encrypted and its key-derivation salt). Returns null when the channel doesn't exist. public Task GetChannelCryptoAsync(string channelName) Parameters channelName string Returns Task GetChannelMetaAsync(string) Fetches a channel's human-facing metadata (message count, unique posters, estimated size, created date, room id) for the /meta command. Returns null if it doesn't exist. public Task GetChannelMetaAsync(string channelName) Parameters channelName string Returns Task GetChannelsAsync() public Task> GetChannelsAsync() Returns Task> GetEncryptionKeyAsync() public Task GetEncryptionKeyAsync() Returns Task GetServerInfoAsync() public Task GetServerInfoAsync() Returns Task GetUserProfileAsync(string) public Task GetUserProfileAsync(string username) Parameters username string Returns Task GetValidTokenAsync() Returns a valid access token, refreshing if expired. Used by EchoHubConnection for SignalR token provider. public Task GetValidTokenAsync() Returns Task KickUserAsync(string, string?) public Task KickUserAsync(string username, string? reason = null) Parameters username string reason string Returns Task LoginAsync(string, string) public Task LoginAsync(string username, string password) Parameters username string password string Returns Task LoginWithRefreshTokenAsync(string) public Task LoginWithRefreshTokenAsync(string refreshToken) Parameters refreshToken string Returns Task LogoutAsync() public Task LogoutAsync() Returns Task MuteUserAsync(string, int?, string?) public Task MuteUserAsync(string username, int? durationMinutes = null, string? reason = null) Parameters username string durationMinutes int? reason string Returns Task NukeChannelAsync(string) public Task NukeChannelAsync(string channelName) Parameters channelName string Returns Task RefreshTokenAsync() public Task RefreshTokenAsync() Returns Task RegisterAsync(string, string, string?) public Task RegisterAsync(string username, string password, string? displayName = null) Parameters username string password string displayName string Returns Task RekeyChannelAsync(string, RekeyChannelRequest) public Task RekeyChannelAsync(string channelName, RekeyChannelRequest request) Parameters channelName string request RekeyChannelRequest Returns Task SendMessageWithAttachmentsAsync(string, string, IReadOnlyList, string?) Sends one message with optional text and one or more file attachments. For end-to-end encrypted channels each attachment carries a declared kind and a room-encrypted preview (empty when none); the caption is likewise room-encrypted. public Task SendMessageWithAttachmentsAsync(string channelName, string content, IReadOnlyList attachments, string? size = null) Parameters channelName string content string attachments IReadOnlyList size string Returns Task SendUrlAsync(string, string, string?) public Task SendUrlAsync(string channelName, string url, string? size = null) Parameters channelName string url string size string Returns Task UnbanUserAsync(string) public Task UnbanUserAsync(string username) Parameters username string Returns Task UnmuteUserAsync(string) public Task UnmuteUserAsync(string username) Parameters username string Returns Task UpdateChannelTopicAsync(string, string?) public Task UpdateChannelTopicAsync(string channelName, string? topic) Parameters channelName string topic string Returns Task UpdateProfileAsync(UpdateProfileRequest) public Task UpdateProfileAsync(UpdateProfileRequest request) Parameters request UpdateProfileRequest Returns Task UploadAvatarAsync(Stream, string) public Task UploadAvatarAsync(Stream imageStream, string fileName) Parameters imageStream Stream fileName string Returns Task Events OnTokensRefreshed public event Action? OnTokensRefreshed Event Type Action" }, "api/client/EchoHub.Client.Services.AsyncRunner.html": { "href": "api/client/EchoHub.Client.Services.AsyncRunner.html", @@ -142,7 +142,7 @@ "api/client/EchoHub.Client.Services.UpdateChecker.html": { "href": "api/client/EchoHub.Client.Services.UpdateChecker.html", "title": "Class UpdateChecker | EchoHub Documentation", - "summary": "Class UpdateChecker Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll public sealed class UpdateChecker : IDisposable Inheritance object UpdateChecker Implements IDisposable Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors UpdateChecker(IApplication) public UpdateChecker(IApplication app) Parameters app IApplication Properties CurrentVersion public static string CurrentVersion { get; } Property Value string Methods CheckNowAsync() public Task CheckNowAsync() Returns Task Dispose() public void Dispose() Start() public void Start()" + "summary": "Class UpdateChecker Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll public sealed class UpdateChecker : IDisposable Inheritance object UpdateChecker Implements IDisposable Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors UpdateChecker(IApplication) public UpdateChecker(IApplication app) Parameters app IApplication Properties CurrentVersion public static string CurrentVersion { get; } Property Value string PendingUpdate Set when the user confirms an update. The host runs this after the Terminal.Gui main loop has exited and the console is restored, so the library's in-place restart doesn't deadlock against a TUI that still owns the console. public Func? PendingUpdate { get; } Property Value Func Methods CheckNowAsync() public Task CheckNowAsync() Returns Task Dispose() public void Dispose() Start() public void Start()" }, "api/client/EchoHub.Client.Services.html": { "href": "api/client/EchoHub.Client.Services.html", @@ -152,7 +152,7 @@ "api/client/EchoHub.Client.Themes.Theme.html": { "href": "api/client/EchoHub.Client.Themes.Theme.html", "title": "Class Theme | EchoHub Documentation", - "summary": "Class Theme Namespace EchoHub.Client.Themes Assembly EchoHub.Client.dll public class Theme Inheritance object Theme Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors Theme() public Theme() Properties Base public ThemeColors Base { get; set; } Property Value ThemeColors Dialog public ThemeColors Dialog { get; set; } Property Value ThemeColors Menu public ThemeColors Menu { get; set; } Property Value ThemeColors Name public required string Name { get; set; } Property Value string Status public ThemeColors Status { get; set; } Property Value ThemeColors" + "summary": "Class Theme Namespace EchoHub.Client.Themes Assembly EchoHub.Client.dll public class Theme Inheritance object Theme Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors Theme() public Theme() Properties Base public ThemeColors Base { get; set; } Property Value ThemeColors Border Colors for the main-window frame borders (and their titles). Null falls back to Base. Lets themes tone borders down independently of text — e.g. the transparent themes use a dim gray for a subtler, glassy look. Supports hex values (\"#6E6E6E\") as well as named colors. public ThemeColors? Border { get; set; } Property Value ThemeColors Dialog public ThemeColors Dialog { get; set; } Property Value ThemeColors Menu public ThemeColors Menu { get; set; } Property Value ThemeColors Name public required string Name { get; set; } Property Value string Status public ThemeColors Status { get; set; } Property Value ThemeColors" }, "api/client/EchoHub.Client.Themes.ThemeColors.html": { "href": "api/client/EchoHub.Client.Themes.ThemeColors.html", @@ -172,12 +172,12 @@ "api/client/EchoHub.Client.UI.Chat.ChatColors.html": { "href": "api/client/EchoHub.Client.UI.Chat.ChatColors.html", "title": "Class ChatColors | EchoHub Documentation", - "summary": "Class ChatColors Namespace EchoHub.Client.UI.Chat Assembly EchoHub.Client.dll Shared color attributes for chat rendering (timestamps, system messages). public static class ChatColors Inheritance object ChatColors Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Fields AudioAttr public static readonly Attribute AudioAttr Field Value Attribute ChannelRefAttr public static readonly Attribute ChannelRefAttr Field Value Attribute EmbedBorderAttr public static readonly Attribute EmbedBorderAttr Field Value Attribute EmbedDescAttr public static readonly Attribute EmbedDescAttr Field Value Attribute EmbedTitleAttr public static readonly Attribute EmbedTitleAttr Field Value Attribute EmbedUrlAttr public static readonly Attribute EmbedUrlAttr Field Value Attribute FileAttr public static readonly Attribute FileAttr Field Value Attribute MentionHighlightAttr public static readonly Attribute MentionHighlightAttr Field Value Attribute MentionTextAttr public static readonly Attribute MentionTextAttr Field Value Attribute SystemAttr public static readonly Attribute SystemAttr Field Value Attribute TimestampAttr public static readonly Attribute TimestampAttr Field Value Attribute Methods SplitMentions(string, Attribute?) Split text around @mentions and #channels, giving each the appropriate accent color. Non-special text uses the provided default color. public static List SplitMentions(string text, Attribute? defaultColor = null) Parameters text string defaultColor Attribute? Returns List" + "summary": "Class ChatColors Namespace EchoHub.Client.UI.Chat Assembly EchoHub.Client.dll Shared color attributes for chat rendering (timestamps, system messages). public static class ChatColors Inheritance object ChatColors Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Fields AudioAttr public static readonly Attribute AudioAttr Field Value Attribute ChannelRefAttr public static readonly Attribute ChannelRefAttr Field Value Attribute DateRuleAttr Horizontal date-separator rules (── Wed, Jul 16 ──…). public static readonly Attribute DateRuleAttr Field Value Attribute EmbedBorderAttr public static readonly Attribute EmbedBorderAttr Field Value Attribute EmbedDescAttr public static readonly Attribute EmbedDescAttr Field Value Attribute EmbedTitleAttr public static readonly Attribute EmbedTitleAttr Field Value Attribute EmbedUrlAttr public static readonly Attribute EmbedUrlAttr Field Value Attribute FileAttr public static readonly Attribute FileAttr Field Value Attribute MentionHighlightAttr public static readonly Attribute MentionHighlightAttr Field Value Attribute MentionTextAttr public static readonly Attribute MentionTextAttr Field Value Attribute RailAttr The dim vertical rail (│) separating the nick column from message text. public static readonly Attribute RailAttr Field Value Attribute SystemAttr public static readonly Attribute SystemAttr Field Value Attribute TimestampAttr public static readonly Attribute TimestampAttr Field Value Attribute UnreadMarkerAttr The irssi-style \"new messages\" unread marker rule. public static readonly Attribute UnreadMarkerAttr Field Value Attribute Methods SplitMentions(string, Attribute?) Split text around @mentions and #channels, giving each the appropriate accent color. Non-special text uses the provided default color. public static List SplitMentions(string text, Attribute? defaultColor = null) Parameters text string defaultColor Attribute? Returns List" }, "api/client/EchoHub.Client.UI.Chat.ChatLine.html": { "href": "api/client/EchoHub.Client.UI.Chat.ChatLine.html", "title": "Class ChatLine | EchoHub Documentation", - "summary": "Class ChatLine Namespace EchoHub.Client.UI.Chat Assembly EchoHub.Client.dll A single line in the chat, composed of colored segments. public class ChatLine Inheritance object ChatLine Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ChatLine(List) public ChatLine(List segments) Parameters segments List ChatLine(string) public ChatLine(string plainText) Parameters plainText string Properties AttachmentFileName public string? AttachmentFileName { get; set; } Property Value string AttachmentKind public AttachmentKind? AttachmentKind { get; set; } Property Value AttachmentKind? AttachmentUrl public string? AttachmentUrl { get; set; } Property Value string ContinuationIndent Number of spaces to prepend on continuation lines when this line is word-wrapped. public int ContinuationIndent { get; set; } Property Value int IsMention public bool IsMention { get; set; } Property Value bool MessageId public Guid? MessageId { get; set; } Property Value Guid? Segments public List Segments { get; } Property Value List SenderUsername public string? SenderUsername { get; set; } Property Value string TextLength public int TextLength { get; } Property Value int Methods FromColoredText(string, Attribute?) Parse a string containing printable color tags into colored segments. Format: {F:RRGGBB} (foreground), {B:RRGGBB} (background), {X} (reset). public static ChatLine FromColoredText(string text, Attribute? defaultAttr = null) Parameters text string defaultAttr Attribute? Returns ChatLine HasColorTags(string) Returns true if a line contains printable color tags. public static bool HasColorTags(string text) Parameters text string Returns bool StripColorTags(string) Remove all color tags from text, returning only the visible characters. public static string StripColorTags(string text) Parameters text string Returns string ToString() public override string ToString() Returns string Wrap(int, int) Wrap this line into multiple lines that fit within the given width. Continuation lines are indented with the specified number of spaces. public List Wrap(int width, int continuationIndent = 0) Parameters width int continuationIndent int Returns List" + "summary": "Class ChatLine Namespace EchoHub.Client.UI.Chat Assembly EchoHub.Client.dll A single line in the chat, composed of colored segments. public class ChatLine Inheritance object ChatLine Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ChatLine(List) public ChatLine(List segments) Parameters segments List ChatLine(string) public ChatLine(string plainText) Parameters plainText string Properties AttachmentFileName public string? AttachmentFileName { get; set; } Property Value string AttachmentKind public AttachmentKind? AttachmentKind { get; set; } Property Value AttachmentKind? AttachmentUrl public string? AttachmentUrl { get; set; } Property Value string ContinuationIndent Number of spaces to prepend on continuation lines when this line is word-wrapped. public int ContinuationIndent { get; set; } Property Value int ContinuationPrefixSegments Colored segments to prepend on continuation lines instead of plain spaces (e.g. the nick-column rail \" │ \"). When set, takes precedence over ContinuationIndent. public List? ContinuationPrefixSegments { get; set; } Property Value List IsMention public bool IsMention { get; set; } Property Value bool IsUnreadMarker True for the \"new messages\" unread-marker rule so it can be removed on channel switch. public bool IsUnreadMarker { get; set; } Property Value bool MessageId public Guid? MessageId { get; set; } Property Value Guid? RuleAttr Color for a rule line; null falls back to DateRuleAttr. public Attribute? RuleAttr { get; set; } Property Value Attribute? RuleLabel When set, this line is a horizontal separator rule (date change, unread marker). The view regenerates it to the current viewport width instead of word-wrapping. public string? RuleLabel { get; set; } Property Value string Segments public List Segments { get; } Property Value List SenderUsername public string? SenderUsername { get; set; } Property Value string TextLength public int TextLength { get; } Property Value int Methods FromColoredText(string, Attribute?) Parse a string containing printable color tags into colored segments. Format: {F:RRGGBB} (foreground), {B:RRGGBB} (background), {X} (reset). public static ChatLine FromColoredText(string text, Attribute? defaultAttr = null) Parameters text string defaultAttr Attribute? Returns ChatLine HasColorTags(string) Returns true if a line contains printable color tags. public static bool HasColorTags(string text) Parameters text string Returns bool StripColorTags(string) Remove all color tags from text, returning only the visible characters. public static string StripColorTags(string text) Parameters text string Returns string ToString() public override string ToString() Returns string Wrap(int, int) Wrap this line into multiple lines that fit within the given width. Continuation lines are indented with the specified number of spaces. public List Wrap(int width, int continuationIndent = 0) Parameters width int continuationIndent int Returns List" }, "api/client/EchoHub.Client.UI.Chat.ChatListSource.html": { "href": "api/client/EchoHub.Client.UI.Chat.ChatListSource.html", @@ -187,7 +187,7 @@ "api/client/EchoHub.Client.UI.Chat.ChatMessageManager.html": { "href": "api/client/EchoHub.Client.UI.Chat.ChatMessageManager.html", "title": "Class ChatMessageManager | EchoHub Documentation", - "summary": "Class ChatMessageManager Namespace EchoHub.Client.UI.Chat Assembly EchoHub.Client.dll Owns chat message storage, formatting, and mutation. Fires MessagesChanged when a channel's message list is modified so the UI layer can refresh. public sealed class ChatMessageManager Inheritance object ChatMessageManager Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ChatMessageManager() public ChatMessageManager() Properties CurrentChannel The currently active channel (used for unread tracking and @mention detection). public string CurrentChannel { get; set; } Property Value string CurrentUser public string CurrentUser { get; } Property Value string Methods AddMessage(MessageDto) Format and store a received message. Increments unread count if not the active channel. public void AddMessage(MessageDto message) Parameters message MessageDto AddStatusMessage(string, string, string) Add a status change message to a channel with colored styling. public void AddStatusMessage(string channelName, string username, string status) Parameters channelName string username string status string AddSystemMessage(string, string) Add a system/informational message to a channel with colored styling. public void AddSystemMessage(string channelName, string text) Parameters channelName string text string ClearAll() Reset all message state (used on disconnect). public void ClearAll() ClearChannelMessages(string) Clear all messages from a specific channel. public void ClearChannelMessages(string channelName) Parameters channelName string ClearUnread(string) public void ClearUnread(string channelName) Parameters channelName string GetMessages(string) public List? GetMessages(string channelName) Parameters channelName string Returns List GetUnreadCount(string) public int GetUnreadCount(string channelName) Parameters channelName string Returns int LoadHistory(string, List) Load historical messages into a channel, replacing any existing messages. public void LoadHistory(string channelName, List messages) Parameters channelName string messages List PrependHistory(string, List) Prepend older messages at the front of a channel's buffer, skipping any that are already present. Fires HistoryPrepended when new lines are actually inserted. public void PrependHistory(string channelName, List olderMessages) Parameters channelName string olderMessages List RemoveMessage(string, Guid) Remove all lines associated with a specific message ID. public void RemoveMessage(string channelName, Guid messageId) Parameters channelName string messageId Guid SetChatWidth(int) public void SetChatWidth(int width) Parameters width int SetCurrentUser(string) public void SetCurrentUser(string username) Parameters username string Events HistoryPrepended Fired after older messages are prepended to a channel's buffer. Parameter is the channel name. public event Action? HistoryPrepended Event Type Action MessagesChanged Fired after any mutation to a channel's messages. Parameter is the channel name. public event Action? MessagesChanged Event Type Action" + "summary": "Class ChatMessageManager Namespace EchoHub.Client.UI.Chat Assembly EchoHub.Client.dll Owns chat message storage, formatting, and mutation. Fires MessagesChanged when a channel's message list is modified so the UI layer can refresh. public sealed class ChatMessageManager Inheritance object ChatMessageManager Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ChatMessageManager() public ChatMessageManager() Fields ContentIndentCols Columns before message text starts: \"HH:mm \" + nick column + \" │ \". public const int ContentIndentCols = 21 Field Value int NickColWidth Columns reserved for the right-aligned nick column (WeeChat-style). public const int NickColWidth = 12 Field Value int Properties CurrentChannel The currently active channel (used for unread tracking and @mention detection). public string CurrentChannel { get; set; } Property Value string CurrentUser public string CurrentUser { get; } Property Value string LastReadIds Last message the user has read per channel — persisted by the orchestrator so unread/mention state can be seeded from history on the next connect. public IReadOnlyDictionary LastReadIds { get; } Property Value IReadOnlyDictionary MentionChannels Channels with an unread @mention of the current user (cleared by ClearUnread(string)). public IReadOnlySet MentionChannels { get; } Property Value IReadOnlySet Methods AddMessage(MessageDto) Format and store a received message. Increments unread count if not the active channel. public void AddMessage(MessageDto message) Parameters message MessageDto AddStatusMessage(string, string, string) Add a status change message to a channel with colored styling. public void AddStatusMessage(string channelName, string username, string status) Parameters channelName string username string status string AddSystemMessage(string, string) Add a system/informational message to a channel with colored styling. public void AddSystemMessage(string channelName, string text) Parameters channelName string text string ClearAll() Reset all message state (used on disconnect). public void ClearAll() ClearChannelMessages(string) Clear all messages from a specific channel. public void ClearChannelMessages(string channelName) Parameters channelName string ClearUnread(string) public void ClearUnread(string channelName) Parameters channelName string GetMessages(string) public List? GetMessages(string channelName) Parameters channelName string Returns List GetUnreadCount(string) public int GetUnreadCount(string channelName) Parameters channelName string Returns int LoadHistory(string, List, Guid?) Load historical messages into a channel, replacing any existing messages. When lastReadId is given (persisted from a previous session), messages after it seed the unread count, @mention highlight, and the \"new messages\" marker — so activity that happened while offline still lights up. public void LoadHistory(string channelName, List messages, Guid? lastReadId = null) Parameters channelName string messages List lastReadId Guid? PrependHistory(string, List) Prepend older messages at the front of a channel's buffer, skipping any that are already present. Fires HistoryPrepended when new lines are actually inserted. public void PrependHistory(string channelName, List olderMessages) Parameters channelName string olderMessages List RemoveMessage(string, Guid) Remove all lines associated with a specific message ID. public void RemoveMessage(string channelName, Guid messageId) Parameters channelName string messageId Guid SetChatWidth(int) public void SetChatWidth(int width) Parameters width int SetCurrentUser(string) public void SetCurrentUser(string username) Parameters username string Events HistoryPrepended Fired after older messages are prepended to a channel's buffer. Parameter is the channel name. public event Action? HistoryPrepended Event Type Action MessagesChanged Fired after any mutation to a channel's messages. Parameter is the channel name. public event Action? MessagesChanged Event Type Action" }, "api/client/EchoHub.Client.UI.Chat.ChatSegment.html": { "href": "api/client/EchoHub.Client.UI.Chat.ChatSegment.html", @@ -279,15 +279,10 @@ "title": "Class UpdateConfirmDialog | EchoHub Documentation", "summary": "Class UpdateConfirmDialog Namespace EchoHub.Client.UI.Dialogs Assembly EchoHub.Client.dll public sealed class UpdateConfirmDialog Inheritance object UpdateConfirmDialog Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors UpdateConfirmDialog() public UpdateConfirmDialog() Methods Show(IApplication, string, string) public static bool Show(IApplication app, string currentVersion, string newVersion) Parameters app IApplication currentVersion string newVersion string Returns bool" }, - "api/client/EchoHub.Client.UI.Dialogs.UpdateProgressDialog.html": { - "href": "api/client/EchoHub.Client.UI.Dialogs.UpdateProgressDialog.html", - "title": "Class UpdateProgressDialog | EchoHub Documentation", - "summary": "Class UpdateProgressDialog Namespace EchoHub.Client.UI.Dialogs Assembly EchoHub.Client.dll public sealed class UpdateProgressDialog Inheritance object UpdateProgressDialog Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors UpdateProgressDialog(IApplication, string) public UpdateProgressDialog(IApplication app, string newVersion) Parameters app IApplication newVersion string Methods Close() public void Close() Show() public void Show() UpdateProgress(float, string) public void UpdateProgress(float fraction, string statusText) Parameters fraction float statusText string" - }, "api/client/EchoHub.Client.UI.Dialogs.html": { "href": "api/client/EchoHub.Client.UI.Dialogs.html", "title": "Namespace EchoHub.Client.UI.Dialogs | EchoHub Documentation", - "summary": "Namespace EchoHub.Client.UI.Dialogs Classes AudioPlayerDialog ChannelPasswordDialog Prompts for a channel password when joining a protected channel. Returns the entered password, or null if the user cancels. ConnectDialog A Terminal.Gui dialog for entering server connection and authentication details. Includes a saved servers selector when saved servers are available. ConnectDialogResult Result returned from the connect dialog. CreateChannelDialog CreateChannelResult ProfileEditDialog A Terminal.Gui dialog for editing the user's profile (display name, bio, nickname color). ProfileEditResult Result returned from the profile edit dialog. ProfileViewDialog Dialog for viewing a user's server profile. Shows Edit Profile / Set Status buttons when viewing own profile. SearchDialog Command-palette style search dialog (Ctrl+K) for navigating channels and triggering app actions. SearchResult StatusDialog A Terminal.Gui dialog for setting the user's status and status message. StatusDialogResult Result returned from the status dialog. UpdateConfirmDialog UpdateProgressDialog Enums ProfileAction Action selected by the user in their own profile dialog. SearchResultType" + "summary": "Namespace EchoHub.Client.UI.Dialogs Classes AudioPlayerDialog ChannelPasswordDialog Prompts for a channel password when joining a protected channel. Returns the entered password, or null if the user cancels. ConnectDialog A Terminal.Gui dialog for entering server connection and authentication details. Includes a saved servers selector when saved servers are available. ConnectDialogResult Result returned from the connect dialog. CreateChannelDialog CreateChannelResult ProfileEditDialog A Terminal.Gui dialog for editing the user's profile (display name, bio, nickname color). ProfileEditResult Result returned from the profile edit dialog. ProfileViewDialog Dialog for viewing a user's server profile. Shows Edit Profile / Set Status buttons when viewing own profile. SearchDialog Command-palette style search dialog (Ctrl+K) for navigating channels and triggering app actions. SearchResult StatusDialog A Terminal.Gui dialog for setting the user's status and status message. StatusDialogResult Result returned from the status dialog. UpdateConfirmDialog Enums ProfileAction Action selected by the user in their own profile dialog. SearchResultType" }, "api/client/EchoHub.Client.UI.Helpers.DroppedFileParser.html": { "href": "api/client/EchoHub.Client.UI.Helpers.DroppedFileParser.html", @@ -304,15 +299,20 @@ "title": "Class HexColorHelper | EchoHub Documentation", "summary": "Class HexColorHelper Namespace EchoHub.Client.UI.Helpers Assembly EchoHub.Client.dll Helper to parse hex colors to Terminal.Gui Attributes. public static class HexColorHelper Inheritance object HexColorHelper Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Methods ParseHexColor(string?) public static Attribute? ParseHexColor(string? hex) Parameters hex string Returns Attribute? ParseHexToColor(string?, Color) Parse a hex color string (with or without #) to a Terminal.Gui Color. Returns fallback if parsing fails. public static Color ParseHexToColor(string? hex, Color fallback = default) Parameters hex string fallback Color Returns Color" }, + "api/client/EchoHub.Client.UI.Helpers.NickColorHelper.html": { + "href": "api/client/EchoHub.Client.UI.Helpers.NickColorHelper.html", + "title": "Class NickColorHelper | EchoHub Documentation", + "summary": "Class NickColorHelper Namespace EchoHub.Client.UI.Helpers Assembly EchoHub.Client.dll Deterministic per-nick colors for users who haven't picked a nickname color. The same nick always maps to the same palette entry (classic IRC client behavior), so a busy channel stays scannable without any configuration. public static class NickColorHelper Inheritance object NickColorHelper Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Methods GetAttribute(string) The color attribute for a nick. Used as a fallback when the user has no explicit nickname color set. public static Attribute GetAttribute(string nick) Parameters nick string Returns Attribute GetPaletteIndex(string, int) Stable palette index for a nick: case-insensitive FNV-1a over the nick, reduced modulo paletteSize. Pure function (no Terminal.Gui types) so it is unit-testable without a display driver. public static int GetPaletteIndex(string nick, int paletteSize) Parameters nick string paletteSize int Returns int" + }, "api/client/EchoHub.Client.UI.Helpers.html": { "href": "api/client/EchoHub.Client.UI.Helpers.html", "title": "Namespace EchoHub.Client.UI.Helpers | EchoHub Documentation", - "summary": "Namespace EchoHub.Client.UI.Helpers Classes DroppedFileParser Recognizes a dragged-and-dropped file (or files) that a terminal delivers into the input as an absolute path. Terminals differ: some paste the whole path at once, others send it character by character; either way this checks whether the current input text resolves to existing file(s). EmojiHelper Converts emoji grapheme clusters to text shortcodes for safe TUI rendering. Terminal width calculations for emoji are unreliable across different terminals, so we replace them with fixed-width ASCII shortcodes for display only. HexColorHelper Helper to parse hex colors to Terminal.Gui Attributes." + "summary": "Namespace EchoHub.Client.UI.Helpers Classes DroppedFileParser Recognizes a dragged-and-dropped file (or files) that a terminal delivers into the input as an absolute path. Terminals differ: some paste the whole path at once, others send it character by character; either way this checks whether the current input text resolves to existing file(s). EmojiHelper Converts emoji grapheme clusters to text shortcodes for safe TUI rendering. Terminal width calculations for emoji are unreliable across different terminals, so we replace them with fixed-width ASCII shortcodes for display only. HexColorHelper Helper to parse hex colors to Terminal.Gui Attributes. NickColorHelper Deterministic per-nick colors for users who haven't picked a nickname color. The same nick always maps to the same palette entry (classic IRC client behavior), so a busy channel stays scannable without any configuration." }, "api/client/EchoHub.Client.UI.ListSources.ChannelListSource.html": { "href": "api/client/EchoHub.Client.UI.ListSources.ChannelListSource.html", "title": "Class ChannelListSource | EchoHub Documentation", - "summary": "Class ChannelListSource Namespace EchoHub.Client.UI.ListSources Assembly EchoHub.Client.dll Custom list data source for colored channel list rendering. Active channel gets a > indicator, unread channels are bright with a count badge. public class ChannelListSource : IListDataSource, IDisposable Inheritance object ChannelListSource Implements IListDataSource IDisposable Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ChannelListSource() public ChannelListSource() Properties Count public int Count { get; } Property Value int MaxItemLength public int MaxItemLength { get; } Property Value int SuspendCollectionChangedEvent public bool SuspendCollectionChangedEvent { get; set; } Property Value bool Methods Dispose() public void Dispose() IsMarked(int) public bool IsMarked(int item) Parameters item int Returns bool Render(ListView, bool, int, int, int, int, int) public void Render(ListView listView, bool selected, int item, int col, int row, int width, int viewportX = 0) Parameters listView ListView selected bool item int col int row int width int viewportX int SetMark(int, bool) public void SetMark(int item, bool value) Parameters item int value bool ToList() public IList ToList() Returns IList Update(List, Dictionary, string, IReadOnlySet?) public void Update(List channels, Dictionary unread, string activeChannel, IReadOnlySet? protectedChannels = null) Parameters channels List unread Dictionary activeChannel string protectedChannels IReadOnlySet Events CollectionChanged public event NotifyCollectionChangedEventHandler? CollectionChanged Event Type NotifyCollectionChangedEventHandler" + "summary": "Class ChannelListSource Namespace EchoHub.Client.UI.ListSources Assembly EchoHub.Client.dll Custom list data source for colored channel list rendering. Active channel gets a > indicator, unread channels are bright with a count badge. public class ChannelListSource : IListDataSource, IDisposable Inheritance object ChannelListSource Implements IListDataSource IDisposable Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ChannelListSource() public ChannelListSource() Properties Count public int Count { get; } Property Value int MaxItemLength public int MaxItemLength { get; } Property Value int SuspendCollectionChangedEvent public bool SuspendCollectionChangedEvent { get; set; } Property Value bool Methods Dispose() public void Dispose() IsMarked(int) public bool IsMarked(int item) Parameters item int Returns bool Render(ListView, bool, int, int, int, int, int) public void Render(ListView listView, bool selected, int item, int col, int row, int width, int viewportX = 0) Parameters listView ListView selected bool item int col int row int width int viewportX int SetMark(int, bool) public void SetMark(int item, bool value) Parameters item int value bool ToList() public IList ToList() Returns IList Update(List, Dictionary, string, IReadOnlySet?, IReadOnlySet?) public void Update(List channels, Dictionary unread, string activeChannel, IReadOnlySet? protectedChannels = null, IReadOnlySet? mentionChannels = null) Parameters channels List unread Dictionary activeChannel string protectedChannels IReadOnlySet mentionChannels IReadOnlySet Events CollectionChanged public event NotifyCollectionChangedEventHandler? CollectionChanged Event Type NotifyCollectionChangedEventHandler" }, "api/client/EchoHub.Client.UI.ListSources.SearchListSource.html": { "href": "api/client/EchoHub.Client.UI.ListSources.SearchListSource.html", @@ -372,7 +372,7 @@ "api/core/EchoHub.Core.Contracts.IChannelService.html": { "href": "api/core/EchoHub.Core.Contracts.IChannelService.html", "title": "Interface IChannelService | EchoHub Documentation", - "summary": "Interface IChannelService Namespace EchoHub.Core.Contracts Assembly EchoHub.Core.dll public interface IChannelService Methods CreateChannelAsync(Guid, string, string?, bool, string?, string?, string?) Task CreateChannelAsync(Guid creatorUserId, string name, string? topic, bool isPublic, string? password = null, string? encryptionSalt = null, string? wrappedRoomKey = null) Parameters creatorUserId Guid name string topic string isPublic bool password string encryptionSalt string wrappedRoomKey string Returns Task DeleteChannelAsync(Guid, string) Task DeleteChannelAsync(Guid callerUserId, string channelName) Parameters callerUserId Guid channelName string Returns Task EnsureChannelMembershipAsync(Guid, string, string?) Task<(bool Success, string? Error, bool PasswordRequired)> EnsureChannelMembershipAsync(Guid userId, string channelName, string? password = null) Parameters userId Guid channelName string password string Returns Task<(bool Success, string Error, bool PasswordRequired)> GetChannelByNameAsync(string) Task GetChannelByNameAsync(string channelName) Parameters channelName string Returns Task GetChannelCryptoAsync(string) Task GetChannelCryptoAsync(string channelName) Parameters channelName string Returns Task GetChannelKeyEnvelopeAsync(string) Task<(string? EncryptionSalt, string? WrappedRoomKey)> GetChannelKeyEnvelopeAsync(string channelName) Parameters channelName string Returns Task<(string EncryptionSalt, string WrappedRoomKey)> GetChannelListAsync() Task> GetChannelListAsync() Returns Task> GetChannelTopicAsync(string) Task<(string? Topic, bool Exists)> GetChannelTopicAsync(string channelName) Parameters channelName string Returns Task<(string Topic, bool Exists)> GetChannelsAsync(Guid, int, int) Task> GetChannelsAsync(Guid userId, int offset, int limit) Parameters userId Guid offset int limit int Returns Task> RekeyChannelAsync(Guid, string, string, string, string, string) Task RekeyChannelAsync(Guid callerUserId, string channelName, string oldPassword, string newPassword, string newEncryptionSalt, string newWrappedRoomKey) Parameters callerUserId Guid channelName string oldPassword string newPassword string newEncryptionSalt string newWrappedRoomKey string Returns Task SetChannelPasswordAsync(Guid, string, string?) Task SetChannelPasswordAsync(Guid callerUserId, string channelName, string? password) Parameters callerUserId Guid channelName string password string Returns Task UpdateTopicAsync(Guid, string, string?) Task UpdateTopicAsync(Guid callerUserId, string channelName, string? topic) Parameters callerUserId Guid channelName string topic string Returns Task" + "summary": "Interface IChannelService Namespace EchoHub.Core.Contracts Assembly EchoHub.Core.dll public interface IChannelService Methods CreateChannelAsync(Guid, string, string?, bool, string?, string?, string?) Task CreateChannelAsync(Guid creatorUserId, string name, string? topic, bool isPublic, string? password = null, string? encryptionSalt = null, string? wrappedRoomKey = null) Parameters creatorUserId Guid name string topic string isPublic bool password string encryptionSalt string wrappedRoomKey string Returns Task DeleteChannelAsync(Guid, string) Task DeleteChannelAsync(Guid callerUserId, string channelName) Parameters callerUserId Guid channelName string Returns Task EnsureChannelMembershipAsync(Guid, string, string?) Task<(bool Success, string? Error, bool PasswordRequired)> EnsureChannelMembershipAsync(Guid userId, string channelName, string? password = null) Parameters userId Guid channelName string password string Returns Task<(bool Success, string Error, bool PasswordRequired)> GetChannelByNameAsync(string) Task GetChannelByNameAsync(string channelName) Parameters channelName string Returns Task GetChannelCryptoAsync(string) Task GetChannelCryptoAsync(string channelName) Parameters channelName string Returns Task GetChannelKeyEnvelopeAsync(string) Task<(string? EncryptionSalt, string? WrappedRoomKey)> GetChannelKeyEnvelopeAsync(string channelName) Parameters channelName string Returns Task<(string EncryptionSalt, string WrappedRoomKey)> GetChannelListAsync() Task> GetChannelListAsync() Returns Task> GetChannelMetaAsync(string) Task GetChannelMetaAsync(string channelName) Parameters channelName string Returns Task GetChannelTopicAsync(string) Task<(string? Topic, bool Exists)> GetChannelTopicAsync(string channelName) Parameters channelName string Returns Task<(string Topic, bool Exists)> GetChannelsAsync(Guid, int, int) Task> GetChannelsAsync(Guid userId, int offset, int limit) Parameters userId Guid offset int limit int Returns Task> RekeyChannelAsync(Guid, string, string, string, string, string) Task RekeyChannelAsync(Guid callerUserId, string channelName, string oldPassword, string newPassword, string newEncryptionSalt, string newWrappedRoomKey) Parameters callerUserId Guid channelName string oldPassword string newPassword string newEncryptionSalt string newWrappedRoomKey string Returns Task SetChannelPasswordAsync(Guid, string, string?) Task SetChannelPasswordAsync(Guid callerUserId, string channelName, string? password) Parameters callerUserId Guid channelName string password string Returns Task UpdateTopicAsync(Guid, string, string?) Task UpdateTopicAsync(Guid callerUserId, string channelName, string? topic) Parameters callerUserId Guid channelName string topic string Returns Task" }, "api/core/EchoHub.Core.Contracts.IChatBroadcaster.html": { "href": "api/core/EchoHub.Core.Contracts.IChatBroadcaster.html", @@ -392,7 +392,7 @@ "api/core/EchoHub.Core.Contracts.IMessageEncryptionService.html": { "href": "api/core/EchoHub.Core.Contracts.IMessageEncryptionService.html", "title": "Interface IMessageEncryptionService | EchoHub Documentation", - "summary": "Interface IMessageEncryptionService Namespace EchoHub.Core.Contracts Assembly EchoHub.Core.dll public interface IMessageEncryptionService Properties EncryptDatabaseEnabled Whether database content should be encrypted at rest (server setting). bool EncryptDatabaseEnabled { get; } Property Value bool Methods Decrypt(string) string Decrypt(string content) Parameters content string Returns string DecryptNullable(string?) string? DecryptNullable(string? value) Parameters value string Returns string Encrypt(string) string Encrypt(string plaintext) Parameters plaintext string Returns string EncryptNullable(string?) string? EncryptNullable(string? value) Parameters value string Returns string" + "summary": "Interface IMessageEncryptionService Namespace EchoHub.Core.Contracts Assembly EchoHub.Core.dll public interface IMessageEncryptionService Fields CiphertextPrefix Prefix marking transport/at-rest encrypted content. Decrypt(string) is a pass-through for values without it. public const string CiphertextPrefix = \"$ENC$v1$\" Field Value string Properties EncryptDatabaseEnabled Whether database content should be encrypted at rest (server setting). bool EncryptDatabaseEnabled { get; } Property Value bool Methods Decrypt(string) string Decrypt(string content) Parameters content string Returns string DecryptNullable(string?) string? DecryptNullable(string? value) Parameters value string Returns string Encrypt(string) string Encrypt(string plaintext) Parameters plaintext string Returns string EncryptNullable(string?) string? EncryptNullable(string? value) Parameters value string Returns string" }, "api/core/EchoHub.Core.Contracts.IUserService.html": { "href": "api/core/EchoHub.Core.Contracts.IUserService.html", @@ -449,6 +449,11 @@ "title": "Enum ChannelError | EchoHub Documentation", "summary": "Enum ChannelError Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll public enum ChannelError Fields AlreadyExists = 1 Forbidden = 3 NotFound = 2 Protected = 4 ValidationFailed = 0" }, + "api/core/EchoHub.Core.DTOs.ChannelMetaDto.html": { + "href": "api/core/EchoHub.Core.DTOs.ChannelMetaDto.html", + "title": "Class ChannelMetaDto | EchoHub Documentation", + "summary": "Class ChannelMetaDto Namespace EchoHub.Core.DTOs Assembly EchoHub.Core.dll Human-facing summary of a channel (the /meta command). For encrypted channels the server still knows these figures — count, timestamps, and stored blob sizes — even though it cannot read the content itself. EstimatedSizeBytes is the sum of stored attachment blob sizes plus message text length, so it is an estimate, not an exact on-disk total. public record ChannelMetaDto : IEquatable Inheritance object ChannelMetaDto Implements IEquatable Inherited Members object.GetType() object.MemberwiseClone() object.Equals(object, object) object.ReferenceEquals(object, object) Constructors ChannelMetaDto(ChannelMetaDto) protected ChannelMetaDto(ChannelMetaDto original) Parameters original ChannelMetaDto ChannelMetaDto(Guid, string, string?, bool, bool, int, int, long, DateTimeOffset) Human-facing summary of a channel (the /meta command). For encrypted channels the server still knows these figures — count, timestamps, and stored blob sizes — even though it cannot read the content itself. EstimatedSizeBytes is the sum of stored attachment blob sizes plus message text length, so it is an estimate, not an exact on-disk total. public ChannelMetaDto(Guid Id, string Name, string? Topic, bool IsEncrypted, bool IsProtected, int MessageCount, int UniqueUserCount, long EstimatedSizeBytes, DateTimeOffset CreatedAt) Parameters Id Guid Name string Topic string IsEncrypted bool IsProtected bool MessageCount int UniqueUserCount int EstimatedSizeBytes long CreatedAt DateTimeOffset Properties CreatedAt public DateTimeOffset CreatedAt { get; init; } Property Value DateTimeOffset EqualityContract protected virtual Type EqualityContract { get; } Property Value Type EstimatedSizeBytes public long EstimatedSizeBytes { get; init; } Property Value long Id public Guid Id { get; init; } Property Value Guid IsEncrypted public bool IsEncrypted { get; init; } Property Value bool IsProtected public bool IsProtected { get; init; } Property Value bool MessageCount public int MessageCount { get; init; } Property Value int Name public string Name { get; init; } Property Value string Topic public string? Topic { get; init; } Property Value string UniqueUserCount public int UniqueUserCount { get; init; } Property Value int Methods Deconstruct(out Guid, out string, out string?, out bool, out bool, out int, out int, out long, out DateTimeOffset) public void Deconstruct(out Guid Id, out string Name, out string? Topic, out bool IsEncrypted, out bool IsProtected, out int MessageCount, out int UniqueUserCount, out long EstimatedSizeBytes, out DateTimeOffset CreatedAt) Parameters Id Guid Name string Topic string IsEncrypted bool IsProtected bool MessageCount int UniqueUserCount int EstimatedSizeBytes long CreatedAt DateTimeOffset Equals(ChannelMetaDto?) public virtual bool Equals(ChannelMetaDto? other) Parameters other ChannelMetaDto Returns bool Equals(object?) public override bool Equals(object? obj) Parameters obj object Returns bool GetHashCode() public override int GetHashCode() Returns int PrintMembers(StringBuilder) protected virtual bool PrintMembers(StringBuilder builder) Parameters builder StringBuilder Returns bool ToString() public override string ToString() Returns string Operators operator ==(ChannelMetaDto?, ChannelMetaDto?) public static bool operator ==(ChannelMetaDto? left, ChannelMetaDto? right) Parameters left ChannelMetaDto right ChannelMetaDto Returns bool operator !=(ChannelMetaDto?, ChannelMetaDto?) public static bool operator !=(ChannelMetaDto? left, ChannelMetaDto? right) Parameters left ChannelMetaDto right ChannelMetaDto Returns bool" + }, "api/core/EchoHub.Core.DTOs.ChannelOperationResult.html": { "href": "api/core/EchoHub.Core.DTOs.ChannelOperationResult.html", "title": "Class ChannelOperationResult | EchoHub Documentation", @@ -582,7 +587,7 @@ "api/core/EchoHub.Core.DTOs.html": { "href": "api/core/EchoHub.Core.DTOs.html", "title": "Namespace EchoHub.Core.DTOs | EchoHub Documentation", - "summary": "Namespace EchoHub.Core.DTOs Classes ApiResponse ApiResponse AssignRoleRequest AttachmentDto A file attached to a message. AsciiPreview holds the color-tag art for images (null otherwise). For end-to-end encrypted channels the content behind Url and the preview are ciphertext the server cannot read. AvatarUploadResponse BanRequest ChannelCryptoDto Public crypto metadata for a channel — enough for a client to derive its join credential from a passphrase. Never includes the wrapped room key. ChannelDto ChannelOperationResult CreateChannelRequest EmbedDto EncryptionKeyResponse ErrorResponse JoinChannelResult KickRequest LoginRequest LoginResponse MessageDto MuteRequest PaginatedResponse RefreshRequest RegisterRequest RekeyChannelRequest Passphrase change for an encrypted channel: the client proves knowledge of the old passphrase (old auth key), then supplies the re-wrapped room key under the new one. SendMessageRequest SendUrlRequest ServerStatusDto UpdateProfileRequest UpdateStatusRequest UpdateTopicRequest UserDto UserOperationResult UserPresenceDto UserProfileDto Enums ChannelError UserError" + "summary": "Namespace EchoHub.Core.DTOs Classes ApiResponse ApiResponse AssignRoleRequest AttachmentDto A file attached to a message. AsciiPreview holds the color-tag art for images (null otherwise). For end-to-end encrypted channels the content behind Url and the preview are ciphertext the server cannot read. AvatarUploadResponse BanRequest ChannelCryptoDto Public crypto metadata for a channel — enough for a client to derive its join credential from a passphrase. Never includes the wrapped room key. ChannelDto ChannelMetaDto Human-facing summary of a channel (the /meta command). For encrypted channels the server still knows these figures — count, timestamps, and stored blob sizes — even though it cannot read the content itself. EstimatedSizeBytes is the sum of stored attachment blob sizes plus message text length, so it is an estimate, not an exact on-disk total. ChannelOperationResult CreateChannelRequest EmbedDto EncryptionKeyResponse ErrorResponse JoinChannelResult KickRequest LoginRequest LoginResponse MessageDto MuteRequest PaginatedResponse RefreshRequest RegisterRequest RekeyChannelRequest Passphrase change for an encrypted channel: the client proves knowledge of the old passphrase (old auth key), then supplies the re-wrapped room key under the new one. SendMessageRequest SendUrlRequest ServerStatusDto UpdateProfileRequest UpdateStatusRequest UpdateTopicRequest UserDto UserOperationResult UserPresenceDto UserProfileDto Enums ChannelError UserError" }, "api/core/EchoHub.Core.Models.Attachment.html": { "href": "api/core/EchoHub.Core.Models.Attachment.html", @@ -739,6 +744,16 @@ "title": "Namespace EchoHub.Server.Auth | EchoHub Documentation", "summary": "Namespace EchoHub.Server.Auth Classes JwtTokenService" }, + "api/server/EchoHub.Server.Config.UploadLimits.html": { + "href": "api/server/EchoHub.Server.Config.UploadLimits.html", + "title": "Class UploadLimits | EchoHub Documentation", + "summary": "Class UploadLimits Namespace EchoHub.Server.Config Assembly EchoHub.Server.dll Admin-configurable upload limits, bound from the Uploads configuration section. Sizes are expressed in megabytes in configuration; the *Bytes accessors convert them for enforcement. Every default mirrors HubConstants so an absent or partial Uploads section preserves the historical built-in limits. public sealed class UploadLimits Inheritance object UploadLimits Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors UploadLimits() public UploadLimits() Properties MaxAttachmentsPerMessage public int MaxAttachmentsPerMessage { get; init; } Property Value int MaxAudioSizeBytes public long MaxAudioSizeBytes { get; } Property Value long MaxAudioSizeMB public int MaxAudioSizeMB { get; init; } Property Value int MaxAvatarSizeBytes public long MaxAvatarSizeBytes { get; } Property Value long MaxAvatarSizeMB public int MaxAvatarSizeMB { get; init; } Property Value int MaxFileSizeBytes public long MaxFileSizeBytes { get; } Property Value long MaxFileSizeMB public int MaxFileSizeMB { get; init; } Property Value int MaxImageSizeBytes public long MaxImageSizeBytes { get; } Property Value long MaxImageSizeMB public int MaxImageSizeMB { get; init; } Property Value int MaxRequestBodyBytes Absolute ceiling for one message request body (largest file × the attachment cap). Used to size the request-body and multipart limits so a configured increase actually takes effect. public long MaxRequestBodyBytes { get; } Property Value long Methods MaxForKind(AttachmentKind) Maximum accepted size for a single attachment of the given kind. public long MaxForKind(AttachmentKind kind) Parameters kind AttachmentKind Returns long" + }, + "api/server/EchoHub.Server.Config.html": { + "href": "api/server/EchoHub.Server.Config.html", + "title": "Namespace EchoHub.Server.Config | EchoHub Documentation", + "summary": "Namespace EchoHub.Server.Config Classes UploadLimits Admin-configurable upload limits, bound from the Uploads configuration section. Sizes are expressed in megabytes in configuration; the *Bytes accessors convert them for enforcement. Every default mirrors HubConstants so an absent or partial Uploads section preserves the historical built-in limits." + }, "api/server/EchoHub.Server.Controllers.AuthController.html": { "href": "api/server/EchoHub.Server.Controllers.AuthController.html", "title": "Class AuthController | EchoHub Documentation", @@ -747,7 +762,7 @@ "api/server/EchoHub.Server.Controllers.ChannelsController.html": { "href": "api/server/EchoHub.Server.Controllers.ChannelsController.html", "title": "Class ChannelsController | EchoHub Documentation", - "summary": "Class ChannelsController Namespace EchoHub.Server.Controllers Assembly EchoHub.Server.dll [ApiController] [Route(\"api/channels\")] [Authorize] [EnableRateLimiting(\"general\")] public class ChannelsController : ControllerBase Inheritance object ControllerBase ChannelsController Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ChannelsController(IChannelService, EchoHubDbContext, FileStorageService, ImageToAsciiService, IHttpClientFactory, IChatService, IMessageEncryptionService) public ChannelsController(IChannelService channelService, EchoHubDbContext db, FileStorageService fileStorage, ImageToAsciiService asciiService, IHttpClientFactory httpClientFactory, IChatService chatService, IMessageEncryptionService encryption) Parameters channelService IChannelService db EchoHubDbContext fileStorage FileStorageService asciiService ImageToAsciiService httpClientFactory IHttpClientFactory chatService IChatService encryption IMessageEncryptionService Methods CreateChannel(CreateChannelRequest) [HttpPost] public Task CreateChannel(CreateChannelRequest request) Parameters request CreateChannelRequest Returns Task DeleteChannel(string) [HttpDelete(\"{channel}\")] public Task DeleteChannel(string channel) Parameters channel string Returns Task GetChannelCrypto(string) Public crypto metadata for a channel: whether it is end-to-end encrypted and the PBKDF2 salt clients need to derive their join credential. Never returns the wrapped room key — that is only handed out after a successful join. [HttpGet(\"{channel}/crypto\")] public Task GetChannelCrypto(string channel) Parameters channel string Returns Task GetChannels(int, int) [HttpGet] public Task GetChannels(int offset = 0, int limit = 50) Parameters offset int limit int Returns Task RekeyChannel(string, RekeyChannelRequest) Changes an encrypted channel's passphrase by re-wrapping its room key. The caller proves knowledge of the old passphrase via the old auth key; history is never re-encrypted (the room content key does not change). [HttpPost(\"{channel}/rekey\")] public Task RekeyChannel(string channel, RekeyChannelRequest request) Parameters channel string request RekeyChannelRequest Returns Task SendMessageWithAttachments(string, string?) Sends one message carrying optional text (content form field) plus zero or more file attachments (Discord-style). For non-encrypted channels the server sniffs each file's kind and renders ASCII previews for images. For end-to-end encrypted channels the client uploads ciphertext blobs and declares each file's kind (kind) and pre-rendered, room-encrypted preview (preview), aligned by file order — the server never inspects them. [HttpPost(\"{channel}/messages\")] [EnableRateLimiting(\"upload\")] [RequestSizeLimit(1048576000)] [RequestFormLimits(MultipartBodyLengthLimit = 1048576000)] public Task SendMessageWithAttachments(string channel, string? size = null) Parameters channel string size string Returns Task SendUrl(string, SendUrlRequest, string?) [HttpPost(\"{channel}/send-url\")] [EnableRateLimiting(\"upload\")] public Task SendUrl(string channel, SendUrlRequest request, string? size = null) Parameters channel string request SendUrlRequest size string Returns Task UpdateTopic(string, UpdateTopicRequest) [HttpPut(\"{channel}/topic\")] public Task UpdateTopic(string channel, UpdateTopicRequest request) Parameters channel string request UpdateTopicRequest Returns Task" + "summary": "Class ChannelsController Namespace EchoHub.Server.Controllers Assembly EchoHub.Server.dll [ApiController] [Route(\"api/channels\")] [Authorize] [EnableRateLimiting(\"general\")] public class ChannelsController : ControllerBase Inheritance object ControllerBase ChannelsController Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ChannelsController(IChannelService, EchoHubDbContext, FileStorageService, ImageToAsciiService, IHttpClientFactory, IChatService, IMessageEncryptionService, UploadLimits) public ChannelsController(IChannelService channelService, EchoHubDbContext db, FileStorageService fileStorage, ImageToAsciiService asciiService, IHttpClientFactory httpClientFactory, IChatService chatService, IMessageEncryptionService encryption, UploadLimits uploadLimits) Parameters channelService IChannelService db EchoHubDbContext fileStorage FileStorageService asciiService ImageToAsciiService httpClientFactory IHttpClientFactory chatService IChatService encryption IMessageEncryptionService uploadLimits UploadLimits Methods CreateChannel(CreateChannelRequest) [HttpPost] public Task CreateChannel(CreateChannelRequest request) Parameters request CreateChannelRequest Returns Task DeleteChannel(string) [HttpDelete(\"{channel}\")] public Task DeleteChannel(string channel) Parameters channel string Returns Task GetChannelCrypto(string) Public crypto metadata for a channel: whether it is end-to-end encrypted and the PBKDF2 salt clients need to derive their join credential. Never returns the wrapped room key — that is only handed out after a successful join. [HttpGet(\"{channel}/crypto\")] public Task GetChannelCrypto(string channel) Parameters channel string Returns Task GetChannelMeta(string) Human-facing summary of a channel (message count, unique posters, estimated size, created date, room id). Available for encrypted channels too — these are metadata the server tracks even though it cannot read the messages themselves. [HttpGet(\"{channel}/meta\")] public Task GetChannelMeta(string channel) Parameters channel string Returns Task GetChannels(int, int) [HttpGet] public Task GetChannels(int offset = 0, int limit = 50) Parameters offset int limit int Returns Task RekeyChannel(string, RekeyChannelRequest) Changes an encrypted channel's passphrase by re-wrapping its room key. The caller proves knowledge of the old passphrase via the old auth key; history is never re-encrypted (the room content key does not change). [HttpPost(\"{channel}/rekey\")] public Task RekeyChannel(string channel, RekeyChannelRequest request) Parameters channel string request RekeyChannelRequest Returns Task SendMessageWithAttachments(string, string?) Sends one message carrying optional text (content form field) plus zero or more file attachments (Discord-style). For non-encrypted channels the server sniffs each file's kind and renders ASCII previews for images. For end-to-end encrypted channels the client uploads ciphertext blobs and declares each file's kind (kind) and pre-rendered, room-encrypted preview (preview), aligned by file order — the server never inspects them. [HttpPost(\"{channel}/messages\")] [EnableRateLimiting(\"upload\")] public Task SendMessageWithAttachments(string channel, string? size = null) Parameters channel string size string Returns Task SendUrl(string, SendUrlRequest, string?) [HttpPost(\"{channel}/send-url\")] [EnableRateLimiting(\"upload\")] public Task SendUrl(string channel, SendUrlRequest request, string? size = null) Parameters channel string request SendUrlRequest size string Returns Task UpdateTopic(string, UpdateTopicRequest) [HttpPut(\"{channel}/topic\")] public Task UpdateTopic(string channel, UpdateTopicRequest request) Parameters channel string request UpdateTopicRequest Returns Task" }, "api/server/EchoHub.Server.Controllers.FilesController.html": { "href": "api/server/EchoHub.Server.Controllers.FilesController.html", @@ -767,7 +782,7 @@ "api/server/EchoHub.Server.Controllers.UsersController.html": { "href": "api/server/EchoHub.Server.Controllers.UsersController.html", "title": "Class UsersController | EchoHub Documentation", - "summary": "Class UsersController Namespace EchoHub.Server.Controllers Assembly EchoHub.Server.dll [ApiController] [Route(\"api/users\")] [Authorize] [EnableRateLimiting(\"general\")] public class UsersController : ControllerBase Inheritance object ControllerBase UsersController Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors UsersController(IUserService, ImageToAsciiService) public UsersController(IUserService userService, ImageToAsciiService asciiService) Parameters userService IUserService asciiService ImageToAsciiService Methods GetProfile(string) [HttpGet(\"{username}/profile\")] public Task GetProfile(string username) Parameters username string Returns Task UpdateProfile(UpdateProfileRequest) [HttpPut(\"profile\")] public Task UpdateProfile(UpdateProfileRequest request) Parameters request UpdateProfileRequest Returns Task UploadAvatar() [HttpPost(\"avatar\")] [EnableRateLimiting(\"upload\")] public Task UploadAvatar() Returns Task" + "summary": "Class UsersController Namespace EchoHub.Server.Controllers Assembly EchoHub.Server.dll [ApiController] [Route(\"api/users\")] [Authorize] [EnableRateLimiting(\"general\")] public class UsersController : ControllerBase Inheritance object ControllerBase UsersController Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors UsersController(IUserService, ImageToAsciiService, UploadLimits) public UsersController(IUserService userService, ImageToAsciiService asciiService, UploadLimits uploadLimits) Parameters userService IUserService asciiService ImageToAsciiService uploadLimits UploadLimits Methods GetProfile(string) [HttpGet(\"{username}/profile\")] public Task GetProfile(string username) Parameters username string Returns Task UpdateProfile(UpdateProfileRequest) [HttpPut(\"profile\")] public Task UpdateProfile(UpdateProfileRequest request) Parameters request UpdateProfileRequest Returns Task UploadAvatar() [HttpPost(\"avatar\")] [EnableRateLimiting(\"upload\")] public Task UploadAvatar() Returns Task" }, "api/server/EchoHub.Server.Controllers.html": { "href": "api/server/EchoHub.Server.Controllers.html", @@ -852,7 +867,7 @@ "api/server/EchoHub.Server.Services.ChannelService.html": { "href": "api/server/EchoHub.Server.Services.ChannelService.html", "title": "Class ChannelService | EchoHub Documentation", - "summary": "Class ChannelService Namespace EchoHub.Server.Services Assembly EchoHub.Server.dll public class ChannelService : IChannelService Inheritance object ChannelService Implements IChannelService Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ChannelService(IServiceScopeFactory, PresenceTracker, ILogger) public ChannelService(IServiceScopeFactory scopeFactory, PresenceTracker presenceTracker, ILogger logger) Parameters scopeFactory IServiceScopeFactory presenceTracker PresenceTracker logger ILogger Methods CreateChannelAsync(Guid, string, string?, bool, string?, string?, string?) public Task CreateChannelAsync(Guid creatorUserId, string name, string? topic, bool isPublic, string? password = null, string? encryptionSalt = null, string? wrappedRoomKey = null) Parameters creatorUserId Guid name string topic string isPublic bool password string encryptionSalt string wrappedRoomKey string Returns Task DeleteChannelAsync(Guid, string) public Task DeleteChannelAsync(Guid callerUserId, string channelName) Parameters callerUserId Guid channelName string Returns Task EnsureChannelMembershipAsync(Guid, string, string?) public Task<(bool Success, string? Error, bool PasswordRequired)> EnsureChannelMembershipAsync(Guid userId, string channelName, string? password = null) Parameters userId Guid channelName string password string Returns Task<(bool Success, string Error, bool PasswordRequired)> GetChannelByNameAsync(string) public Task GetChannelByNameAsync(string channelName) Parameters channelName string Returns Task GetChannelCryptoAsync(string) public Task GetChannelCryptoAsync(string channelName) Parameters channelName string Returns Task GetChannelKeyEnvelopeAsync(string) public Task<(string? EncryptionSalt, string? WrappedRoomKey)> GetChannelKeyEnvelopeAsync(string channelName) Parameters channelName string Returns Task<(string EncryptionSalt, string WrappedRoomKey)> GetChannelListAsync() public Task> GetChannelListAsync() Returns Task> GetChannelTopicAsync(string) public Task<(string? Topic, bool Exists)> GetChannelTopicAsync(string channelName) Parameters channelName string Returns Task<(string Topic, bool Exists)> GetChannelsAsync(Guid, int, int) public Task> GetChannelsAsync(Guid userId, int offset, int limit) Parameters userId Guid offset int limit int Returns Task> RekeyChannelAsync(Guid, string, string, string, string, string) Changes an encrypted channel's passphrase by swapping the join-gate hash and the wrapped room key. The room content key itself never changes, so history stays readable — the client re-wraps it under the new passphrase-derived key. Creator only: admins cannot rekey a room whose passphrase they don't know. public Task RekeyChannelAsync(Guid callerUserId, string channelName, string oldPassword, string newPassword, string newEncryptionSalt, string newWrappedRoomKey) Parameters callerUserId Guid channelName string oldPassword string newPassword string newEncryptionSalt string newWrappedRoomKey string Returns Task SetChannelPasswordAsync(Guid, string, string?) Sets, changes, or clears (null) a channel's join password. Creator or admin only. Not available on end-to-end encrypted channels — those change passphrase via RekeyChannelAsync(Guid, string, string, string, string, string) so the room key envelope stays consistent. public Task SetChannelPasswordAsync(Guid callerUserId, string channelName, string? password) Parameters callerUserId Guid channelName string password string Returns Task UpdateTopicAsync(Guid, string, string?) public Task UpdateTopicAsync(Guid callerUserId, string channelName, string? topic) Parameters callerUserId Guid channelName string topic string Returns Task" + "summary": "Class ChannelService Namespace EchoHub.Server.Services Assembly EchoHub.Server.dll public class ChannelService : IChannelService Inheritance object ChannelService Implements IChannelService Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ChannelService(IServiceScopeFactory, PresenceTracker, ILogger) public ChannelService(IServiceScopeFactory scopeFactory, PresenceTracker presenceTracker, ILogger logger) Parameters scopeFactory IServiceScopeFactory presenceTracker PresenceTracker logger ILogger Methods CreateChannelAsync(Guid, string, string?, bool, string?, string?, string?) public Task CreateChannelAsync(Guid creatorUserId, string name, string? topic, bool isPublic, string? password = null, string? encryptionSalt = null, string? wrappedRoomKey = null) Parameters creatorUserId Guid name string topic string isPublic bool password string encryptionSalt string wrappedRoomKey string Returns Task DeleteChannelAsync(Guid, string) public Task DeleteChannelAsync(Guid callerUserId, string channelName) Parameters callerUserId Guid channelName string Returns Task EnsureChannelMembershipAsync(Guid, string, string?) public Task<(bool Success, string? Error, bool PasswordRequired)> EnsureChannelMembershipAsync(Guid userId, string channelName, string? password = null) Parameters userId Guid channelName string password string Returns Task<(bool Success, string Error, bool PasswordRequired)> GetChannelByNameAsync(string) public Task GetChannelByNameAsync(string channelName) Parameters channelName string Returns Task GetChannelCryptoAsync(string) public Task GetChannelCryptoAsync(string channelName) Parameters channelName string Returns Task GetChannelKeyEnvelopeAsync(string) public Task<(string? EncryptionSalt, string? WrappedRoomKey)> GetChannelKeyEnvelopeAsync(string channelName) Parameters channelName string Returns Task<(string EncryptionSalt, string WrappedRoomKey)> GetChannelListAsync() public Task> GetChannelListAsync() Returns Task> GetChannelMetaAsync(string) public Task GetChannelMetaAsync(string channelName) Parameters channelName string Returns Task GetChannelTopicAsync(string) public Task<(string? Topic, bool Exists)> GetChannelTopicAsync(string channelName) Parameters channelName string Returns Task<(string Topic, bool Exists)> GetChannelsAsync(Guid, int, int) public Task> GetChannelsAsync(Guid userId, int offset, int limit) Parameters userId Guid offset int limit int Returns Task> RekeyChannelAsync(Guid, string, string, string, string, string) Changes an encrypted channel's passphrase by swapping the join-gate hash and the wrapped room key. The room content key itself never changes, so history stays readable — the client re-wraps it under the new passphrase-derived key. Creator only: admins cannot rekey a room whose passphrase they don't know. public Task RekeyChannelAsync(Guid callerUserId, string channelName, string oldPassword, string newPassword, string newEncryptionSalt, string newWrappedRoomKey) Parameters callerUserId Guid channelName string oldPassword string newPassword string newEncryptionSalt string newWrappedRoomKey string Returns Task SetChannelPasswordAsync(Guid, string, string?) Sets, changes, or clears (null) a channel's join password. Creator or admin only. Not available on end-to-end encrypted channels — those change passphrase via RekeyChannelAsync(Guid, string, string, string, string, string) so the room key envelope stays consistent. public Task SetChannelPasswordAsync(Guid callerUserId, string channelName, string? password) Parameters callerUserId Guid channelName string password string Returns Task UpdateTopicAsync(Guid, string, string?) public Task UpdateTopicAsync(Guid callerUserId, string channelName, string? topic) Parameters callerUserId Guid channelName string topic string Returns Task" }, "api/server/EchoHub.Server.Services.ChatService.html": { "href": "api/server/EchoHub.Server.Services.ChatService.html", @@ -954,10 +969,15 @@ "title": "Docker | EchoHub Documentation", "summary": "Docker Quick Start cp .env.example .env # create your config docker compose up -d # start the server On first run the server automatically generates JWT and encryption keys, creates the database, and seeds a #general channel. Connect with the EchoHub client to http://localhost:5000. Using a Pre-built Image Instead of building locally, you can pull from GHCR. In docker-compose.yml, replace the build block: services: echohub-server: image: ghcr.io/huebyte/echohub-server:latest # build: # context: ./src # dockerfile: EchoHub.Server/Dockerfile Configuration All settings are configured through the .env file. These are environment variables that override appsettings.json — the __ (double underscore) maps to JSON nesting levels. For example, Server__Name overrides the Server:Name key in appsettings.json. See the Configuration guide for the full reference of all available settings and how the override hierarchy works. Common Docker-relevant variables: Variable Default Description Server__Name My EchoHub Server Display name for your server Server__Admins__0 (empty) Admin username (use __1, __2 for more) Irc__Enabled false Enable the IRC gateway Serilog__MinimumLevel__Default Information Log level (Debug, Warning, etc.) Persistent Data All server state lives in a single Docker volume mounted at /app/data: /app/data/ ├── appsettings.json # generated config with JWT/encryption keys ├── echohub.db # SQLite database ├── uploads/ # uploaded files and avatars └── logs/ # rolling log files (14-day retention) Backup # stop the server to ensure a consistent snapshot docker compose stop # copy the data volume to a local directory docker cp echohub-server:/app/data ./backup docker compose start IRC Gateway To enable IRC, set these in your .env: Irc__Enabled=true Then uncomment the port in docker-compose.yml: ports: - \"5000:5000\" - \"6697:6697\" # IRC (TLS encrypted, preferred) For TLS, also set: Irc__TlsEnabled=true Irc__TlsCertPath=/app/data/cert.pfx Irc__TlsCertPassword=your_password Mount your certificate into the data volume or bind-mount it directly. IRC users must have an existing EchoHub account. See Getting Started for client connection examples. Updating # if using pre-built images docker compose pull docker compose up -d # if building locally docker compose build docker compose up -d Data persists across updates since it lives in the named volume. Troubleshooting Port already in use -- Another process is using port 5000. Change the host port in docker-compose.yml: ports: - \"8080:5000\" # access via http://localhost:8080 Permission denied on volume -- The container runs as a non-root echohub user (UID 999). If using bind mounts instead of named volumes, ensure the directory is writable. View logs -- Check the container output: docker compose logs -f echohub-server File-based logs are also available inside the volume at /app/data/logs/." }, + "articles/encrypted-rooms.html": { + "href": "articles/encrypted-rooms.html", + "title": "Encrypted Rooms (Password-Protected Channels) | EchoHub Documentation", + "summary": "Encrypted Rooms (Password-Protected Channels) An encrypted room is a channel whose entire content — every message and every file — is end-to-end encrypted with a key derived from a shared passphrase. Only people who know the passphrase can read the room. Not even the server owner can read the content, yet the server can still gate who joins, and it can count and measure what's stored (message count, file sizes, timestamps) without ever seeing the plaintext. This is a stronger guarantee than the transport and at-rest encryption described elsewhere, where the server decrypts every message to process it. Here the server is treated as untrusted for content: it holds only ciphertext and wrapped keys. The passphrase is the only key. There is no recovery. If everyone who knows a room's passphrase forgets it, that room's history is permanently unreadable — by design. What the server can and cannot see The server can see The server cannot see That the channel is encrypted Message text Message count and timestamps File contents Who sent each message (sender identity) Image previews (ASCII art) Each attachment's file name and byte size The passphrase, the room key, or the key-encryption key The estimated total size (via /meta) Anything that would let it decrypt the above File names are stored in plaintext so the file list stays usable — treat a file name itself as non-secret. Everything inside the file is encrypted. Key hierarchy Three keys are derived from one passphrase. The passphrase, the key-encryption key, and the room content key never leave the client. flowchart TD P[Passphrase] -->|PBKDF2-SHA256, 210k iterations, per-room salt| OKM[64-byte output] OKM --> AK[\"Auth key (first 32 bytes)\"] OKM --> KEK[\"Key-encryption key / KEK (last 32 bytes)\"] AK -->|sent as lowercase hex| SRV1[\"Server: BCrypt-hash as the join gate\"] RCK[\"Room Content Key (random 256-bit)\"] -->|encrypts all content| CONTENT[Messages + files + previews] RCK -->|AES-256-GCM wrap under KEK| WRAP[\"Wrapped room key\"] WRAP -->|stored| SRV2[\"Server: stores wrapped key + salt only\"] Auth key — the join credential. Derived from the passphrase, sent to the server as hex, and stored only as a BCrypt hash. Proving knowledge of it is what lets you join; it reveals nothing about the content key. Key-encryption key (KEK) — never sent. Used locally to wrap (encrypt) and unwrap the room content key. Room Content Key (RCK) — a random 256-bit key generated once, at room creation. It encrypts every message and file. The server stores it only in wrapped form, so it can hand the wrapped key to a joiner but can never unwrap it itself. All content encryption is AES-256-GCM with a random 12-byte nonce and a 16-byte authentication tag per item, so identical inputs never produce identical ciphertext, and any tampering is detected. Room-encrypted text carries a self-describing prefix so clients and the server can tell it apart from plaintext: $RC1$base64(nonce || tag || ciphertext) Creating a room The client does all the cryptography locally, then hands the server only what it needs to gate joins and store (but not read) the content. sequenceDiagram participant Client participant Server Client->>Client: generate salt + random Room Content Key (RCK) Client->>Client: DeriveKeys(passphrase, salt) → auth key + KEK Client->>Client: wrap RCK under KEK Client->>Server: create channel { authKey(hex), salt, wrappedRoomKey } Server->>Server: BCrypt-hash auth key, store salt + wrapped key Note over Server: Server never receives passphrase, KEK, or RCK Joining a room sequenceDiagram participant Client participant Server Client->>Server: GET /crypto → { isEncrypted, salt } Client->>Client: DeriveKeys(passphrase, salt) → auth key + KEK Client->>Server: join { authKey(hex) } Server->>Server: BCrypt-verify against stored hash alt correct passphrase Server->>Client: history (ciphertext) + wrapped room key Client->>Client: unwrap RCK with KEK, then decrypt everything locally else wrong passphrase Server->>Client: rejected (join gate fails) end A wrong passphrase fails the BCrypt gate, so the server never even hands out the wrapped key. Even if it did, an attacker without the KEK cannot unwrap it. What gets encrypted When you send a message or attach files to an encrypted room, the client encrypts each part with the room content key before uploading: Message text → $RC1$… ciphertext. Files (any kind) → the whole blob is AES-256-GCM encrypted client-side; the server stores an opaque ciphertext blob. Image ASCII previews → rendered on the client, then room-encrypted. The server never sees the rendered art. The server records each attachment's kind, file name, and byte size (of the ciphertext blob) as metadata, and broadcasts the ciphertext to other members, who decrypt locally. Changing the passphrase /passwd rotates the passphrase. Because only the wrapping of the room content key changes — not the RCK itself — all existing history stays readable: The client proves knowledge of the old passphrase (old auth key). It unwraps the RCK with the old KEK, then re-wraps it under the new KEK (new salt). It uploads the new auth key + salt + re-wrapped key. The content is never re-encrypted. Inspecting a room Use /meta in any channel to see what the server knows about it, including encrypted rooms: Room info for #private-room: Room ID 3f2a…-…-… Created 7/16/2026 2:31 PM Messages 128 Unique users 4 Est. size 42.5 MB Protection end-to-end encrypted Est. size is the sum of stored attachment blob sizes plus message text length — an estimate of the room's footprint, computed entirely from metadata the server holds without reading any content. Limitations & security notes No recovery. A lost passphrase means unrecoverable history. Keep it safe; there is no reset. File names are plaintext. They stay readable so the file list works — don't put secrets in a file name. IRC is disabled for encrypted rooms. The IRC gateway forwards plaintext and cannot participate in the room's key scheme, so encrypted channels are not bridged to IRC. Metadata is visible. Message counts, timestamps, sender identities, file names, and sizes are intentionally readable so the server can moderate at the metadata level and report /meta. Endpoint trust. End-to-end encryption protects content from the server and the network, not from a compromised client device that already holds the passphrase. Related Message Encryption — transport ($ENC$v1$) and optional at-rest database encryption, where the server does decrypt content for processing. Encrypted rooms are a separate, stronger layer that sits on top." + }, "articles/encryption.html": { "href": "articles/encryption.html", "title": "Message Encryption | EchoHub Documentation", - "summary": "Message Encryption EchoHub uses application-layer AES-256-GCM encryption to protect message content in transit between clients and the server. This is an additional layer on top of TLS, protecting against ISPs, proxies, and any middleman that can inspect HTTPS traffic (e.g. corporate proxies with trusted root CA certificates). How It Works sequenceDiagram participant Sender as TUI Client (Sender) participant Server participant Receiver as TUI Client (Receiver) participant IRC as IRC Client Sender->>Sender: encrypt(plaintext) Sender->>Server: $ENC$v1$... (SignalR) Server->>Server: decrypt → validate/sanitize Server->>Server: fetch embeds on plaintext Server->>Server: (optional) encrypt for DB storage Server->>Server: encrypt(plaintext) with fresh nonce Server->>Receiver: $ENC$v1$... (SignalR broadcast) Receiver->>Receiver: decrypt → display Server->>Server: decrypt for IRC Server->>IRC: plaintext (IRC PRIVMSG) Client encrypts the message before sending it over SignalR Server decrypts to validate content, sanitize newlines, and fetch link embeds Server re-encrypts with a fresh nonce and broadcasts to all connected SignalR clients Clients decrypt the broadcast and display the plaintext IRC clients receive plaintext automatically (the IRC broadcaster decrypts before forwarding) Each encryption uses a random 12-byte nonce, so the same message produces different ciphertext every time. Encryption Key A 256-bit AES key is auto-generated on first server startup and saved to appsettings.json: { \"Encryption\": { \"Key\": \"base64-encoded-32-byte-key\", \"EncryptDatabase\": false } } The key is generated by FirstRunSetup using RandomNumberGenerator.GetBytes(32). If the key is missing or empty when the server starts, a new one is created automatically. Clients fetch the key after login via an authenticated endpoint (GET /api/server/encryption-key). No manual configuration is needed on the client side. Encrypted Content Format Encrypted content uses a self-describing format: $ENC$v1${nonce_base64}${ciphertext+tag_base64} $ENC$v1$ — version prefix (allows future algorithm changes) Nonce — 12 bytes, Base64-encoded, randomly generated per message Ciphertext + Tag — AES-256-GCM output with 16-byte authentication tag appended The authentication tag ensures both confidentiality and integrity — any tampering with the ciphertext is detected during decryption. Database Encryption (Optional) By default, messages are stored as plaintext in the database. Transport encryption still protects everything on the wire, but the SQLite file itself contains readable messages. To encrypt messages at rest, enable the setting: { \"Encryption\": { \"Key\": \"...\", \"EncryptDatabase\": true } } Behavior by Setting Setting DB Storage Transport Key Rotation Risk false (default) Plaintext Encrypted None - stored data is unaffected true Encrypted Encrypted Changing the key makes old messages unreadable Mixed Content The server handles mixed encrypted/plaintext content in the database gracefully. When reading messages: Content starting with $ENC$v1$ is decrypted Everything else is treated as plaintext This means you can safely toggle EncryptDatabase at any time. Old messages remain readable regardless of the current setting. Enabling Encryption at Rest When you set EncryptDatabase: true, only new messages are encrypted going forward. Existing plaintext messages in the database are not retroactively encrypted. This is intentional — it keeps key rotation safe and avoids irreversible bulk changes. Key Rotation Changing the encryption key is safe: Plaintext messages — always readable regardless of key Messages encrypted with the old key — will show [encrypted message — decryption failed] New messages — encrypted with the new key going forward If you need to recover old encrypted messages, restore the original key from a backup of appsettings.json. Since plaintext messages are never retroactively encrypted, you'll never lose access to your entire history from a key change. Security Considerations What This Protects Against Passive network sniffing — messages are encrypted even if captured off the wire ISP/proxy inspection — content is encrypted at the application layer, independent of TLS Database theft (when EncryptDatabase: true) — SQLite file contains only ciphertext Limitations TLS-inspecting proxies — if a corporate proxy terminates TLS with a trusted root CA, it can intercept the key exchange (GET /api/server/encryption-key) and read all traffic. A future upgrade to ECDH key exchange would address this. Server has full access — the server decrypts all messages for processing. This is not end-to-end encryption between users; it's transport encryption between client and server. IRC clients receive plaintext — IRC is an open protocol and third-party clients cannot participate in the encryption scheme. Troubleshooting Messages show [encrypted message — decryption failed, try re-logging to fetch the latest key] The client's encryption key doesn't match the server's. This happens when: The server's encryption key was rotated while the client was connected The client cached a stale key Fix: Disconnect and reconnect (re-login). The client fetches the current key on each login. Messages show [encrypted message — decryption failed] in channel history The server cannot decrypt messages stored in the database. This happens when: EncryptDatabase was enabled, and the key was changed afterwards Fix: Restore the original key from a backup. There is no way to recover messages encrypted with a lost key." + "summary": "Message Encryption EchoHub uses application-layer AES-256-GCM encryption to protect message content in transit between clients and the server. This is an additional layer on top of TLS, protecting against ISPs, proxies, and any middleman that can inspect HTTPS traffic (e.g. corporate proxies with trusted root CA certificates). How It Works sequenceDiagram participant Sender as TUI Client (Sender) participant Server participant Receiver as TUI Client (Receiver) participant IRC as IRC Client Sender->>Sender: encrypt(plaintext) Sender->>Server: $ENC$v1$... (SignalR) Server->>Server: decrypt → validate/sanitize Server->>Server: fetch embeds on plaintext Server->>Server: (optional) encrypt for DB storage Server->>Server: encrypt(plaintext) with fresh nonce Server->>Receiver: $ENC$v1$... (SignalR broadcast) Receiver->>Receiver: decrypt → display Server->>Server: decrypt for IRC Server->>IRC: plaintext (IRC PRIVMSG) Client encrypts the message before sending it over SignalR Server decrypts to validate content, sanitize newlines, and fetch link embeds Server re-encrypts with a fresh nonce and broadcasts to all connected SignalR clients Clients decrypt the broadcast and display the plaintext IRC clients receive plaintext automatically (the IRC broadcaster decrypts before forwarding) Each encryption uses a random 12-byte nonce, so the same message produces different ciphertext every time. Encryption Key A 256-bit AES key is auto-generated on first server startup and saved to appsettings.json: { \"Encryption\": { \"Key\": \"base64-encoded-32-byte-key\", \"EncryptDatabase\": false } } The key is generated by FirstRunSetup using RandomNumberGenerator.GetBytes(32). If the key is missing or empty when the server starts, a new one is created automatically. Clients fetch the key after login via an authenticated endpoint (GET /api/server/encryption-key). No manual configuration is needed on the client side. Encrypted Content Format Encrypted content uses a self-describing format: $ENC$v1${nonce_base64}${ciphertext+tag_base64} $ENC$v1$ — version prefix (allows future algorithm changes) Nonce — 12 bytes, Base64-encoded, randomly generated per message Ciphertext + Tag — AES-256-GCM output with 16-byte authentication tag appended The authentication tag ensures both confidentiality and integrity — any tampering with the ciphertext is detected during decryption. Database Encryption (Optional) By default, messages are stored as plaintext in the database. Transport encryption still protects everything on the wire, but the SQLite file itself contains readable messages. To encrypt messages at rest, enable the setting: { \"Encryption\": { \"Key\": \"...\", \"EncryptDatabase\": true } } Behavior by Setting Setting DB Storage Transport Key Rotation Risk false (default) Plaintext Encrypted None - stored data is unaffected true Encrypted Encrypted Changing the key makes old messages unreadable Mixed Content The server handles mixed encrypted/plaintext content in the database gracefully. When reading messages: Content starting with $ENC$v1$ is decrypted Everything else is treated as plaintext This means you can safely toggle EncryptDatabase at any time. Old messages remain readable regardless of the current setting. Enabling Encryption at Rest When you set EncryptDatabase: true, only new messages are encrypted going forward. Existing plaintext messages in the database are not retroactively encrypted. This is intentional — it keeps key rotation safe and avoids irreversible bulk changes. Key Rotation Changing the encryption key is safe: Plaintext messages — always readable regardless of key Messages encrypted with the old key — will show [encrypted message — decryption failed] New messages — encrypted with the new key going forward If you need to recover old encrypted messages, restore the original key from a backup of appsettings.json. Since plaintext messages are never retroactively encrypted, you'll never lose access to your entire history from a key change. Security Considerations What This Protects Against Passive network sniffing — messages are encrypted even if captured off the wire ISP/proxy inspection — content is encrypted at the application layer, independent of TLS Database theft (when EncryptDatabase: true) — SQLite file contains only ciphertext Limitations TLS-inspecting proxies — if a corporate proxy terminates TLS with a trusted root CA, it can intercept the key exchange (GET /api/server/encryption-key) and read all traffic. A future upgrade to ECDH key exchange would address this. Server has full access — the server decrypts all messages for processing. This is not end-to-end encryption between users; it's transport encryption between client and server. For true end-to-end encryption where the server cannot read content, use encrypted rooms. IRC clients receive plaintext — IRC is an open protocol and third-party clients cannot participate in the encryption scheme. Troubleshooting Messages show [encrypted message — decryption failed, try re-logging to fetch the latest key] The client's encryption key doesn't match the server's. This happens when: The server's encryption key was rotated while the client was connected The client cached a stale key Fix: Disconnect and reconnect (re-login). The client fetches the current key on each login. Messages show [encrypted message — decryption failed] in channel history The server cannot decrypt messages stored in the database. This happens when: EncryptDatabase was enabled, and the key was changed afterwards Fix: Restore the original key from a backup. There is no way to recover messages encrypted with a lost key." }, "articles/getting-started.html": { "href": "articles/getting-started.html", @@ -972,7 +992,7 @@ "changelog/index.html": { "href": "changelog/index.html", "title": "Changelog | EchoHub Documentation", - "summary": "Changelog Release history for EchoHub. Releases v0.2.12 - End-to-End Encrypted Channels, IRC Channel Keys, Image Save & Ctrl+W Crash Fix v0.2.11 - EchoHubSpace Auth, Live Directory Updates & Server Browser Metadata v0.2.10 - Command Palette, Infinite History Scroll & Auto-Updater Fixes v0.2.9 - Install Script & Chocolatey Fixes v0.2.8 - Docker Support, IRC Account Creation & BOM Fix v0.2.7 - User List Fix & Terminal.Gui NuGet Migration v0.2.6 - Major Refactoring & Code Organization v0.2.5 - Session Persistence, Auto-Updates, Audio & Transparent Theme v0.2.4 - E2E Message Encryption v0.2.3 - Moderation, Embeds & UI Overhaul v0.2.2 - Startup & Shutdown Fixes v0.2.1 - Shutdown & CI Fixes v0.2.0 - IRC Gateway v0.1.1 - Directory Connection Self-Healing v0.1.0 - Initial Release" + "summary": "Changelog Release history for EchoHub. Releases v0.2.13 - Chat Visual Overhaul, Auto-Join All Channels & Persistent Read Positions v0.2.12 - End-to-End Encrypted Channels, IRC Channel Keys, Image Save & Ctrl+W Crash Fix v0.2.11 - EchoHubSpace Auth, Live Directory Updates & Server Browser Metadata v0.2.10 - Command Palette, Infinite History Scroll & Auto-Updater Fixes v0.2.9 - Install Script & Chocolatey Fixes v0.2.8 - Docker Support, IRC Account Creation & BOM Fix v0.2.7 - User List Fix & Terminal.Gui NuGet Migration v0.2.6 - Major Refactoring & Code Organization v0.2.5 - Session Persistence, Auto-Updates, Audio & Transparent Theme v0.2.4 - E2E Message Encryption v0.2.3 - Moderation, Embeds & UI Overhaul v0.2.2 - Startup & Shutdown Fixes v0.2.1 - Shutdown & CI Fixes v0.2.0 - IRC Gateway v0.1.1 - Directory Connection Self-Healing v0.1.0 - Initial Release" }, "changelog/v0.1.0.html": { "href": "changelog/v0.1.0.html", @@ -1007,7 +1027,12 @@ "changelog/v0.2.12.html": { "href": "changelog/v0.2.12.html", "title": "v0.2.12 | EchoHub Documentation", - "summary": "v0.2.12 Private channels are now genuinely private: password-protected channels are end-to-end encrypted, so the server (and its operators) can gate joins and measure storage but cannot read message or file contents. The IRC gateway grows real MODE/TOPIC support and channel keys, and the client gets image \"save original\", a transparent-light theme, drag-and-drop file sending, Ctrl+V paste, and a fix for the intermittent Ctrl+W crash. New Features End-to-end encrypted channels — creating a channel with a password now provisions a zero-knowledge room: The passphrase never leaves the client. It derives (PBKDF2-SHA256, 210k iterations) two keys: an auth key sent to the server as the join credential, and a key-encryption key that never leaves the machine. A random room content key encrypts every message and file with AES-256-GCM. The server only ever stores the room key wrapped under the passphrase, so it can gate joins and report a channel's message count, storage size, and attachments — but cannot decrypt any of it. Even the server owner cannot read a private room's contents. Members' clients cache the derived room key locally (in the per-server config, like saved sessions) so the passphrase isn't retyped every launch; joining on a new device prompts for it once. Change the passphrase with /passwd (channel creator only). The room key is re-wrapped, not rotated, so existing history stays readable and members who join later with the new passphrase can still read older messages. Files and images are encrypted client-side before upload; for images the ASCII-art preview is rendered on the client and stored room-encrypted too. Sending images by URL is disabled in encrypted channels (the server can't fetch-and-render without the key). End-to-end encrypted channels cannot be joined over the IRC gateway (that would require the server to hold the room key) — IRC JOIN returns 475 directing users to the EchoHub client. Password-protected channels — set an optional password when creating a channel (masked field in the Create Channel dialog, password on POST /api/channels). Passwords are BCrypt-hashed server-side; the join gate applies on first join only (existing members and the creator are unaffected). Protected channels show a * marker in the channel list and +k in the status bar Save original images — image messages now show a clickable \"[↓ save original]\" line under the ASCII-art preview that downloads the full-resolution original to your Downloads folder (decrypting locally in encrypted channels) Messages with attachments (Discord-style) — a message is now text plus a list of attachments instead of being either text or a single file. One message can carry a caption and several files (images, audio, docs) together: Compose with a staging tray: /send or dropping files onto the terminal stages them (shown on the input bar); the next Enter sends your typed caption and all staged files as one message. /clear drops staged files. /send still posts an image immediately. Each image attachment renders its own ASCII preview with its own \"save original\" action; audio/file attachments each get their own play/download line. In encrypted channels every attachment is encrypted individually (blob + ASCII preview), and the caption is room-encrypted — the server still stores only ciphertext and can report count/size but not contents. Up to 10 attachments per message. Right-click message menu — right-click any message for a context menu: save/download/play its attachment, mention the sender, view their profile, copy the text, copy the message ID (for linking or command arguments), or delete the message. (Keyboard: F6 focuses the message list for arrow-key selection + Delete.) The selected message is now highlighted while the list is focused. Message deletion — press Delete on a selected message to remove it. You can always delete your own messages; moderators and above can delete others' messages, but only from users below their own role (a mod can't delete an admin's or owner's message). Deleting a message also removes its attachment blobs from server storage. Customizable download folder — /downloadpath opens your OS-native folder picker (Windows Explorer / macOS Finder / Linux GTK or KDE) to choose where downloaded attachments and saved images go; /downloadpath sets it directly (the fallback when no native picker is available). Downloaded files now land in that folder (with automatic (n) de-duplication) instead of a temp directory. /join [password] — join protected channels inline, or let the client prompt: joining a protected channel without a password opens a masked prompt that re-prompts on a wrong password IRC channel keys — JOIN #room works against room passwords (RFC 1459 comma-paired key lists supported); keyless or wrong-key joins get 475 ERR_BADCHANNELKEY IRC MODE implemented — MODE #chan reports +k/+, MODE #chan +k sets and -k clears the room password (channel creator or admin only), ban-list probes get a clean empty reply, and CHANMODES is advertised in ISUPPORT IRC TOPIC set support — the channel creator can change the topic from IRC; the change broadcasts to connected TUI clients (previously topic changes were rejected with a stub error) Attach a file by drag & drop or by pasting — drop a file onto the terminal, or copy a file in your file manager and press Ctrl+V, to stage it as an attachment (the next Enter sends it with your caption). Multiple files at once are supported. Ctrl+V still pastes text when the clipboard holds text; Ctrl+Y is a paste alias. On Windows the copied-file paste reads the clipboard's file list directly (Windows Terminal never pastes copied files as text), with xclip/wl-paste used on Linux Pick ASCII-art size for attached images — /size opens a Small/Medium/Large picker (40×40 / 80×80 / 120×120) with descriptions, /size sets it directly, and /send -l sets it for that message. The choice is a saved preference and applies to copy-paste/drag-drop images (which have no per-file flag); the current size is shown in the staging tray New TransparentLight theme — dark characters on a transparent background, for light terminal color schemes (/theme transparentlight) Timestamps in messages are now aware of the current culture and display the short time pattern for today's messages and the short date+time pattern for older messages. Bug Fixes Transparent themes no longer draw an opaque box behind the message input. The input TextView renders with the Editable visual role, which Terminal.Gui derives as an opaque color when a theme leaves it unset; the themes now pin Editable/ReadOnly to their base colors so the input matches its (transparent) background. Attachments whose files have been pruned (retention cleanup deletes blobs older than Storage:RetentionDays but left the message rows) no longer render a dead download/preview. When channel history loads, the server checks which attachment blobs still exist: missing ones are dropped from the message, and an attachment-only message whose files are all gone is removed from the database. Fixed intermittent crash on Ctrl+W — Terminal.Gui binds Ctrl+W to clipboard-cut, and Windows clipboard contention (another app holding the clipboard) threw an unhandled Win32Exception that took the app down. Ctrl+W now deletes the previous word (readline behavior, no clipboard), and all clipboard shortcuts (Ctrl+X/C/V/Y) are guarded so transient clipboard failures log a warning instead of crashing Fixed emoji shortcode replacement permanently disabling itself if a cursor update threw mid-replacement IRC LIST no longer leaks private channels; protected channels are marked [+k] API Changes ChannelDto gains isProtected and isEncrypted; CreateChannelRequest gains optional password, encryptionSalt, and wrappedRoomKey; SignalR JoinChannel takes an optional second password argument and JoinChannelResult gains passwordRequired, encryptionSalt, and wrappedRoomKey (older clients must update to join over SignalR) New endpoints: GET /api/channels/{channel}/crypto (public crypto metadata — salt only, never the wrapped key) and POST /api/channels/{channel}/rekey (creator-only passphrase change) The upload endpoint accepts type and content form fields for encrypted channels, where the client supplies the declared message type and room-encrypted content ImageToAsciiService and FileValidationHelper moved from EchoHub.Server to EchoHub.Core so the client can render ASCII art and detect file types for encrypted uploads Message shape change: MessageDto drops Type/AttachmentUrl/AttachmentFileName/AttachmentFileSize and gains Attachments (a list of AttachmentDto { Kind, Url, FileName, FileSize, AsciiPreview }, null/empty for plain text). New Attachment entity + table with a cascade FK to Message New endpoint POST /api/channels/{channel}/messages (multipart: content + N files, plus kind/preview per file for encrypted channels) replaces the single-file upload endpoint; DELETE /api/moderation/messages/{id} now enforces the own-or-higher-role rule New EF migrations AddChannelPasswordHash, AddChannelEncryptionEnvelope, and AddMessageAttachments (applied automatically on server start); a one-time startup data migration folds legacy single-attachment messages into the new model" + "summary": "v0.2.12 Private channels are now genuinely private: password-protected channels are end-to-end encrypted, so the server (and its operators) can gate joins and measure storage but cannot read message or file contents. The IRC gateway grows real MODE/TOPIC support and channel keys, and the client gets image \"save original\", a transparent-light theme, drag-and-drop file sending, Ctrl+V paste, and a fix for the intermittent Ctrl+W crash. New Features End-to-end encrypted channels — creating a channel with a password now provisions a zero-knowledge room: The passphrase never leaves the client. It derives (PBKDF2-SHA256, 210k iterations) two keys: an auth key sent to the server as the join credential, and a key-encryption key that never leaves the machine. A random room content key encrypts every message and file with AES-256-GCM. The server only ever stores the room key wrapped under the passphrase, so it can gate joins and report a channel's message count, storage size, and attachments — but cannot decrypt any of it. Even the server owner cannot read a private room's contents. Members' clients cache the derived room key locally (in the per-server config, like saved sessions) so the passphrase isn't retyped every launch; joining on a new device prompts for it once. Change the passphrase with /passwd (channel creator only). The room key is re-wrapped, not rotated, so existing history stays readable and members who join later with the new passphrase can still read older messages. Files and images are encrypted client-side before upload; for images the ASCII-art preview is rendered on the client and stored room-encrypted too. Sending images by URL is disabled in encrypted channels (the server can't fetch-and-render without the key). End-to-end encrypted channels cannot be joined over the IRC gateway (that would require the server to hold the room key) — IRC JOIN returns 475 directing users to the EchoHub client. Password-protected channels — set an optional password when creating a channel (masked field in the Create Channel dialog, password on POST /api/channels). Passwords are BCrypt-hashed server-side; the join gate applies on first join only (existing members and the creator are unaffected). Protected channels show a * marker in the channel list and +k in the status bar Save original images — image messages now show a clickable \"[↓ save original]\" line under the ASCII-art preview that downloads the full-resolution original to your Downloads folder (decrypting locally in encrypted channels) Messages with attachments (Discord-style) — a message is now text plus a list of attachments instead of being either text or a single file. One message can carry a caption and several files (images, audio, docs) together: Compose with a staging tray: /send or dropping files onto the terminal stages them (shown on the input bar); the next Enter sends your typed caption and all staged files as one message. /clear drops staged files. /send still posts an image immediately. Each image attachment renders its own ASCII preview with its own \"save original\" action; audio/file attachments each get their own play/download line. In encrypted channels every attachment is encrypted individually (blob + ASCII preview), and the caption is room-encrypted — the server still stores only ciphertext and can report count/size but not contents. Up to 10 attachments per message. Right-click message menu — right-click any message for a context menu: save/download/play its attachment, mention the sender, view their profile, copy the text, copy the message ID (for linking or command arguments), or delete the message. (Keyboard: F6 focuses the message list for arrow-key selection + Delete.) The selected message is now highlighted while the list is focused. Message deletion — press Delete on a selected message to remove it. You can always delete your own messages; moderators and above can delete others' messages, but only from users below their own role (a mod can't delete an admin's or owner's message). Deleting a message also removes its attachment blobs from server storage. Customizable download folder — /downloadpath opens your OS-native folder picker (Windows Explorer / macOS Finder / Linux GTK or KDE) to choose where downloaded attachments and saved images go; /downloadpath sets it directly (the fallback when no native picker is available). Downloaded files now land in that folder (with automatic (n) de-duplication) instead of a temp directory. /join [password] — join protected channels inline, or let the client prompt: joining a protected channel without a password opens a masked prompt that re-prompts on a wrong password IRC channel keys — JOIN #room works against room passwords (RFC 1459 comma-paired key lists supported); keyless or wrong-key joins get 475 ERR_BADCHANNELKEY IRC MODE implemented — MODE #chan reports +k/+, MODE #chan +k sets and -k clears the room password (channel creator or admin only), ban-list probes get a clean empty reply, and CHANMODES is advertised in ISUPPORT IRC TOPIC set support — the channel creator can change the topic from IRC; the change broadcasts to connected TUI clients (previously topic changes were rejected with a stub error) Attach a file by drag & drop or by pasting — drop a file onto the terminal, or copy a file in your file manager and press Ctrl+V, to stage it as an attachment (the next Enter sends it with your caption). Multiple files at once are supported. Ctrl+V still pastes text when the clipboard holds text; Ctrl+Y is a paste alias. On Windows the copied-file paste reads the clipboard's file list directly (Windows Terminal never pastes copied files as text), with xclip/wl-paste used on Linux Pick ASCII-art size for attached images — /size opens a Small/Medium/Large picker (40×40 / 80×80 / 120×120) with descriptions, /size sets it directly, and /send -l sets it for that message. The choice is a saved preference and applies to copy-paste/drag-drop images (which have no per-file flag); the current size is shown in the staging tray New TransparentLight theme — dark characters on a transparent background, for light terminal color schemes (/theme transparentlight) /meta command — shows a summary of the current room: room id, created date, message count, unique participant count, estimated storage size, and protection level (open / password-protected / end-to-end encrypted). Works for encrypted rooms too, since these are all metadata the server tracks without reading content. (/info is an alias.) Configurable upload limits — a new Uploads section in the server settings sets the maximum size per file, image, audio clip, and avatar, plus the maximum attachments per message. Absent values fall back to the previous built-in limits, so existing servers are unaffected until they opt in. Updater progress bar — the self-updater now draws a real progress bar on the console (Downloading [████████░░] 62%) for each step, replacing the plain status lines, so a large download shows visible progress. Timestamps in messages are culture-aware and now always include the date: today's messages show a compact date + short time, and older messages show the general short date/time. Bug Fixes Transparent themes no longer draw an opaque box behind the message input. The input TextView renders with the Editable visual role, which Terminal.Gui derives as an opaque color when a theme leaves it unset; the themes now pin Editable/ReadOnly to their base colors so the input matches its (transparent) background. Attachments whose files have been pruned (retention cleanup deletes blobs older than Storage:RetentionDays but left the message rows) no longer render a dead download/preview. When channel history loads, the server checks which attachment blobs still exist: missing ones are dropped from the message, and an attachment-only message whose files are all gone is removed from the database. Fixed intermittent crash on Ctrl+W — Terminal.Gui binds Ctrl+W to clipboard-cut, and Windows clipboard contention (another app holding the clipboard) threw an unhandled Win32Exception that took the app down. Ctrl+W now deletes the previous word (readline behavior, no clipboard), and all clipboard shortcuts (Ctrl+X/C/V/Y) are guarded so transient clipboard failures log a warning instead of crashing Fixed emoji shortcode replacement permanently disabling itself if a cursor update threw mid-replacement Fixed new messages showing no date — today's messages rendered time-only, and the \"is it today?\" check compared the server's UTC date against the local date, so the classification could also be wrong near midnight. Timestamps are now converted to local time first and always include the date. Fixed the self-updater hanging at \"extracting\" — the update ran while the Terminal.Gui main loop still owned the console, so the old and new processes deadlocked over it. The update now runs after the TUI shuts down, on a clean console IRC LIST no longer leaks private channels; protected channels are marked [+k] API Changes ChannelDto gains isProtected and isEncrypted; CreateChannelRequest gains optional password, encryptionSalt, and wrappedRoomKey; SignalR JoinChannel takes an optional second password argument and JoinChannelResult gains passwordRequired, encryptionSalt, and wrappedRoomKey (older clients must update to join over SignalR) New endpoints: GET /api/channels/{channel}/crypto (public crypto metadata — salt only, never the wrapped key) and POST /api/channels/{channel}/rekey (creator-only passphrase change) New endpoint GET /api/channels/{channel}/meta returns a ChannelMetaDto (room id, name, topic, encrypted/protected flags, message count, unique user count, estimated size, created date) backing the /meta command New server Uploads configuration section (MaxFileSizeMB, MaxImageSizeMB, MaxAudioSizeMB, MaxAvatarSizeMB, MaxAttachmentsPerMessage); the message-upload endpoint's request-body ceiling is now derived from these values at runtime rather than from compile-time constants The upload endpoint accepts type and content form fields for encrypted channels, where the client supplies the declared message type and room-encrypted content ImageToAsciiService and FileValidationHelper moved from EchoHub.Server to EchoHub.Core so the client can render ASCII art and detect file types for encrypted uploads Message shape change: MessageDto drops Type/AttachmentUrl/AttachmentFileName/AttachmentFileSize and gains Attachments (a list of AttachmentDto { Kind, Url, FileName, FileSize, AsciiPreview }, null/empty for plain text). New Attachment entity + table with a cascade FK to Message New endpoint POST /api/channels/{channel}/messages (multipart: content + N files, plus kind/preview per file for encrypted channels) replaces the single-file upload endpoint; DELETE /api/moderation/messages/{id} now enforces the own-or-higher-role rule New EF migrations AddChannelPasswordHash, AddChannelEncryptionEnvelope, and AddMessageAttachments (applied automatically on server start); a one-time startup data migration folds legacy single-attachment messages into the new model" + }, + "changelog/v0.2.13.html": { + "href": "changelog/v0.2.13.html", + "title": "v0.2.13 | EchoHub Documentation", + "summary": "v0.2.13 A visual overhaul of the chat client — WeeChat-style column layout, per-user nick colors, date rules, unread markers, an activity status segment, and an ASCII welcome banner — plus reliable cross-channel notifications: the client now joins all your channels at connect so unread counts and @mentions light up everywhere, and read positions persist across restarts so activity that happened while you were offline still shows. Modern polish, old IRC soul. New Features Aligned message layout (WeeChat-style) — every message renders as HH:mm nickname │ text: a dim timestamp, the nick right-aligned in a fixed 12-column gutter, and a dim │ rail separating names from content. Word-wrapped continuation lines, multi-line messages, attachments, ASCII-art image previews, and link embeds all align under the text column and extend the rail, so conversations read as a clean two-column grid. Over-long nicks are truncated with … in the gutter only — profiles, mentions, and the users panel always use the full name. System and status lines (joined, is now Away, …) show -- in the nick column, replacing the old ** prefix. Deterministic nickname colors — users who haven't picked a /color now get an automatic, stable color derived from their nick (12-entry palette chosen to stay readable on dark and light backgrounds). The same user is the same color in message headers and the online-users panel, on every client, every session. An explicitly chosen nickname color still takes priority. Date separator rules — a full-width ── Wed, Jul 16 2026 ───── rule is inserted whenever the calendar day changes (live, in loaded history, and when scrolling back through older messages). Timestamps are now a compact HH:mm — the date lives in the rules instead of being repeated on every line. \"New messages\" marker — the first message that arrives in a channel you're not viewing gets an orange ── new messages ── rule above it (irssi-style). Switch to the channel and the marker shows exactly where you left off; it survives the history reload on channel switch and is consumed when you move away again. Status bar activity segment — channels with unread messages appear in the status bar as │ Act: #dev,#random (up to 4, +n overflow). Channels where you were @mentioned show in orange, others in cyan. Connecting spinner — transitional connection states (Connecting, Reconnecting, Authenticating…) now animate a braille spinner in the status bar instead of sitting on static text. Mention-aware channel list — a channel where you were @mentioned turns orange in the channel list (name and unread badge), escalating above the cyan plain-unread highlight. The highlight clears when you view the channel. Welcome banner — with no channel selected (fresh start, or after disconnecting) the chat pane shows a gold-gradient ASCII \"ECHOHUB\" logo with the version and key hints, instead of an empty box. Narrow panes get a compact variant. Rounded frame borders — the channels, chat, input, and users panels draw with rounded corners. Auto-join all channels at connect — the client now joins every channel it lists for you (public channels and prior memberships) as part of connecting, so message events flow for all of them: unread badges, @mention highlights, and the status-bar activity segment work without having to open each channel first. Protected channels you've never entered are skipped silently (joining them stays a prompted, manual action), and channels you /leave are remembered and excluded until you join them again. Read positions survive restarts — the client persists the last message you read per channel (locally, per server). On the next connect it compares that against fetched history, so messages that arrived while you were offline still produce unread counts, mention highlights, and a correctly placed \"new messages\" marker. Theme-tinted frame borders — themes can now color the window frame borders independently of text via a new optional border section (hex values supported). The Transparent and TransparentLight themes use it to draw dim gray borders instead of stark white ones, for a subtler, glassier look. Custom theme JSON files without a border section keep their base colors. Bug Fixes The @mention highlight no longer drops off the second and later lines of a long mention message — wrapped continuation lines now inherit the highlight. The unread-position marker is re-anchored after a channel switch reloads history, instead of being wiped by the reload before you could see it. Cached room keys for end-to-end encrypted channels no longer disappear from the config on reconnect. Saving the server entry after a successful connect replaced it wholesale, wiping the cached keys (and forcing a passphrase re-entry on the next launch); the entry is now updated in place. A failed channel rejoin after a reconnect (e.g. a channel deleted while you were away) no longer aborts rejoining the remaining channels. Client config file access is now serialized across threads (token refresh, room-key cache, and read-position checkpoints all write it), preventing rare config corruption." }, "changelog/v0.2.2.html": { "href": "changelog/v0.2.2.html", diff --git a/manifest.json b/manifest.json index 83f45af..93f636a 100644 --- a/manifest.json +++ b/manifest.json @@ -780,20 +780,6 @@ "Title": "EchoHub.Client.UI.Dialogs.UpdateConfirmDialog", "Summary": null }, - { - "type": "ManagedReference", - "source_relative_path": "_api_meta/client/EchoHub.Client.UI.Dialogs.UpdateProgressDialog.yml", - "output": { - ".html": { - "relative_path": "api/client/EchoHub.Client.UI.Dialogs.UpdateProgressDialog.html" - } - }, - "version": "", - "Uid": null, - "IsMRef": true, - "Title": "EchoHub.Client.UI.Dialogs.UpdateProgressDialog", - "Summary": null - }, { "type": "ManagedReference", "source_relative_path": "_api_meta/client/EchoHub.Client.UI.Dialogs.yml", @@ -850,6 +836,20 @@ "Title": "EchoHub.Client.UI.Helpers.HexColorHelper", "Summary": "

      Helper to parse hex colors to Terminal.Gui Attributes.

      \n" }, + { + "type": "ManagedReference", + "source_relative_path": "_api_meta/client/EchoHub.Client.UI.Helpers.NickColorHelper.yml", + "output": { + ".html": { + "relative_path": "api/client/EchoHub.Client.UI.Helpers.NickColorHelper.html" + } + }, + "version": "", + "Uid": null, + "IsMRef": true, + "Title": "EchoHub.Client.UI.Helpers.NickColorHelper", + "Summary": "

      Deterministic per-nick colors for users who haven't picked a nickname color.\nThe same nick always maps to the same palette entry (classic IRC client behavior),\nso a busy channel stays scannable without any configuration.

      \n" + }, { "type": "ManagedReference", "source_relative_path": "_api_meta/client/EchoHub.Client.UI.Helpers.yml", @@ -1242,6 +1242,20 @@ "Title": "EchoHub.Core.DTOs.ChannelError", "Summary": null }, + { + "type": "ManagedReference", + "source_relative_path": "_api_meta/core/EchoHub.Core.DTOs.ChannelMetaDto.yml", + "output": { + ".html": { + "relative_path": "api/core/EchoHub.Core.DTOs.ChannelMetaDto.html" + } + }, + "version": "", + "Uid": null, + "IsMRef": true, + "Title": "EchoHub.Core.DTOs.ChannelMetaDto", + "Summary": "

      Human-facing summary of a channel (the /meta command). For encrypted channels the\nserver still knows these figures — count, timestamps, and stored blob sizes — even though it\ncannot read the content itself. is the sum of stored\nattachment blob sizes plus message text length, so it is an estimate, not an exact on-disk total.

      \n" + }, { "type": "ManagedReference", "source_relative_path": "_api_meta/core/EchoHub.Core.DTOs.ChannelOperationResult.yml", @@ -2026,6 +2040,34 @@ "Title": "EchoHub.Server.Auth", "Summary": null }, + { + "type": "ManagedReference", + "source_relative_path": "_api_meta/server/EchoHub.Server.Config.UploadLimits.yml", + "output": { + ".html": { + "relative_path": "api/server/EchoHub.Server.Config.UploadLimits.html" + } + }, + "version": "", + "Uid": null, + "IsMRef": true, + "Title": "EchoHub.Server.Config.UploadLimits", + "Summary": "

      Admin-configurable upload limits, bound from the Uploads configuration section.\nSizes are expressed in megabytes in configuration; the *Bytes accessors convert them\nfor enforcement. Every default mirrors so an absent or partial\nUploads section preserves the historical built-in limits.

      \n" + }, + { + "type": "ManagedReference", + "source_relative_path": "_api_meta/server/EchoHub.Server.Config.yml", + "output": { + ".html": { + "relative_path": "api/server/EchoHub.Server.Config.html" + } + }, + "version": "", + "Uid": null, + "IsMRef": true, + "Title": "EchoHub.Server.Config", + "Summary": null + }, { "type": "ManagedReference", "source_relative_path": "_api_meta/server/EchoHub.Server.Controllers.AuthController.yml", @@ -2708,6 +2750,16 @@ }, "version": "" }, + { + "type": "Conceptual", + "source_relative_path": "articles/encrypted-rooms.md", + "output": { + ".html": { + "relative_path": "articles/encrypted-rooms.html" + } + }, + "version": "" + }, { "type": "Conceptual", "source_relative_path": "articles/encryption.md", @@ -2844,6 +2896,16 @@ }, "version": "" }, + { + "type": "Conceptual", + "source_relative_path": "changelog/v0.2.13.md", + "output": { + ".html": { + "relative_path": "changelog/v0.2.13.html" + } + }, + "version": "" + }, { "type": "Conceptual", "source_relative_path": "changelog/v0.2.2.md", diff --git a/xrefmap.yml b/xrefmap.yml index c70fe3f..5567cfc 100644 --- a/xrefmap.yml +++ b/xrefmap.yml @@ -55,6 +55,18 @@ references: commentId: Overload:EchoHub.Client.AppOrchestrator.MainWindow fullName: EchoHub.Client.AppOrchestrator.MainWindow nameWithType: AppOrchestrator.MainWindow +- uid: EchoHub.Client.AppOrchestrator.PendingUpdate + name: PendingUpdate + href: api/client/EchoHub.Client.AppOrchestrator.html#EchoHub_Client_AppOrchestrator_PendingUpdate + commentId: P:EchoHub.Client.AppOrchestrator.PendingUpdate + fullName: EchoHub.Client.AppOrchestrator.PendingUpdate + nameWithType: AppOrchestrator.PendingUpdate +- uid: EchoHub.Client.AppOrchestrator.PendingUpdate* + name: PendingUpdate + href: api/client/EchoHub.Client.AppOrchestrator.html#EchoHub_Client_AppOrchestrator_PendingUpdate_ + commentId: Overload:EchoHub.Client.AppOrchestrator.PendingUpdate + fullName: EchoHub.Client.AppOrchestrator.PendingUpdate + nameWithType: AppOrchestrator.PendingUpdate - uid: EchoHub.Client.Commands name: EchoHub.Client.Commands href: api/client/EchoHub.Client.Commands.html @@ -199,6 +211,12 @@ references: commentId: E:EchoHub.Client.Commands.CommandHandler.OnQuit fullName: EchoHub.Client.Commands.CommandHandler.OnQuit nameWithType: CommandHandler.OnQuit +- uid: EchoHub.Client.Commands.CommandHandler.OnRoomInfo + name: OnRoomInfo + href: api/client/EchoHub.Client.Commands.CommandHandler.html#EchoHub_Client_Commands_CommandHandler_OnRoomInfo + commentId: E:EchoHub.Client.Commands.CommandHandler.OnRoomInfo + fullName: EchoHub.Client.Commands.CommandHandler.OnRoomInfo + nameWithType: CommandHandler.OnRoomInfo - uid: EchoHub.Client.Commands.CommandHandler.OnSendFile name: OnSendFile href: api/client/EchoHub.Client.Commands.CommandHandler.html#EchoHub_Client_Commands_CommandHandler_OnSendFile @@ -790,6 +808,30 @@ references: commentId: Overload:EchoHub.Client.Config.SavedServer.LastConnected fullName: EchoHub.Client.Config.SavedServer.LastConnected nameWithType: SavedServer.LastConnected +- uid: EchoHub.Client.Config.SavedServer.LastReadMessages + name: LastReadMessages + href: api/client/EchoHub.Client.Config.SavedServer.html#EchoHub_Client_Config_SavedServer_LastReadMessages + commentId: P:EchoHub.Client.Config.SavedServer.LastReadMessages + fullName: EchoHub.Client.Config.SavedServer.LastReadMessages + nameWithType: SavedServer.LastReadMessages +- uid: EchoHub.Client.Config.SavedServer.LastReadMessages* + name: LastReadMessages + href: api/client/EchoHub.Client.Config.SavedServer.html#EchoHub_Client_Config_SavedServer_LastReadMessages_ + commentId: Overload:EchoHub.Client.Config.SavedServer.LastReadMessages + fullName: EchoHub.Client.Config.SavedServer.LastReadMessages + nameWithType: SavedServer.LastReadMessages +- uid: EchoHub.Client.Config.SavedServer.LeftChannels + name: LeftChannels + href: api/client/EchoHub.Client.Config.SavedServer.html#EchoHub_Client_Config_SavedServer_LeftChannels + commentId: P:EchoHub.Client.Config.SavedServer.LeftChannels + fullName: EchoHub.Client.Config.SavedServer.LeftChannels + nameWithType: SavedServer.LeftChannels +- uid: EchoHub.Client.Config.SavedServer.LeftChannels* + name: LeftChannels + href: api/client/EchoHub.Client.Config.SavedServer.html#EchoHub_Client_Config_SavedServer_LeftChannels_ + commentId: Overload:EchoHub.Client.Config.SavedServer.LeftChannels + fullName: EchoHub.Client.Config.SavedServer.LeftChannels + nameWithType: SavedServer.LeftChannels - uid: EchoHub.Client.Config.SavedServer.Name name: Name href: api/client/EchoHub.Client.Config.SavedServer.html#EchoHub_Client_Config_SavedServer_Name @@ -1006,6 +1048,21 @@ references: commentId: Overload:EchoHub.Client.Services.ApiClient.GetChannelCryptoAsync fullName: EchoHub.Client.Services.ApiClient.GetChannelCryptoAsync nameWithType: ApiClient.GetChannelCryptoAsync +- uid: EchoHub.Client.Services.ApiClient.GetChannelMetaAsync(System.String) + name: GetChannelMetaAsync(string) + href: api/client/EchoHub.Client.Services.ApiClient.html#EchoHub_Client_Services_ApiClient_GetChannelMetaAsync_System_String_ + commentId: M:EchoHub.Client.Services.ApiClient.GetChannelMetaAsync(System.String) + name.vb: GetChannelMetaAsync(String) + fullName: EchoHub.Client.Services.ApiClient.GetChannelMetaAsync(string) + fullName.vb: EchoHub.Client.Services.ApiClient.GetChannelMetaAsync(String) + nameWithType: ApiClient.GetChannelMetaAsync(string) + nameWithType.vb: ApiClient.GetChannelMetaAsync(String) +- uid: EchoHub.Client.Services.ApiClient.GetChannelMetaAsync* + name: GetChannelMetaAsync + href: api/client/EchoHub.Client.Services.ApiClient.html#EchoHub_Client_Services_ApiClient_GetChannelMetaAsync_ + commentId: Overload:EchoHub.Client.Services.ApiClient.GetChannelMetaAsync + fullName: EchoHub.Client.Services.ApiClient.GetChannelMetaAsync + nameWithType: ApiClient.GetChannelMetaAsync - uid: EchoHub.Client.Services.ApiClient.GetChannelsAsync name: GetChannelsAsync() href: api/client/EchoHub.Client.Services.ApiClient.html#EchoHub_Client_Services_ApiClient_GetChannelsAsync @@ -2956,6 +3013,18 @@ references: commentId: Overload:EchoHub.Client.Services.UpdateChecker.Dispose fullName: EchoHub.Client.Services.UpdateChecker.Dispose nameWithType: UpdateChecker.Dispose +- uid: EchoHub.Client.Services.UpdateChecker.PendingUpdate + name: PendingUpdate + href: api/client/EchoHub.Client.Services.UpdateChecker.html#EchoHub_Client_Services_UpdateChecker_PendingUpdate + commentId: P:EchoHub.Client.Services.UpdateChecker.PendingUpdate + fullName: EchoHub.Client.Services.UpdateChecker.PendingUpdate + nameWithType: UpdateChecker.PendingUpdate +- uid: EchoHub.Client.Services.UpdateChecker.PendingUpdate* + name: PendingUpdate + href: api/client/EchoHub.Client.Services.UpdateChecker.html#EchoHub_Client_Services_UpdateChecker_PendingUpdate_ + commentId: Overload:EchoHub.Client.Services.UpdateChecker.PendingUpdate + fullName: EchoHub.Client.Services.UpdateChecker.PendingUpdate + nameWithType: UpdateChecker.PendingUpdate - uid: EchoHub.Client.Services.UpdateChecker.Start name: Start() href: api/client/EchoHub.Client.Services.UpdateChecker.html#EchoHub_Client_Services_UpdateChecker_Start @@ -3010,6 +3079,18 @@ references: commentId: Overload:EchoHub.Client.Themes.Theme.Base fullName: EchoHub.Client.Themes.Theme.Base nameWithType: Theme.Base +- uid: EchoHub.Client.Themes.Theme.Border + name: Border + href: api/client/EchoHub.Client.Themes.Theme.html#EchoHub_Client_Themes_Theme_Border + commentId: P:EchoHub.Client.Themes.Theme.Border + fullName: EchoHub.Client.Themes.Theme.Border + nameWithType: Theme.Border +- uid: EchoHub.Client.Themes.Theme.Border* + name: Border + href: api/client/EchoHub.Client.Themes.Theme.html#EchoHub_Client_Themes_Theme_Border_ + commentId: Overload:EchoHub.Client.Themes.Theme.Border + fullName: EchoHub.Client.Themes.Theme.Border + nameWithType: Theme.Border - uid: EchoHub.Client.Themes.Theme.Dialog name: Dialog href: api/client/EchoHub.Client.Themes.Theme.html#EchoHub_Client_Themes_Theme_Dialog @@ -3217,6 +3298,12 @@ references: commentId: F:EchoHub.Client.UI.Chat.ChatColors.ChannelRefAttr fullName: EchoHub.Client.UI.Chat.ChatColors.ChannelRefAttr nameWithType: ChatColors.ChannelRefAttr +- uid: EchoHub.Client.UI.Chat.ChatColors.DateRuleAttr + name: DateRuleAttr + href: api/client/EchoHub.Client.UI.Chat.ChatColors.html#EchoHub_Client_UI_Chat_ChatColors_DateRuleAttr + commentId: F:EchoHub.Client.UI.Chat.ChatColors.DateRuleAttr + fullName: EchoHub.Client.UI.Chat.ChatColors.DateRuleAttr + nameWithType: ChatColors.DateRuleAttr - uid: EchoHub.Client.UI.Chat.ChatColors.EmbedBorderAttr name: EmbedBorderAttr href: api/client/EchoHub.Client.UI.Chat.ChatColors.html#EchoHub_Client_UI_Chat_ChatColors_EmbedBorderAttr @@ -3259,6 +3346,12 @@ references: commentId: F:EchoHub.Client.UI.Chat.ChatColors.MentionTextAttr fullName: EchoHub.Client.UI.Chat.ChatColors.MentionTextAttr nameWithType: ChatColors.MentionTextAttr +- uid: EchoHub.Client.UI.Chat.ChatColors.RailAttr + name: RailAttr + href: api/client/EchoHub.Client.UI.Chat.ChatColors.html#EchoHub_Client_UI_Chat_ChatColors_RailAttr + commentId: F:EchoHub.Client.UI.Chat.ChatColors.RailAttr + fullName: EchoHub.Client.UI.Chat.ChatColors.RailAttr + nameWithType: ChatColors.RailAttr - uid: EchoHub.Client.UI.Chat.ChatColors.SplitMentions(System.String,System.Nullable{Terminal.Gui.Drawing.Attribute}) name: SplitMentions(string, Attribute?) href: api/client/EchoHub.Client.UI.Chat.ChatColors.html#EchoHub_Client_UI_Chat_ChatColors_SplitMentions_System_String_System_Nullable_Terminal_Gui_Drawing_Attribute__ @@ -3286,6 +3379,12 @@ references: commentId: F:EchoHub.Client.UI.Chat.ChatColors.TimestampAttr fullName: EchoHub.Client.UI.Chat.ChatColors.TimestampAttr nameWithType: ChatColors.TimestampAttr +- uid: EchoHub.Client.UI.Chat.ChatColors.UnreadMarkerAttr + name: UnreadMarkerAttr + href: api/client/EchoHub.Client.UI.Chat.ChatColors.html#EchoHub_Client_UI_Chat_ChatColors_UnreadMarkerAttr + commentId: F:EchoHub.Client.UI.Chat.ChatColors.UnreadMarkerAttr + fullName: EchoHub.Client.UI.Chat.ChatColors.UnreadMarkerAttr + nameWithType: ChatColors.UnreadMarkerAttr - uid: EchoHub.Client.UI.Chat.ChatLine name: ChatLine href: api/client/EchoHub.Client.UI.Chat.ChatLine.html @@ -3367,6 +3466,18 @@ references: commentId: Overload:EchoHub.Client.UI.Chat.ChatLine.ContinuationIndent fullName: EchoHub.Client.UI.Chat.ChatLine.ContinuationIndent nameWithType: ChatLine.ContinuationIndent +- uid: EchoHub.Client.UI.Chat.ChatLine.ContinuationPrefixSegments + name: ContinuationPrefixSegments + href: api/client/EchoHub.Client.UI.Chat.ChatLine.html#EchoHub_Client_UI_Chat_ChatLine_ContinuationPrefixSegments + commentId: P:EchoHub.Client.UI.Chat.ChatLine.ContinuationPrefixSegments + fullName: EchoHub.Client.UI.Chat.ChatLine.ContinuationPrefixSegments + nameWithType: ChatLine.ContinuationPrefixSegments +- uid: EchoHub.Client.UI.Chat.ChatLine.ContinuationPrefixSegments* + name: ContinuationPrefixSegments + href: api/client/EchoHub.Client.UI.Chat.ChatLine.html#EchoHub_Client_UI_Chat_ChatLine_ContinuationPrefixSegments_ + commentId: Overload:EchoHub.Client.UI.Chat.ChatLine.ContinuationPrefixSegments + fullName: EchoHub.Client.UI.Chat.ChatLine.ContinuationPrefixSegments + nameWithType: ChatLine.ContinuationPrefixSegments - uid: EchoHub.Client.UI.Chat.ChatLine.FromColoredText(System.String,System.Nullable{Terminal.Gui.Drawing.Attribute}) name: FromColoredText(string, Attribute?) href: api/client/EchoHub.Client.UI.Chat.ChatLine.html#EchoHub_Client_UI_Chat_ChatLine_FromColoredText_System_String_System_Nullable_Terminal_Gui_Drawing_Attribute__ @@ -3409,6 +3520,18 @@ references: commentId: Overload:EchoHub.Client.UI.Chat.ChatLine.IsMention fullName: EchoHub.Client.UI.Chat.ChatLine.IsMention nameWithType: ChatLine.IsMention +- uid: EchoHub.Client.UI.Chat.ChatLine.IsUnreadMarker + name: IsUnreadMarker + href: api/client/EchoHub.Client.UI.Chat.ChatLine.html#EchoHub_Client_UI_Chat_ChatLine_IsUnreadMarker + commentId: P:EchoHub.Client.UI.Chat.ChatLine.IsUnreadMarker + fullName: EchoHub.Client.UI.Chat.ChatLine.IsUnreadMarker + nameWithType: ChatLine.IsUnreadMarker +- uid: EchoHub.Client.UI.Chat.ChatLine.IsUnreadMarker* + name: IsUnreadMarker + href: api/client/EchoHub.Client.UI.Chat.ChatLine.html#EchoHub_Client_UI_Chat_ChatLine_IsUnreadMarker_ + commentId: Overload:EchoHub.Client.UI.Chat.ChatLine.IsUnreadMarker + fullName: EchoHub.Client.UI.Chat.ChatLine.IsUnreadMarker + nameWithType: ChatLine.IsUnreadMarker - uid: EchoHub.Client.UI.Chat.ChatLine.MessageId name: MessageId href: api/client/EchoHub.Client.UI.Chat.ChatLine.html#EchoHub_Client_UI_Chat_ChatLine_MessageId @@ -3421,6 +3544,30 @@ references: commentId: Overload:EchoHub.Client.UI.Chat.ChatLine.MessageId fullName: EchoHub.Client.UI.Chat.ChatLine.MessageId nameWithType: ChatLine.MessageId +- uid: EchoHub.Client.UI.Chat.ChatLine.RuleAttr + name: RuleAttr + href: api/client/EchoHub.Client.UI.Chat.ChatLine.html#EchoHub_Client_UI_Chat_ChatLine_RuleAttr + commentId: P:EchoHub.Client.UI.Chat.ChatLine.RuleAttr + fullName: EchoHub.Client.UI.Chat.ChatLine.RuleAttr + nameWithType: ChatLine.RuleAttr +- uid: EchoHub.Client.UI.Chat.ChatLine.RuleAttr* + name: RuleAttr + href: api/client/EchoHub.Client.UI.Chat.ChatLine.html#EchoHub_Client_UI_Chat_ChatLine_RuleAttr_ + commentId: Overload:EchoHub.Client.UI.Chat.ChatLine.RuleAttr + fullName: EchoHub.Client.UI.Chat.ChatLine.RuleAttr + nameWithType: ChatLine.RuleAttr +- uid: EchoHub.Client.UI.Chat.ChatLine.RuleLabel + name: RuleLabel + href: api/client/EchoHub.Client.UI.Chat.ChatLine.html#EchoHub_Client_UI_Chat_ChatLine_RuleLabel + commentId: P:EchoHub.Client.UI.Chat.ChatLine.RuleLabel + fullName: EchoHub.Client.UI.Chat.ChatLine.RuleLabel + nameWithType: ChatLine.RuleLabel +- uid: EchoHub.Client.UI.Chat.ChatLine.RuleLabel* + name: RuleLabel + href: api/client/EchoHub.Client.UI.Chat.ChatLine.html#EchoHub_Client_UI_Chat_ChatLine_RuleLabel_ + commentId: Overload:EchoHub.Client.UI.Chat.ChatLine.RuleLabel + fullName: EchoHub.Client.UI.Chat.ChatLine.RuleLabel + nameWithType: ChatLine.RuleLabel - uid: EchoHub.Client.UI.Chat.ChatLine.Segments name: Segments href: api/client/EchoHub.Client.UI.Chat.ChatLine.html#EchoHub_Client_UI_Chat_ChatLine_Segments @@ -3811,6 +3958,12 @@ references: commentId: Overload:EchoHub.Client.UI.Chat.ChatMessageManager.ClearUnread fullName: EchoHub.Client.UI.Chat.ChatMessageManager.ClearUnread nameWithType: ChatMessageManager.ClearUnread +- uid: EchoHub.Client.UI.Chat.ChatMessageManager.ContentIndentCols + name: ContentIndentCols + href: api/client/EchoHub.Client.UI.Chat.ChatMessageManager.html#EchoHub_Client_UI_Chat_ChatMessageManager_ContentIndentCols + commentId: F:EchoHub.Client.UI.Chat.ChatMessageManager.ContentIndentCols + fullName: EchoHub.Client.UI.Chat.ChatMessageManager.ContentIndentCols + nameWithType: ChatMessageManager.ContentIndentCols - uid: EchoHub.Client.UI.Chat.ChatMessageManager.CurrentChannel name: CurrentChannel href: api/client/EchoHub.Client.UI.Chat.ChatMessageManager.html#EchoHub_Client_UI_Chat_ChatMessageManager_CurrentChannel @@ -3871,27 +4024,57 @@ references: commentId: E:EchoHub.Client.UI.Chat.ChatMessageManager.HistoryPrepended fullName: EchoHub.Client.UI.Chat.ChatMessageManager.HistoryPrepended nameWithType: ChatMessageManager.HistoryPrepended -- uid: EchoHub.Client.UI.Chat.ChatMessageManager.LoadHistory(System.String,System.Collections.Generic.List{EchoHub.Core.DTOs.MessageDto}) - name: LoadHistory(string, List) - href: api/client/EchoHub.Client.UI.Chat.ChatMessageManager.html#EchoHub_Client_UI_Chat_ChatMessageManager_LoadHistory_System_String_System_Collections_Generic_List_EchoHub_Core_DTOs_MessageDto__ - commentId: M:EchoHub.Client.UI.Chat.ChatMessageManager.LoadHistory(System.String,System.Collections.Generic.List{EchoHub.Core.DTOs.MessageDto}) - name.vb: LoadHistory(String, List(Of MessageDto)) - fullName: EchoHub.Client.UI.Chat.ChatMessageManager.LoadHistory(string, System.Collections.Generic.List) - fullName.vb: EchoHub.Client.UI.Chat.ChatMessageManager.LoadHistory(String, System.Collections.Generic.List(Of EchoHub.Core.DTOs.MessageDto)) - nameWithType: ChatMessageManager.LoadHistory(string, List) - nameWithType.vb: ChatMessageManager.LoadHistory(String, List(Of MessageDto)) +- uid: EchoHub.Client.UI.Chat.ChatMessageManager.LastReadIds + name: LastReadIds + href: api/client/EchoHub.Client.UI.Chat.ChatMessageManager.html#EchoHub_Client_UI_Chat_ChatMessageManager_LastReadIds + commentId: P:EchoHub.Client.UI.Chat.ChatMessageManager.LastReadIds + fullName: EchoHub.Client.UI.Chat.ChatMessageManager.LastReadIds + nameWithType: ChatMessageManager.LastReadIds +- uid: EchoHub.Client.UI.Chat.ChatMessageManager.LastReadIds* + name: LastReadIds + href: api/client/EchoHub.Client.UI.Chat.ChatMessageManager.html#EchoHub_Client_UI_Chat_ChatMessageManager_LastReadIds_ + commentId: Overload:EchoHub.Client.UI.Chat.ChatMessageManager.LastReadIds + fullName: EchoHub.Client.UI.Chat.ChatMessageManager.LastReadIds + nameWithType: ChatMessageManager.LastReadIds +- uid: EchoHub.Client.UI.Chat.ChatMessageManager.LoadHistory(System.String,System.Collections.Generic.List{EchoHub.Core.DTOs.MessageDto},System.Nullable{System.Guid}) + name: LoadHistory(string, List, Guid?) + href: api/client/EchoHub.Client.UI.Chat.ChatMessageManager.html#EchoHub_Client_UI_Chat_ChatMessageManager_LoadHistory_System_String_System_Collections_Generic_List_EchoHub_Core_DTOs_MessageDto__System_Nullable_System_Guid__ + commentId: M:EchoHub.Client.UI.Chat.ChatMessageManager.LoadHistory(System.String,System.Collections.Generic.List{EchoHub.Core.DTOs.MessageDto},System.Nullable{System.Guid}) + name.vb: LoadHistory(String, List(Of MessageDto), Guid?) + fullName: EchoHub.Client.UI.Chat.ChatMessageManager.LoadHistory(string, System.Collections.Generic.List, System.Guid?) + fullName.vb: EchoHub.Client.UI.Chat.ChatMessageManager.LoadHistory(String, System.Collections.Generic.List(Of EchoHub.Core.DTOs.MessageDto), System.Guid?) + nameWithType: ChatMessageManager.LoadHistory(string, List, Guid?) + nameWithType.vb: ChatMessageManager.LoadHistory(String, List(Of MessageDto), Guid?) - uid: EchoHub.Client.UI.Chat.ChatMessageManager.LoadHistory* name: LoadHistory href: api/client/EchoHub.Client.UI.Chat.ChatMessageManager.html#EchoHub_Client_UI_Chat_ChatMessageManager_LoadHistory_ commentId: Overload:EchoHub.Client.UI.Chat.ChatMessageManager.LoadHistory fullName: EchoHub.Client.UI.Chat.ChatMessageManager.LoadHistory nameWithType: ChatMessageManager.LoadHistory +- uid: EchoHub.Client.UI.Chat.ChatMessageManager.MentionChannels + name: MentionChannels + href: api/client/EchoHub.Client.UI.Chat.ChatMessageManager.html#EchoHub_Client_UI_Chat_ChatMessageManager_MentionChannels + commentId: P:EchoHub.Client.UI.Chat.ChatMessageManager.MentionChannels + fullName: EchoHub.Client.UI.Chat.ChatMessageManager.MentionChannels + nameWithType: ChatMessageManager.MentionChannels +- uid: EchoHub.Client.UI.Chat.ChatMessageManager.MentionChannels* + name: MentionChannels + href: api/client/EchoHub.Client.UI.Chat.ChatMessageManager.html#EchoHub_Client_UI_Chat_ChatMessageManager_MentionChannels_ + commentId: Overload:EchoHub.Client.UI.Chat.ChatMessageManager.MentionChannels + fullName: EchoHub.Client.UI.Chat.ChatMessageManager.MentionChannels + nameWithType: ChatMessageManager.MentionChannels - uid: EchoHub.Client.UI.Chat.ChatMessageManager.MessagesChanged name: MessagesChanged href: api/client/EchoHub.Client.UI.Chat.ChatMessageManager.html#EchoHub_Client_UI_Chat_ChatMessageManager_MessagesChanged commentId: E:EchoHub.Client.UI.Chat.ChatMessageManager.MessagesChanged fullName: EchoHub.Client.UI.Chat.ChatMessageManager.MessagesChanged nameWithType: ChatMessageManager.MessagesChanged +- uid: EchoHub.Client.UI.Chat.ChatMessageManager.NickColWidth + name: NickColWidth + href: api/client/EchoHub.Client.UI.Chat.ChatMessageManager.html#EchoHub_Client_UI_Chat_ChatMessageManager_NickColWidth + commentId: F:EchoHub.Client.UI.Chat.ChatMessageManager.NickColWidth + fullName: EchoHub.Client.UI.Chat.ChatMessageManager.NickColWidth + nameWithType: ChatMessageManager.NickColWidth - uid: EchoHub.Client.UI.Chat.ChatMessageManager.PrependHistory(System.String,System.Collections.Generic.List{EchoHub.Core.DTOs.MessageDto}) name: PrependHistory(string, List) href: api/client/EchoHub.Client.UI.Chat.ChatMessageManager.html#EchoHub_Client_UI_Chat_ChatMessageManager_PrependHistory_System_String_System_Collections_Generic_List_EchoHub_Core_DTOs_MessageDto__ @@ -5557,69 +5740,6 @@ references: commentId: Overload:EchoHub.Client.UI.Dialogs.UpdateConfirmDialog.Show fullName: EchoHub.Client.UI.Dialogs.UpdateConfirmDialog.Show nameWithType: UpdateConfirmDialog.Show -- uid: EchoHub.Client.UI.Dialogs.UpdateProgressDialog - name: UpdateProgressDialog - href: api/client/EchoHub.Client.UI.Dialogs.UpdateProgressDialog.html - commentId: T:EchoHub.Client.UI.Dialogs.UpdateProgressDialog - fullName: EchoHub.Client.UI.Dialogs.UpdateProgressDialog - nameWithType: UpdateProgressDialog -- uid: EchoHub.Client.UI.Dialogs.UpdateProgressDialog.#ctor(Terminal.Gui.App.IApplication,System.String) - name: UpdateProgressDialog(IApplication, string) - href: api/client/EchoHub.Client.UI.Dialogs.UpdateProgressDialog.html#EchoHub_Client_UI_Dialogs_UpdateProgressDialog__ctor_Terminal_Gui_App_IApplication_System_String_ - commentId: M:EchoHub.Client.UI.Dialogs.UpdateProgressDialog.#ctor(Terminal.Gui.App.IApplication,System.String) - name.vb: New(IApplication, String) - fullName: EchoHub.Client.UI.Dialogs.UpdateProgressDialog.UpdateProgressDialog(Terminal.Gui.App.IApplication, string) - fullName.vb: EchoHub.Client.UI.Dialogs.UpdateProgressDialog.New(Terminal.Gui.App.IApplication, String) - nameWithType: UpdateProgressDialog.UpdateProgressDialog(IApplication, string) - nameWithType.vb: UpdateProgressDialog.New(IApplication, String) -- uid: EchoHub.Client.UI.Dialogs.UpdateProgressDialog.#ctor* - name: UpdateProgressDialog - href: api/client/EchoHub.Client.UI.Dialogs.UpdateProgressDialog.html#EchoHub_Client_UI_Dialogs_UpdateProgressDialog__ctor_ - commentId: Overload:EchoHub.Client.UI.Dialogs.UpdateProgressDialog.#ctor - name.vb: New - fullName: EchoHub.Client.UI.Dialogs.UpdateProgressDialog.UpdateProgressDialog - fullName.vb: EchoHub.Client.UI.Dialogs.UpdateProgressDialog.New - nameWithType: UpdateProgressDialog.UpdateProgressDialog - nameWithType.vb: UpdateProgressDialog.New -- uid: EchoHub.Client.UI.Dialogs.UpdateProgressDialog.Close - name: Close() - href: api/client/EchoHub.Client.UI.Dialogs.UpdateProgressDialog.html#EchoHub_Client_UI_Dialogs_UpdateProgressDialog_Close - commentId: M:EchoHub.Client.UI.Dialogs.UpdateProgressDialog.Close - fullName: EchoHub.Client.UI.Dialogs.UpdateProgressDialog.Close() - nameWithType: UpdateProgressDialog.Close() -- uid: EchoHub.Client.UI.Dialogs.UpdateProgressDialog.Close* - name: Close - href: api/client/EchoHub.Client.UI.Dialogs.UpdateProgressDialog.html#EchoHub_Client_UI_Dialogs_UpdateProgressDialog_Close_ - commentId: Overload:EchoHub.Client.UI.Dialogs.UpdateProgressDialog.Close - fullName: EchoHub.Client.UI.Dialogs.UpdateProgressDialog.Close - nameWithType: UpdateProgressDialog.Close -- uid: EchoHub.Client.UI.Dialogs.UpdateProgressDialog.Show - name: Show() - href: api/client/EchoHub.Client.UI.Dialogs.UpdateProgressDialog.html#EchoHub_Client_UI_Dialogs_UpdateProgressDialog_Show - commentId: M:EchoHub.Client.UI.Dialogs.UpdateProgressDialog.Show - fullName: EchoHub.Client.UI.Dialogs.UpdateProgressDialog.Show() - nameWithType: UpdateProgressDialog.Show() -- uid: EchoHub.Client.UI.Dialogs.UpdateProgressDialog.Show* - name: Show - href: api/client/EchoHub.Client.UI.Dialogs.UpdateProgressDialog.html#EchoHub_Client_UI_Dialogs_UpdateProgressDialog_Show_ - commentId: Overload:EchoHub.Client.UI.Dialogs.UpdateProgressDialog.Show - fullName: EchoHub.Client.UI.Dialogs.UpdateProgressDialog.Show - nameWithType: UpdateProgressDialog.Show -- uid: EchoHub.Client.UI.Dialogs.UpdateProgressDialog.UpdateProgress(System.Single,System.String) - name: UpdateProgress(float, string) - href: api/client/EchoHub.Client.UI.Dialogs.UpdateProgressDialog.html#EchoHub_Client_UI_Dialogs_UpdateProgressDialog_UpdateProgress_System_Single_System_String_ - commentId: M:EchoHub.Client.UI.Dialogs.UpdateProgressDialog.UpdateProgress(System.Single,System.String) - name.vb: UpdateProgress(Single, String) - fullName: EchoHub.Client.UI.Dialogs.UpdateProgressDialog.UpdateProgress(float, string) - fullName.vb: EchoHub.Client.UI.Dialogs.UpdateProgressDialog.UpdateProgress(Single, String) - nameWithType: UpdateProgressDialog.UpdateProgress(float, string) - nameWithType.vb: UpdateProgressDialog.UpdateProgress(Single, String) -- uid: EchoHub.Client.UI.Dialogs.UpdateProgressDialog.UpdateProgress* - name: UpdateProgress - href: api/client/EchoHub.Client.UI.Dialogs.UpdateProgressDialog.html#EchoHub_Client_UI_Dialogs_UpdateProgressDialog_UpdateProgress_ - commentId: Overload:EchoHub.Client.UI.Dialogs.UpdateProgressDialog.UpdateProgress - fullName: EchoHub.Client.UI.Dialogs.UpdateProgressDialog.UpdateProgress - nameWithType: UpdateProgressDialog.UpdateProgress - uid: EchoHub.Client.UI.Helpers name: EchoHub.Client.UI.Helpers href: api/client/EchoHub.Client.UI.Helpers.html @@ -5719,6 +5839,42 @@ references: commentId: Overload:EchoHub.Client.UI.Helpers.HexColorHelper.ParseHexToColor fullName: EchoHub.Client.UI.Helpers.HexColorHelper.ParseHexToColor nameWithType: HexColorHelper.ParseHexToColor +- uid: EchoHub.Client.UI.Helpers.NickColorHelper + name: NickColorHelper + href: api/client/EchoHub.Client.UI.Helpers.NickColorHelper.html + commentId: T:EchoHub.Client.UI.Helpers.NickColorHelper + fullName: EchoHub.Client.UI.Helpers.NickColorHelper + nameWithType: NickColorHelper +- uid: EchoHub.Client.UI.Helpers.NickColorHelper.GetAttribute(System.String) + name: GetAttribute(string) + href: api/client/EchoHub.Client.UI.Helpers.NickColorHelper.html#EchoHub_Client_UI_Helpers_NickColorHelper_GetAttribute_System_String_ + commentId: M:EchoHub.Client.UI.Helpers.NickColorHelper.GetAttribute(System.String) + name.vb: GetAttribute(String) + fullName: EchoHub.Client.UI.Helpers.NickColorHelper.GetAttribute(string) + fullName.vb: EchoHub.Client.UI.Helpers.NickColorHelper.GetAttribute(String) + nameWithType: NickColorHelper.GetAttribute(string) + nameWithType.vb: NickColorHelper.GetAttribute(String) +- uid: EchoHub.Client.UI.Helpers.NickColorHelper.GetAttribute* + name: GetAttribute + href: api/client/EchoHub.Client.UI.Helpers.NickColorHelper.html#EchoHub_Client_UI_Helpers_NickColorHelper_GetAttribute_ + commentId: Overload:EchoHub.Client.UI.Helpers.NickColorHelper.GetAttribute + fullName: EchoHub.Client.UI.Helpers.NickColorHelper.GetAttribute + nameWithType: NickColorHelper.GetAttribute +- uid: EchoHub.Client.UI.Helpers.NickColorHelper.GetPaletteIndex(System.String,System.Int32) + name: GetPaletteIndex(string, int) + href: api/client/EchoHub.Client.UI.Helpers.NickColorHelper.html#EchoHub_Client_UI_Helpers_NickColorHelper_GetPaletteIndex_System_String_System_Int32_ + commentId: M:EchoHub.Client.UI.Helpers.NickColorHelper.GetPaletteIndex(System.String,System.Int32) + name.vb: GetPaletteIndex(String, Integer) + fullName: EchoHub.Client.UI.Helpers.NickColorHelper.GetPaletteIndex(string, int) + fullName.vb: EchoHub.Client.UI.Helpers.NickColorHelper.GetPaletteIndex(String, Integer) + nameWithType: NickColorHelper.GetPaletteIndex(string, int) + nameWithType.vb: NickColorHelper.GetPaletteIndex(String, Integer) +- uid: EchoHub.Client.UI.Helpers.NickColorHelper.GetPaletteIndex* + name: GetPaletteIndex + href: api/client/EchoHub.Client.UI.Helpers.NickColorHelper.html#EchoHub_Client_UI_Helpers_NickColorHelper_GetPaletteIndex_ + commentId: Overload:EchoHub.Client.UI.Helpers.NickColorHelper.GetPaletteIndex + fullName: EchoHub.Client.UI.Helpers.NickColorHelper.GetPaletteIndex + nameWithType: NickColorHelper.GetPaletteIndex - uid: EchoHub.Client.UI.ListSources name: EchoHub.Client.UI.ListSources href: api/client/EchoHub.Client.UI.ListSources.html @@ -5860,15 +6016,15 @@ references: commentId: Overload:EchoHub.Client.UI.ListSources.ChannelListSource.ToList fullName: EchoHub.Client.UI.ListSources.ChannelListSource.ToList nameWithType: ChannelListSource.ToList -- uid: EchoHub.Client.UI.ListSources.ChannelListSource.Update(System.Collections.Generic.List{System.String},System.Collections.Generic.Dictionary{System.String,System.Int32},System.String,System.Collections.Generic.IReadOnlySet{System.String}) - name: Update(List, Dictionary, string, IReadOnlySet?) - href: api/client/EchoHub.Client.UI.ListSources.ChannelListSource.html#EchoHub_Client_UI_ListSources_ChannelListSource_Update_System_Collections_Generic_List_System_String__System_Collections_Generic_Dictionary_System_String_System_Int32__System_String_System_Collections_Generic_IReadOnlySet_System_String__ - commentId: M:EchoHub.Client.UI.ListSources.ChannelListSource.Update(System.Collections.Generic.List{System.String},System.Collections.Generic.Dictionary{System.String,System.Int32},System.String,System.Collections.Generic.IReadOnlySet{System.String}) - name.vb: Update(List(Of String), Dictionary(Of String, Integer), String, IReadOnlySet(Of String)) - fullName: EchoHub.Client.UI.ListSources.ChannelListSource.Update(System.Collections.Generic.List, System.Collections.Generic.Dictionary, string, System.Collections.Generic.IReadOnlySet?) - fullName.vb: EchoHub.Client.UI.ListSources.ChannelListSource.Update(System.Collections.Generic.List(Of String), System.Collections.Generic.Dictionary(Of String, Integer), String, System.Collections.Generic.IReadOnlySet(Of String)) - nameWithType: ChannelListSource.Update(List, Dictionary, string, IReadOnlySet?) - nameWithType.vb: ChannelListSource.Update(List(Of String), Dictionary(Of String, Integer), String, IReadOnlySet(Of String)) +- uid: EchoHub.Client.UI.ListSources.ChannelListSource.Update(System.Collections.Generic.List{System.String},System.Collections.Generic.Dictionary{System.String,System.Int32},System.String,System.Collections.Generic.IReadOnlySet{System.String},System.Collections.Generic.IReadOnlySet{System.String}) + name: Update(List, Dictionary, string, IReadOnlySet?, IReadOnlySet?) + href: api/client/EchoHub.Client.UI.ListSources.ChannelListSource.html#EchoHub_Client_UI_ListSources_ChannelListSource_Update_System_Collections_Generic_List_System_String__System_Collections_Generic_Dictionary_System_String_System_Int32__System_String_System_Collections_Generic_IReadOnlySet_System_String__System_Collections_Generic_IReadOnlySet_System_String__ + commentId: M:EchoHub.Client.UI.ListSources.ChannelListSource.Update(System.Collections.Generic.List{System.String},System.Collections.Generic.Dictionary{System.String,System.Int32},System.String,System.Collections.Generic.IReadOnlySet{System.String},System.Collections.Generic.IReadOnlySet{System.String}) + name.vb: Update(List(Of String), Dictionary(Of String, Integer), String, IReadOnlySet(Of String), IReadOnlySet(Of String)) + fullName: EchoHub.Client.UI.ListSources.ChannelListSource.Update(System.Collections.Generic.List, System.Collections.Generic.Dictionary, string, System.Collections.Generic.IReadOnlySet?, System.Collections.Generic.IReadOnlySet?) + fullName.vb: EchoHub.Client.UI.ListSources.ChannelListSource.Update(System.Collections.Generic.List(Of String), System.Collections.Generic.Dictionary(Of String, Integer), String, System.Collections.Generic.IReadOnlySet(Of String), System.Collections.Generic.IReadOnlySet(Of String)) + nameWithType: ChannelListSource.Update(List, Dictionary, string, IReadOnlySet?, IReadOnlySet?) + nameWithType.vb: ChannelListSource.Update(List(Of String), Dictionary(Of String, Integer), String, IReadOnlySet(Of String), IReadOnlySet(Of String)) - uid: EchoHub.Client.UI.ListSources.ChannelListSource.Update* name: Update href: api/client/EchoHub.Client.UI.ListSources.ChannelListSource.html#EchoHub_Client_UI_ListSources_ChannelListSource_Update_ @@ -7141,6 +7297,21 @@ references: commentId: Overload:EchoHub.Core.Contracts.IChannelService.GetChannelListAsync fullName: EchoHub.Core.Contracts.IChannelService.GetChannelListAsync nameWithType: IChannelService.GetChannelListAsync +- uid: EchoHub.Core.Contracts.IChannelService.GetChannelMetaAsync(System.String) + name: GetChannelMetaAsync(string) + href: api/core/EchoHub.Core.Contracts.IChannelService.html#EchoHub_Core_Contracts_IChannelService_GetChannelMetaAsync_System_String_ + commentId: M:EchoHub.Core.Contracts.IChannelService.GetChannelMetaAsync(System.String) + name.vb: GetChannelMetaAsync(String) + fullName: EchoHub.Core.Contracts.IChannelService.GetChannelMetaAsync(string) + fullName.vb: EchoHub.Core.Contracts.IChannelService.GetChannelMetaAsync(String) + nameWithType: IChannelService.GetChannelMetaAsync(string) + nameWithType.vb: IChannelService.GetChannelMetaAsync(String) +- uid: EchoHub.Core.Contracts.IChannelService.GetChannelMetaAsync* + name: GetChannelMetaAsync + href: api/core/EchoHub.Core.Contracts.IChannelService.html#EchoHub_Core_Contracts_IChannelService_GetChannelMetaAsync_ + commentId: Overload:EchoHub.Core.Contracts.IChannelService.GetChannelMetaAsync + fullName: EchoHub.Core.Contracts.IChannelService.GetChannelMetaAsync + nameWithType: IChannelService.GetChannelMetaAsync - uid: EchoHub.Core.Contracts.IChannelService.GetChannelTopicAsync(System.String) name: GetChannelTopicAsync(string) href: api/core/EchoHub.Core.Contracts.IChannelService.html#EchoHub_Core_Contracts_IChannelService_GetChannelTopicAsync_System_String_ @@ -7726,6 +7897,12 @@ references: commentId: T:EchoHub.Core.Contracts.IMessageEncryptionService fullName: EchoHub.Core.Contracts.IMessageEncryptionService nameWithType: IMessageEncryptionService +- uid: EchoHub.Core.Contracts.IMessageEncryptionService.CiphertextPrefix + name: CiphertextPrefix + href: api/core/EchoHub.Core.Contracts.IMessageEncryptionService.html#EchoHub_Core_Contracts_IMessageEncryptionService_CiphertextPrefix + commentId: F:EchoHub.Core.Contracts.IMessageEncryptionService.CiphertextPrefix + fullName: EchoHub.Core.Contracts.IMessageEncryptionService.CiphertextPrefix + nameWithType: IMessageEncryptionService.CiphertextPrefix - uid: EchoHub.Core.Contracts.IMessageEncryptionService.Decrypt(System.String) name: Decrypt(string) href: api/core/EchoHub.Core.Contracts.IMessageEncryptionService.html#EchoHub_Core_Contracts_IMessageEncryptionService_Decrypt_System_String_ @@ -9532,6 +9709,270 @@ references: commentId: F:EchoHub.Core.DTOs.ChannelError.ValidationFailed fullName: EchoHub.Core.DTOs.ChannelError.ValidationFailed nameWithType: ChannelError.ValidationFailed +- uid: EchoHub.Core.DTOs.ChannelMetaDto + name: ChannelMetaDto + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html + commentId: T:EchoHub.Core.DTOs.ChannelMetaDto + fullName: EchoHub.Core.DTOs.ChannelMetaDto + nameWithType: ChannelMetaDto +- uid: EchoHub.Core.DTOs.ChannelMetaDto.#ctor(EchoHub.Core.DTOs.ChannelMetaDto) + name: ChannelMetaDto(ChannelMetaDto) + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto__ctor_EchoHub_Core_DTOs_ChannelMetaDto_ + commentId: M:EchoHub.Core.DTOs.ChannelMetaDto.#ctor(EchoHub.Core.DTOs.ChannelMetaDto) + name.vb: New(ChannelMetaDto) + fullName: EchoHub.Core.DTOs.ChannelMetaDto.ChannelMetaDto(EchoHub.Core.DTOs.ChannelMetaDto) + fullName.vb: EchoHub.Core.DTOs.ChannelMetaDto.New(EchoHub.Core.DTOs.ChannelMetaDto) + nameWithType: ChannelMetaDto.ChannelMetaDto(ChannelMetaDto) + nameWithType.vb: ChannelMetaDto.New(ChannelMetaDto) +- uid: EchoHub.Core.DTOs.ChannelMetaDto.#ctor(System.Guid,System.String,System.String,System.Boolean,System.Boolean,System.Int32,System.Int32,System.Int64,System.DateTimeOffset) + name: ChannelMetaDto(Guid, string, string?, bool, bool, int, int, long, DateTimeOffset) + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto__ctor_System_Guid_System_String_System_String_System_Boolean_System_Boolean_System_Int32_System_Int32_System_Int64_System_DateTimeOffset_ + commentId: M:EchoHub.Core.DTOs.ChannelMetaDto.#ctor(System.Guid,System.String,System.String,System.Boolean,System.Boolean,System.Int32,System.Int32,System.Int64,System.DateTimeOffset) + name.vb: New(Guid, String, String, Boolean, Boolean, Integer, Integer, Long, DateTimeOffset) + fullName: EchoHub.Core.DTOs.ChannelMetaDto.ChannelMetaDto(System.Guid, string, string?, bool, bool, int, int, long, System.DateTimeOffset) + fullName.vb: EchoHub.Core.DTOs.ChannelMetaDto.New(System.Guid, String, String, Boolean, Boolean, Integer, Integer, Long, System.DateTimeOffset) + nameWithType: ChannelMetaDto.ChannelMetaDto(Guid, string, string?, bool, bool, int, int, long, DateTimeOffset) + nameWithType.vb: ChannelMetaDto.New(Guid, String, String, Boolean, Boolean, Integer, Integer, Long, DateTimeOffset) +- uid: EchoHub.Core.DTOs.ChannelMetaDto.#ctor* + name: ChannelMetaDto + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto__ctor_ + commentId: Overload:EchoHub.Core.DTOs.ChannelMetaDto.#ctor + name.vb: New + fullName: EchoHub.Core.DTOs.ChannelMetaDto.ChannelMetaDto + fullName.vb: EchoHub.Core.DTOs.ChannelMetaDto.New + nameWithType: ChannelMetaDto.ChannelMetaDto + nameWithType.vb: ChannelMetaDto.New +- uid: EchoHub.Core.DTOs.ChannelMetaDto.CreatedAt + name: CreatedAt + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_CreatedAt + commentId: P:EchoHub.Core.DTOs.ChannelMetaDto.CreatedAt + fullName: EchoHub.Core.DTOs.ChannelMetaDto.CreatedAt + nameWithType: ChannelMetaDto.CreatedAt +- uid: EchoHub.Core.DTOs.ChannelMetaDto.CreatedAt* + name: CreatedAt + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_CreatedAt_ + commentId: Overload:EchoHub.Core.DTOs.ChannelMetaDto.CreatedAt + fullName: EchoHub.Core.DTOs.ChannelMetaDto.CreatedAt + nameWithType: ChannelMetaDto.CreatedAt +- uid: EchoHub.Core.DTOs.ChannelMetaDto.Deconstruct(System.Guid@,System.String@,System.String@,System.Boolean@,System.Boolean@,System.Int32@,System.Int32@,System.Int64@,System.DateTimeOffset@) + name: Deconstruct(out Guid, out string, out string?, out bool, out bool, out int, out int, out long, out DateTimeOffset) + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_Deconstruct_System_Guid__System_String__System_String__System_Boolean__System_Boolean__System_Int32__System_Int32__System_Int64__System_DateTimeOffset__ + commentId: M:EchoHub.Core.DTOs.ChannelMetaDto.Deconstruct(System.Guid@,System.String@,System.String@,System.Boolean@,System.Boolean@,System.Int32@,System.Int32@,System.Int64@,System.DateTimeOffset@) + name.vb: Deconstruct(Guid, String, String, Boolean, Boolean, Integer, Integer, Long, DateTimeOffset) + fullName: EchoHub.Core.DTOs.ChannelMetaDto.Deconstruct(out System.Guid, out string, out string?, out bool, out bool, out int, out int, out long, out System.DateTimeOffset) + fullName.vb: EchoHub.Core.DTOs.ChannelMetaDto.Deconstruct(System.Guid, String, String, Boolean, Boolean, Integer, Integer, Long, System.DateTimeOffset) + nameWithType: ChannelMetaDto.Deconstruct(out Guid, out string, out string?, out bool, out bool, out int, out int, out long, out DateTimeOffset) + nameWithType.vb: ChannelMetaDto.Deconstruct(Guid, String, String, Boolean, Boolean, Integer, Integer, Long, DateTimeOffset) +- uid: EchoHub.Core.DTOs.ChannelMetaDto.Deconstruct* + name: Deconstruct + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_Deconstruct_ + commentId: Overload:EchoHub.Core.DTOs.ChannelMetaDto.Deconstruct + fullName: EchoHub.Core.DTOs.ChannelMetaDto.Deconstruct + nameWithType: ChannelMetaDto.Deconstruct +- uid: EchoHub.Core.DTOs.ChannelMetaDto.EqualityContract + name: EqualityContract + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_EqualityContract + commentId: P:EchoHub.Core.DTOs.ChannelMetaDto.EqualityContract + fullName: EchoHub.Core.DTOs.ChannelMetaDto.EqualityContract + nameWithType: ChannelMetaDto.EqualityContract +- uid: EchoHub.Core.DTOs.ChannelMetaDto.EqualityContract* + name: EqualityContract + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_EqualityContract_ + commentId: Overload:EchoHub.Core.DTOs.ChannelMetaDto.EqualityContract + fullName: EchoHub.Core.DTOs.ChannelMetaDto.EqualityContract + nameWithType: ChannelMetaDto.EqualityContract +- uid: EchoHub.Core.DTOs.ChannelMetaDto.Equals(EchoHub.Core.DTOs.ChannelMetaDto) + name: Equals(ChannelMetaDto?) + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_Equals_EchoHub_Core_DTOs_ChannelMetaDto_ + commentId: M:EchoHub.Core.DTOs.ChannelMetaDto.Equals(EchoHub.Core.DTOs.ChannelMetaDto) + name.vb: Equals(ChannelMetaDto) + fullName: EchoHub.Core.DTOs.ChannelMetaDto.Equals(EchoHub.Core.DTOs.ChannelMetaDto?) + fullName.vb: EchoHub.Core.DTOs.ChannelMetaDto.Equals(EchoHub.Core.DTOs.ChannelMetaDto) + nameWithType: ChannelMetaDto.Equals(ChannelMetaDto?) + nameWithType.vb: ChannelMetaDto.Equals(ChannelMetaDto) +- uid: EchoHub.Core.DTOs.ChannelMetaDto.Equals(System.Object) + name: Equals(object?) + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_Equals_System_Object_ + commentId: M:EchoHub.Core.DTOs.ChannelMetaDto.Equals(System.Object) + name.vb: Equals(Object) + fullName: EchoHub.Core.DTOs.ChannelMetaDto.Equals(object?) + fullName.vb: EchoHub.Core.DTOs.ChannelMetaDto.Equals(Object) + nameWithType: ChannelMetaDto.Equals(object?) + nameWithType.vb: ChannelMetaDto.Equals(Object) +- uid: EchoHub.Core.DTOs.ChannelMetaDto.Equals* + name: Equals + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_Equals_ + commentId: Overload:EchoHub.Core.DTOs.ChannelMetaDto.Equals + fullName: EchoHub.Core.DTOs.ChannelMetaDto.Equals + nameWithType: ChannelMetaDto.Equals +- uid: EchoHub.Core.DTOs.ChannelMetaDto.EstimatedSizeBytes + name: EstimatedSizeBytes + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_EstimatedSizeBytes + commentId: P:EchoHub.Core.DTOs.ChannelMetaDto.EstimatedSizeBytes + fullName: EchoHub.Core.DTOs.ChannelMetaDto.EstimatedSizeBytes + nameWithType: ChannelMetaDto.EstimatedSizeBytes +- uid: EchoHub.Core.DTOs.ChannelMetaDto.EstimatedSizeBytes* + name: EstimatedSizeBytes + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_EstimatedSizeBytes_ + commentId: Overload:EchoHub.Core.DTOs.ChannelMetaDto.EstimatedSizeBytes + fullName: EchoHub.Core.DTOs.ChannelMetaDto.EstimatedSizeBytes + nameWithType: ChannelMetaDto.EstimatedSizeBytes +- uid: EchoHub.Core.DTOs.ChannelMetaDto.GetHashCode + name: GetHashCode() + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_GetHashCode + commentId: M:EchoHub.Core.DTOs.ChannelMetaDto.GetHashCode + fullName: EchoHub.Core.DTOs.ChannelMetaDto.GetHashCode() + nameWithType: ChannelMetaDto.GetHashCode() +- uid: EchoHub.Core.DTOs.ChannelMetaDto.GetHashCode* + name: GetHashCode + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_GetHashCode_ + commentId: Overload:EchoHub.Core.DTOs.ChannelMetaDto.GetHashCode + fullName: EchoHub.Core.DTOs.ChannelMetaDto.GetHashCode + nameWithType: ChannelMetaDto.GetHashCode +- uid: EchoHub.Core.DTOs.ChannelMetaDto.Id + name: Id + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_Id + commentId: P:EchoHub.Core.DTOs.ChannelMetaDto.Id + fullName: EchoHub.Core.DTOs.ChannelMetaDto.Id + nameWithType: ChannelMetaDto.Id +- uid: EchoHub.Core.DTOs.ChannelMetaDto.Id* + name: Id + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_Id_ + commentId: Overload:EchoHub.Core.DTOs.ChannelMetaDto.Id + fullName: EchoHub.Core.DTOs.ChannelMetaDto.Id + nameWithType: ChannelMetaDto.Id +- uid: EchoHub.Core.DTOs.ChannelMetaDto.IsEncrypted + name: IsEncrypted + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_IsEncrypted + commentId: P:EchoHub.Core.DTOs.ChannelMetaDto.IsEncrypted + fullName: EchoHub.Core.DTOs.ChannelMetaDto.IsEncrypted + nameWithType: ChannelMetaDto.IsEncrypted +- uid: EchoHub.Core.DTOs.ChannelMetaDto.IsEncrypted* + name: IsEncrypted + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_IsEncrypted_ + commentId: Overload:EchoHub.Core.DTOs.ChannelMetaDto.IsEncrypted + fullName: EchoHub.Core.DTOs.ChannelMetaDto.IsEncrypted + nameWithType: ChannelMetaDto.IsEncrypted +- uid: EchoHub.Core.DTOs.ChannelMetaDto.IsProtected + name: IsProtected + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_IsProtected + commentId: P:EchoHub.Core.DTOs.ChannelMetaDto.IsProtected + fullName: EchoHub.Core.DTOs.ChannelMetaDto.IsProtected + nameWithType: ChannelMetaDto.IsProtected +- uid: EchoHub.Core.DTOs.ChannelMetaDto.IsProtected* + name: IsProtected + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_IsProtected_ + commentId: Overload:EchoHub.Core.DTOs.ChannelMetaDto.IsProtected + fullName: EchoHub.Core.DTOs.ChannelMetaDto.IsProtected + nameWithType: ChannelMetaDto.IsProtected +- uid: EchoHub.Core.DTOs.ChannelMetaDto.MessageCount + name: MessageCount + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_MessageCount + commentId: P:EchoHub.Core.DTOs.ChannelMetaDto.MessageCount + fullName: EchoHub.Core.DTOs.ChannelMetaDto.MessageCount + nameWithType: ChannelMetaDto.MessageCount +- uid: EchoHub.Core.DTOs.ChannelMetaDto.MessageCount* + name: MessageCount + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_MessageCount_ + commentId: Overload:EchoHub.Core.DTOs.ChannelMetaDto.MessageCount + fullName: EchoHub.Core.DTOs.ChannelMetaDto.MessageCount + nameWithType: ChannelMetaDto.MessageCount +- uid: EchoHub.Core.DTOs.ChannelMetaDto.Name + name: Name + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_Name + commentId: P:EchoHub.Core.DTOs.ChannelMetaDto.Name + fullName: EchoHub.Core.DTOs.ChannelMetaDto.Name + nameWithType: ChannelMetaDto.Name +- uid: EchoHub.Core.DTOs.ChannelMetaDto.Name* + name: Name + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_Name_ + commentId: Overload:EchoHub.Core.DTOs.ChannelMetaDto.Name + fullName: EchoHub.Core.DTOs.ChannelMetaDto.Name + nameWithType: ChannelMetaDto.Name +- uid: EchoHub.Core.DTOs.ChannelMetaDto.PrintMembers(System.Text.StringBuilder) + name: PrintMembers(StringBuilder) + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_PrintMembers_System_Text_StringBuilder_ + commentId: M:EchoHub.Core.DTOs.ChannelMetaDto.PrintMembers(System.Text.StringBuilder) + fullName: EchoHub.Core.DTOs.ChannelMetaDto.PrintMembers(System.Text.StringBuilder) + nameWithType: ChannelMetaDto.PrintMembers(StringBuilder) +- uid: EchoHub.Core.DTOs.ChannelMetaDto.PrintMembers* + name: PrintMembers + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_PrintMembers_ + commentId: Overload:EchoHub.Core.DTOs.ChannelMetaDto.PrintMembers + fullName: EchoHub.Core.DTOs.ChannelMetaDto.PrintMembers + nameWithType: ChannelMetaDto.PrintMembers +- uid: EchoHub.Core.DTOs.ChannelMetaDto.ToString + name: ToString() + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_ToString + commentId: M:EchoHub.Core.DTOs.ChannelMetaDto.ToString + fullName: EchoHub.Core.DTOs.ChannelMetaDto.ToString() + nameWithType: ChannelMetaDto.ToString() +- uid: EchoHub.Core.DTOs.ChannelMetaDto.ToString* + name: ToString + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_ToString_ + commentId: Overload:EchoHub.Core.DTOs.ChannelMetaDto.ToString + fullName: EchoHub.Core.DTOs.ChannelMetaDto.ToString + nameWithType: ChannelMetaDto.ToString +- uid: EchoHub.Core.DTOs.ChannelMetaDto.Topic + name: Topic + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_Topic + commentId: P:EchoHub.Core.DTOs.ChannelMetaDto.Topic + fullName: EchoHub.Core.DTOs.ChannelMetaDto.Topic + nameWithType: ChannelMetaDto.Topic +- uid: EchoHub.Core.DTOs.ChannelMetaDto.Topic* + name: Topic + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_Topic_ + commentId: Overload:EchoHub.Core.DTOs.ChannelMetaDto.Topic + fullName: EchoHub.Core.DTOs.ChannelMetaDto.Topic + nameWithType: ChannelMetaDto.Topic +- uid: EchoHub.Core.DTOs.ChannelMetaDto.UniqueUserCount + name: UniqueUserCount + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_UniqueUserCount + commentId: P:EchoHub.Core.DTOs.ChannelMetaDto.UniqueUserCount + fullName: EchoHub.Core.DTOs.ChannelMetaDto.UniqueUserCount + nameWithType: ChannelMetaDto.UniqueUserCount +- uid: EchoHub.Core.DTOs.ChannelMetaDto.UniqueUserCount* + name: UniqueUserCount + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_UniqueUserCount_ + commentId: Overload:EchoHub.Core.DTOs.ChannelMetaDto.UniqueUserCount + fullName: EchoHub.Core.DTOs.ChannelMetaDto.UniqueUserCount + nameWithType: ChannelMetaDto.UniqueUserCount +- uid: EchoHub.Core.DTOs.ChannelMetaDto.op_Equality(EchoHub.Core.DTOs.ChannelMetaDto,EchoHub.Core.DTOs.ChannelMetaDto) + name: operator ==(ChannelMetaDto?, ChannelMetaDto?) + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_op_Equality_EchoHub_Core_DTOs_ChannelMetaDto_EchoHub_Core_DTOs_ChannelMetaDto_ + commentId: M:EchoHub.Core.DTOs.ChannelMetaDto.op_Equality(EchoHub.Core.DTOs.ChannelMetaDto,EchoHub.Core.DTOs.ChannelMetaDto) + name.vb: =(ChannelMetaDto, ChannelMetaDto) + fullName: EchoHub.Core.DTOs.ChannelMetaDto.operator ==(EchoHub.Core.DTOs.ChannelMetaDto?, EchoHub.Core.DTOs.ChannelMetaDto?) + fullName.vb: EchoHub.Core.DTOs.ChannelMetaDto.=(EchoHub.Core.DTOs.ChannelMetaDto, EchoHub.Core.DTOs.ChannelMetaDto) + nameWithType: ChannelMetaDto.operator ==(ChannelMetaDto?, ChannelMetaDto?) + nameWithType.vb: ChannelMetaDto.=(ChannelMetaDto, ChannelMetaDto) +- uid: EchoHub.Core.DTOs.ChannelMetaDto.op_Equality* + name: operator == + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_op_Equality_ + commentId: Overload:EchoHub.Core.DTOs.ChannelMetaDto.op_Equality + name.vb: = + fullName: EchoHub.Core.DTOs.ChannelMetaDto.operator == + fullName.vb: EchoHub.Core.DTOs.ChannelMetaDto.= + nameWithType: ChannelMetaDto.operator == + nameWithType.vb: ChannelMetaDto.= +- uid: EchoHub.Core.DTOs.ChannelMetaDto.op_Inequality(EchoHub.Core.DTOs.ChannelMetaDto,EchoHub.Core.DTOs.ChannelMetaDto) + name: operator !=(ChannelMetaDto?, ChannelMetaDto?) + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_op_Inequality_EchoHub_Core_DTOs_ChannelMetaDto_EchoHub_Core_DTOs_ChannelMetaDto_ + commentId: M:EchoHub.Core.DTOs.ChannelMetaDto.op_Inequality(EchoHub.Core.DTOs.ChannelMetaDto,EchoHub.Core.DTOs.ChannelMetaDto) + name.vb: <>(ChannelMetaDto, ChannelMetaDto) + fullName: EchoHub.Core.DTOs.ChannelMetaDto.operator !=(EchoHub.Core.DTOs.ChannelMetaDto?, EchoHub.Core.DTOs.ChannelMetaDto?) + fullName.vb: EchoHub.Core.DTOs.ChannelMetaDto.<>(EchoHub.Core.DTOs.ChannelMetaDto, EchoHub.Core.DTOs.ChannelMetaDto) + nameWithType: ChannelMetaDto.operator !=(ChannelMetaDto?, ChannelMetaDto?) + nameWithType.vb: ChannelMetaDto.<>(ChannelMetaDto, ChannelMetaDto) +- uid: EchoHub.Core.DTOs.ChannelMetaDto.op_Inequality* + name: operator != + href: api/core/EchoHub.Core.DTOs.ChannelMetaDto.html#EchoHub_Core_DTOs_ChannelMetaDto_op_Inequality_ + commentId: Overload:EchoHub.Core.DTOs.ChannelMetaDto.op_Inequality + name.vb: <> + fullName: EchoHub.Core.DTOs.ChannelMetaDto.operator != + fullName.vb: EchoHub.Core.DTOs.ChannelMetaDto.<> + nameWithType: ChannelMetaDto.operator != + nameWithType.vb: ChannelMetaDto.<> - uid: EchoHub.Core.DTOs.ChannelOperationResult name: ChannelOperationResult href: api/core/EchoHub.Core.DTOs.ChannelOperationResult.html @@ -16168,6 +16609,168 @@ references: commentId: F:EchoHub.Server.Auth.JwtTokenService.RefreshTokenLifetime fullName: EchoHub.Server.Auth.JwtTokenService.RefreshTokenLifetime nameWithType: JwtTokenService.RefreshTokenLifetime +- uid: EchoHub.Server.Config + name: EchoHub.Server.Config + href: api/server/EchoHub.Server.Config.html + commentId: N:EchoHub.Server.Config + fullName: EchoHub.Server.Config + nameWithType: EchoHub.Server.Config +- uid: EchoHub.Server.Config.UploadLimits + name: UploadLimits + href: api/server/EchoHub.Server.Config.UploadLimits.html + commentId: T:EchoHub.Server.Config.UploadLimits + fullName: EchoHub.Server.Config.UploadLimits + nameWithType: UploadLimits +- uid: EchoHub.Server.Config.UploadLimits.#ctor + name: UploadLimits() + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits__ctor + commentId: M:EchoHub.Server.Config.UploadLimits.#ctor + name.vb: New() + fullName: EchoHub.Server.Config.UploadLimits.UploadLimits() + fullName.vb: EchoHub.Server.Config.UploadLimits.New() + nameWithType: UploadLimits.UploadLimits() + nameWithType.vb: UploadLimits.New() +- uid: EchoHub.Server.Config.UploadLimits.#ctor* + name: UploadLimits + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits__ctor_ + commentId: Overload:EchoHub.Server.Config.UploadLimits.#ctor + name.vb: New + fullName: EchoHub.Server.Config.UploadLimits.UploadLimits + fullName.vb: EchoHub.Server.Config.UploadLimits.New + nameWithType: UploadLimits.UploadLimits + nameWithType.vb: UploadLimits.New +- uid: EchoHub.Server.Config.UploadLimits.MaxAttachmentsPerMessage + name: MaxAttachmentsPerMessage + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxAttachmentsPerMessage + commentId: P:EchoHub.Server.Config.UploadLimits.MaxAttachmentsPerMessage + fullName: EchoHub.Server.Config.UploadLimits.MaxAttachmentsPerMessage + nameWithType: UploadLimits.MaxAttachmentsPerMessage +- uid: EchoHub.Server.Config.UploadLimits.MaxAttachmentsPerMessage* + name: MaxAttachmentsPerMessage + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxAttachmentsPerMessage_ + commentId: Overload:EchoHub.Server.Config.UploadLimits.MaxAttachmentsPerMessage + fullName: EchoHub.Server.Config.UploadLimits.MaxAttachmentsPerMessage + nameWithType: UploadLimits.MaxAttachmentsPerMessage +- uid: EchoHub.Server.Config.UploadLimits.MaxAudioSizeBytes + name: MaxAudioSizeBytes + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxAudioSizeBytes + commentId: P:EchoHub.Server.Config.UploadLimits.MaxAudioSizeBytes + fullName: EchoHub.Server.Config.UploadLimits.MaxAudioSizeBytes + nameWithType: UploadLimits.MaxAudioSizeBytes +- uid: EchoHub.Server.Config.UploadLimits.MaxAudioSizeBytes* + name: MaxAudioSizeBytes + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxAudioSizeBytes_ + commentId: Overload:EchoHub.Server.Config.UploadLimits.MaxAudioSizeBytes + fullName: EchoHub.Server.Config.UploadLimits.MaxAudioSizeBytes + nameWithType: UploadLimits.MaxAudioSizeBytes +- uid: EchoHub.Server.Config.UploadLimits.MaxAudioSizeMB + name: MaxAudioSizeMB + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxAudioSizeMB + commentId: P:EchoHub.Server.Config.UploadLimits.MaxAudioSizeMB + fullName: EchoHub.Server.Config.UploadLimits.MaxAudioSizeMB + nameWithType: UploadLimits.MaxAudioSizeMB +- uid: EchoHub.Server.Config.UploadLimits.MaxAudioSizeMB* + name: MaxAudioSizeMB + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxAudioSizeMB_ + commentId: Overload:EchoHub.Server.Config.UploadLimits.MaxAudioSizeMB + fullName: EchoHub.Server.Config.UploadLimits.MaxAudioSizeMB + nameWithType: UploadLimits.MaxAudioSizeMB +- uid: EchoHub.Server.Config.UploadLimits.MaxAvatarSizeBytes + name: MaxAvatarSizeBytes + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxAvatarSizeBytes + commentId: P:EchoHub.Server.Config.UploadLimits.MaxAvatarSizeBytes + fullName: EchoHub.Server.Config.UploadLimits.MaxAvatarSizeBytes + nameWithType: UploadLimits.MaxAvatarSizeBytes +- uid: EchoHub.Server.Config.UploadLimits.MaxAvatarSizeBytes* + name: MaxAvatarSizeBytes + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxAvatarSizeBytes_ + commentId: Overload:EchoHub.Server.Config.UploadLimits.MaxAvatarSizeBytes + fullName: EchoHub.Server.Config.UploadLimits.MaxAvatarSizeBytes + nameWithType: UploadLimits.MaxAvatarSizeBytes +- uid: EchoHub.Server.Config.UploadLimits.MaxAvatarSizeMB + name: MaxAvatarSizeMB + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxAvatarSizeMB + commentId: P:EchoHub.Server.Config.UploadLimits.MaxAvatarSizeMB + fullName: EchoHub.Server.Config.UploadLimits.MaxAvatarSizeMB + nameWithType: UploadLimits.MaxAvatarSizeMB +- uid: EchoHub.Server.Config.UploadLimits.MaxAvatarSizeMB* + name: MaxAvatarSizeMB + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxAvatarSizeMB_ + commentId: Overload:EchoHub.Server.Config.UploadLimits.MaxAvatarSizeMB + fullName: EchoHub.Server.Config.UploadLimits.MaxAvatarSizeMB + nameWithType: UploadLimits.MaxAvatarSizeMB +- uid: EchoHub.Server.Config.UploadLimits.MaxFileSizeBytes + name: MaxFileSizeBytes + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxFileSizeBytes + commentId: P:EchoHub.Server.Config.UploadLimits.MaxFileSizeBytes + fullName: EchoHub.Server.Config.UploadLimits.MaxFileSizeBytes + nameWithType: UploadLimits.MaxFileSizeBytes +- uid: EchoHub.Server.Config.UploadLimits.MaxFileSizeBytes* + name: MaxFileSizeBytes + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxFileSizeBytes_ + commentId: Overload:EchoHub.Server.Config.UploadLimits.MaxFileSizeBytes + fullName: EchoHub.Server.Config.UploadLimits.MaxFileSizeBytes + nameWithType: UploadLimits.MaxFileSizeBytes +- uid: EchoHub.Server.Config.UploadLimits.MaxFileSizeMB + name: MaxFileSizeMB + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxFileSizeMB + commentId: P:EchoHub.Server.Config.UploadLimits.MaxFileSizeMB + fullName: EchoHub.Server.Config.UploadLimits.MaxFileSizeMB + nameWithType: UploadLimits.MaxFileSizeMB +- uid: EchoHub.Server.Config.UploadLimits.MaxFileSizeMB* + name: MaxFileSizeMB + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxFileSizeMB_ + commentId: Overload:EchoHub.Server.Config.UploadLimits.MaxFileSizeMB + fullName: EchoHub.Server.Config.UploadLimits.MaxFileSizeMB + nameWithType: UploadLimits.MaxFileSizeMB +- uid: EchoHub.Server.Config.UploadLimits.MaxForKind(EchoHub.Core.Models.AttachmentKind) + name: MaxForKind(AttachmentKind) + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxForKind_EchoHub_Core_Models_AttachmentKind_ + commentId: M:EchoHub.Server.Config.UploadLimits.MaxForKind(EchoHub.Core.Models.AttachmentKind) + fullName: EchoHub.Server.Config.UploadLimits.MaxForKind(EchoHub.Core.Models.AttachmentKind) + nameWithType: UploadLimits.MaxForKind(AttachmentKind) +- uid: EchoHub.Server.Config.UploadLimits.MaxForKind* + name: MaxForKind + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxForKind_ + commentId: Overload:EchoHub.Server.Config.UploadLimits.MaxForKind + fullName: EchoHub.Server.Config.UploadLimits.MaxForKind + nameWithType: UploadLimits.MaxForKind +- uid: EchoHub.Server.Config.UploadLimits.MaxImageSizeBytes + name: MaxImageSizeBytes + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxImageSizeBytes + commentId: P:EchoHub.Server.Config.UploadLimits.MaxImageSizeBytes + fullName: EchoHub.Server.Config.UploadLimits.MaxImageSizeBytes + nameWithType: UploadLimits.MaxImageSizeBytes +- uid: EchoHub.Server.Config.UploadLimits.MaxImageSizeBytes* + name: MaxImageSizeBytes + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxImageSizeBytes_ + commentId: Overload:EchoHub.Server.Config.UploadLimits.MaxImageSizeBytes + fullName: EchoHub.Server.Config.UploadLimits.MaxImageSizeBytes + nameWithType: UploadLimits.MaxImageSizeBytes +- uid: EchoHub.Server.Config.UploadLimits.MaxImageSizeMB + name: MaxImageSizeMB + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxImageSizeMB + commentId: P:EchoHub.Server.Config.UploadLimits.MaxImageSizeMB + fullName: EchoHub.Server.Config.UploadLimits.MaxImageSizeMB + nameWithType: UploadLimits.MaxImageSizeMB +- uid: EchoHub.Server.Config.UploadLimits.MaxImageSizeMB* + name: MaxImageSizeMB + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxImageSizeMB_ + commentId: Overload:EchoHub.Server.Config.UploadLimits.MaxImageSizeMB + fullName: EchoHub.Server.Config.UploadLimits.MaxImageSizeMB + nameWithType: UploadLimits.MaxImageSizeMB +- uid: EchoHub.Server.Config.UploadLimits.MaxRequestBodyBytes + name: MaxRequestBodyBytes + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxRequestBodyBytes + commentId: P:EchoHub.Server.Config.UploadLimits.MaxRequestBodyBytes + fullName: EchoHub.Server.Config.UploadLimits.MaxRequestBodyBytes + nameWithType: UploadLimits.MaxRequestBodyBytes +- uid: EchoHub.Server.Config.UploadLimits.MaxRequestBodyBytes* + name: MaxRequestBodyBytes + href: api/server/EchoHub.Server.Config.UploadLimits.html#EchoHub_Server_Config_UploadLimits_MaxRequestBodyBytes_ + commentId: Overload:EchoHub.Server.Config.UploadLimits.MaxRequestBodyBytes + fullName: EchoHub.Server.Config.UploadLimits.MaxRequestBodyBytes + nameWithType: UploadLimits.MaxRequestBodyBytes - uid: EchoHub.Server.Controllers name: EchoHub.Server.Controllers href: api/server/EchoHub.Server.Controllers.html @@ -16252,15 +16855,15 @@ references: commentId: T:EchoHub.Server.Controllers.ChannelsController fullName: EchoHub.Server.Controllers.ChannelsController nameWithType: ChannelsController -- uid: EchoHub.Server.Controllers.ChannelsController.#ctor(EchoHub.Core.Contracts.IChannelService,EchoHub.Server.Data.EchoHubDbContext,EchoHub.Server.Services.FileStorageService,EchoHub.Core.Services.ImageToAsciiService,System.Net.Http.IHttpClientFactory,EchoHub.Core.Contracts.IChatService,EchoHub.Core.Contracts.IMessageEncryptionService) - name: ChannelsController(IChannelService, EchoHubDbContext, FileStorageService, ImageToAsciiService, IHttpClientFactory, IChatService, IMessageEncryptionService) - href: api/server/EchoHub.Server.Controllers.ChannelsController.html#EchoHub_Server_Controllers_ChannelsController__ctor_EchoHub_Core_Contracts_IChannelService_EchoHub_Server_Data_EchoHubDbContext_EchoHub_Server_Services_FileStorageService_EchoHub_Core_Services_ImageToAsciiService_System_Net_Http_IHttpClientFactory_EchoHub_Core_Contracts_IChatService_EchoHub_Core_Contracts_IMessageEncryptionService_ - commentId: M:EchoHub.Server.Controllers.ChannelsController.#ctor(EchoHub.Core.Contracts.IChannelService,EchoHub.Server.Data.EchoHubDbContext,EchoHub.Server.Services.FileStorageService,EchoHub.Core.Services.ImageToAsciiService,System.Net.Http.IHttpClientFactory,EchoHub.Core.Contracts.IChatService,EchoHub.Core.Contracts.IMessageEncryptionService) - name.vb: New(IChannelService, EchoHubDbContext, FileStorageService, ImageToAsciiService, IHttpClientFactory, IChatService, IMessageEncryptionService) - fullName: EchoHub.Server.Controllers.ChannelsController.ChannelsController(EchoHub.Core.Contracts.IChannelService, EchoHub.Server.Data.EchoHubDbContext, EchoHub.Server.Services.FileStorageService, EchoHub.Core.Services.ImageToAsciiService, System.Net.Http.IHttpClientFactory, EchoHub.Core.Contracts.IChatService, EchoHub.Core.Contracts.IMessageEncryptionService) - fullName.vb: EchoHub.Server.Controllers.ChannelsController.New(EchoHub.Core.Contracts.IChannelService, EchoHub.Server.Data.EchoHubDbContext, EchoHub.Server.Services.FileStorageService, EchoHub.Core.Services.ImageToAsciiService, System.Net.Http.IHttpClientFactory, EchoHub.Core.Contracts.IChatService, EchoHub.Core.Contracts.IMessageEncryptionService) - nameWithType: ChannelsController.ChannelsController(IChannelService, EchoHubDbContext, FileStorageService, ImageToAsciiService, IHttpClientFactory, IChatService, IMessageEncryptionService) - nameWithType.vb: ChannelsController.New(IChannelService, EchoHubDbContext, FileStorageService, ImageToAsciiService, IHttpClientFactory, IChatService, IMessageEncryptionService) +- uid: EchoHub.Server.Controllers.ChannelsController.#ctor(EchoHub.Core.Contracts.IChannelService,EchoHub.Server.Data.EchoHubDbContext,EchoHub.Server.Services.FileStorageService,EchoHub.Core.Services.ImageToAsciiService,System.Net.Http.IHttpClientFactory,EchoHub.Core.Contracts.IChatService,EchoHub.Core.Contracts.IMessageEncryptionService,EchoHub.Server.Config.UploadLimits) + name: ChannelsController(IChannelService, EchoHubDbContext, FileStorageService, ImageToAsciiService, IHttpClientFactory, IChatService, IMessageEncryptionService, UploadLimits) + href: api/server/EchoHub.Server.Controllers.ChannelsController.html#EchoHub_Server_Controllers_ChannelsController__ctor_EchoHub_Core_Contracts_IChannelService_EchoHub_Server_Data_EchoHubDbContext_EchoHub_Server_Services_FileStorageService_EchoHub_Core_Services_ImageToAsciiService_System_Net_Http_IHttpClientFactory_EchoHub_Core_Contracts_IChatService_EchoHub_Core_Contracts_IMessageEncryptionService_EchoHub_Server_Config_UploadLimits_ + commentId: M:EchoHub.Server.Controllers.ChannelsController.#ctor(EchoHub.Core.Contracts.IChannelService,EchoHub.Server.Data.EchoHubDbContext,EchoHub.Server.Services.FileStorageService,EchoHub.Core.Services.ImageToAsciiService,System.Net.Http.IHttpClientFactory,EchoHub.Core.Contracts.IChatService,EchoHub.Core.Contracts.IMessageEncryptionService,EchoHub.Server.Config.UploadLimits) + name.vb: New(IChannelService, EchoHubDbContext, FileStorageService, ImageToAsciiService, IHttpClientFactory, IChatService, IMessageEncryptionService, UploadLimits) + fullName: EchoHub.Server.Controllers.ChannelsController.ChannelsController(EchoHub.Core.Contracts.IChannelService, EchoHub.Server.Data.EchoHubDbContext, EchoHub.Server.Services.FileStorageService, EchoHub.Core.Services.ImageToAsciiService, System.Net.Http.IHttpClientFactory, EchoHub.Core.Contracts.IChatService, EchoHub.Core.Contracts.IMessageEncryptionService, EchoHub.Server.Config.UploadLimits) + fullName.vb: EchoHub.Server.Controllers.ChannelsController.New(EchoHub.Core.Contracts.IChannelService, EchoHub.Server.Data.EchoHubDbContext, EchoHub.Server.Services.FileStorageService, EchoHub.Core.Services.ImageToAsciiService, System.Net.Http.IHttpClientFactory, EchoHub.Core.Contracts.IChatService, EchoHub.Core.Contracts.IMessageEncryptionService, EchoHub.Server.Config.UploadLimits) + nameWithType: ChannelsController.ChannelsController(IChannelService, EchoHubDbContext, FileStorageService, ImageToAsciiService, IHttpClientFactory, IChatService, IMessageEncryptionService, UploadLimits) + nameWithType.vb: ChannelsController.New(IChannelService, EchoHubDbContext, FileStorageService, ImageToAsciiService, IHttpClientFactory, IChatService, IMessageEncryptionService, UploadLimits) - uid: EchoHub.Server.Controllers.ChannelsController.#ctor* name: ChannelsController href: api/server/EchoHub.Server.Controllers.ChannelsController.html#EchoHub_Server_Controllers_ChannelsController__ctor_ @@ -16312,6 +16915,21 @@ references: commentId: Overload:EchoHub.Server.Controllers.ChannelsController.GetChannelCrypto fullName: EchoHub.Server.Controllers.ChannelsController.GetChannelCrypto nameWithType: ChannelsController.GetChannelCrypto +- uid: EchoHub.Server.Controllers.ChannelsController.GetChannelMeta(System.String) + name: GetChannelMeta(string) + href: api/server/EchoHub.Server.Controllers.ChannelsController.html#EchoHub_Server_Controllers_ChannelsController_GetChannelMeta_System_String_ + commentId: M:EchoHub.Server.Controllers.ChannelsController.GetChannelMeta(System.String) + name.vb: GetChannelMeta(String) + fullName: EchoHub.Server.Controllers.ChannelsController.GetChannelMeta(string) + fullName.vb: EchoHub.Server.Controllers.ChannelsController.GetChannelMeta(String) + nameWithType: ChannelsController.GetChannelMeta(string) + nameWithType.vb: ChannelsController.GetChannelMeta(String) +- uid: EchoHub.Server.Controllers.ChannelsController.GetChannelMeta* + name: GetChannelMeta + href: api/server/EchoHub.Server.Controllers.ChannelsController.html#EchoHub_Server_Controllers_ChannelsController_GetChannelMeta_ + commentId: Overload:EchoHub.Server.Controllers.ChannelsController.GetChannelMeta + fullName: EchoHub.Server.Controllers.ChannelsController.GetChannelMeta + nameWithType: ChannelsController.GetChannelMeta - uid: EchoHub.Server.Controllers.ChannelsController.GetChannels(System.Int32,System.Int32) name: GetChannels(int, int) href: api/server/EchoHub.Server.Controllers.ChannelsController.html#EchoHub_Server_Controllers_ChannelsController_GetChannels_System_Int32_System_Int32_ @@ -16630,15 +17248,15 @@ references: commentId: T:EchoHub.Server.Controllers.UsersController fullName: EchoHub.Server.Controllers.UsersController nameWithType: UsersController -- uid: EchoHub.Server.Controllers.UsersController.#ctor(EchoHub.Core.Contracts.IUserService,EchoHub.Core.Services.ImageToAsciiService) - name: UsersController(IUserService, ImageToAsciiService) - href: api/server/EchoHub.Server.Controllers.UsersController.html#EchoHub_Server_Controllers_UsersController__ctor_EchoHub_Core_Contracts_IUserService_EchoHub_Core_Services_ImageToAsciiService_ - commentId: M:EchoHub.Server.Controllers.UsersController.#ctor(EchoHub.Core.Contracts.IUserService,EchoHub.Core.Services.ImageToAsciiService) - name.vb: New(IUserService, ImageToAsciiService) - fullName: EchoHub.Server.Controllers.UsersController.UsersController(EchoHub.Core.Contracts.IUserService, EchoHub.Core.Services.ImageToAsciiService) - fullName.vb: EchoHub.Server.Controllers.UsersController.New(EchoHub.Core.Contracts.IUserService, EchoHub.Core.Services.ImageToAsciiService) - nameWithType: UsersController.UsersController(IUserService, ImageToAsciiService) - nameWithType.vb: UsersController.New(IUserService, ImageToAsciiService) +- uid: EchoHub.Server.Controllers.UsersController.#ctor(EchoHub.Core.Contracts.IUserService,EchoHub.Core.Services.ImageToAsciiService,EchoHub.Server.Config.UploadLimits) + name: UsersController(IUserService, ImageToAsciiService, UploadLimits) + href: api/server/EchoHub.Server.Controllers.UsersController.html#EchoHub_Server_Controllers_UsersController__ctor_EchoHub_Core_Contracts_IUserService_EchoHub_Core_Services_ImageToAsciiService_EchoHub_Server_Config_UploadLimits_ + commentId: M:EchoHub.Server.Controllers.UsersController.#ctor(EchoHub.Core.Contracts.IUserService,EchoHub.Core.Services.ImageToAsciiService,EchoHub.Server.Config.UploadLimits) + name.vb: New(IUserService, ImageToAsciiService, UploadLimits) + fullName: EchoHub.Server.Controllers.UsersController.UsersController(EchoHub.Core.Contracts.IUserService, EchoHub.Core.Services.ImageToAsciiService, EchoHub.Server.Config.UploadLimits) + fullName.vb: EchoHub.Server.Controllers.UsersController.New(EchoHub.Core.Contracts.IUserService, EchoHub.Core.Services.ImageToAsciiService, EchoHub.Server.Config.UploadLimits) + nameWithType: UsersController.UsersController(IUserService, ImageToAsciiService, UploadLimits) + nameWithType.vb: UsersController.New(IUserService, ImageToAsciiService, UploadLimits) - uid: EchoHub.Server.Controllers.UsersController.#ctor* name: UsersController href: api/server/EchoHub.Server.Controllers.UsersController.html#EchoHub_Server_Controllers_UsersController__ctor_ @@ -18883,6 +19501,21 @@ references: commentId: Overload:EchoHub.Server.Services.ChannelService.GetChannelListAsync fullName: EchoHub.Server.Services.ChannelService.GetChannelListAsync nameWithType: ChannelService.GetChannelListAsync +- uid: EchoHub.Server.Services.ChannelService.GetChannelMetaAsync(System.String) + name: GetChannelMetaAsync(string) + href: api/server/EchoHub.Server.Services.ChannelService.html#EchoHub_Server_Services_ChannelService_GetChannelMetaAsync_System_String_ + commentId: M:EchoHub.Server.Services.ChannelService.GetChannelMetaAsync(System.String) + name.vb: GetChannelMetaAsync(String) + fullName: EchoHub.Server.Services.ChannelService.GetChannelMetaAsync(string) + fullName.vb: EchoHub.Server.Services.ChannelService.GetChannelMetaAsync(String) + nameWithType: ChannelService.GetChannelMetaAsync(string) + nameWithType.vb: ChannelService.GetChannelMetaAsync(String) +- uid: EchoHub.Server.Services.ChannelService.GetChannelMetaAsync* + name: GetChannelMetaAsync + href: api/server/EchoHub.Server.Services.ChannelService.html#EchoHub_Server_Services_ChannelService_GetChannelMetaAsync_ + commentId: Overload:EchoHub.Server.Services.ChannelService.GetChannelMetaAsync + fullName: EchoHub.Server.Services.ChannelService.GetChannelMetaAsync + nameWithType: ChannelService.GetChannelMetaAsync - uid: EchoHub.Server.Services.ChannelService.GetChannelTopicAsync(System.String) name: GetChannelTopicAsync(string) href: api/server/EchoHub.Server.Services.ChannelService.html#EchoHub_Server_Services_ChannelService_GetChannelTopicAsync_System_String_