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,