un2266 commited on
Commit
6f0ed7a
·
1 Parent(s): 3ced5f9

排版重構 Phase 1:四邊留白/三欄比例/輸入框多行/頂部列精簡

Browse files

千卉用參考圖比對現況後逐項定案的版面調整(大範圍改動的第一階段,
先做低風險、互相獨立的項目):

- 四邊留白:真因不是原本猜測的 .contain wrapper padding(devtools 逐層
量測後排除,那層 padding 早就是 0),是 Gradio 的 .app 元素被設了
max-width:1280px + auto 置中 margin,改成 max-width:100% + margin 歸零
- 三欄比例 nav:chat:canvas 從 2:4:7 改成 2:3:7
- 對話輸入框 lines=3 變成真正多行 textarea;對話視窗跟輸入框改用
flex-grow(6:1)依可用高度動態分配,不寫死 px,實測比例約 5.4:1
- 頂部列(app-header)從三行大標題+額外進度條精簡成單行產品名+步驟列,
拿掉進度條(跟步驟列的已完成/進行中/失敗三態是重複資訊)

A/B/D 三欄頂端對齊問題:實測 devtools 確認三欄 DOM 頂端本來就是像素級
對齊(皆 top:73px),不是 CSS bug,是 nav-col 第一個項目沒有卡片邊框
(收合按鈕裸露),視覺上比相鄰有邊框卡片的欄位「感覺」低,留給下一階段
nav-col 視覺重做時一併處理。

已用 Playwright 驗證:三欄邊界貼齊、輸入框可打多行且高度比例正確、
頂部列精簡且無進度條、reset 流程正常回首頁無殘留,首頁本身不受
.app 這個全域選擇器改動影響。

Files changed (1) hide show
  1. app_agent.py +82 -74
app_agent.py CHANGED
@@ -1394,9 +1394,10 @@ _STEPS = [
1394
  def _build_header_html(done_steps: list = None, current_step: int = None,
1395
  failed_step: int = None) -> str:
1396
  """
1397
- 產生完整標題區塊 HTML(標題+動態步驟列+進度條),確定性、不依賴 LLM。
1398
- 取代原本寫死不會變的步驟列,以及原本另外放在右欄、跟這裡資訊重複的
1399
- 「執行進度」面板——進度資訊現在只在這裡出現一次。
 
1400
  done_steps: 已完成步驟(1-based list)
1401
  current_step: 目前執行步驟(1-based int)
1402
  failed_step: 執行失敗時卡住的步驟(1-based int),失敗時該步驟改用失敗樣式,
@@ -1427,15 +1428,10 @@ def _build_header_html(done_steps: list = None, current_step: int = None,
1427
  f'<span class="step-num">{num}</span>{name}</span>'
1428
  )
1429
 
1430
- pct = int(len(done_steps) / len(_STEPS) * 100)
1431
-
1432
  return (
1433
  '<div class="app-header">'
1434
- ' <h1>譯彩紛呈:重譯文本分析系統</h1>'
1435
- ' <p class="subtitle">TransPrism: An Analytical Framework for Retranslation</p>'
1436
- ' <p class="subtitle-3">II 分析引擎 SpectraEngine</p>'
1437
  f' <div class="steps">{"".join(pills)}</div>'
1438
- f' <div class="step-progress-track"><div class="step-progress-fill" style="width:{pct}%"></div></div>'
1439
  '</div>'
1440
  )
1441
 
@@ -1849,12 +1845,19 @@ _CUSTOM_CSS = """
1849
  padding: 0 0 1.5rem !important;
1850
  }
1851
  /* .gradio-container 內部還包了一層 Gradio 自己的 .app(class="main fillable app")
1852
- wrapper,本身固定帶 32px 左右 padding(非我們設定,Gradio 內建),這才是
1853
- 拿掉 .gradio-container 左右 padding 後畫面仍貼不到邊界的真正原因(已用
1854
- devtools 逐層量到)。一併歸零 */
 
 
 
 
1855
  .app {
1856
  padding-left: 0 !important;
1857
  padding-right: 0 !important;
 
 
 
1858
  }
