Spaces:
Sleeping
Sleeping
| """ | |
| app.py | |
| ------ | |
| Dash app 進入點。 | |
| 職責(僅此而已): | |
| 1. 建立 Dash app 實例 | |
| 2. 組裝 AppShell layout | |
| 3. 掛載全域 callback(navbar、主題、MathJax) | |
| 4. 啟動 dev server | |
| Build pipeline(MD → JSON)已移至 build.py。 | |
| - 若希望每次啟動都重建:取消下方 build_if_needed 的 force=True 呼叫 | |
| - 若只想手動重建:直接執行 python build.py --force | |
| """ | |
| import dash | |
| from dash import Dash, html, dcc | |
| from dash import Input, Output, clientside_callback | |
| import dash_mantine_components as dmc | |
| from pathlib import Path | |
| from layout.shell.navbar import generate_navbar | |
| from layout.shell.header import generate_header | |
| from layout.shell.main import generate_main | |
| from layout.shell.footer import generate_footer | |
| from layout.shell.callbacks import generate_toggle_navbar, switch_light_dark | |
| # 確保頁面 section 切換 callback 在啟動時就被註冊 | |
| import layout.page.page_callbacks # noqa: F401 | |
| # 學生登入元件 | |
| from student_login import create_student_store, create_login_modal, create_student_badge | |
| # Firebase quiz hooks(答題記錄) | |
| from firebase_quiz_hooks import create_dummy_stores | |
| import firebase_quiz_hooks # noqa: F401 ← 掛載 callback | |
| # Session 狀態保存(重整後恢復 section、答題、解法)由 assets/session_state.js 處理 | |
| from session_state import create_session_stores | |
| # Build pipeline:只在啟動時呼叫,不影響 app 本體邏輯 | |
| from build import build_if_needed | |
| BASE_DIR = Path(__file__).resolve().parent | |
| CONFIG_PATH = BASE_DIR / "paths.json" | |
| import os | |
| if not os.getenv("SPACE_ID"): # 不在 HF 上才 build | |
| build_if_needed(CONFIG_PATH, force=False) | |
| # --------------------------------------------------------------------------- | |
| # App 實例 | |
| # --------------------------------------------------------------------------- | |
| app = Dash( | |
| __name__, | |
| use_pages=True, | |
| external_stylesheets=dmc.styles.ALL, | |
| suppress_callback_exceptions=True, | |
| ) | |
| # --------------------------------------------------------------------------- | |
| # Callbacks | |
| # --------------------------------------------------------------------------- | |
| generate_toggle_navbar() | |
| switch_light_dark() | |
| # --------------------------------------------------------------------------- | |
| # Layout 元件 | |
| # --------------------------------------------------------------------------- | |
| header = generate_header() | |
| navbar = generate_navbar() | |
| main = generate_main() | |
| footer = generate_footer() | |
| layout = dmc.AppShell( | |
| id="appshell", | |
| children=[ | |
| dcc.Store(id="noop-store"), | |
| html.Div(id="theme-dummy", style={"display": "none"}), | |
| # ── 學生登入相關 ── | |
| create_student_store(), # localStorage 學號 Store | |
| create_login_modal(), # 學號輸入 Modal | |
| *create_dummy_stores(), # Firebase 虛擬 Store | |
| *create_session_stores(), # Session 狀態 Store | |
| header, navbar, main, footer, | |
| ], | |
| header={"height": "4rem"}, | |
| navbar={ | |
| "width": {"xs": 200, "sm": 250, "md": 300, "lg": 300}, | |
| "breakpoint": "sm", | |
| "collapsed": {"mobile": True, "desktop": False}, | |
| }, | |
| footer={"height": "2rem"}, | |
| padding="lg", | |
| ) | |
| app.layout = dmc.MantineProvider( | |
| id="mantine-provider", | |
| children=layout, | |
| defaultColorScheme="light", | |
| ) | |
| # --------------------------------------------------------------------------- | |
| # MathJax:每次換頁都重新 typeset | |
| # --------------------------------------------------------------------------- | |
| clientside_callback( | |
| """ | |
| function(pathname) { | |
| if (window.MathJax) { | |
| window.MathJax.typeset(); | |
| } | |
| return ""; | |
| } | |
| """, | |
| Output("noop-store", "data"), | |
| Input("_pages_location", "pathname"), | |
| ) | |
| # --------------------------------------------------------------------------- | |
| # 啟動 | |
| # --------------------------------------------------------------------------- | |
| server = app.server # 給 gunicorn 用:gunicorn app:server | |
| if __name__ == "__main__": | |
| # app.run(debug=True) | |
| # 部署時改用: | |
| app.run(host="0.0.0.0", port=7860, debug=False) | |