refactor(apps): final SVG-to-Phosphor pass for photos, clock, mukke, inventar

Replace remaining inline SVG icons in photos (25 SVGs), mukke player
controls, clock world-clock, and inventar settings. All remaining
inline SVGs are now exclusively spinners, brand logos, or dynamic
icon rendering via {@html}.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-03-31 12:41:43 +02:00
parent 59e535af94
commit 25e39620ec
16 changed files with 65 additions and 350 deletions

View file

@ -6,6 +6,7 @@
import { POPULAR_TIMEZONES } from '@clock/shared';
import type { WorldClock } from '@clock/shared';
import WorldMap from '$lib/components/WorldMap.svelte';
import { Monitor } from '@manacore/shared-icons';
// Get live query data from layout context
const allWorldClocks: { readonly value: WorldClock[] } = getContext('worldClocks');
@ -159,21 +160,7 @@
onclick={() => (showMap = !showMap)}
title={showMap ? 'Karte ausblenden' : 'Karte anzeigen'}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="12" cy="12" r="10" />
<path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" />
<path d="M2 12h20" />
</svg>
<Monitor size={20} />
</button>
<button class="btn btn-primary btn-sm" onclick={openAddModal}>
+ {$_('worldClock.add')}

View file

@ -3,8 +3,7 @@
import { authStore } from '$lib/stores/auth.svelte';
import { userSettings } from '$lib/stores/user-settings.svelte';
import { APP_VERSION } from '$lib/version';
import {
import { Envelope, SignOut, Tag, User } from '@manacore/shared-icons';
import { Info, SignOut, Tag, User, import { Envelope } from '@manacore/shared-icons';
SettingsPage,
SettingsSection,
SettingsCard,
@ -58,14 +57,7 @@
<SettingsSection title="Über">
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<Info size={20} />
{/snippet}
<SettingsCard>
<SettingsRow label="Version" border={false}>

View file

@ -1,7 +1,7 @@
<script lang="ts">
import { playerStore } from '$lib/stores/player.svelte';
import FrequencyBars from '$lib/visualizer/FrequencyBars.svelte';
import { MusicNote, Pause, Play, X } from '@manacore/shared-icons';
import { MusicNote, Pause, Play, Warning, X } from '@manacore/shared-icons';
let progress = $derived(
playerStore.duration > 0 ? (playerStore.currentTime / playerStore.duration) * 100 : 0
@ -22,14 +22,7 @@
<div
class="flex items-center gap-2 px-4 py-2 bg-red-500/10 border-b border-red-500/20 text-sm text-red-500"
>
<svg class="w-4 h-4 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<Warning size={16} class="shrink-0" />
<span class="truncate">{playerStore.error}</span>
<button
onclick={() => playerStore.clearError()}

View file

@ -2,7 +2,14 @@
import { audioStore } from '$lib/stores/audio.svelte';
import { editorStore } from '$lib/stores/editor.svelte';
import { formatTime } from '$lib/utils/time-format';
import { Pause, Play, SkipBack, SkipForward } from '@manacore/shared-icons';
import {
MagnifyingGlassMinus,
MagnifyingGlassPlus,
Pause,
Play,
SkipBack,
SkipForward,
} from '@manacore/shared-icons';
interface Props {
onPlay?: () => void;
@ -145,14 +152,7 @@
class="p-2 rounded-lg hover:bg-surface-hover transition-colors"
aria-label="Zoom out"
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM13 10H7"
/>
</svg>
<MagnifyingGlassMinus size={16} />
</button>
<span class="text-xs text-foreground-secondary min-w-[40px] text-center">
{Math.round(editorStore.zoom * 100)}%
@ -162,14 +162,7 @@
class="p-2 rounded-lg hover:bg-surface-hover transition-colors"
aria-label="Zoom in"
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v6m3-3H7"
/>
</svg>
<MagnifyingGlassPlus size={16} />
</button>
</div>

View file

@ -1,7 +1,7 @@
<script lang="ts">
import { libraryStore } from '$lib/stores/library.svelte';
import { MukkeEvents } from '@manacore/shared-utils/analytics';
import { Check, DownloadSimple, Plus, X } from '@manacore/shared-icons';
import { Check, DownloadSimple, Plus, Warning, X } from '@manacore/shared-icons';
interface UploadFile {
file: File;
@ -187,19 +187,7 @@
{:else if uf.status === 'uploaded'}
<Check size={20} class="text-green-500" />
{:else if uf.status === 'error'}
<svg
class="w-5 h-5 text-red-500 flex-shrink-0"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<Warning size={20} class="text-red-500 flex-shrink-0" />
{:else}
<div class="w-5 h-5 rounded-full bg-foreground-secondary/20 flex-shrink-0"></div>
{/if}

View file

@ -1,5 +1,6 @@
<script lang="ts">
import type { Album } from '@photos/shared';
import { Folder } from '@manacore/shared-icons';
interface Props {
album: Album;
@ -14,19 +15,7 @@
<img src={album.coverUrl} alt={album.name} class="album-cover" />
{:else}
<div class="album-placeholder">
<svg
xmlns="http://www.w3.org/2000/svg"
width="48"
height="48"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
>
<path
d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"
/>
</svg>
<Folder size={20} />
</div>
{/if}
<div class="album-overlay">

View file

@ -1,5 +1,6 @@
<script lang="ts">
import { _ } from 'svelte-i18n';
import { X } from '@manacore/shared-icons';
interface Props {
onClose: () => void;
@ -45,18 +46,7 @@
<header class="modal-header">
<h2 class="text-lg font-semibold">{$_('albums.create')}</h2>
<button class="close-btn" onclick={onClose} type="button">
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path d="M18 6 6 18" />
<path d="m6 6 12 12" />
</svg>
<X size={20} />
</button>
</header>

View file

@ -1,6 +1,7 @@
<script lang="ts">
import type { Photo } from '@photos/shared';
import { photoStore } from '$lib/stores/photos.svelte';
import { Heart, Image } from '@manacore/shared-icons';
interface Props {
photo: Photo;
@ -41,19 +42,7 @@
{#if error}
<div class="error-placeholder">
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
>
<rect width="18" height="18" x="3" y="3" rx="2" ry="2" />
<circle cx="9" cy="9" r="2" />
<path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" />
</svg>
<Image size={20} />
</div>
{/if}
@ -65,19 +54,7 @@
onclick={handleFavoriteClick}
title={photo.isFavorited ? 'Remove from favorites' : 'Add to favorites'}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill={photo.isFavorited ? 'currentColor' : 'none'}
stroke="currentColor"
stroke-width="2"
>
<path
d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"
/>
</svg>
<Heart size={20} />
</button>
</div>
</div>

View file

@ -3,6 +3,14 @@
import { format } from 'date-fns';
import type { Photo } from '@photos/shared';
import { photoStore } from '$lib/stores/photos.svelte';
import {
ArrowDown,
CaretRight,
DownloadSimple,
Heart,
ShareNetwork,
X,
} from '@manacore/shared-icons';
interface Props {
photo: Photo;
@ -49,18 +57,7 @@
<div class="lightbox-container">
<!-- Close button -->
<button class="close-btn" onclick={onClose} title={$_('common.close')}>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path d="M18 6 6 18" />
<path d="m6 6 12 12" />
</svg>
<X size={20} />
</button>
<!-- Main image -->
@ -79,52 +76,18 @@
title={$_('photo.hideInfo')}
aria-label={$_('photo.hideInfo')}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path d="m15 18-6-6 6-6" />
</svg>
<CaretRight size={20} />
</button>
</div>
<!-- Actions -->
<div class="info-actions">
<button class="action-btn" class:favorited={photo.isFavorited} onclick={handleFavorite}>
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill={photo.isFavorited ? 'currentColor' : 'none'}
stroke="currentColor"
stroke-width="2"
>
<path
d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"
/>
</svg>
<Heart size={20} />
{photo.isFavorited ? $_('photo.unfavorite') : $_('photo.favorite')}
</button>
<a class="action-btn" href={photo.url} download target="_blank" rel="noopener noreferrer">
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
<polyline points="7 10 12 15 17 10" />
<line x1="12" x2="12" y1="15" y2="3" />
</svg>
<DownloadSimple size={20} />
{$_('photo.download')}
</a>
</div>
@ -195,19 +158,7 @@
rel="noopener noreferrer"
>
View on map
<svg
xmlns="http://www.w3.org/2000/svg"
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
<polyline points="15 3 21 3 21 9" />
<line x1="10" x2="21" y1="14" y2="3" />
</svg>
<ShareNetwork size={20} />
</a>
</div>
{/if}
@ -229,19 +180,7 @@
</div>
{:else}
<button class="show-info-btn" onclick={() => (showInfo = true)} title="Show info">
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<circle cx="12" cy="12" r="10" />
<path d="M12 16v-4" />
<path d="M12 8h.01" />
</svg>
<ArrowDown size={20} />
</button>
{/if}
</div>

View file

@ -1,5 +1,6 @@
<script lang="ts">
import { _ } from 'svelte-i18n';
import { DownloadSimple } from '@manacore/shared-icons';
interface Props {
onFilesSelected: (files: File[]) => void;
@ -66,22 +67,17 @@
<div class="dropzone-content">
<div class="dropzone-icon">
<svg
xmlns="http://www.w3.org/2000/svg"
width="48"
height="48"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
>
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
<polyline points="17 8 12 3 7 8" />
<line x1="12" x2="12" y1="3" y2="15" />
</svg>
<DownloadSimple size={20} />
</div>
<p class="dropzone-text">{$_('upload.dropzone')}</p>
<button type="button" class="btn btn-primary mt-4" onclick={(e) => { e.stopPropagation(); openFilePicker(); }}>
<button
type="button"
class="btn btn-primary mt-4"
onclick={(e) => {
e.stopPropagation();
openFilePicker();
}}
>
{$_('upload.selectFiles')}
</button>
</div>

View file

@ -5,6 +5,7 @@
import PhotoGrid from '$lib/components/gallery/PhotoGrid.svelte';
import PhotoDetailModal from '$lib/components/gallery/PhotoDetailModal.svelte';
import FilterBar from '$lib/components/filters/FilterBar.svelte';
import { Image } from '@manacore/shared-icons';
let showFilters = $state(false);
@ -71,22 +72,7 @@
</div>
{:else if photoStore.photos.length === 0 && !photoStore.loading}
<div class="empty-state">
<svg
xmlns="http://www.w3.org/2000/svg"
width="64"
height="64"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
class="text-muted-foreground"
>
<rect width="18" height="18" x="3" y="3" rx="2" ry="2" />
<circle cx="9" cy="9" r="2" />
<path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" />
</svg>
<Image size={20} class="text-muted-foreground" />
<h2 class="text-lg font-medium mt-4">{$_('gallery.empty')}</h2>
<p class="text-muted-foreground">{$_('gallery.emptyHint')}</p>
</div>

View file

@ -7,6 +7,7 @@
import AlbumGrid from '$lib/components/albums/AlbumGrid.svelte';
import CreateAlbumModal from '$lib/components/albums/CreateAlbumModal.svelte';
import type { Album, AlbumItem } from '@photos/shared';
import { Folder, Minus } from '@manacore/shared-icons';
const allAlbums: { readonly value: Album[] } = getContext('albums');
const allAlbumItems: { readonly value: AlbumItem[] } = getContext('albumItems');
@ -36,43 +37,14 @@
<header class="page-header">
<h1 class="text-2xl font-bold">{$_('albums.title')}</h1>
<button class="btn btn-primary" onclick={() => (showCreateModal = true)}>
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="mr-1"
>
<path d="M5 12h14" />
<path d="M12 5v14" />
</svg>
<Minus size={20} class="mr-1" />
{$_('albums.create')}
</button>
</header>
{#if albums.length === 0}
<div class="empty-state">
<svg
xmlns="http://www.w3.org/2000/svg"
width="64"
height="64"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
class="text-muted-foreground"
>
<path
d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"
/>
</svg>
<Folder size={20} class="text-muted-foreground" />
<h2 class="text-lg font-medium mt-4">{$_('albums.empty')}</h2>
<p class="text-muted-foreground">{$_('albums.emptyHint')}</p>
<button class="btn btn-primary mt-4" onclick={() => (showCreateModal = true)}>

View file

@ -9,6 +9,7 @@
import PhotoGrid from '$lib/components/gallery/PhotoGrid.svelte';
import PhotoDetailModal from '$lib/components/gallery/PhotoDetailModal.svelte';
import type { Album, AlbumItem, Photo } from '@photos/shared';
import { CaretRight, Minus } from '@manacore/shared-icons';
const allAlbums: { readonly value: Album[] } = getContext('albums');
const allAlbumItems: { readonly value: AlbumItem[] } = getContext('albumItems');
@ -49,19 +50,7 @@
<header class="page-header">
<div class="flex items-center gap-3">
<button class="icon-btn" onclick={() => goto('/albums')} title="Back">
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m15 18-6-6 6-6" />
</svg>
<CaretRight size={20} />
</button>
<div>
<h1 class="text-2xl font-bold">{currentAlbum.name}</h1>
@ -80,21 +69,7 @@
onclick={handleDeleteAlbum}
title={$_('albums.delete')}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M3 6h18" />
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" />
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" />
</svg>
<Minus size={20} />
</button>
</div>
</header>

View file

@ -6,6 +6,7 @@
import PhotoDetailModal from '$lib/components/gallery/PhotoDetailModal.svelte';
import type { Photo } from '@photos/shared';
import type { LocalFavorite } from '$lib/data/local-store';
import { Heart } from '@manacore/shared-icons';
const allFavorites: { readonly value: LocalFavorite[] } = getContext('favorites');
@ -38,22 +39,7 @@
{#if favorites.length === 0}
<div class="empty-state">
<svg
xmlns="http://www.w3.org/2000/svg"
width="64"
height="64"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
class="text-muted-foreground"
>
<path
d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"
/>
</svg>
<Heart size={20} class="text-muted-foreground" />
<h2 class="text-lg font-medium mt-4">{$_('favorites.empty')}</h2>
<p class="text-muted-foreground">{$_('favorites.emptyHint')}</p>
</div>

View file

@ -4,6 +4,7 @@
import { setLocale, supportedLocales, type SupportedLocale } from '$lib/i18n';
import { THEME_DEFINITIONS, DEFAULT_THEME_VARIANTS } from '@manacore/shared-theme';
import { APP_VERSION } from '$lib/version';
import { Moon, Sun } from '@manacore/shared-icons';
let selectedLocale = $state<SupportedLocale>('de');
@ -36,25 +37,7 @@
class:active={theme.mode === 'light'}
onclick={() => theme.setMode('light')}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<circle cx="12" cy="12" r="4" />
<path d="M12 2v2" />
<path d="M12 20v2" />
<path d="m4.93 4.93 1.41 1.41" />
<path d="m17.66 17.66 1.41 1.41" />
<path d="M2 12h2" />
<path d="M20 12h2" />
<path d="m6.34 17.66-1.41 1.41" />
<path d="m19.07 4.93-1.41 1.41" />
</svg>
<Sun size={20} />
Light
</button>
<button
@ -62,17 +45,7 @@
class:active={theme.mode === 'dark'}
onclick={() => theme.setMode('dark')}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" />
</svg>
<Moon size={20} />
Dark
</button>
<button

View file

@ -6,6 +6,7 @@
const MEDIA_URL = import.meta.env.PUBLIC_MANA_MEDIA_URL || 'http://localhost:3015';
import UploadDropzone from '$lib/components/upload/UploadDropzone.svelte';
import { X } from '@manacore/shared-icons';
interface UploadFile {
file: File;
@ -130,18 +131,7 @@
<div class="file-overlay">
{#if file.status === 'pending'}
<button class="remove-btn" onclick={() => removeFile(index)}>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path d="M18 6 6 18" />
<path d="m6 6 12 12" />
</svg>
<X size={20} />
</button>
{:else if file.status === 'uploading'}
<div class="progress-ring">
@ -173,18 +163,7 @@
</div>
{:else if file.status === 'error'}
<div class="status-icon error" title={file.error}>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path d="M18 6 6 18" />
<path d="m6 6 12 12" />
</svg>
<X size={20} />
</div>
{/if}
</div>