feat: add shared Phosphor IconPicker, migrate habits from emoji to icons, add photos upload

- Add curated icon registry (73 Phosphor icons, 8 categories) in shared-icons
- Add DynamicIcon atom and IconPicker molecule in shared-ui
- Migrate habits module from emoji strings to Phosphor icon names
- Add Dexie version(2) migration for emoji→icon field rename
- Replace inline SVGs in habits with Phosphor components
- Add drag-and-drop photo upload to Photos workbench ListView
- Add blob: to CSP img-src for upload previews
- Add dev:media script and include mana-media in dev:manacore:servers
- Add ./toast export to shared-ui package.json

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-03 21:37:01 +02:00
parent ebfc2facaa
commit 8218037841
24 changed files with 1489 additions and 721 deletions

View file

@ -0,0 +1,251 @@
/**
* Curated Phosphor Icon Registry
*
* Provides a namecomponent mapping for dynamic icon rendering
* and a categorized icon list for picker UIs.
*
* Named imports from the barrel export Vite/SvelteKit tree-shakes
* unused icons at build time, so only the ~73 curated icons ship.
*/
import type { Component } from 'svelte';
import {
Airplane,
Alarm,
AppleLogo,
Barbell,
Bed,
BeerStein,
Bell,
Bicycle,
BookOpen,
Brain,
CalendarBlank,
Camera,
Campfire,
CarSimple,
Cat,
ChatCircle,
CheckSquare,
Cigarette,
Clock,
CloudSun,
Coffee,
Confetti,
CookingPot,
Crown,
Dog,
Drop,
Envelope,
Eye,
FilmStrip,
Fire,
FirstAid,
Flag,
Flower,
ForkKnife,
GameController,
Globe,
GraduationCap,
Headphones,
Heart,
Heartbeat,
House,
Leaf,
Lightning,
MapPin,
Martini,
Medal,
Microphone,
Moon,
MusicNote,
Notebook,
PaintBrush,
Palette,
PawPrint,
PencilSimple,
PersonSimple,
PersonSimpleRun,
PersonSimpleTaiChi,
PersonSimpleWalk,
Pill,
Pizza,
Rocket,
Shower,
Smiley,
Sparkle,
Star,
Sun,
Sword,
Target,
Timer,
Tooth,
Tree,
Trophy,
Wind,
Wine,
} from 'phosphor-svelte';
// ─── Icon Registry ───────────────────────────────────────────
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type IconComponent = Component<any>;
export const ICON_REGISTRY: Record<string, IconComponent> = {
airplane: Airplane,
alarm: Alarm,
'apple-logo': AppleLogo,
barbell: Barbell,
bed: Bed,
'beer-stein': BeerStein,
bell: Bell,
bicycle: Bicycle,
'book-open': BookOpen,
brain: Brain,
'calendar-blank': CalendarBlank,
camera: Camera,
campfire: Campfire,
'car-simple': CarSimple,
cat: Cat,
'chat-circle': ChatCircle,
'check-square': CheckSquare,
cigarette: Cigarette,
clock: Clock,
'cloud-sun': CloudSun,
coffee: Coffee,
confetti: Confetti,
'cooking-pot': CookingPot,
crown: Crown,
dog: Dog,
drop: Drop,
envelope: Envelope,
eye: Eye,
'film-strip': FilmStrip,
fire: Fire,
'first-aid': FirstAid,
flag: Flag,
flower: Flower,
'fork-knife': ForkKnife,
'game-controller': GameController,
globe: Globe,
'graduation-cap': GraduationCap,
headphones: Headphones,
heart: Heart,
heartbeat: Heartbeat,
house: House,
leaf: Leaf,
lightning: Lightning,
'map-pin': MapPin,
martini: Martini,
medal: Medal,
microphone: Microphone,
moon: Moon,
'music-note': MusicNote,
notebook: Notebook,
'paint-brush': PaintBrush,
palette: Palette,
'paw-print': PawPrint,
'pencil-simple': PencilSimple,
'person-simple': PersonSimple,
'person-simple-run': PersonSimpleRun,
'person-simple-tai-chi': PersonSimpleTaiChi,
'person-simple-walk': PersonSimpleWalk,
pill: Pill,
pizza: Pizza,
rocket: Rocket,
shower: Shower,
smiley: Smiley,
sparkle: Sparkle,
star: Star,
sun: Sun,
sword: Sword,
target: Target,
timer: Timer,
tooth: Tooth,
tree: Tree,
trophy: Trophy,
wind: Wind,
wine: Wine,
};
export type IconName = keyof typeof ICON_REGISTRY;
// ─── Categories ──────────────────────────────────────────────
export const ICON_CATEGORIES: Record<string, string[]> = {
Gesundheit: ['heart', 'heartbeat', 'pill', 'first-aid', 'tooth', 'shower', 'bed'],
Aktivitat: [
'barbell',
'person-simple-run',
'person-simple-walk',
'bicycle',
'person-simple-tai-chi',
'person-simple',
'sword',
],
'Essen & Trinken': [
'coffee',
'drop',
'beer-stein',
'wine',
'martini',
'pizza',
'apple-logo',
'fork-knife',
'cooking-pot',
'cigarette',
],
Lernen: ['book-open', 'brain', 'pencil-simple', 'graduation-cap', 'notebook', 'eye', 'globe'],
Freizeit: [
'music-note',
'headphones',
'microphone',
'game-controller',
'film-strip',
'camera',
'palette',
'paint-brush',
],
Natur: ['tree', 'flower', 'leaf', 'sun', 'moon', 'cloud-sun', 'wind', 'campfire', 'paw-print'],
Produktivitat: [
'target',
'lightning',
'rocket',
'clock',
'timer',
'alarm',
'check-square',
'flag',
'calendar-blank',
],
Sonstiges: [
'star',
'sparkle',
'fire',
'smiley',
'confetti',
'trophy',
'medal',
'crown',
'bell',
'house',
'envelope',
'chat-circle',
'map-pin',
'airplane',
'car-simple',
'dog',
'cat',
],
};
// ─── Lookup Functions ────────────────────────────────────────
export function getIconComponent(name: string): IconComponent | null {
return ICON_REGISTRY[name] ?? null;
}
export function getAllIconNames(): string[] {
return Object.keys(ICON_REGISTRY);
}

View file

@ -14,3 +14,5 @@
*/
export * from 'phosphor-svelte';
export { ICON_REGISTRY, ICON_CATEGORIES, getIconComponent, getAllIconNames } from './icon-registry';
export type { IconName } from './icon-registry';