feat(icons): migrate to phosphor-svelte for all icon usage

- Replace custom Icon.svelte component with phosphor-svelte library
- Remove iconPaths.ts with manually maintained SVG paths
- Update @manacore/shared-icons to re-export phosphor-svelte
- Migrate shared-ui Modal and ConfirmationModal components
- Migrate shared-theme-ui ThemeToggle and ThemeModeSelector
- Migrate all chat app components to use Phosphor Icons
- All apps now use consistent icon API: <IconName size={24} weight="bold" />

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Till-JS 2025-11-29 07:02:59 +01:00
parent 655da23d14
commit b97149ac12
17 changed files with 255 additions and 451 deletions

View file

@ -1,4 +1,6 @@
<script lang="ts">
import { PaperPlaneTilt } from '@manacore/shared-icons';
interface Props {
onSend: (message: string) => void;
disabled?: boolean;
@ -66,14 +68,7 @@
disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-blue-600
transition-colors"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"
/>
</svg>
<PaperPlaneTilt size={20} weight="bold" />
</button>
</div>
<p class="text-xs text-gray-500 dark:text-gray-400 text-center mt-2">

View file

@ -2,6 +2,7 @@
import { page } from '$app/stores';
import { conversationsStore } from '$lib/stores/conversations.svelte';
import type { Conversation } from '@chat/types';
import { Plus } from '@manacore/shared-icons';
interface Props {
conversations: Conversation[];
@ -42,9 +43,7 @@
bg-blue-600 hover:bg-blue-700 text-white rounded-lg
font-medium transition-colors"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
<Plus size={20} weight="bold" />
Neuer Chat
</a>
</div>

View file

@ -3,6 +3,7 @@
import type { Message } from '@chat/types';
import MessageBubble from './MessageBubble.svelte';
import TypingIndicator from './TypingIndicator.svelte';
import { ChatCircleDots } from '@manacore/shared-icons';
interface Props {
messages: Message[];
@ -34,14 +35,7 @@
<div bind:this={containerEl} class="flex-1 overflow-y-auto px-4 py-6">
{#if messages.length === 0}
<div class="flex flex-col items-center justify-center h-full text-gray-500 dark:text-gray-400">
<svg class="w-16 h-16 mb-4 opacity-50" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.5"
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
/>
</svg>
<ChatCircleDots size={64} weight="light" class="mb-4 opacity-50" />
<p class="text-lg font-medium">Keine Nachrichten</p>
<p class="text-sm">Starte eine Konversation!</p>
</div>

View file

@ -1,5 +1,6 @@
<script lang="ts">
import type { AIModel } from '@chat/types';
import { CaretDown } from '@manacore/shared-icons';
interface Props {
models: AIModel[];
@ -36,8 +37,6 @@
{/if}
</select>
<div class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
<CaretDown size={16} weight="bold" class="text-gray-500" />
</div>
</div>

View file

@ -1,5 +1,6 @@
<script lang="ts">
import type { Space } from '@chat/types';
import { UsersThree, DotsThreeVertical, Gear, Trash, SignOut } from '@manacore/shared-icons';
interface Props {
space: Space;
@ -46,19 +47,7 @@
<div class="flex items-start justify-between gap-3">
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2 mb-1">
<svg
class="w-5 h-5 text-blue-500 flex-shrink-0"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
/>
</svg>
<UsersThree size={20} weight="bold" class="text-blue-500 flex-shrink-0" />
<h3 class="text-base font-semibold text-gray-900 dark:text-white truncate">
{space.name}
</h3>
@ -90,14 +79,7 @@
hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
aria-label="Optionen"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z"
/>
</svg>
<DotsThreeVertical size={20} weight="bold" />
</button>
{#if showMenu}
@ -116,20 +98,7 @@
hover:bg-gray-100 dark:hover:bg-gray-700"
role="menuitem"
>
<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="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
<Gear size={16} weight="bold" />
Einstellungen
</button>
<button
@ -138,14 +107,7 @@
hover:bg-red-50 dark:hover:bg-red-900/20"
role="menuitem"
>
<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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
/>
</svg>
<Trash size={16} weight="bold" />
Löschen
</button>
{:else}
@ -155,14 +117,7 @@
hover:bg-red-50 dark:hover:bg-red-900/20"
role="menuitem"
>
<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="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"
/>
</svg>
<SignOut size={16} weight="bold" />
Verlassen
</button>
{/if}

View file

@ -1,5 +1,6 @@
<script lang="ts">
import type { Template } from '@chat/types';
import { Star, PencilSimple, Trash } from '@manacore/shared-icons';
interface Props {
template: Template;
@ -61,14 +62,7 @@
title="Als Standard setzen"
aria-label="Als Standard setzen"
>
<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="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z"
/>
</svg>
<Star size={16} weight="bold" />
</button>
{/if}
<button
@ -77,14 +71,7 @@
title="Bearbeiten"
aria-label="Bearbeiten"
>
<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="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"
/>
</svg>
<PencilSimple size={16} weight="bold" />
</button>
<button
onclick={() => onDelete(template.id)}
@ -92,14 +79,7 @@
title="Löschen"
aria-label="Löschen"
>
<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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
/>
</svg>
<Trash size={16} weight="bold" />
</button>
</div>
</div>

View file

@ -3,14 +3,99 @@
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import { authStore } from '$lib/stores/auth.svelte';
import { theme } from '$lib/stores/theme';
import {
isSidebarMode as sidebarModeStore,
isNavCollapsed as collapsedStore,
} from '$lib/stores/navigation';
import { PillNavigation } from '@manacore/shared-ui';
import type { PillNavItem } from '@manacore/shared-ui';
import type { LayoutData } from './$types';
let { children, data }: { children: any; data: LayoutData } = $props();
let isChecking = $state(true);
let isSidebarMode = $state(false);
let isCollapsed = $state(false);
// Use theme store's isDark directly
let isDark = $derived(theme.isDark);
// Navigation items for Chat
const navItems: PillNavItem[] = [
{ href: '/', label: 'Chat', icon: 'home' },
{ href: '/templates', label: 'Templates', icon: 'document' },
{ href: '/spaces', label: 'Spaces', icon: 'building' },
{ href: '/documents', label: 'Dokumente', icon: 'archive' },
{ href: '/archive', label: 'Archiv', icon: 'list' },
];
// Navigation shortcuts (Ctrl+1-5)
const navRoutes = navItems.map((item) => item.href);
function handleKeydown(event: KeyboardEvent) {
const target = event.target as HTMLElement;
if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) {
return;
}
if ((event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey) {
const num = parseInt(event.key);
if (num >= 1 && num <= navRoutes.length) {
event.preventDefault();
const route = navRoutes[num - 1];
if (route) {
goto(route);
}
}
}
}
function handleModeChange(isSidebar: boolean) {
isSidebarMode = isSidebar;
sidebarModeStore.set(isSidebar);
if (typeof localStorage !== 'undefined') {
localStorage.setItem('chat-nav-sidebar', String(isSidebar));
}
}
function handleCollapsedChange(collapsed: boolean) {
isCollapsed = collapsed;
collapsedStore.set(collapsed);
if (typeof localStorage !== 'undefined') {
localStorage.setItem('chat-nav-collapsed', String(collapsed));
}
}
function handleToggleTheme() {
theme.toggleMode();
}
async function handleLogout() {
await authStore.signOut();
goto('/login');
}
// Check auth on mount and redirect if not authenticated
onMount(async () => {
// Initialize theme
theme.initialize();
// Initialize sidebar mode from localStorage
const savedSidebar = localStorage.getItem('chat-nav-sidebar');
if (savedSidebar === 'true') {
isSidebarMode = true;
sidebarModeStore.set(true);
}
// Initialize collapsed state from localStorage
const savedCollapsed = localStorage.getItem('chat-nav-collapsed');
if (savedCollapsed === 'true') {
isCollapsed = true;
collapsedStore.set(true);
}
await authStore.initialize();
if (!authStore.isAuthenticated) {
@ -21,112 +106,95 @@
isChecking = false;
});
async function handleSignOut() {
await authStore.signOut();
goto('/login');
}
</script>
<svelte:window onkeydown={handleKeydown} />
{#if isChecking}
<!-- Loading state while checking auth -->
<div class="min-h-screen bg-gray-50 dark:bg-gray-900 flex items-center justify-center">
<div
class="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900 dark:border-white"
></div>
<div class="flex min-h-screen items-center justify-center bg-gray-50 dark:bg-gray-900">
<div class="text-center">
<div
class="mb-4 inline-block h-12 w-12 animate-spin rounded-full border-4 border-solid border-blue-500 border-r-transparent"
></div>
<p class="text-gray-500 dark:text-gray-400">Laden...</p>
</div>
</div>
{:else}
<div class="min-h-screen bg-gray-50 dark:bg-gray-900">
<!-- Top Navigation -->
<nav class="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16">
<div class="flex items-center">
<a href="/" class="text-xl font-bold text-gray-900 dark:text-white"> ManaChat </a>
<div class="hidden sm:ml-8 sm:flex sm:space-x-4">
<a
href="/"
class="px-3 py-2 rounded-lg text-sm font-medium transition-colors
{$page.url.pathname === '/' || $page.url.pathname.startsWith('/chat')
? 'bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-white'
: 'text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'}"
>
Chat
</a>
<a
href="/templates"
class="px-3 py-2 rounded-lg text-sm font-medium transition-colors
{$page.url.pathname.startsWith('/templates')
? 'bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-white'
: 'text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'}"
>
Templates
</a>
<a
href="/spaces"
class="px-3 py-2 rounded-lg text-sm font-medium transition-colors
{$page.url.pathname.startsWith('/spaces')
? 'bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-white'
: 'text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'}"
>
Spaces
</a>
<a
href="/documents"
class="px-3 py-2 rounded-lg text-sm font-medium transition-colors
{$page.url.pathname.startsWith('/documents')
? 'bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-white'
: 'text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'}"
>
Dokumente
</a>
<a
href="/archive"
class="px-3 py-2 rounded-lg text-sm font-medium transition-colors
{$page.url.pathname.startsWith('/archive')
? 'bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-white'
: 'text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'}"
>
Archiv
</a>
</div>
</div>
<!-- Navigation Layout -->
<div class="layout-container">
<!-- Floating/Sidebar Pill Navigation -->
<PillNavigation
items={navItems}
currentPath={$page.url.pathname}
appName="ManaChat"
homeRoute="/"
onToggleTheme={handleToggleTheme}
{isDark}
{isSidebarMode}
onModeChange={handleModeChange}
{isCollapsed}
onCollapsedChange={handleCollapsedChange}
showThemeToggle={true}
showLanguageSwitcher={false}
showLogout={true}
onLogout={handleLogout}
primaryColor="#3b82f6"
/>
<div class="flex items-center gap-4">
{#if authStore.user}
<span class="text-sm text-gray-600 dark:text-gray-400 hidden sm:block">
{authStore.user.email}
</span>
{/if}
<a
href="/profile"
class="p-2 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
aria-label="Profil"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
/>
</svg>
</a>
<button
onclick={handleSignOut}
class="px-3 py-2 text-sm font-medium text-gray-600 dark:text-gray-300
hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
>
Abmelden
</button>
</div>
</div>
<!-- Main Content with dynamic padding based on nav mode -->
<main
class="main-content bg-gray-50 dark:bg-gray-900"
class:sidebar-mode={isSidebarMode && !isCollapsed}
class:floating-mode={!isSidebarMode && !isCollapsed}
>
<div class="content-wrapper">
{@render children()}
</div>
</nav>
<!-- Main Content -->
<main>
{@render children()}
</main>
</div>
{/if}
<style>
.layout-container {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.main-content {
flex: 1;
transition: all 300ms ease;
}
/* Floating nav mode - add top padding for fixed nav */
.main-content.floating-mode {
padding-top: 100px;
}
/* Sidebar mode - add left padding for sidebar nav */
.main-content.sidebar-mode {
padding-left: 180px;
}
.content-wrapper {
max-width: 80rem;
margin-left: auto;
margin-right: auto;
padding: 2rem 1rem;
}
@media (min-width: 640px) {
.content-wrapper {
padding-left: 1.5rem;
padding-right: 1.5rem;
}
}
@media (min-width: 1024px) {
.content-wrapper {
padding-left: 2rem;
padding-right: 2rem;
}
}
</style>

View file

@ -1,15 +1,16 @@
<script lang="ts">
import { onMount } from 'svelte';
import { conversationsStore } from '$lib/stores/conversations.svelte';
import { authStore } from '$lib/stores/auth.svelte';
import ConversationList from '$lib/components/chat/ConversationList.svelte';
import { X, List } from '@manacore/shared-icons';
let { children }: { children: any } = $props();
let showSidebar = $state(true);
onMount(async () => {
if (authStore.user) {
await conversationsStore.loadConversations(authStore.user.id);
// Wait for auth to be initialized before loading conversations
$effect(() => {
if (authStore.initialized && authStore.user) {
conversationsStore.loadConversations(authStore.user.id);
}
});
@ -27,23 +28,9 @@
aria-label={showSidebar ? 'Seitenleiste schließen' : 'Seitenleiste öffnen'}
>
{#if showSidebar}
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<X size={24} weight="bold" />
{:else}
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 6h16M4 12h16M4 18h16"
/>
</svg>
<List size={24} weight="bold" />
{/if}
</button>

View file

@ -1,5 +1,4 @@
<script lang="ts">
import { onMount } from 'svelte';
import { goto } from '$app/navigation';
import { chatService } from '$lib/services/chat';
import { conversationService } from '$lib/services/conversation';
@ -11,6 +10,7 @@
import ModelSelector from '$lib/components/chat/ModelSelector.svelte';
import { theme } from '$lib/stores/theme';
import type { AIModel, Message, Template } from '@chat/types';
import { FileText, Moon } from '@manacore/shared-icons';
let models = $state<AIModel[]>([]);
let templates = $state<Template[]>([]);
@ -21,11 +21,20 @@
let isLoading = $state(true);
let isSending = $state(false);
let error = $state<string | null>(null);
let dataLoaded = $state(false);
// Get selected template
const selectedTemplate = $derived(templates.find((t) => t.id === selectedTemplateId));
onMount(async () => {
// Wait for auth to be initialized before loading data
$effect(() => {
if (authStore.initialized && !dataLoaded) {
loadData();
}
});
async function loadData() {
dataLoaded = true;
models = await chatService.getModels();
if (models.length > 0) {
selectedModelId = models[0].id;
@ -37,7 +46,7 @@
}
isLoading = false;
});
}
async function handleSend(text: string) {
if (!authStore.user || !selectedModelId) return;
@ -175,14 +184,7 @@
hover:bg-opacity-80 disabled:opacity-50"
title="Dokumentmodus aktivieren"
>
<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="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/>
</svg>
<FileText size={16} weight="bold" />
Dokument
</button>
</div>
@ -195,14 +197,7 @@
hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
aria-label="Theme wechseln"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"
/>
</svg>
<Moon size={20} weight="bold" />
</button>
</div>
</div>

View file

@ -1,8 +1,8 @@
{
"name": "@manacore/shared-icons",
"version": "0.1.0",
"version": "1.0.0",
"private": true,
"description": "Shared Phosphor Icons (Bold) for Manacore SvelteKit web apps",
"description": "Phosphor Icons for all Manacore SvelteKit web apps",
"type": "module",
"svelte": "./src/index.ts",
"main": "./src/index.ts",
@ -11,13 +11,6 @@
".": {
"svelte": "./src/index.ts",
"default": "./src/index.ts"
},
"./Icon.svelte": {
"svelte": "./src/Icon.svelte",
"default": "./src/Icon.svelte"
},
"./iconPaths": {
"default": "./src/iconPaths.ts"
}
},
"scripts": {
@ -30,5 +23,8 @@
"svelte": "^5.16.6",
"svelte-check": "^4.2.1",
"typescript": "^5.9.3"
},
"dependencies": {
"phosphor-svelte": "^3.0.1"
}
}

View file

@ -1,39 +0,0 @@
<script lang="ts">
/**
* Shared Icon Component for Manacore Web Apps
* Uses Phosphor Icons (Bold weight)
*
* Usage:
* import { Icon } from '@manacore/shared-icons';
* <Icon name="user-plus" size={24} />
* <Icon name="sign-in" size={20} class="text-primary" />
*/
import { iconPaths } from './iconPaths';
interface Props {
name: keyof typeof iconPaths;
size?: number;
class?: string;
color?: string;
}
let { name, size = 24, class: className = '', color }: Props = $props();
const path = $derived(iconPaths[name]);
</script>
{#if path}
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
fill={color || 'currentColor'}
viewBox="0 0 256 256"
class={className}
aria-hidden="true"
>
{@html path}
</svg>
{:else}
<span class="text-red-500" title="Icon '{name}' not found"></span>
{/if}

View file

@ -1,148 +0,0 @@
/**
* Phosphor Icons (Bold weight) - Shared icons for Manacore web apps
*
* This is a centralized icon catalog for all SvelteKit applications.
* All icons use the Bold weight for consistency.
*
* To add new icons:
* 1. Find the icon at https://phosphoricons.com/
* 2. Copy the SVG content (the <path> tag) from the Bold variant
* 3. Add it to this file with a descriptive key
*
* Usage:
* import { Icon } from '@manacore/shared-icons';
* <Icon name="user-plus" size={24} />
*/
export const iconPaths = {
// Auth & User
'user-plus':
'<path d="M256,136a12,12,0,0,1-12,12h-8v8a12,12,0,0,1-24,0v-8h-8a12,12,0,0,1,0-24h8v-8a12,12,0,0,1,24,0v8h8A12,12,0,0,1,256,136Zm-54.81,56.28a12,12,0,1,1-18.38,15.44C169.12,191.42,145,172,108,172c-28.89,0-55.46,12.68-74.81,35.72a12,12,0,0,1-18.38-15.44A124.08,124.08,0,0,1,63.5,156.53a72,72,0,1,1,89,0A124,124,0,0,1,201.19,192.28ZM108,148a48,48,0,1,0-48-48A48.05,48.05,0,0,0,108,148Z"/>',
'sign-in':
'<path d="M144.49,136.49l-40,40a12,12,0,0,1-17-17L107,140H24a12,12,0,0,1,0-24h83L87.51,96.49a12,12,0,0,1,17-17l40,40A12,12,0,0,1,144.49,136.49ZM200,28H136a12,12,0,0,0,0,24h52V204H136a12,12,0,0,0,0,24h64a12,12,0,0,0,12-12V40A12,12,0,0,0,200,28Z"/>',
'sign-out':
'<path d="M116,216a12,12,0,0,1-12,12H48a20,20,0,0,1-20-20V48A20,20,0,0,1,48,28h56a12,12,0,0,1,0,24H52V204h52A12,12,0,0,1,116,216Zm108.49-96.49-40-40a12,12,0,0,0-17,17L187,116H104a12,12,0,0,0,0,24h83l-19.52,19.51a12,12,0,0,0,17,17l40-40A12,12,0,0,0,224.49,119.51Z"/>',
user: '<path d="M234.38,210a123.36,123.36,0,0,0-60.78-53.23,76,76,0,1,0-91.2,0A123.36,123.36,0,0,0,21.62,210a12,12,0,1,0,20.77,12c18.12-31.32,50.12-50,85.61-50s67.49,18.69,85.61,50a12,12,0,0,0,20.77-12ZM76,96a52,52,0,1,1,52,52A52.06,52.06,0,0,1,76,96Z"/>',
users:
'<path d="M125.18,156.94a64,64,0,1,0-82.36,0,100.23,100.23,0,0,0-39.49,32,12,12,0,0,0,20.54,12.08C36.18,184.88,55.25,172,76,172c22.44,0,41.84,14.31,54.86,40.39a12,12,0,1,0,21.19-11.17A99.33,99.33,0,0,0,125.18,156.94ZM44,108a40,40,0,1,1,40,40A40,40,0,0,1,44,108Zm206.1,97.67a12,12,0,0,1-16.78-2.57C221.44,183.6,202,172,180,172a41.86,41.86,0,0,0-13.54,2.24,12,12,0,0,1-7.84-22.68,64.36,64.36,0,0,0,35.69-27.15,64,64,0,1,0-43.49-45.85,12,12,0,0,1-23.71-3.15,88,88,0,1,1,73.54,84.28,99.33,99.33,0,0,1,26.84,44.53A12,12,0,0,1,250.1,205.67Z"/>',
// Navigation & Arrows
'arrow-left':
'<path d="M228,128a12,12,0,0,1-12,12H69l51.52,51.51a12,12,0,0,1-17,17l-72-72a12,12,0,0,1,0-17l72-72a12,12,0,0,1,17,17L69,116H216A12,12,0,0,1,228,128Z"/>',
'arrow-right':
'<path d="M224.49,136.49l-72,72a12,12,0,0,1-17-17L187,140H40a12,12,0,0,1,0-24H187L135.51,64.48a12,12,0,0,1,17-17l72,72A12,12,0,0,1,224.49,136.49Z"/>',
'arrow-up':
'<path d="M208.49,152.49a12,12,0,0,1-17,17L140,118v98a12,12,0,0,1-24,0V118L64.49,169.51a12,12,0,0,1-17-17l72-72a12,12,0,0,1,17,0Z"/>',
'arrow-down':
'<path d="M208.49,168.49l-72,72a12,12,0,0,1-17,0l-72-72a12,12,0,0,1,17-17L116,203V40a12,12,0,0,1,24,0V203l51.51-51.52a12,12,0,0,1,17,17Z"/>',
'caret-down':
'<path d="M216.49,104.49l-80,80a12,12,0,0,1-17,0l-80-80a12,12,0,0,1,17-17L128,159l71.51-71.52a12,12,0,0,1,17,17Z"/>',
'caret-up':
'<path d="M216.49,168.49a12,12,0,0,1-17,17L128,114,56.49,185.51a12,12,0,0,1-17-17l80-80a12,12,0,0,1,17,0Z"/>',
'caret-left':
'<path d="M168.49,199.51a12,12,0,0,1-17,17l-80-80a12,12,0,0,1,0-17l80-80a12,12,0,0,1,17,17L97,128Z"/>',
'caret-right':
'<path d="M184.49,136.49l-80,80a12,12,0,0,1-17-17L159,128,87.51,56.49a12,12,0,0,1,17-17l80,80A12,12,0,0,1,184.49,136.49Z"/>',
// Actions
plus: '<path d="M228,128a12,12,0,0,1-12,12H140v76a12,12,0,0,1-24,0V140H40a12,12,0,0,1,0-24h76V40a12,12,0,0,1,24,0v76h76A12,12,0,0,1,228,128Z"/>',
minus: '<path d="M228,128a12,12,0,0,1-12,12H40a12,12,0,0,1,0-24H216A12,12,0,0,1,228,128Z"/>',
x: '<path d="M208.49,191.51a12,12,0,0,1-17,17L128,145,64.49,208.49a12,12,0,0,1-17-17L111,128,47.51,64.49a12,12,0,0,1,17-17L128,111l63.51-63.52a12,12,0,0,1,17,17L145,128Z"/>',
check:
'<path d="M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z"/>',
trash:
'<path d="M216,48H180V36A28,28,0,0,0,152,8H104A28,28,0,0,0,76,36V48H40a12,12,0,0,0,0,24h4V208a20,20,0,0,0,20,20H192a20,20,0,0,0,20-20V72h4a12,12,0,0,0,0-24ZM100,36a4,4,0,0,1,4-4h48a4,4,0,0,1,4,4V48H100Zm88,168H68V72H188ZM116,104v64a12,12,0,0,1-24,0V104a12,12,0,0,1,24,0Zm48,0v64a12,12,0,0,1-24,0V104a12,12,0,0,1,24,0Z"/>',
copy: '<path d="M216,28H88A12,12,0,0,0,76,40V76H40A12,12,0,0,0,28,88V216a12,12,0,0,0,12,12H168a12,12,0,0,0,12-12V180h36a12,12,0,0,0,12-12V40A12,12,0,0,0,216,28ZM156,204H52V100H156Zm48-48H180V88a12,12,0,0,0-12-12H100V52H204Z"/>',
// Media
play: '<path d="M240.82,114.67,88.82,26.67a16,16,0,0,0-16.12-.41A15.68,15.68,0,0,0,64,40.36V215.64a15.68,15.68,0,0,0,8.7,14.1,16,16,0,0,0,16.12-.41l152-88a15.76,15.76,0,0,0,0-26.66ZM88,199.93V56.07L216.16,128Z"/>',
pause:
'<path d="M200,28H160a20,20,0,0,0-20,20V208a20,20,0,0,0,20,20h40a20,20,0,0,0,20-20V48A20,20,0,0,0,200,28Zm-4,176H164V52h32ZM96,28H56A20,20,0,0,0,36,48V208a20,20,0,0,0,20,20H96a20,20,0,0,0,20-20V48A20,20,0,0,0,96,28ZM92,204H60V52H92Z"/>',
microphone:
'<path d="M128,176a52.06,52.06,0,0,0,52-52V64a52,52,0,0,0-104,0v60A52.06,52.06,0,0,0,128,176ZM100,64a28,28,0,0,1,56,0v60a28,28,0,0,1-56,0Zm112,56a12,12,0,0,1-24,0,76,76,0,0,0-152,0,12,12,0,0,1-24,0,100.11,100.11,0,0,1,88-99.26V8a12,12,0,0,1,24,0V20.74A100.11,100.11,0,0,1,212,120Z"/>',
'skip-back':
'<path d="M201.75,30.52a20,20,0,0,0-20.3.53L68,102V40a12,12,0,0,0-24,0V216a12,12,0,0,0,24,0V154l113.45,71A20,20,0,0,0,212,208.12V47.88A19.86,19.86,0,0,0,201.75,30.52ZM188,200.73,71.7,128,188,55.27Z"/>',
'skip-forward':
'<path d="M200,28a12,12,0,0,0-12,12v62l-113.45-71A20,20,0,0,0,44,47.88V208.12A20,20,0,0,0,74.55,225L188,154v62a12,12,0,0,0,24,0V40A12,12,0,0,0,200,28ZM68,200.73V55.27L184.3,128Z"/>',
// Edit
pencil:
'<path d="M230.14,70.54,185.46,25.85a20,20,0,0,0-28.29,0L33.86,149.17A19.85,19.85,0,0,0,28,163.31V208a20,20,0,0,0,20,20H92.69a19.86,19.86,0,0,0,14.14-5.86L230.14,98.82a20,20,0,0,0,0-28.28ZM91,204H52V165l84-84,39,39ZM192,103,153,64l18.34-18.34,39,39Z"/>',
pen: '<path d="M228.12,67.07,188.93,27.88a20,20,0,0,0-28.28,0L33.88,154.64A19.86,19.86,0,0,0,28,168.78V208a20,20,0,0,0,20,20H87.22a19.86,19.86,0,0,0,14.14-5.86L228.12,95.35a20,20,0,0,0,0-28.28ZM91,204H52V165l84-84,39,39ZM192,103,153,64l21.52-21.52L213.49,81.5Z"/>',
'note-pencil':
'<path d="M228,128v80a20,20,0,0,1-20,20H48a20,20,0,0,1-20-20V48A20,20,0,0,1,48,28h80a12,12,0,0,1,0,24H52V204H204V132a12,12,0,0,1,24,0Zm-26.34-84.49a12,12,0,0,0-17,0L112.3,115.86a20,20,0,0,0-5.59,11.63l-5.46,32.74a12,12,0,0,0,14.79,14.05l32.73-8.73a20.08,20.08,0,0,0,11.07-6.41l72.4-72.39a12,12,0,0,0,0-17Zm-47.29,63.9-18.64,5-3.53-3.52,4.95-18.63,40.31-40.31,16.72,16.72Z"/>',
// Files & Folders
folder:
'<path d="M216,68H130.67L102.93,51.2a20.12,20.12,0,0,0-11.07-3.2H40A20,20,0,0,0,20,68V200a20,20,0,0,0,20,20H216a20,20,0,0,0,20-20V88A20,20,0,0,0,216,68Zm-4,128H44V92H212Z"/>',
'folder-open':
'<path d="M248.23,112.31l-60,112A12,12,0,0,1,177.6,228H36a20,20,0,0,1-20-20V88A20,20,0,0,1,36,68H88a11.93,11.93,0,0,1,7.88,2.93l29.51,25.89A4,4,0,0,0,128,98h88a12,12,0,0,1,10.62,6.38A11.88,11.88,0,0,1,248.23,112.31ZM218.72,122H123.88L96,99.47,40,92v112H172.52Z"/>',
file: '<path d="M213.66,82.34l-56-56A8,8,0,0,0,152,24H56A20,20,0,0,0,36,44V212a20,20,0,0,0,20,20H200a20,20,0,0,0,20-20V88A8,8,0,0,0,213.66,82.34ZM160,51.31,188.69,80H160ZM196,208H60V48h76V92a12,12,0,0,0,12,12h48Z"/>',
// UI Elements
'dots-three':
'<path d="M144,128a16,16,0,1,1-16-16A16,16,0,0,1,144,128Zm56-16a16,16,0,1,0,16,16A16,16,0,0,0,200,112ZM56,112a16,16,0,1,0,16,16A16,16,0,0,0,56,112Z"/>',
'dots-three-vertical':
'<path d="M128,112a16,16,0,1,0,16,16A16,16,0,0,0,128,112Zm0-56a16,16,0,1,0-16-16A16,16,0,0,0,128,56Zm0,144a16,16,0,1,0,16,16A16,16,0,0,0,128,200Z"/>',
list: '<path d="M228,128a12,12,0,0,1-12,12H40a12,12,0,0,1,0-24H216A12,12,0,0,1,228,128ZM40,76H216a12,12,0,0,0,0-24H40a12,12,0,0,0,0,24ZM216,180H40a12,12,0,0,0,0,24H216a12,12,0,0,0,0-24Z"/>',
'magnifying-glass':
'<path d="M232.49,215.51,185,168a92.12,92.12,0,1,0-17,17l47.53,47.54a12,12,0,0,0,17-17ZM44,112a68,68,0,1,1,68,68A68.07,68.07,0,0,1,44,112Z"/>',
// Misc
key: '<path d="M196,76a16,16,0,1,1-16-16A16,16,0,0,1,196,76Zm48,22.74A84.3,84.3,0,0,1,160.11,180H160a83.52,83.52,0,0,1-23.65-3.38l-7.86,7.87A12,12,0,0,1,120,188H108v12a12,12,0,0,1-12,12H84v12a12,12,0,0,1-12,12H40a20,20,0,0,1-20-20V187.31a19.86,19.86,0,0,1,5.86-14.14l53.52-53.52A84,84,0,1,1,244,98.74ZM202.43,53.57A59.48,59.48,0,0,0,158,36c-32,1-58,27.89-58,59.89a59.69,59.69,0,0,0,4.2,22.19,12,12,0,0,1-2.55,13.21L44,189v23H60V200a12,12,0,0,1,12-12H84V176a12,12,0,0,1,12-12h19l9.65-9.65a12,12,0,0,1,13.22-2.55A59.58,59.58,0,0,0,160,156h.08c32,0,58.87-26.07,59.89-58A59.55,59.55,0,0,0,202.43,53.57Z"/>',
info: '<path d="M108,84a16,16,0,1,1,16,16A16,16,0,0,1,108,84Zm128,44A108,108,0,1,1,128,20,108.12,108.12,0,0,1,236,128Zm-24,0a84,84,0,1,0-84,84A84.09,84.09,0,0,0,212,128Zm-72,36.68V132a20,20,0,0,0-20-20,12,12,0,0,0-4,23.32V168a20,20,0,0,0,20,20,12,12,0,0,0,4-23.32Z"/>',
tag: '<path d="M246.66,123.56,201,55.13A20,20,0,0,0,184.06,44H39.38A20.07,20.07,0,0,0,20,63.38V192.62A20.07,20.07,0,0,0,39.38,212H184.06a20,20,0,0,0,16.94-11.13l45.66-68.43A12,12,0,0,0,246.66,123.56ZM180.91,188H44V68H180.91l40,60ZM96,84a16,16,0,1,1-16,16A16,16,0,0,1,96,84Z"/>',
share:
'<path d="M236,200a12,12,0,0,1-12,12H32a12,12,0,0,1,0-24H224A12,12,0,0,1,236,200ZM88.49,80.49,116,53v83a12,12,0,0,0,24,0V53l27.51,27.52a12,12,0,1,0,17-17l-48-48a12,12,0,0,0-17,0l-48,48a12,12,0,1,0,17,17Z"/>',
download:
'<path d="M228,144v64a20,20,0,0,1-20,20H48a20,20,0,0,1-20-20V144a12,12,0,0,1,24,0v60H204V144a12,12,0,0,1,24,0Zm-108.49,8.49a12,12,0,0,0,17,0l40-40a12,12,0,0,0-17-17L140,115V24a12,12,0,0,0-24,0v91L96.49,95.51a12,12,0,0,0-17,17Z"/>',
upload:
'<path d="M228,144v64a20,20,0,0,1-20,20H48a20,20,0,0,1-20-20V144a12,12,0,0,1,24,0v60H204V144a12,12,0,0,1,24,0Zm-108.49-4.49a12,12,0,0,0,17,0l40-40a12,12,0,0,0-17-17L140,101V24a12,12,0,0,0-24,0v77L96.49,81.51a12,12,0,1,0-17,17Z"/>',
link: '<path d="M122.34,109.66a12,12,0,0,1,0-17l32-32a52,52,0,0,1,73.56,73.56L196,166.14a52,52,0,0,1-73.56,0,12,12,0,1,1,17-17,28,28,0,0,0,39.6,0l31.89-31.88a28,28,0,0,0-39.6-39.6l-32,32A12,12,0,0,1,122.34,109.66Zm-20.68,36.68-32,32a28,28,0,0,0,39.6,39.6l31.89-31.88a28,28,0,0,0,0-39.6,12,12,0,0,0-17,17,4,4,0,0,1,0,5.66L92.22,200.94a4,4,0,0,1-5.66,0,4,4,0,0,1,0-5.66l32-32a12,12,0,0,0-17-17Z"/>',
eye: '<path d="M251.89,122.47a140.13,140.13,0,0,0-46.65-50.37C185,60,157.49,52,128,52S71,60,50.76,72.1a140.13,140.13,0,0,0-46.65,50.37,12.44,12.44,0,0,0,0,11.06,140.13,140.13,0,0,0,46.65,50.37C71,196,98.51,204,128,204s57-8,77.24-20.1a140.13,140.13,0,0,0,46.65-50.37A12.44,12.44,0,0,0,251.89,122.47ZM128,180c-42.86,0-78.64-23.44-105.73-63C49.36,77.44,85.14,54,128,54s78.64,23.44,105.73,63C206.64,156.56,170.86,180,128,180Zm0-98a34,34,0,1,0,34,34A34,34,0,0,0,128,82Zm0,44a10,10,0,1,1,10-10A10,10,0,0,1,128,126Z"/>',
'eye-slash':
'<path d="M55.58,37.76a12,12,0,0,0-17,17l20.7,22.77C29.09,102.4,11.72,137.48,4.09,151.56a12.44,12.44,0,0,0,0,11.06A140.13,140.13,0,0,0,50.74,213c20.26,12.12,47.75,20.1,77.26,20.1a127.36,127.36,0,0,0,51.71-10.52l22.71,25a12,12,0,0,0,17-17Zm49.77,120A34,34,0,0,1,94,128a33.55,33.55,0,0,1,1-8.27l45.64,50.21A33.56,33.56,0,0,1,128,172,34,34,0,0,1,105.35,157.76Zm116.81,53.3a12,12,0,0,1-6.16,16,128,128,0,0,1-51.62,9.78,34,34,0,0,1-45.31-50.35L98.11,163.44a58.05,58.05,0,0,0,79.46,19.74,34,34,0,0,0,12.33-12A127.85,127.85,0,0,0,206.16,211.06ZM237.94,133.56a140.13,140.13,0,0,1-46.65,50.37A130.12,130.12,0,0,1,128,203.93a57.6,57.6,0,0,1-15.47-1.9l-19.24-21.16a82.06,82.06,0,0,1,0-117.74A82,82,0,0,1,128,50h.09a133.85,133.85,0,0,1,63,19.93,140.13,140.13,0,0,1,46.65,50.37A12.44,12.44,0,0,1,237.94,133.56ZM215.82,128C188.83,87.44,153,64,128,64a58.1,58.1,0,0,0-40.66,16.55L128,123.3a10,10,0,0,1,0,14.13,10.19,10.19,0,0,1-14.13,0l-40.66,44.72A82,82,0,0,0,128,204c25,0,60.83-23.44,87.82-64C215.82,128,215.82,128,215.82,128Z"/>',
// Alias for eye-slash
'eye-off':
'<path d="M55.58,37.76a12,12,0,0,0-17,17l20.7,22.77C29.09,102.4,11.72,137.48,4.09,151.56a12.44,12.44,0,0,0,0,11.06A140.13,140.13,0,0,0,50.74,213c20.26,12.12,47.75,20.1,77.26,20.1a127.36,127.36,0,0,0,51.71-10.52l22.71,25a12,12,0,0,0,17-17Zm49.77,120A34,34,0,0,1,94,128a33.55,33.55,0,0,1,1-8.27l45.64,50.21A33.56,33.56,0,0,1,128,172,34,34,0,0,1,105.35,157.76Zm116.81,53.3a12,12,0,0,1-6.16,16,128,128,0,0,1-51.62,9.78,34,34,0,0,1-45.31-50.35L98.11,163.44a58.05,58.05,0,0,0,79.46,19.74,34,34,0,0,0,12.33-12A127.85,127.85,0,0,0,206.16,211.06ZM237.94,133.56a140.13,140.13,0,0,1-46.65,50.37A130.12,130.12,0,0,1,128,203.93a57.6,57.6,0,0,1-15.47-1.9l-19.24-21.16a82.06,82.06,0,0,1,0-117.74A82,82,0,0,1,128,50h.09a133.85,133.85,0,0,1,63,19.93,140.13,140.13,0,0,1,46.65,50.37A12.44,12.44,0,0,1,237.94,133.56ZM215.82,128C188.83,87.44,153,64,128,64a58.1,58.1,0,0,0-40.66,16.55L128,123.3a10,10,0,0,1,0,14.13,10.19,10.19,0,0,1-14.13,0l-40.66,44.72A82,82,0,0,0,128,204c25,0,60.83-23.44,87.82-64C215.82,128,215.82,128,215.82,128Z"/>',
lock: '<path d="M208,76H180V56a52,52,0,0,0-104,0V76H48A20,20,0,0,0,28,96V208a20,20,0,0,0,20,20H208a20,20,0,0,0,20-20V96A20,20,0,0,0,208,76ZM100,56a28,28,0,0,1,56,0V76H100ZM204,204H52V100H204Zm-72-76a16,16,0,1,1-16-16A16,16,0,0,1,132,128Z"/>',
star: '<path d="M234.29,114.85l-45,38.83L203,211.75a20.81,20.81,0,0,1-7.24,21.31,20.25,20.25,0,0,1-21.7,1.32L128,213.15,81.94,234.38a20.25,20.25,0,0,1-21.7-1.32,20.81,20.81,0,0,1-7.24-21.31l13.72-58.07-45-38.83A20.86,20.86,0,0,1,33.31,86.56l59.51-5.16,23.17-55.23a20.88,20.88,0,0,1,38,0l23.17,55.23,59.51,5.16a20.86,20.86,0,0,1,11.58,28.29Zm-74.81-28.8-18.28-43.54a.84.84,0,0,0-1.58,0L121.34,86.05a12,12,0,0,1-10.09,7.34L51.58,98.83a.87.87,0,0,0-.49,1.53L89.83,131.8a12,12,0,0,1,3.86,11.93L82.84,201.1a.87.87,0,0,0,.34.94.88.88,0,0,0,1,0l43.45-19.95a12.09,12.09,0,0,1,10.68,0l43.45,19.95a.88.88,0,0,0,1,0,.87.87,0,0,0,.34-.94l-10.85-57.37a12,12,0,0,1,3.86-11.93l38.74-33.44a.87.87,0,0,0-.49-1.53l-59.67-5.17A12,12,0,0,1,159.48,86.05Z"/>',
heart:
'<path d="M178,36c-21.4,0-39.31,10.64-50,27.14C117.31,46.64,99.4,36,78,36a66.08,66.08,0,0,0-66,66c0,72.25,105.53,130.44,110.66,132.94a12,12,0,0,0,10.68,0C138.47,232.44,244,174.25,244,102A66.08,66.08,0,0,0,178,36Zm-5.42,142.24c-18.3,10.88-38.26,21.14-44.58,24.78-6.32-3.64-26.28-13.9-44.58-24.78C56.75,159.16,28,136,28,102A42,42,0,0,1,70,60c18.12,0,33.05,9.48,42.64,27.22a12,12,0,0,0,20.72,0C142.94,69.48,157.88,60,176,60a42,42,0,0,1,42,42C220,136,191.25,159.16,172.58,178.24Z"/>',
bell: '<path d="M225.29,165.93C216.61,151,212,129.57,212,104a84,84,0,0,0-168,0c0,25.58-4.59,47-13.27,61.93A20.08,20.08,0,0,0,30.66,186a19.77,19.77,0,0,0,17.79,11H94.2a36,36,0,0,0,67.6,0H207.55a19.77,19.77,0,0,0,17.79-11A20.08,20.08,0,0,0,225.29,165.93ZM128,216a12,12,0,0,1-11.62-9h23.24A12,12,0,0,1,128,216Zm-76.55-39C62.19,157.17,68,133.1,68,104a60,60,0,0,1,120,0c0,29.1,5.81,53.17,16.55,73Z"/>',
calendar:
'<path d="M208,28H188V24a12,12,0,0,0-24,0v4H92V24a12,12,0,0,0-24,0v4H48A20,20,0,0,0,28,48V208a20,20,0,0,0,20,20H208a20,20,0,0,0,20-20V48A20,20,0,0,0,208,28ZM68,52a12,12,0,0,0,24,0h72a12,12,0,0,0,24,0h16V76H52V52ZM52,204V100H204V204Z"/>',
clock:
'<path d="M128,20A108,108,0,1,0,236,128,108.12,108.12,0,0,0,128,20Zm0,192a84,84,0,1,1,84-84A84.09,84.09,0,0,1,128,212Zm76-84a12,12,0,0,1-12,12H128a12,12,0,0,1-12-12V72a12,12,0,0,1,24,0v44h52A12,12,0,0,1,204,128Z"/>',
image:
'<path d="M216,36H40A20,20,0,0,0,20,56V200a20,20,0,0,0,20,20H216a20,20,0,0,0,20-20V56A20,20,0,0,0,216,36Zm-4,24V158.75l-26.07-26.06a20,20,0,0,0-28.28,0l-23.23,23.23-59.8-59.8a20,20,0,0,0-28.28,0L44,98.75V60ZM44,196V121.66l56-56,59.8,59.8a20,20,0,0,0,28.28,0l23.23-23.23L212,103V196Zm28-96a20,20,0,1,0-20-20A20,20,0,0,0,72,100Z"/>',
'shield-check':
'<path d="M208,36H48A20,20,0,0,0,28,56v56c0,54.29,26.32,87.22,48.4,105.29,23.71,19.39,47.44,26,48.44,26.29a12.1,12.1,0,0,0,6.32,0c1-.28,24.73-6.9,48.44-26.29,22.08-18.07,48.4-51,48.4-105.29V56A20,20,0,0,0,208,36Zm-4,76c0,35.71-13.09,64.69-38.91,86.15A126.28,126.28,0,0,1,128,219.38a126.14,126.14,0,0,1-37.09-21.23C65.09,176.69,52,147.71,52,112V60H204ZM79.51,144.49a12,12,0,1,1,17-17L112,143l47.51-47.52a12,12,0,0,1,17,17l-56,56a12,12,0,0,1-17,0Z"/>',
envelope:
'<path d="M224,44H32A12,12,0,0,0,20,56V192a20,20,0,0,0,20,20H216a20,20,0,0,0,20-20V56A12,12,0,0,0,224,44Zm-96,83.72L62.85,68h130.3ZM92.79,128,44,172.72V83.28Zm17.76,16.28,9.34,8.57a12,12,0,0,0,16.22,0l9.34-8.57L193.15,188H62.85ZM163.21,128,212,83.28v89.44Z"/>',
'envelope-open':
'<path d="M228.44,89.34l-96-64a12,12,0,0,0-13.32,0L23.56,89.34A12,12,0,0,0,20,98.67V200a20,20,0,0,0,20,20H216a20,20,0,0,0,20-20V98.67A12,12,0,0,0,228.44,89.34ZM128,49.81l68.32,45.55L132,136.83a8,8,0,0,1-8,0L59.68,95.36ZM44,196V112.52l64.55,38.73a32,32,0,0,0,30.9,0L204,112.52V196Z"/>',
'mail-open':
'<path d="M228.44,89.34l-96-64a12,12,0,0,0-13.32,0L23.56,89.34A12,12,0,0,0,20,98.67V200a20,20,0,0,0,20,20H216a20,20,0,0,0,20-20V98.67A12,12,0,0,0,228.44,89.34ZM128,49.81l68.32,45.55L132,136.83a8,8,0,0,1-8,0L59.68,95.36ZM44,196V112.52l64.55,38.73a32,32,0,0,0,30.9,0L204,112.52V196Z"/>',
'arrows-left-right':
'<path d="M216.49,184.49l-32,32a12,12,0,0,1-17-17L179,188H48a12,12,0,0,1,0-24H179l-11.52-11.51a12,12,0,0,1,17-17l32,32A12,12,0,0,1,216.49,184.49Zm-145-64a12,12,0,0,0,17-17L77,92H208a12,12,0,0,0,0-24H77L88.49,56.49a12,12,0,0,0-17-17l-32,32a12,12,0,0,0,0,17Z"/>',
globe:
'<path d="M128,20A108,108,0,1,0,236,128,108.12,108.12,0,0,0,128,20Zm0,192a84,84,0,1,1,84-84A84.09,84.09,0,0,1,128,212Zm0-160a76,76,0,1,0,76,76A76.08,76.08,0,0,0,128,52Zm0,128a51.6,51.6,0,0,1-19.08-39h38.16A51.6,51.6,0,0,1,128,180Zm-19.08-63a51.6,51.6,0,0,1,38.16,0A51.6,51.6,0,0,1,108.92,117Zm38.16-24H108.92a51.6,51.6,0,0,1,38.16,0ZM92,128a52.06,52.06,0,0,1,8.92-29.31A75.51,75.51,0,0,0,76.39,128,75.51,75.51,0,0,0,100.92,157.31,52.06,52.06,0,0,1,92,128Zm63.08,29.31A52.06,52.06,0,0,1,164,128a52.06,52.06,0,0,1-8.92,29.31A75.51,75.51,0,0,0,179.61,128,75.51,75.51,0,0,0,155.08,157.31Z"/>',
gear: '<path d="M128,76a52,52,0,1,0,52,52A52.06,52.06,0,0,0,128,76Zm0,80a28,28,0,1,1,28-28A28,28,0,0,1,128,156Zm92-28a92.11,92.11,0,0,1-2.33,20.84A12,12,0,0,1,206.5,158l-16.29-6.73a68.35,68.35,0,0,1-24.91,14.4L163.37,182a12,12,0,0,1-10.06,9.89,92.75,92.75,0,0,1-25.44,0A12,12,0,0,1,117.81,182l-1.93-16.29a68.35,68.35,0,0,1-24.91-14.4L74.68,158a12,12,0,0,1-11.17-9.16,92.11,92.11,0,0,1,0-41.68A12,12,0,0,1,74.68,98l16.29,6.73a68.35,68.35,0,0,1,24.91-14.4L117.81,74a12,12,0,0,1,10.06-9.89,92.75,92.75,0,0,1,25.44,0A12,12,0,0,1,163.37,74l1.93,16.29a68.35,68.35,0,0,1,24.91,14.4L206.5,98a12,12,0,0,1,11.17,9.16A92.11,92.11,0,0,1,220,128Z"/>',
warning:
'<path d="M128,20A108,108,0,1,0,236,128,108.12,108.12,0,0,0,128,20Zm0,192a84,84,0,1,1,84-84A84.09,84.09,0,0,1,128,212Zm-12-80V80a12,12,0,0,1,24,0v52a12,12,0,0,1-24,0Zm28,40a16,16,0,1,1-16-16A16,16,0,0,1,144,172Z"/>',
'alert-triangle':
'<path d="M236.8,188.09,149.35,36.22h0a24.76,24.76,0,0,0-42.7,0L19.2,188.09a23.51,23.51,0,0,0,0,23.72A24.35,24.35,0,0,0,40.55,224h174.9a24.35,24.35,0,0,0,21.33-12.19A23.51,23.51,0,0,0,236.8,188.09ZM222.93,203.8a.6.6,0,0,1-.48.2H40.55a.6.6,0,0,1-.48-.2.51.51,0,0,1,0-.52L127.62,51.37a.72.72,0,0,1,1.24,0l87.55,151.91A.51.51,0,0,1,222.93,203.8ZM116,144V104a12,12,0,0,1,24,0v40a12,12,0,0,1-24,0Zm28,40a16,16,0,1,1-16-16A16,16,0,0,1,144,184Z"/>',
'alert-circle':
'<path d="M128,20A108,108,0,1,0,236,128,108.12,108.12,0,0,0,128,20Zm0,192a84,84,0,1,1,84-84A84.09,84.09,0,0,1,128,212Zm-12-80V80a12,12,0,0,1,24,0v52a12,12,0,0,1-24,0Zm28,40a16,16,0,1,1-16-16A16,16,0,0,1,144,172Z"/>',
question:
'<path d="M128,20A108,108,0,1,0,236,128,108.12,108.12,0,0,0,128,20Zm0,192a84,84,0,1,1,84-84A84.09,84.09,0,0,1,128,212Zm0-140a32,32,0,0,0-32,32,12,12,0,0,0,24,0,8,8,0,1,1,8,8,12,12,0,0,0-12,12v8a12,12,0,0,0,24,0v-1.26A32,32,0,0,0,128,72Zm12,100a16,16,0,1,1-16-16A16,16,0,0,1,140,172Z"/>',
house:
'<path d="M224,120v96a8,8,0,0,1-8,8H160a8,8,0,0,1-8-8V164a4,4,0,0,0-4-4H108a4,4,0,0,0-4,4v52a8,8,0,0,1-8,8H40a8,8,0,0,1-8-8V120a16,16,0,0,1,5.17-11.78l80-75.48.11-.11a16,16,0,0,1,21.53,0,1.14,1.14,0,0,0,.11.11l80,75.48A16,16,0,0,1,224,120Z"/>',
music:
'<path d="M212.92,17.69a12,12,0,0,0-9.73-2.8L94.66,30.79A12,12,0,0,0,84,42.68V174.78A48,48,0,1,0,108,208V86.68l84.47-12.07a12,12,0,0,0,0-23.62L108,63.06V42.68l84.47-12.07A48,48,0,1,0,212.92,17.69ZM60,232a24,24,0,1,1,24-24A24,24,0,0,1,60,232Zm132-64a24,24,0,1,1,24-24A24,24,0,0,1,192,168Z"/>',
refresh:
'<path d="M228,128a100,100,0,0,1-98.66,100H128a99.39,99.39,0,0,1-68.62-27.29,12,12,0,0,1,16.48-17.46,76,76,0,1,0-1.57-109c-.13.13-.25.25-.39.37L48,99.27V72a12,12,0,0,0-24,0v48a12,12,0,0,0,12,12H84a12,12,0,0,0,0-24H55.93l27.35-25.29A51.5,51.5,0,0,1,128,52a52,52,0,1,1,0,104,12,12,0,0,0,0,24A100,100,0,0,0,228,128Z"/>',
} as const;
export type IconName = keyof typeof iconPaths;

View file

@ -1,2 +1,16 @@
export { default as Icon } from './Icon.svelte';
export { iconPaths, type IconName } from './iconPaths';
/**
* @manacore/shared-icons
*
* Phosphor Icons for all Manacore SvelteKit web apps
* https://phosphoricons.com
*
* Usage:
* import { House, User, Gear, Plus } from '@manacore/shared-icons';
*
* <House size={24} weight="bold" />
* <User size={20} weight="regular" class="text-blue-500" />
*
* Available weights: thin, light, regular, bold, fill, duotone
*/
export * from 'phosphor-svelte';

View file

@ -1,6 +1,6 @@
<script lang="ts">
import type { ThemeStore, ThemeMode } from '@manacore/shared-theme';
import { Icon } from '@manacore/shared-icons';
import { Sun, Moon, Desktop } from '@manacore/shared-icons';
interface Props {
/** Theme store instance */
@ -11,15 +11,15 @@
let { theme, class: className = '' }: Props = $props();
const modes: { mode: ThemeMode; icon: string; label: string }[] = [
{ mode: 'light', icon: 'sun', label: 'Light' },
{ mode: 'dark', icon: 'moon', label: 'Dark' },
{ mode: 'system', icon: 'monitor', label: 'System' },
const modes: { mode: ThemeMode; label: string }[] = [
{ mode: 'light', label: 'Light' },
{ mode: 'dark', label: 'Dark' },
{ mode: 'system', label: 'System' },
];
</script>
<div class="mode-selector {className}" role="radiogroup" aria-label="Theme mode">
{#each modes as { mode, icon, label }}
{#each modes as { mode, label }}
{@const isActive = theme.mode === mode}
<button
type="button"
@ -30,7 +30,13 @@
aria-checked={isActive}
aria-label="{label} mode"
>
<Icon name={icon} size={16} />
{#if mode === 'light'}
<Sun size={16} weight="bold" />
{:else if mode === 'dark'}
<Moon size={16} weight="bold" />
{:else}
<Desktop size={16} weight="bold" />
{/if}
<span class="label">{label}</span>
</button>
{/each}

View file

@ -1,6 +1,6 @@
<script lang="ts">
import type { ThemeStore } from '@manacore/shared-theme';
import { Icon } from '@manacore/shared-icons';
import { Sun, Moon } from '@manacore/shared-icons';
interface Props {
/** Theme store instance */
@ -31,9 +31,9 @@
title={showTooltip ? getTooltipText() : undefined}
>
{#if theme.effectiveMode === 'dark'}
<Icon name="sun" {size} class="theme-toggle-icon" />
<Sun {size} weight="bold" class="theme-toggle-icon" />
{:else}
<Icon name="moon" {size} class="theme-toggle-icon" />
<Moon {size} weight="bold" class="theme-toggle-icon" />
{/if}
</button>

View file

@ -31,7 +31,7 @@
* ```
*/
import { Icon } from '@manacore/shared-icons';
import { Warning, WarningCircle, Info } from '@manacore/shared-icons';
import Modal from './Modal.svelte';
import { Text, Button } from '../atoms';
@ -72,20 +72,17 @@
const variantConfig: Record<
ConfirmationVariant,
{ iconName: string; iconColor: string; buttonVariant: 'danger' | 'primary' }
{ iconColor: string; buttonVariant: 'danger' | 'primary' }
> = {
danger: {
iconName: 'alert-triangle',
iconColor: 'text-red-500',
buttonVariant: 'danger',
},
warning: {
iconName: 'alert-circle',
iconColor: 'text-yellow-500',
buttonVariant: 'primary',
},
info: {
iconName: 'info',
iconColor: 'text-blue-500',
buttonVariant: 'primary',
},
@ -100,8 +97,14 @@
<Modal {visible} {onClose} {title} maxWidth="sm">
{#snippet icon()}
<div class="p-2 rounded-full bg-menu-hover">
<Icon name={config.iconName} size={20} class={config.iconColor} />
<div class="p-2 rounded-full bg-menu-hover {config.iconColor}">
{#if variant === 'danger'}
<Warning size={20} weight="bold" />
{:else if variant === 'warning'}
<WarningCircle size={20} weight="bold" />
{:else}
<Info size={20} weight="bold" />
{/if}
</div>
{/snippet}

View file

@ -1,6 +1,6 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { Icon } from '@manacore/shared-icons';
import { X } from '@manacore/shared-icons';
import Text from '../atoms/Text.svelte';
interface Props {
@ -87,7 +87,7 @@
class="p-2 rounded-full hover:bg-menu-hover transition-colors"
aria-label="Close"
>
<Icon name="x" size={20} class="text-theme-muted" />
<X size={20} weight="bold" class="text-theme-muted" />
</button>
</div>
{/if}