{
- if (isInteractive && onclick && (e.key === 'Enter' || e.key === ' ')) {
- e.preventDefault();
- onclick(e as unknown as MouseEvent);
- }
- }}
->
- {#if header}
-
- {/if}
-
-
- {@render children()}
+{#if interactive}
+
+{:else}
+
+ {#if children}{@render children()}{/if}
-
- {#if footer}
-
- {/if}
-
+{/if}
diff --git a/packages/shared-ui/src/atoms/DynamicIcon.stories.svelte b/packages/shared-ui/src/atoms/DynamicIcon.stories.svelte
new file mode 100644
index 000000000..0ed7f1900
--- /dev/null
+++ b/packages/shared-ui/src/atoms/DynamicIcon.stories.svelte
@@ -0,0 +1,112 @@
+
+
+
+ {#snippet children()}
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+
+
+
+
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+ {#each ALL_ICONS as iconName (iconName)}
+
+
+ {iconName}
+
+ {/each}
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+
+ erledigt
+
+
+ warnung
+
+
+ fehler
+
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+ "
+ size="lg"
+ />
+ {/snippet}
+
diff --git a/packages/shared-ui/src/atoms/DynamicIcon.svelte b/packages/shared-ui/src/atoms/DynamicIcon.svelte
index 4679fdd36..646044ebc 100644
--- a/packages/shared-ui/src/atoms/DynamicIcon.svelte
+++ b/packages/shared-ui/src/atoms/DynamicIcon.svelte
@@ -1,26 +1,200 @@
-
-{#if IconComponent}
-
-{/if}
+
+
+
diff --git a/packages/shared-ui/src/atoms/Skeleton.stories.svelte b/packages/shared-ui/src/atoms/Skeleton.stories.svelte
new file mode 100644
index 000000000..430217f1a
--- /dev/null
+++ b/packages/shared-ui/src/atoms/Skeleton.stories.svelte
@@ -0,0 +1,58 @@
+
+
+
+ {#snippet children()}
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+
+
+
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+ {#each Array(3) as _, i (i)}
+
+ {/each}
+
+ {/snippet}
+
diff --git a/packages/shared-ui/src/atoms/Skeleton.svelte b/packages/shared-ui/src/atoms/Skeleton.svelte
new file mode 100644
index 000000000..4fe0cbc03
--- /dev/null
+++ b/packages/shared-ui/src/atoms/Skeleton.svelte
@@ -0,0 +1,111 @@
+
+
+{#if shape === 'card'}
+
+{:else if count > 1}
+
+ {#each Array(count) as _, i (i)}
+
+ {/each}
+
+{:else}
+
+{/if}
+
+
diff --git a/packages/shared-ui/src/atoms/Spinner.stories.svelte b/packages/shared-ui/src/atoms/Spinner.stories.svelte
new file mode 100644
index 000000000..ccce7838e
--- /dev/null
+++ b/packages/shared-ui/src/atoms/Spinner.stories.svelte
@@ -0,0 +1,49 @@
+
+
+
+ {#snippet children()}
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+
+
+
+
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+
+
+
+
+
+
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+
+ Lade Karten …
+
+ {/snippet}
+
diff --git a/packages/shared-ui/src/atoms/Spinner.svelte b/packages/shared-ui/src/atoms/Spinner.svelte
new file mode 100644
index 000000000..37cc5d3e8
--- /dev/null
+++ b/packages/shared-ui/src/atoms/Spinner.svelte
@@ -0,0 +1,93 @@
+
+
+
+
+
diff --git a/packages/shared-ui/src/atoms/Text.stories.svelte b/packages/shared-ui/src/atoms/Text.stories.svelte
new file mode 100644
index 000000000..05451dd82
--- /dev/null
+++ b/packages/shared-ui/src/atoms/Text.stories.svelte
@@ -0,0 +1,53 @@
+
+
+
+ {#snippet children()}
+ Standardtext im Body-Modus.
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+ Default — Standard-Foreground
+ Muted — sekundärer Text, Caption, Meta
+ Primary — Akzent-Farbe
+ Error — Fehler, kritisch
+ Success — Erfolg
+ Warning — Warnung
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+ 2xl bold (h1)
+ xl semibold (h2)
+ lg medium (h3)
+ base normal
+ sm muted (caption)
+ xs muted (meta)
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+
+ Sehr langer Text, der über die verfügbare Breite hinausgeht und mit Ellipsis abgeschnitten
+ werden soll.
+
+
+ {/snippet}
+
diff --git a/packages/shared-ui/src/atoms/Text.svelte b/packages/shared-ui/src/atoms/Text.svelte
index 1b259e8e3..9150f6180 100644
--- a/packages/shared-ui/src/atoms/Text.svelte
+++ b/packages/shared-ui/src/atoms/Text.svelte
@@ -1,54 +1,111 @@
-
- {@render children?.()}
-
+
+ {#if children}{@render children()}{/if}
+
+
+
diff --git a/packages/shared-ui/src/atoms/index.ts b/packages/shared-ui/src/atoms/index.ts
index 1ff0414a7..ffa1f5f0f 100644
--- a/packages/shared-ui/src/atoms/index.ts
+++ b/packages/shared-ui/src/atoms/index.ts
@@ -1,5 +1,7 @@
-export { default as Text } from './Text.svelte';
export { default as Button } from './Button.svelte';
export { default as Badge } from './Badge.svelte';
export { default as Card } from './Card.svelte';
+export { default as Text } from './Text.svelte';
+export { default as Skeleton } from './Skeleton.svelte';
+export { default as Spinner } from './Spinner.svelte';
export { default as DynamicIcon } from './DynamicIcon.svelte';
diff --git a/packages/shared-ui/src/bottom-stack/BottomStack.svelte b/packages/shared-ui/src/bottom-stack/BottomStack.svelte
deleted file mode 100644
index 8c7503ec4..000000000
--- a/packages/shared-ui/src/bottom-stack/BottomStack.svelte
+++ /dev/null
@@ -1,121 +0,0 @@
-
-
-{#if children}
-
- {@render children()}
-
-{/if}
-
-{#if top}
-
- {@render top()}
-
-{/if}
-
-
diff --git a/packages/shared-ui/src/bottom-stack/MinimizedTabs.svelte b/packages/shared-ui/src/bottom-stack/MinimizedTabs.svelte
deleted file mode 100644
index 82b4be7a1..000000000
--- a/packages/shared-ui/src/bottom-stack/MinimizedTabs.svelte
+++ /dev/null
@@ -1,174 +0,0 @@
-
-
-{#if pages.length > 0}
-
- {#each pages as pg (pg.id)}
-
onRestore(pg.id)}
- onkeydown={(e) => e.key === 'Enter' && onRestore(pg.id)}
- >
-
-
{pg.title}
-
-
- {#if onMaximize}
-
- {/if}
-
-
-
- {/each}
-
-
-{/if}
-
-
diff --git a/packages/shared-ui/src/bottom-stack/NotificationBar.svelte b/packages/shared-ui/src/bottom-stack/NotificationBar.svelte
deleted file mode 100644
index 5d950d40f..000000000
--- a/packages/shared-ui/src/bottom-stack/NotificationBar.svelte
+++ /dev/null
@@ -1,165 +0,0 @@
-
-
-{#if active}
-
-
{active.message}
-
- {#if active.action}
-
- {/if}
- {#if active.dismissible !== false}
-
- {/if}
-
-
-{/if}
-
-
diff --git a/packages/shared-ui/src/bottom-stack/index.ts b/packages/shared-ui/src/bottom-stack/index.ts
deleted file mode 100644
index b0b589f01..000000000
--- a/packages/shared-ui/src/bottom-stack/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export { default as BottomStack } from './BottomStack.svelte';
-export { default as MinimizedTabs } from './MinimizedTabs.svelte';
-export { default as NotificationBar } from './NotificationBar.svelte';
-export type { MinimizedPage, MinimizedTabsCallbacks, BottomNotification } from './types';
diff --git a/packages/shared-ui/src/bottom-stack/types.ts b/packages/shared-ui/src/bottom-stack/types.ts
deleted file mode 100644
index e14eb0e52..000000000
--- a/packages/shared-ui/src/bottom-stack/types.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-export interface MinimizedPage {
- id: string;
- title: string;
- color: string;
-}
-
-export interface MinimizedTabsCallbacks {
- restore: (pageId: string) => void;
- remove: (pageId: string) => void;
- add: () => void;
-}
-
-export interface BottomNotification {
- id: string;
- message: string;
- type: 'info' | 'warning' | 'error';
- action?: { label: string; icon?: any; onClick: () => void };
- dismissible?: boolean;
- onDismiss?: () => void;
-}
diff --git a/packages/shared-ui/src/charts/ActivityHeatmap.svelte b/packages/shared-ui/src/charts/ActivityHeatmap.svelte
deleted file mode 100644
index b169483c7..000000000
--- a/packages/shared-ui/src/charts/ActivityHeatmap.svelte
+++ /dev/null
@@ -1,294 +0,0 @@
-
-
-
-
{title}
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/shared-ui/src/charts/DonutChart.svelte b/packages/shared-ui/src/charts/DonutChart.svelte
deleted file mode 100644
index f0b69678e..000000000
--- a/packages/shared-ui/src/charts/DonutChart.svelte
+++ /dev/null
@@ -1,260 +0,0 @@
-
-
-
-
{title}
-
-
-
-
-
-
-
- {#if showLegend}
-
- {#each data as item}
-
(hoveredSegment = item.id)}
- onmouseleave={() => (hoveredSegment = null)}
- role="button"
- tabindex="0"
- >
-
- {item.label}
- {item.count}
-
- {/each}
-
- {/if}
-
-
-
-
diff --git a/packages/shared-ui/src/charts/ProgressBars.svelte b/packages/shared-ui/src/charts/ProgressBars.svelte
deleted file mode 100644
index 1e89272d3..000000000
--- a/packages/shared-ui/src/charts/ProgressBars.svelte
+++ /dev/null
@@ -1,192 +0,0 @@
-
-
-
-
{title}
-
- {#if sortedData.length === 0}
-
{emptyMessage}
- {:else}
-
- {#each sortedData as item (item.id)}
-
-
-
-
-
-
- {#if item.completed > 0}
-
- {/if}
-
-
- {#if item.inProgress && item.inProgress > 0}
-
- {/if}
-
-
-
{item.percentage}%
-
-
- {/each}
-
- {/if}
-
-
-
diff --git a/packages/shared-ui/src/charts/StatisticsSkeleton.svelte b/packages/shared-ui/src/charts/StatisticsSkeleton.svelte
deleted file mode 100644
index 7e0e23c84..000000000
--- a/packages/shared-ui/src/charts/StatisticsSkeleton.svelte
+++ /dev/null
@@ -1,272 +0,0 @@
-
-
-
-
-
- {#each Array(statCards) as _, i}
-
- {/each}
-
-
-
-
-
-
-
-
- {#each Array(7) as _}
-
- {#each Array(12) as _}
-
- {/each}
-
- {/each}
-
-
-
-
-
-
-
-
-
- {#each Array(7) as _, i}
-
-
-
-
- {/each}
-
-
-
-
-
-
-
-
-
-
- {#each Array(legendItems) as _}
-
-
-
-
- {/each}
-
-
-
-
-
-
-
-
- {#each Array(progressItems) as _, i}
-
-
-
-
- {/each}
-
-
-
-
-
- {#if showAdditionalStats}
-
- {#each Array(3) as _}
-
-
-
-
- {/each}
-
- {/if}
-
-
-
diff --git a/packages/shared-ui/src/charts/StatsGrid.svelte b/packages/shared-ui/src/charts/StatsGrid.svelte
deleted file mode 100644
index d23057011..000000000
--- a/packages/shared-ui/src/charts/StatsGrid.svelte
+++ /dev/null
@@ -1,136 +0,0 @@
-
-
-
- {#each visibleItems as item (item.id)}
-
-
-
-
-
- {item.value}
- {item.label}
-
-
- {/each}
-
-
-
diff --git a/packages/shared-ui/src/charts/TrendLineChart.svelte b/packages/shared-ui/src/charts/TrendLineChart.svelte
deleted file mode 100644
index 6766a6056..000000000
--- a/packages/shared-ui/src/charts/TrendLineChart.svelte
+++ /dev/null
@@ -1,241 +0,0 @@
-
-
-
-
{title}
-
-
-
-
-
diff --git a/packages/shared-ui/src/charts/index.ts b/packages/shared-ui/src/charts/index.ts
deleted file mode 100644
index 6246fd8a8..000000000
--- a/packages/shared-ui/src/charts/index.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-// Charts - Statistics Visualization Components
-export { default as StatsGrid } from './StatsGrid.svelte';
-export { default as ActivityHeatmap } from './ActivityHeatmap.svelte';
-export { default as TrendLineChart } from './TrendLineChart.svelte';
-export { default as DonutChart } from './DonutChart.svelte';
-export { default as ProgressBars } from './ProgressBars.svelte';
-export { default as StatisticsSkeleton } from './StatisticsSkeleton.svelte';
-
-// Types
-export type {
- StatVariant,
- StatItem,
- HeatmapDataPoint,
- TrendDataPoint,
- DonutSegment,
- ProgressItem,
-} from './types';
-
-// Constants
-export { STAT_VARIANT_COLORS } from './types';
diff --git a/packages/shared-ui/src/charts/types.ts b/packages/shared-ui/src/charts/types.ts
deleted file mode 100644
index 774b0d993..000000000
--- a/packages/shared-ui/src/charts/types.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-/**
- * Shared Types for Chart Components
- */
-
-import type { Component } from 'svelte';
-
-// Stat card variant colors
-export type StatVariant = 'success' | 'primary' | 'neutral' | 'danger' | 'info' | 'accent';
-
-export const STAT_VARIANT_COLORS: Record
= {
- success: { bg: 'rgba(16, 185, 129, 0.15)', color: '#10B981' },
- primary: { bg: 'rgba(139, 92, 246, 0.15)', color: '#8B5CF6' },
- neutral: { bg: 'rgba(107, 114, 128, 0.15)', color: '#6B7280' },
- danger: { bg: 'rgba(239, 68, 68, 0.15)', color: '#EF4444' },
- info: { bg: 'rgba(59, 130, 246, 0.15)', color: '#3B82F6' },
- accent: { bg: 'rgba(236, 72, 153, 0.15)', color: '#EC4899' },
-};
-
-// StatsGrid types
-export interface StatItem {
- id: string;
- label: string;
- value: number | string;
- icon: Component;
- variant: StatVariant;
- /** Optional: only show this stat if condition is true */
- showCondition?: boolean;
-}
-
-// ActivityHeatmap types
-export interface HeatmapDataPoint {
- date: string; // YYYY-MM-DD format
- count: number;
- dayOfWeek: number; // 0-6 (Sunday-Saturday)
-}
-
-// TrendLineChart types
-export interface TrendDataPoint {
- date: string; // YYYY-MM-DD format
- count: number;
- label?: string;
-}
-
-// DonutChart types
-export interface DonutSegment {
- id: string;
- label: string;
- count: number;
- percentage: number;
- color: string;
-}
-
-// ProgressBars types
-export interface ProgressItem {
- id: string;
- name: string;
- color: string;
- total: number;
- completed: number;
- inProgress?: number;
- percentage: number;
-}
diff --git a/packages/shared-ui/src/components/DevBuildBadge.svelte b/packages/shared-ui/src/components/DevBuildBadge.svelte
deleted file mode 100644
index 1de94ebaf..000000000
--- a/packages/shared-ui/src/components/DevBuildBadge.svelte
+++ /dev/null
@@ -1,77 +0,0 @@
-
-
-
-
-
diff --git a/packages/shared-ui/src/components/ImmersiveModeToggle.svelte b/packages/shared-ui/src/components/ImmersiveModeToggle.svelte
deleted file mode 100644
index 31d9bec91..000000000
--- a/packages/shared-ui/src/components/ImmersiveModeToggle.svelte
+++ /dev/null
@@ -1,113 +0,0 @@
-
-
-{#if visible}
-
-{/if}
-
-
diff --git a/packages/shared-ui/src/components/SyncIndicator.svelte b/packages/shared-ui/src/components/SyncIndicator.svelte
deleted file mode 100644
index a4bd39bb2..000000000
--- a/packages/shared-ui/src/components/SyncIndicator.svelte
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-
-
-{#if visible}
- {@const colorClass = isOnline ? 'bg-green-600 text-green-50' : 'bg-amber-600 text-amber-50'}
-
- {#if !isOnline}
-
-
Offline
- {:else if showReconnected}
-
-
Wieder online
- {/if}
-
-{/if}
diff --git a/packages/shared-ui/src/context-menu/index.ts b/packages/shared-ui/src/context-menu/index.ts
deleted file mode 100644
index 59acefbf5..000000000
--- a/packages/shared-ui/src/context-menu/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export { default as ContextMenu } from './ContextMenu.svelte';
-export type { ContextMenuItem, ContextMenuState } from './types';
-export { createContextMenuState } from './types';
diff --git a/packages/shared-ui/src/context-menu/types.ts b/packages/shared-ui/src/context-menu/types.ts
deleted file mode 100644
index c1bb498ba..000000000
--- a/packages/shared-ui/src/context-menu/types.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import type { Component } from 'svelte';
-
-export interface ContextMenuItem {
- /** Unique identifier for the item */
- id: string;
- /** Display label */
- label: string;
- /** Icon component to render (Phosphor icon or any Svelte component) */
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- icon?: Component;
- /** Keyboard shortcut hint */
- shortcut?: string;
- /** Whether the item is disabled */
- disabled?: boolean;
- /** Visual variant */
- variant?: 'default' | 'danger';
- /** Item type - use 'divider' for separator */
- type?: 'item' | 'divider';
- /** Action to perform when clicked */
- action?: () => void;
- /** Additional data attached to the item */
- data?: unknown;
- /** Show a toggle switch (for boolean settings) */
- toggle?: boolean;
- /** Current toggle state (only used when toggle is true) */
- checked?: boolean;
-}
-
-export interface ContextMenuState {
- visible: boolean;
- x: number;
- y: number;
- target: T | null;
-}
-
-/**
- * Creates a context menu state object
- */
-export function createContextMenuState(): ContextMenuState {
- return {
- visible: false,
- x: 0,
- y: 0,
- target: null,
- };
-}
diff --git a/packages/shared-ui/src/dnd/ActionZone.svelte b/packages/shared-ui/src/dnd/ActionZone.svelte
index 05f8b6050..067146533 100644
--- a/packages/shared-ui/src/dnd/ActionZone.svelte
+++ b/packages/shared-ui/src/dnd/ActionZone.svelte
@@ -1,57 +1,45 @@
{#if visible}
- {@const Icon = iconComponent}
-
+ {#if iconSvg}
+
+ {:else}
+
+ {/if}
{#if label}
{label}
{/if}
@@ -80,11 +72,15 @@
gap: 0.5rem;
padding: 0.75rem 1.5rem;
border-radius: 9999px;
- backdrop-filter: blur(12px);
- -webkit-backdrop-filter: blur(12px);
- transition: all 0.2s ease;
+ border-width: 1.5px;
+ border-style: solid;
+ transition:
+ background-color 200ms ease,
+ border-color 200ms ease,
+ transform 200ms ease;
animation: action-zone-in 200ms ease-out;
cursor: default;
+ font-family: inherit;
}
@keyframes action-zone-in {
@@ -104,32 +100,30 @@
white-space: nowrap;
}
- /* Variants */
.variant-danger {
- background: rgba(239, 68, 68, 0.15);
- border: 1.5px solid rgba(239, 68, 68, 0.3);
- color: #ef4444;
+ background: hsl(var(--color-error) / 0.15);
+ border-color: hsl(var(--color-error) / 0.3);
+ color: hsl(var(--color-error));
}
.variant-warning {
- background: rgba(245, 158, 11, 0.15);
- border: 1.5px solid rgba(245, 158, 11, 0.3);
- color: #f59e0b;
+ background: hsl(var(--color-warning) / 0.15);
+ border-color: hsl(var(--color-warning) / 0.3);
+ color: hsl(var(--color-warning));
}
.variant-info {
- background: rgba(59, 130, 246, 0.15);
- border: 1.5px solid rgba(59, 130, 246, 0.3);
- color: #3b82f6;
+ background: hsl(var(--color-primary) / 0.15);
+ border-color: hsl(var(--color-primary) / 0.3);
+ color: hsl(var(--color-primary));
}
.variant-success {
- background: rgba(16, 185, 129, 0.15);
- border: 1.5px solid rgba(16, 185, 129, 0.3);
- color: #10b981;
+ background: hsl(var(--color-success) / 0.15);
+ border-color: hsl(var(--color-success) / 0.3);
+ color: hsl(var(--color-success));
}
- /* Hover state (when item is over the zone) */
:global(.action-zone.mana-drop-target-hover),
:global(.action-zone.action-zone-active) {
transform: translateX(-50%) scale(1.1);
@@ -137,33 +131,28 @@
:global(.variant-danger.mana-drop-target-hover),
:global(.variant-danger.action-zone-active) {
- background: rgba(239, 68, 68, 0.3);
- border-color: rgba(239, 68, 68, 0.6);
- box-shadow: 0 0 20px rgba(239, 68, 68, 0.3);
+ background: hsl(var(--color-error) / 0.3);
+ border-color: hsl(var(--color-error) / 0.6);
}
:global(.variant-warning.mana-drop-target-hover),
:global(.variant-warning.action-zone-active) {
- background: rgba(245, 158, 11, 0.3);
- border-color: rgba(245, 158, 11, 0.6);
- box-shadow: 0 0 20px rgba(245, 158, 11, 0.3);
+ background: hsl(var(--color-warning) / 0.3);
+ border-color: hsl(var(--color-warning) / 0.6);
}
:global(.variant-info.mana-drop-target-hover),
:global(.variant-info.action-zone-active) {
- background: rgba(59, 130, 246, 0.3);
- border-color: rgba(59, 130, 246, 0.6);
- box-shadow: 0 0 20px rgba(59, 130, 246, 0.3);
+ background: hsl(var(--color-primary) / 0.3);
+ border-color: hsl(var(--color-primary) / 0.6);
}
:global(.variant-success.mana-drop-target-hover),
:global(.variant-success.action-zone-active) {
- background: rgba(16, 185, 129, 0.3);
- border-color: rgba(16, 185, 129, 0.6);
- box-shadow: 0 0 20px rgba(16, 185, 129, 0.3);
+ background: hsl(var(--color-success) / 0.3);
+ border-color: hsl(var(--color-success) / 0.6);
}
- /* Success flash after drop */
:global(.action-zone.mana-drop-target-success),
:global(.action-zone.mana-passive-zone-success) {
animation: action-success 400ms ease-out;
@@ -180,24 +169,4 @@
transform: translateX(-50%) scale(1);
}
}
-
- :global(.dark) .variant-danger {
- background: rgba(239, 68, 68, 0.2);
- border-color: rgba(239, 68, 68, 0.4);
- }
-
- :global(.dark) .variant-warning {
- background: rgba(245, 158, 11, 0.2);
- border-color: rgba(245, 158, 11, 0.4);
- }
-
- :global(.dark) .variant-info {
- background: rgba(59, 130, 246, 0.2);
- border-color: rgba(59, 130, 246, 0.4);
- }
-
- :global(.dark) .variant-success {
- background: rgba(16, 185, 129, 0.2);
- border-color: rgba(16, 185, 129, 0.4);
- }
diff --git a/packages/shared-ui/src/dnd/DragPreview.svelte b/packages/shared-ui/src/dnd/DragPreview.svelte
index 863deb0b7..2367d67a0 100644
--- a/packages/shared-ui/src/dnd/DragPreview.svelte
+++ b/packages/shared-ui/src/dnd/DragPreview.svelte
@@ -6,15 +6,12 @@
*
*
* It reads from dragState and renders a pill showing what's being dragged.
- * For tags: colored dot + tag name.
- * For entities: app color dot + item title + app name.
*/
import { dragState } from './drag-state.svelte';
import type { TagDragData } from './types';
interface Props {
- /** Resolve display data for a dragged entity. */
resolveEntity?: (
type: string,
data: Record
@@ -55,7 +52,11 @@
{tagData.name}
{:else if entityData()}
{@const entity = entityData()}
-
+ {#if entity?.color}
+
+ {:else}
+
+ {/if}
{entity?.title}
{#if entity?.appName}
{entity.appName}
@@ -76,23 +77,14 @@
gap: 0.375rem;
padding: 0.375rem 0.75rem;
border-radius: 9999px;
- background: rgba(255, 255, 255, 0.95);
- backdrop-filter: blur(12px);
- -webkit-backdrop-filter: blur(12px);
- border: 1px solid rgba(0, 0, 0, 0.12);
- box-shadow:
- 0 8px 24px -4px rgba(0, 0, 0, 0.15),
- 0 2px 6px -1px rgba(0, 0, 0, 0.1);
+ background: hsl(var(--color-surface));
+ border: 1px solid hsl(var(--color-border));
font-size: 0.8125rem;
white-space: nowrap;
max-width: 280px;
transform: scale(1.05);
animation: drag-preview-in 150ms ease-out;
- }
-
- :global(.dark) .drag-preview {
- background: rgba(30, 30, 30, 0.95);
- border-color: rgba(255, 255, 255, 0.15);
+ font-family: inherit;
}
@keyframes drag-preview-in {
@@ -113,18 +105,19 @@
flex-shrink: 0;
}
+ .fallback-dot {
+ background: hsl(var(--color-muted-foreground));
+ }
+
.preview-title {
font-weight: 600;
- color: #1a1a1a;
+ color: hsl(var(--color-foreground));
overflow: hidden;
text-overflow: ellipsis;
}
- :global(.dark) .preview-title {
- color: #e5e5e5;
- }
.preview-title.fallback {
- color: #6b7280;
+ color: hsl(var(--color-muted-foreground));
text-transform: capitalize;
font-weight: 500;
}
@@ -132,7 +125,7 @@
.preview-app {
font-size: 0.6875rem;
font-weight: 400;
- color: #9ca3af;
+ color: hsl(var(--color-muted-foreground));
flex-shrink: 0;
}
diff --git a/packages/shared-ui/src/help/HelpModal.svelte b/packages/shared-ui/src/help/HelpModal.svelte
deleted file mode 100644
index 4ad0f29a9..000000000
--- a/packages/shared-ui/src/help/HelpModal.svelte
+++ /dev/null
@@ -1,184 +0,0 @@
-
-
-
-
-
-
-
-
-
- {#if effectiveTab() === 'shortcuts' && config.shortcuts}
-
- {:else if effectiveTab() === 'syntax' && config.syntax}
-
- {/if}
-
-
-
-
-
diff --git a/packages/shared-ui/src/help/KeyboardShortcutsPanel.svelte b/packages/shared-ui/src/help/KeyboardShortcutsPanel.svelte
deleted file mode 100644
index b484e9b64..000000000
--- a/packages/shared-ui/src/help/KeyboardShortcutsPanel.svelte
+++ /dev/null
@@ -1,251 +0,0 @@
-
-
-
- {#each categories as category}
- {@const CategoryIcon = getCategoryIcon(category)}
-
-
-
-
- {#each category.shortcuts as shortcut}
- {@const ShortcutIcon = getShortcutIcon(shortcut.keys)}
-
-
-
-
-
- {#each shortcut.keys as key, i}
- {#if i > 0}+{/if}
- {key}
- {/each}
- {#if shortcut.altKeys && !compact}
-
- oder
- {#each shortcut.altKeys as key, i}
- {#if i > 0}+{/if}
- {key}
- {/each}
-
- {/if}
-
-
{shortcut.description}
-
- {/each}
-
-
- {/each}
-
-
-
diff --git a/packages/shared-ui/src/help/SyntaxHelpPanel.svelte b/packages/shared-ui/src/help/SyntaxHelpPanel.svelte
deleted file mode 100644
index 164e0f003..000000000
--- a/packages/shared-ui/src/help/SyntaxHelpPanel.svelte
+++ /dev/null
@@ -1,415 +0,0 @@
-
-
-
- {#if introText && !compact}
-
{introText}
- {/if}
-
- {#each groups as group}
-
-
{group.title}
-
- {#each group.items as item}
- {@const Icon = item.icon ?? getPatternIcon(item.pattern)}
-
-
-
-
-
-
-
- {#each item.examples as ex}
- {#if typeof ex === 'string'}
- {ex}
- {:else}
-
- {ex.text}
- {#if ex.label}
- {ex.label}
- {/if}
-
- {/if}
- {/each}
-
-
-
- {/each}
-
-
- {/each}
-
- {#if showLiveExample && !compact}
-
-
Beispiel-Eingabe
-
- {#each example.highlights as hl}
- {hl.content}
- {/each}
-
-
- {/if}
-
-
-
diff --git a/packages/shared-ui/src/help/constants.ts b/packages/shared-ui/src/help/constants.ts
deleted file mode 100644
index e57e158a7..000000000
--- a/packages/shared-ui/src/help/constants.ts
+++ /dev/null
@@ -1,122 +0,0 @@
-import type { ShortcutCategory, SyntaxGroup } from './types';
-
-/**
- * Common keyboard shortcuts shared across all apps with InputBar
- */
-export const COMMON_SHORTCUTS: ShortcutCategory[] = [
- {
- id: 'inputbar',
- title: 'Eingabefeld',
- shortcuts: [
- {
- keys: ['Enter'],
- description: 'Auswahl bestätigen / Erstellen',
- category: 'inputbar',
- },
- {
- keys: ['Cmd', 'Enter'],
- altKeys: ['Ctrl', 'Enter'],
- description: 'Direkt erstellen',
- category: 'inputbar',
- },
- {
- keys: ['Esc'],
- description: 'Schließen & Eingabe löschen',
- category: 'inputbar',
- },
- {
- keys: ['↑', '↓'],
- description: 'Durch Ergebnisse navigieren',
- category: 'inputbar',
- },
- {
- keys: ['Rechtsklick'],
- description: 'Einstellungen öffnen',
- category: 'inputbar',
- },
- ],
- },
- {
- id: 'dialogs',
- title: 'Dialoge',
- shortcuts: [
- {
- keys: ['Esc'],
- description: 'Dialog schließen',
- category: 'dialogs',
- },
- ],
- },
-];
-
-/**
- * Common syntax patterns shared across all apps with InputBar
- */
-export const COMMON_SYNTAX: SyntaxGroup[] = [
- {
- title: 'Kategorien & Tags',
- items: [
- {
- pattern: '#tag',
- description: 'Tag hinzufügen',
- examples: ['#arbeit', '#privat', '#wichtig'],
- color: 'primary',
- },
- {
- pattern: '@name',
- description: 'Kalender oder Projekt zuweisen',
- examples: ['@team', '@privat', '@projekt'],
- color: 'success',
- },
- ],
- },
- {
- title: 'Zeit & Datum',
- items: [
- {
- pattern: 'Datum',
- description: 'Natürliche Datumsangaben',
- examples: ['heute', 'morgen', 'montag', 'in 3 tagen', 'nächste woche'],
- color: 'accent',
- },
- {
- pattern: 'Uhrzeit',
- description: 'Zeitangaben',
- examples: ['14:00', '9 uhr', 'um 15:30'],
- color: 'accent',
- },
- ],
- },
- {
- title: 'Priorität',
- items: [
- {
- pattern: 'Priorität',
- description: 'Dringlichkeit festlegen',
- examples: [
- { text: '!!!', label: 'dringend', color: 'error' },
- { text: '!!', label: 'hoch', color: 'warning' },
- { text: '!', label: 'normal', color: 'warning-soft' },
- ],
- color: 'error',
- },
- ],
- },
-];
-
-/**
- * Default live example for syntax highlighting demo
- */
-export const DEFAULT_LIVE_EXAMPLE = {
- text: 'Meeting mit Team morgen 14:00 @arbeit #wichtig',
- highlights: [
- { type: 'text' as const, content: 'Meeting mit Team ' },
- { type: 'date' as const, content: 'morgen' },
- { type: 'text' as const, content: ' ' },
- { type: 'time' as const, content: '14:00' },
- { type: 'text' as const, content: ' ' },
- { type: 'reference' as const, content: '@arbeit' },
- { type: 'text' as const, content: ' ' },
- { type: 'tag' as const, content: '#wichtig' },
- ],
-};
diff --git a/packages/shared-ui/src/help/index.ts b/packages/shared-ui/src/help/index.ts
deleted file mode 100644
index cf2bbb318..000000000
--- a/packages/shared-ui/src/help/index.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-// Help Components
-export { default as HelpModal } from './HelpModal.svelte';
-export { default as KeyboardShortcutsPanel } from './KeyboardShortcutsPanel.svelte';
-export { default as SyntaxHelpPanel } from './SyntaxHelpPanel.svelte';
-
-// Types
-export type {
- KeyboardShortcut,
- ShortcutCategory,
- SyntaxColor,
- SyntaxExample,
- SyntaxPattern,
- SyntaxGroup,
- HelpModalConfig,
-} from './types';
-
-// Constants
-export { COMMON_SHORTCUTS, COMMON_SYNTAX, DEFAULT_LIVE_EXAMPLE } from './constants';
diff --git a/packages/shared-ui/src/help/types.ts b/packages/shared-ui/src/help/types.ts
deleted file mode 100644
index 6fca61d09..000000000
--- a/packages/shared-ui/src/help/types.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-import type { Component } from 'svelte';
-
-/**
- * Represents a single keyboard shortcut
- */
-export interface KeyboardShortcut {
- /** Keys to press, e.g. ['Cmd', 'Enter'] or ['↑', '↓'] */
- keys: string[];
- /** Description of what the shortcut does */
- description: string;
- /** Category ID for grouping */
- category: string;
- /** Alternative keys (e.g. Ctrl instead of Cmd) */
- altKeys?: string[];
-}
-
-/**
- * A category/group of related shortcuts
- */
-export interface ShortcutCategory {
- /** Unique identifier */
- id: string;
- /** Display title */
- title: string;
- /** Optional icon component */
- icon?: Component;
- /** Shortcuts in this category */
- shortcuts: KeyboardShortcut[];
-}
-
-/**
- * Color variants for syntax highlighting
- */
-export type SyntaxColor = 'primary' | 'success' | 'accent' | 'error' | 'warning' | 'warning-soft';
-
-/**
- * A syntax example - can be a simple string or an object with label
- */
-export type SyntaxExample =
- | string
- | {
- text: string;
- label?: string;
- color?: SyntaxColor;
- };
-
-/**
- * A syntax pattern that can be used in the InputBar
- */
-export interface SyntaxPattern {
- /** The pattern syntax, e.g. '#tag', '@name', 'Datum' */
- pattern: string;
- /** Description of what the pattern does */
- description: string;
- /** Example usages */
- examples: SyntaxExample[];
- /** Color for highlighting */
- color: SyntaxColor;
- /** Optional icon component */
- icon?: Component;
-}
-
-/**
- * A group of related syntax patterns
- */
-export interface SyntaxGroup {
- /** Group title */
- title: string;
- /** Patterns in this group */
- items: SyntaxPattern[];
-}
-
-/**
- * Configuration for the HelpModal
- */
-export interface HelpModalConfig {
- /** Shortcut categories to display */
- shortcuts?: ShortcutCategory[];
- /** Syntax groups to display */
- syntax?: SyntaxGroup[];
- /** Whether to show tabs (auto-detected if both shortcuts and syntax are provided) */
- showTabs?: boolean;
- /** Default tab to show */
- defaultTab?: 'shortcuts' | 'syntax';
- /** Live example text for syntax highlighting demo */
- liveExample?: {
- text: string;
- highlights: Array<{
- type: 'text' | 'tag' | 'reference' | 'date' | 'time' | 'priority';
- content: string;
- }>;
- };
-}
diff --git a/packages/shared-ui/src/index.ts b/packages/shared-ui/src/index.ts
index 38a03b227..e01e7b29e 100644
--- a/packages/shared-ui/src/index.ts
+++ b/packages/shared-ui/src/index.ts
@@ -1,304 +1,15 @@
-// Atoms
-export { Text, Button, Badge, Card } from './atoms';
+/**
+ * @mana/shared-ui — Vereins-UI-Komponenten, strikte 12-Token-Disziplin.
+ *
+ * Detail-Spec: mana/docs/THEMING.md
+ * Status: PORTING_PLAN.md
+ */
-// Molecules
-export {
- Toggle,
- Input,
- Select,
- Textarea,
- Checkbox,
- FilterDropdown,
- FavoriteButton,
- ColorPicker,
- COLORS_12,
- COLORS_16,
- DEFAULT_COLOR,
- getRandomColor,
- ReminderPicker,
-} from './molecules';
-export type { SelectOption, FilterDropdownOption } from './molecules';
-
-// Stats
-export { GlassCard, StatRow } from './molecules';
-
-// Tags
-export {
- TagBadge,
- TagChip,
- TagColorPicker,
- TagEditModal,
- TagSelector,
- TagField,
- TagList,
- TAG_COLORS,
- DEFAULT_TAG_COLOR,
- getRandomTagColor,
- getTagColorByName,
-} from './molecules';
-export type { Tag, TagData, TagColorName, TagColorHex } from './molecules';
-
-// Media
-export { AudioPlayer } from './molecules';
-
-// Loading/Skeletons
-export {
- SkeletonBox,
- SkeletonText,
- SkeletonAvatar,
- SkeletonRow,
- SkeletonList,
- SkeletonCard,
- SkeletonGrid,
- AppLoadingSkeleton,
- calculateFadeOpacity,
-} from './molecules';
-
-// Feedback
-export { EmptyState } from './molecules';
-
-// Contacts
-export { ContactAvatar, ContactBadge, ContactSelector } from './molecules';
-
-// Layout
-export { ModalFooter, DataCard, PageHeader, KeyboardShortcutsPanel } from './molecules';
-
-// Confirmation (inline popover)
-export { ConfirmationPopover } from './molecules';
-
-// Organisms
-export { Modal, ConfirmationModal, FormModal, AppSlider, BaseListView } from './organisms';
-export type { AppItem } from './organisms';
-
-// Network Graph
-export {
- NetworkGraph,
- NetworkControls,
- stringToColor,
- getInitials,
- SIMULATION_CONFIG,
- NODE_CONFIG,
- LABEL_CONFIG,
-} from './organisms';
-export type {
- NetworkNode,
- NetworkLink,
- NetworkTag,
- NetworkTransform,
- NetworkGraphProps,
- NetworkControlsProps,
- NetworkGraphResponse,
- SimulationNode,
- SimulationLink,
-} from './organisms';
-
-// Navigation
-export {
- NavLink,
- Navbar,
- Sidebar,
- SidebarSection,
- PillNavigation,
- PillDropdown,
- PillDropdownBar,
- AppDrawer,
- GlobalSpotlight,
- createGlobalSpotlightState,
- PillTabGroup,
- PillTagSelector,
- PillTimeRangeSelector,
- PillViewSwitcher,
- PillToolbar,
- PillToolbarButton,
- PillToolbarDivider,
- TagStrip,
- ExpandableToolbar,
- createAppNavigationStore,
- getFavoriteApps,
- getRecentApps,
- getUsageCounts,
- toggleFavoriteApp,
- recordAppVisit,
- clearRecentApps,
-} from './navigation';
-export type {
- NavItem,
- NavbarProps,
- SidebarProps,
- NavLinkProps,
- KeyboardShortcut,
- PillNavItem,
- PillDropdownItem,
- PillNavElement,
- PillBarConfig,
- PillNavigationProps,
- PillTabOption,
- PillTabGroupConfig,
- PillTagItem,
- PillTagSelectorConfig,
- ExpandableToolbarProps,
- RecentAppEntry,
- SpotlightAction,
- ContentSearcher,
- ContentSearchResult,
- ContentSearchGroup,
-} from './navigation';
-
-// Settings
-export {
- SettingsPage,
- SettingsSection,
- SettingsCard,
- SettingsRow,
- SettingsToggle,
- SettingsSelect,
- SettingsNumberInput,
- SettingsTimeInput,
- SettingsDangerZone,
- SettingsDangerButton,
- GlobalSettingsSection,
-} from './settings';
-
-// Input Bar
-export {
- InputBar,
- QuickInputBar,
- InputBarContextMenu,
- InputBarHelpModal,
- // Recent history
- getRecentTags,
- getRecentReferences,
- addRecentTag,
- addRecentReference,
- extractAndSaveFromInput,
- clearRecentHistory,
- createRecentInputHistoryStore,
- // Settings
- loadInputBarSettings,
- saveInputBarSettings,
- updateInputBarSetting,
- resetInputBarSettings,
- createInputBarSettingsStore,
- getInputBarSettingsStore,
-} from './quick-input';
-export type { QuickInputItem, QuickAction, CreatePreview, InputBarSettings } from './quick-input';
-
-// Shared search/command core — highlight patterns, debounce, common helpers.
-export { getHighlightPatterns, highlightText, SEARCH_DEBOUNCE_MS } from './search-core';
-export type { HighlightPattern } from './search-core';
-
-// Pages
-export { default as AppsPage } from './pages/AppsPage.svelte';
-export { default as OfflinePage } from './pages/OfflinePage.svelte';
-export { default as ProfilePage } from './pages/ProfilePage.svelte';
-export type { UserProfile, ProfileActions } from './pages/profile-types';
-
-// Onboarding
-export { createAppOnboardingStore } from './onboarding/create-app-onboarding.svelte';
-export { default as MiniOnboardingModal } from './onboarding/MiniOnboardingModal.svelte';
-export type {
- AppOnboardingOption,
- AppOnboardingStepType,
- AppOnboardingStepBase,
- AppOnboardingSelectStep,
- AppOnboardingToggleStep,
- AppOnboardingInfoStep,
- AppOnboardingStep,
- AppOnboardingConfig,
- AppOnboardingPreferences,
- AppOnboardingStore,
- MiniOnboardingModalProps,
-} from './onboarding/types';
-
-// Charts - Statistics Visualization
-export {
- StatsGrid,
- ActivityHeatmap,
- TrendLineChart,
- DonutChart,
- ProgressBars,
- StatisticsSkeleton,
- STAT_VARIANT_COLORS,
-} from './charts';
-export type {
- StatVariant,
- StatItem,
- HeatmapDataPoint,
- TrendDataPoint,
- DonutSegment,
- ProgressItem,
-} from './charts';
-
-// Context Menu
-export { ContextMenu, createContextMenuState } from './context-menu';
-export type { ContextMenuItem, ContextMenuState } from './context-menu';
-
-// Help Components
-export {
- HelpModal,
- KeyboardShortcutsPanel as HelpKeyboardShortcutsPanel,
- SyntaxHelpPanel,
- COMMON_SHORTCUTS,
- COMMON_SYNTAX,
- DEFAULT_LIVE_EXAMPLE,
-} from './help';
-export type {
- KeyboardShortcut as HelpKeyboardShortcut,
- ShortcutCategory,
- SyntaxColor,
- SyntaxExample,
- SyntaxPattern,
- SyntaxGroup,
- HelpModalConfig,
-} from './help';
-
-// Immersive Mode
-export { default as ImmersiveModeToggle } from './components/ImmersiveModeToggle.svelte';
-export { default as DevBuildBadge } from './components/DevBuildBadge.svelte';
-export { default as SyncIndicator } from './components/SyncIndicator.svelte';
-
-// Toast & Global Error Handling
-export {
- toastStore,
- toast,
- handleApiError,
- ToastContainer,
- setupGlobalErrorHandler,
- GLOBAL_ERROR_TRANSLATIONS,
-} from './toast';
-export type {
- Toast,
- ToastType,
- GlobalErrorHandlerOptions,
- GlobalErrorHandlerTranslations,
-} from './toast';
-
-// Bottom Stack
-export { BottomStack, MinimizedTabs, NotificationBar } from './bottom-stack';
-export type { MinimizedPage, MinimizedTabsCallbacks, BottomNotification } from './bottom-stack';
-
-// Actions
-export { focusTrap } from './actions';
-
-// Drag & Drop
-export {
- dragSource,
- dropTarget,
- passiveDropZone,
- dragState,
- registerSvelteActionDrag,
- clearSvelteActionDrag,
- isTypeBeingDragged,
- DragPreview,
- ActionZone,
-} from './dnd';
-export type {
- DragType,
- DragPayload,
- TagDragData,
- TaskDragData,
- DragSourceOptions,
- DropTargetOptions,
- PassiveDropZoneOptions,
- ActionZoneProps,
-} from './dnd';
+export * from './atoms/index';
+export * from './molecules/index';
+export * from './navigation/index';
+export * from './organisms/index';
+export * from './pages/index';
+export * from './toast/index';
+export * from './dnd/index';
+export * from './quick-input/index';
diff --git a/packages/shared-ui/src/molecules/Checkbox.stories.svelte b/packages/shared-ui/src/molecules/Checkbox.stories.svelte
new file mode 100644
index 000000000..70fd947ef
--- /dev/null
+++ b/packages/shared-ui/src/molecules/Checkbox.stories.svelte
@@ -0,0 +1,52 @@
+
+
+
+ {#snippet children()}
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+
+
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+ {/snippet}
+
diff --git a/packages/shared-ui/src/molecules/Checkbox.svelte b/packages/shared-ui/src/molecules/Checkbox.svelte
index 158808706..e4d75225e 100644
--- a/packages/shared-ui/src/molecules/Checkbox.svelte
+++ b/packages/shared-ui/src/molecules/Checkbox.svelte
@@ -1,173 +1,160 @@
-
diff --git a/packages/shared-ui/src/molecules/ColorPicker.constants.ts b/packages/shared-ui/src/molecules/ColorPicker.constants.ts
deleted file mode 100644
index 852446530..000000000
--- a/packages/shared-ui/src/molecules/ColorPicker.constants.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * Standard color palettes for use with ColorPicker.
- */
-
-/** 12-color palette (same as TAG_COLORS) — good for tags, labels, categories */
-export const COLORS_12 = [
- '#ef4444',
- '#f97316',
- '#f59e0b',
- '#84cc16',
- '#22c55e',
- '#14b8a6',
- '#06b6d4',
- '#3b82f6',
- '#6366f1',
- '#8b5cf6',
- '#ec4899',
- '#64748b',
-] as const;
-
-/** 16-color extended palette — good for projects, clients, folders */
-export const COLORS_16 = [
- '#ef4444',
- '#f97316',
- '#f59e0b',
- '#eab308',
- '#84cc16',
- '#22c55e',
- '#14b8a6',
- '#06b6d4',
- '#0ea5e9',
- '#3b82f6',
- '#6366f1',
- '#8b5cf6',
- '#a855f7',
- '#d946ef',
- '#ec4899',
- '#f43f5e',
-] as const;
-
-/** Default color (blue) */
-export const DEFAULT_COLOR = '#3b82f6';
-
-/** Get a random color from the 12-color palette */
-export function getRandomColor(): string {
- return COLORS_12[Math.floor(Math.random() * COLORS_12.length)];
-}
diff --git a/packages/shared-ui/src/molecules/ColorPicker.svelte b/packages/shared-ui/src/molecules/ColorPicker.svelte
deleted file mode 100644
index 6bfb3244c..000000000
--- a/packages/shared-ui/src/molecules/ColorPicker.svelte
+++ /dev/null
@@ -1,83 +0,0 @@
-
-
-
- {#each colors as color}
- {@const isSelected = selectedColor?.toLowerCase() === color.toLowerCase()}
-
- {/each}
-
diff --git a/packages/shared-ui/src/molecules/ColorPicker.test.ts b/packages/shared-ui/src/molecules/ColorPicker.test.ts
deleted file mode 100644
index 111d7567a..000000000
--- a/packages/shared-ui/src/molecules/ColorPicker.test.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-import { describe, it, expect, vi } from 'vitest';
-import { render, screen, fireEvent } from '@testing-library/svelte';
-import ColorPicker from './ColorPicker.svelte';
-import { COLORS_12, COLORS_16, DEFAULT_COLOR, getRandomColor } from './ColorPicker.constants';
-
-const testColors = ['#ef4444', '#3b82f6', '#22c55e'];
-
-describe('ColorPicker', () => {
- it('renders all provided colors as radio buttons', () => {
- render(ColorPicker, { props: { colors: testColors, onColorChange: vi.fn() } });
- const radios = screen.getAllByRole('radio');
- expect(radios).toHaveLength(3);
- });
-
- it('marks selected color as checked', () => {
- render(ColorPicker, {
- props: { colors: testColors, selectedColor: '#3b82f6', onColorChange: vi.fn() },
- });
- const blue = screen.getByRole('radio', { name: '#3b82f6' });
- expect(blue.getAttribute('aria-checked')).toBe('true');
- });
-
- it('calls onColorChange when clicked', async () => {
- const onColorChange = vi.fn();
- render(ColorPicker, { props: { colors: testColors, onColorChange } });
- await fireEvent.click(screen.getByRole('radio', { name: '#22c55e' }));
- expect(onColorChange).toHaveBeenCalledWith('#22c55e');
- });
-
- it('supports keyboard selection', async () => {
- const onColorChange = vi.fn();
- render(ColorPicker, { props: { colors: testColors, onColorChange } });
- await fireEvent.keyDown(screen.getByRole('radio', { name: '#ef4444' }), { key: 'Enter' });
- expect(onColorChange).toHaveBeenCalledWith('#ef4444');
- });
-
- it('has accessible radiogroup role', () => {
- render(ColorPicker, { props: { colors: testColors, onColorChange: vi.fn() } });
- expect(screen.getByRole('radiogroup')).toBeInTheDocument();
- });
-
- it('uses custom label', () => {
- render(ColorPicker, {
- props: { colors: testColors, onColorChange: vi.fn(), label: 'Pick a color' },
- });
- expect(screen.getByRole('radiogroup', { name: 'Pick a color' })).toBeInTheDocument();
- });
-});
-
-describe('Color constants', () => {
- it('COLORS_12 has 12 entries', () => {
- expect(COLORS_12).toHaveLength(12);
- });
-
- it('COLORS_16 has 16 entries', () => {
- expect(COLORS_16).toHaveLength(16);
- });
-
- it('DEFAULT_COLOR is blue', () => {
- expect(DEFAULT_COLOR).toBe('#3b82f6');
- });
-
- it('getRandomColor returns a color from COLORS_12', () => {
- const validColors = new Set(COLORS_12);
- for (let i = 0; i < 30; i++) {
- expect(validColors.has(getRandomColor() as (typeof COLORS_12)[number])).toBe(true);
- }
- });
-});
diff --git a/packages/shared-ui/src/molecules/ConfirmationPopover.svelte b/packages/shared-ui/src/molecules/ConfirmationPopover.svelte
deleted file mode 100644
index 829e5eec4..000000000
--- a/packages/shared-ui/src/molecules/ConfirmationPopover.svelte
+++ /dev/null
@@ -1,415 +0,0 @@
-
-
-
-
-
- {
- if (e.key === 'Enter' || e.key === ' ') handleTriggerClick(e as unknown as MouseEvent);
- }}
- role="button"
- tabindex="0"
->
- {@render children()}
-
-
-
-{#if visible}
-
-
-
-
-
- {#if message}
-
{message}
- {/if}
-
-
-
-
-
-
-
-{/if}
-
-
diff --git a/packages/shared-ui/src/molecules/ContactAvatar.stories.svelte b/packages/shared-ui/src/molecules/ContactAvatar.stories.svelte
new file mode 100644
index 000000000..a58a23e2b
--- /dev/null
+++ b/packages/shared-ui/src/molecules/ContactAvatar.stories.svelte
@@ -0,0 +1,50 @@
+
+
+
+ {#snippet children()}
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+
+
+
+
+
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+
+
+
+
+ {/snippet}
+
diff --git a/packages/shared-ui/src/molecules/ContactAvatar.svelte b/packages/shared-ui/src/molecules/ContactAvatar.svelte
new file mode 100644
index 000000000..180c6cf76
--- /dev/null
+++ b/packages/shared-ui/src/molecules/ContactAvatar.svelte
@@ -0,0 +1,133 @@
+
+
+
+ {#if imageUrl}
+
+ {:else if initials}
+ {initials}
+ {:else}
+
+
+
+ {/if}
+
+ {#if status}
+
+ {/if}
+
+
+
diff --git a/packages/shared-ui/src/molecules/DataCard.stories.svelte b/packages/shared-ui/src/molecules/DataCard.stories.svelte
new file mode 100644
index 000000000..64175406a
--- /dev/null
+++ b/packages/shared-ui/src/molecules/DataCard.stories.svelte
@@ -0,0 +1,53 @@
+
+
+
+ {#snippet children()}
+
+
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+
+
+
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+
+ {#snippet icon()}{/snippet}
+
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+
+
+ {/snippet}
+
diff --git a/packages/shared-ui/src/molecules/DataCard.svelte b/packages/shared-ui/src/molecules/DataCard.svelte
index 4ae54ef16..cca4d8c49 100644
--- a/packages/shared-ui/src/molecules/DataCard.svelte
+++ b/packages/shared-ui/src/molecules/DataCard.svelte
@@ -1,161 +1,109 @@
-
-
+ {#if hint}{hint}{/if}
+
+ {/if}
+
diff --git a/packages/shared-ui/src/molecules/EmptyState.svelte b/packages/shared-ui/src/molecules/EmptyState.svelte
new file mode 100644
index 000000000..1479be5a8
--- /dev/null
+++ b/packages/shared-ui/src/molecules/EmptyState.svelte
@@ -0,0 +1,99 @@
+
+
+
+
+ {#if icon}
+ {@render icon()}
+ {:else}
+
+ {/if}
+
+
+
{title}
+
+ {#if message}
+
{message}
+ {/if}
+
+ {#if actionLabel || secondaryActionLabel}
+
+ {#if secondaryActionLabel && onSecondaryAction}
+
+ {/if}
+ {#if actionLabel && onAction}
+
+ {/if}
+
+ {/if}
+
+
+
diff --git a/packages/shared-ui/src/molecules/FavoriteButton.svelte b/packages/shared-ui/src/molecules/FavoriteButton.svelte
index 5d3f69ac0..c6090ff84 100644
--- a/packages/shared-ui/src/molecules/FavoriteButton.svelte
+++ b/packages/shared-ui/src/molecules/FavoriteButton.svelte
@@ -1,38 +1,24 @@
+
+
diff --git a/packages/shared-ui/src/molecules/FavoriteButton.test.ts b/packages/shared-ui/src/molecules/FavoriteButton.test.ts
deleted file mode 100644
index e0e00c330..000000000
--- a/packages/shared-ui/src/molecules/FavoriteButton.test.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { describe, it, expect, vi } from 'vitest';
-import { render, screen, fireEvent } from '@testing-library/svelte';
-import FavoriteButton from './FavoriteButton.svelte';
-
-describe('FavoriteButton', () => {
- it('renders with heart icon by default', () => {
- const { container } = render(FavoriteButton, {
- props: { active: false, onclick: vi.fn() },
- });
- expect(container.querySelector('button')).toBeInTheDocument();
- });
-
- it('has correct aria-label when inactive', () => {
- render(FavoriteButton, {
- props: { active: false, onclick: vi.fn() },
- });
- expect(screen.getByRole('button', { name: 'Favorit' })).toBeInTheDocument();
- });
-
- it('has correct aria-label when active', () => {
- render(FavoriteButton, {
- props: { active: true, onclick: vi.fn() },
- });
- expect(screen.getByRole('button', { name: 'Favorit entfernen' })).toBeInTheDocument();
- });
-
- it('calls onclick when clicked', async () => {
- const onclick = vi.fn();
- render(FavoriteButton, { props: { active: false, onclick } });
- await fireEvent.click(screen.getByRole('button'));
- expect(onclick).toHaveBeenCalledOnce();
- });
-
- it('uses pin labels for pin variant', () => {
- render(FavoriteButton, {
- props: { active: false, onclick: vi.fn(), variant: 'pin' },
- });
- expect(screen.getByRole('button', { name: 'Anpinnen' })).toBeInTheDocument();
- });
-
- it('uses custom label when provided', () => {
- render(FavoriteButton, {
- props: { active: false, onclick: vi.fn(), label: 'Custom' },
- });
- expect(screen.getByRole('button', { name: 'Custom' })).toBeInTheDocument();
- });
-});
diff --git a/packages/shared-ui/src/molecules/FilterDropdown.svelte b/packages/shared-ui/src/molecules/FilterDropdown.svelte
deleted file mode 100644
index 5c6063e0a..000000000
--- a/packages/shared-ui/src/molecules/FilterDropdown.svelte
+++ /dev/null
@@ -1,716 +0,0 @@
-
-
-
-
-
-
- {#if isOpen}
-
-
-
-
-
-
- {#if showSearch}
-
-
-
- {#if searchQuery}
-
- {/if}
-
- {/if}
-
-
-
- {#if filteredOptions.length === 0}
-
Keine Ergebnisse
- {:else}
- {#each [...groupedOptions] as [groupName, groupOptions], groupIndex}
- {#if groupName}
-
- {/if}
- {#each groupOptions as option, optionIndex}
- {@const flatIndex = selectableOptions.indexOf(option)}
-
- {/each}
- {/each}
- {/if}
-
-
-
- {#if multiSelect && Array.isArray(value) && value.length > 0}
-
- {/if}
-
- {/if}
-
-
-
diff --git a/packages/shared-ui/src/molecules/FilterDropdown.types.ts b/packages/shared-ui/src/molecules/FilterDropdown.types.ts
deleted file mode 100644
index 39e45266c..000000000
--- a/packages/shared-ui/src/molecules/FilterDropdown.types.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-export interface FilterDropdownOption {
- value: string;
- label: string;
- icon?: string;
- disabled?: boolean;
- divider?: boolean;
- group?: string;
-}
diff --git a/packages/shared-ui/src/molecules/IconPicker.constants.ts b/packages/shared-ui/src/molecules/IconPicker.constants.ts
deleted file mode 100644
index b535afddb..000000000
--- a/packages/shared-ui/src/molecules/IconPicker.constants.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export { ICON_CATEGORIES, getAllIconNames } from '@mana/shared-icons';
-
-export const DEFAULT_ICON = 'star';
diff --git a/packages/shared-ui/src/molecules/IconPicker.svelte b/packages/shared-ui/src/molecules/IconPicker.svelte
index a363578cd..31e1fe9af 100644
--- a/packages/shared-ui/src/molecules/IconPicker.svelte
+++ b/packages/shared-ui/src/molecules/IconPicker.svelte
@@ -1,65 +1,48 @@
-
-
+
{#if showSearch}
-
+
{/if}
- {#each Object.entries(filteredCategories) as [category, icons]}
-
+ {#each Object.entries(filteredCategories) as [category, icons] (category)}
+
{#if showCategories}
-
- {category}
-
+
{category}
{/if}
-
- {#each icons as iconName}
- {@const isSelected = selectedIcon === iconName}
- {@const IconComp = getIconComponent(iconName)}
- {#if IconComp}
-
- {/if}
+
+ {#each icons as icon (icon.name)}
+ {@const isSelected = selectedIcon === icon.name}
+
{/each}
{/each}
{#if Object.keys(filteredCategories).length === 0}
-
- Kein Icon gefunden
-
+
{emptyLabel}
{/if}
+
+
diff --git a/packages/shared-ui/src/molecules/Input.stories.svelte b/packages/shared-ui/src/molecules/Input.stories.svelte
new file mode 100644
index 000000000..07eaab551
--- /dev/null
+++ b/packages/shared-ui/src/molecules/Input.stories.svelte
@@ -0,0 +1,57 @@
+
+
+
+ {#snippet children()}
+
+
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+
+
+
+
+ {/snippet}
+
diff --git a/packages/shared-ui/src/molecules/Input.svelte b/packages/shared-ui/src/molecules/Input.svelte
index 001840ab5..7d0d45c8d 100644
--- a/packages/shared-ui/src/molecules/Input.svelte
+++ b/packages/shared-ui/src/molecules/Input.svelte
@@ -1,80 +1,193 @@
-
+
+
+
diff --git a/packages/shared-ui/src/molecules/KeyboardShortcutsPanel.svelte b/packages/shared-ui/src/molecules/KeyboardShortcutsPanel.svelte
deleted file mode 100644
index 5bb25f2ba..000000000
--- a/packages/shared-ui/src/molecules/KeyboardShortcutsPanel.svelte
+++ /dev/null
@@ -1,173 +0,0 @@
-
-
-{#if !compact}
-
-
-
-
-
- {#if expanded || !collapsible}
-
- {#each Object.entries(groupedShortcuts()) as [category, categoryShortcuts]}
-
- {#if Object.keys(groupedShortcuts()).length > 1}
-
- {category}
-
- {/if}
-
- {#each categoryShortcuts as shortcut}
-
-
- {shortcut.label}
-
-
- {#each shortcut.keys as key, i}
-
- {key}
-
- {#if i < shortcut.keys.length - 1}
- +
- {/if}
- {/each}
-
-
- {/each}
-
-
- {/each}
-
- {/if}
-
-{:else}
-
-
-{/if}
diff --git a/packages/shared-ui/src/molecules/ModalFooter.svelte b/packages/shared-ui/src/molecules/ModalFooter.svelte
deleted file mode 100644
index f7d7fbb16..000000000
--- a/packages/shared-ui/src/molecules/ModalFooter.svelte
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
-
diff --git a/packages/shared-ui/src/molecules/PageHeader.stories.svelte b/packages/shared-ui/src/molecules/PageHeader.stories.svelte
new file mode 100644
index 000000000..ae0c5b233
--- /dev/null
+++ b/packages/shared-ui/src/molecules/PageHeader.stories.svelte
@@ -0,0 +1,72 @@
+
+
+
+ {#snippet children()}
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+
+ {#snippet actions()}
+
+
+ {/snippet}
+
+
+ {/snippet}
+
+
+
+ {#snippet children()}
+
+
+ {#snippet breadcrumb()}
+ Decks
+ /
+ Spanisch
+ /
+ Karte 12
+ {/snippet}
+
+
+ {/snippet}
+
diff --git a/packages/shared-ui/src/molecules/PageHeader.svelte b/packages/shared-ui/src/molecules/PageHeader.svelte
index 08b0fc0f1..37026279a 100644
--- a/packages/shared-ui/src/molecules/PageHeader.svelte
+++ b/packages/shared-ui/src/molecules/PageHeader.svelte
@@ -1,213 +1,99 @@
-