diff --git a/apps/mana/apps/web/src/lib/modules/comic/components/CharacterBuilder.svelte b/apps/mana/apps/web/src/lib/modules/comic/components/CharacterBuilder.svelte index 8fba2985d..99c84fff6 100644 --- a/apps/mana/apps/web/src/lib/modules/comic/components/CharacterBuilder.svelte +++ b/apps/mana/apps/web/src/lib/modules/comic/components/CharacterBuilder.svelte @@ -194,7 +194,7 @@ id="character-add-prompt" type="text" bind:value={addPrompt} - placeholder="z.B. "freundlicher Ausdruck", "casual outfit", "action pose"" + placeholder="z.B. freundlicher Ausdruck, casual outfit, action pose" maxlength={200} class="block w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary disabled:opacity-50" disabled={busy} diff --git a/apps/mana/apps/web/src/lib/modules/comic/components/CharacterRefPicker.svelte b/apps/mana/apps/web/src/lib/modules/comic/components/CharacterRefPicker.svelte new file mode 100644 index 000000000..2defd322a --- /dev/null +++ b/apps/mana/apps/web/src/lib/modules/comic/components/CharacterRefPicker.svelte @@ -0,0 +1,233 @@ + + + +
+ + {#if usableCharacters.length > 0} +
+ + +
+ {/if} + + {#if mode === 'character'} +
+
+

+ Comic-Character wählen +

+

+ Iterier vorher einen Character mit deinem Stil — alle Panels nutzen dann denselben + gepinnten Look. +

+
+ + {#if usableCharacters.length === 0} + {#if !hasFace} + + {:else} +
+

Noch keine Characters mit Pin.

+

+ Bau einen Comic-Character aus deinem Foto — Stil wählen, 4 Varianten generieren, beste + pinnen. +

+ + + Character bauen + +
+ {/if} + {:else} +
+ {#each usableCharacters as character (character.id)} + {@const isSelected = selectedCharacterId === character.id} + {@const cover$ = usePanelImage(character.pinnedVariantId ?? null)} + {@const cover = cover$.value} + + {/each} + + + + Neuer Character + +
+ {/if} +
+ {:else} + +
+
+

+ Quick-Modus (Roh-Refs) +

+

+ Direkt face-ref + optional body-ref + Garments aus dem Schrank — ohne Character-Iteration. + Konsistenz zwischen Panels schwächer. +

+
+ +
+ {/if} +
diff --git a/apps/mana/apps/web/src/lib/modules/comic/components/StoryForm.svelte b/apps/mana/apps/web/src/lib/modules/comic/components/StoryForm.svelte index f23e1fea2..1ac68f2fa 100644 --- a/apps/mana/apps/web/src/lib/modules/comic/components/StoryForm.svelte +++ b/apps/mana/apps/web/src/lib/modules/comic/components/StoryForm.svelte @@ -15,10 +15,11 @@ import { comicStoriesStore } from '../stores/stories.svelte'; import type { ComicStyle } from '../types'; import StylePicker from './StylePicker.svelte'; - import CharacterPicker from './CharacterPicker.svelte'; + import CharacterRefPicker from './CharacterRefPicker.svelte'; let title = $state(''); let style = $state('comic'); + let characterId = $state(null); let characterMediaIds = $state([]); let storyContext = $state(''); let submitting = $state(false); @@ -38,6 +39,7 @@ const story = await comicStoriesStore.createStory({ title: title.trim(), style, + characterId, characterMediaIds, storyContext: storyContext.trim() || null, }); @@ -81,10 +83,15 @@

- - (characterMediaIds = next)} + + { + characterId = nextId; + characterMediaIds = nextRefs; + }} disabled={submitting} /> 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 5402e58e6..dd377f972 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 @@ -24,6 +24,10 @@ export interface CreateStoryInput { title: string; style: ComicStyle; characterMediaIds: string[]; + /** When the story is bound to a comicCharacter (Character-Mode), the + * FK lands here for display + cross-ref. Quick-Mode stories pass + * `null` and only fill `characterMediaIds` with raw face/body/garments. */ + characterId?: string | null; description?: string | null; storyContext?: string | null; tags?: string[]; @@ -45,6 +49,7 @@ export const comicStoriesStore = { title: input.title, description: input.description ?? null, style: input.style, + characterId: input.characterId ?? null, characterMediaIds: [...input.characterMediaIds], storyContext: input.storyContext ?? null, panelImageIds: [], diff --git a/apps/mana/apps/web/src/lib/modules/comic/types.ts b/apps/mana/apps/web/src/lib/modules/comic/types.ts index 5922652de..445ad1a52 100644 --- a/apps/mana/apps/web/src/lib/modules/comic/types.ts +++ b/apps/mana/apps/web/src/lib/modules/comic/types.ts @@ -65,10 +65,24 @@ export interface LocalComicStory extends BaseRecord { title: string; description?: string | null; style: ComicStyle; + /** + * FK to the comicCharacter that drives this story (Character-Mode, + * Mc3+). Plaintext — used for "Charakter: "-Anzeige im + * DetailView und für `useStoriesByCharacter`-Cross-Refs. + * + * `null` when the story was created in **Quick-Mode** (rohes + * face/body/garments-Setup ohne Character-Iteration). Beide Modi + * funktionieren parallel — die Story hängt am `characterMediaIds`- + * Array für die eigentliche Render-Logik (Snapshot-Pattern). + */ + characterId?: string | null; /** * Reference-image IDs passed unchanged to every panel-generate call. - * Minimum: the primary face-ref from meImages. Optional additions: - * body-ref + up to ~3 wardrobe-garment photos for a costume-setup. + * Character-Mode: enthält genau die `pinnedVariantMediaId` des + * referenzierten comicCharacters zum Story-Create-Zeitpunkt + * (Snapshot — Re-Pinning ändert das nicht rückwirkend). + * Quick-Mode: enthält face-ref + optional body-ref + Wardrobe- + * Garment-Photos. * Capped at 8 by the backend (MAX_REFERENCE_IMAGES in the /picture/ * generate-with-reference endpoint). */ @@ -104,6 +118,8 @@ export interface ComicStory { title: string; description?: string; style: ComicStyle; + /** FK to the comic-character driving this story; undefined in Quick-Mode. */ + characterId?: string; characterMediaIds: string[]; storyContext?: string; panelImageIds: string[]; @@ -122,6 +138,7 @@ export function toStory(local: LocalComicStory): ComicStory { title: local.title, description: local.description ?? undefined, style: local.style, + characterId: local.characterId ?? undefined, characterMediaIds: local.characterMediaIds ?? [], storyContext: local.storyContext ?? undefined, panelImageIds: local.panelImageIds ?? [],