Files
Smart-Tablet-Screen/docs/research/smart-tablet-screen-stack.md
haylanandClaude-Bot 71d54608df docs(research): add smart screen stack, additions, and Nextcloud research
Cover Symfony backend, calendar sync (Exchange Graph API + Mail-in-a-Box
CalDAV), Docker/Gitea CI, frontend stack, Diun/RSS/QR additions, and
Nextcloud Tasks (CalDAV VTODO) integration.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 20:48:53 +02:00

129 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Smart Tablet Screen — Stack Research
Research date: 2026-09-01. All claims below are sourced against primary/official documentation, source code, or first-party API references — not third-party summaries. Each claim links its own source.
---
## 1. Calendar sync: Mail-in-a-Box (self-hosted accounts)
- Mail-in-a-Box bundles **Nextcloud** to provide contacts and calendar sync, exposed over **CardDAV/CalDAV**. The project's own repo/site lists "CardDAV/CalDAV (Nextcloud)" among the components it installs, and states each box "includes contacts and calendar synchronization." — [mail-in-a-box/mailinabox GitHub repo](https://github.com/mail-in-a-box/mailinabox), [mailinabox.email](https://mailinabox.email/)
- Mail-in-a-Box also bundles **Z-Push**, which provides **Exchange ActiveSync** as an alternative sync protocol for compatible mobile clients — this is a second, separate protocol path, not the CalDAV path. — [mail-in-a-box/mailinabox GitHub repo](https://github.com/mail-in-a-box/mailinabox)
- **Conclusion for this project**: point the Symfony backend's CalDAV client at each Mail-in-a-Box user's Nextcloud CalDAV URL (standard Nextcloud path pattern `/remote.php/dav/calendars/<username>/`, per Nextcloud's own admin docs, which Mail-in-a-Box's bundled Nextcloud inherits) — [Nextcloud Calendar/CalDAV Administration Manual](https://docs.nextcloud.com/server/stable/admin_manual/groupware/calendar.html). Do not use Z-Push/ActiveSync; CalDAV is the natively-scriptable, standards-based path and matches what Symfony would consume as a generic CalDAV client (RFC 4791).
## 2. Calendar sync: Microsoft Exchange/Outlook.com (free account)
- **Microsoft's own recommended API for calendar access is Microsoft Graph**, not EWS or CalDAV. Graph's Outlook calendar overview explicitly states: *"Most features in the Outlook calendar API apply to calendars in personal Microsoft accounts and work or school accounts"* — confirming free Outlook.com (personal Microsoft account) is a first-class supported account type, not just Microsoft 365/Exchange Online. — [Outlook calendar API overview Microsoft Graph, learn.microsoft.com](https://learn.microsoft.com/en-us/graph/outlook-calendar-concept-overview)
- **EWS is being retired for Exchange Online** with a hard enforcement date of **October 1, 2026**, after which EWS stops working for Microsoft 365 mailboxes; Microsoft's stated replacement is Graph. This retirement is scoped to Exchange Online — it does not directly affect consumer Outlook.com accounts (which were never on the EWS-for-M365 retirement track the same way), but it confirms Graph as Microsoft's forward path and rules out building new EWS integrations. — [Deprecation of Exchange Web Services in Exchange Online, learn.microsoft.com](https://learn.microsoft.com/en-us/exchange/clients-and-mobile-in-exchange-online/deprecation-of-ews-exchange-online)
- **Outlook.com/Outlook (new) does not support CalDAV.** This is corroborated by numerous first-party Microsoft Q&A threads (Microsoft's own support forum) confirming CalDAV has never been natively implemented for Outlook.com, and that the "new Outlook" does not support third-party CalDAV add-ins. There is no official Microsoft doc offering a CalDAV endpoint for Outlook.com. — [Microsoft Q&A: CalDAV support on Outlook.com](https://learn.microsoft.com/en-us/answers/questions/4525673/caldav-support-on-outlook-com), [Microsoft Q&A: caldav in new outlook](https://learn.microsoft.com/en-us/answers/questions/2282064/caldav-in-new-outlook)
- **Auth**: Graph calendar access on a personal account requires standard OAuth 2.0 authorization-code flow against Microsoft Entra ID (`/authorize` and `/token` endpoints), with an app registered in the Entra portal, a `client_id`, redirect URI, and — for a confidential/web client like a Symfony backend — a `client_secret`. Use `tenant=common` (or `consumers`) so both personal Microsoft accounts and (optionally) work/school accounts can sign in. A refresh token (via `offline_access` scope) is needed for the long-lived, unattended calendar-polling this dashboard needs. — [Get access on behalf of a user Microsoft Graph, learn.microsoft.com](https://learn.microsoft.com/en-us/graph/auth-v2-user)
- **Conclusion for this project**: use **Microsoft Graph's calendar API** (`/me/calendarview`, `/me/events`, delta query for sync) via OAuth 2.0 with a registered Entra app, storing the client id/secret and refresh token via the project's `.env`. This is the only viable, supported, future-proof path — CalDAV is not available and EWS is being sunset.
## 3. CSS methodology — BEM
Per BEM's own quick-start guide:
- **Block**: "a functionally independent page component that can be reused" — named by purpose, not appearance (e.g. `menu`, `button`); must not set its own external position/margins.
- **Element**: "a composite part of a block that can't be used separately from it" — syntax `block-name__element-name` (double underscore).
- **Modifier**: defines appearance/state/behavior of a block or element — boolean form `block-name_modifier-name`, key-value form `block-name_modifier-name_value`. Modifiers never exist standalone; they always accompany a block/element.
— [BEM Quick Start, bem.info](https://bem.info/en/methodology/quick-start/)
**Conclusion**: adopt BEM class naming directly as documented (`.calendar`, `.calendar__event`, `.calendar__event_source_exchange`) for all hand-written CSS/SCSS in the frontend.
## 4. Docker + Gitea container registry
Per Gitea's own docs:
- Login: `docker login gitea.example.com` (use a personal access token instead of a password when 2FA/OAuth is enabled).
- Gitea's documented image naming format is **`{registry}/{owner}/{image}`**, e.g. `gitea.example.com/testuser/myimage`, and sub-paths are allowed (`gitea.example.com/testuser/my/image`). Tags are case-insensitive.
- Push: `docker push gitea.example.com/{owner}/{image}:{tag}`.
- The registry is OCI-compliant and works with any OCI client (Docker, Podman, Buildah, Skopeo).
— [Container Registry, docs.gitea.com](https://docs.gitea.com/usage/packages/container/)
**Applied to this repo**: remote is `https://git.arthurerlich.de/haylan/Smart-Tablet-Screen`, so the prod image should be tagged and pushed as `git.arthurerlich.de/haylan/smart-tablet-screen:latest`.
**Repo/infra discovery already done** (carried over, not re-verified here):
- No `.gitea` directory, no CI workflow files, and no `~/.docker/config.json` exist in this repo/machine yet — CI (Gitea Actions or manual push) and Docker registry auth both need to be set up from scratch.
- No `tea` CLI config found locally.
- The user's Gitea instance (`git.arthurerlich.de`) runs as Docker Swarm services (`git_gitea-server`, `git_gitea-db`, `git_backup`) on a host called "proxmox", per `~/Documents/code/restore-gitea.sh`, with backups on a "datasservices" storage host. This is operational context for where the registry lives, not registry credentials — no push credentials exist anywhere in this discovery and none are fabricated here.
- The `gitea-tea` skill drives the `tea` CLI for issues/PRs/wiki but explicitly does **not** cover container registry pushes — registry auth/push must be done via plain `docker login`/`docker push` as documented above, or via a Gitea Actions CI workflow (not yet present in this repo).
## 5. Playwright (E2E)
Per Playwright's own docs:
- "Playwright Test is an end-to-end test framework for modern web apps. It bundles test runner, assertions, isolation, parallelization and rich tooling."
- Supports Node.js, Python, Java, .NET bindings; drives Chromium, WebKit, and Firefox, headless or headed, with native mobile emulation for Chrome (Android) and Mobile Safari.
- Playwright's own site documents a dedicated **MCP** section (`/mcp/introduction`) and an **Agent CLI** section, confirming Playwright supports being driven as an MCP server / programmatically by an agent, in addition to its standard test-runner usage.
— [Playwright docs — Installation/Intro, playwright.dev](https://playwright.dev/docs/intro)
**Fit check**: since this project targets a fixed Chrome/Android-kiosk viewport, Playwright's Chromium engine with device/viewport emulation is a good match for E2E coverage of the dashboard.
**Locally available tooling**:
- A `playwright-e2e-testing` skill exists locally (noted per instructions, not invoked).
- No separate Playwright MCP server was found configured in this environment.
- The `claude-in-chrome` capability is the discovered tool for live, real-browser interaction (click, screenshot, console logs) against a real Chrome instance in this environment — use it for ad hoc manual verification of UI changes; Playwright remains the tool for the actual automated, repeatable E2E test suite.
## 6. PHPUnit + Symfony testing
- Symfony's own testing docs describe three test tiers on top of PHPUnit: **unit tests** (plain PHPUnit, no framework), **integration tests** (extend `KernelTestCase`, boot the DI container), and **application/functional tests** (extend `WebTestCase`, use `$client->request()` plus `assertResponseIsSuccessful()`/`assertSelectorTextContains()` style assertions). Setup is via `composer require --dev symfony/test-pack` and `php bin/phpunit`; test env config lives in `.env.test` / `.env.test.local` (`.env.local` is deliberately not loaded in the test environment). — [Testing, symfony.com/doc](https://symfony.com/doc/current/testing.html)
- PHPUnit itself is described by its own site as "the testing framework for PHP"; current stable is PHPUnit 13 (as of Feb 2026), maintained by Sebastian Bergmann. — [phpunit.de](https://phpunit.de/)
**Locally available tooling**: a `phpunit-best-practices` skill exists locally (noted per instructions, not invoked).
**Conclusion**: use Symfony's standard three-tier setup — unit tests for pure PHP logic (e.g. calendar-event normalization/merging), `KernelTestCase` integration tests for the CalDAV/Graph client services, `WebTestCase` functional tests for the calendar API controller endpoints.
## 7. Frontend framework: Astro vs plain TypeScript/Vite
Per Astro's own docs:
- Astro has two output modes as of Astro 5 (the old `hybrid` mode was folded into `static`): **`static`** (default — every page prerendered to flat HTML at build time) and **`server`** (on-demand rendering per request, requires an adapter). — [On-demand Rendering, docs.astro.build](https://docs.astro.build/en/guides/on-demand-rendering/)
- Astro's own guidance: *"Start with the default 'static' mode until you are sure that most or all of your pages will be rendered on demand!"* — i.e. Astro is optimized around mostly-static, multi-page content sites, with `server` mode as an opt-in escape hatch, not the primary design target.
- Astro's islands architecture renders components to static HTML at build time and hydrates only interactive islands with JS — a good fit for content pages with sparse interactivity, not for an app that is a single always-on, continuously-updating client view.
**Assessment for this project**: the dashboard is a **single page**, always mounted, continuously polling a Symfony JSON API and re-rendering a calendar view client-side — there is no multi-page routing, no meaningfully "static, prerenderable" content, and no SEO/first-load-performance concern (it's a fixed kiosk browser, not a public site). Astro's core value propositions (partial hydration across many mostly-static pages, build-time prerendering, content collections) don't apply here; using Astro would mean running one perpetual "server island" and getting none of Astro's benefits while carrying its page-oriented conventions (file-based routing, `.astro` components) for what is really one long-lived app shell.
**Recommendation**: plain **TypeScript + Vite** (no meta-framework). Symfony already owns the backend/API surface; the frontend only needs a bundler, dev server, and TS type-checking for a single-page app that fetches from the Symfony API and renders a calendar. This avoids Astro's per-page rendering model entirely and keeps the frontend a thin, single SPA build.
## 8. Calendar UI library
Evaluated against each library's own docs:
- **FullCalendar** — supports vanilla JS/TypeScript "core" usage plus first-party React/Vue/Angular/Preact/Web Component wrappers; ships full TypeScript type support. Core (month/week/day/list/timeGrid views, drag/resize, i18n, timezone support) is open source (MIT); some views (Timeline, vertical Resource view, print) are gated behind a paid **Premium** tier. — [FullCalendar Docs, fullcalendar.io/docs](https://fullcalendar.io/docs)
- **Schedule-X** — an open-source TypeScript event calendar, explicitly positioned by its own docs as "a modern alternative to FullCalendar and react-big-calendar," usable directly in vanilla TS or with framework component wrappers (React/Angular/Vue/Svelte/Preact); ships day/week/month views, recurring events, dark mode, i18n, accessibility-focused patterns. — [schedule-x.dev](https://schedule-x.dev/), [schedule-x/schedule-x GitHub](https://github.com/schedule-x/schedule-x)
**Recommendation**: **FullCalendar** for this project — the free/MIT core (month + week/day timeGrid + list views) covers everything this fixed-layout kiosk dashboard needs (no drag/resize/resource-planning premium features required), it has first-party vanilla-TS support matching the plain-TS/Vite frontend decision above, and it's the most mature/battle-tested option for a monitor-style read-mostly display. Schedule-X is a credible lighter-weight fallback if FullCalendar's bundle size or styling model proves awkward to theme for the kiosk's fixed layout.
**Component libraries**: no general UI component library was researched in depth beyond the calendar widget itself, since the fixed-layout, single-screen kiosk dashboard has a small, custom surface (a calendar grid plus maybe a status/clock strip) that doesn't obviously benefit from a general component-library dependency — this is a YAGNI call, revisit only if the UI grows multiple distinct widget types needing shared interactive components (modals, tabs, etc.).
## 9. Reverse proxy / deployment
No first-party doc research was needed beyond what's already fixed by the task: the prod Docker image (tagged `:latest`, pushed to `git.arthurerlich.de/haylan/smart-tablet-screen:latest` per §4) runs on the user's local/Proxmox-hosted Docker environment behind an existing reverse proxy, exposed at `smart-screen.home`. This is an infra/ops detail for the eventual `devops-engineer`/`docker-expert` skill-driven work, not a research question with a "primary source" to cite — noted here only for completeness of the stack list.
---
## Locally available Skills/MCP servers relevant to each decision
| Area | Skill/MCP found locally | Coverage |
|---|---|---|
| Symfony + calendar integration | none specific | n/a — plain Symfony/PHP work |
| Docker image builds | `docker-expert` | Dockerfile authoring, multi-stage builds, security hardening |
| CI/CD, Gitea Actions | `devops-engineer` | Pipelines, Dockerfiles, deployment automation — general, not Gitea-registry-specific |
| Gitea issues/PRs/wiki | `gitea-tea` | Drives `tea` CLI — explicitly does **not** cover container registry pushes (see §4) |
| BEM/CSS | none specific | n/a |
| Playwright E2E | `playwright-e2e-testing` | Present locally; noted, not invoked per task instructions |
| PHPUnit | `phpunit-best-practices` | Present locally; noted, not invoked per task instructions |
| Astro / frontend framework choice | none specific | n/a |
| Calendar UI library | none specific | n/a |
| Live browser testing | `claude-in-chrome` | The available "drive a real Chrome browser" tool in this environment (click, screenshot, console logs) — no separate Playwright MCP server is configured here |
---
## Recommended stack (summary)
- **Backend**: Symfony (as specified), organized with standard MVC — controllers thin, calendar-aggregation/normalization logic in dedicated services, kept unit-testable in isolation from HTTP/DB.
- **Calendar sync — Mail-in-a-Box**: CalDAV client against each account's Nextcloud CalDAV URL (RFC 4791 / Nextcloud's documented `/remote.php/dav/calendars/<user>/` path) — it's the standards-based protocol Mail-in-a-Box actually exposes; skip Z-Push/ActiveSync, it's a mobile-sync path, not a scriptable API.
- **Calendar sync — Outlook/Exchange**: Microsoft Graph API via OAuth 2.0 authorization-code flow (Entra app registration, `tenant=common`, `offline_access` for refresh tokens) — Graph is Microsoft's only supported, future-proof calendar API for personal Microsoft accounts; CalDAV isn't offered and EWS is being retired.
- **Frontend framework**: plain **TypeScript + Vite**, no meta-framework — Astro's static/islands model targets multi-page content sites, not a single always-on kiosk SPA; Symfony already owns the backend, so the frontend only needs a lean TS build.
- **Calendar UI**: **FullCalendar** (core/MIT) — mature, first-party vanilla-TS support, month/week/list views cover the read-mostly kiosk display without needing its paid Premium tier.
- **CSS**: **BEM** (as specified) — block/element/modifier naming (`__`, `_`) applied directly per bem.info's quick-start convention.
- **E2E testing**: **Playwright** — first-party Chromium engine with device/viewport emulation fits the fixed Android/Chrome kiosk target; use the local `playwright-e2e-testing` skill when writing the suite; use `claude-in-chrome` for ad hoc live-browser checks during development, not as a substitute for the automated suite.
- **Unit/integration testing**: **PHPUnit** via Symfony's own three-tier convention (unit / `KernelTestCase` / `WebTestCase`) — use the local `phpunit-best-practices` skill when writing tests.
- **Docker/CI**: two Dockerfiles (dev, prod), prod built and pushed as `git.arthurerlich.de/haylan/smart-tablet-screen:latest` via `docker login`/`docker push` per Gitea's documented OCI registry path format; no Gitea Actions workflow exists yet in this repo — CI automation is a follow-up, not yet configured, and would use `devops-engineer`/`docker-expert` skills.
- **Reverse proxy/deployment**: existing local reverse-proxy setup exposing the prod container as `smart-screen.home` — no new research needed here, this is ops execution against the infra already discovered (Proxmox-hosted Docker/Swarm).