Skip to content

Commit 4395a3b

Browse files
committed
Theme-aware topbar, dropdown, and inline Load All abort button
- Replace hardcoded topbar background with --bg-topbar CSS variable - Remove dbc.Navbar color="dark" that overrode CSS with inline style - Make auth dropdown use var(--bg-panel) instead of hardcoded #0a0f20 - Theme-aware input backgrounds and select options in dropdown - Move Load All abort button inline next to status message - Auto-dismiss "All pages loaded" after 10s instead of 5s - Clear fetch status bar on Execute button click Co-authored-by: Isaac
1 parent cdf2a64 commit 4395a3b

3 files changed

Lines changed: 46 additions & 48 deletions

File tree

app.py

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,55 +1053,55 @@ def build_sidebar() -> html.Div:
10531053
_THEMES = {
10541054
"midnight": {"label": "Midnight", "dark": True, "icon": "bi-moon-stars-fill",
10551055
"bg-void": "#050810", "bg-dark": "#080c18", "bg-panel": "#0d1225",
1056-
"bg-surface": "#141a2e",
1056+
"bg-surface": "#141a2e", "bg-topbar": "rgba(8,12,24,0.95)",
10571057
"bg-card": "rgba(255,255,255,0.028)", "bg-hover": "rgba(255,255,255,0.055)",
10581058
"bg-active": "rgba(0,212,255,0.09)", "border": "rgba(255,255,255,0.07)",
10591059
"border-hi": "rgba(0,212,255,0.45)", "text-hi": "#f8fafc",
10601060
"text-1": "#e2e8f0", "text-2": "#94a3b8", "text-3": "#4b5563",
10611061
"accent": "#00d4ff", "card-bg": "#1a1d23"},
10621062
"obsidian": {"label": "Obsidian", "dark": True, "icon": "bi-gem",
10631063
"bg-void": "#0a0a0a", "bg-dark": "#111111", "bg-panel": "#181818",
1064-
"bg-surface": "#222222",
1064+
"bg-surface": "#222222", "bg-topbar": "rgba(10,10,10,0.95)",
10651065
"bg-card": "rgba(255,255,255,0.035)", "bg-hover": "rgba(255,255,255,0.06)",
10661066
"bg-active": "rgba(168,85,247,0.1)", "border": "rgba(255,255,255,0.08)",
10671067
"border-hi": "rgba(168,85,247,0.5)", "text-hi": "#fafafa",
10681068
"text-1": "#e0e0e0", "text-2": "#9e9e9e", "text-3": "#555555",
10691069
"accent": "#a855f7", "card-bg": "#1e1e1e"},
10701070
"deep-ocean": {"label": "Deep Ocean", "dark": True, "icon": "bi-water",
10711071
"bg-void": "#020617", "bg-dark": "#0f172a", "bg-panel": "#1e293b",
1072-
"bg-surface": "#263448",
1072+
"bg-surface": "#263448", "bg-topbar": "rgba(2,6,23,0.95)",
10731073
"bg-card": "rgba(255,255,255,0.03)", "bg-hover": "rgba(255,255,255,0.05)",
10741074
"bg-active": "rgba(56,189,248,0.1)", "border": "rgba(255,255,255,0.06)",
10751075
"border-hi": "rgba(56,189,248,0.5)", "text-hi": "#f8fafc",
10761076
"text-1": "#cbd5e1", "text-2": "#64748b", "text-3": "#475569",
10771077
"accent": "#38bdf8", "card-bg": "#1e293b"},
10781078
"aurora": {"label": "Aurora", "dark": True, "icon": "bi-stars",
10791079
"bg-void": "#030712", "bg-dark": "#0c1427", "bg-panel": "#162032",
1080-
"bg-surface": "#1e2a3e",
1080+
"bg-surface": "#1e2a3e", "bg-topbar": "rgba(3,7,18,0.95)",
10811081
"bg-card": "rgba(255,255,255,0.03)", "bg-hover": "rgba(255,255,255,0.055)",
10821082
"bg-active": "rgba(16,185,129,0.1)", "border": "rgba(255,255,255,0.07)",
10831083
"border-hi": "rgba(16,185,129,0.5)", "text-hi": "#ecfdf5",
10841084
"text-1": "#d1fae5", "text-2": "#6ee7b7", "text-3": "#34d399",
10851085
"accent": "#10b981", "card-bg": "#1a2332"},
10861086
"snowlight": {"label": "Snowlight", "dark": False, "icon": "bi-sun-fill",
10871087
"bg-void": "#f8fafc", "bg-dark": "#f1f5f9", "bg-panel": "#ffffff",
1088-
"bg-surface": "#eef2f7",
1088+
"bg-surface": "#eef2f7", "bg-topbar": "rgba(255,255,255,0.92)",
10891089
"bg-card": "rgba(0,0,0,0.03)", "bg-hover": "rgba(0,0,0,0.05)",
10901090
"bg-active": "rgba(99,102,241,0.08)", "border": "rgba(0,0,0,0.1)",
10911091
"border-hi": "rgba(99,102,241,0.5)", "text-hi": "#0f172a",
10921092
"text-1": "#1e293b", "text-2": "#64748b", "text-3": "#94a3b8",
10931093
"accent": "#6366f1", "card-bg": "#ffffff"},
10941094
"paper": {"label": "Paper", "dark": False, "icon": "bi-file-earmark-text",
10951095
"bg-void": "#fafaf9", "bg-dark": "#f5f5f4", "bg-panel": "#ffffff",
1096-
"bg-surface": "#edeceb",
1096+
"bg-surface": "#edeceb", "bg-topbar": "rgba(255,255,255,0.92)",
10971097
"bg-card": "rgba(0,0,0,0.025)", "bg-hover": "rgba(0,0,0,0.04)",
10981098
"bg-active": "rgba(234,88,12,0.08)", "border": "rgba(0,0,0,0.08)",
10991099
"border-hi": "rgba(234,88,12,0.45)", "text-hi": "#1c1917",
11001100
"text-1": "#292524", "text-2": "#78716c", "text-3": "#a8a29e",
11011101
"accent": "#ea580c", "card-bg": "#ffffff"},
11021102
"cloud": {"label": "Cloud", "dark": False, "icon": "bi-cloud-sun-fill",
11031103
"bg-void": "#f0f9ff", "bg-dark": "#e0f2fe", "bg-panel": "#ffffff",
1104-
"bg-surface": "#daeaf8",
1104+
"bg-surface": "#daeaf8", "bg-topbar": "rgba(255,255,255,0.92)",
11051105
"bg-card": "rgba(0,0,0,0.02)", "bg-hover": "rgba(0,0,0,0.04)",
11061106
"bg-active": "rgba(14,165,233,0.08)", "border": "rgba(0,0,0,0.08)",
11071107
"border-hi": "rgba(14,165,233,0.45)", "text-hi": "#0c4a6e",
@@ -1405,7 +1405,7 @@ def _theme_card(tid: str, t: dict) -> html.Div:
14051405
),
14061406
], className="d-flex align-items-center"),
14071407
], fluid=True),
1408-
color="dark", dark=True, className="topbar",
1408+
dark=True, className="topbar",
14091409
)
14101410