1859
  *, *::before, *::after { box-sizing: border-box; }
1860
  /* 全域安全網(陷阱 13 第 3 點):Gradio 的 markdown 渲染統一包在
@@ -1864,10 +1867,14 @@ _CUSTOM_CSS = """
1864
  字級仍留給各自的選擇器決定,這裡不覆寫 font-size。 */
1865
  .md { display: block !important; }
1866
 
1867
- /* ── 2. HEADER(卡片化)─────────────────────────────────────────── */
1868
  .app-header {
1869
- text-align: center;
1870
- padding: 1.6rem 1.5rem 1.25rem;
 
 
 
 
1871
  position: relative;
1872
  margin: 1rem 0 1.25rem;
1873
  border: 1px solid var(--c-border);
@@ -1892,41 +1899,22 @@ _CUSTOM_CSS = """
1892
  `background-image: linear-gradient(...) !important;` 這個子屬性本身,
1893
  跳過簡寫展開的解析路徑——已用瀏覽器 devtools 即時修改驗證過,改長寫法
1894
  後漸層立即正常顯示。*/
1895
- .app-header h1 {
1896
  font-family: 'Crimson Pro', 'Noto Serif TC', Georgia, serif !important;
1897
- font-size: 2.1rem !important;
1898
  font-weight: 700 !important;
1899
  -webkit-background-clip: text;
1900
  -webkit-text-fill-color: transparent;
1901
  background-clip: text;
1902
- margin: 0 0 .5rem !important;
1903
  letter-spacing: -.01em;
1904
  line-height: 1.2 !important;
 
 
1905
  }
1906
- .app-header .subtitle {
1907
- font-size: 1.1rem !important;
1908
- line-height: 1.5 !important;
1909
- margin: 0 0 .25rem !important;
1910
- -webkit-background-clip: text !important;
1911
- -webkit-text-fill-color: transparent !important;
1912
- background-clip: text !important;
1913
- color: transparent !important;
1914
- }
1915
- /* 第三排:字型比第二排大、比主標題小 */
1916
- .app-header .subtitle-3 {
1917
- font-size: 1.5rem !important;
1918
- font-weight: 700 !important;
1919
- line-height: 1.4 !important;
1920
- margin: 0 !important;
1921
- -webkit-background-clip: text !important;
1922
- -webkit-text-fill-color: transparent !important;
1923
- background-clip: text !important;
1924
- color: transparent !important;
1925
- }
1926
- .app-header .subtitle strong {
1927
- -webkit-text-fill-color: transparent !important;
1928
- color: transparent !important;
1929
- font-weight: 600;
1930
  }
1931
  .badge {
1932
  display: inline-flex; align-items: center; gap: 4px;
@@ -1948,10 +1936,12 @@ _CUSTOM_CSS = """
1948
  color: var(--c-accent2-strong);
1949
  border: 1px solid rgba(var(--c-accent2-rgb),.30);
1950
  }
1951
- /* step indicators */
 
 
1952
  .app-header .steps {
1953
- display: flex; justify-content: center; flex-wrap: wrap;
1954
- gap: 4px; margin-top: 1rem;
1955
  }
1956
  .step-pill {
1957
  display: inline-flex; align-items: center; gap: 6px;
@@ -2018,21 +2008,6 @@ _CUSTOM_CSS = """
2018
  background: #dc2626;
2019
  color: #fff;
2020
  }
2021
- /* 步驟列下方的整體進度條,取代原本另外放在右欄、跟步驟列資訊重複的執行進度面板 */
2022
- .step-progress-track {
2023
- height: 4px;
2024
- background: var(--c-border);
2025
- border-radius: 2px;
2026
- margin-top: .75rem;
2027
- overflow: hidden;
2028
- }
2029
- .step-progress-fill {
2030
- height: 100%;
2031
- background: linear-gradient(90deg, var(--c-accent-strong), var(--c-accent2));
2032
- border-radius: 2px;
2033
- transition: width .3s ease;
2034
- }
2035
-
2036
  /* ── 3. TABS ───────────────────────────────────────────────────── */
2037
  /* 注意:這裡原本用 backdrop-filter 做毛玻璃效果,搭配 position: sticky
2038
  會讓瀏覽器每個捲動影格都要重新計算模糊,頁面一長(大表格、多張圖)就會
@@ -2117,6 +2092,17 @@ _CUSTOM_CSS = """
2117
  overflow-y: auto !important;
2118
  overflow-x: hidden !important;
2119
  }
 
 
 
 
 
 
 
 
 
 
 
