Phase 1 step 7 of VNCprodbuild. integration/tests/11-electron-notification.spec.ts
launches the actual Electron shell, logs in as alice against this repo's
existing docker-compose Stalwart fixture, injects a message over real SMTP
(same helpers/smtp.ts sendMail() 02-mail-sync.spec.ts uses), and asserts a
native notification fires via electron/main.ts's __notificationCallCount
test hook - proving the full real pipeline, not just the synthetic IPC call
step 3's smoke test exercises: SMTP -> Stalwart -> JMAP push
(lib/jmap/client.ts) -> stores/email-store.ts's handleStateChange ->
handleNewEmailNotification -> the page effect -> lib/electron-bridge.ts ->
the contextBridge/IPC bridge -> electron/main.ts's Notification call.
Runs against a `next dev` server (electron/main.ts's new ELECTRON_LOAD_URL
escape hatch), not the standalone build, because this fixture's Stalwart is
deliberately plain HTTP and production's CSP correctly refuses non-TLS
connections - the identical trade-off integration/webmail.Dockerfile already
makes for the browser-based suite. New playwright.integration-electron.config.ts
+ global-setup-electron.ts (brings up only the `stalwart` compose service,
not `webmail`, which this suite never touches and which may not even be
startable on a given host - see its own header comment) keep this fully
separate from the main dockerized integration run, which has no Electron
binary compatible with that container's platform; playwright.integration.config.ts
gets a matching testIgnore so a plain `npm run test:integration` never tries
to sweep this file in. Wired as `npm run test:integration:electron`.
On "the real WebSocket path": confirmed against this fixture's actual
`stalwartlabs/stalwart:v0.16` (same as the sandbox server) that its
/jmap/ws requires the same Authorization header as every other JMAP
endpoint on the handshake itself, which the browser WebSocket API cannot
attach - so the WS attempt reaches the network correctly (see the CSP fix
in the previous commit) but always fails auth here, and the circuit
breaker falls back to SSE within about a second. That fallback is what
delivers the push this test observes - documented in detail in the spec's
header comment, including why asserting the WS handshake itself succeeds
here would be asserting something that cannot be true from a browser
against this specific server.
Known flakiness, root-caused not eliminated (see
playwright.integration-electron.config.ts's retries: 2 and its comment):
`next dev`'s on-demand route compilation + Fast Refresh occasionally races
the SSE stream during the login -> inbox transition and drops that one push
event with no error anywhere - reproduced by running the identical test
repeatedly against an already-warm stack (IT_NO_DOCKER=1): identical
request sequence logged every time, but the outcome wasn't always the same.
This is specific to the dev-server workaround this test needs for the
plaintext-Stalwart fixture, not a bug in the feature it's verifying - the
WS circuit breaker and SSE fallback fire exactly as designed in every run's
own logs, pass or fail.
Verified: passed cleanly standalone multiple times; with retries: 2 in
place, passed within the retry budget on every attempt made.
Follow-up to 218a584f - these edits (electron npm scripts, "main" field,
electron/electron-builder/electron-updater deps, dist-electron/**
gitignore) were made alongside that commit but got left unstaged when it
landed. No behavior change beyond what that commit already described.
Remove withMicrofrontends wrap + @vercel/microfrontends dep. Grouping is
organizational (separate Vercel team), not a microfrontends group. App
serves at its own root again (NEXT_PUBLIC_BASE_PATH removed on Vercel).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wrap next.config with withMicrofrontends; add @vercel/microfrontends.
Served at /mail under the suite shell (via NEXT_PUBLIC_BASE_PATH set on the
Vercel project). Logged in vnc/VNC-CHANGES.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- tailwind.config.ts is not actually being used so removed
- moved animate-fade-in to globals.css
- added tw-animate-css package to make animate-in work
- added a definition for animate-shake as it doesn't exist in tw-animate-css
Add an end-to-end integration harness that runs the webmail against a real
Stalwart mail server in Docker and drives it with Playwright, focused on the
mail/folder synchronisation behaviour (unread/total counters, folder-list
sync, account-scoped Unified Mailbox) that the unified-mailbox work touches.
- integration/ stack: Stalwart (declarative bootstrap: alice/bob/carol,
submission + IMAP listeners, permissive CORS) + webmail (dev mode, so the
browser's plaintext cross-origin JMAP calls aren't blocked by the prod CSP).
- Helpers: dependency-free SMTP submit client, JMAP client for seeding /
inspecting server state, and page helpers (login, add/switch account,
locale-independent folder-counter reads).
- Specs: login, single-account sync (receive/read/move/delete/folder-create/
burst) and multi-account (per-account isolation + cross-account Unified
Inbox aggregation + background-account delivery). 12 tests, all green.
- Add focused data-testid hooks to the mail UI (folder rows + counters, email
list items, account switcher, composer) for stable selectors.
- Exclude examples/ and integration/ from tsconfig/eslint/.dockerignore.
* feat: add Jalali (Persian/Shamsi) calendar support with Saturday as week start
- Add jalaali-js library for Gregorian ↔ Jalali date conversion
- Create lib/jalali-utils.ts with Jalali calendar utilities
- Create hooks/use-calendar-locale.ts for unified calendar locale handling
- Expand FirstDayOfWeek type to include 6 (Saturday)
- Update all calendar views (month, week, day, mini, toolbar) to support
Jalali calendar display and Saturday-first week ordering
- Add Jalali month names (Farvardin … Esfand) to all locale files
- Add Persian (fa) locale with full translations
- Update settings UI to include Saturday as first day of week option
- Update useFormatEventDate to show Jalali dates when locale is fa
- Auto-detect Jalali calendar when fa locale is active
The calendar system automatically switches to Jalali when the locale is
set to Persian (fa). All internal date handling remains Gregorian (ISO
8601) for JMAP protocol compatibility; Jalali conversion is purely at
the display layer.
* Add PR template for Jalali calendar feature
* chore: remove accidentally added PR template
* fix: add image_too_large key to fa locale for PR #462 compatibility
Next 16 removed the `next lint` subcommand, so `npm run lint` failed with
"Invalid project directory provided, no such directory: .../lint". Point the
lint and lint:fix scripts at ESLint directly, using the existing flat config
(eslint.config.mjs).
- MIME: Stalwart's download endpoint often returns application/octet-stream, so
blob: previews silently downloaded (UUID filename) instead of rendering.
Resolve the most specific MIME (attachment type -> filename ext -> blob type)
and re-wrap the blob; also fixes inline preview for images and video.
- Desktop PDF: render via <iframe> (reliable for blob: PDFs) instead of <object>.
- Mobile PDF: no usable inline viewer (Android shows a blank frame / silent
download; iOS Safari renders only the first page of a PDF in an <iframe>), so
render with pdf.js (canvas, dynamic-imported so it stays off the desktop
bundle; iOS-safe canvas cap). Double-tap zoom (fit -> 2x -> 3x -> fit) and
2-finger pinch zoom (to 4x), both centred on the gesture and pannable via
native scrolling.
- Route to pdf.js when navigator.pdfViewerEnabled is false (Android) and on iOS
(incl. iPadOS, which reports true yet shows only the first page in a frame).
- Modal header gains an open-in-new-tab icon (next to download/close); the
Android/browser Back button closes the preview instead of navigating the page.
- On a pdf.js render failure, offer an open-in-new-tab action as fallback.
Drops the 0.15 REST management API and routes all account/auth/crypto/
principal operations through Stalwart 0.16's schema-driven JMAP
endpoint via a single passthrough (/api/account/stalwart/jmap).
- New client helper `stalwartJmap` + typed `requireResult`
- account-security-store rewritten against x:AccountPassword, x:AppPassword,
x:AccountSettings, x:Account (with currentSecret for TOTP ops)
- Client-side TOTP setup via `otpauth`; server-generated app password
secrets shown once on create
- Admin check switched to /api/account permissions
(sysAccountQuery/sysTenantQuery/sysSystemSettingsGet)
- Removed sieve vacation-overwrite workaround (fixed upstream #1251)
- Deleted old REST routes, StalwartClient, stale tests; added new
tests for passthrough + store