14111411

@@ -1621,12 +1621,8 @@ def _custom_section() -> html.Div:
16211621
style={"display": "none"},
16221622
),
16231623
html.Button(
1624-
[html.I(className="bi bi-x-lg me-1"), "Abort"],
1625-
id="load-all-abort-btn",
1626-
n_clicks=0,
1624+
"Abort", id="load-all-abort-btn", n_clicks=0,
16271625
className="load-all-abort-btn",
1628-
title="Cancel loading",
1629-
style={"display": "none"},
16301626
),
16311627
], className="response-panel"),
16321628
], className="main-content"),
@@ -1814,6 +1810,7 @@ def update_settings(tz, lang, theme_clicks, current):
18141810
root.style.setProperty('--bg-dark', theme['bg-dark']);
18151811
root.style.setProperty('--bg-panel', theme['bg-panel']);
18161812
root.style.setProperty('--bg-surface', theme['bg-surface']);
1813+
root.style.setProperty('--bg-topbar', theme['bg-topbar']);
18171814
root.style.setProperty('--bg-card', theme['bg-card']);
18181815
root.style.setProperty('--bg-hover', theme['bg-hover']);
18191816
root.style.setProperty('--bg-active', theme['bg-active']);
@@ -2370,6 +2367,7 @@ def restore_cached_response(endpoint, cache):
23702367
Output("chips-store", "data"),
23712368
Output("response-cache", "data", allow_duplicate=True),
23722369
Output("sp-load-all-btn", "style"),
2370+
Output("fetch-status-bar", "children", allow_duplicate=True),
23732371
Input("execute-btn", "n_clicks"),
23742372
State("selected-endpoint", "data"),
23752373
State({"type": "param-input", "name": ALL}, "value"),
@@ -2383,7 +2381,7 @@ def restore_cached_response(endpoint, cache):
23832381
def execute_api_call(n_clicks, endpoint, param_values, param_ids, body_text, timeout_val, conn_config, cache):
23842382
"""Callback 10: Execute the selected API call and render the response."""
23852383
if not n_clicks or not endpoint:
2386-
return no_update, no_update, no_update, no_update, no_update, no_update
2384+
return no_update, no_update, no_update, no_update, no_update, no_update, no_update
23872385