2120
 
2121
  /* 訊息從頂部開始排列,消除空白 */
2122
  div[role="log"].bubble-wrap,
@@ -2344,12 +2330,25 @@ div[role="log"][aria-label="chatbot conversation"],
2344
  display: flex !important;
2345
  flex-direction: column !important;
2346
  flex-wrap: nowrap !important;
2347
- gap: 0 !important;
2348
  height: calc(100vh - 150px) !important;
2349
- overflow-x: hidden !important;
2350
- overflow-y: auto !important;
2351
  min-width: 0 !important;
2352
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2353
  /* canvas-col 本身「不」捲動——它只負責把步驟列(固定)跟 canvas-scroll
2354
  (捲動)疊在一起、限制在同一個總高度內。踩坑記錄:原本想讓步驟列用
2355
  position:sticky 貼在 canvas-col 頂端,但 sticky 元素只能在「自己最近的
@@ -2425,7 +2424,7 @@ div[role="log"][aria-label="chatbot conversation"],
2425
  }
2426
  .homepage-hero h1 {
2427
  margin: 1rem 0 .6rem; font-size: 2.1rem; line-height: 1.3; font-weight: 700;
2428
- /* 同 .app-header h1 的漸層文字踩坑(見 _DARK_MODE_HEAD_CSS 上方註解):
2429
  background 簡寫在瀏覽器展開成子屬性時 background-image 會解析成
2430
  空值,改直接寫 background-image 這個子屬性本身即可 */
2431
  background-image: linear-gradient(100deg, var(--c-accent) 10%, var(--c-accent2) 90%);
@@ -2654,11 +2653,19 @@ img.button-icon { opacity: .82; }
2654
  margin: .6rem 0 0 !important;
2655
  width: 100% !important;
2656
  }
2657
- /* chat-col 是 gap:0 的 flex 欄,上傳卡片搬到「對話視窗+技術執行紀錄」
2658
- 這組下方後,要用明顯留白跟上面那組分開,不能只靠卡片自己 .6rem 的
2659
- 預設頂距(那個距離是設計給同張卡片內部元件用的,太窄) */
 
2660
  #chat_upload_zone {
2661
- margin-top: 1.6rem !important;
 
 
 
 
 
 
 
2662
  }
2663
  /* 格式要求直接寫在上傳卡片裡,跟卡片本身有明顯區隔但仍在同一張卡片內,
2664
  使用者不用往上捲回對話視窗找 AI 說過的規則 */
@@ -3052,7 +3059,7 @@ a:hover { color: var(--c-link-hover); }
3052
  .canvas-scroll { overflow-y: visible !important; min-height: 0 !important; }
3053
  }
