Files
web-app-launcher/plans/phase-4-7-full-expansion/phase-3-widget-frontend.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

179 lines
8.9 KiB
Markdown

# Phase 3: New Widget Components
**Status:** ✅ Complete
**Parent plan:** [PLAN.md](./PLAN.md)
**Domain:** frontend
## Objective
Build all 8 new widget UI components with polished design, integrate them into the existing WidgetRenderer and WidgetCreationForm, and ensure they work with the drag-and-drop system.
## Tasks
### 3.1 Clock/Weather Widget
- [x] Create `src/lib/components/widget/ClockWeatherWidget.svelte`
- Digital clock: large time display with configurable timezone, date below
- Analog clock: SVG clock face with hour/minute/second hands, smooth animation via $effect
- Weather section (optional): current temp, condition icon (Lucide), location label
- Fetches weather from `/api/widgets/weather` on mount + interval
- Config-driven: clockStyle (analog|digital), showWeather, timezone
### 3.2 System Stats Widget
- [x] Create `src/lib/components/widget/SystemStatsWidget.svelte`
- Donut/gauge charts for each metric (CPU, RAM, disk) using SVG
- Threshold coloring: green (<60%), yellow (60-85%), red (>85%) via CSS classes
- Auto-refresh at configurable interval
- Fetches from `/api/widgets/system-stats`
- Compact layout: metrics side-by-side with labels below
### 3.3 RSS/Feed Widget
- [x] Create `src/lib/components/widget/RssFeedWidget.svelte`
- List of feed items: title + relative date
- Expandable summary on click (slide transition)
- Link icon to open in new tab
- Fetches from `/api/widgets/rss`
- Loading skeleton while fetching
- Empty state when feed has no items
### 3.4 Calendar Widget
- [x] Create `src/lib/components/widget/CalendarWidget.svelte`
- Compact event list grouped by day (Today, Tomorrow, then dates)
- Color dot per calendar source
- Time range display (or "All day")
- Location shown if available
- Fetches from `/api/widgets/calendar`
- Empty state: "No upcoming events"
### 3.5 Markdown Widget
- [x] Create `src/lib/components/widget/MarkdownWidget.svelte`
- Rendered markdown view (default) using `marked` + `isomorphic-dompurify`
- Edit mode: split-pane with textarea left, preview right
- Syntax highlighting for code blocks (use existing `marked` setup or add `highlight.js`)
- Toggle edit/view mode button
- Save updates config via API
- Proper typography styling for headers, lists, code, blockquotes
### 3.6 Metric/Counter Widget
- [x] Create `src/lib/components/widget/MetricWidget.svelte`
- Large centered number with unit suffix
- Label below the number
- Trend arrow: up (green), down (red), flat (gray) — SVG arrow icon
- Auto-refresh at interval
- Fetches from `/api/widgets/metric`
- Number formatting (locale-aware, abbreviate large numbers)
### 3.7 Link Group Widget
- [x] Create `src/lib/components/widget/LinkGroupWidget.svelte`
- Compact vertical list of links with optional icons
- Each link: icon (Lucide or none) + label, opens in new tab
- Collapsible header if config.collapsible is true (slide transition)
- Hover highlight on each link row
- No external data fetching — config-driven only
### 3.8 Camera/Stream Widget
- [x] Create `src/lib/components/widget/CameraStreamWidget.svelte`
- Snapshot mode: `<img>` tag refreshed at interval via `/api/widgets/camera`
- MJPEG mode: direct `<img src={streamUrl}>` (continuous stream)
- HLS mode: `<video>` tag with HLS.js (lazy-loaded if needed)
- Click opens fullscreen modal (use Bits UI Dialog)
- Aspect ratio from config (default 16:9)
- Loading state and error fallback image
### 3.9 Update WidgetRenderer
- [x] Update `src/lib/components/widget/WidgetRenderer.svelte`
- Add cases for all 8 new widget types
- Import new widget components
- Parse config and pass correct props
### 3.10 Update WidgetCreationForm
- [x] Update `src/lib/components/widget/WidgetCreationForm.svelte`
- Add all 8 new widget types to the type picker (with icons and labels)
- Add dynamic config form fields for each new type:
- Clock: timezone select, clock style toggle, weather checkbox, lat/lng inputs
- System Stats: source URL, source type select, metrics checkboxes, refresh interval
- RSS: feed URL input, max items slider, show summary checkbox
- Calendar: iCal URL list (add/remove), days ahead slider
- Markdown: content textarea (full height)
- Metric: label, source type select, value/URL/query inputs based on source, unit, refresh
- Link Group: link list (add/remove rows with label+URL+icon), collapsible checkbox
- Camera: stream URL, type select, refresh interval, aspect ratio select
## Files to Modify/Create
- `src/lib/components/widget/ClockWeatherWidget.svelte` — new
- `src/lib/components/widget/SystemStatsWidget.svelte` — new
- `src/lib/components/widget/RssFeedWidget.svelte` — new
- `src/lib/components/widget/CalendarWidget.svelte` — new
- `src/lib/components/widget/MarkdownWidget.svelte` — new
- `src/lib/components/widget/MetricWidget.svelte` — new
- `src/lib/components/widget/LinkGroupWidget.svelte` — new
- `src/lib/components/widget/CameraStreamWidget.svelte` — new
- `src/lib/components/widget/WidgetRenderer.svelte` — modify
- `src/lib/components/widget/WidgetCreationForm.svelte` — modify
## Acceptance Criteria
- All 8 widgets render correctly with sample config data
- Widgets that fetch external data show loading states and handle errors gracefully
- Edit/create form correctly generates config JSON for each widget type
- WidgetRenderer routes to the correct component for each type
- All widgets work with the drag-and-drop system (no interference)
- Widgets use existing design system (Tailwind classes, CSS variables, dark mode support)
- Responsive: widgets adapt to different container widths
## Notes
- Follow existing widget component patterns (see AppWidget.svelte, BookmarkWidget.svelte)
- Use Svelte 5 runes: $props for inputs, $state for local state, $derived for computed, $effect for side effects
- Use onMount for initial data fetches, setInterval for auto-refresh (clean up in onDestroy or $effect return)
- All external data fetches go through the backend API (no direct client-side calls to external services)
- Keep each widget component focused — extract shared utilities if patterns repeat
## Review Checklist
- [x] All tasks completed
- [x] Code follows project conventions
- [ ] 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
- Created 8 new widget components: ClockWeatherWidget, SystemStatsWidget, RssFeedWidget, CalendarWidget, MarkdownWidget, MetricWidget, LinkGroupWidget, CameraStreamWidget
- Updated WidgetRenderer to route all 8 new widget types to their components with parsed config props
- Updated WidgetCreationForm with all 8 new widget types in the type picker (13 total) and dynamic config forms for each
- Updated WidgetGrid to include new full-width widget types (system_stats, rss, calendar, markdown, camera)
### What the next phase needs to know
- All widget components follow the established pattern: `interface Props { config: XxxWidgetConfig }` with `$props()`
- ClockWeatherWidget supports digital, analog, and 24h clock styles; analog uses SVG; weather fetches from `/api/widgets/weather`
- SystemStatsWidget renders SVG donut/gauge charts with threshold coloring (green/yellow/red)
- RssFeedWidget has expandable summaries using Svelte `slide` transition
- CalendarWidget groups events by day (Today, Tomorrow, date) with color dots per calendar source
- MarkdownWidget reuses the project's existing `marked` + `isomorphic-dompurify` setup (same as NoteWidget); has split-pane edit mode with save-to-API
- MetricWidget supports static, JSON, and Prometheus sources; shows trend arrows and abbreviates large numbers
- LinkGroupWidget is config-driven only (no API fetch); supports collapsible mode
- CameraStreamWidget supports image/MJPEG/HLS modes; HLS uses lazy-loaded `hls.js`; has a custom fullscreen modal overlay
- WidgetCreationForm uses IconGrid for type selection (5 columns) and dynamic form sections per type
- All interval-based refreshes use `$effect` cleanup pattern for proper teardown
### Potential concerns
- CameraStreamWidget fullscreen modal is a custom implementation (not Bits UI Dialog as spec suggested) because it avoids adding a component dependency for a simple overlay. If consistency with other modals is needed, it could be refactored to use Bits UI Dialog.
- HLS.js is dynamically imported (`import('hls.js')`); if hls.js is not installed as a dependency, the import will fail gracefully and fall back to native HLS support (Safari). The project may need `npm install hls.js` if HLS camera streams are used.
- MarkdownWidget save uses PATCH to `/api/widgets/{id}` which must exist in the API routes (standard widget update endpoint).
- The WidgetCreationForm is now a larger component (~500 lines) due to 13 widget types. If this becomes unwieldy, consider extracting per-type form sections into subcomponents.