23882386
params: Dict[str, Any] = {}
23892387
ep_param_map = {p["name"]: p for p in endpoint.get("params", [])}
@@ -2403,7 +2401,7 @@ def execute_api_call(n_clicks, endpoint, param_values, param_ids, body_text, tim
24032401
for pp in endpoint.get("path_params", []):
24042402
val = params.pop(pp, "")
24052403
if not val:
2406-
return build_error_panel(f"Path parameter '{pp}' is required."), no_update, time.time(), None, no_update, {"display": "none"}
2404+
return build_error_panel(f"Path parameter '{pp}' is required."), no_update, time.time(), None, no_update, {"display": "none"}, ""
24072405
path = path.replace(f"{{{pp}}}", str(val))
24082406

24092407
method = endpoint.get("method", "GET")
@@ -2412,11 +2410,11 @@ def execute_api_call(n_clicks, endpoint, param_values, param_ids, body_text, tim
24122410
try:
24132411
body = json.loads(body_text)
24142412
except json.JSONDecodeError as e:
2415-
return build_error_panel(f"Invalid JSON body: {e}"), no_update, time.time(), None, no_update, {"display": "none"}
2413+
return build_error_panel(f"Invalid JSON body: {e}"), no_update, time.time(), None, no_update, {"display": "none"}, ""
24162414

24172415
ws_host, ws_token = _resolve_conn(conn_config)
24182416
if not ws_host:
2419-
return build_error_panel("No workspace host. Configure a connection in the user menu."), no_update, time.time(), None, no_update, {"display": "none"}
2417+
return build_error_panel("No workspace host. Configure a connection in the user menu."), no_update, time.time(), None, no_update, {"display": "none"}, ""
24202418

24212419
# Account-scope endpoints need a token issued for the accounts console
24222420
is_account = endpoint.get("scope") == "account"
@@ -2427,7 +2425,7 @@ def execute_api_call(n_clicks, endpoint, param_values, param_ids, body_text, tim
24272425

24282426
if not token:
24292427
msg = "No auth token for the accounts console. Ensure your CLI profile has an account_id configured." if is_account else "No auth token. Configure a connection in the user menu."
2430-
return build_error_panel(msg), no_update, time.time(), None, no_update, {"display": "none"}
2428+
return build_error_panel(msg), no_update, time.time(), None, no_update, {"display": "none"}, ""
24312429

24322430
try:
24332431
ep_timeout = max(1, int(timeout_val or endpoint.get("timeout", 30)))
@@ -2464,7 +2462,7 @@ def execute_api_call(n_clicks, endpoint, param_values, param_ids, body_text, tim
24642462
new_cache = dict(cache or {})
24652463
new_cache[ep_id] = {"result": result, "chips": chips or None}
24662464
btn_style = {"display": "inline-flex"} if has_more else {"display": "none"}
2467-
return build_response_panel(result, chips), last_req, time.time(), chips or None, new_cache, btn_style
2465+
return build_response_panel(result, chips), last_req, time.time(), chips or None, new_cache, btn_style, ""
24682466

24692467

24702468
# 11. Signal tick_fetch to start a new pagination run whenever execute fires.
@@ -2787,15 +2785,14 @@ def _load_all_worker(last_req, host, token, list_key, initial_data):
27872785
Output("load-all-ticker", "disabled"),
27882786
Output("fetch-status-bar", "children", allow_duplicate=True),
27892787
Output("sp-load-all-btn", "style", allow_duplicate=True),
2790-
Output("load-all-abort-btn", "style"),
27912788
Input("sp-load-all-btn", "n_clicks"),
27922789
State("last-request", "data"),
27932790
State("conn-config", "data"),
27942791
prevent_initial_call=True,
27952792
)
27962793
def start_load_all(n_clicks, last_req, conn_config):
27972794
"""Callback 11h-start: Launch the background Load All thread and enable the ticker."""
2798-
NO = (True, no_update, no_update, no_update)
2795+
NO = (True, no_update, no_update)
27992796
if not n_clicks or not last_req:
28002797
return NO
28012798
initial_data = last_req.get("initial_data", {})
@@ -2830,9 +2827,12 @@ def start_load_all(n_clicks, last_req, conn_config):
28302827
status = html.Div([
28312828
html.I(className="bi bi-arrow-repeat me-2 spin-icon"),
28322829
"Loading page 1…",
2830+
html.Button([html.I(className="bi bi-x-lg me-1"), "Abort"],
2831+
id="load-all-abort-btn", n_clicks=0,
2832+
className="load-all-abort-inline"),
28332833
], className="fetch-status-inner loading")
28342834

2835-
return False, status, {"display": "none"}, {"display": "inline-flex"}
2835+
return False, status, {"display": "none"}
28362836

28372837

28382838
# 11h-tick: Poll progress from background thread
@@ -2842,34 +2842,35 @@ def start_load_all(n_clicks, last_req, conn_config):
28422842
Output("chips-store", "data", allow_duplicate=True),
28432843
Output("fetch-status-bar", "children", allow_duplicate=True),
28442844
Output("load-all-ticker", "disabled", allow_duplicate=True),
2845-
Output("load-all-abort-btn", "style", allow_duplicate=True),
28462845
Input("load-all-ticker", "n_intervals"),
28472846
State("response-cache", "data"),
28482847
prevent_initial_call=True,
28492848
)
28502849
def poll_load_all(n_intervals, cache):
28512850
"""Callback 11h-tick: Poll the background Load All thread for progress."""
2852-
NO = (no_update, no_update, no_update, no_update, no_update, no_update)
2851+
NO = (no_update, no_update, no_update, no_update, no_update)
28532852
state = _load_all_state
28542853
pages = state.get("pages", 0)
28552854
total = state.get("total_items", 0)
28562855
elapsed = state.get("elapsed_ms", 0)
2857-
HIDE = {"display": "none"}
28582856

28592857
if state.get("running"):
28602858
# Still loading — update status bar only
28612859
status = html.Div([
28622860
html.I(className="bi bi-arrow-repeat me-2 spin-icon"),
28632861
f"Loading page {pages + 1}… ({total:,} items so far · {elapsed:,}ms)",
2862+
html.Button([html.I(className="bi bi-x-lg me-1"), "Abort"],
2863+
id="load-all-abort-btn", n_clicks=0,
2864+
className="load-all-abort-inline"),
28642865
], className="fetch-status-inner loading")
2865-
return no_update, no_update, no_update, status, False, no_update
2866+
return no_update, no_update, no_update, status, False
28662867

2867-
# Auto-dismiss: if finished_at was set, wait 5s then clear status bar
2868+
# Auto-dismiss: if finished_at was set, wait 10s then clear status bar
28682869
finished_at = state.get("finished_at")
28692870
if finished_at and not state.get("done"):
2870-
if time.time() - finished_at >= 5:
2871+
if time.time() - finished_at >= 10:
28712872
state.pop("finished_at", None)
2872-
return no_update, no_update, no_update, "", True, HIDE # clear status, stop ticker
2873+
return no_update, no_update, no_update, "", True # clear status, stop ticker
28732874
return NO # keep ticking, waiting to dismiss
28742875

28752876
if not state.get("done"):
@@ -2915,33 +2916,31 @@ def poll_load_all(n_intervals, cache):
29152916
# Mark done, set dismiss timer — keep ticker running for auto-dismiss
29162917
_load_all_state.update({"done": False, "items": [], "finished_at": time.time()})
29172918

2918-
return build_response_panel(merged_result, chips), new_cache, chips or None, status, False, HIDE
2919+
return build_response_panel(merged_result, chips), new_cache, chips or None, status, False
29192920

29202921

29212922
# 11h-abort: Stop background Load All thread
29222923
@app.callback(
29232924
Output("fetch-status-bar", "children", allow_duplicate=True),
2924-
Output("load-all-abort-btn", "style", allow_duplicate=True),
29252925
Input("load-all-abort-btn", "n_clicks"),
29262926
prevent_initial_call=True,
29272927
)
29282928
def abort_load_all(n_clicks):
29292929
"""Callback 11h-abort: Signal the background Load All thread to stop."""
29302930
if not n_clicks:
2931-
return no_update, no_update
2931+
return no_update
29322932
_load_all_state["running"] = False
29332933
_load_all_state["error"] = "Cancelled"
29342934
return html.Div([
29352935
html.I(className="bi bi-x-circle-fill me-2"),
29362936
"Aborting…",
2937-
], className="fetch-status-inner cancelled"), {"display": "none"}
2937+
], className="fetch-status-inner cancelled")
29382938