3054
  @media (max-width: 768px) {
3055
- .app-header h1 { font-size: 1.6rem !important; }
3056
  .tabitem { padding: 1rem .75rem !important; }
3057
  .app-header .steps { gap: 3px; }
3058
  .step-pill { font-size: 0.62rem; padding: 2px 7px; }
@@ -3079,9 +3086,7 @@ a:hover { color: var(--c-link-hover); }
3079
  # 同一批修掉(見 _CUSTOM_CSS 對應區塊)。
3080
  _DARK_MODE_HEAD_CSS = """
3081
  <style>
3082
- .app-header h1,
3083
- .app-header .subtitle,
3084
- .app-header .subtitle-3 {
3085
  background-image: linear-gradient(130deg, var(--c-accent-strong) 0%, var(--c-accent2) 55%, var(--c-accent3) 100%) !important;
3086
  -webkit-background-clip: text !important;
3087
  background-clip: text !important;
@@ -3569,10 +3574,12 @@ def create_app():
3569
  elem_id="nav_report_btn", elem_classes=["result-nav-btn"], variant="secondary",
3570
  )
3571
 
3572
- with gr.Column(scale=4, min_width=300, elem_classes=["chat-col"]):
 
 
 
3573
  chatbot = gr.Chatbot(
3574
  show_label=False,
3575
- height=400,
3576
  elem_classes=["main-chat"],
3577
  )
3578
  # Phase 8(千卉實測後回頭調整):對話輸入框改回「常駐顯示、
@@ -3589,6 +3596,7 @@ def create_app():
3589
  sources=[],
3590
  placeholder="對話輸入框(功能開發中)",
3591
  show_label=False,
 
3592
  )
3593
 
3594
  with gr.Column(scale=7, min_width=380, elem_classes=["canvas-col"]):
 
1394
  def _build_header_html(done_steps: list = None, current_step: int = None,
1395
  failed_step: int = None) -> str:
1396
  """
1397
+ 產生完整標題區塊 HTML(精簡單行產品名+動態步驟列),確定性、不依賴 LLM。
1398
+ 2026-07-23 千卉回報原本三行大標題+額外進度條佔比過高,改成單行精簡
1399
+ 產品名、拿掉進度條(進度資訊本來就已經完整表達在步驟列的已完成/進行中
1400
+ /失敗三種狀態上,進度條是重複資訊)。
1401
  done_steps: 已完成步驟(1-based list)
1402
  current_step: 目前執行步驟(1-based int)
1403
  failed_step: 執行失敗時卡住的步驟(1-based int),失敗時該步驟改用失敗樣式,
 
1428
  f'<span class="step-num">{num}</span>{name}</span>'
1429
  )
1430
 
 
 
1431
  return (
1432
  '<div class="app-header">'
1433
+ ' <p class="title-line">譯彩紛呈 <span class="en">SpectraEngine</span></p>'
 
 
1434
  f' <div class="steps">{"".join(pills)}</div>'
 
1435
  '</div>'
1436
  )
1437
 
 
1845
  padding: 0 0 1.5rem !important;
1846
  }
1847
  /* .gradio-container 內部還包了一層 Gradio 自己的 .app(class="main fillable app")
1848
+ wrapper。原本以為是它的左右 padding 造成貼不到邊界,歸零後留白仍在;
1849
+ 千卉 2026-07-23 截圖回報四邊仍有留白,重新用 devtools 沿著
1850
+ .main-layout-row 往上逐層量 computed style 才找到真因:.app 被 Gradio
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-left: 0 !important;
1857
  padding-right: 0 !important;
1858
+ max-width: 100% !important;
1859
+ margin-left: 0 !important;
1860
+ margin-right: 0 !important;
1861
  }
1862
  *, *::before, *::after { box-sizing: border-box; }
1863
  /* 全域安全網(陷阱 13 第 3 點):Gradio 的 markdown 渲染統一包在
 
1867
  字級仍留給各自的選擇器決定,這裡不覆寫 font-size。 */
1868
  .md { display: block !important; }
1869
 
