Commit Graph
114 Commits
Author SHA1 Message Date
Steven RYDELL eda20cbe2f chore: release v1.1.3
Fix column sort headers so a third click clears the sort and restores
the list's default order.
2026-08-01 23:31:27 +02:00
Steven RYDELL 41b3824894 chore: release v1.1.2
Add a Changelog page that renders CHANGELOG.md from the header menu,
switch the default color theme to the brand-accurate Stalwart theme, and
document all changes since v1.1.1.
2026-08-01 23:25:02 +02:00
Steven RYDELL f5524eb45a fix(ui): close sidebar group when picking a link outside it
Plain links (not wrapped in a collapsible) are siblings of collapsible
groups within the same AccordionLevel, but their onClick never touched
that level's openId — so a group left expanded stayed expanded even
after navigating to an unrelated top-level page, or to a sibling link
outside any group.

Both SidebarTopItem and SidebarSubItem's link branches now read the
AccordionLevelContext they're rendered in and call setOpenId(null)
before navigating. For a link inside its own group, this is
immediately overridden by AccordionCollapsible's existing
containsActive effect (which reopens the branch that now contains the
active page), so nested navigation is unaffected — only a truly
unrelated group gets collapsed.

Verified: opening Directory (showing Groups), then clicking Cluster (a
plain top-level link) now collapses Directory instead of leaving it
expanded.
2026-08-01 23:07:48 +02:00
Steven RYDELL 637d8bb286 feat(appearance): rename default theme, add real brand-accurate Stalwart theme
The color theme previously named "Stalwart" was actually just the
neutral black/white default — it never used Stalwart's own brand
color. Renamed it to "Default" (ColorTheme 'stalwart' -> 'default'
value; label/i18n key updated) and added a genuinely new "Stalwart"
theme in its place.

Colors sourced directly from the official site's own design tokens
(D:\project\stalwart_website/src/styles/tokens.css): brand red/pink
#db2d54 (light primary/ring) and #ff4570 (--brand-hi, dark
primary/ring), converted to oklch via the sRGB->OKLab->OKLCH matrix
transform to match this project's existing token format. Neutrals use
near-true-gray values (also converted from the site's actual light/
dark backgrounds and text colors) rather than being re-hued toward the
brand color like the other theme presets — matching how the real site
treats color (saturated only on the accent, neutral everywhere else).

Verified in the running dev server: Default reverts to the original
neutral look, Stalwart renders the brand red across primary buttons,
active nav state, checkboxes, and badges in both light and dark mode.
No regression (243 tests, typecheck, lint all pass).
2026-08-01 23:02:32 +02:00
Steven RYDELL 410a01700b feat(lists): make the most relevant columns sortable on the new tables
Mailing Lists, Roles, and Domains got new/existing columns in prior
commits but no way to sort them — the account-client-sort mechanism
was only wired up in accountColumns.ts. Extracted its one-line
clientSortable() column tagger into schemaDeviationTypes.ts (shared,
same intersection-type pattern) and used it in all four column-patch
files:

- Mailing Lists: Email Address, Description, Aliases
- Roles: Description, Enabled Permissions, Disabled Permissions
- Domains: Domain Name, Enabled, Aliases

Re-verified server sort support live, per list this time rather than
just Accounts: every property tried returns unsupportedSort except one
surprise — x:Domain/query actually accepts sort by "name", despite the
schema not declaring it. Documented in SCHEMA_DEVIATIONS.md and
deliberately routed through the same client-sort path as everything
else instead of adding a one-off "trust an undeclared sort" mechanism
for that single case.

Verified in the running dev server: sort indicators appear on the
intended columns for all three lists, clicking reorders rows correctly
(alphabetical on Mailing Lists' Email Address, numeric on Roles'
Enabled Permissions: 1 -> 3 -> 50 -> 229 -> 244 -> 452), Domains sorts
without error. No regression on Accounts/Groups.
2026-08-01 22:31:25 +02:00
Steven RYDELL 26a3878faa feat(domains): add Aliases count column
Same account-alias-count-column deviation already used on Accounts,
Groups, and Mailing Lists, extended to x:Domain: adds a synthetic
aliasCount column resolved from the real aliases property (a `set` of
alias domain names here, vs. an objectList elsewhere — same id-keyed
wire format, so the existing generic COUNT_COLUMN_SOURCES handling
needed no changes).

