From cf70a197cdf298842ba71f8c56f1984ca2b89287 Mon Sep 17 00:00:00 2001 From: Schalli Date: Tue, 22 Sep 2026 12:22:37 +0200 Subject: [PATCH] fix(quick-260922-ge2): Rahmenhoehe in der Kachel = Vorschauhoehe, Vorschau ohne Querbalken MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Befund aus dem Browser-Rundgang: example.com setzt `margin: 15vh` — mit 3000 px Vorschauhoehe lag die Ueberschrift bei y 450, in der Kachel mit 720 px Rahmenhoehe bei y 108. Der in der Vorschau gewaehlte Ausschnitt zeigte in der Kachel also etwas anderes. Die Kachel nutzt jetzt dieselbe Layouthoehe wie die Vorschau (3000), damit vh-relative Seiten identisch umbrechen; der Rest wird ohnehin weggeschnitten. Vorschau: `overflow-x-hidden`, die Eckgriffe am rechten Rand erzeugten einen 4-px-Querbalken. Co-Authored-By: Claude Opus 5 (1M context) --- .../dashboard/widgets/xframe-crop.test.ts | 16 +++++++------- .../dashboard/widgets/xframe-crop.ts | 22 ++++++++++++------- .../dashboard/widgets/xframe-widget.test.tsx | 4 ++-- .../settings/xframe-config-form.tsx | 2 +- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/apps/web/src/components/dashboard/widgets/xframe-crop.test.ts b/apps/web/src/components/dashboard/widgets/xframe-crop.test.ts index fe427f3..48c7f26 100644 --- a/apps/web/src/components/dashboard/widgets/xframe-crop.test.ts +++ b/apps/web/src/components/dashboard/widgets/xframe-crop.test.ts @@ -15,11 +15,11 @@ const START = { x: 100, y: 100, w: 400, h: 300 }; describe('computeCropLayout (quick-260922-ge2)', () => { it('Test 1: breitenbegrenzt — 1280x720 in 640x720 -> scale 0.5, left 0, top 180', () => { - expect(computeCropLayout(FULL, 640, 720)).toEqual({ scale: 0.5, left: 0, top: 180, frameHeight: 720 }); + expect(computeCropLayout(FULL, 640, 720)).toEqual({ scale: 0.5, left: 0, top: 180, frameHeight: 3000 }); }); it('Test 2: hoehenbegrenzt — 1280x720 in 1280x180 -> scale 0.25, left 480, top 0', () => { - expect(computeCropLayout(FULL, 1280, 180)).toEqual({ scale: 0.25, left: 480, top: 0, frameHeight: 720 }); + expect(computeCropLayout(FULL, 1280, 180)).toEqual({ scale: 0.25, left: 480, top: 0, frameHeight: 3000 }); }); it('Test 3: Vergroesserung erlaubt — 200x100 in 800x400 -> scale 4', () => { @@ -27,18 +27,18 @@ describe('computeCropLayout (quick-260922-ge2)', () => { scale: 4, left: 0, top: 0, - frameHeight: 720, + frameHeight: 3000, }); }); - it('Test 4: frameHeight mindestens 720, sonst y + h', () => { - expect(computeCropLayout({ x: 0, y: 0, w: 1280, h: 100 }, 640, 360).frameHeight).toBe(720); - expect(computeCropLayout({ x: 0, y: 3000, w: 1280, h: 400 }, 640, 360).frameHeight).toBe(3400); + it('Test 4: frameHeight ist immer die Vorschauhoehe 3000 — gleiches Layout wie in der Vorschau (vh-Masse)', () => { + expect(computeCropLayout({ x: 0, y: 0, w: 1280, h: 100 }, 640, 360).frameHeight).toBe(3000); + expect(computeCropLayout({ x: 0, y: 3000, w: 1280, h: 400 }, 640, 360).frameHeight).toBe(3000); }); it('Test 5: ungemessene Kachel (0x0 oder 640x0) -> scale 0, left 0, top 0 (nichts rendern)', () => { - expect(computeCropLayout(FULL, 0, 0)).toEqual({ scale: 0, left: 0, top: 0, frameHeight: 720 }); - expect(computeCropLayout(FULL, 640, 0)).toEqual({ scale: 0, left: 0, top: 0, frameHeight: 720 }); + expect(computeCropLayout(FULL, 0, 0)).toEqual({ scale: 0, left: 0, top: 0, frameHeight: 3000 }); + expect(computeCropLayout(FULL, 640, 0)).toEqual({ scale: 0, left: 0, top: 0, frameHeight: 3000 }); }); }); diff --git a/apps/web/src/components/dashboard/widgets/xframe-crop.ts b/apps/web/src/components/dashboard/widgets/xframe-crop.ts index 904af26..1292355 100644 --- a/apps/web/src/components/dashboard/widgets/xframe-crop.ts +++ b/apps/web/src/components/dashboard/widgets/xframe-crop.ts @@ -8,10 +8,14 @@ * der beiden Massstaebe (darf > 1 sein: ein kleiner Ausschnitt in einer * grossen Kachel wird vergroessert) und die Zentrierung im Rest. * - * `frameHeight` ist mindestens 720: die eingebettete Seite braucht eine - * Layouthoehe, sonst rendern viele Seiten ihren Inhalt nicht oder brechen - * ihn anders um; liegt der Ausschnitt tiefer, reicht der Rahmen bis zu - * seiner Unterkante. + * `frameHeight` ist IMMER die Vorschauhoehe (3000 Seitenpixel), nicht + * "Unterkante des Ausschnitts": viele Seiten setzen Masse relativ zur + * Fensterhoehe (`vh`, `100vh`-Zentrierung, `margin: 15vh` bei example.com). + * Haetten Vorschau und Kachel verschiedene Rahmenhoehen, laege derselbe + * Inhalt an verschiedenen Stellen — der in der Vorschau gewaehlte Ausschnitt + * zeigte in der Kachel etwas anderes (Browser-Rundgang 22.09.2026: Ueberschrift + * in der Vorschau bei y 450, in der Kachel mit 720 px Rahmen bei y 108). + * Gleiche Hoehe = gleiches Layout; der Rest wird ohnehin weggeschnitten. * * Ziehmathematik der Vorschau: `move` verschiebt bei fester Groesse und * klemmt an den Seitenraendern; die Eckmodi bewegen nur die zwei Kanten @@ -29,10 +33,12 @@ import { clampXframeCrop, } from './xframe-config'; -/** Hoehe der Vorschau-Seite in Seitenpixeln (Vorschau und Ziehen); 4000 bleibt die Speichergrenze. */ +/** + * Hoehe der Seite in Seitenpixeln — in der Vorschau (Stage, Ziehen) UND als + * Layouthoehe des Rahmens in der Kachel (siehe Kopfkommentar); 4000 bleibt + * die Speichergrenze fuer `crop.y + crop.h`. + */ export const XFRAME_PREVIEW_PAGE_HEIGHT = 3000; -/** Mindest-Layouthoehe des Rahmens im Ausschnitt-Modus. */ -const MIN_FRAME_HEIGHT = 720; export type XframeDragMode = 'move' | 'nw' | 'ne' | 'sw' | 'se'; @@ -45,7 +51,7 @@ export interface XframeCropLayout { } export function computeCropLayout(crop: XframeCrop, tileW: number, tileH: number): XframeCropLayout { - const frameHeight = Math.max(crop.y + crop.h, MIN_FRAME_HEIGHT); + const frameHeight = XFRAME_PREVIEW_PAGE_HEIGHT; if (tileW <= 0 || tileH <= 0) return { scale: 0, left: 0, top: 0, frameHeight }; const scale = Math.min(tileW / crop.w, tileH / crop.h); return { diff --git a/apps/web/src/components/dashboard/widgets/xframe-widget.test.tsx b/apps/web/src/components/dashboard/widgets/xframe-widget.test.tsx index cac2160..261441c 100644 --- a/apps/web/src/components/dashboard/widgets/xframe-widget.test.tsx +++ b/apps/web/src/components/dashboard/widgets/xframe-widget.test.tsx @@ -179,7 +179,7 @@ describe('XframeWidget — Ausschnitt, Zoom, readOnly (quick-260922-ge2)', () => expect(f.style.left).toBe('0px'); expect(f.style.top).toBe('0px'); expect(f.style.width).toBe('1280px'); - expect(f.style.height).toBe('720px'); + expect(f.style.height).toBe('3000px'); expect(f.style.transform).toBe('scale(0.5)'); expect(f.style.transformOrigin).toBe('0 0'); expect(f.className).not.toContain('h-full'); @@ -201,7 +201,7 @@ describe('XframeWidget — Ausschnitt, Zoom, readOnly (quick-260922-ge2)', () => const f = frame(); expect(f.style.left).toBe('-160px'); expect(f.style.top).toBe('-80px'); - expect(f.style.height).toBe('720px'); + expect(f.style.height).toBe('3000px'); expect(f.style.transform).toBe('scale(0.8)'); }); diff --git a/apps/web/src/components/settings/xframe-config-form.tsx b/apps/web/src/components/settings/xframe-config-form.tsx index 64c9b83..994947d 100644 --- a/apps/web/src/components/settings/xframe-config-form.tsx +++ b/apps/web/src/components/settings/xframe-config-form.tsx @@ -344,7 +344,7 @@ function CropPreview({ url, crop, onCommit, rectangleLabel, emptyText }: CropPre
{url === null ? (