diff --git a/apps/mana/apps/web/src/lib/modules/comic/components/CharacterPicker.svelte b/apps/mana/apps/web/src/lib/modules/comic/components/CharacterPicker.svelte
index 8277104b4..2ae71ef6f 100644
--- a/apps/mana/apps/web/src/lib/modules/comic/components/CharacterPicker.svelte
+++ b/apps/mana/apps/web/src/lib/modules/comic/components/CharacterPicker.svelte
@@ -142,8 +142,10 @@
type="button"
{disabled}
onclick={toggleBody}
- class="relative h-20 w-20 overflow-hidden rounded-md border transition-colors
- {bodyInValue ? 'border-primary/50' : 'border-border opacity-50 hover:opacity-100'}"
+ class="relative h-20 w-20 overflow-hidden rounded-md border transition-all active:translate-y-px
+ {bodyInValue
+ ? 'border-primary shadow-sm shadow-primary/20'
+ : 'border-border opacity-60 hover:border-primary/50 hover:opacity-100 hover:shadow-sm'}"
aria-pressed={bodyInValue}
title={bodyInValue ? 'Body-Ref entfernen' : 'Body-Ref hinzufügen'}
>
@@ -208,7 +210,10 @@
type="button"
{disabled}
onclick={() => (showGarmentPicker = !showGarmentPicker)}
- class="flex h-20 w-20 flex-col items-center justify-center gap-1 rounded-md border border-dashed border-border bg-background text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
+ class="flex h-20 w-20 flex-col items-center justify-center gap-1 rounded-md border border-dashed border-border bg-background text-muted-foreground transition-all hover:border-primary/50 hover:bg-primary/5 hover:text-foreground hover:shadow-sm active:translate-y-px"
+ class:!border-primary={showGarmentPicker}
+ class:!bg-primary={showGarmentPicker}
+ class:bg-opacity-10={showGarmentPicker}
aria-expanded={showGarmentPicker}
>
diff --git a/apps/mana/apps/web/src/lib/modules/comic/components/StylePicker.svelte b/apps/mana/apps/web/src/lib/modules/comic/components/StylePicker.svelte
index 5db300a9b..cdf703843 100644
--- a/apps/mana/apps/web/src/lib/modules/comic/components/StylePicker.svelte
+++ b/apps/mana/apps/web/src/lib/modules/comic/components/StylePicker.svelte
@@ -3,6 +3,11 @@
story-create time, fixed afterward (restyling = new story). Each
tile carries a short "what this looks like" hint so the user can
pick without having to memorise the preset mapping in styles.ts.
+
+ Markup pattern matches `PanelModelPicker` / wardrobe's
+ `TryOnModelPicker` so the create-flow has a coherent "click these
+ cards" affordance — both border AND background shift on hover so
+ the tiles read clearly as buttons.
-->
-
+
{#each STYLE_ORDER as style (style)}
{/each}
+
+
diff --git a/apps/mana/apps/web/src/lib/modules/comic/stores/stories.svelte.ts b/apps/mana/apps/web/src/lib/modules/comic/stores/stories.svelte.ts
index 27b964901..5402e58e6 100644
--- a/apps/mana/apps/web/src/lib/modules/comic/stores/stories.svelte.ts
+++ b/apps/mana/apps/web/src/lib/modules/comic/stores/stories.svelte.ts
@@ -35,16 +35,21 @@ export const comicStoriesStore = {
if (input.characterMediaIds.length === 0) {
throw new Error('Story needs at least one character reference image');
}
+ // Spread incoming arrays to break Svelte 5 $state proxies — the
+ // caller (StoryForm) declares `characterMediaIds`/`tags` as
+ // `$state
([])` and passes them directly. IndexedDB's
+ // structured-clone refuses to clone proxies, so without this
+ // `comicStoriesTable.add(...)` throws DataCloneError.
const newLocal: LocalComicStory = {
id: crypto.randomUUID(),
title: input.title,
description: input.description ?? null,
style: input.style,
- characterMediaIds: input.characterMediaIds,
+ characterMediaIds: [...input.characterMediaIds],
storyContext: input.storyContext ?? null,
panelImageIds: [],
panelMeta: {},
- tags: input.tags ?? [],
+ tags: input.tags ? [...input.tags] : [],
isFavorite: input.isFavorite ?? false,
visibility: defaultVisibilityFor(getActiveSpace()?.type),
};
@@ -64,7 +69,15 @@ export const comicStoriesStore = {
Pick
>
): Promise {
- const wrapped = { ...patch } as Record;
+ // Same proxy-breaking copy as createStory: any array on the patch
+ // might be a $state proxy if the caller is a Svelte 5 component.
+ const wrapped: Record = { ...patch };
+ if (Array.isArray(wrapped.characterMediaIds)) {
+ wrapped.characterMediaIds = [...(wrapped.characterMediaIds as string[])];
+ }
+ if (Array.isArray(wrapped.tags)) {
+ wrapped.tags = [...(wrapped.tags as string[])];
+ }
await encryptRecord('comicStories', wrapped);
await comicStoriesTable.update(id, {
...wrapped,