Verified in the running dev server: the column renders correctly
alongside the domain's existing Enabled/Certificate/DNS columns.
2026-08-01 22:25:26 +02:00
Steven RYDELL 503cae465f feat(roles): add Enabled/Disabled Permissions count columns
New SCHEMA-DEVIATION: role-permission-count-columns — the Roles list
only shows Description, so seeing how broad or restrictive a role is
required opening it and counting permissions by hand.

Generalized DynamicList's existing alias-count handling (previously
hardcoded to the single `aliasCount` -> `aliases` mapping) into
COUNT_COLUMN_SOURCES, a table of synthetic "*Count" column names to the
real set/objectList property they count. account-alias-count-column
now runs through the same generic path with no behavior change;
role-permission-count-columns (enabledPermissionCount/
disabledPermissionCount -> enabledPermissions/disabledPermissions) is
the second consumer, added with zero new DynamicList.tsx branching.

Verified against the live dev server: correct counts for both seeded
custom roles (Support Agent: 3/0, Read-only Auditor: 1/0) and the
built-in roles (System Administrator: 452/0, User: 244/0, etc.).
Confirmed no regression on Accounts' existing Aliases column/sort.
2026-08-01 22:23:48 +02:00
Steven RYDELL 9cd21a2d34 feat(mailing-lists): add Aliases count column
Same account-alias-count-column deviation already used on Accounts and
Groups, extended to x:MailingList: adds a synthetic aliasCount column
and lets DynamicList's existing generic column-name-driven handling
resolve/render it — no changes needed there, the mechanism was already
list-agnostic.

Verified in the running dev server: the column renders 0 with no
aliases set on either seeded mailing list.
2026-08-01 22:19:35 +02:00
Steven RYDELL d9b073ad6c feat(dev): add scripts/dev-seed to populate sample test data
New scripts/dev-seed.sh (+ .ps1), run after dev-server-init: seeds the
test server with Users (alice, bob, carol — spanning User/Admin roles,
group membership, an alias), Groups (engineering, marketing), Mailing
Lists (newsletter, support), and custom Roles (Support Agent,
Read-only Auditor), so each admin panel screen has real data to test
against instead of an empty list.

Idempotent (skips if 'alice' already exists), documented as workflow
step 3 in DEVELOPMENT.md (renumbering the steps after it) and listed
in AGENTS.md. Verified end-to-end with both scripts against a fresh
server, including idempotency and the resulting UI (Accounts, Groups,
Mailing Lists, Roles all populated as expected).
2026-08-01 22:10:43 +02:00
Steven RYDELL face5b4161 fix(dev): respect $PORT env var in vite.config.ts
Preview/dev tooling that assigns a free port via $PORT (because the
project's usual 5173 is taken) had no way to make Vite actually bind
there — Vite ignores PORT by default and falls back to its own
auto-increment, so the dev server would come up on a different port
than the one the tooling proxied to.
2026-08-01 21:58:34 +02:00
Steven RYDELL 93b10c3ed1 refactor(sort): make client-side column sorting generic, not view-hardcoded
Previous commit hardcoded which lists (viewName === 'x:Account/User' /
'x:Account/Group') and columns (a fixed accessor map) got client-side
sort. Moved the "which columns" decision into the schema itself instead:

- New ClientSortableColumn type in schemaDeviationTypes.ts (intersection
  with the official Column type, same pattern as ClientOnlyFilterEnum —
  src/types/schema.ts stays untouched).
- withAccountListColumns tags Email Address, Full Name, quotaUsage, and
  aliasCount with clientSortable: true when it builds the Accounts/
  Groups column lists — the deviation-specific knowledge lives where the
  columns themselves are defined.
- DynamicList reads that flag generically (clientSortableColumns, a
  useMemo over resolved.list.columns) with no viewName check at all.
  getClientSortValue() replaces the old per-list accessor map: real
  columns compare their own property directly, only the two synthetic
  columns need a value override.

Net effect for Accounts/Groups is unchanged (verified: sort indicators
still only on Email/Full Name/Usage/Aliases, ascending/descending still
works). Any other list can now opt into the same client-sort mechanism
by tagging a column clientSortable, without touching DynamicList.tsx.
2026-08-01 20:32:04 +02:00
Steven RYDELL 518ec4059a feat(accounts): add client-side column sorting to Accounts and Groups
Verified against a live server: x:Account/query rejects sort on every
property tried, including real ones like emailAddress, with
unsupportedSort — the schema's empty list.sort is accurate, this is a
systemic server gap, not something specific to this fork's synthetic
columns.

Added client-side sorting (SCHEMA-DEVIATION: account-client-sort) for
Email Address, Full Name, Usage/Quota, and Aliases on both lists,
reusing the fetch-all-then-sort-locally mechanism already established
for mailbox-client-hierarchy-sort: clicking a sortable header switches
that one query to an unpaginated fetch, sorts the results in memory by
the appropriate accessor (numeric for Usage/Aliases, string compare
otherwise), then paginates client-side. Role isn't included (not a
sortable scalar). Normal server-paginated lists are unaffected — this
only activates when a client-sortable column is actually clicked.