29392939

29402940
# 11h-abort-on-switch: Cancel Load All when a different endpoint is selected
29412941
@app.callback(
29422942
Output("load-all-ticker", "disabled", allow_duplicate=True),
29432943
Output("fetch-status-bar", "children", allow_duplicate=True),
2944-
Output("load-all-abort-btn", "style", allow_duplicate=True),
29452944
Input("selected-endpoint", "data"),
29462945
prevent_initial_call=True,
29472946
)
@@ -2952,7 +2951,7 @@ def abort_load_all_on_switch(endpoint):
29522951
_load_all_state["error"] = "Cancelled"
29532952
# Also clear any lingering dismiss timer
29542953
_load_all_state.pop("finished_at", None)
2955-
return True, "", {"display": "none"}
2954+
return True, ""
29562955

29572956

29582957
# 13. Search filter

assets/style.css

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
--bg-dark: #080c18;
1111
--bg-panel: #0d1225;
1212
--bg-surface: #141a2e;
13+
--bg-topbar: rgba(8,12,24,0.95);
1314
--bg-card: rgba(255,255,255,0.028);
1415
--bg-hover: rgba(255,255,255,0.055);
1516
--bg-active: rgba(0,212,255,0.09);
@@ -58,7 +59,7 @@ body {
5859
/* ── Top Navigation Bar ───────────────────────────────────────────── */
5960
.topbar {
6061
height: 56px;
61-
background: rgba(8,12,24,0.95) !important;
62+
background: var(--bg-topbar) !important;
6263
border-bottom: 1px solid var(--border) !important;
6364
backdrop-filter: blur(20px);
6465
-webkit-backdrop-filter: blur(20px);
@@ -872,25 +873,23 @@ body {
872873
border-color: rgba(239,68,68,0.6);
873874
box-shadow: 0 0 8px rgba(239,68,68,0.2);
874875
}
875-
.load-all-abort-btn {
876-
position: absolute;
877-
right: 18px;
878-
top: 6px;
879-
z-index: 10;
876+
.load-all-abort-btn { display: none; }
877+
.load-all-abort-inline {
880878
background: rgba(239,68,68,0.1);
881879
border: 1px solid rgba(239,68,68,0.3);
882880
color: var(--red);
883881
border-radius: var(--r-sm);
884-
padding: 2px 10px;
882+
padding: 1px 8px;
885883
font-size: 11px;
886884
font-family: var(--font-mono);
887885
cursor: pointer;
888886
display: inline-flex;
889887
align-items: center;
890888
white-space: nowrap;
889+
margin-left: 12px;
891890
transition: all 0.15s;
892891
}
893-
.load-all-abort-btn:hover {
892+
.load-all-abort-inline:hover {
894893
background: rgba(239,68,68,0.22);
895894
border-color: rgba(239,68,68,0.6);
896895
}
@@ -1132,14 +1131,14 @@ body {
11321131
top: 56px;
11331132
right: 0;
11341133
width: 520px;
1135-
background: #0a0f20;
1134+
background: var(--bg-panel);
11361135
border-left: 1px solid rgba(0,212,255,0.18);
11371136
border-bottom: 1px solid rgba(0,212,255,0.18);
11381137
border-radius: 0 0 0 var(--r-lg);
11391138
z-index: 9999;
11401139
max-height: calc(100vh - 56px);
11411140
overflow-y: auto;
1142-
box-shadow: -10px 10px 40px rgba(0,0,0,0.7);
1141+
box-shadow: -10px 10px 40px rgba(0,0,0,0.35);
11431142
scrollbar-width: thin;
11441143
scrollbar-color: rgba(255,255,255,0.08) transparent;
11451144
}
@@ -1187,7 +1186,7 @@ body {
11871186
margin-bottom: 4px;
11881187
}
11891188
.conn-select, .conn-input {
1190-
background: rgba(255,255,255,0.04) !important;
1189+
background: var(--bg-card) !important;
11911190
border: 1px solid var(--border) !important;
11921191
color: var(--text-1) !important;
11931192
font-size: 12px !important;
@@ -1198,7 +1197,7 @@ body {
11981197
border-color: var(--border-hi) !important;
11991198
box-shadow: 0 0 0 2px rgba(0,212,255,0.1) !important;
12001199
}
1201-
.conn-select option { background: #0d1225; }
1200+
.conn-select option { background: var(--bg-panel); }
12021201
.conn-hint { font-size: 11px; color: var(--text-3); }
12031202

12041203
/* Apply / Connect button */

version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
549
1+
581

0 commit comments

Comments
 (0)