1870
+ /* ── 2. HEADER(卡片化,2026-07-23 精簡成單行)───────────────────── */
1871
  .app-header {
1872
+ display: flex;
1873
+ align-items: center;
1874
+ flex-wrap: wrap;
1875
+ gap: .9rem;
1876
+ text-align: left;
1877
+ padding: .85rem 1.25rem;
1878
  position: relative;
1879
  margin: 1rem 0 1.25rem;
1880
  border: 1px solid var(--c-border);
 
1899
  `background-image: linear-gradient(...) !important;` 這個子屬性本身,
1900
  跳過簡寫展開的解析路徑——已用瀏覽器 devtools 即時修改驗證過,改長寫法
1901
  後漸層立即正常顯示。*/
1902
+ .app-header .title-line {
1903
  font-family: 'Crimson Pro', 'Noto Serif TC', Georgia, serif !important;
1904
+ font-size: 1.3rem !important;
1905
  font-weight: 700 !important;
1906
  -webkit-background-clip: text;
1907
  -webkit-text-fill-color: transparent;
1908
  background-clip: text;
1909
+ margin: 0 !important;
1910
  letter-spacing: -.01em;
1911
  line-height: 1.2 !important;
1912
+ white-space: nowrap;
1913
+ flex: none;
1914
  }
1915
+ .app-header .title-line .en {
1916
+ font-size: 1.05rem !important;
1917
+ font-weight: 600 !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1918
  }
1919
  .badge {
1920
  display: inline-flex; align-items: center; gap: 4px;
 
1936
  color: var(--c-accent2-strong);
1937
  border: 1px solid rgba(var(--c-accent2-rgb),.30);
1938
  }
1939
+ /* step indicators:2026-07-23 header 改單行版面後,steps 改成跟標題
1940
+ 同一行左右排列(flex-wrap 讓視窗窄時自動換行),不再置中、不再需要
1941
+ 靠 margin-top 跟上方大標題拉開距離(間距已交給 .app-header 的 gap) */
1942
  .app-header .steps {
1943
+ display: flex; justify-content: flex-start; flex-wrap: wrap;
1944
+ gap: 4px; margin: 0; flex: 1 1 auto; min-width: 0;
1945
  }
1946
  .step-pill {
1947
  display: inline-flex; align-items: center; gap: 6px;
 
2008
  background: #dc2626;
2009
  color: #fff;
2010
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2011
  /* ── 3. TABS ───────────────────────────────────────────────────── */
2012
  /* 注意:這裡原本用 backdrop-filter 做毛玻璃效果,搭配 position: sticky
2013
  會讓瀏覽器每個捲動影格都要重新計算模糊,頁面一長(大表格、多張圖)就會
 
2092
  overflow-y: auto !important;
2093
  overflow-x: hidden !important;
2094
  }
2095
+ /* 2026-07-23:.main-chat 外層改用 flex-grow 決定高度(見「5b. COLUMN
2096
+ LAYOUT」區塊的 5:1 比例設定),中間這層 wrapper 也要跟著撐滿,
2097
+ 高度才會一路傳到 bubble-wrap,捲動區域才會是「剩下的全部空間」
2098
+ 而不是「內容多高就多高」。 */
2099
+ .main-chat > div,
2100
+ .main-chat [class*="wrapper"] {
2101
+ flex: 1 1 auto !important;
2102
+ min-height: 0 !important;
2103
+ display: flex !important;
2104
+ flex-direction: column !important;
2105
+ }
2106
 
2107
  /* 訊息從頂部開始排列,消除空白 */
2108
  div[role="log"].bubble-wrap,
 
2330
  display: flex !important;
2331
  flex-direction: column !important;
2332
  flex-wrap: nowrap !important;
2333
+ gap: 1rem !important;
2334
  height: calc(100vh - 150px) !important;
2335
+ overflow: hidden !important;
 
2336
  min-width: 0 !important;
2337
  }
2338
+ /* 對話視窗:輸入框高度比例 5:1(千卉要求,2026-07-23)。用 flex-grow
2339
+ 依 chat-col 當下的可用高度分配,不寫死 px,縮放視窗時比例仍會維持;
2340
+ chatbot 元件不再用 Gradio 原生 height= 參數(那是寫死 px),改讓外層
2341
+ wrapper 高度完全交給 flex 決定。訊息列表本身的捲動仍是 Gradio 原生
2342
+ 行為(.bubble-wrap 內部自己有捲軸),外層 wrapper 用 overflow:hidden
2343
+ 避免出現雙捲軸。 */
2344
+ .main-chat {
2345
+ flex: 6 1 0 !important;
2346
+ min-height: 0 !important;
2347
+ height: auto !important;
2348
+ overflow: hidden !important;
2349
+ display: flex !important;
2350
+ flex-direction: column !important;
2351
+ }
2352
  /* canvas-col 本身「不」捲動——它只負責把步驟列(固定)跟 canvas-scroll
2353
  (捲動)疊在一起、限制在同一個總高度內。踩坑記錄:原本想讓步驟列用
2354
  position:sticky 貼在 canvas-col 頂端,但 sticky 元素只能在「自己最近的
 
2424
  }
2425
  .homepage-hero h1 {
2426
  margin: 1rem 0 .6rem; font-size: 2.1rem; line-height: 1.3; font-weight: 700;
2427
+ /* 同 .app-header .title-line 的漸層文字踩坑(見 _DARK_MODE_HEAD_CSS 上方註解):
2428
  background 簡寫在瀏覽器展開成子屬性時 background-image 會解析成
2429
  空值,改直接寫 background-image 這個子屬性本身即可 */
2430
  background-image: linear-gradient(100deg, var(--c-accent) 10%, var(--c-accent2) 90%);
 
2653
  margin: .6rem 0 0 !important;
2654
  width: 100% !important;
2655
  }
