From 41c705a30368f040f4562cf2030a19cfc9b55b2a Mon Sep 17 00:00:00 2001 From: Till JS Date: Thu, 9 Apr 2026 14:28:01 +0200 Subject: [PATCH] feat(mana/web): per-module icons + wire workbench title link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an optional icon field to AppDescriptor and assigns a Phosphor icon to all 33 registered apps (CheckSquare for todo, Calendar for calendar, AddressBook for contacts, …). AppPage now passes both the icon and titleHref={`/${appId}`} to PageShell, so workbench cards show the module's icon next to the now-clickable title instead of the generic color dot. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../apps/web/src/lib/app-registry/apps.ts | 66 ++++++++++++++++++- .../apps/web/src/lib/app-registry/types.ts | 1 + .../lib/components/workbench/AppPage.svelte | 3 + 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/apps/mana/apps/web/src/lib/app-registry/apps.ts b/apps/mana/apps/web/src/lib/app-registry/apps.ts index 30875f16d..e2b25803d 100644 --- a/apps/mana/apps/web/src/lib/app-registry/apps.ts +++ b/apps/mana/apps/web/src/lib/app-registry/apps.ts @@ -7,7 +7,40 @@ */ import { registerApp } from './registry'; -import { Plus } from '@mana/shared-icons'; +import { + Plus, + CheckSquare, + Calendar, + AddressBook, + Repeat, + NotePencil, + Moon, + Drop, + Wallet, + MapPin, + ChatCircle, + Brain, + Clock, + Quotes, + Cards, + Image, + MusicNotes, + Camera, + HardDrives, + ForkKnife, + Plant, + Presentation, + Package, + Lightbulb, + Question, + Tree, + Smiley, + Buildings, + UploadSimple, + Calculator, + Lightning, + Sparkle, +} from '@mana/shared-icons'; // ── Apps with entity capabilities ─────────────────────────── @@ -15,6 +48,7 @@ registerApp({ id: 'todo', name: 'Todo', color: '#8B5CF6', + icon: CheckSquare, views: { list: { load: () => import('$lib/modules/todo/ListView.svelte') }, detail: { load: () => import('$lib/modules/todo/views/DetailView.svelte') }, @@ -61,6 +95,7 @@ registerApp({ id: 'calendar', name: 'Kalender', color: '#3B82F6', + icon: Calendar, views: { list: { load: () => import('$lib/modules/calendar/ListView.svelte') }, detail: { load: () => import('$lib/modules/calendar/views/DetailView.svelte') }, @@ -139,6 +174,7 @@ registerApp({ id: 'contacts', name: 'Kontakte', color: '#22C55E', + icon: AddressBook, views: { list: { load: () => import('$lib/modules/contacts/ListView.svelte') }, detail: { load: () => import('$lib/modules/contacts/views/DetailView.svelte') }, @@ -173,6 +209,7 @@ registerApp({ id: 'habits', name: 'Habits', color: '#8B5CF6', + icon: Repeat, views: { list: { load: () => import('$lib/modules/habits/ListView.svelte') }, }, @@ -217,6 +254,7 @@ registerApp({ id: 'notes', name: 'Notes', color: '#F59E0B', + icon: NotePencil, views: { list: { load: () => import('$lib/modules/notes/ListView.svelte') }, }, @@ -263,6 +301,7 @@ registerApp({ id: 'dreams', name: 'Dreams', color: '#6366F1', + icon: Moon, views: { list: { load: () => import('$lib/modules/dreams/ListView.svelte') }, }, @@ -305,6 +344,7 @@ registerApp({ id: 'cycles', name: 'Cycles', color: '#ec4899', + icon: Drop, views: { list: { load: () => import('$lib/modules/cycles/ListView.svelte') }, }, @@ -339,6 +379,7 @@ registerApp({ id: 'finance', name: 'Finance', color: '#22C55E', + icon: Wallet, views: { list: { load: () => import('$lib/modules/finance/ListView.svelte') }, }, @@ -365,6 +406,7 @@ registerApp({ id: 'places', name: 'Places', color: '#0EA5E9', + icon: MapPin, views: { list: { load: () => import('$lib/modules/places/ListView.svelte') }, detail: { load: () => import('$lib/modules/places/views/DetailView.svelte') }, @@ -399,6 +441,7 @@ registerApp({ id: 'chat', name: 'Chat', color: '#6366F1', + icon: ChatCircle, views: { list: { load: () => import('$lib/modules/chat/ListView.svelte') }, }, @@ -408,6 +451,7 @@ registerApp({ id: 'context', name: 'Context', color: '#7C3AED', + icon: Brain, views: { list: { load: () => import('$lib/modules/context/ListView.svelte') }, }, @@ -417,6 +461,7 @@ registerApp({ id: 'times', name: 'Times', color: '#F59E0B', + icon: Clock, views: { list: { load: () => import('$lib/modules/times/ListView.svelte') }, detail: { load: () => import('$lib/modules/times/views/DetailView.svelte') }, @@ -427,6 +472,7 @@ registerApp({ id: 'zitare', name: 'Zitare', color: '#EC4899', + icon: Quotes, views: { list: { load: () => import('$lib/modules/zitare/ListView.svelte') }, detail: { load: () => import('$lib/modules/zitare/views/DetailView.svelte') }, @@ -437,6 +483,7 @@ registerApp({ id: 'cards', name: 'Cards', color: '#EF4444', + icon: Cards, views: { list: { load: () => import('$lib/modules/cards/ListView.svelte') }, detail: { load: () => import('$lib/modules/cards/views/DetailView.svelte') }, @@ -447,6 +494,7 @@ registerApp({ id: 'picture', name: 'Picture', color: '#8B5CF6', + icon: Image, views: { list: { load: () => import('$lib/modules/picture/ListView.svelte') }, }, @@ -456,6 +504,7 @@ registerApp({ id: 'music', name: 'Music', color: '#F97316', + icon: MusicNotes, views: { list: { load: () => import('$lib/modules/music/ListView.svelte') }, detail: { load: () => import('$lib/modules/music/views/DetailView.svelte') }, @@ -466,6 +515,7 @@ registerApp({ id: 'photos', name: 'Photos', color: '#06B6D4', + icon: Camera, views: { list: { load: () => import('$lib/modules/photos/ListView.svelte') }, }, @@ -475,6 +525,7 @@ registerApp({ id: 'storage', name: 'Storage', color: '#6B7280', + icon: HardDrives, views: { list: { load: () => import('$lib/modules/storage/ListView.svelte') }, detail: { load: () => import('$lib/modules/storage/views/DetailView.svelte') }, @@ -485,6 +536,7 @@ registerApp({ id: 'nutriphi', name: 'Nutriphi', color: '#22C55E', + icon: ForkKnife, views: { list: { load: () => import('$lib/modules/nutriphi/ListView.svelte') }, }, @@ -494,6 +546,7 @@ registerApp({ id: 'planta', name: 'Planta', color: '#16A34A', + icon: Plant, views: { list: { load: () => import('$lib/modules/planta/ListView.svelte') }, detail: { load: () => import('$lib/modules/planta/views/DetailView.svelte') }, @@ -504,6 +557,7 @@ registerApp({ id: 'presi', name: 'Presi', color: '#A855F7', + icon: Presentation, views: { list: { load: () => import('$lib/modules/presi/ListView.svelte') }, detail: { load: () => import('$lib/modules/presi/views/DetailView.svelte') }, @@ -514,6 +568,7 @@ registerApp({ id: 'inventar', name: 'Inventar', color: '#78716C', + icon: Package, views: { list: { load: () => import('$lib/modules/inventar/ListView.svelte') }, detail: { load: () => import('$lib/modules/inventar/views/DetailView.svelte') }, @@ -524,6 +579,7 @@ registerApp({ id: 'memoro', name: 'Memoro', color: '#F59E0B', + icon: Lightbulb, views: { list: { load: () => import('$lib/modules/memoro/ListView.svelte') }, detail: { load: () => import('$lib/modules/memoro/views/DetailView.svelte') }, @@ -534,6 +590,7 @@ registerApp({ id: 'questions', name: 'Questions', color: '#2563EB', + icon: Question, views: { list: { load: () => import('$lib/modules/questions/ListView.svelte') }, detail: { load: () => import('$lib/modules/questions/views/DetailView.svelte') }, @@ -544,6 +601,7 @@ registerApp({ id: 'skilltree', name: 'SkillTree', color: '#D946EF', + icon: Tree, views: { list: { load: () => import('$lib/modules/skilltree/ListView.svelte') }, detail: { load: () => import('$lib/modules/skilltree/views/DetailView.svelte') }, @@ -554,6 +612,7 @@ registerApp({ id: 'moodlit', name: 'Moodlit', color: '#F97316', + icon: Smiley, views: { list: { load: () => import('$lib/modules/moodlit/ListView.svelte') }, }, @@ -563,6 +622,7 @@ registerApp({ id: 'citycorners', name: 'CityCorners', color: '#14B8A6', + icon: Buildings, views: { list: { load: () => import('$lib/modules/citycorners/ListView.svelte') }, detail: { load: () => import('$lib/modules/citycorners/views/DetailView.svelte') }, @@ -573,6 +633,7 @@ registerApp({ id: 'uload', name: 'uLoad', color: '#0EA5E9', + icon: UploadSimple, views: { list: { load: () => import('$lib/modules/uload/ListView.svelte') }, detail: { load: () => import('$lib/modules/uload/views/DetailView.svelte') }, @@ -583,6 +644,7 @@ registerApp({ id: 'calc', name: 'Calc', color: '#6B7280', + icon: Calculator, views: { list: { load: () => import('$lib/modules/calc/ListView.svelte') }, }, @@ -592,6 +654,7 @@ registerApp({ id: 'automations', name: 'Automations', color: '#8B5CF6', + icon: Lightning, views: { list: { load: () => import('$lib/modules/automations/ListView.svelte') }, }, @@ -601,6 +664,7 @@ registerApp({ id: 'playground', name: 'Playground', color: '#9CA3AF', + icon: Sparkle, views: { list: { load: () => import('$lib/modules/playground/ListView.svelte') }, }, diff --git a/apps/mana/apps/web/src/lib/app-registry/types.ts b/apps/mana/apps/web/src/lib/app-registry/types.ts index 42edc910b..a92459d3b 100644 --- a/apps/mana/apps/web/src/lib/app-registry/types.ts +++ b/apps/mana/apps/web/src/lib/app-registry/types.ts @@ -36,6 +36,7 @@ export interface AppDescriptor { id: string; name: string; color: string; + icon?: AnyComponent; // -- Views -- views: { diff --git a/apps/mana/apps/web/src/lib/components/workbench/AppPage.svelte b/apps/mana/apps/web/src/lib/components/workbench/AppPage.svelte index ed62ae1a8..e3420ba80 100644 --- a/apps/mana/apps/web/src/lib/components/workbench/AppPage.svelte +++ b/apps/mana/apps/web/src/lib/components/workbench/AppPage.svelte @@ -41,6 +41,7 @@ }: Props = $props(); let app = $derived(getApp(appId)); + let appIcon = $derived(app?.icon); let appName = $derived.by(() => { const key = `apps.${appId}`; const translated = $_(key); @@ -300,7 +301,9 @@ {heightPx} {maximized} title={appName} + titleHref={`/${appId}`} color={appColor} + icon={appIcon} {onClose} {onMinimize} {onMaximize}