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

@@ -339,6 +339,10 @@ body {
max-width: 100%;
}
.canvas-area--relative {
position: relative;
}
canvas {
display: block;
max-width: 100%;
@@ -1003,33 +1007,76 @@ select:focus {
}
/* =====================================================
PROMPT EDITOR
PROMPT BAR (collapsible bottom panel in canvas-area)
===================================================== */
.prompt-editor {
width: 100%;
display: flex;
flex-direction: column;
gap: 6px;
.prompt-bar {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--r-lg);
padding: 10px;
flex: 1;
min-height: 0;
border-top: 1px solid var(--border);
z-index: 10;
box-shadow: 0 -4px 16px rgba(13,21,38,.08);
}
.prompt-editor-toolbar {
.prompt-bar-header {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 14px;
cursor: pointer;
user-select: none;
min-height: 40px;
}
.prompt-bar-header:hover {
background: var(--surface-2);
}
.prompt-bar-chevron {
font-size: 11px;
color: var(--text-3);
flex-shrink: 0;
width: 12px;
}
.prompt-bar-title {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--text-3);
}
.prompt-bar-layout-name {
font-size: 12px;
color: var(--text-2);
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: var(--r-full);
padding: 1px 8px;
}
.prompt-bar-actions {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
margin-left: auto;
}
.prompt-bar-body {
padding: 0 14px 12px;
display: flex;
flex-direction: column;
gap: 8px;
}
.prompt-layout-select {
width: auto !important;
min-width: 120px;
max-width: 200px;
font-size: 12.5px !important;
min-width: 110px;
max-width: 180px;
font-size: 12px !important;
padding: 4px 8px !important;
}
@@ -1037,7 +1084,7 @@ select:focus {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 10px;
padding: 6px 10px;
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: var(--r-md);
@@ -1051,8 +1098,8 @@ select:focus {
.prompt-textarea {
width: 100%;
flex: 1;
min-height: 180px;
min-height: 160px;
max-height: 40vh;
resize: vertical;
padding: 10px 12px;
background: var(--surface-2);

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,10 +289,9 @@ 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%' }}>
{/* 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}
@@ -302,11 +302,14 @@ export default function GenerateIt() {
readOnly
/>
</div>
</div>
{/* Prompt Editor */}
<div className="prompt-editor">
<div className="prompt-editor-toolbar">
{/* 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}
@@ -321,20 +324,20 @@ export default function GenerateIt() {
className="btn-ghost btn-sm btn-danger"
onClick={() => handleDeleteLayout(selectedLayoutName)}
title="Layout löschen"
>
</button>
></button>
)}
<button
className="btn-ghost btn-sm"
style={{ marginLeft: 'auto' }}
onClick={() => { setNewLayoutName(''); setShowSaveDialog(s => !s) }}
>
<SaveIcon />
Als Layout speichern
Speichern
</button>
</div>
</div>
{promptOpen && (
<div className="prompt-bar-body">
{showSaveDialog && (
<div className="prompt-save-dialog">
<input
@@ -353,7 +356,6 @@ export default function GenerateIt() {
</button>
</div>
)}
<textarea
className="prompt-textarea"
value={promptText}
@@ -361,6 +363,8 @@ export default function GenerateIt() {
placeholder="Prompt eingeben…"
/>
</div>
)}
</div>
</main>
{/* Words */}