House rules: numbers through one formatter, no made-up server errors, and the rules fail the build
Build / Build (push) Successful in 33s

- Every number shown on a page goes through NumbersExtensions: counts,
  percentages, the nerd readouts and the values written into a style.
- A failure the client builds itself (offline, timeout, retries run out, an
  answer it can't read) is a 400 that names its kind; only a server's own 5XX
  passes through.
- Login, sign-up, recovery and password confirmations leave autocomplete to
  the browser, so it can offer the saved login.
- The Daisy components start with empty lists and attributes, and a
  decorative image still renders alt="".
- Arasaka.HouseRules.Analyzers checks all of this in every build, together
  with PrivaPub.ClientModels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W4YtEJrgxG12Pc39Fai8Bd
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-11-10 01:26:39 +01:00
1 parent 697b2c8897
commit 3827d903a3
37 files changed
+165 -111

No files matched your search

+1 -1
View File
@@ -57,7 +57,7 @@
placeholder="@Localizer["Invitation code"]" data-group-code>
<Prefix><i class="ion-md-key"></i></Prefix>
</DInput>
<DInput TValue="string" type="password" @bind-Value="joining.InvitationPassword" Immediate="true" autocomplete="current-password" maxlength="128"
<DInput TValue="string" type="password" @bind-Value="joining.InvitationPassword" Immediate="true" maxlength="128"
placeholder="@Localizer["Invitation password, if it has one"]" />
<DButton Type="submit" Size="DSize.Sm" class="self-end" Disabled="@(IsDefaultDisabled || isSaving || string.IsNullOrWhiteSpace(joining.InvitationCode))" data-group-join-submit>
<i class="ion-md-log-in"></i><span>@Localizer["Join"]</span>
+1 -1
View File
@@ -692,7 +692,7 @@
if (RequestStats.Latest(1).FirstOrDefault() is { } last)
{
yield return NerdRow.Of(Localizer["last request"], $"{last.Method} {last.Route}");
yield return NerdRow.Of(Localizer["answer"], $"{last.Status} · {last.DurationMs:0} ms · {NerdRow.Bytes(last.Bytes) ?? "?"}");
yield return NerdRow.Of(Localizer["answer"], $"{last.Status} · {last.DurationMs.ToDefaultCount()} ms · {NerdRow.Bytes(last.Bytes) ?? "?"}");
yield return NerdRow.Of("X-RateLimit-Remaining", last.RateLimitRemaining);
}
}
+2 -2
View File
@@ -29,13 +29,13 @@
<DInput TValue="string" class="w-full font-mono text-xs" @bind-Value="form.InvitationCode" autocomplete="off" autocapitalize="none" spellcheck="false"
maxlength="@PrivaPub.ClientModels.Constants.GroupInvitationLength" data-join-code />
</label>
<DInput TValue="string" Type="password" class="w-full" @bind-Value="form.InvitationPassword" autocomplete="off"
<DInput TValue="string" Type="password" class="w-full" @bind-Value="form.InvitationPassword"
placeholder="@Localizer["Invitation password, if it has one"]" data-join-code-password />
<b class="text-sm">@Localizer["Your login"]</b>
<label class="flex flex-col gap-1">
<span class="text-sm">@Localizer["Username"]</span>
<DInput TValue="string" class="w-full" @bind-Value="form.UserName" autocomplete="username" autocapitalize="none" spellcheck="false" data-join-username>
<DInput TValue="string" class="w-full" @bind-Value="form.UserName" autocapitalize="none" spellcheck="false" data-join-username>
<Prefix><i class="ion-md-person"></i></Prefix>
</DInput>
</label>
+2 -2
View File
@@ -60,13 +60,13 @@
<label class="flex flex-col gap-1">
<span class="text-sm">@Localizer["Username"]</span>
<DInput TValue="string" class="w-full" @bind-Value="form.UserName" autocomplete="username" autocapitalize="none" spellcheck="false">
<DInput TValue="string" class="w-full" @bind-Value="form.UserName" autocapitalize="none" spellcheck="false">
<Prefix><i class="ion-md-person"></i></Prefix>
</DInput>
</label>
<label class="flex flex-col gap-1">
<span class="text-sm">@Localizer["Password"]</span>
<DInput TValue="string" Type="password" class="w-full" @bind-Value="form.Password" autocomplete="current-password">
<DInput TValue="string" Type="password" class="w-full" @bind-Value="form.Password">
<Prefix><i class="ion-md-key"></i></Prefix>
</DInput>
</label>
+2 -2
View File
@@ -35,7 +35,7 @@
<label class="flex flex-col gap-1">
<span class="text-sm">@Localizer["Username"]</span>
<DInput TValue="string" class="w-full" Value="request.UserName" ValueChanged="v => request.UserName = NullIfBlank(v)" ValueExpression="() => request.UserName"
Immediate="true" disabled="@request.IsUsernameDisabled" autocomplete="username" autocapitalize="none" spellcheck="false" data-recovery-username>
Immediate="true" disabled="@request.IsUsernameDisabled" autocapitalize="none" spellcheck="false" data-recovery-username>
<Prefix><i class="ion-md-person"></i></Prefix>
</DInput>
</label>
@@ -43,7 +43,7 @@
<label class="flex flex-col gap-1">
<span class="text-sm">@Localizer["Email address"]</span>
<DInput TValue="string" Type="email" class="w-full" Value="request.Email" ValueChanged="v => request.Email = NullIfBlank(v)" ValueExpression="() => request.Email"
Immediate="true" disabled="@request.IsEmailDisabled" autocomplete="email" inputmode="email" autocapitalize="none" spellcheck="false" data-recovery-email>
Immediate="true" disabled="@request.IsEmailDisabled" inputmode="email" autocapitalize="none" spellcheck="false" data-recovery-email>
<Prefix><i class="ion-md-mail"></i></Prefix>
</DInput>
</label>
+1 -1
View File
@@ -41,7 +41,7 @@
<b class="text-sm">@Localizer["Your login"]</b>
<label class="flex flex-col gap-1">
<span class="text-sm">@Localizer["Username"]</span>
<DInput TValue="string" class="w-full" @bind-Value="form.UserName" autocomplete="username" autocapitalize="none" spellcheck="false" data-setup-username>
<DInput TValue="string" class="w-full" @bind-Value="form.UserName" autocapitalize="none" spellcheck="false" data-setup-username>
<Prefix><i class="ion-md-person"></i></Prefix>
</DInput>
</label>