Generieren: Bild wie Annotieren-Seite groß, Prompt als eingeklappte Leiste

- Canvas-Struktur identisch zu DrawIt (canvas-area > canvas-frame > DrawCanvas)
  → kein Schrumpfen beim Bild-Wechsel mehr
- Prompt-Editor als absolute Leiste am unteren Rand des Canvas-Bereichs
- Eingeklappt: zeigt Titel + Layout-Name + Aktionen
- Ausgeklappt: Textarea + Speichern-Dialog

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-26 08:23:08 +02:00
parent 7f85b90a82
commit 99a8d7e0aa
2 changed files with 135 additions and 84 deletions

View File

@@ -139,6 +139,7 @@ export default function GenerateIt() {
const [promptText, setPromptText] = useState(() => loadLayouts()[0]?.prompt ?? DEFAULT_PROMPT)
const [showSaveDialog, setShowSaveDialog] = useState(false)
const [newLayoutName, setNewLayoutName] = useState('')
const [promptOpen, setPromptOpen] = useState(false)
const currentPicture: DirectusPicture | null =
currentIndex >= 0 && currentIndex < pictureList.length ? pictureList[currentIndex] : null
@@ -288,78 +289,81 @@ export default function GenerateIt() {
</div>
</aside>
{/* Center: Canvas + Prompt Editor */}
<main className="canvas-area" style={{ alignItems: 'flex-start', justifyContent: 'flex-start', flexDirection: 'column', gap: 12, padding: 16 }}>
<div style={{ width: '100%', maxHeight: 320, overflow: 'hidden' }}>
<div className="canvas-frame" style={{ display: 'inline-flex', maxWidth: '100%' }}>
<DrawCanvas
ref={canvasRef}
imageSrc={currentPicture && token ? directusAssetUrl(currentPicture.media, token) : null}
objects={canvasObjects}
selectedObjectId={selectedObjId}
mode="rect"
onHasSelection={() => {}}
readOnly
/>
</div>
{/* Center: Canvas + Prompt Bar */}
<main className="canvas-area canvas-area--relative">
<div className="canvas-frame">
<DrawCanvas
ref={canvasRef}
imageSrc={currentPicture && token ? directusAssetUrl(currentPicture.media, token) : null}
objects={canvasObjects}
selectedObjectId={selectedObjId}
mode="rect"
onHasSelection={() => {}}
readOnly
/>
</div>
{/* Prompt Editor */}
<div className="prompt-editor">
<div className="prompt-editor-toolbar">
<select
className="prompt-layout-select"
value={selectedLayoutName}
onChange={e => handleSelectLayout(e.target.value)}
>
{layouts.map(l => (
<option key={l.name} value={l.name}>{l.name}</option>
))}
</select>
{selectedLayoutName !== 'Standard' && (
<button
className="btn-ghost btn-sm btn-danger"
onClick={() => handleDeleteLayout(selectedLayoutName)}
title="Layout löschen"
{/* Collapsible Prompt Bar */}
<div className={`prompt-bar${promptOpen ? ' prompt-bar--open' : ''}`}>
<div className="prompt-bar-header" onClick={() => setPromptOpen(o => !o)}>
<span className="prompt-bar-chevron">{promptOpen ? '▾' : '▸'}</span>
<span className="prompt-bar-title">Prompt</span>
<span className="prompt-bar-layout-name">{selectedLayoutName}</span>
<div className="prompt-bar-actions" onClick={e => e.stopPropagation()}>
<select
className="prompt-layout-select"
value={selectedLayoutName}
onChange={e => handleSelectLayout(e.target.value)}
>
</button>
)}
<button
className="btn-ghost btn-sm"
style={{ marginLeft: 'auto' }}
onClick={() => { setNewLayoutName(''); setShowSaveDialog(s => !s) }}
>
<SaveIcon />
Als Layout speichern
</button>
</div>
{showSaveDialog && (
<div className="prompt-save-dialog">
<input
type="text"
placeholder="Layout-Name"
value={newLayoutName}
onChange={e => setNewLayoutName(e.target.value)}
onKeyDown={e => { if (e.key === 'Enter') handleSaveLayout(); if (e.key === 'Escape') setShowSaveDialog(false) }}
autoFocus
/>
<button className="btn-ghost btn-sm" onClick={handleSaveLayout} disabled={!newLayoutName.trim()}>
{layouts.map(l => (
<option key={l.name} value={l.name}>{l.name}</option>
))}
</select>
{selectedLayoutName !== 'Standard' && (
<button
className="btn-ghost btn-sm btn-danger"
onClick={() => handleDeleteLayout(selectedLayoutName)}
title="Layout löschen"
></button>
)}
<button
className="btn-ghost btn-sm"
onClick={() => { setNewLayoutName(''); setShowSaveDialog(s => !s) }}
>
<SaveIcon />
Speichern
</button>
<button className="btn-ghost btn-sm" onClick={() => setShowSaveDialog(false)}>
Abbrechen
</button>
</div>
</div>
{promptOpen && (
<div className="prompt-bar-body">
{showSaveDialog && (
<div className="prompt-save-dialog">
<input
type="text"
placeholder="Layout-Name"
value={newLayoutName}
onChange={e => setNewLayoutName(e.target.value)}
onKeyDown={e => { if (e.key === 'Enter') handleSaveLayout(); if (e.key === 'Escape') setShowSaveDialog(false) }}
autoFocus
/>
<button className="btn-ghost btn-sm" onClick={handleSaveLayout} disabled={!newLayoutName.trim()}>
Speichern
</button>
<button className="btn-ghost btn-sm" onClick={() => setShowSaveDialog(false)}>
Abbrechen
</button>
</div>
)}
<textarea
className="prompt-textarea"
value={promptText}
onChange={e => setPromptText(e.target.value)}
placeholder="Prompt eingeben…"
/>
</div>
)}
<textarea
className="prompt-textarea"
value={promptText}
onChange={e => setPromptText(e.target.value)}
placeholder="Prompt eingeben…"
/>
</div>
</main>