1c0a7cb850
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.
8.9 KiB
8.9 KiB
Phase 3: New Widget Components
Status: ✅ Complete Parent plan: 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
- 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/weatheron mount + interval - Config-driven: clockStyle (analog|digital), showWeather, timezone
3.2 System Stats Widget
- 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
- 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
- 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
- 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
markedsetup or addhighlight.js) - Toggle edit/view mode button
- Save updates config via API
- Proper typography styling for headers, lists, code, blockquotes
- Rendered markdown view (default) using
3.6 Metric/Counter Widget
- 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
- 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
- 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
- Snapshot mode:
3.9 Update WidgetRenderer
- 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
- 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— newsrc/lib/components/widget/SystemStatsWidget.svelte— newsrc/lib/components/widget/RssFeedWidget.svelte— newsrc/lib/components/widget/CalendarWidget.svelte— newsrc/lib/components/widget/MarkdownWidget.svelte— newsrc/lib/components/widget/MetricWidget.svelte— newsrc/lib/components/widget/LinkGroupWidget.svelte— newsrc/lib/components/widget/CameraStreamWidget.svelte— newsrc/lib/components/widget/WidgetRenderer.svelte— modifysrc/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
- All tasks completed
- 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
slidetransition - CalendarWidget groups events by day (Today, Tomorrow, date) with color dots per calendar source
- MarkdownWidget reuses the project's existing
marked+isomorphic-dompurifysetup (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
$effectcleanup 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 neednpm install hls.jsif 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.