Verified end-to-end: sort indicators appear only on the intended
columns, clicking Email Address/Aliases correctly reorders rows and
toggles ascending/descending, Groups behaves the same as Accounts.
2026-08-01 20:23:42 +02:00
Steven RYDELL 3241dea5e4 feat(accounts): add Aliases count column to Accounts and Groups
Same pattern as the existing quotaUsage deviation: neither list's
schema exposes an alias count as a column, only the full `aliases`
objectList on the detail view. Added a synthetic `aliasCount` column
(SCHEMA-DEVIATION: account-alias-count-column, documented in
SCHEMA_DEVIATIONS.md) that DynamicList resolves by fetching the real
`aliases` property and counting its entries.

Verified against a live test server: adding a real alias to an
account correctly bumps its Aliases count from 0 to 1 in the list.
2026-08-01 20:18:01 +02:00
Steven RYDELL 0aa92c394c feat(accounts): add Usage/Quota column to Groups, show unlimited as ∞
Groups have real usedDiskQuota/quotas fields, same as Users, but the
Groups list schema doesn't expose usage as a column any more than the
Accounts list did — extend the existing account-quota-usage-column
deviation to x:Account/Group too (SCHEMA_DEVIATIONS.md updated).

The isAccountsList gate that resolved/rendered quotaUsage was hardcoded
to viewName === 'x:Account/User', so it silently no-oped for Groups.
Replaced with hasQuotaUsageColumn, derived from whether the resolved
list's own columns include the synthetic quotaUsage column — works for
any list withAccountListColumns patches, not just a hardcoded pair of
view names.

