Spaces:
Sleeping
Phase 8-B:nav-col/chat-col/canvas-col 三欄式工作區
Browse filesworkspace_row 從原本 chat-col(scale6)+action-col(scale4) 兩欄改成
nav-col(scale2,固定窄寬)+chat-col(scale4)+canvas-col(scale7)三欄:
- nav-col:開始新分析/確認彈窗、scenario_btn_col(空,留給wiring相容);結果
導覽留給 Phase 8-D
- chat-col:chatbot+chat_upload(常駐輸入框),exec_log_accordion 搬出
- canvas-col:頂部步驟列(app_header_html,原本在全頁最上方、首頁也會看到,
現在只在 workspace_row 顯示後才出現)+canvas-scroll(新增的捲動子容器,
裝 upload_row/metadata_row/exec_log_accordion)
過程中排查並修好三個真實 CSS bug(皆已寫入程式碼註解,供之後參考):
1. canvas-col 內容一度水平位移一整欄寬度——根因是 Gradio 的 .column 預設
帶 flex-wrap:wrap,一旦父層設固定 height,排不下的子元件會往右開新欄
而非垂直捲動;修法:flex-wrap:nowrap。
2. 連帶補上 overflow-x:hidden + min-width:0(flex 子項防撐爆,見程式工作
守則13.1)。
3. 步驟列想用 position:sticky 貼在 Canvas 頂端,但 Gradio 自動包的 wrapper
高度只比步驟列本身高一點點,sticky 的可移動範圍被那層 wrapper 框住,
捲動超過那一點高度就整個跟著捲走。改用結構性分離:步驟列留在
canvas-col 本身(不捲動),另外包一層 canvas-scroll 才是真正捲動區。
已用 Playwright 完整驗證:三欄正確渲染、對話/Canvas 各自獨立捲動不互相
干擾、步驟列全程固定可見、上傳→格式驗證→Metadata 表單全流程正常、開始
新分析可正確返回首頁。
- app_agent.py +185 -104
|
@@ -2278,7 +2278,60 @@ div[role="log"][aria-label="chatbot conversation"],
|
|
| 2278 |
/* ── 5b. COLUMN LAYOUT ─────────────────────────────────────────── */
|
| 2279 |
/* gap: 0 讓對話視窗跟緊接著的技術執行紀錄 Accordion 完全貼合,
|
| 2280 |
視覺上合併成一張卡片(見「融合」設計討論) */
|
| 2281 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2282 |
|
| 2283 |
/* ── 5c. SCENARIO BUTTONS(取代原本打字輸入研究情境的輸入列)────────── */
|
| 2284 |
.scenario-buttons {
|
|
@@ -2701,11 +2754,22 @@ a:hover { color: var(--c-link-hover); }
|
|
| 2701 |
}
|
| 2702 |
|
| 2703 |
/* ── 17. RESPONSIVE ────────────────────────────────────────────── */
|
| 2704 |
-
/*
|
| 2705 |
-
|
|
|
|
|
|
|
| 2706 |
@media (max-width: 900px) {
|
| 2707 |
.main-layout-row { flex-direction: column !important; }
|
| 2708 |
-
.main-layout-row .
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2709 |
}
|
| 2710 |
@media (max-width: 768px) {
|
| 2711 |
.app-header h1 { font-size: 1.6rem !important; }
|
|
@@ -2948,9 +3012,6 @@ def create_app():
|
|
| 2948 |
# launch(head=...) 注入的 <script>(見 _APP_HEAD_SCRIPT)。
|
| 2949 |
) as demo:
|
| 2950 |
|
| 2951 |
-
# ── Header(含動態步驟列,取代原本右側「執行進度」面板)──────────
|
| 2952 |
-
app_header_html = gr.HTML(_build_header_html())
|
| 2953 |
-
|
| 2954 |
session_state = gr.State(None)
|
| 2955 |
|
| 2956 |
with gr.Tabs():
|
|
@@ -2979,23 +3040,39 @@ def create_app():
|
|
| 2979 |
sc_btn_4 = gr.Button("4 不同時期重譯本的歷時比較", variant="secondary",
|
| 2980 |
elem_id="sc_btn_4", elem_classes=["sc-bridge-btn"])
|
| 2981 |
|
| 2982 |
-
#
|
| 2983 |
-
#
|
| 2984 |
-
#
|
| 2985 |
-
# 只在長時間運算(指標計算/統計分析/報告撰寫)時自動展開
|
| 2986 |
with gr.Row(elem_classes=["main-layout-row"], visible=False) as workspace_row:
|
| 2987 |
-
with gr.Column(scale=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2988 |
chatbot = gr.Chatbot(
|
| 2989 |
show_label=False,
|
| 2990 |
height=400,
|
| 2991 |
elem_classes=["main-chat"],
|
| 2992 |
)
|
| 2993 |
-
with gr.Accordion("技術執行紀錄", open=False, elem_id="exec_log_accordion") as exec_log_accordion:
|
| 2994 |
-
exec_log = gr.Markdown(
|
| 2995 |
-
value="",
|
| 2996 |
-
elem_id="exec_log",
|
| 2997 |
-
elem_classes=["exec-log"],
|
| 2998 |
-
)
|
| 2999 |
# Phase 8(千卉實測後回頭調整):對話輸入框改回「常駐顯示、
|
| 3000 |
# 不負責上傳」——業主原本設計就是用拖曳/點擊上傳的卡片
|
| 3001 |
# (見下方 upload_row),對話框目前也沒有其他打字用途
|
|
@@ -3012,94 +3089,98 @@ def create_app():
|
|
| 3012 |
show_label=False,
|
| 3013 |
)
|
| 3014 |
|
| 3015 |
-
with gr.Column(scale=
|
| 3016 |
-
#
|
| 3017 |
-
#
|
| 3018 |
-
#
|
| 3019 |
-
#
|
| 3020 |
-
#
|
| 3021 |
-
#
|
| 3022 |
-
#
|
| 3023 |
-
|
| 3024 |
-
|
| 3025 |
-
|
| 3026 |
-
#
|
| 3027 |
-
|
| 3028 |
-
|
| 3029 |
-
|
| 3030 |
-
|
| 3031 |
-
|
| 3032 |
-
|
| 3033 |
-
|
| 3034 |
-
|
| 3035 |
-
)
|
| 3036 |
-
|
| 3037 |
-
|
| 3038 |
-
|
| 3039 |
-
|
| 3040 |
-
|
| 3041 |
-
|
| 3042 |
-
|
| 3043 |
-
|
| 3044 |
-
|
| 3045 |
-
|
| 3046 |
-
gr.
|
| 3047 |
-
|
| 3048 |
-
|
| 3049 |
-
|
| 3050 |
-
|
| 3051 |
-
|
| 3052 |
-
|
| 3053 |
-
|
| 3054 |
-
|
| 3055 |
-
|
| 3056 |
-
|
| 3057 |
-
|
| 3058 |
-
|
| 3059 |
-
|
| 3060 |
-
|
| 3061 |
-
|
| 3062 |
-
|
| 3063 |
-
|
| 3064 |
-
|
| 3065 |
-
|
| 3066 |
-
|
| 3067 |
-
|
| 3068 |
-
|
| 3069 |
-
|
| 3070 |
-
|
| 3071 |
-
|
| 3072 |
-
|
| 3073 |
-
|
| 3074 |
-
|
| 3075 |
-
|
| 3076 |
-
|
| 3077 |
-
|
| 3078 |
-
|
| 3079 |
-
datatype=["str"] * 5,
|
| 3080 |
-
col_count=(5, "fixed"),
|
| 3081 |
-
row_count=(1, "dynamic"),
|
| 3082 |
-
interactive=True,
|
| 3083 |
-
label="群組層",
|
| 3084 |
-
)
|
| 3085 |
|
| 3086 |
-
|
| 3087 |
-
|
| 3088 |
-
|
| 3089 |
-
|
| 3090 |
-
|
| 3091 |
-
|
| 3092 |
-
|
| 3093 |
-
|
| 3094 |
-
|
| 3095 |
|
| 3096 |
-
|
| 3097 |
-
|
| 3098 |
-
|
| 3099 |
-
|
| 3100 |
-
|
| 3101 |
-
|
| 3102 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3103 |
|
| 3104 |
# ══════════════════════════════════════════════════════════════
|
| 3105 |
# 結果分頁:初始隱藏,分析完成後顯示
|
|
|
|
| 2278 |
/* ── 5b. COLUMN LAYOUT ─────────────────────────────────────────── */
|
| 2279 |
/* gap: 0 讓對話視窗跟緊接著的技術執行紀錄 Accordion 完全貼合,
|
| 2280 |
視覺上合併成一張卡片(見「融合」設計討論) */
|
| 2281 |
+
/* Phase 8-B:三欄式工作區。nav-col 窄、固定寬度不隨視窗縮放;chat-col/
|
| 2282 |
+
canvas-col 各自獨立捲動(各自的內容不會因為對方變長而互相影響),高度
|
| 2283 |
+
統一扣掉頁面上方 Tab 列與外距抓一個概略值,之後看實際渲染再微調 */
|
| 2284 |
+
.nav-col {
|
| 2285 |
+
flex: 0 0 220px !important;
|
| 2286 |
+
max-width: 220px !important;
|
| 2287 |
+
display: flex !important;
|
| 2288 |
+
flex-direction: column !important;
|
| 2289 |
+
gap: .6rem !important;
|
| 2290 |
+
}
|
| 2291 |
+
/* 真因排查記錄:Gradio 的 .column 預設會帶 flex-wrap:wrap。平常沒設高度時
|
| 2292 |
+
不會有事,但這裡額外設了固定 height 讓內容用捲動取代撐高,一旦內容總高
|
| 2293 |
+
度超過這個 height,flex-wrap:wrap 會把「排不下」的子元件換到*右邊新開一
|
| 2294 |
+
欄*(水平並排),而不是照預期垂直捲動——這就是 canvas-col 一度出現內容
|
| 2295 |
+
跑到欄外、往右位移一整欄寬度的真正原因(不是 sticky、也不是 overflow-x
|
| 2296 |
+
單獨造成的,那兩個只是讓症狀被看見的地方)。修法:flex-wrap 明確關掉。 */
|
| 2297 |
+
.chat-col {
|
| 2298 |
+
display: flex !important;
|
| 2299 |
+
flex-direction: column !important;
|
| 2300 |
+
flex-wrap: nowrap !important;
|
| 2301 |
+
gap: 0 !important;
|
| 2302 |
+
height: calc(100vh - 150px) !important;
|
| 2303 |
+
overflow-x: hidden !important;
|
| 2304 |
+
overflow-y: auto !important;
|
| 2305 |
+
min-width: 0 !important;
|
| 2306 |
+
}
|
| 2307 |
+
/* canvas-col 本身「不」捲動——它只負責把步驟列(固定)跟 canvas-scroll
|
| 2308 |
+
(捲動)疊在一起、限制在同一個總高度內。踩坑記錄:原本想讓步驟列用
|
| 2309 |
+
position:sticky 貼在 canvas-col 頂端,但 sticky 元素只能在「自己最近的
|
| 2310 |
+
容器」範圍內移動,而 Gradio 自動包在 gr.HTML() 外面的 wrapper 高度只比
|
| 2311 |
+
步驟列本身高一點點,捲動超過那一點高度後 sticky 就跟著捲走、貼不住。
|
| 2312 |
+
改成結構性分離:步驟列跟 canvas-scroll 是兩個獨立區塊,前者天生不參與
|
| 2313 |
+
捲動,後者自己 overflow-y:auto,兩者高度互不相干,不用依賴 sticky。 */
|
| 2314 |
+
.canvas-col {
|
| 2315 |
+
display: flex !important;
|
| 2316 |
+
flex-direction: column !important;
|
| 2317 |
+
flex-wrap: nowrap !important;
|
| 2318 |
+
gap: 0 !important;
|
| 2319 |
+
height: calc(100vh - 150px) !important;
|
| 2320 |
+
overflow: hidden !important;
|
| 2321 |
+
min-width: 0 !important;
|
| 2322 |
+
}
|
| 2323 |
+
.canvas-col .app-header {
|
| 2324 |
+
flex-shrink: 0 !important;
|
| 2325 |
+
}
|
| 2326 |
+
.canvas-scroll {
|
| 2327 |
+
display: flex !important;
|
| 2328 |
+
flex-direction: column !important;
|
| 2329 |
+
flex-wrap: nowrap !important;
|
| 2330 |
+
flex: 1 1 auto !important;
|
| 2331 |
+
min-height: 0 !important;
|
| 2332 |
+
overflow-x: hidden !important;
|
| 2333 |
+
overflow-y: auto !important;
|
| 2334 |
+
}
|
| 2335 |
|
| 2336 |
/* ── 5c. SCENARIO BUTTONS(取代原本打字輸入研究情境的輸入列)────────── */
|
| 2337 |
.scenario-buttons {
|
|
|
|
| 2754 |
}
|
| 2755 |
|
| 2756 |
/* ── 17. RESPONSIVE ────────────────────────────────────────────── */
|
| 2757 |
+
/* 三欄式工作區(nav-col/chat-col/canvas-col)僅限桌面寬度;手機或窄視窗
|
| 2758 |
+
(≤900px)改回上下堆疊,且各欄不再各自獨立捲動(避免小螢幕出現多層
|
| 2759 |
+
巢狀捲軸,體驗更差),改用頁面本身捲動。桌面版收合成 icon bar、手機版
|
| 2760 |
+
漢堡抽屜留給 Phase 8-E 一起做 */
|
| 2761 |
@media (max-width: 900px) {
|
| 2762 |
.main-layout-row { flex-direction: column !important; }
|
| 2763 |
+
.main-layout-row .nav-col,
|
| 2764 |
+
.main-layout-row .chat-col,
|
| 2765 |
+
.main-layout-row .canvas-col {
|
| 2766 |
+
width: 100% !important;
|
| 2767 |
+
max-width: 100% !important;
|
| 2768 |
+
flex: 1 1 auto !important;
|
| 2769 |
+
height: auto !important;
|
| 2770 |
+
overflow-y: visible !important;
|
| 2771 |
+
}
|
| 2772 |
+
.canvas-scroll { overflow-y: visible !important; min-height: 0 !important; }
|
| 2773 |
}
|
| 2774 |
@media (max-width: 768px) {
|
| 2775 |
.app-header h1 { font-size: 1.6rem !important; }
|
|
|
|
| 3012 |
# launch(head=...) 注入的 <script>(見 _APP_HEAD_SCRIPT)。
|
| 3013 |
) as demo:
|
| 3014 |
|
|
|
|
|
|
|
|
|
|
| 3015 |
session_state = gr.State(None)
|
| 3016 |
|
| 3017 |
with gr.Tabs():
|
|
|
|
| 3040 |
sc_btn_4 = gr.Button("4 不同時期重譯本的歷時比較", variant="secondary",
|
| 3041 |
elem_id="sc_btn_4", elem_classes=["sc-bridge-btn"])
|
| 3042 |
|
| 3043 |
+
# Phase 8-B:三欄式工作區——nav-col(窄,導覽/重置)+chat-col
|
| 3044 |
+
# (對話視窗+輸入框,獨立捲動)+canvas-col(步驟列+上傳/表單/
|
| 3045 |
+
# 執行紀錄,獨立捲動)。手機寬度見 CSS 的 media query 改上下堆疊。
|
|
|
|
| 3046 |
with gr.Row(elem_classes=["main-layout-row"], visible=False) as workspace_row:
|
| 3047 |
+
with gr.Column(scale=2, min_width=200, elem_classes=["nav-col"]) as nav_col:
|
| 3048 |
+
# 研究情境是封閉式的固定四選一,改用可點擊按鈕取代打字輸入
|
| 3049 |
+
# 數字再解析,選定後即隱藏,對話視窗回歸純輸出角色
|
| 3050 |
+
# Phase 8:情境選擇按鈕已搬到 homepage_col(見上方,理由是
|
| 3051 |
+
# Gradio 對建構時 visible=False 的容器不掛載進 DOM,見該處
|
| 3052 |
+
# 註解)。這個 Column 目前是空的,只留來讓 on_scenario_pick/
|
| 3053 |
+
# on_reset_confirm 既有的可見性輸出繼續運作,不動更多 wiring;
|
| 3054 |
+
# Phase 8-F 全站清理時一併拿掉。
|
| 3055 |
+
with gr.Column(elem_classes=["scenario-buttons"]) as scenario_btn_col:
|
| 3056 |
+
pass
|
| 3057 |
+
|
| 3058 |
+
# 「開始新分析」與其確認彈窗:換情境/重新開始都走這一套共用
|
| 3059 |
+
# 機制。情境是封閉式按鈕選擇,不透過對話框打字,所以「換
|
| 3060 |
+
# 情境」也統一走這顆按鈕,不恢復文字解析路徑。
|
| 3061 |
+
reset_btn = gr.Button("🆕 開始新分析", elem_classes=["reset-btn"], variant="secondary")
|
| 3062 |
+
with gr.Column(visible=False, elem_classes=["reset-confirm"]) as reset_confirm_row:
|
| 3063 |
+
gr.Markdown("確定要開始新分析嗎?目前進度將會遺失。")
|
| 3064 |
+
with gr.Row(elem_classes=["upload-actions"]):
|
| 3065 |
+
reset_cancel_btn = gr.Button("取消", scale=1, variant="secondary")
|
| 3066 |
+
reset_confirm_btn = gr.Button("確定重新開始", scale=2, variant="primary")
|
| 3067 |
+
# 結果導覽(🏠對話/📊語言指標分析/📈統計比較分析/📝LLM
|
| 3068 |
+
# 輔助分析)與下載區留給 Phase 8-D 一起做,這裡先不放。
|
| 3069 |
+
|
| 3070 |
+
with gr.Column(scale=4, min_width=300, elem_classes=["chat-col"]):
|
| 3071 |
chatbot = gr.Chatbot(
|
| 3072 |
show_label=False,
|
| 3073 |
height=400,
|
| 3074 |
elem_classes=["main-chat"],
|
| 3075 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3076 |
# Phase 8(千卉實測後回頭調整):對話輸入框改回「常駐顯示、
|
| 3077 |
# 不負責上傳」——業主原本設計就是用拖曳/點擊上傳的卡片
|
| 3078 |
# (見下方 upload_row),對話框目前也沒有其他打字用途
|
|
|
|
| 3089 |
show_label=False,
|
| 3090 |
)
|
| 3091 |
|
| 3092 |
+
with gr.Column(scale=7, min_width=380, elem_classes=["canvas-col"]):
|
| 3093 |
+
# 頂部步驟列+情境標籤:原本在全頁最上方(首頁也會看到),
|
| 3094 |
+
# Phase 8-B 搬進 Canvas 內部,只在 workspace_row 顯示後
|
| 3095 |
+
# 才出現,首頁不再看到。
|
| 3096 |
+
# 注意:這裡不能靠 app_header_html 自己 position:sticky
|
| 3097 |
+
# 達到「頂部固定」——它的直接 wrapper(Gradio 自動包的
|
| 3098 |
+
# .block)高度只比它自己的內容高一點點,sticky 的可移動
|
| 3099 |
+
# 範圍被��層 wrapper 框住,捲動一小段(wrapper 多出來的
|
| 3100 |
+
# 高度)後就整個跟著捲走,不會真的貼在 Canvas 頂端不動
|
| 3101 |
+
# (已實測踩坑)。正確做法:步驟列留在捲動區外面(跟著
|
| 3102 |
+
# canvas-col 本身固定不動),下面另外包一層
|
| 3103 |
+
# canvas-scroll 才是真正會捲動的區域,兩者高度各自獨立。
|
| 3104 |
+
app_header_html = gr.HTML(_build_header_html())
|
| 3105 |
+
|
| 3106 |
+
with gr.Column(elem_classes=["canvas-scroll"]):
|
| 3107 |
+
# Phase 8(千卉實測後回頭調整):這張卡片改回正式使用
|
| 3108 |
+
# 中的上傳入口(拖曳/點擊上傳),符合業主原本設計;
|
| 3109 |
+
# Phase 7 曾經把它永久隱藏、改用對話框上傳,這次反
|
| 3110 |
+
# 過來。程式邏輯完全沒動,只是把可見性開關從
|
| 3111 |
+
# chat_upload_col 換回這裡。
|
| 3112 |
+
with gr.Column(visible=False, elem_id="upload_zone", elem_classes=["upload-zone"]) as upload_row:
|
| 3113 |
+
upload_hint_md = gr.Markdown(value="", elem_classes=["upload-hint"])
|
| 3114 |
+
file_input = gr.File(
|
| 3115 |
+
show_label=False,
|
| 3116 |
+
file_types=[".txt"], file_count="multiple",
|
| 3117 |
+
)
|
| 3118 |
+
with gr.Row(elem_classes=["upload-actions"]):
|
| 3119 |
+
cancel_btn = gr.Button("取消", scale=1, variant="secondary")
|
| 3120 |
+
upload_btn = gr.Button("確認上傳", scale=2, variant="primary")
|
| 3121 |
+
|
| 3122 |
+
# ── 語料 Metadata 說明表單(上傳後、分析前)───────
|
| 3123 |
+
with gr.Column(visible=False, elem_id="metadata_zone", elem_classes=["metadata-zone"]) as metadata_row:
|
| 3124 |
+
gr.Markdown("### 📑 語料 Metadata 說明\n請於正式分析前說明語料來源、範圍與比較結構,將寫入研究報告。")
|
| 3125 |
+
|
| 3126 |
+
gr.Markdown("**原文資訊**")
|
| 3127 |
+
with gr.Row():
|
| 3128 |
+
meta_title = gr.Textbox(label="原文標題", scale=2)
|
| 3129 |
+
meta_author = gr.Textbox(label="作者", scale=2)
|
| 3130 |
+
meta_lang = gr.Textbox(label="語言", scale=1)
|
| 3131 |
+
with gr.Row():
|
| 3132 |
+
meta_pubyear = gr.Textbox(label="出版年", scale=1)
|
| 3133 |
+
meta_version = gr.Textbox(label="版本", scale=2)
|
| 3134 |
+
|
| 3135 |
+
# 比較結構(僅情境 3、4 需要;情境 1、2 固定二群)
|
| 3136 |
+
with gr.Group(visible=False) as meta_compare_group:
|
| 3137 |
+
gr.Markdown("**比較結構**")
|
| 3138 |
+
with gr.Row():
|
| 3139 |
+
meta_ngroups = gr.Radio(
|
| 3140 |
+
["二群", "三群"], label="群組數量", value="二群", scale=1,
|
| 3141 |
+
)
|
| 3142 |
+
meta_method = gr.Radio(
|
| 3143 |
+
["群組間比較", "個體間比較"], label="比較方式",
|
| 3144 |
+
value="群組間比較", scale=1,
|
| 3145 |
+
)
|
| 3146 |
+
|
| 3147 |
+
gr.Markdown("**群組層資訊**(共通屬性)")
|
| 3148 |
+
meta_group_df = gr.Dataframe(
|
| 3149 |
+
headers=["群組標籤", "翻譯類型", "翻譯方向", "譯者背景", "目標讀者"],
|
| 3150 |
+
datatype=["str"] * 5,
|
| 3151 |
+
col_count=(5, "fixed"),
|
| 3152 |
+
row_count=(1, "dynamic"),
|
| 3153 |
+
interactive=True,
|
| 3154 |
+
label="群組層",
|
| 3155 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3156 |
|
| 3157 |
+
gr.Markdown("**個體層資訊**(每個譯本/檔案)")
|
| 3158 |
+
meta_indiv_df = gr.Dataframe(
|
| 3159 |
+
headers=["檔案", "譯者/系統名稱", "出版年", "譯者母語", "後編輯程度", "重譯序位"],
|
| 3160 |
+
datatype=["str"] * 6,
|
| 3161 |
+
col_count=(6, "fixed"),
|
| 3162 |
+
row_count=(1, "dynamic"),
|
| 3163 |
+
interactive=True,
|
| 3164 |
+
label="個體層",
|
| 3165 |
+
)
|
| 3166 |
|
| 3167 |
+
gr.Markdown(
|
| 3168 |
+
"以下資訊會寫入報告開頭,您也可以先略過(略過後這次分析將不會用到這些補充說明)",
|
| 3169 |
+
elem_classes=["upload-hint"],
|
| 3170 |
+
)
|
| 3171 |
+
with gr.Row(elem_classes=["upload-actions"]):
|
| 3172 |
+
meta_cancel_btn = gr.Button("略過", scale=1, variant="secondary")
|
| 3173 |
+
meta_confirm_btn = gr.Button("確認並開始分析", scale=2, variant="primary")
|
| 3174 |
+
|
| 3175 |
+
# 技術執行紀錄:Phase 8-B 從 chat-col 搬到 Canvas 底
|
| 3176 |
+
# 部,跟中段內容同一塊可捲動捲軸,非 sticky,點擊
|
| 3177 |
+
# 往下展開。
|
| 3178 |
+
with gr.Accordion("技術執行紀錄", open=False, elem_id="exec_log_accordion") as exec_log_accordion:
|
| 3179 |
+
exec_log = gr.Markdown(
|
| 3180 |
+
value="",
|
| 3181 |
+
elem_id="exec_log",
|
| 3182 |
+
elem_classes=["exec-log"],
|
| 3183 |
+
)
|
| 3184 |
|
| 3185 |
# ══════════════════════════════════════════════════════════════
|
| 3186 |
# 結果分頁:初始隱藏,分析完成後顯示
|