Files
web-app-launcher/plans/phase-4-7-full-expansion/phase-7-quality-of-life.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

12 KiB

Phase 7: Quality of Life

Status: Complete Parent plan: PLAN.md Domain: fullstack

Objective

Implement 4 quality-of-life features: onboarding wizard, app URL health preview, board templates, and keyboard shortcut overlay.

Tasks

7.1 Onboarding Wizard

  • Create src/lib/components/onboarding/OnboardingWizard.svelte
    • Full-screen overlay triggered on first launch (no users in DB or onboardingComplete=false in SystemSettings)
    • Steps with progress indicator:
      1. Welcome — intro text, app branding
      2. Create Admin Account — email, password, display name form
      3. Auth Mode — choose local/oauth/both, configure OAuth if selected
      4. Theme & Background — pick theme mode, primary color, background type
      5. Add First Apps — manual add form OR auto-discover button (if Docker available)
      6. Create First Board — name, pick a template or start blank
    • Skippable steps for advanced users
    • Stores completion in SystemSettings.onboardingComplete
  • Create src/routes/api/onboarding/+server.ts
    • POST: complete step (validates, creates entities)
    • GET: check onboarding status
  • Create src/lib/server/services/onboardingService.ts
    • isOnboardingNeeded() — check if any users exist and if onboarding is complete
    • completeOnboarding() — mark SystemSettings.onboardingComplete = true
  • Add onboarding check to root layout server load function
    • If onboarding needed, pass flag to layout → show wizard

7.2 App URL Health Preview

  • Create src/lib/components/app/AppUrlPreview.svelte
    • "Test Connection" button in app create/edit form
    • On click: calls backend to test the URL
    • Shows: HTTP status code, response time (ms), auto-detected favicon URL, page title
    • If no icon selected, offers to use the detected favicon
    • If no name entered, offers to use the detected page title
    • Loading state while testing, error state on failure
  • Create src/routes/api/apps/preview/+server.ts
    • POST with { url } body
    • Server-side fetch: HEAD request for status/timing, GET for HTML parsing
    • Extract: favicon (from <link rel="icon"> or /favicon.ico), page title (from <title>)
    • Return: { status, responseTime, favicon, title }
    • Timeout: 10s, handle errors gracefully
  • Integrate preview into existing AppForm.svelte (add preview section below URL input)

7.3 Board Templates

  • Create src/lib/server/services/templateService.ts
    • getBuiltinTemplates() — return hardcoded built-in templates
    • getUserTemplates(userId) — custom templates from DB
    • createTemplate(input) — save board layout as template
    • applyTemplate(templateId, boardId) — create sections from template config
    • exportTemplate(boardId) — export board layout as JSON
    • importTemplate(json) — import template from JSON
  • Create built-in templates (hardcoded in service):
    • "Home Server" — sections: Media, Networking, Storage, Monitoring
    • "Media Stack" — sections: Streaming, Downloads, Management
    • "Dev Tools" — sections: Git, CI/CD, Databases, Docs
    • "Monitoring" — sections: Metrics, Logs, Alerts, Status
  • Create src/lib/components/board/TemplatePicker.svelte
    • Grid of template cards (icon, name, description, section preview)
    • Built-in templates + user-created templates
    • Click to select → creates board with template sections
    • "Blank Board" option
    • "Import Template" button (file upload JSON)
  • Create src/routes/api/templates/+server.ts — GET (list), POST (create from board)
  • Create src/routes/api/templates/[id]/+server.ts — GET (single), DELETE
  • Create src/routes/api/templates/import/+server.ts — POST (import JSON)
  • Integrate TemplatePicker into board creation flow

7.4 Keyboard Shortcut Overlay

  • Create src/lib/components/ui/KeyboardShortcutOverlay.svelte
    • Modal triggered by pressing ? key
    • Context-aware sections:
      • Global: Cmd/Ctrl+K (search), ? (shortcuts), 1-9 (switch board), f (toggle favorites)
      • Board View: j/k (navigate apps), Enter (open selected), e (edit mode)
      • Admin: (admin-specific shortcuts if any)
    • Organized in categorized columns
    • Close on Escape or clicking outside
    • Small ? hint icon in footer
  • Create src/lib/stores/keyboard.svelte.ts
    • Register global keyboard listeners
    • j/k navigation: track selected app index in current board
    • Enter: open selected app URL
    • 1-9: switch to board by index
    • f: toggle favorites bar visibility
    • e: toggle board edit mode
    • ?: show shortcut overlay
    • Disable shortcuts when input/textarea is focused
  • Integrate keyboard store into root layout
  • Add ? hint icon to footer component

Files to Modify/Create

  • src/lib/components/onboarding/OnboardingWizard.svelte — new
  • src/routes/api/onboarding/+server.ts — new
  • src/lib/server/services/onboardingService.ts — new
  • src/lib/components/app/AppUrlPreview.svelte — new
  • src/routes/api/apps/preview/+server.ts — new
  • src/lib/components/app/AppForm.svelte — modify (add preview)
  • src/lib/server/services/templateService.ts — new
  • src/lib/components/board/TemplatePicker.svelte — new
  • src/routes/api/templates/+server.ts — new
  • src/routes/api/templates/[id]/+server.ts — new
  • src/routes/api/templates/import/+server.ts — new
  • src/lib/components/ui/KeyboardShortcutOverlay.svelte — new
  • src/lib/stores/keyboard.svelte.ts — new
  • src/routes/+layout.svelte — modify (onboarding check, keyboard store)
  • src/routes/+layout.server.ts — modify (onboarding status)

