Spaces:
Sleeping
排版重構:徹底清除頂部列/底部連結周圍殘留留白
Browse files上一個 commit(9b8d839)只解決了「捲動時被拖走」的問題,千卉重新整理
後回報「上下還是有空白」並附截圖標出頂部/底部兩塊實際留白區域,這次
才是真正把空白清零。
逐層用 devtools 量 computed style 揪出三層獨立成因,缺一個都還會留下
可見空白:
1. `.app`/`.gradio-container` 本身還有上下 padding(16px/24px):
之前為了「左右貼齊邊界」只清過左右方向,這次一併清掉上下方向;
`.app-header` 自己的 margin-top(1rem) 也拿掉。
2. 手機漢堡選單的兩個橋接元件(mobile_nav_toggle_html/
nav_backdrop_html):內層內容雖然 display:none,但 Gradio 自動包的
wrapper(帶 "padded" class)本身有約 20px 的空氣行高,即使內容是
display:none 也不會消失,兩個相加 40px。第一輪嘗試用
elem_classes+display:contents 處理,結果沒用——排查後發現這版
Gradio 的 elem_classes 會同時蓋在外層 .block 跟內層 .prose 兩層
div 上(本檔 8-B 就記過這個坑,這次加新元件時忘了套用),兩層
display:contents 疊加沒有真的清零。改用 elem_id 直接鎖定 wrapper、
寫死 display:none,JS 對應方式也從切內層元素改成切 wrapper 本身。
3. 光 display:none 還不夠:wrapper 被 JS 切成 display:block 讓手機
斷點能用時,即使自己 height:0,只要還是「排版流程內」的 flex 子項,
外層 column 的 gap:16px 照樣在它前後插入間距(CSS flexbox 的 gap
只認 display:none/不算 box 高度)——這是全新踩坑類別,跟先前任何
一次 flex-wrap/min-width 的坑都不同源。用 devtools 逐一
display:none 排查(不是用 display:contents 或 height:0 這種治標
猜測)才鎖定真因。修法:桌面寬度(>900px)用 media query +
!important 強制這兩個 wrapper 永遠 display:none,蓋過 JS 可能設的
inline display:block,只有手機寬度才放行讓 JS 判斷生效。
4. 最後 10px 殘留來自 Gradio 幫每個 gr.HTML() 元件包的 .html-container
通用 padding,用 `.html-container:has(.app-header)` 精準只清掉包住
頂部列的那一個,不影響 canvas-col 裡其他 gr.HTML()(如
computing_html)。
已用 Playwright 驗證:headerTop 與 footerBottom 現在精確等於 0/
viewport 高度,桌面/手機皆確認;手機漢堡按鈕、抽屜開關、開始新分析
reset 流程全部重測一輪皆正常;console 0 error。
- app_agent.py +117 -14
|
@@ -1842,7 +1842,7 @@ _CUSTOM_CSS = """
|
|
| 1842 |
width: 100% !important;
|
| 1843 |
max-width: 100% !important;
|
| 1844 |
margin: 0 !important;
|
| 1845 |
-
padding: 0
|
| 1846 |
}
|
| 1847 |
/* .gradio-container 內部還包了一層 Gradio 自己的 .app(class="main fillable app")
|
| 1848 |
wrapper。原本以為是它的左右 padding 造成貼不到邊界,歸零後留白仍在;
|
|
@@ -1851,10 +1851,13 @@ _CUSTOM_CSS = """
|
|
| 1851 |
設了 `max-width:1280px` 搭配 auto 置中的 margin-left(量到 77.5px =
|
| 1852 |
(1435-1280)/2,抓包抓得很準),是 margin 不是 padding。真正修法是拿掉
|
| 1853 |
max-width 限制、margin 歸零,padding 那條規則留著無害但其實沒解決過
|
| 1854 |
-
問題。
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1855 |
.app {
|
| 1856 |
-
padding
|
| 1857 |
-
padding-right: 0 !important;
|
| 1858 |
max-width: 100% !important;
|
| 1859 |
margin-left: 0 !important;
|
| 1860 |
margin-right: 0 !important;
|
|
@@ -1903,6 +1906,29 @@ gradio-app {
|
|
| 1903 |
footer {
|
| 1904 |
flex-shrink: 0 !important;
|
| 1905 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1906 |
*, *::before, *::after { box-sizing: border-box; }
|
| 1907 |
/* 全域安全網(陷阱 13 第 3 點):Gradio 的 markdown 渲染統一包在
|
| 1908 |
<span class="md ...">,預設是 inline 卻常常裝 block 層級的 <p>/<ul> 等,
|
|
@@ -1920,7 +1946,7 @@ footer {
|
|
| 1920 |
text-align: left;
|
| 1921 |
padding: .85rem 1.25rem;
|
| 1922 |
position: relative;
|
| 1923 |
-
margin:
|
| 1924 |
border: 1px solid var(--c-border);
|
| 1925 |
border-radius: 16px;
|
| 1926 |
background: linear-gradient(180deg, rgba(var(--c-accent-rgb),.07) 0%, rgba(var(--c-bg-rgb),.45) 100%);
|
|
@@ -2359,7 +2385,7 @@ div[role="log"][aria-label="chatbot conversation"],
|
|
| 2359 |
display: flex !important;
|
| 2360 |
flex-direction: column !important;
|
| 2361 |
gap: .6rem !important;
|
| 2362 |
-
height:
|
| 2363 |
overflow-y: auto !important;
|
| 2364 |
overflow-x: hidden !important;
|
| 2365 |
}
|
|
@@ -2374,7 +2400,7 @@ div[role="log"][aria-label="chatbot conversation"],
|
|
| 2374 |
flex-direction: column !important;
|
| 2375 |
flex-wrap: nowrap !important;
|
| 2376 |
gap: 1rem !important;
|
| 2377 |
-
height:
|
| 2378 |
overflow: hidden !important;
|
| 2379 |
min-width: 0 !important;
|
| 2380 |
}
|
|
@@ -2404,13 +2430,21 @@ div[role="log"][aria-label="chatbot conversation"],
|
|
| 2404 |
flex-direction: column !important;
|
| 2405 |
flex-wrap: nowrap !important;
|
| 2406 |
gap: 0 !important;
|
| 2407 |
-
height:
|
| 2408 |
overflow: hidden !important;
|
| 2409 |
min-width: 0 !important;
|
| 2410 |
}
|
| 2411 |
.canvas-col .app-header {
|
| 2412 |
flex-shrink: 0 !important;
|
| 2413 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2414 |
.canvas-scroll {
|
| 2415 |
display: flex !important;
|
| 2416 |
flex-direction: column !important;
|
|
@@ -2480,8 +2514,46 @@ div[role="log"][aria-label="chatbot conversation"],
|
|
| 2480 |
}
|
| 2481 |
/* ── 5d. HOMEPAGE(Phase 8:首頁 4 張情境卡片)──────────────────── */
|
| 2482 |
/* 隱藏版情境���鈕:純作 JS 橋接目標(見 _APP_HEAD_SCRIPT),
|
| 2483 |
-
點卡片=模擬點這裡的按鈕,Python 端 .click() 綁定完全不動
|
|
|
|
|
|
|
|
|
|
| 2484 |
.sc-bridge-btn { display: none !important; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2485 |
|
| 2486 |
.homepage-col { max-width: 50rem; margin: 2.5rem auto 3rem; padding: 0 1rem; }
|
| 2487 |
.homepage-hero { text-align: center; margin-bottom: 2.4rem; }
|
|
@@ -3574,9 +3646,16 @@ _APP_HEAD_SCRIPT = """
|
|
| 3574 |
當下)nav-col 根本還不存在,套不上。都改成搭這裡既有的
|
| 3575 |
setInterval 一起輪詢,不額外開新計時器。 */
|
| 3576 |
function _syncMobileHamburger() {
|
| 3577 |
-
|
| 3578 |
-
|
| 3579 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3580 |
}
|
| 3581 |
function _initNavCollapseIfNeeded() {
|
| 3582 |
var navCol = document.querySelector('.nav-col');
|
|
@@ -3709,11 +3788,35 @@ def create_app():
|
|
| 3709 |
# display:none,只在手機寬度且 workspace 啟用時由 JS 顯示(見
|
| 3710 |
# _APP_HEAD_SCRIPT 的 _syncMobileHamburger()),桌面寬度/首頁
|
| 3711 |
# 狀態下完全不佔版面也不可點。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3712 |
mobile_nav_toggle_html = gr.HTML(
|
| 3713 |
f'<button type="button" id="mobile_nav_toggle" class="mobile-nav-toggle" '
|
| 3714 |
-
f'aria-label="開啟導覽選單" aria-expanded="false">{_icon("bars-3", 22)}</button>'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3715 |
)
|
| 3716 |
-
nav_backdrop_html = gr.HTML('<div id="nav_backdrop" class="nav-backdrop"></div>')
|
| 3717 |
|
| 3718 |
# Phase 8-B:三欄式工作區——nav-col(窄,導覽/重置)+chat-col
|
| 3719 |
# (對話視窗+輸入框,獨立捲動)+canvas-col(步驟列+上傳/表單/
|
|
|
|
| 1842 |
width: 100% !important;
|
| 1843 |
max-width: 100% !important;
|
| 1844 |
margin: 0 !important;
|
| 1845 |
+
padding: 0 !important;
|
| 1846 |
}
|
| 1847 |
/* .gradio-container 內部還包了一層 Gradio 自己的 .app(class="main fillable app")
|
| 1848 |
wrapper。原本以為是它的左右 padding 造成貼不到邊界,歸零後留白仍在;
|
|
|
|
| 1851 |
設了 `max-width:1280px` 搭配 auto 置中的 margin-left(量到 77.5px =
|
| 1852 |
(1435-1280)/2,抓包抓得很準),是 margin 不是 padding。真正修法是拿掉
|
| 1853 |
max-width 限制、margin 歸零,padding 那條規則留著無害但其實沒解決過
|
| 1854 |
+
問題。
|
| 1855 |
+
後續(同日):千卉截圖標出頂部/底部還有大片空白,實測 .app 本身還有
|
| 1856 |
+
`padding:16px 0px`(上下各16px,之前只清過左右)、.gradio-container
|
| 1857 |
+
也還有 `padding:0 0 24px`(底部24px)——這兩個之前為了「左右貼齊」
|
| 1858 |
+
只處理過左右方向,上下方向從沒動過,這次一併歸零。 */
|
| 1859 |
.app {
|
| 1860 |
+
padding: 0 !important;
|
|
|
|
| 1861 |
max-width: 100% !important;
|
| 1862 |
margin-left: 0 !important;
|
| 1863 |
margin-right: 0 !important;
|
|
|
|
| 1906 |
footer {
|
| 1907 |
flex-shrink: 0 !important;
|
| 1908 |
}
|
| 1909 |
+
/* 千卉截圖回報頂部列還是沒有真正貼齊頂端,量到 main-layout-row 距離
|
| 1910 |
+
.contain 頂端還有 72px 空隙——追出來是 .nav-col/.chat-col/.canvas-col
|
| 1911 |
+
原本寫死的 `calc(100vh - 150px)` 這個 magic number,是照舊版「頁面
|
| 1912 |
+
周圍還有一堆 padding/margin」的年代校準出來的高度預算。這次把
|
| 1913 |
+
.app/.gradio-container 的上下 padding 都清空、footer 也鎖定
|
| 1914 |
+
flex-shrink:0 之後,實際可用空間比 150px 這個預留值還多,多出來的
|
| 1915 |
+
空間沒地方去,就變成三欄跟外層容器之間看得到的空隙。
|
| 1916 |
+
修法:不要再猜一個固定 px 預算,改成整條鏈都用 flex 100% 往下傳,
|
| 1917 |
+
有多少空間就填多少,不會再跟實際版面脫節。.contain 底下只有一個
|
| 1918 |
+
直屬子元素(Gradio Blocks 的根 Column),這裡讓它 flex 撐滿
|
| 1919 |
+
.contain,裡面的 main-layout-row 再 flex 撐滿它,nav-col/chat-col/
|
| 1920 |
+
canvas-col 三欄改成 height:100%(相對 main-layout-row 的高度),
|
| 1921 |
+
不再各自寫死 calc(100vh - 150px)。 */
|
| 1922 |
+
.contain > .column {
|
| 1923 |
+
display: flex !important;
|
| 1924 |
+
flex-direction: column !important;
|
| 1925 |
+
height: 100% !important;
|
| 1926 |
+
min-height: 0 !important;
|
| 1927 |
+
}
|
| 1928 |
+
.main-layout-row {
|
| 1929 |
+
flex: 1 1 auto !important;
|
| 1930 |
+
min-height: 0 !important;
|
| 1931 |
+
}
|
| 1932 |
*, *::before, *::after { box-sizing: border-box; }
|
| 1933 |
/* 全域安全網(陷阱 13 第 3 點):Gradio 的 markdown 渲染統一包在
|
| 1934 |
<span class="md ...">,預設是 inline 卻常常裝 block 層級的 <p>/<ul> 等,
|
|
|
|
| 1946 |
text-align: left;
|
| 1947 |
padding: .85rem 1.25rem;
|
| 1948 |
position: relative;
|
| 1949 |
+
margin: 0 0 1.25rem;
|
| 1950 |
border: 1px solid var(--c-border);
|
| 1951 |
border-radius: 16px;
|
| 1952 |
background: linear-gradient(180deg, rgba(var(--c-accent-rgb),.07) 0%, rgba(var(--c-bg-rgb),.45) 100%);
|
|
|
|
| 2385 |
display: flex !important;
|
| 2386 |
flex-direction: column !important;
|
| 2387 |
gap: .6rem !important;
|
| 2388 |
+
height: 100% !important;
|
| 2389 |
overflow-y: auto !important;
|
| 2390 |
overflow-x: hidden !important;
|
| 2391 |
}
|
|
|
|
| 2400 |
flex-direction: column !important;
|
| 2401 |
flex-wrap: nowrap !important;
|
| 2402 |
gap: 1rem !important;
|
| 2403 |
+
height: 100% !important;
|
| 2404 |
overflow: hidden !important;
|
| 2405 |
min-width: 0 !important;
|
| 2406 |
}
|
|
|
|
| 2430 |
flex-direction: column !important;
|
| 2431 |
flex-wrap: nowrap !important;
|
| 2432 |
gap: 0 !important;
|
| 2433 |
+
height: 100% !important;
|
| 2434 |
overflow: hidden !important;
|
| 2435 |
min-width: 0 !important;
|
| 2436 |
}
|
| 2437 |
.canvas-col .app-header {
|
| 2438 |
flex-shrink: 0 !important;
|
| 2439 |
}
|
| 2440 |
+
/* 最後一段殘餘留白(10px):Gradio 幫所有 gr.HTML() 元件的內容包了一層
|
| 2441 |
+
.html-container,本身有 padding:10px 12px,這是全站每個 gr.HTML()
|
| 2442 |
+
共用的預設樣式(首頁卡片、運算中提示等都靠它),不能整個歸零,只精準
|
| 2443 |
+
鎖定「包住 .app-header 的那一個」html-container,不影響其他 gr.HTML()
|
| 2444 |
+
元件(例如同樣在 canvas-col 裡的 computing_html)。 */
|
| 2445 |
+
.html-container:has(.app-header) {
|
| 2446 |
+
padding: 0 !important;
|
| 2447 |
+
}
|
| 2448 |
.canvas-scroll {
|
| 2449 |
display: flex !important;
|
| 2450 |
flex-direction: column !important;
|
|
|
|
| 2514 |
}
|
| 2515 |
/* ── 5d. HOMEPAGE(Phase 8:首頁 4 張情境卡片)──────────────────── */
|
| 2516 |
/* 隱藏版情境���鈕:純作 JS 橋接目標(見 _APP_HEAD_SCRIPT),
|
| 2517 |
+
點卡片=模擬點這裡的按鈕,Python 端 .click() 綁定完全不動。
|
| 2518 |
+
gr.Button 的 elem_classes 直接掛在 <button> 標籤本身(不像
|
| 2519 |
+
gr.HTML() 會多一層 wrapper),所以這裡單純 display:none 就完全
|
| 2520 |
+
不佔版面,沒有下面 .nav-html-bridge 那個問題。 */
|
| 2521 |
.sc-bridge-btn { display: none !important; }
|
| 2522 |
+
/* 2026-07-23 踩坑:gr.HTML() 元件的實際內容跟 Gradio 自動產生的外層
|
| 2523 |
+
wrapper(.block)是兩個不同的 DOM 節點——只把內層內容(例如
|
| 2524 |
+
#mobile_nav_toggle/#nav_backdrop)設 display:none,wrapper 本身
|
| 2525 |
+
還是會依自己的預設 min-height/padding 佔用版面高度,疊加上跟相鄰
|
| 2526 |
+
元件的 flex gap,變成看得到但摸不著原因的空白區塊(千卉截圖回報
|
| 2527 |
+
頂部/底部大片空白時發現)。第一輪試過 display:contents 沒有真的
|
| 2528 |
+
清零(懷疑跟 elem_classes 同時疊在 .block 與 .prose 兩層有關,
|
| 2529 |
+
detail 見 create_app() 裡兩個 gr.HTML() 呼叫旁的完整記錄),改用
|
| 2530 |
+
最直接可靠的做法:wrapper 直接用 elem_id 鎖定、預設寫死
|
| 2531 |
+
display:none——一般 block 元素包一個 display:none 的子元素天生
|
| 2532 |
+
零高度,不用依賴 display:contents 這種較冷門的屬性。JS 對應改成
|
| 2533 |
+
切換這兩個 wrapper 本身的 display(見 _APP_HEAD_SCRIPT 的
|
| 2534 |
+
_syncMobileHamburger()),內層 #mobile_nav_toggle/#nav_backdrop
|
| 2535 |
+
自己原本的桌面/手機斷點 CSS 完全不用動。 */
|
| 2536 |
+
/* 兩層踩坑疊在一起:①這個 wrapper 帶 Gradio 自己的 "padded" class,
|
| 2537 |
+
就算內層內容 display:none,wrapper 本身還是會因為 line-height留下
|
| 2538 |
+
約20px空氣行高②就算加 height:0 讓 wrapper 自己不佔高度,JS 為了
|
| 2539 |
+
手機斷點把它切成 display:block 時,它仍然是「有在排版流程裡」的
|
| 2540 |
+
flex 子項,外層 column 的 gap:16px 還是會在它前後插入間距——CSS
|
| 2541 |
+
flexbox 的 gap 只認「這個子項是不是 display:none」,不管它自己的
|
| 2542 |
+
box 高度是不是 0。用 devtools 逐一 display:none 排查才抓到這一層,
|
| 2543 |
+
單靠 height:0 治標不治本。
|
| 2544 |
+
真正修法:桌面寬度(>900px)這兩個 wrapper 必須是貨真價實的
|
| 2545 |
+
display:none(徹底退出 flex 排版,不只是零高度),用 !important
|
| 2546 |
+
蓋掉 JS 可能設的 inline display:block;手機寬度(<=900px)才放行讓
|
| 2547 |
+
JS 的 block/none 判斷生效。這樣桌面寬度不管 nav-col 存不存在,這兩個
|
| 2548 |
+
wrapper 永遠不佔用任何 flex 空間。 */
|
| 2549 |
+
#mobile_nav_toggle_wrap, #nav_backdrop_wrap {
|
| 2550 |
+
display: none;
|
| 2551 |
+
}
|
| 2552 |
+
@media (min-width: 901px) {
|
| 2553 |
+
#mobile_nav_toggle_wrap, #nav_backdrop_wrap {
|
| 2554 |
+
display: none !important;
|
| 2555 |
+
}
|
| 2556 |
+
}
|
| 2557 |
|
| 2558 |
.homepage-col { max-width: 50rem; margin: 2.5rem auto 3rem; padding: 0 1rem; }
|
| 2559 |
.homepage-hero { text-align: center; margin-bottom: 2.4rem; }
|
|
|
|
| 3646 |
當下)nav-col 根本還不存在,套不上。都改成搭這裡既有的
|
| 3647 |
setInterval 一起輪詢,不額外開新計時器。 */
|
| 3648 |
function _syncMobileHamburger() {
|
| 3649 |
+
/* 2026-07-23:改成切換 wrapper(#mobile_nav_toggle_wrap/
|
| 3650 |
+
#nav_backdrop_wrap)本身的 display,不是內層按鈕/遮罩自己
|
| 3651 |
+
的 style——wrapper 預設 display:none 才能真正零高度(見
|
| 3652 |
+
CSS 對應註解),內層元素桌面/手機斷點的顯示邏輯完全不變,
|
| 3653 |
+
這裡只負責「nav-col 存不存在」這個外層開關。 */
|
| 3654 |
+
var navColExists = !!document.querySelector('.nav-col');
|
| 3655 |
+
var toggleWrap = document.getElementById('mobile_nav_toggle_wrap');
|
| 3656 |
+
var backdropWrap = document.getElementById('nav_backdrop_wrap');
|
| 3657 |
+
if (toggleWrap) toggleWrap.style.display = navColExists ? 'block' : 'none';
|
| 3658 |
+
if (backdropWrap) backdropWrap.style.display = navColExists ? 'block' : 'none';
|
| 3659 |
}
|
| 3660 |
function _initNavCollapseIfNeeded() {
|
| 3661 |
var navCol = document.querySelector('.nav-col');
|
|
|
|
| 3788 |
# display:none,只在手機寬度且 workspace 啟用時由 JS 顯示(見
|
| 3789 |
# _APP_HEAD_SCRIPT 的 _syncMobileHamburger()),桌面寬度/首頁
|
| 3790 |
# 狀態下完全不佔版面也不可點。
|
| 3791 |
+
# 2026-07-23 踩坑(分兩輪才修對):這兩個元件的實際內容
|
| 3792 |
+
# (#mobile_nav_toggle/#nav_backdrop)本身雖然預設 display:none,
|
| 3793 |
+
# 但那只是隱藏「內層」元素——Gradio 自動包在 gr.HTML() 外面的
|
| 3794 |
+
# wrapper(.block)沒有被一併處理,仍然佔用版面高度+跟相鄰元件的
|
| 3795 |
+
# flex gap,變成看得到但摸不著原因的空白區塊(千卉截圖回報頂部/
|
| 3796 |
+
# 底部有大片空白才發現)。
|
| 3797 |
+
# 第一輪嘗試:elem_classes 掛 nav-html-bridge,CSS 設
|
| 3798 |
+
# `display:contents`,理論上該讓 wrapper 不產生盒模型——實測
|
| 3799 |
+
# 沒用,逐一 display:none 排查後才發現:這版 Gradio 的
|
| 3800 |
+
# elem_classes 會同時蓋在外層 .block wrapper 跟內層 .prose
|
| 3801 |
+
# wrapper 兩層 div 上(這個踩坑本檔 8-B 就記過一次「.block.<class>
|
| 3802 |
+
# 跟.prose.<class>」的雙重疊加,這次加新元件時忘了套用同一個
|
| 3803 |
+
# 教訓),兩層 display:contents 疊加後不知為何沒有真的清零
|
| 3804 |
+
# flex gap 佔用的空間。
|
| 3805 |
+
# 第二輪改用最直接可靠的做法:wrapper 用 elem_id 直接鎖定,
|
| 3806 |
+
# 預設寫死 display:none(一般 block 元素包一個 display:none 的
|
| 3807 |
+
# 子元素,天生就是零高度,不用依賴 display:contents 這種比較
|
| 3808 |
+
# 少人踩過的屬性),JS 改成切換這個 wrapper 本身的 display,
|
| 3809 |
+
# 內層 #mobile_nav_toggle/#nav_backdrop 自己的 CSS(桌面/手機
|
| 3810 |
+
# 斷點判斷)完全不用動,兩層互不干擾。
|
| 3811 |
mobile_nav_toggle_html = gr.HTML(
|
| 3812 |
f'<button type="button" id="mobile_nav_toggle" class="mobile-nav-toggle" '
|
| 3813 |
+
f'aria-label="開啟導覽選單" aria-expanded="false">{_icon("bars-3", 22)}</button>',
|
| 3814 |
+
elem_id="mobile_nav_toggle_wrap",
|
| 3815 |
+
)
|
| 3816 |
+
nav_backdrop_html = gr.HTML(
|
| 3817 |
+
'<div id="nav_backdrop" class="nav-backdrop"></div>',
|
| 3818 |
+
elem_id="nav_backdrop_wrap",
|
| 3819 |
)
|
|
|
|
| 3820 |
|
| 3821 |
# Phase 8-B:三欄式工作區——nav-col(窄,導覽/重置)+chat-col
|
| 3822 |
# (對話視窗+輸入框,獨立捲動)+canvas-col(步驟列+上傳/表單/
|