Also: "Unlimited" (no quota configured) now renders as "∞" instead of
the word, matching the size formatting style used elsewhere in the
column.
2026-08-01 20:02:10 +02:00
Steven RYDELL 09910a7bf9 fix(lint): remove unused isBackendIconKnown, unblocking CI
Pre-existing dead code (defined, never imported anywhere) that also
tripped react-refresh/only-export-components since it mixed a
non-component export into a component-only file — eslint treats that
as an error, which was silently failing every CI run's lint step
(including this release build) well before this session's changes.
2026-08-01 19:31:57 +02:00
Steven RYDELL 95053d7dc8 chore: release v1.1.1 2026-08-01 19:24:08 +02:00
Steven RYDELL 52123bce29 fix(assets): use upstream's favicon.ico instead of a mis-cropped fork export
This fork's favicon.ico had drifted from upstream's: same Stalwart logo
and color (#DB2D54) in both, but the fork's version had visible padding
around the logo (trimmed bounding box 120x104 within the 128x128 icon)
while upstream's fills the full canvas edge-to-edge. Also encoded at a
lower bit depth (8-bit indexed vs upstream's 32-bit true color).

Replaced with the exact file from stalwartlabs/webui (md5 e9f5eaa1...,
matches byte-for-byte) rather than re-exporting, so it can't drift
again the same way.
2026-08-01 19:21:19 +02:00
Steven RYDELL 40164a6652 feat(webapps): show and correctly set the active WebUI's resource URL
The "Active WebUI" card (Settings > Web Applications) only showed
description and __APP_VERSION__, never the resourceUrl it's actually
built from — add it as a linked "Source" row.

That card also turned out to be misleading in local dev: it reads the
x:Application record whose urlPrefix matches /admin or /account, and a
freshly bootstrapped server keeps Stalwart's seeded default there
("Stalwart Web Interface", pointing at stalwartlabs/webui's release),
regardless of what's actually being served — in dev that's this fork,
served directly by Vite, which never touches that record at all.

dev-server-init.sh/.ps1 now point that record's description and
resourceUrl at this fork's own release, so the card (now including the
visible Source URL) reflects what's actually running instead of
Stalwart's factory default.

Verified end-to-end against a fresh container with both scripts.
2026-08-01 19:17:44 +02:00
Steven RYDELL 7fab3540ea fix(ui): keep mobile sidebar open when switching sections
Tapping Management/Settings/Account in the sidebar footer navigated to
that section's default page, which triggered the same effect that
closes the sidebar after picking a leaf page on mobile — so the
sidebar snapped shut right after switching sections, forcing users to
reopen it just to browse the new section's pages.

Have handleSectionClick flag the navigation as section-only via a ref;
the mobile auto-close effect consumes that flag and skips closing for
that one navigation, leaving normal leaf-page navigation unaffected.

Verified on a mobile viewport: switching sections now keeps the
sidebar open, while picking a specific page still closes it as before.
2026-08-01 19:07:22 +02:00
Steven RYDELL 18264a5b76 docs(dev): add troubleshooting section, fix stale "one command" claim
Add a Troubleshooting section to DEVELOPMENT.md covering the case that
just happened: npm run dev only reads .env.development.local at
startup, so regenerating a token or restarting the Docker container
while it's already running silently leaves the UI unable to reach the
backend until it's restarted.

README.md's "one command" claim for spinning up the test server was
stale since dev-server-init.sh became a separate one-time step.
2026-08-01 18:39:33 +02:00
Steven RYDELL adca6d8730 feat(dev): switch dev tokens to a real admin account with configurable duration
Requested: dev-token.sh/.ps1 tokens should last 3h by default, with an
argument to override the duration.

Server-verified finding: STALWART_RECOVERY_ADMIN (the break-glass
account docker-compose.yml and the scripts used) always issues OAuth
tokens with a fixed 1h expiry, regardless of the server's configured
accessTokenExpiry — confirmed against the live container, including
after changing the setting and restarting. Confirmed x:ApiKey objects,
by contrast, support an arbitrary expiresAt set per request, and their
secret works directly as a bearer token.

Add scripts/dev-server-init.sh (+ .ps1): a one-time, idempotent setup
step that completes the server's bootstrap wizard (default domain, no
TLS certificate request), creates a real "devadmin" admin account, and
sets the server's default OAuth token lifetime to 3h.

Rework dev-token.sh/.ps1 to authenticate as devadmin and create an
x:ApiKey with a caller-supplied expiry (`dev-token.sh 1800` for 30
minutes, defaults to 10800s/3h) instead of running the OAuth PKCE flow
against the recovery account. Verified end-to-end against a fresh
container, including a real browser session against the running WebUI.

Also includes an incidental package-lock.json sync (was still pinned to
v1.0.8 / stale dependency ranges from before the upstream merge).
2026-08-01 18:32:13 +02:00
Steven RYDELL f4c8f8f21c feat(dev): add one-command local Stalwart test server + workflow docs
Add docker-compose.yml: a disposable Stalwart instance with a fixed dev
admin account (STALWART_RECOVERY_ADMIN), matching the credentials
scripts/dev-token.ps1 already expected. Wired up via new npm scripts
(dev:server, dev:server:down, dev:server:logs).

scripts/dev-token.ps1 was previously untracked (the whole scripts/
directory was gitignored) even though it's part of the documented dev
workflow — un-ignored it, and added scripts/dev-token.sh, a POSIX
equivalent for non-Windows shells and AI agents without PowerShell.

New DEVELOPMENT.md documents the full loop end-to-end (start server,
get a token, run the dev server, verify), written so it's actionable by
both humans and AI coding agents without needing a browser. Linked from
AGENTS.md (Commands) and README.md (Getting started).

Verified manually: docker compose up brings the server to a healthy
state, /api/auth + /auth/token issue a working bearer token, and
/jmap/session returns 200 with it end-to-end.
2026-08-01 18:05:43 +02:00
Steven RYDELL 38abba39f8 docs(readme): document fork identity, deviations policy, and UI switching
Rewrite everything below the project header to actually describe this
fork instead of a generic copy of upstream's README:

- "About this fork" + list of official Stalwart repositories.
- Corrected the "Nothing is hardcoded" overclaim; Features now lists
  what's shared with upstream vs. this fork's own additions, and links
  to SCHEMA_DEVIATIONS.md for the tracked exceptions.
- New "Switching your server to this fork's UI" section: how to point
  a Stalwart server's WEBAPP application at this fork's release build
  via stalwart-cli, and how to switch back.
- Support section now distinguishes fork-specific issues (this repo)
  from Stalwart Mail Server support (upstream channels).
- License/Copyright text left untouched; added one line noting fork
  changes stay under the same dual license per each file's SPDX header.

The project header block (logo, title, badges) is unchanged.
2026-08-01 17:54:24 +02:00
Steven RYDELL 101b24b361 fix(schema): track client-side schema deviations, restore schema.ts fidelity
src/types/schema.ts had drifted from the official schema contract: a
`clientOnly` field had been added to `FilterEnum` to support client-side
log filtering. Restored it to match upstream exactly and moved the
deviation-only type into a new intersection type in
src/lib/schemaDeviationTypes.ts instead.

Audited the codebase for every place the UI does something the official
schema doesn't support and tagged each with `// SCHEMA-DEVIATION: <id>`,
documented in the new SCHEMA_DEVIATIONS.md registry (what, why, and the
ideal server-side fix):

- log-client-filters: Level/Event filters on Log Entries, applied
  client-side because x:Log/query rejects them as JMAP filters.
- account-quota-usage-column: synthetic quotaUsage column on the
  Accounts list.
- mailbox-client-hierarchy-sort: full-fetch + client-side sort to
  reconstruct mailbox parent/child hierarchy.
- webapp-enabled-column-fallback: synthetic "Enabled" column label for
  x:Application when the schema list doesn't define one.

Other viewName/objectName special cases (x:OtpAuth, x:Expression,
x:Rate, x:Action, x:Trace, CustomComponent/*) were checked against
upstream and are pre-existing architecture, not fork deviations.
2026-08-01 17:54:08 +02:00
Steven RYDELL 4a6340e83f chore(agents): add AGENTS.md/CLAUDE.md rules for AI coding agents
Any AI agent working in this repo (Claude Code, Codex, Kimi, or any
other) must stay schema-driven and never hardcode object types, field
names, filters, or columns as a shortcut. AGENTS.md is the index (tech
stack, commands, universal rules); CLAUDE.md just points Claude Code at
it so the same rules apply without duplicating content. The detailed
schema-fidelity rule lives in .agents/rules/.

.gitignore previously excluded *.md except README/CHANGELOG, which
would have hidden these files (and SCHEMA_DEVIATIONS.md) from git.
2026-08-01 17:53:04 +02:00
Steven RYDELL 137e6c6ed9 fix(merge): remove upstream loadLogoOnce leftover, restore schema.ts formatting
main.tsx no longer needs an eager logo fetch call: Logo.tsx already
triggers ensureLogoLoaded() from logoCache.ts (the fork's own
implementation, kept over upstream's during the sync merge).
2026-08-01 17:00:12 +02:00
Steven RYDELL 4c953e599d Merge remote-tracking branch 'upstream/main' into sync-upstream
# Conflicts:
#	CHANGELOG.md
#	package-lock.json
#	package.json
#	src/components/common/CommandPalette.tsx
#	src/components/common/LoadingFallback.tsx
#	src/components/common/Logo.tsx
#	src/components/forms/DynamicForm.tsx
#	src/components/forms/FieldWidget.tsx
#	src/components/layout/MainContent.tsx
#	src/components/layout/Sidebar.tsx
#	src/components/layout/TopBar.tsx
#	src/components/lists/DynamicList.tsx
#	src/components/ui/calendar.tsx
#	src/components/ui/command.tsx
#	src/components/ui/dialog.tsx
#	src/hooks/useDocumentTitle.ts
#	src/hooks/useGlobalSearch.ts
#	src/i18n/en.json
#	src/lib/lastVisited.ts
#	src/lib/logoCache.ts
#	src/pages/AdminPanel.lazy.tsx
#	src/pages/AdminPanel.tsx
#	src/pages/LoginPage.tsx
#	src/pages/NotFound.tsx
#	src/pages/OAuthCallback.tsx
#	src/stores/authStore.test.ts
#	src/stores/authStore.ts
#	src/stores/cacheStore.ts
#	vite.config.ts
2026-08-01 16:59:31 +02:00
Maurus Decimus 8cab61a9c5 v1.0.8 2026-07-31 15:52:36 +02:00
Steven RYDELL a4b324bb8b fix(ui): prefill empty date/time picker with now 2026-07-30 20:15:07 +02:00
Steven RYDELL 08382472f4 fix(ui): theme date/time picker for dark mode 2026-07-30 20:08:10 +02:00
Steven RYDELL 75823ebb21 chore: release v1.1.0 2026-07-30 19:55:14 +02:00
Steven RYDELL 0dec14c3f8 feat(accounts): highlight negative disk usage with recalculate hint 2026-07-30 19:54:46 +02:00
Steven RYDELL 1ab574a42a fix(ui): make admin lists usable on mobile viewports 2026-07-30 19:54:34 +02:00
Steven RYDELL d1284de415 feat: add iOS home-screen icon and Stalwart title without service worker 2026-07-30 18:56:29 +02:00
Steven RYDELL 3189be2de0 refactor(logo): encapsulate shared logo cache with AbortController cleanup 2026-07-30 18:49:35 +02:00
Steven RYDELL eda9c9c8bf fix: remove PWA service worker that breaks Stalwart /admin and /account mounts 2026-07-30 18:30:36 +02:00
Steven RYDELL 4e089d9b6b fix(appearance): keep Rounded corners preview curved in square mode 2026-07-30 18:09:51 +02:00
Steven RYDELLandClaude Sonnet 5 8b5b9c8f8e docs: add v1.0.9 CHANGELOG entry
Documents everything since the v1.0.8 entry: icon batches, the Web
Applications active-app info card, PWA support, Log Entries filters
and refresh button, Accounts list columns, the account-switch fixes
(reviewed against and aligned with upstream's own fix for the same
issue), the WebUI Fork rename, mailbox hierarchy, new color themes,
and the confirmed-backend-only limitations (Log filter properties,
exact-match-only text search across the admin API).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 17:51:04 +02:00
Steven RYDELLandClaude Sonnet 5 bd00b4e922 fix: persist active account across reload, remount views on switch
Aligns with upstream stalwartlabs/webui@189e270 (v1.0.7, "fixes #17"),
reviewed after they independently landed a fix for the same issue:

- authStore now persists activeAccountId (sessionStorage) and
  setSession preserves it across a session refresh instead of always
  resetting to primaryAccountId, so a hard reload keeps you on the
  group account you had selected instead of bouncing back to your own.
- switchAccount now clears cacheStore (displayNames/objectLists) when
  actually changing account, since those were resolved against the
  previous account and would otherwise show stale labels.
- The ErrorBoundary wrapping MainContent is now keyed on
  activeAccountId, forcing a full remount of every view on switch.
  This is more robust than gating individual components' fetch
  effects on activeAccountId (our earlier fix in DynamicList.tsx,
  kept as-is — harmless now, but no longer load-bearing on its own)
  since it covers every current and future view type, not just lists.

Verified against a live instance: switching to a group account updates
the JMAP accountId immediately (no tab switch needed), and a full page
reload keeps the group account active instead of resetting to admin.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 17:48:19 +02:00
Steven RYDELLandClaude Sonnet 5 8c1e826d63 feat: add Rose/Amber/Teal color themes, default to Ocean + Square
Adds three new color themes alongside the existing Stalwart/Ocean/
Forest/Violet set so more people can find one they like, and switches
the out-of-the-box defaults to Ocean + square corners instead of the
neutral Stalwart theme + rounded corners (existing users' saved
preferences are untouched, this only changes what a fresh install
starts with).

Verified in the browser: all three new themes render with legible
primary-foreground contrast in both light and dark mode, and the
pre-hydration flash-prevention script in main.tsx (which reads
localStorage before React mounts) now recognizes the new theme names.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 17:38:40 +02:00
Steven RYDELLandClaude Sonnet 5 7a96fa6786 feat: show mailbox hierarchy in the Mailboxes list
Fixes stalwartlabs/webui#16. Mailboxes had no visual indication of
parent/child relationships. Since a mailbox's parent can land on a
different server page than the mailbox itself, the Mailbox list now
always fetches the full set (like the existing client-filter path)
and orders it depth-first by parentId, then indents each row's name
with a corner connector proportional to its depth.

Verified against a live instance with a 3-level nested hierarchy
(Projects > Alpha > Docs) alongside the default flat mailboxes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 17:27:48 +02:00
Maurus Decimus 189e270785 v1.0.7 (fixes #17) 2026-07-30 17:16:05 +02:00
Steven RYDELLandClaude Sonnet 5 3e08dcaff5 chore: rename WebUI label to "Stalwart WebUI Fork"
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 17:07:44 +02:00
Steven RYDELLandClaude Sonnet 5 2ac3526da9 fix: refresh account-scoped lists when switching accounts
Switching accounts via the profile dropdown (TopBar) only updates
authStore.activeAccountId with no navigation, but DynamicList's fetch
effect depended on [viewName, sort, resolved?.list, appliedFilters] —
missing activeAccountId, and account resolution only happened through
a non-reactive getState() snapshot inside getAccountId(). So views
like Mailboxes, Calendars, or Sieve Scripts kept showing the previous
account's data until an unrelated viewName change (switching tabs)
incidentally re-ran the effect.

Reproduced and verified against a live Stalwart instance: a mailbox
list stayed on accountId "b" after switching to a group account "d"
in the dropdown, and only picked up "d" after navigating away and
back. Subscribing to activeAccountId reactively and adding it to the
effect's dependencies fixes the switch to apply immediately.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 17:05:12 +02:00
Steven RYDELLandClaude Sonnet 5 3dda6d1527 feat: show Role and Usage/Quota columns on the Accounts list
Created At is replaced with two columns that previously required
opening each account individually: Role (badge, resolved against
x:UserRoles specifically since the list's merged User/Group field
definitions otherwise resolve `roles` against the wrong object) and
Usage / Quota (usedDiskQuota vs quotas.maxDiskQuota, "Unlimited" when
no limit is set).

Also makes renderCellValue's generic 'object' case resolve variant
labels via schema.schemas, instead of only ever printing the raw
@type string.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 16:47:26 +02:00
Steven RYDELLandClaude Sonnet 5 3113e6309d feat: add rate-limited manual refresh button to Log Entries
Adds a Refresh button next to Filters, right-aligned, specific to the
Logs list. Guarded to one click per 5 seconds (button disabled and
re-enabled via a timer) to avoid hammering the server if left clicked
repeatedly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 16:36:25 +02:00
Steven RYDELLandClaude Sonnet 5 21b183327c feat: add client-side Level/Event filters to Log Entries
The backend rejects `level`/`event` as x:Log/query filter conditions
(unsupportedFilter), even though both are already returned per row.
Since a real fix requires backend changes outside this repo, the two
filters are injected into the schema client-side (clientOnly flag) and
applied entirely in the browser: excluded from the JMAP filter sent to
the server, and used to narrow an eagerly-fetched, locally-paginated
result set instead.

Also makes large enum filters (Event has 634 values) render as a
searchable Combobox instead of a plain Select, generically for any
list with more than 15 enum options.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 16:31:36 +02:00
Steven RYDELLandClaude Sonnet 5 da2c99990f feat: add PWA support (manifest, service worker, icons)
Adds vite-plugin-pwa with a generated manifest, icon set derived from
the Stalwart mark (192/512/maskable/apple-touch), and theme-color meta
tags. /api and /jmap are excluded from the service worker's navigate
fallback and are never precached or runtime-cached, so authenticated
mail data can't end up in Cache Storage.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 16:06:59 +02:00
Steven RYDELL 25a38cd560 fix: prevent custom logo blink with global loading state 2026-07-30 15:42:27 +02:00
Steven RYDELL fe24a9bec0 chore: upgrade Vite to 8.2.0 and @vitejs/plugin-react to 6.0.5 2026-07-30 15:42:16 +02:00