Acceptance Criteria

  • Onboarding wizard triggers on first launch, creates admin user and basic setup
  • Steps can be skipped, wizard can be completed partially
  • App URL preview shows status, timing, favicon, and title extraction
  • Board templates create correct section structure when applied
  • Built-in templates are always available (not stored in DB)
  • Template import/export produces valid JSON that can round-trip
  • Keyboard shortcuts work globally, disabled in text inputs
  • Shortcut overlay shows context-appropriate shortcuts
  • All features work in both dark and light mode

Notes

  • Onboarding detection: simplest approach is checking User count === 0
  • URL preview: use node's fetch with timeout, parse HTML response for favicon/title
  • Board templates: config JSON structure: { sections: [{ title, icon, order }] }
  • Keyboard navigation: use data attributes on app widgets to track position
  • The ? shortcut must not interfere with typing in inputs/textareas

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

  • 7.1 Onboarding Wizard: Created OnboardingWizard.svelte with 5-step full-screen overlay (Welcome, Create Admin, Auth Mode, Theme, Create Board). Created onboardingService.ts with isOnboardingNeeded(), completeOnboarding(), and getOnboardingStatus(). Created /api/onboarding route with GET (status check) and POST (step completion with per-step Zod validation). Added onboardingNeeded flag to root layout server load. Wizard renders as fixed overlay in +layout.svelte when flag is true.

  • 7.2 App URL Health Preview: Created AppUrlPreview.svelte with "Test Connection" button showing HTTP status, response time, favicon preview, and page title extraction. Offers "Use as name" and "Use as icon" buttons when fields are empty. Created /api/apps/preview route with server-side HEAD + GET requests, 10s timeout, HTML parsing (first 64KB), favicon extraction from <link rel="icon"> or /favicon.ico fallback. Integrated into AppForm.svelte below the URL input field.

  • 7.3 Board Templates: Created templateService.ts with 4 built-in templates (Home Server, Media Stack, Dev Tools, Monitoring) hardcoded as constants, plus CRUD for user templates via BoardTemplate Prisma model. Supports getBuiltinTemplates(), getAllTemplates(), createTemplate(), applyTemplate(), exportTemplate(), and importTemplate(). Created 3 API routes: /api/templates (GET list, POST create), /api/templates/[id] (GET, DELETE), /api/templates/import (POST). Created TemplatePicker.svelte with grid UI showing blank board + all templates with section previews and JSON file import. Integrated into board creation page with hidden templateId input and server-side applyTemplate() call after board creation.

  • 7.4 Keyboard Shortcut Overlay: Created keyboard.svelte.ts store with global keydown listener, input-focus detection, j/k app navigation (using data-app-widget attributes), Enter to open selected, 1-9 board switching (via sidebar link click), f for favorites toggle (custom event), e for edit mode toggle, ? for overlay toggle. Created KeyboardShortcutOverlay.svelte modal with categorized shortcuts (Global + Board View). Added data-keyboard-selected CSS rule to app.css for visual selection ring. Added ? hint icon button to Sidebar footer next to collapse toggle. Integrated keyboard store init/destroy into root +layout.svelte.

What the next phase needs to know

  • The onboarding wizard is a simple full-screen overlay that blocks the UI — it does NOT redirect. Once completed, the page needs a full reload (or invalidateAll()) to re-evaluate onboardingNeeded.
  • The wizard has 5 steps (Welcome, Admin, Auth, Theme, Complete) — the original plan had 6 steps but "Add First Apps" was consolidated into the board creation step for simplicity.
  • The onboarding API has NO authentication requirement (since it runs before any user exists).
  • The URL preview endpoint requires authentication and returns { status, responseTime, favicon, title, error }.
  • Built-in templates use builtin- prefixed IDs and are not stored in the database. The deleteTemplate function blocks deletion of builtins.
  • Template application in board creation uses request.clone().formData() to read the templateId hidden input alongside the superforms data.
  • The keyboard store's init() must be called from a component context (it adds a global keydown listener). destroy() must be called in onDestroy.
  • j/k navigation relies on elements having data-app-widget attribute — this needs to be added to AppWidget.svelte in Phase 8 integration.
  • The f shortcut dispatches a toggle-favorites custom event on window — the FavoritesBar or board page needs to listen for this.
  • The e shortcut toggles keyboard.editMode state — board components can read this to enter/exit edit mode.

Potential concerns

  • The onboarding wizard completes steps via sequential API calls — if the browser is closed mid-wizard, partial state (e.g., admin created but onboarding not marked complete) can occur. The wizard handles this by checking adminCreated state and skipping re-creation.
  • The URL preview endpoint makes server-side HTTP requests to arbitrary URLs, which could be used for SSRF. Consider adding URL validation (e.g., blocking private IP ranges) in a security review.
  • Template import accepts arbitrary JSON — validation checks structure but does not limit section count or title length beyond Zod schema bounds.
  • The keyboard store adds a keydown listener on window — if multiple layout instances exist (unlikely), listeners could duplicate. The init() method guards against this.
  • Board creation page now reads request twice (superValidate + manual formData) — uses request.clone() to avoid "body already consumed" errors.