From 474fd8b8893dc7a748e01cf462bc39325e2ee908 Mon Sep 17 00:00:00 2001 From: oliver Date: Mon, 21 Sep 2026 15:57:03 +0800 Subject: [PATCH] Restore native fullscreen for compare board and strengthen Key/Compare/Display zones. Co-authored-by: Cursor --- web/src/index.css | 112 ++++++++++++++++++----- web/src/pages/network/BizComparePage.tsx | 97 +++++++++++++++++--- 2 files changed, 170 insertions(+), 39 deletions(-) diff --git a/web/src/index.css b/web/src/index.css index 1580fe0..494f1ca 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -11321,27 +11321,43 @@ html.login-page--paused .login-page__flare { rgba(8, 13, 24, 0.4); } -/* CSS immersive board — avoids native Fullscreen + modal focus-trap breaking clicks */ -.bs-cmp-board.is-fullscreen { +/* Native :fullscreen + CSS immersive fallback (class only when FS API blocked) */ +.bs-cmp-board.is-fullscreen, +.bs-cmp-board:fullscreen { --bs-cmp-surface: rgba(15, 23, 42, 0.92); + background: #0b1220; + color: #e2e8f0; + padding: 12px 14px; + gap: 10px; + overflow: hidden; + box-sizing: border-box; +} + +.bs-cmp-board:fullscreen { + width: 100%; + height: 100%; + max-height: 100%; + min-height: 100%; + border-radius: 0; + margin: 0; +} + +/* CSS fallback only — used when requestFullscreen is blocked */ +.bs-cmp-board.is-fullscreen:not(:fullscreen) { position: fixed; inset: 0; - z-index: 12000; + z-index: 2147483000; width: 100vw; height: 100vh; max-height: 100vh; min-height: 100vh; margin: 0; border-radius: 0; - padding: 12px 14px; - gap: 10px; - overflow: hidden; - background: #0b1220; - color: #e2e8f0; box-shadow: none; } -body:has(.bs-cmp-board.is-fullscreen) { +body:has(.bs-cmp-board.is-fullscreen), +body:has(.bs-cmp-board:fullscreen) { overflow: hidden; } @@ -12235,21 +12251,21 @@ body:has(.bs-cmp-board.is-fullscreen) { } .bs-cmp-group--key { - background: rgba(37, 99, 235, 0.18) !important; - color: #93c5fd !important; - border-bottom: 2px solid rgba(59, 130, 246, 0.55); + background: rgba(37, 99, 235, 0.28) !important; + color: #bfdbfe !important; + border-bottom: 2px solid #3b82f6; } .bs-cmp-group--compare { - background: rgba(245, 158, 11, 0.16) !important; - color: #fcd34d !important; - border-bottom: 2px solid rgba(245, 158, 11, 0.55); + background: rgba(245, 158, 11, 0.26) !important; + color: #fde68a !important; + border-bottom: 2px solid #f59e0b; } .bs-cmp-group--display { - background: rgba(148, 163, 184, 0.12) !important; - color: #cbd5e1 !important; - border-bottom: 2px solid rgba(148, 163, 184, 0.35); + background: rgba(100, 116, 139, 0.28) !important; + color: #e2e8f0 !important; + border-bottom: 2px solid #94a3b8; } .bs-cmp-diff-table tbody td { @@ -12300,27 +12316,75 @@ body:has(.bs-cmp-board.is-fullscreen) { } .bs-cmp-diff-table thead tr.bs-cmp-field-row th.bs-cmp-col-key { - background: rgba(22, 36, 66, 0.98); + background: rgba(30, 58, 110, 0.95); + color: #bfdbfe; } .bs-cmp-diff-table thead tr.bs-cmp-field-row th.bs-cmp-col-compare { - background: rgba(42, 32, 16, 0.98); + background: rgba(78, 52, 12, 0.95); + color: #fde68a; } .bs-cmp-diff-table thead tr.bs-cmp-field-row th.bs-cmp-col-display { - background: rgba(15, 23, 42, 0.98); + background: rgba(40, 48, 64, 0.98); + color: #cbd5e1; } +/* Zone banding: tint body cells so Key / Compare / Display stay distinct while scrolling */ .bs-cmp-col-key { - background: rgba(59, 130, 246, 0.05); + --bs-cmp-zone: #3b82f6; + background: rgba(59, 130, 246, 0.08); } .bs-cmp-col-compare { - background: rgba(245, 158, 11, 0.04); + --bs-cmp-zone: #f59e0b; + background: rgba(245, 158, 11, 0.07); } .bs-cmp-col-display { - background: transparent; + --bs-cmp-zone: #94a3b8; + background: rgba(148, 163, 184, 0.04); +} + +.bs-cmp-zone-start { + box-shadow: inset 3px 0 0 var(--bs-cmp-zone, #64748b); +} + +.bs-cmp-diff-table tbody tr:nth-child(even) td.bs-cmp-col-key { + background: rgba(59, 130, 246, 0.12); +} + +.bs-cmp-diff-table tbody tr:nth-child(even) td.bs-cmp-col-compare { + background: rgba(245, 158, 11, 0.1); +} + +.bs-cmp-diff-table tbody tr:nth-child(even) td.bs-cmp-col-display { + background: rgba(148, 163, 184, 0.07); +} + +.bs-cmp-diff-table tbody tr:hover td.bs-cmp-col-key { + background: rgba(59, 130, 246, 0.16); +} + +.bs-cmp-diff-table tbody tr:hover td.bs-cmp-col-compare { + background: rgba(245, 158, 11, 0.14); +} + +.bs-cmp-diff-table tbody tr:hover td.bs-cmp-col-display { + background: rgba(148, 163, 184, 0.1); +} + +/* Diff highlight still wins inside compare/display cells */ +.bs-cmp-diff-table tbody td.bs-cmp-val-cell--diff { + background: rgba(245, 158, 11, 0.14) !important; +} + +.bs-cmp-diff-table tbody td.bs-cmp-val-cell--pair.is-removed { + background: rgba(239, 68, 68, 0.12) !important; +} + +.bs-cmp-diff-table tbody td.bs-cmp-val-cell--pair.is-added { + background: rgba(34, 197, 94, 0.12) !important; } .bs-cmp-overview__verdict { diff --git a/web/src/pages/network/BizComparePage.tsx b/web/src/pages/network/BizComparePage.tsx index f585ef4..bf232c7 100644 --- a/web/src/pages/network/BizComparePage.tsx +++ b/web/src/pages/network/BizComparePage.tsx @@ -166,18 +166,31 @@ function PairCell(props: { reason?: string; beforeLabel: string; afterLabel: string; + zoneClass?: string; + zoneStart?: boolean; }) { - const { beforeText, afterText, kind, mismatch, reason, beforeLabel, afterLabel } = props; + const { + beforeText, + afterText, + kind, + mismatch, + reason, + beforeLabel, + afterLabel, + zoneClass = "", + zoneStart = false, + } = props; const pre = beforeText || "—"; const post = afterText || "—"; const isAdded = kind === "added"; const isRemoved = kind === "removed"; const isDiff = mismatch || isAdded || isRemoved; + const zone = `${zoneClass}${zoneStart ? " bs-cmp-zone-start" : ""}`.trim(); // Matched values: single line (no 前/后 stack) — cuts density on pass-heavy sheets. if (!isDiff) { return ( - + {pre} ); @@ -204,7 +217,7 @@ function PairCell(props: {
@@ -872,6 +885,9 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage setRuns([]); setRunDetail(null); setResultSheetId(""); + if (document.fullscreenElement === boardRef.current) { + void document.exitFullscreen().catch(() => undefined); + } setBoardFs(false); }, [pageMode]); @@ -1216,8 +1232,19 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage tableScrollPosRef.current = { top: wrap.scrollTop, left: wrap.scrollLeft }; }, []); + // Native Fullscreen escapes modal transform containing-block (CSS fixed fails inside dialog). useEffect(() => { - if (!boardFs) return; + const syncFs = () => { + const el = boardRef.current; + setBoardFs(Boolean(el && document.fullscreenElement === el)); + }; + document.addEventListener("fullscreenchange", syncFs); + return () => document.removeEventListener("fullscreenchange", syncFs); + }, []); + + // CSS-fallback immersive: Esc exits without closing the job modal. + useEffect(() => { + if (!boardFs || document.fullscreenElement) return; const onKey = (e: KeyboardEvent) => { if (e.key !== "Escape") return; e.preventDefault(); @@ -1238,8 +1265,22 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage wrap.scrollLeft = left; }, [boardFs]); - const toggleBoardFullscreen = () => { + const toggleBoardFullscreen = async () => { + const el = boardRef.current; + if (!el) return; rememberTableScroll(); + try { + if (document.fullscreenElement === el) { + await document.exitFullscreen(); + return; + } + if (!document.fullscreenElement) { + await el.requestFullscreen(); + return; + } + } catch { + // Native FS blocked — CSS immersive fallback + } setBoardFs((v) => !v); }; @@ -1705,6 +1746,9 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage }; const closeJob = () => { + if (document.fullscreenElement === boardRef.current) { + void document.exitFullscreen().catch(() => undefined); + } setBoardFs(false); setJobId(""); setRuns([]); @@ -2829,7 +2873,7 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage size="sm" variant="secondary" isDisabled={!runDetail} - onPress={() => toggleBoardFullscreen()} + onPress={() => void toggleBoardFullscreen()} > {boardFs ? t("bizCompare.exitFullscreen") : t("bizCompare.fullscreen")} @@ -3110,18 +3154,27 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage )} {hasGroups ? ( - {keyCols.map((k) => ( - + {keyCols.map((k, ki) => ( + {k} ))} - {compareCols.map((f) => ( - + {compareCols.map((f, fi) => ( + {f} ))} - {displayCols.map((f) => ( - + {displayCols.map((f, fi) => ( + {f} ))} @@ -3161,16 +3214,26 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage : failFieldsLabel(d)} ) : null} - {resultColumns.keys.map((k) => ( - + {resultColumns.keys.map((k, ki) => ( + {cellText(d.key?.[k] ?? pre[k] ?? post[k]) || "—"} ))} - {resultColumns.extras.map((f) => { + {resultColumns.extras.map((f, fi) => { const pv = cellText(pre[f]); const av = cellText(post[f]); const ch = d.changes?.[f]; const isCmp = resultColumns.compareSet.has(f); + const prev = resultColumns.extras[fi - 1]; + const prevCmp = prev + ? resultColumns.compareSet.has(prev) + : null; + const zoneStart = fi === 0 || prevCmp !== isCmp; const mismatch = d.kind === "added" || d.kind === "removed" ? Boolean(pv || av) @@ -3187,6 +3250,10 @@ export function BizComparePage({ pageMode = "all" }: { pageMode?: BizComparePage reason={ch?.reason} beforeLabel={t("bizCompare.pairBefore")} afterLabel={t("bizCompare.pairAfter")} + zoneClass={ + isCmp ? "bs-cmp-col-compare" : "bs-cmp-col-display" + } + zoneStart={zoneStart} /> ); })}