un2266 Claude Sonnet 5 commited on
Commit
00301ba
·
1 Parent(s): e06c7b8

UI改版 Phase 1:字級統一、Metadata標題拿掉跳號、footer精簡、對話框視覺調整

Browse files

- 全站字級收斂為 --font-size-base token;Metadata表單標題拿掉數字編號;
footer 移除 Gradio 品牌連結;Metadata略過文案定稿
- 對話氣泡改回貼合文字內容、加大四邊留白、移除色條邊線改用淺灰底色,
並修正 Gradio Chatbot 元件內建的 message-row margin/max-width 限制,
讓 AI/使用者訊息都能貼近對話視窗邊緣

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Files changed (2) hide show
  1. .gitignore +1 -0
  2. app_agent.py +55 -36
.gitignore CHANGED
@@ -23,6 +23,7 @@ LLM_Analysis-*.txt
23
 
24
  # Pics / design assets
25
  00pic/
 
26
 
27
  # Spec workflow
28
  .spec-workflow/
 
23
 
24
  # Pics / design assets
25
  00pic/
26
+ docs/design/
27
 
28
  # Spec workflow
29
  .spec-workflow/
app_agent.py CHANGED
@@ -1683,6 +1683,8 @@ _CUSTOM_CSS = """
1683
  --c-scrollbar: #cbd5e1;
1684
  --c-scrollbar-hover: #94a3b8;
1685
 
 
 
1686
  --body-background-fill: var(--c-bg) !important;
1687
  --background-fill-primary: var(--c-surface) !important;
1688
  --background-fill-secondary: #eef2f7 !important;
@@ -2024,15 +2026,27 @@ div[role="log"][aria-label="chatbot conversation"],
2024
  }
2025
 
2026
  /* ── 通用訊息氣泡 ── */
2027
- /* 訊息改成無底色、只用左/右色條標記起訖後,不同時間產生的訊息之間如果
2028
- 間距太小,色條會看起來像連在一起、分不出是好幾則獨立訊息還是同一則。
2029
- 拉大訊息列間距,讓每則訊息之間有清楚的留白斷開 */
2030
- .main-chat .message-row { margin-bottom: 1.1rem !important; }
 
 
 
 
 
 
 
 
 
 
 
2031
  .main-chat .message-row .message {
2032
  border-radius: 10px !important;
2033
- font-size: 0.80rem !important;
2034
  line-height: 1.7 !important;
2035
- padding: .6rem .95rem !important;
 
2036
  max-width: 90% !important;
2037
  transition: box-shadow .2s !important;
2038
  }
@@ -2046,45 +2060,42 @@ div[role="log"][aria-label="chatbot conversation"],
2046
  max-width: 100% !important;
2047
  }
2048
 
2049
- /* 使用者訊息:不用底色跟橢圓外框,比照 AI 訊息只用一條色條標記起訖,
2050
- 改在右邊、用綠色跟 AI 的左邊藍色做區分 */
2051
  /* Gradio 實際輸出的內層元素同時帶 user/bot 與 message 兩個 class(非巢狀父子),
2052
  用複合選擇器只命中該元素本身,避免命中內層 panel-full-width 造成雙重框線 */
2053
  .main-chat .message-row.user .message,
2054
  .main-chat .message-row.user-row .user.message {
2055
  background: transparent !important;
2056
  border: none !important;
2057
- border-right: 3px solid var(--c-success) !important;
2058
  border-radius: 0 !important;
2059
  color: var(--c-text) !important;
2060
  margin-left: auto !important;
2061
  font-weight: 500 !important;
2062
  box-shadow: none !important;
2063
- padding: .4rem .8rem !important;
2064
- width: fit-content !important;
2065
  max-width: 80% !important;
 
2066
  }
2067
  .main-chat .message-row.user,
2068
  .main-chat .message-row.user-row { margin-bottom: 1.1rem !important; }
2069
 
2070
- /* AI 訊息(主對話視窗):不用卡片框線與底色,只靠左邊色條標記訊息起訖,
2071
- 視覺上更簡潔,融入對話視窗本身的背景 */
2072
  .main-chat .message-row.bot .message,
2073
  .main-chat .message-row.bot-row .bot.message {
2074
  background: transparent !important;
2075
  border: none !important;
2076
- border-left: 3px solid var(--c-accent-strong) !important;
2077
  border-radius: 0 !important;
2078
  color: var(--c-text) !important;
2079
- font-size: 0.80rem !important;
2080
  line-height: 1.7 !important;
2081
  box-shadow: none !important;
 
2082
  }
2083
  /* Gradio 產生的 <p> 段落本身有自己的 font-size(實測為固定 16px),
2084
  不會沿用上面設在外層容器的字級,且段落間距預設高達 16px,
2085
  視覺上偏大又有明顯空行,這裡直接命中 <p> 覆寫 */
2086
  .main-chat .message-row .message p {
2087
- font-size: 0.80rem !important;
2088
  line-height: 1.6 !important;
2089
  margin: 0 0 .5rem !important;
2090
  }
@@ -2095,8 +2106,11 @@ div[role="log"][aria-label="chatbot conversation"],
2095
  改成 block 並統一字級後,這段隱形空隙就會消失 */
2096
  .main-chat .message-row .message .md {
2097
  display: block !important;
2098
- font-size: 0.80rem !important;
2099
  line-height: 1.6 !important;
 
 
 
2100
  }
2101
  .main-chat .message-row.bot .message strong,
2102
  .main-chat .message-row.bot .message b,
@@ -2134,7 +2148,7 @@ div[role="log"][aria-label="chatbot conversation"],
2134
  }
2135
  #exec_log_accordion .label-wrap {
2136
  color: var(--c-text-muted) !important;
2137
- font-size: 0.78rem !important;
2138
  }
2139
 
2140
  /* 執行視窗(簡易訊息顯示欄,非對話氣泡) */
@@ -2150,7 +2164,7 @@ div[role="log"][aria-label="chatbot conversation"],
2150
  margin-top: 0 !important;
2151
  }
2152
  .exec-log p {
2153
- font-size: 0.80rem !important;
2154
  line-height: 1.5 !important;
2155
  margin: 0 0 .3rem !important;
2156
  padding: 0 !important;
@@ -2164,13 +2178,13 @@ div[role="log"][aria-label="chatbot conversation"],
2164
  background: none !important;
2165
  color: var(--c-accent-strong) !important;
2166
  padding: 0 .15rem 0 0 !important;
2167
- font-size: 0.80rem !important;
2168
  }
2169
  /* 跟對話視窗一樣:包住段落的 <span class="md"> 預設 inline 卻裝 block 的
2170
  <p>,會在最前面產生依自己字級算出的隱形空隙,這裡一併改成 block 並統一字級 */
2171
  .exec-log .md {
2172
  display: block !important;
2173
- font-size: 0.80rem !important;
2174
  }
2175
 
2176
  /* ── 5b. COLUMN LAYOUT ─────────────────────────────────────────── */
@@ -2230,15 +2244,15 @@ div[role="log"][aria-label="chatbot conversation"],
2230
  border-bottom: 1px solid rgba(var(--c-accent-rgb),.16) !important;
2231
  padding: 0 0 .9rem !important;
2232
  margin-bottom: .9rem !important;
2233
- font-size: 0.80rem !important;
2234
  }
2235
- .upload-hint p, .upload-hint li { font-size: 0.80rem !important; color: var(--c-text-soft) !important; }
2236
  .upload-hint strong { color: var(--c-accent-strong) !important; }
2237
  /* 同陷阱 13 第 3 點:<span class="md"> 預設 inline 卻裝 block 的 <p>,
2238
  會在最前面產生依自己字級算出的隱形空隙,這裡一併修正 */
2239
  .upload-hint .md {
2240
  display: block !important;
2241
- font-size: 0.80rem !important;
2242
  }
2243
  .upload-actions {
2244
  margin-top: 1rem !important;
@@ -2303,7 +2317,7 @@ button.primary, button[data-testid="submit"] {
2303
  border: none !important;
2304
  border-radius: 9px !important;
2305
  font-weight: 700 !important;
2306
- font-size: 0.84rem !important;
2307
  color: #fff !important;
2308
  box-shadow: 0 2px 12px rgba(2,132,199,.28) !important;
2309
  transition: transform .18s ease, box-shadow .18s ease, opacity .18s !important;
@@ -2319,7 +2333,7 @@ button.secondary {
2319
  color: var(--c-accent-strong) !important;
2320
  border-radius: 9px !important;
2321
  font-weight: 600 !important;
2322
- font-size: 0.84rem !important;
2323
  cursor: pointer !important;
2324
  transition: background .18s ease, border-color .18s ease !important;
2325
  }
@@ -2365,7 +2379,7 @@ table thead th {
2365
  table tbody td {
2366
  background: var(--c-surface) !important;
2367
  color: var(--c-text-soft) !important;
2368
- font-size: 0.80rem !important;
2369
  border-color: rgba(var(--c-accent-rgb),.06) !important;
2370
  font-family: 'JetBrains Mono', monospace !important;
2371
  padding: 7px 12px !important;
@@ -2512,7 +2526,7 @@ a:hover { color: var(--c-link-hover); }
2512
  }
2513
  .legend-item {
2514
  display: inline-flex; align-items: center; gap: .5rem;
2515
- font-size: 0.78rem;
2516
  color: var(--c-text-subtle);
2517
  }
2518
  .legend-item .dot {
@@ -2773,7 +2787,7 @@ def create_app():
2773
  with gr.Column(visible=False, elem_id="metadata_zone", elem_classes=["metadata-zone"]) as metadata_row:
2774
  gr.Markdown("### 📑 語料 Metadata 說明\n請於正式分析前說明語料來源、範圍與比較結構,將寫入研究報告。")
2775
 
2776
- gr.Markdown("**一、原文資訊**")
2777
  with gr.Row():
2778
  meta_title = gr.Textbox(label="原文標題", scale=2)
2779
  meta_author = gr.Textbox(label="作者", scale=2)
@@ -2784,7 +2798,7 @@ def create_app():
2784
 
2785
  # 比較結構(僅情境 3、4 需要;情境 1、2 固定二群)
2786
  with gr.Group(visible=False) as meta_compare_group:
2787
- gr.Markdown("**二、比較結構**")
2788
  with gr.Row():
2789
  meta_ngroups = gr.Radio(
2790
  ["二群", "三群"], label="群組數量", value="二群", scale=1,
@@ -2794,7 +2808,7 @@ def create_app():
2794
  value="群組間比較", scale=1,
2795
  )
2796
 
2797
- gr.Markdown("**三、群組層資訊**(共通屬性)")
2798
  meta_group_df = gr.Dataframe(
2799
  headers=["群組標籤", "翻譯類型", "翻譯方向", "譯者背景", "目標讀者"],
2800
  datatype=["str"] * 5,
@@ -2804,7 +2818,7 @@ def create_app():
2804
  label="群組層",
2805
  )
2806
 
2807
- gr.Markdown("**四、個體層資訊**(每個譯本/檔案)")
2808
  meta_indiv_df = gr.Dataframe(
2809
  headers=["檔案", "譯者/系統名稱", "出版年", "譯者母語", "後編輯程度", "重譯序位"],
2810
  datatype=["str"] * 6,
@@ -2814,6 +2828,10 @@ def create_app():
2814
  label="個體層",
2815
  )
2816
 
 
 
 
 
2817
  with gr.Row(elem_classes=["upload-actions"]):
2818
  meta_cancel_btn = gr.Button("略過", scale=1, variant="secondary")
2819
  meta_confirm_btn = gr.Button("確認並開始分析", scale=2, variant="primary")
@@ -3245,24 +3263,24 @@ def create_app():
3245
  ngroups, method, group_df, indiv_df):
3246
  sc = state.get("scenario", "")
3247
  L = ["## 語料 Metadata", "",
3248
- "### 一、原文資訊",
3249
  f"- 原文標題:{title or '(未填)'}",
3250
  f"- 作者:{author or '(未填)'}",
3251
  f"- 語言:{lang or '(未填)'}",
3252
  f"- 出版年:{pubyear or '(未填)'}",
3253
  f"- 版本:{version or '(未填)'}", ""]
3254
  if sc in ("3", "4"):
3255
- L += ["### 二、比較結構",
3256
  f"- 群組數量:{ngroups}",
3257
  f"- 比較方式:{method}", ""]
3258
  gh = ["群組標籤", "翻譯類型", "翻譯方向", "譯者背景", "目標讀者"]
3259
- L += ["### 三、群組層資訊"]
3260
  for r in _df_rows(group_df):
3261
  if not any(str(c).strip() for c in r):
3262
  continue
3263
  L.append("- " + ";".join(f"{h}:{str(v).strip()}" for h, v in zip(gh, r) if str(v).strip()))
3264
  ih = ["檔案", "譯者/系統名稱", "出版年", "譯者母語", "後編輯程度", "重譯序位"]
3265
- L += ["", "### 四、個體層資訊"]
3266
  for r in _df_rows(indiv_df):
3267
  if not any(str(c).strip() for c in r):
3268
  continue
@@ -3408,4 +3426,5 @@ if __name__ == "__main__":
3408
  inbrowser=False,
3409
  allowed_paths=[str(SESSIONS_DIR)],
3410
  head=_APP_HEAD,
 
3411
  )
 
1683
  --c-scrollbar: #cbd5e1;
1684
  --c-scrollbar-hover: #94a3b8;
1685
 
1686
+ --font-size-base: 0.875rem;
1687
+
1688
  --body-background-fill: var(--c-bg) !important;
1689
  --background-fill-primary: var(--c-surface) !important;
1690
  --background-fill-secondary: #eef2f7 !important;
 
2026
  }
2027
 
2028
  /* ── 通用訊息氣泡 ── */
2029
+ /* 不同時間產生的訊息之間如果間距太小,會分不出是好幾則獨立訊息還是同一則,
2030
+ 拉大訊息列間距讓每則訊息之間有清楚的留白斷開 */
2031
+ /* Gradio 預設給每個 message-row 左右各留 20px margin(原本應是保留給頭像用的
2032
+ 空間,本專案沒有用頭像),導致 AI 訊息離左邊、使用者訊息離右邊都比預期遠,
2033
+ 這裡歸零讓氣泡貼近對話視窗邊緣。另外 Gradio 還設了 max-width: calc(100% - 60px),
2034
+ AI 訊息貼左邊不受影響,但使用者訊息靠 margin-left:auto 貼右邊,這 60px
2035
+ 缺口會整個落在最右側,一併覆寫掉 */
2036
+ .main-chat .message-row {
2037
+ margin-bottom: 1.1rem !important;
2038
+ margin-left: 0 !important;
2039
+ margin-right: 0 !important;
2040
+ width: 100% !important;
2041
+ max-width: 100% !important;
2042
+ align-self: stretch !important;
2043
+ }
2044
  .main-chat .message-row .message {
2045
  border-radius: 10px !important;
2046
+ font-size: var(--font-size-base) !important;
2047
  line-height: 1.7 !important;
2048
+ padding: 0.75rem 1rem !important;
2049
+ width: fit-content !important;
2050
  max-width: 90% !important;
2051
  transition: box-shadow .2s !important;
2052
  }
 
2060
  max-width: 100% !important;
2061
  }
2062
 
2063
+ /* 使用者訊息:淺灰底色跟白色頁面背景錯開,不用外框線,靠右對齊與置左的
2064
+ AI 訊息做區分 */
2065
  /* Gradio 實際輸出的內層元素同時帶 user/bot 與 message 兩個 class(非巢狀父子),
2066
  用複合選擇器只命中該元素本身,避免命中內層 panel-full-width 造成雙重框線 */
2067
  .main-chat .message-row.user .message,
2068
  .main-chat .message-row.user-row .user.message {
2069
  background: transparent !important;
2070
  border: none !important;
 
2071
  border-radius: 0 !important;
2072
  color: var(--c-text) !important;
2073
  margin-left: auto !important;
2074
  font-weight: 500 !important;
2075
  box-shadow: none !important;
 
 
2076
  max-width: 80% !important;
2077
+ text-align: right !important;
2078
  }
2079
  .main-chat .message-row.user,
2080
  .main-chat .message-row.user-row { margin-bottom: 1.1rem !important; }
2081
 
2082
+ /* AI 訊息(主對話視窗):淺灰底色跟白色頁面背景錯開,不用外框線 */
 
2083
  .main-chat .message-row.bot .message,
2084
  .main-chat .message-row.bot-row .bot.message {
2085
  background: transparent !important;
2086
  border: none !important;
 
2087
  border-radius: 0 !important;
2088
  color: var(--c-text) !important;
2089
+ font-size: var(--font-size-base) !important;
2090
  line-height: 1.7 !important;
2091
  box-shadow: none !important;
2092
+ text-align: left !important;
2093
  }
2094
  /* Gradio 產生的 <p> 段落本身有自己的 font-size(實測為固定 16px),
2095
  不會沿用上面設在外層容器的字級,且段落間距預設高達 16px,
2096
  視覺上偏大又有明顯空行,這裡直接命中 <p> 覆寫 */
2097
  .main-chat .message-row .message p {
2098
+ font-size: var(--font-size-base) !important;
2099
  line-height: 1.6 !important;
2100
  margin: 0 0 .5rem !important;
2101
  }
 
2106
  改成 block 並統一字級後,這段隱形空隙就會消失 */
2107
  .main-chat .message-row .message .md {
2108
  display: block !important;
2109
+ font-size: var(--font-size-base) !important;
2110
  line-height: 1.6 !important;
2111
+ padding: 0.9rem 1.1rem !important;
2112
+ background: var(--c-surface2) !important;
2113
+ border-radius: 10px !important;
2114
  }
2115
  .main-chat .message-row.bot .message strong,
2116
  .main-chat .message-row.bot .message b,
 
2148
  }
2149
  #exec_log_accordion .label-wrap {
2150
  color: var(--c-text-muted) !important;
2151
+ font-size: var(--font-size-base) !important;
2152
  }
2153
 
2154
  /* 執行視窗(簡易訊息顯示欄,非對話氣泡) */
 
2164
  margin-top: 0 !important;
2165
  }
2166
  .exec-log p {
2167
+ font-size: var(--font-size-base) !important;
2168
  line-height: 1.5 !important;
2169
  margin: 0 0 .3rem !important;
2170
  padding: 0 !important;
 
2178
  background: none !important;
2179
  color: var(--c-accent-strong) !important;
2180
  padding: 0 .15rem 0 0 !important;
2181
+ font-size: var(--font-size-base) !important;
2182
  }
2183
  /* 跟對話視窗一樣:包住段落的 <span class="md"> 預設 inline 卻裝 block 的
2184
  <p>,會在最前面產生依自己字級算出的隱形空隙,這裡一併改成 block 並統一字級 */
2185
  .exec-log .md {
2186
  display: block !important;
2187
+ font-size: var(--font-size-base) !important;
2188
  }
2189
 
2190
  /* ── 5b. COLUMN LAYOUT ─────────────────────────────────────────── */
 
2244
  border-bottom: 1px solid rgba(var(--c-accent-rgb),.16) !important;
2245
  padding: 0 0 .9rem !important;
2246
  margin-bottom: .9rem !important;
2247
+ font-size: var(--font-size-base) !important;
2248
  }
2249
+ .upload-hint p, .upload-hint li { font-size: var(--font-size-base) !important; color: var(--c-text-soft) !important; }
2250
  .upload-hint strong { color: var(--c-accent-strong) !important; }
2251
  /* 同陷阱 13 第 3 點:<span class="md"> 預設 inline 卻裝 block 的 <p>,
2252
  會在最前面產生依自己字級算出的隱形空隙,這裡一併修正 */
2253
  .upload-hint .md {
2254
  display: block !important;
2255
+ font-size: var(--font-size-base) !important;
2256
  }
2257
  .upload-actions {
2258
  margin-top: 1rem !important;
 
2317
  border: none !important;
2318
  border-radius: 9px !important;
2319
  font-weight: 700 !important;
2320
+ font-size: var(--font-size-base) !important;
2321
  color: #fff !important;
2322
  box-shadow: 0 2px 12px rgba(2,132,199,.28) !important;
2323
  transition: transform .18s ease, box-shadow .18s ease, opacity .18s !important;
 
2333
  color: var(--c-accent-strong) !important;
2334
  border-radius: 9px !important;
2335
  font-weight: 600 !important;
2336
+ font-size: var(--font-size-base) !important;
2337
  cursor: pointer !important;
2338
  transition: background .18s ease, border-color .18s ease !important;
2339
  }
 
2379
  table tbody td {
2380
  background: var(--c-surface) !important;
2381
  color: var(--c-text-soft) !important;
2382
+ font-size: var(--font-size-base) !important;
2383
  border-color: rgba(var(--c-accent-rgb),.06) !important;
2384
  font-family: 'JetBrains Mono', monospace !important;
2385
  padding: 7px 12px !important;
 
2526
  }
2527
  .legend-item {
2528
  display: inline-flex; align-items: center; gap: .5rem;
2529
+ font-size: var(--font-size-base);
2530
  color: var(--c-text-subtle);
2531
  }
2532
  .legend-item .dot {
 
2787
  with gr.Column(visible=False, elem_id="metadata_zone", elem_classes=["metadata-zone"]) as metadata_row:
2788
  gr.Markdown("### 📑 語料 Metadata 說明\n請於正式分析前說明語料來源、範圍與比較結構,將寫入研究報告。")
2789
 
2790
+ gr.Markdown("**原文資訊**")
2791
  with gr.Row():
2792
  meta_title = gr.Textbox(label="原文標題", scale=2)
2793
  meta_author = gr.Textbox(label="作者", scale=2)
 
2798
 
2799
  # 比較結構(僅情境 3、4 需要;情境 1、2 固定二群)
2800
  with gr.Group(visible=False) as meta_compare_group:
2801
+ gr.Markdown("**比較結構**")
2802
  with gr.Row():
2803
  meta_ngroups = gr.Radio(
2804
  ["二群", "三群"], label="群組數量", value="二群", scale=1,
 
2808
  value="群組間比較", scale=1,
2809
  )
2810
 
2811
+ gr.Markdown("**群組層資訊**(共通屬性)")
2812
  meta_group_df = gr.Dataframe(
2813
  headers=["群組標籤", "翻譯類型", "翻譯方向", "譯者背景", "目標讀者"],
2814
  datatype=["str"] * 5,
 
2818
  label="群組層",
2819
  )
2820
 
2821
+ gr.Markdown("**個體層資訊**(每個譯本/檔案)")
2822
  meta_indiv_df = gr.Dataframe(
2823
  headers=["檔案", "譯者/系統名稱", "出版年", "譯者母語", "後編輯程度", "重譯序位"],
2824
  datatype=["str"] * 6,
 
2828
  label="個體層",
2829
  )
2830
 
2831
+ gr.Markdown(
2832
+ "以下資訊會寫入報告開頭,您也可以先略過(略過後這次分析將不會用到這些補充說明)",
2833
+ elem_classes=["upload-hint"],
2834
+ )
2835
  with gr.Row(elem_classes=["upload-actions"]):
2836
  meta_cancel_btn = gr.Button("略過", scale=1, variant="secondary")
2837
  meta_confirm_btn = gr.Button("確認並開始分析", scale=2, variant="primary")
 
3263
  ngroups, method, group_df, indiv_df):
3264
  sc = state.get("scenario", "")
3265
  L = ["## 語料 Metadata", "",
3266
+ "### 原文資訊",
3267
  f"- 原文標題:{title or '(未填)'}",
3268
  f"- 作者:{author or '(未填)'}",
3269
  f"- 語言:{lang or '(未填)'}",
3270
  f"- 出版年:{pubyear or '(未填)'}",
3271
  f"- 版本:{version or '(未填)'}", ""]
3272
  if sc in ("3", "4"):
3273
+ L += ["### 比較結構",
3274
  f"- 群組數量:{ngroups}",
3275
  f"- 比較方式:{method}", ""]
3276
  gh = ["群組標籤", "翻譯類型", "翻譯方向", "譯者背景", "目標讀者"]
3277
+ L += ["### 群組層資訊"]
3278
  for r in _df_rows(group_df):
3279
  if not any(str(c).strip() for c in r):
3280
  continue
3281
  L.append("- " + ";".join(f"{h}:{str(v).strip()}" for h, v in zip(gh, r) if str(v).strip()))
3282
  ih = ["檔案", "譯者/系統名稱", "出版年", "譯者母語", "後編輯程度", "重譯序位"]
3283
+ L += ["", "### 個體層資訊"]
3284
  for r in _df_rows(indiv_df):
3285
  if not any(str(c).strip() for c in r):
3286
  continue
 
3426
  inbrowser=False,
3427
  allowed_paths=[str(SESSIONS_DIR)],
3428
  head=_APP_HEAD,
3429
+ footer_links=["api", "settings"],
3430
  )