- Solution
TextBox.slnxcontains two projects:src/TextBox/TextBox.csproj(Blazor Web App,net10.0, Server interactivity) +src/TextBox.Sdk/TextBox.Sdk.csproj(NSwag-generated NuGet client,netstandard2.0). Phone-style dashboard at/(Components/Pages/Home.razor= conversation list +HomeAsideside panel,Conversation.razor= chat + detail modal,Sdk.razor= SDK docs at/sdkvia full-widthDocsLayout;MainLayout.razoris the static phone shell andTopNavbar/BlazorErrorUiare shared across both layouts — seeDESIGN.md§3, pages render only inner content), SMS mock API at/api/messages(Endpoints/MessageEndpoints.cs, LiteDB-backed async store inServices/LiteDbMessageStore.cswithIMemoryCacheread-through, contracts inServices/IMessageStore.cs, models inModels/SmsMessage.cs). DB path viaMessageStore:Path(defaultData/textbox.db, gitignored).POST /api/messagesoptionally requires an API key (ASP.NET Core auth pipeline inAuth/:ApiKeyAuthOptions.csscheme options +ApiKeyAuthHandler.cs, enforced viaRequireAuthorization()when a key is configured,ApiKeysection inappsettings.json, open by default;Auth/ApiKeyService.csis the single source of truth for the key — never readIOptions<>directly, the scheme binds the named instance). Message timestamps are monotonic per store instance (LiteDB persists ms precision, so back-to-back sends would tie — do not revert to rawUtcNow). Realtime fanout over SignalR (Hubs/MessageHub.csat/hubs/messages,Hubs/MessageNotifier.cs, per-circuitServices/MessageLiveFeed.csclient). Scalar API docs at/scalar(OpenAPI at/openapi/v1.json, mapped in all environments). - SDK (
src/TextBox.Sdk/,TextBox.Sdk0.1.0,netstandard2.0): NSwag 14.7.1CSharpClientgenerated from committedopenapi.jsonsnapshot (export via running server/openapi/v1.jsonwith a key configured so the Bearer scheme is present; regenerate manually —cd src/TextBox.Sdkthennswag run nswag.json, never on build; globalNSwag.ConsoleCoretool +"runtime": "Net100"innswag.json; spec/input paths resolve from CWD). GeneratedTextBoxClient(Generated/TextBoxClient.g.cs,ITextBoxClient, op-style methods) is the public API; hand-written partials addApiKey(PrepareRequesthook), options ctors (TextBoxOptions), friendly aliases (SendAsync/ListAsync/GetAsync/ClearAsync),SendSmsRequest.Create, optionalAddTextBoxClient()DI.IsExternalInit.csshim +System.ComponentModel.Annotationsref are netstandard2.0 requirements — do not remove. NuGetPackageIconmust be PNG (SVG fails NU5045):icon.pngcopied frombranding-assets/(256px). Branding source of truth isbranding-assets/(SVGs + PNGs, spacey filenames — copy to clean names);wwwroot/favicon.svg(xs variant) +favicon.pngfallback linked inApp.razor. - Legacy
TextBox.slnstill present but unused — always specifyTextBox.slnxexplicitly (baredotnet sln list/dotnet buildfails with "Found more than one solution file"). - Docker:
src/TextBox/Dockerfile(standardbase/build/publish/finalstages, non-rootaspnetruntime, port8080; context is the repo root —docker build -f src/TextBox/Dockerfile .; SDK csproj +package.jsoncopied beforedotnet restore/npm cifor layer caching); DB lives at/app/Data— mount-v textbox-data:/app/Datato persist. CI:.github/workflows/ci.yml(build + test + SDK pack artifact + docker build on push/PR tomaster, pushes image to GHCR onmaster;sdk-publishjob pushes to NuGet only ontextbox-sdk-v*tags via trusted publishing (OIDCNuGet/login@v1asParsaG72, no stored key — policy must allow repoParsaGachkar/TextBox, workflowci.yml; no tags pushed yet). - Contributions: follow
CONTRIBUTING.md— Conventional Commits (type(scope): subject), PR titles linted in CI. - Git is pre-initial-commit on
master, no remote, no commits.bin/,obj/,packages/are gitignored.
- .NET SDK
10.0.302available (dotnet --version). Host targetsnet10.0, SDK targetsnetstandard2.0with<LangVersion>latest</LangVersion>(netstandard2.0 defaults to C# 7.3, which the generated client cannot compile against) — see each.csprojTargetFramework. - Node.js + npm required: the build runs the Tailwind v4 CLI (
node ./node_modules/@tailwindcss/cli/dist/index.mjs -i ./Styles/app.css -o ./wwwroot/css/app.css) via a.csprojtarget; daisyUI theme (#512bd4primary) lives inStyles/app.css. For live CSS rebuilds runnpm run watch:cssinsrc/TextBox. TheDockerfileadditionally runsnpm run build:cssas an explicit step (with a file-existence check) so a broken stylesheet fails the image build instead of 404ing at runtime. - IDE is Rider (JetBrains).
.idea/is nested at.idea/.idea.TextBox/.idea/— do not commit; root.gitignorealready ignoresbin//obj/but not.idea/at this path, so avoid staging it.
- Build all:
dotnet build TextBox.slnx - Run API/dashboard (single host):
dotnet run --project src/TextBox/TextBox.csproj - Test (xUnit + NSubstitute + bUnit in
tests/TextBox.Tests):dotnet test TextBox.slnx/ single test:dotnet test TextBox.slnx --filter <TestName> - Add project to solution:
dotnet new <template> -n <Name> -o <Dir>thendotnet sln TextBox.slnx add <Dir>/<Name>.csproj - Verify solution state:
dotnet sln TextBox.slnx list(baredotnet sln listfails — two solution files) - Repo helper scripts (
scripts/, PowerShell 5.1):Set-SdkVersion.ps1 -Version <semver>,Update-OpenApiSnapshot.ps1(needs built app),Regen-SdkClient.ps1(needs globalNSwag.ConsoleCoretool),Seed-FakeData.ps1 -ApiKey <key> [-ClearFirst](needs running server; pass byte-encoded UTF8 bodies — PS 5.1 mangles non-ASCII string bodies)
TextBox.slnis legacy/empty — ignore it, always buildTextBox.slnx.- Use
workdirparam for tool calls instead ofSet-Location/cdin PowerShell.