Spaces:
Sleeping
Sleeping
File size: 4,155 Bytes
e66cfb4 2e32dbb e66cfb4 3160eea e66cfb4 8defc51 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | """
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)
|