Files
web-app-launcher/plans/phase-4-7-full-expansion/phase-4-visual-styling.md
T
alexei.dolgolyov 1c0a7cb850 feat: Phases 4-7 — Full Feature Expansion (26 features)
Phase 4 — New Widget Types:
- Clock/Weather, System Stats, RSS/Feed, Calendar, Markdown,
  Metric/Counter, Link Group, Camera/Stream widgets
- Backend services with caching for each data source
- Full creation form with dynamic config fields per type

Phase 5 — Visual & Styling Enhancements:
- Glassmorphism card style (solid/glass/outline)
- Board-level themes with per-board hue/saturation
- Animated SVG status rings replacing static dots
- Card size options (compact/medium/large)
- Custom CSS injection (admin + per-board, sanitized)
- Wallpaper backgrounds with blur/overlay/parallax

Phase 6 — Functional Features:
- Favorites bar with drag-and-drop reordering
- Recent apps tracking with privacy toggle
- Uptime dashboard page (/status, guest-accessible)
- Notifications system (Discord/Slack/Telegram/HTTP webhooks)
- App tags with filtering in board view
- Multi-URL app cards with expandable sub-links
- Personal API tokens with scoped permissions
- Audit log with retention and admin viewer

Phase 7 — Quality of Life:
- Onboarding wizard (5-step first-launch setup)
- App URL health preview with favicon/title detection
- Board templates (4 built-in + custom import/export)
- Keyboard shortcut overlay (j/k nav, 1-9 boards, ? help)

212 files changed, 15641 insertions, 980 deletions.
Build, lint, type check, and 222 tests all pass.
2026-03-25 14:18:10 +03:00

163 lines
10 KiB
Markdown

