Skip to content

Commit a48fe3c

Browse files
committed
refactor: simplify demo and improve recent results table UI
Remove Datasets and SQL query tabs from the explorer demo, show recent results in a selectable table instead of a dropdown, and add tab_ui helpers so mo.stop in result cells does not block the tab layout.
1 parent c494a18 commit a48fe3c

4 files changed

Lines changed: 148 additions & 92 deletions

File tree

examples/demo.py

Lines changed: 7 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -36,80 +36,31 @@ def _(hm, mo, os):
3636
def _(hm, workspace):
3737
client = workspace.client
3838
status = hm.connections_panel(client)
39-
browser = hm.table_browser(client)
40-
editor = hm.sql_editor(
41-
client,
42-
default_sql="SELECT 1 AS ok",
43-
)
4439
recent = hm.recent_results(client, limit=20)
4540
history = hm.run_history(client, limit=10)
46-
return browser, client, editor, history, recent, status
41+
return client, history, recent, status
4742

4843

4944
@app.cell
5045
def _(mo):
5146
mo.md(r"""
5247
## HotData explorer
53-
Use the tabs below to switch between available workspaces, connection status, dataset browsing, and SQL queries.
48+
Use the tabs below to switch between workspaces, connection status, recent results, and run history.
5449
""")
5550
return
5651

5752

5853
@app.cell
59-
def _(browser):
60-
# Register connection/table widget deps so Marimo reruns layout cells on change.
61-
if browser._conn_pick is not None:
62-
_ = browser._conn_pick.value
63-
_ = browser.table_pick.value
64-
return
65-
66-
67-
@app.cell
68-
def _(browser):
69-
browser_ui = browser.ui
70-
return (browser_ui,)
71-
72-
73-
@app.cell
74-
def _(editor):
75-
editor_ui = editor.ui
76-
return (editor_ui,)
77-
78-
79-
@app.cell
80-
def _(editor, hm):
81-
_run = editor.run.value
82-
_rerun = editor.rerun.value
83-
_clear = editor.clear.value
84-
sql_result = hm.query_result(editor.result)
85-
return (sql_result,)
86-
87-
88-
@app.cell
89-
def _(editor_ui, mo, sql_result):
90-
sql_tab = mo.vstack([editor_ui, sql_result], gap=2)
91-
return (sql_tab,)
92-
93-
94-
@app.cell
95-
def _(hm, recent):
96-
recent_result = hm.query_result(recent.result, label="Recent result")
97-
return (recent_result,)
98-
99-
100-
@app.cell
101-
def _(mo, recent, recent_result):
102-
recent_tab = mo.vstack([recent.ui, recent_result], gap=2)
54+
def _(recent):
55+
recent_tab = recent.tab_ui
10356
return (recent_tab,)
10457

10558

