diff --git a/docs/plans/livewire-material.md b/docs/plans/livewire-material.md new file mode 100644 index 0000000..3787b43 --- /dev/null +++ b/docs/plans/livewire-material.md @@ -0,0 +1,223 @@ +# SealShare on Livewire Material (2.0.0) + +> The package itself — its decisions, the wave plan (Phases 1–10) and its tests — moved to +> the package repo on 2026-09-13: [noNameWEB/livewire-material · docs/plans/livewire-material.md](https://gitea.nonameweb.ch/noNameWEB/livewire-material/src/branch/main/docs/plans/livewire-material.md). +> This file keeps what SealShare does once the package reaches `1.0.0`. + +## Goal + +SealShare's UI is maryUI 2.9 on daisyUI 5 — a generic web-page look. After this change it runs +on **`nonameweb/livewire-material` `^1.0`**: a clean, calm indigo Material 3 Expressive app with +a top app bar, light / dark / system theme, and two Expressive moments — the upload drop zone +and "link ready" — shipped as SealShare 2.0.0. + +## Context + +**Stacks.** SealShare: Laravel 13.31, Livewire 4.4, maryUI 2.9.10 (no prefix), daisyUI 5.7, +Tailwind 4.3, Pest 5.1, Octane on FrankenPHP, PHP 8.5; public on GitHub under MIT, image +published to `ghcr.io/surtic86/sealshare`. ReStride: same Laravel / Livewire / Tailwind / Pest, +private on `gitea.nonameweb.ch`, CI through Gitea act_runner. + +**SealShare's UI surface** (inventory, 2026-09-13): + +- maryUI tags: `button` 30, `input` 18, `password` 16, `icon` 14, `card` 9 (6 `actions` + slots), `menu`/`menu-item` 1/4 (settings nav), `theme-toggle` 3, `toggle` 2, `select` 2, + `modal` 2, `table` 1 (`:headers :rows :sort-by with-pagination`, `@scope`), `textarea` 1, + `toast` 1 (never triggered). +- Raw daisyUI: `btn` (+ `-primary/-ghost/-sm/-xs/-error/-outline/-disabled`), `alert` ×6, + `card`/`card-body` (4 admin stat tiles), `join` (2 copy fields), `progress` ×2, + `loading` ×2, `badge-success/-error`, `divider`, `link link-primary` ×5, `label`, + `file-input`, `checkbox`; tokens `bg-base-*`, `border-base-300`, `text-error/success`, + `border-primary(/50)`, `bg-primary/5`; raw `text-green-600`, `bg-white` (QR code). + Secondary text is `opacity-50/60/70`. +- 20 Heroicons (outline), through `blade-heroicons` pulled in transitively by maryUI. +- No `Mary\` PHP coupling. Admin settings flashes `session('message')` into an alert + (`AdminSettings.php:116,128,135`). 3 `wire:confirm`. +- Layouts: `layouts/app` → `app/sidebar` (centered `max-w-5xl` + footer nav), used by the + Livewire pages and all settings SFCs (`config/livewire.php:47`); `layouts/auth` → + `auth/simple`. The theme script sits *outside* `` and hard-codes dark, while maryUI's + toggle defaults from the OS. `partials/head` loads Instrument Sans from fonts.bunny.net. +- Dead: `/dashboard` (starter placeholder, and Fortify's `home`), `welcome`, + `pages/auth/register` (still referenced by `Fortify::registerView`, + `FortifyServiceProvider.php:52`), `layouts/app/header`, `layouts/auth/{card,split}`, + `components/app-logo`, `components/desktop-user-menu`, `components/placeholder-pattern`; + the `alpinejs` npm dependency; the Flux credentials step in `tests.yml` and `docker.yml`. +- Settings `profile` and `password` show "Saved." through `components/action-message`, + listening for `profile-updated` / `password-updated`; `partials/settings-heading` uses a + daisyUI `divider`. The 3 `wire:confirm` are admin settings (remove logo, clear system + password) and admin dashboard (delete share). `AdminDashboard::headers()` exists only for + maryUI's table. +- Tests assert text only, never markup; no browser tests. +- Docker: the image's caches run in `docker/entrypoint.sh` (`config:cache`, `route:cache`, + `view:cache`); `docker/dev-entrypoint.sh` runs `npm run build` against the host's mounted + `vendor/` without a `composer install`. The Flux credentials step is in `tests.yml`, + `docker.yml` **and** `lint.yml`. +- Screens: setup, system password, upload, share created, share download, admin dashboard, + admin settings, settings (profile, password, appearance, two-factor), Fortify pages (login, + forgot, reset, 2FA challenge, confirm, verify email). Stock Laravel error pages and mails. + +**Constraints found.** + +- SealShare's `Dockerfile` builds assets (stage 1) **before** `composer install` (stage 2); + CSS imported from `vendor/` needs the order swapped. +- Laravel **replaces** the `errors` view namespace at render time with + `config('view.paths')` + `/errors` and the framework's own + (`Illuminate/Foundation/Exceptions/RegisterErrorViewPaths.php`), so error views a package + adds with `addNamespace('errors', …)` are wiped; only a path in `view.paths` survives. +- The package lives at `https://gitea.nonameweb.ch/noNameWEB/livewire-material.git` (public, + anonymous reads verified 2026-09-13). + +## Decisions + +The package's decisions are in its own plan. SealShare's: + +- **Converts after `1.0.0`, in one pass, by hand** (~150 tags; no codemod), on branch + `material`, released as **2.0.0**. +- **Moving SealShare to Gitea is a separate plan** — this plan works wherever it is hosted. +- **Seed `#4f46e5` (the favicon's indigo), Tonal Spot**; Vibrant generated alongside on the + upload page for one visual comparison before committing. +- **Theme default `system`**, storage key `sealshare-theme`, legacy `mary-theme` adopted once. + Appearance is a Light / Dark / System connected button group. +- **One top app bar everywhere** — logo and site title; a theme toggle for guests, an avatar + account menu (Upload, Admin dashboard, Admin settings, Settings, theme, Log out) for users; + centered content; Admin and Settings sub-pages as secondary tabs (menu picker on a phone); + auth pages a centered card under the same bar. No rail, no bottom bar. +- **Expressive components plus two hero moments** — an Expressive shape behind the upload icon + that morphs while files are dragged over, the wavy progress indicator for uploads, a + shape-backed check when the link is ready; admin stats count up once. Instant under + `prefers-reduced-motion`. +- **The public download page uses no anchored components** (no menus, no tooltips) — it must + work for recipients on iOS below 18.4. +- **Starter-kit cleanup during the conversion** — delete the placeholder `/dashboard`, point + Fortify `home` at the admin dashboard, delete the unused views and the `registerView` + binding, drop `alpinejs` from npm and the Flux step from CI. +- **Confirmations become M3 basic dialogs** (the 3 `wire:confirm`) — the browser's native + confirm cannot be themed and reads as a different app. *(Not asked in the interview; object + in review if you prefer the native confirm.)* +- **Save feedback becomes a snackbar** through the package's `Toasts` concern — admin + settings' flashed `session('message')` alert and settings' "Saved." `action-message` alike. + *(Follows from the snackbar; not asked separately.)* +- **The font is self-hosted** — the fonts.bunny.net request goes, which also suits a + privacy-minded self-hosted app. +- **Tests: updated feature tests, the package's guard as `DesignLanguageTest`, Livewire tests + for changed behaviour, and four browser tests** with `pestphp/pest-plugin-browser` (new dev + dependency, approved). + +## Out of scope + +- ReStride adopting the package — its own plan, after `1.0.0`. +- Moving SealShare's repository, CI and image registry to Gitea — its own plan. +- Everything the package plan puts out of scope. +- Changes to SealShare's features, routes or information architecture beyond the cleanup above. + +## Implementation steps + +Step numbers continue the original plan's, so references elsewhere stay valid. + +### Phase 11 — SealShare 2.0.0 (after `1.0.0`) + +34. **Branch** `material` from `main`; open the PR so CI runs. +35. **Dependencies.** Add the `vcs` repository and `composer require nonameweb/livewire-material:^1.0`; + `composer remove robsontenorio/mary` (drops `blade-heroicons` with it); + `npm remove daisyui alpinejs`; `composer require --dev pestphp/pest-plugin-browser`. + maryUI goes **first** because its class components would shadow the package's same-named + anonymous ones; the branch is therefore red from here until step 44, which is accepted — + it merges once, green (Decisions: one pass). +36. **CI and Docker.** Remove the Flux credentials step from `.github/workflows/tests.yml`, + `docker.yml` and `lint.yml`; install Playwright browsers in `tests.yml`. `Dockerfile`: run + the Composer stage first and `COPY --from=vendor /app/vendor ./vendor` into the Node stage + before `npm run build`. `docker/entrypoint.sh`: add `php artisan icons:cache` beside the + other caches. `docker/dev-entrypoint.sh`: run `composer install` when `vendor/` is missing, + before `npm run build`. +37. **Styles and scheme.** `resources/css/app.css`: `@import 'tailwindcss'`, the package entry + from `vendor/`, `./material-scheme.css`, `@source '../views'` and the package's views; drop + the daisyUI plugin, maryUI and pagination `@source`s and the swap safelist. + `resources/js/app.js` imports the package JS. Run + `php artisan material:scheme "#4f46e5" --variant=tonal-spot`; generate Vibrant to a + temporary output, compare on the upload page in both themes, commit the chosen one. +38. **Head and theme.** `partials/head`: remove fonts.bunny.net; include `` + before `@vite` (it currently sits outside ``). Publish the config with + `theme.default = system`, `storage_key = sealshare-theme`, `legacy_keys = ['mary-theme']`. +39. **Layouts.** Rebuild `layouts/app.blade.php` (absorbing `app/sidebar`): `` with + `app-logo-icon` / branding logo and site title, `` for guests or + `` for users (Upload, Admin dashboard, Admin settings, Settings, theme, Log + out through `App\Livewire\Actions\Logout`), centered content, ``. + `layouts/auth.blade.php` (absorbing `auth/simple`): the same bar and a centered card. +40. **Cleanup.** Delete the `/dashboard` route, `dashboard.blade.php`, `placeholder-pattern`, + `welcome`, `pages/auth/register` and its `Fortify::registerView` line, + `layouts/app/{header,sidebar}`, `layouts/auth/{card,split,simple}`, `app-logo`, + `desktop-user-menu`. Fortify `home` → `/admin/dashboard`. Update `AuthenticationTest:22` + and `EmailVerificationTest:32,63` to the new redirect; `DashboardTest` is rewritten to + assert that a signed-in admin lands on the admin dashboard and `/dashboard` is gone + (replacing its placeholder tests, approved in the interview). `RegistrationTest` stays. +41. **Public pages.** `livewire/file-uploader`: drop zone with `` behind the upload + icon morphing while `dragging`, existing Alpine folder walking and `livewire-upload-*` + wiring kept, wavy ``, `` for processing, selected files as + ``, Share Options `` (``, ``, number ``s), + `` for storage full, filled primary "Create Share Link". + `share-created`: shape-backed check, `` for the link, four ``, + info ``, "Upload More". `share-download`: password `` with + ``, files as `` with download icon buttons, "Download All" — no menus + or tooltips. `system-password-prompt`, `setup-wizard` onto fields and buttons. +42. **Auth pages** (`login`, `forgot-password`, `reset-password`, `two-factor-challenge`, + `confirm-password`, `verify-email`): fields, `` for remember me, `link` utility + for text links, `auth-session-status` onto `` (drops `text-green-600`). +43. **Settings.** `pages/settings/layout` → ``; `partials/settings-heading` + drops the daisyUI divider for ``; `profile` and `password` show "Saved." as a + snackbar through `Toasts` (the `profile-updated` / `password-updated` dispatches stay for + any listener) and `components/action-message` is deleted; + `appearance` → Light / Dark / System `` on `$store.theme` (the only toggle on the + page); `two-factor` → `` status, `` setup with the QR on a + white token surface, `` key, recovery codes; `delete-user-form` → + `` with a `danger` action. +44. **Admin.** `admin-dashboard`: four `` (counting up once), disk usage + ``, hand-written `` with `` and pagination (the + `@scope` cells become plain Blade and `AdminDashboard::headers()` goes), view and delete + icon buttons, delete confirmation in a basic `` instead of `wire:confirm`. + `admin-settings`: cards, ``, `` for the logo with preview, + ``, ``, ``; "Remove the logo?" and "Remove the system + password?" become basic dialogs instead of `wire:confirm`; `AdminSettings` uses `Toasts` + instead of `session()->flash('message')` (3 places) and the alert block goes. Keep every + existing `data-test` attribute on the element that now plays its role. +45. **Error pages and mail.** Confirm the package's error views render in SealShare's theme; + set `config/mail.php` `markdown.theme` to `livewire-material::mail.theme`; check the + password-reset and verify-email mails. +46. **Guards.** `tests/Feature/DesignLanguageTest.php` using `DesignGuard` over + `resources/views` and `app/` — no maryUI, no daisyUI, only declared colours, only existing + icons. A grep for `base-content|bg-base|btn|mary` returns nothing. +47. **Rules and AI.** `php artisan boost:update --discover` to install the package guideline and + skill; `record-rule` for SealShare: the scheme is regenerated with `material:scheme`, + never hand-edited; the download page stays free of anchored components; the theme key. +48. **Docs.** README tech stack and the "Dark Mode" feature line; CHANGELOG `2.0.0`. +49. **Ship.** Full suite green on the PR; merge; tag `v2.0.0` (publishes the image through + `docker.yml`). + +## Testing + +- Feature tests updated where redirects or text change: `AuthenticationTest`, + `EmailVerificationTest`, `DashboardTest` (rewritten), `AdminSettingsTest` (asserts the + toast is dispatched instead of the flash), `TwoFactorAuthenticationTest`, + `AdminDashboardTest`, `ShareDownloadTest`. +- `DesignLanguageTest` through the package guard. +- Livewire tests: admin settings save/remove-logo/clear-password dispatch toasts; profile and + password updates dispatch the "Saved." toast (`ProfileUpdateTest`, `PasswordUpdateTest`); + delete share, remove logo and clear system password go through their dialogs' confirm + actions. +- Browser tests (`tests/Browser`): upload by drop and by Browse → progress → share created → + copy link; the password-protected download page at 393px; admin table sort and delete + dialog; a first visit follows the OS theme and Appearance switches it. +- Narrow runs per step; the full suite on the PR's CI. + +## Risks and open questions + +- **Scope and time.** The whole catalogue (~45 components plus extras) comes before SealShare + changes at all, so its starter-kit bugs (the placeholder `/dashboard`) stay until then. + Mitigation: waves tagged `0.x`, each reviewed in the showcase; SealShare keeps working + meanwhile. +- **Gitea becomes a build dependency.** Every SealShare CI run and Docker build fetches the + package from `gitea.nonameweb.ch`; an outage or a sign-in setting reverting breaks builds. + Mitigation: dist archives cached by Composer in CI; revisit Packagist if it bites. +- **iOS / Safari below 18.4.** Anchored menus and tooltips do not position there. Mitigation: + SealShare's download page uses none; native `