Eternet.Web.Chat.UI
1.0.10
Prefix Reserved
dotnet add package Eternet.Web.Chat.UI --version 1.0.10
NuGet\Install-Package Eternet.Web.Chat.UI -Version 1.0.10
<PackageReference Include="Eternet.Web.Chat.UI" Version="1.0.10" />
<PackageVersion Include="Eternet.Web.Chat.UI" Version="1.0.10" />
<PackageReference Include="Eternet.Web.Chat.UI" />
paket add Eternet.Web.Chat.UI --version 1.0.10
#r "nuget: Eternet.Web.Chat.UI, 1.0.10"
#:package Eternet.Web.Chat.UI@1.0.10
#addin nuget:?package=Eternet.Web.Chat.UI&version=1.0.10
#tool nuget:?package=Eternet.Web.Chat.UI&version=1.0.10
Eternet.Web.Chat.UI
Provider-agnostic Razor Class Library for reusable Eternet chat components.
P4-W1 establishes the first package-ready chat shells, shared UI abstractions, and contract-backed component parameters.
Static asset imports
Import the packaged global assets from the consuming host:
<link href="_content/Eternet.Web.Chat.UI/chat-ui.css" rel="stylesheet" />
<script src="_content/Eternet.Web.Chat.UI/chat-ui.js"></script>
The current hosted implementation imports those assets once in src/Eternet.Web.Chat.Server\Components\App.razor. Because the server host maps both the Eternet.Web.Chat.WebClient assembly and the Eternet.Web.Chat.UI assembly through the same Blazor Web App shell, that single import covers both /operator/chat and /operator/chat/client.
chat-ui.js keeps the stable _content/Eternet.Web.Chat.UI/chat-ui.js import path while exposing reusable chat primitives for timeline capture/restore, near-bottom detection, scroll-to-end, clipboard copy, keyboard shortcuts, and horizontal resize handles.
Localization
Register the package services in the consuming host before rendering the components:
builder.Services.AddEternetChatUi();
The package ships component-scoped resources for en-US and es-AR. Component-owned fallback labels, empty/loading
states, accessibility labels, delivery statuses, and date formatting follow CultureInfo.CurrentUICulture /
CultureInfo.CurrentCulture. Host-provided labels, route chrome, provider wording, and data projected through the
read models remain host-owned and should be localized before they are passed into the RCL.
Server hosts should also configure request localization with the cultures they expose to users. The package provides
ChatUiLocalization.DefaultCultureName and ChatUiLocalization.SupportedCultureNames for the built-in cultures.
Resources are intentionally split by component, for example Components/ConversationList.es-AR.resx, instead of being
kept in one global file. That keeps translation ownership aligned with the component API and avoids unrelated strings
changing in the same resource file as the library grows.
Host theming variables
chat-ui.css now exposes a small host-owned token surface so consuming apps can align the package with local brand colors without reintroducing scoped chat CSS:
:root {
--chat-ui-font-family: "Segoe UI", system-ui, sans-serif;
--chat-ui-page-background: #f6f8fa;
--chat-ui-page-foreground: #24292f;
--chat-ui-title-foreground: #0f172a;
--chat-ui-muted-foreground: #475569;
--chat-ui-subtle-foreground: #64748b;
--chat-ui-header-foreground: #f8fafc;
--chat-ui-header-muted-foreground: #cbd5e1;
--chat-ui-header-chip-foreground: #94a3b8;
--chat-ui-panel-background: #ffffff;
--chat-ui-panel-border: #d8dee4;
--chat-ui-control-border: #d0d7de;
--chat-ui-soft-background: #f8fafc;
--chat-ui-accent: #2563eb;
--chat-ui-accent-soft: #dbeafe;
--chat-ui-accent-foreground: #1d4ed8;
--chat-ui-header-gradient-start: #0f172a;
--chat-ui-header-gradient-mid: #111827;
--chat-ui-header-gradient-end: #1e293b;
}
Hosts should override those tokens in their own global stylesheet after importing the package assets. Keep provider wording, admin-only labels, route chrome, and any campaign-specific filters or metrics in the host layer rather than pushing them back into the package.
Rollout notes
Replace a legacy campaign chat screen only when the consuming host:
- imports
chat-ui.cssandchat-ui.jsonce from the shared Blazor shell that serves both/operator/chatand/operator/chat/client - keeps package theming in global host CSS through the documented
--chat-ui-*custom properties instead of adding new chat-scoped CSS - preserves the existing bounded queue and timeline loading model rather than reintroducing full-collection materialization
- accepts the remaining admin-specific parity gaps as host-owned follow-up work: route chrome, family/pending-only filters, refresh-command placement, and persisted keyboard/resize behavior
If rollout validation fails, move operators back to the previous host route; the package has no runtime dependency on a campaign host and the current rollback decision stays entirely in host routing/enablement.
Campaign parity migration boundary
P6-W1 confirmed that this package owns the provider-neutral workspace shell, queue/timeline/composer/context
components, responsive global assets, and the reusable JS primitives needed by consuming hosts.
The package does not own campaign admin-specific route chrome or provider wording, family selector and pending-only filter
semantics, host-supplied context tabs, or whether a host wires keyboard shortcuts and persisted resize behavior on top
of the exported chat-ui.js helpers.
Usage examples
Workspace shell composition
@using Eternet.Web.Chat.UI.Abstractions
@using Eternet.Web.Chat.Contracts.Conversations.Models
@using Eternet.Web.Chat.UI.Components
<ChatWorkspace
Header="@HeaderContent"
Filters="@FilterContent"
Sidebar="@SidebarContent"
Main="@MainContent"
Context="@ContextContent" />
@code {
private RenderFragment HeaderContent => @<ChatWorkspaceHeader Header="_header" />;
private RenderFragment FilterContent => @<ChatWorkspaceFilters Groups="_filterGroups" />;
private RenderFragment SidebarContent => @<ConversationList Items="@Conversations"
SelectedConversationId="@SelectedConversationId"
IsLoading="@IsLoadingConversations"
OnConversationSelected="HandleConversationSelectedAsync" />;
private RenderFragment MainContent => @<MessageTimeline Conversation="@SelectedConversation"
Items="@TimelineItems"
IsLoading="@IsLoadingTimeline" />;
private RenderFragment ContextContent => @<ChatContextPanel Sections="_contextSections" />;
private readonly ChatWorkspaceHeaderModel _header = new()
{
Eyebrow = "Operator workspace",
Title = "Chat workspace",
Summary = "Provider-neutral shell ready for host composition.",
Stats =
[
new ChatWorkspaceStatModel { Label = "Visible", Value = "24" },
new ChatWorkspaceStatModel { Label = "Pending", Value = "6" }
]
};
private readonly ChatFilterGroupModel[] _filterGroups =
[
new()
{
Label = "Response",
Options =
[
new ChatFilterOptionModel { Key = "all", Label = "All", IsActive = true },
new ChatFilterOptionModel { Key = "pending", Label = "Pending", BadgeText = "6" }
]
}
];
private readonly ChatContextSectionModel[] _contextSections =
[
new()
{
Title = "Participant",
Fields =
[
new ChatContextFieldModel { Label = "Name", Value = "Jane Doe" },
new ChatContextFieldModel { Label = "Address", Value = "+15551234567" }
]
}
];
private IReadOnlyList<ConversationSummaryReadModel> Conversations = [];
private string? SelectedConversationId;
private bool IsLoadingConversations;
private ConversationDetailReadModel? SelectedConversation;
private IReadOnlyList<MessageTimelineItemReadModel> TimelineItems = [];
private bool IsLoadingTimeline;
private Task HandleConversationSelectedAsync(ConversationSummaryReadModel conversation)
{
SelectedConversationId = conversation.ConversationId;
return Task.CompletedTask;
}
}
Timeline and composer composition
@using Eternet.Web.Chat.Contracts.Conversations.Models
@using Eternet.Web.Chat.Contracts.Messages.Models
@using Eternet.Web.Chat.UI.Components
@using Eternet.Web.Chat.UI.Models
<MessageTimeline Conversation="@SelectedConversation"
Items="@TimelineItems"
IsLoading="@IsLoadingTimeline" />
<MessageComposer ConversationId="@SelectedConversation?.ConversationId"
ReplyWindow="@(SelectedConversation?.ReplyWindow ?? ReplyWindowStateReadModel.Closed)"
IsBusy="@IsSending"
ErrorState="@ComposerError"
Title="Reply composer"
OnSubmit="HandleSubmitAsync" />
@code {
private ConversationDetailReadModel? SelectedConversation;
private IReadOnlyList<MessageTimelineItemReadModel> TimelineItems = [];
private bool IsLoadingTimeline;
private bool IsSending;
private ChatStateBannerModel? ComposerError;
private Task HandleSubmitAsync(ChatComposerSubmitRequest request)
{
ComposerError = null;
LastSubmittedRequest = request;
return Task.CompletedTask;
}
private ChatComposerSubmitRequest? LastSubmittedRequest { get; set; }
}
Packaging boundary rule: this library must stay free of provider SDKs, Botmaker import namespaces and API clients, EF Core, transport hosts, and service-specific projects. Provider adapters, SignalR clients, and persistence remain in consuming hosts.
Allowed dependency surface for future waves
- Allowed: BCL dependencies, Blazor/RCL framework packages needed to render provider-agnostic components, chat-owned abstractions or models that stay package-ready, and
Eternet.Web.Chat.Contractsfor provider-neutral read models passed into UI shells. - Forbidden:
Eternet.Web.Chat.Server,Eternet.Web.Chat.WebClient,Eternet.Messaging.WhatsApp, host-specific campaign web projects,Eternet.Web.Infrastructure,Eternet.AspNetCore.ServiceFabric, Botmaker import namespaces and API client packages, EF Core, SignalR server hubs, and provider SDK packages.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Eternet.Web.Chat.Contracts (>= 1.0.10)
- Eternet.Web.UI (>= 2.0.34)
- Microsoft.AspNetCore.Components.Web (>= 10.0.10)
- Microsoft.Extensions.Localization (>= 10.0.10)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.10 | 126 | 8/10/2026 |