10659
@app.cell
107-
def _(browser_ui, history, mo, recent_tab, sql_tab, status, workspace):
60+
def _(history, mo, recent_tab, status, workspace):
10861
mo.ui.tabs({
10962
"Workspaces": workspace.ui,
11063
"Connections": status,
111-
"Datasets": browser_ui,
112-
"SQL query": sql_tab,
11364
"Recent results": recent_tab,
11465
"Run history": history,
11566
})
@@ -119,10 +70,10 @@ def _(browser_ui, history, mo, recent_tab, sql_tab, status, workspace):
11970
@app.cell
12071
def _(client, mo):
12172
_df = mo.sql(
122-
"""
73+
f"""
12374
SELECT 1 AS example_value
12475
""",
125-
engine=client,
76+
engine=client
12677
)
12778
return
12879

hotdata_marimo/display.py

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66

77
from hotdata_runtime import HotdataClient, QueryResult, workspace_health_lines
88

9-
from hotdata_marimo._options import empty_dropdown, unique_label_options
10-
119

1210
def query_result(
1311
result: QueryResult,
@@ -61,36 +59,71 @@ class RecentResults:
6159
def __init__(self, client: HotdataClient, *, limit: int = 50) -> None:
6260
self._client = client
6361
self._results = client.list_recent_results(limit=limit, offset=0)
64-
option_pairs = [
65-
(f"{r.created_at} · {r.status} · {r.result_id}", r.result_id)
62+
self._rows: list[dict[str, object]] = [
63+
{
64+
"created_at": r.created_at,
65+
"status": r.status,
66+
"result_id": r.result_id,
67+
}
6668
for r in self._results
6769
]
68-
options = unique_label_options(option_pairs)
69-
self.pick = (
70-
empty_dropdown(label="Recent results", message="(no results)")
71-
if not options
72-
else mo.ui.dropdown(
73-
options=options,
70+
self.table = (
71+
mo.ui.table(
72+
self._rows,
7473
label="Recent results",
75-
full_width=True,
74+
pagination=True,
75+
page_size=min(10, limit),
76+
selection="single",
77+
max_height=320,
7678
)
79+
if self._rows
80+
else None
7781
)
7882

7983
@property
8084
def selected_result_id(self) -> str | None:
81-
v = self.pick.value
82-
return v if v else None
85+
if self.table is None:
86+
return None
87+
selected = self.table.value
88+
if not selected:
89+
return None
90+
row = selected[0]
91+
if not isinstance(row, dict):
92+
return None
93+
rid = row.get("result_id")
94+
return rid if rid else None
8395

8496
@property
8597
def result(self) -> QueryResult:
8698
rid = self.selected_result_id
87-
mo.stop(rid is None, mo.md("Pick a result id to load."))
99+
mo.stop(rid is None, mo.md("Select a result row to load."))
88100
return self._client.get_result(rid or "")
89101

102+
@property
103+
def result_panel(self):
104+
rid = self.selected_result_id
105+
if rid is None:
106+
return mo.md("_Select a result row to load._")
107+
return query_result(self._client.get_result(rid), label="Recent result")
108+
109+
@property
110+
def tab_ui(self):
111+
if self.table is not None:
112+
_ = self.table.value
113+
return mo.vstack([self.ui, self.result_panel], gap=2)
114+
90115
@property
91116
def ui(self):
92-
_ = self.pick.value
93-
return mo.vstack([self.pick], gap=1)
117+
if self.table is None:
118+
return mo.md("_No recent results._")
119+
_ = self.table.value
120+
return mo.vstack(
121+
[
122+
mo.md("### Recent results"),
123+
self.table,
124+
],
125+
gap=1,
126+
)
94127

95128

96129
def recent_results(client: HotdataClient, *, limit: int = 50) -> RecentResults:

hotdata_marimo/sql_editor.py

Lines changed: 70 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -88,33 +88,22 @@ def ui(self):
8888
gap=1,
8989
)
9090

91-
@property
92-
def result(self) -> QueryResult:
93-
run_n = self.run.value
94-
rerun_n = self.rerun.value
95-
clear_n = self.clear.value
96-
91+
def _apply_clear(self, clear_n: int) -> bool:
9792
if clear_n > 0 and self._last_clear_n != clear_n:
9893
self._result_cache = None
9994
self._cached_sql = None
10095
self._last_clear_n = clear_n
101-
mo.stop(True, mo.md("Result cleared. Click **Run on Hotdata** to execute again."))
96+
return True
97+
return False
10298

103-
mo.stop(
104-
run_n == 0 and rerun_n == 0,
105-
mo.md(
106-
"**Run on Hotdata** is on the SQL editor UI (a cell that **returns** "
107-
"`editor.ui` or `mo.vstack([browser.ui, editor.ui])`). Click it there, "
108-
"then this cell will run."
109-
),
110-
)
99+
def _execute_or_cached(self) -> QueryResult | None:
111100
sql_text = self.sql.value
101+
run_n = self.run.value
102+
rerun_n = self.rerun.value
112103

113104
if rerun_n > 0 and rerun_n != self._last_rerun_n:
114-
mo.stop(
115-
self._cached_sql is None,
116-
mo.md("No previous SQL to rerun yet — click **Run on Hotdata** first."),
117-
)
105+
if self._cached_sql is None:
106+
return None
118107
with mo.status.spinner(
119108
title="Running on Hotdata",
120109
subtitle="Re-running last query and waiting for results…",
@@ -138,8 +127,69 @@ def result(self) -> QueryResult:
138127
if self._result_cache is not None and sql_text == self._cached_sql:
139128
return self._result_cache
140129

130+
return None
131+
132+
@property
133+
def result_panel(self):
134+
from hotdata_marimo.display import query_result
135+
136+
run_n = self.run.value
137+
rerun_n = self.rerun.value
138+
clear_n = self.clear.value
139+
140+
if self._apply_clear(clear_n):
141+
return mo.md("Result cleared. Click **Run on Hotdata** to execute again.")
142+
143+
if run_n == 0 and rerun_n == 0 and self._result_cache is None:
144+
return mo.md("_Click **Run on Hotdata** to execute._")
145+
146+
if rerun_n > 0 and rerun_n != self._last_rerun_n and self._cached_sql is None:
147+
return mo.md("No previous SQL to rerun yet — click **Run on Hotdata** first.")
148+
149+
result = self._execute_or_cached()
150+
if result is not None:
151+
return query_result(result)
152+
153+
return mo.md("SQL changed — click **Run on Hotdata** again to execute.")
154+
155+
@property
156+
def tab_ui(self):
157+
_ = self.run.value
158+
_ = self.rerun.value
159+
_ = self.clear.value
160+
_ = self.show_history.value
161+
return mo.vstack([self.ui, self.result_panel], gap=2)
162+
163+
@property
164+
def result(self) -> QueryResult:
165+
run_n = self.run.value
166+
rerun_n = self.rerun.value
167+
clear_n = self.clear.value
168+
169+
if self._apply_clear(clear_n):
170+
mo.stop(True, mo.md("Result cleared. Click **Run on Hotdata** to execute again."))
171+
172+
mo.stop(
173+
run_n == 0 and rerun_n == 0,
174+
mo.md(
175+
"**Run on Hotdata** is on the SQL editor UI (a cell that **returns** "
176+
"`editor.ui` or `mo.vstack([browser.ui, editor.ui])`). Click it there, "
177+
"then this cell will run."
178+
),
179+
)
180+
181+
if rerun_n > 0 and rerun_n != self._last_rerun_n:
182+
mo.stop(
183+
self._cached_sql is None,
184+
mo.md("No previous SQL to rerun yet — click **Run on Hotdata** first."),
185+
)
186+
187+
result = self._execute_or_cached()
188+
if result is not None:
189+
return result
190+
141191
mo.stop(
142-
self._cached_sql is None or sql_text != self._cached_sql,
192+
True,
143193
mo.md("SQL changed — click **Run on Hotdata** again to execute."),
144194
)
145195

tests/test_hotdata_marimo.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,28 @@ def test_connections_panel_lists_connections(mock_client):
128128
assert panel is not None
129129

130130

131+
def test_recent_results_table_selection(mock_client):
132+
mock_client.list_recent_results.return_value = [
133+
SimpleNamespace(
134+
created_at="2026-05-18T12:00:00Z",
135+
status="succeeded",
136+
result_id="res_1",
137+
),
138+
SimpleNamespace(
139+
created_at="2026-05-18T11:00:00Z",
140+
status="failed",
141+
result_id="res_2",
142+
),
143+
]
144+
table = MagicMock()
145+
table.value = [{"result_id": "res_2"}]
146+
with patch("hotdata_marimo.display.mo.ui.table", return_value=table):
147+
recent = hm.recent_results(mock_client, limit=20)
148+
assert recent.selected_result_id == "res_2"
149+
table.value = []
150+
assert recent.selected_result_id is None
151+
152+
131153
def test_table_browser_rebuilds_tables_when_connection_changes(mock_client):
132154
pick = MagicMock()
133155
pick.value = ""

0 commit comments

Comments
 (0)