Files
Smart-Tablet-Screen/docs/research/smart-tablet-screen-stack.md
T
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

18 KiB
Raw Blame History

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, 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
  • 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. 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
  • 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
  • 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, Microsoft Q&A: 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
  • 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

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

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

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
  • 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

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
  • 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
  • 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, schedule-x/schedule-x GitHub

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

  • 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).