2656
+ /* 2026-07-23:chat-col 改成 gap:1rem 的 flex 欄,跟 chatbot 之間的留白
2657
+ 已經由 gap 統一處理,這裡不用再疊加 margin-top(舊註解提到的「技術
2658
+ 執行紀錄」分組已在 Phase 8-B 搬去 canvas-col,跟這裡無關,是過期
2659
+ 說明)。改成跟 .main-chat 同一套 5:1 flex-grow 比例分配高度。 */
2660
  #chat_upload_zone {
2661
+ flex: 1 1 0 !important;
2662
+ min-height: 0 !important;
2663
+ display: flex !important;
2664
+ flex-direction: column !important;
2665
+ }
2666
+ #chat_upload_zone .multimodal-textbox,
2667
+ #chat_upload_zone textarea {
2668
+ height: 100% !important;
2669
  }
2670
  /* 格式要求直接寫在上傳卡片裡,跟卡片本身有明顯區隔但仍在同一張卡片內,
2671
  使用者不用往上捲回對話視窗找 AI 說過的規則 */
 
3059
  .canvas-scroll { overflow-y: visible !important; min-height: 0 !important; }
3060
  }
3061
  @media (max-width: 768px) {
3062
+ .app-header .title-line { font-size: 1.1rem !important; }
3063
  .tabitem { padding: 1rem .75rem !important; }
3064
  .app-header .steps { gap: 3px; }
3065
  .step-pill { font-size: 0.62rem; padding: 2px 7px; }
 
3086
  # 同一批修掉(見 _CUSTOM_CSS 對應區塊)。
3087
  _DARK_MODE_HEAD_CSS = """
3088
  <style>
3089
+ .app-header .title-line {
 
 
3090
  background-image: linear-gradient(130deg, var(--c-accent-strong) 0%, var(--c-accent2) 55%, var(--c-accent3) 100%) !important;
3091
  -webkit-background-clip: text !important;
3092
  background-clip: text !important;
 
3574
  elem_id="nav_report_btn", elem_classes=["result-nav-btn"], variant="secondary",
3575
  )
3576
 
3577
+ with gr.Column(scale=3, min_width=280, elem_classes=["chat-col"]):
3578
+ # 高度不再寫死 px,改用 CSS flex-grow 讓對話視窗跟輸入框依
3579
+ # chat-col 當下的可用高度按 5:1 比例分配(見 .main-chat/
3580
+ # #chat_upload_zone 的 flex 設定),視窗縮放時比例仍會維持。
3581
  chatbot = gr.Chatbot(
3582
  show_label=False,
 
3583
  elem_classes=["main-chat"],
3584
  )
3585
  # Phase 8(千卉實測後回頭調整):對話輸入框改回「常駐顯示、
 
3596
  sources=[],
3597
  placeholder="對話輸入框(功能開發中)",
3598
  show_label=False,
3599
+ lines=3,
3600
  )
3601
 
3602
  with gr.Column(scale=7, min_width=380, elem_classes=["canvas-col"]):