# Phase 4: Visual & Styling Enhancements
**Status:** ✅ Complete
**Parent plan:** [PLAN.md](./PLAN.md)
**Domain:** frontend
## Objective
Implement all 6 visual/styling features: glassmorphism cards, board-level themes, animated status rings, card size options, custom CSS injection, and wallpaper backgrounds.
## Tasks
### 4.1 Glassmorphism Card Style
- [x] Add card style system to theme store — extend `src/lib/stores/theme.svelte.ts`:
- New property: `cardStyle: 'solid' | 'glass' | 'outline'` (default: 'solid')
- Persist to localStorage, broadcast across tabs
- [x] Add CSS classes in `src/app.css`:
- `.card-solid` — current default card style
- `.card-glass``backdrop-filter: blur(12px); background: hsl(var(--card) / 0.6); border: 1px solid hsl(var(--border) / 0.3)`
- `.card-outline``background: transparent; border: 1px solid hsl(var(--border))`
- [x] Update widget/card components to use dynamic card style class
- [x] Add card style picker to theme settings UI (3-way toggle: solid/glass/outline)
### 4.2 Board-Level Themes
- [x] Create `src/lib/components/board/BoardThemeProvider.svelte`
- Reads board's themeHue, themeSaturation, backgroundType from board data
- Overrides CSS variables when viewing that board (--primary-h, --primary-s)
- Smooth transition when switching boards (CSS transition on :root variables)
- Restores global theme when navigating away from the board
- [x] Update board edit form to include theme settings:
- Hue slider (0-360 with color preview)
- Saturation slider (0-100)
- Background type selector (mesh/particles/aurora/none/wallpaper)
- [x] Update board data loading to include theme fields
- [x] Fix updateBoard server action to extract theme fields from formData
- [x] Fix backgroundType validator to accept all background types (mesh/particles/aurora/wallpaper/none)
### 4.3 Animated SVG Status Ring
- [x] Create `src/lib/components/app/AnimatedStatusRing.svelte`
- SVG circle around app icon with status-dependent animation:
- Online: animated green fill sweep (stroke-dashoffset animation)
- Offline: pulsing red ring (opacity animation)
- Degraded: partial yellow arc (75% fill, subtle pulse)
- Unknown: gray dashed ring (rotating dash pattern)
- Props: status, size (scales with card size), animated (boolean)
- [x] Replace static status dots in AppWidget.svelte with AnimatedStatusRing
- [x] Ensure ring scales appropriately with compact/medium/large card sizes
### 4.4 Card Size Options
- [x] Add `CardSize` support to section and board levels:
- Per-section: `section.cardSize` overrides board default
- Per-board: `board.cardSize` as fallback
- Global default: 'medium'
- [x] Create card size variants in widget components:
- `compact` — icon + name only, smaller padding, single row grid
- `medium` — current default (icon + name + status + description on hover)
- `large` — icon + name + description + sparkline + tags, more padding
- [x] Add card size picker to section edit form (DraggableSection) and board settings
- [x] Update WidgetGrid to adjust grid columns based on card size
- [x] Wire up onUpdateSection handler through DraggableBoard to board edit page
### 4.5 Custom CSS Injection
- [x] Create `src/lib/components/settings/CustomCssEditor.svelte`
- Textarea with monospace font for custom CSS
- Live preview toggle
- Sanitization: strip `<script>` tags, limit selectors to `.app-scope` or descendant selectors
- [x] Add custom CSS field to admin system settings form (SettingsForm.svelte)
- [x] Add per-board custom CSS field to board edit form
- [x] Create `src/lib/components/layout/CustomCssInjector.svelte`
- Injects `<style>` tag with sanitized CSS from system settings + current board
- Wraps CSS in `.custom-css-scope` to prevent breaking critical UI
- [x] Add CustomCssInjector to root layout
### 4.6 Wallpaper Backgrounds
- [x] Create `src/lib/components/background/WallpaperBackground.svelte`
- Displays uploaded image or Unsplash URL as board background
- Configurable: blur amount (0-20px), overlay opacity (0-1), parallax (boolean), position (fixed/scroll)
- Fallback to procedural background if wallpaper fails to load
- [x] Add wallpaper upload endpoint: `src/routes/api/wallpaper/+server.ts`
- Accept image upload (PNG, JPG, WebP), save to `static/uploads/wallpapers/`
- Return URL path
- Max file size: 5MB
- [x] Add wallpaper configuration to board edit form:
- Image upload button or URL input
- Blur slider, overlay opacity slider
- Parallax toggle
- [x] Integrate WallpaperBackground into AmbientBackground component (new background type)
- [ ] Optional Unsplash integration (deferred — requires external API key infrastructure)
## Files to Modify/Create
- `src/lib/stores/theme.svelte.ts` — extend with cardStyle
- `src/app.css` — add glassmorphism classes
- `src/lib/components/board/BoardThemeProvider.svelte` — new
- `src/lib/components/app/AnimatedStatusRing.svelte` — new
- `src/lib/components/widget/AppWidget.svelte` — modify (use AnimatedStatusRing)
- `src/lib/components/widget/WidgetGrid.svelte` — modify (card size grid)
- `src/lib/components/settings/CustomCssEditor.svelte` — new
- `src/lib/components/layout/CustomCssInjector.svelte` — new
- `src/lib/components/background/WallpaperBackground.svelte` — new
- `src/routes/api/wallpaper/+server.ts` — new
- Board edit form components — modify
- Section edit form components — modify
- Root layout — modify (add CustomCssInjector)
## Acceptance Criteria
- Glassmorphism effect works in both light and dark mode, ambient bg bleeds through
- Board themes override global theme smoothly, restore on navigation
- Status rings animate correctly for all 4 statuses
- Card sizes adjust grid layout and widget content appropriately
- Custom CSS is properly sandboxed (cannot break critical UI elements)
- Wallpaper backgrounds display correctly with all configuration options
- All visual changes respect dark/light mode
## Notes
- Glassmorphism requires `backdrop-filter` support (all modern browsers)
- Board theme transitions: use CSS `transition: --primary-h 0.3s, --primary-s 0.3s` on :root
- Custom CSS sanitization: use a simple regex-based approach to strip dangerous selectors, or wrap all custom CSS in a scoped parent selector
- Wallpaper upload: reuse existing upload infrastructure if available (check static/uploads/)
## Review Checklist
- [x] All tasks completed
- [x] Code follows project conventions
- [x] No unintended side effects
- [ ] Build passes (Big Bang: code quality check only)
- [ ] Tests pass (Big Bang: skipped for intermediate phase)
## Handoff to Next Phase
### What was done
- **4.1 Glassmorphism**: Theme store already had `cardStyle` property with localStorage persistence and broadcast sync. CSS classes `.card-solid`, `.card-glass`, `.card-outline` exist in `app.css` with dark mode variants. AppWidget uses dynamic `card-${theme.cardStyle}` class. ThemeCustomizer has 3-way card style picker.
- **4.2 Board-Level Themes**: BoardThemeProvider applies board-specific `--primary-h`/`--primary-s` CSS variables and now properly restores global theme values on cleanup. Board edit form has hue slider, saturation slider, background type selector, and card size picker. Fixed the `updateBoard` server action to extract all theme fields from formData. Fixed `updateBoardSchema` backgroundType enum to accept `mesh/particles/aurora/wallpaper/none`.
- **4.3 Animated SVG Status Ring**: AnimatedStatusRing component renders an SVG circle with 4 status-dependent animations (fill sweep for online, pulse opacity for offline, degraded pulse for degraded, rotating dash for unknown). Replaces status dots in AppWidget at all 3 card sizes.
- **4.4 Card Size Options**: Section-level `cardSize` overrides board-level default. WidgetGrid adjusts grid columns per card size. AppWidget renders compact/medium/large variants. Added card size picker dropdown to DraggableSection with onUpdateSection handler wired through DraggableBoard to the board edit page.
- **4.5 Custom CSS Injection**: CustomCssEditor with validation, sanitization, and live preview. CustomCssInjector sanitizes and injects `<style>` tag scoped to `.custom-css-scope`. Added to root layout for system-level CSS and board view page for board-level CSS. Added CustomCssEditor to admin SettingsForm for system-wide CSS.
- **4.6 Wallpaper Backgrounds**: WallpaperBackground component with blur, overlay, parallax, and position options. Upload API endpoint at `/api/wallpaper` with type/size validation. Board edit form has upload, URL input, blur slider, overlay slider, and parallax toggle. Integrated into AmbientBackground. Unsplash integration deferred.
### What the next phase needs to know
- All visual/styling features are implemented and wired end-to-end
- The `updateBoardSchema` backgroundType now uses inline string enum `['mesh', 'particles', 'aurora', 'wallpaper', 'none']` instead of the `BackgroundType` constant (which only had `none/color/wallpaper`)
- BoardThemeProvider now imports `theme` store to restore global values on cleanup
- DraggableSection and DraggableBoard now support an optional `onUpdateSection` callback for section-level edits (currently used for cardSize)
- System-level custom CSS is loaded in the root layout server data and injected via CustomCssInjector
- Board-level custom CSS is injected on the board view page via CustomCssInjector
- Unsplash integration was deferred as it requires external API key management infrastructure
### Potential concerns
- The `BackgroundType` constant in `constants.ts` (`none/color/wallpaper`) does not match the actual background types used by the theme system (`mesh/particles/aurora/wallpaper/none`). The validator was fixed to use inline strings, but the constant may cause confusion if used elsewhere.
- The board edit form uses native HTML forms with `use:enhance` — theme fields are now extracted in the server action but numeric parsing from formData strings could produce NaN if invalid input sneaks through. The Zod schema provides a safety net.
- Custom CSS sanitization is regex-based. It blocks common XSS vectors but is not a full CSS parser. A determined attacker with admin access could potentially craft CSS that affects layout outside `.custom-css-scope`.