frontend: Zoom skaliert Inhalt, Vollhöhen-Layout mit fixen Panes

Bisher wuchs beim Zoomen das Diagramm-Panel mit. Jetzt füllt die App die
Bildschirmhöhe (Body als Flex-Spalte), beide Panels sind flex-Container
mit fixem, scrollendem Inhaltsbereich. Der Zoom (CSS-`zoom` auf dem Baum)
skaliert damit nur den Inhalt; das Panel behält seine Größe, der Baum
scrollt im Diagramm-Viewport.

Splitter im Stapelmodus jetzt über die Grid-Zeile (--drow) statt
Diagramm-Höhe; Editor-Textarea füllt seinen Pane und scrollt intern.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Michael Hoennig
2026-07-21 11:18:02 +02:00
co-authored by Claude Opus 4.8
parent 486415d409
commit f32cda676b
+47 -34
View File
@@ -27,10 +27,11 @@
repeating-linear-gradient(0deg,transparent 0 23px,var(--grid) 23px 24px),
repeating-linear-gradient(90deg,transparent 0 23px,var(--grid) 23px 24px),
var(--paper);
min-height:100vh;
padding:32px 20px 56px;
height:100vh;overflow:hidden;
display:flex;flex-direction:column;
padding:24px 20px 12px;
}
header{max-width:1200px;margin:0 auto 24px}
header{max-width:1200px;width:100%;margin:0 auto 16px;flex:0 0 auto}
h1{font-size:1.25rem;font-weight:600;letter-spacing:-.01em}
header p{margin-top:4px;color:var(--muted);font-size:.9rem}
.site-footer{
@@ -42,32 +43,22 @@
Vertikal (Toggle an, .side): Editor links, Diagramm rechts.
Zwischen beiden liegt ein verschiebbarer Gutter (Splitter). */
.app{
max-width:1200px;margin:0 auto;
max-width:1200px;width:100%;margin:0 auto;
flex:1 1 auto;min-height:0;
display:grid;gap:0;
grid-template-columns:1fr;
grid-template-columns:minmax(0,1fr);
grid-template-rows:var(--drow,60%) 14px minmax(0,1fr);
grid-template-areas:"diagram" "gutter" "editor";
align-items:start;
align-items:stretch;
}
.app.side{
grid-template-columns:var(--col,360px) 14px 1fr;
grid-template-columns:var(--col,360px) 14px minmax(0,1fr);
grid-template-rows:minmax(0,1fr);
grid-template-areas:"editor gutter diagram";
}
.panel.editor{grid-area:editor}
.panel.right{grid-area:diagram}
.gutter{grid-area:gutter}
/* Im gestapelten Modus die Diagrammhöhe per Gutter begrenzen (scrollt intern). */
.app:not(.side) .diagram{max-height:var(--diagram-h,none)}
@media (max-width:820px){
.app.side,.app.side.collapse-editor,.app.side.collapse-diagram{
grid-template-columns:1fr;
grid-template-areas:"diagram" "gutter" "editor";
}
/* Im schmalen Viewport verhält sich Minimieren wie gestapelt (Kopf horizontal). */
.app.side .panel.collapsed .panel-head{flex-direction:row;height:auto;padding:10px 16px}
.app.side .panel.collapsed .panel-head>span{writing-mode:horizontal-tb;transform:none}
.app.side .panel.collapsed .winbtns{flex-direction:row}
}
/* Splitter/Gutter: stapelweise horizontal, seitlich vertikal. */
.gutter{
position:relative;align-self:stretch;justify-self:stretch;
@@ -90,11 +81,12 @@
.panel.collapsed .copybtn,
.panel.collapsed .switch,
.panel.collapsed .seg{display:none}
/* Gestapelt: nur die Kopfzeile bleibt, das andere Panel füllt. */
.app.collapse-diagram{grid-template-rows:auto 14px 1fr}
/* Gestapelt: kollabiertes Panel = Titelzeile (auto), anderes füllt. */
.app:not(.side).collapse-diagram{grid-template-rows:auto 14px minmax(0,1fr)}
.app:not(.side).collapse-editor{grid-template-rows:minmax(0,1fr) 14px auto}
/* Seitlich: schmaler vertikaler Streifen mit gedrehter Beschriftung. */
.app.side.collapse-editor{grid-template-columns:auto 14px 1fr}
.app.side.collapse-diagram{grid-template-columns:1fr 14px auto}
.app.side.collapse-editor{grid-template-columns:auto 14px minmax(0,1fr)}
.app.side.collapse-diagram{grid-template-columns:minmax(0,1fr) 14px auto}
.app.side .panel.collapsed{align-self:stretch}
.app.side .panel.collapsed .panel-head{
flex-direction:column;align-items:center;gap:12px;
@@ -106,10 +98,26 @@
}
.app.side .panel.collapsed .winbtns{flex-direction:column}
/* Schmaler Viewport: Seitenmodus fällt auf gestapelt zurück. */
@media (max-width:820px){
.app.side{
grid-template-columns:minmax(0,1fr);
grid-template-rows:var(--drow,60%) 14px minmax(0,1fr);
grid-template-areas:"diagram" "gutter" "editor";
}
.app.side.collapse-diagram{grid-template-columns:minmax(0,1fr);grid-template-rows:auto 14px minmax(0,1fr)}
.app.side.collapse-editor{grid-template-columns:minmax(0,1fr);grid-template-rows:minmax(0,1fr) 14px auto}
.app.side .panel.collapsed .panel-head{flex-direction:row;align-items:center;height:auto;padding:10px 16px}
.app.side .panel.collapsed .panel-head>span{writing-mode:horizontal-tb;transform:none}
.app.side .panel.collapsed .winbtns{flex-direction:row}
.app.side .gutter{height:14px;width:auto;cursor:row-resize}
.app.side .gutter::before{width:46px;height:4px}
}
/* Editor-Innenaufbau: Textarea + aufklappbare Agenda.
Horizontal nebeneinander, vertikal (.side) untereinander. */
.editor-body{display:flex;align-items:stretch}
.editor-body textarea{flex:1 1 auto;min-width:0}
.editor-body{display:flex;align-items:stretch;flex:1 1 auto;min-height:0}
.editor-body textarea{flex:1 1 auto;min-width:0;min-height:0}
.agenda{
flex:0 0 auto;
border-left:1px dashed rgba(36,52,71,.18);
@@ -126,7 +134,9 @@
.agenda>summary::-webkit-details-marker{display:none}
.agenda>summary::before{content:'▸';display:inline-block;transition:transform .15s}
.agenda[open]>summary::before{transform:rotate(90deg)}
.agenda .hint{border-top:0;overflow:auto}
.agenda{min-height:0}
.agenda[open]{overflow:hidden}
.agenda .hint{border-top:0;flex:1 1 auto;min-height:0;overflow:auto}
.side .editor-body{flex-direction:column;align-items:stretch}
.side .agenda{border-left:0;border-top:1px dashed rgba(36,52,71,.18)}
.side .agenda[open]{width:auto}
@@ -136,8 +146,10 @@
border:1px solid rgba(36,52,71,.14);
border-radius:12px;
overflow:hidden;
display:flex;flex-direction:column;min-height:0;min-width:0;
}
.panel-head{
flex:0 0 auto;
padding:10px 16px;
display:flex;justify-content:space-between;align-items:center;gap:14px;
font-family:'IBM Plex Mono',monospace;
@@ -197,8 +209,8 @@
}
.copybtn:hover,.copybtn.done{border-color:var(--or);color:var(--or)}
textarea{
display:block;width:100%;min-height:380px;
border:0;outline:none;resize:vertical;
display:block;width:100%;min-height:0;
border:0;outline:none;resize:none;
padding:14px 16px;
font-family:'IBM Plex Mono',monospace;
font-size:.85rem;line-height:1.7;
@@ -217,6 +229,7 @@
.hint .or-code{color:var(--or);border-color:rgba(15,118,110,.4)}
.diagram{
flex:1 1 auto;min-height:0;
padding:28px 16px 24px;
overflow:auto;
background:transparent;
@@ -674,7 +687,7 @@ const diagramPanel = document.querySelector('.panel.right');
function applySplit(){
/* Preset-/Drag-Größen bei Preset-Wechsel zurücksetzen */
app.style.removeProperty('--col');
app.style.removeProperty('--diagram-h');
app.style.removeProperty('--drow');
app.classList.toggle('collapse-diagram', splitState==='a');
app.classList.toggle('collapse-editor', splitState==='b');
diagramPanel.classList.toggle('collapsed', splitState==='a');
@@ -704,15 +717,15 @@ gutter.addEventListener('pointerdown', e => {
});
gutter.addEventListener('pointermove', e => {
if(!dragging) return;
const rect = app.getBoundingClientRect();
if(app.classList.contains('side')){
const rect = app.getBoundingClientRect();
let w = e.clientX - rect.left;
w = Math.max(220, Math.min(rect.width - 280, w));
app.style.setProperty('--col', w + 'px');
} else {
let h = e.clientY - diagramEl.getBoundingClientRect().top;
h = Math.max(140, h);
app.style.setProperty('--diagram-h', h + 'px');
let h = e.clientY - rect.top;
h = Math.max(120, Math.min(rect.height - 140, h));
app.style.setProperty('--drow', h + 'px');
}
});
function endDrag(e){