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

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/weather on 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 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

  • 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)
  • 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

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 — 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

  • 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 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.