Files
SealShare/docs/plans/livewire-material.md
T
Andreas Reinhold / reiniandClaude Opus 5 c4a17b65c8 Move SealShare onto Livewire Material
Replaces maryUI and daisyUI with nonameweb/livewire-material: the Vibrant
indigo scheme, a system/light/dark theme under sealshare-theme, one top
app bar with the account menu, the upload drop zone and link-ready
moments, M3 fields, dialogs instead of wire:confirm, snackbars instead of
flashed messages, a sortable admin table, and the starter-kit cleanup.
Docker builds assets after Composer; CI drops the Flux step.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
2026-09-13 10:49:38 +02:00

15 KiB
Raw Blame History

SealShare on Livewire Material (2.0.0)

The package itself — its decisions, the wave plan (Phases 110) and its tests — moved to the package repo on 2026-09-13: noNameWEB/livewire-material · 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/appapp/sidebar (centered max-w-5xl + footer nav), used by the Livewire pages and all settings SFCs (config/livewire.php:47); layouts/authauth/simple. The theme script sits outside <head> 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), Vibrant — chosen after comparing it with Tonal Spot on the upload page in both themes (2026-09-13): Tonal Spot read grey-lavender on this seed.
  • 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)

  1. Branch material from main; open the PR so CI runs.
  2. 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).
  3. 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/dev-entrypoint.sh: run composer install when vendor/ is missing, before npm run build. No icons:cache anywhere: the package draws its symbols without blade-icons.
  4. 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 @sources 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.
  5. Head and theme. partials/head: remove fonts.bunny.net; include <x-theme-script /> before @vite (it currently sits outside <head>). Publish the config with theme.default = system, storage_key = sealshare-theme, legacy_keys = ['mary-theme'].
  6. Layouts. Rebuild layouts/app.blade.php (absorbing app/sidebar): <x-app-bar> with app-logo-icon / branding logo and site title, <x-theme-toggle> for guests or <x-account-menu> for users (Upload, Admin dashboard, Admin settings, Settings, theme, Log out through App\Livewire\Actions\Logout), centered content, <x-toast>. layouts/auth.blade.php (absorbing auth/simple): the same bar and a centered card.
  7. 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.
  8. Public pages. livewire/file-uploader: drop zone with <x-shape> behind the upload icon morphing while dragging, existing Alpine folder walking and livewire-upload-* wiring kept, wavy <x-progress>, <x-loading> for processing, selected files as <x-list>, Share Options <x-card> (<x-toggle>, <x-select>, number <x-input>s), <x-alert> for storage full, filled primary "Create Share Link". share-created: shape-backed check, <x-input copyable> for the link, four <x-stat>, info <x-alert>, "Upload More". share-download: password <x-card> with <x-password>, files as <x-list> with download icon buttons, "Download All" — no menus or tooltips. system-password-prompt, setup-wizard onto fields and buttons.
  9. Auth pages (login, forgot-password, reset-password, two-factor-challenge, confirm-password, verify-email): fields, <x-checkbox> for remember me, link utility for text links, auth-session-status onto <x-alert> (drops text-green-600).
  10. Settings. pages/settings/layout<x-section-nav>; partials/settings-heading drops the daisyUI divider for <x-divider>; 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 <x-group> on $store.theme (the only toggle on the page); two-factor<x-badge> status, <x-modal fullscreen> setup with the QR on a white token surface, <x-input copyable> key, recovery codes; delete-user-form<x-modal> with a danger action.
  11. Admin. admin-dashboard: four <x-stat> (counting up once), disk usage <x-progress>, hand-written <x-table> with <x-sort-header> and pagination (the @scope cells become plain Blade and AdminDashboard::headers() goes), view and delete icon buttons, delete confirmation in a basic <x-modal> instead of wire:confirm. admin-settings: cards, <x-textarea>, <x-file> for the logo with preview, <x-toggle>, <x-select>, <x-input suffix>; "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.
  12. 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.
  13. 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.
  14. 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.
  15. Docs. README tech stack and the "Dark Mode" feature line; CHANGELOG 2.0.0.
  16. 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 <select> stays the fallback everywhere.
  • Scheme and spring values are tuned by eye; Tonal Spot may read washed out on indigo — the Vibrant comparison in step 37 is the check.