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.
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:
- Welcome — intro text, app branding
- Create Admin Account — email, password, display name form
- Auth Mode — choose local/oauth/both, configure OAuth if selected
- Theme & Background — pick theme mode, primary color, background type
- Add First Apps — manual add form OR auto-discover button (if Docker available)
- 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.tsisOnboardingNeeded()— check if any users exist and if onboarding is completecompleteOnboarding()— 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
- POST with
- Integrate preview into existing AppForm.svelte (add preview section below URL input)
7.3 Board Templates
- Create
src/lib/server/services/templateService.tsgetBuiltinTemplates()— return hardcoded built-in templatesgetUserTemplates(userId)— custom templates from DBcreateTemplate(input)— save board layout as templateapplyTemplate(templateId, boardId)— create sections from template configexportTemplate(boardId)— export board layout as JSONimportTemplate(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
- Modal triggered by pressing
- 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— newsrc/routes/api/onboarding/+server.ts— newsrc/lib/server/services/onboardingService.ts— newsrc/lib/components/app/AppUrlPreview.svelte— newsrc/routes/api/apps/preview/+server.ts— newsrc/lib/components/app/AppForm.svelte— modify (add preview)src/lib/server/services/templateService.ts— newsrc/lib/components/board/TemplatePicker.svelte— newsrc/routes/api/templates/+server.ts— newsrc/routes/api/templates/[id]/+server.ts— newsrc/routes/api/templates/import/+server.ts— newsrc/lib/components/ui/KeyboardShortcutOverlay.svelte— newsrc/lib/stores/keyboard.svelte.ts— newsrc/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.sveltewith 5-step full-screen overlay (Welcome, Create Admin, Auth Mode, Theme, Create Board). CreatedonboardingService.tswithisOnboardingNeeded(),completeOnboarding(), andgetOnboardingStatus(). Created/api/onboardingroute with GET (status check) and POST (step completion with per-step Zod validation). AddedonboardingNeededflag to root layout server load. Wizard renders as fixed overlay in+layout.sveltewhen flag is true. -
7.2 App URL Health Preview: Created
AppUrlPreview.sveltewith "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/previewroute with server-side HEAD + GET requests, 10s timeout, HTML parsing (first 64KB), favicon extraction from<link rel="icon">or/favicon.icofallback. Integrated intoAppForm.sveltebelow the URL input field. -
7.3 Board Templates: Created
templateService.tswith 4 built-in templates (Home Server, Media Stack, Dev Tools, Monitoring) hardcoded as constants, plus CRUD for user templates viaBoardTemplatePrisma model. SupportsgetBuiltinTemplates(),getAllTemplates(),createTemplate(),applyTemplate(),exportTemplate(), andimportTemplate(). Created 3 API routes:/api/templates(GET list, POST create),/api/templates/[id](GET, DELETE),/api/templates/import(POST). CreatedTemplatePicker.sveltewith grid UI showing blank board + all templates with section previews and JSON file import. Integrated into board creation page with hiddentemplateIdinput and server-sideapplyTemplate()call after board creation. -
7.4 Keyboard Shortcut Overlay: Created
keyboard.svelte.tsstore with global keydown listener, input-focus detection, j/k app navigation (usingdata-app-widgetattributes), Enter to open selected, 1-9 board switching (via sidebar link click),ffor favorites toggle (custom event),efor edit mode toggle,?for overlay toggle. CreatedKeyboardShortcutOverlay.sveltemodal with categorized shortcuts (Global + Board View). Addeddata-keyboard-selectedCSS rule toapp.cssfor 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-evaluateonboardingNeeded. - 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. ThedeleteTemplatefunction blocks deletion of builtins. - Template application in board creation uses
request.clone().formData()to read thetemplateIdhidden input alongside the superforms data. - The keyboard store's
init()must be called from a component context (it adds a globalkeydownlistener).destroy()must be called inonDestroy. - j/k navigation relies on elements having
data-app-widgetattribute — this needs to be added toAppWidget.sveltein Phase 8 integration. - The
fshortcut dispatches atoggle-favoritescustom event onwindow— the FavoritesBar or board page needs to listen for this. - The
eshortcut toggleskeyboard.editModestate — 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
adminCreatedstate 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
keydownlistener onwindow— if multiple layout instances exist (unlikely), listeners could duplicate. Theinit()method guards against this. - Board creation page now reads
requesttwice (superValidate + manual formData) — usesrequest.clone()to avoid "body already consumed" errors.