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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Eternet.Web.Chat.UI" Version="1.0.10" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Eternet.Web.Chat.UI" Version="1.0.10" />
                    
Directory.Packages.props
<PackageReference Include="Eternet.Web.Chat.UI" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Eternet.Web.Chat.UI --version 1.0.10
                    
#r "nuget: Eternet.Web.Chat.UI, 1.0.10"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Eternet.Web.Chat.UI@1.0.10
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Eternet.Web.Chat.UI&version=1.0.10
                    
Install as a Cake Addin
#tool nuget:?package=Eternet.Web.Chat.UI&version=1.0.10
                    
Install as a Cake Tool

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.css and chat-ui.js once from the shared Blazor shell that serves both /operator/chat and /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.Contracts for 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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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 128 8/10/2026