hotdata-marimo 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,70 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v[0-9]*'
7
+
8
+ concurrency:
9
+ group: pypi-publish-${{ github.ref_name }}
10
+ cancel-in-progress: false
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ jobs:
16
+ build:
17
+ name: Build distribution
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
21
+
22
+ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
23
+ with:
24
+ python-version: '3.12'
25
+
26
+ - name: Install build tooling
27
+ run: python -m pip install --upgrade build twine
28
+
29
+ - name: Verify tag matches pyproject version
30
+ run: |
31
+ # Release tags must start with `v` followed by a PEP 440 version (e.g. v1.2.3, v1.2.3a1).
32
+ if [[ ! "$GITHUB_REF_NAME" =~ ^v[0-9] ]]; then
33
+ echo "Release tag '$GITHUB_REF_NAME' must start with 'v' followed by a digit (e.g. v1.0.0)" >&2
34
+ exit 1
35
+ fi
36
+ tag="${GITHUB_REF_NAME#v}"
37
+ pkg_version=$(python -c "import tomllib,pathlib; print(tomllib.loads(pathlib.Path('pyproject.toml').read_text())['project']['version'])")
38
+ if [ "$tag" != "$pkg_version" ]; then
39
+ echo "Release tag ($tag) does not match pyproject.toml version ($pkg_version)" >&2
40
+ exit 1
41
+ fi
42
+
43
+ - name: Build sdist and wheel
44
+ run: python -m build
45
+
46
+ - name: Check distribution metadata
47
+ run: python -m twine check --strict dist/*
48
+
49
+ - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
50
+ with:
51
+ name: dist
52
+ path: dist/
53
+
54
+ publish:
55
+ name: Publish to PyPI
56
+ needs: build
57
+ runs-on: ubuntu-latest
58
+ environment:
59
+ name: pypi
60
+ url: https://pypi.org/p/hotdata-marimo
61
+ permissions:
62
+ id-token: write
63
+ steps:
64
+ - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
65
+ with:
66
+ name: dist
67
+ path: dist/
68
+
69
+ - name: Publish via Trusted Publishing
70
+ uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
@@ -0,0 +1,22 @@
1
+ .DS_Store
2
+
3
+ # Python
4
+ __pycache__/
5
+ *.py[cod]
6
+ *.pyd
7
+ .pytest_cache/
8
+ .mypy_cache/
9
+ .ruff_cache/
10
+
11
+ # Virtualenvs
12
+ .venv/
13
+ venv/
14
+
15
+ # Build artifacts
16
+ dist/
17
+ build/
18
+ *.egg-info/
19
+
20
+ # Marimo editor session (local UI state; keep notebook source as examples/*.py only)
21
+ **/__marimo__/session/
22
+
@@ -0,0 +1,14 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2026-05-06
9
+
10
+ ### Added
11
+
12
+ - Initial release: `HotdataClient`, `sql_editor`, `table_browser`, `query_result`, and `from_env`.
13
+ - `connection_status` callout and `mo.ui.hotdata_*` aliases (via `register_mo_ui_hotdata_aliases` on import).
14
+ - SQL editor: run button with cached results, spinner while executing, and prompt to re-run after SQL edits.
@@ -0,0 +1,124 @@
1
+ Metadata-Version: 2.4
2
+ Name: hotdata-marimo
3
+ Version: 0.1.0
4
+ Summary: Marimo integration for Hotdata runtime
5
+ License: MIT
6
+ Requires-Python: >=3.10
7
+ Requires-Dist: hotdata-runtime>=0.1.0
8
+ Requires-Dist: marimo>=0.10.0
9
+ Description-Content-Type: text/markdown
10
+
11
+ # hotdata-marimo
12
+
13
+ Marimo UI helpers for [Hotdata](https://hotdata.dev): run SQL from a notebook, browse catalog metadata, and render results as tables.
14
+
15
+ ## Features
16
+
17
+ - **Workspace-aware setup** — build a `HotdataClient` from environment variables, or use `workspace_selector_from_env()` to choose a workspace interactively when no workspace is pinned.
18
+ - **Connection health** — show a compact status callout with API, workspace, and optional sandbox context.
19
+ - **Catalog browsing** — browse Hotdata connections, schemas, tables, and columns from Marimo UI controls.
20
+ - **SQL editor widget** — run SQL against Hotdata, cache the latest successful result, and render results in downstream reactive cells.
21
+ - **Native `mo.sql` engine** — register `HotdataMarimoEngine` so Marimo SQL cells can execute through a live `HotdataClient` with `engine=client`.
22
+ - **Result display helpers** — render query results, recent results, and run history as notebook-friendly UI.
23
+ - **Marimo UI aliases** — importing `hotdata_marimo` attaches helpers such as `mo.ui.hotdata_sql_editor` and `mo.ui.hotdata_table_browser` for discoverability.
24
+
25
+ ## Install
26
+
27
+ ```bash
28
+ uv pip install hotdata-marimo
29
+ # or: pip install hotdata-marimo
30
+ ```
31
+
32
+ Requires Python 3.10+, **Marimo**, and [**hotdata-runtime**](https://github.com/hotdata-dev/hotdata-runtime) (Hotdata SDK + runtime/session semantics — pulled in automatically when you `pip install hotdata-marimo`).
33
+
34
+ ## Environment
35
+
36
+ | Variable | Required | Description |
37
+ |----------|----------|-------------|
38
+ | `HOTDATA_API_KEY` | Yes | API key for the Hotdata API |
39
+ | `HOTDATA_API_URL` | No | API base URL (default: `https://api.hotdata.dev`) |
40
+ | `HOTDATA_WORKSPACE` | No | Workspace id; if unset, the first active workspace is used |
41
+ | `HOTDATA_SANDBOX` | No | Sandbox session id, passed through to the SDK |
42
+
43
+ ## Minimal notebook
44
+
45
+ ```python
46
+ import marimo as mo
47
+ import hotdata_marimo as hm
48
+
49
+ client = hm.from_env()
50
+ editor = hm.sql_editor(client, default_sql="SELECT 1 AS ok")
51
+ return editor.ui
52
+ ```
53
+
54
+ ```python
55
+ return hm.query_result(editor.result)
56
+ ```
57
+
58
+ Importing `hotdata_marimo` registers discoverability aliases on Marimo’s UI namespace, so you can also use `mo.ui.hotdata_sql_editor`, `mo.ui.hotdata_table_browser`, `mo.ui.hotdata_query_result`, and `mo.ui.hotdata_connection_status`.
59
+
60
+ Use `hm.connection_status(client)` (or `mo.ui.hotdata_connection_status(client)`) for a small API/workspace health callout.
61
+
62
+ ## Marimo SQL Cells
63
+
64
+ Register the Hotdata SQL engine once during setup, then pass a `HotdataClient` to Marimo SQL cells:
65
+
66
+ ```python
67
+ import hotdata_marimo as hm
68
+
69
+ hm.register_hotdata_sql_engine()
70
+ client = hm.from_env()
71
+ ```
72
+
73
+ ```python
74
+ _df = mo.sql(
75
+ """
76
+ SELECT 1 AS example_value
77
+ """,
78
+ engine=client,
79
+ )
80
+ ```
81
+
82
+ The engine also exposes Hotdata catalog metadata to Marimo's data-source UI. Hotdata connections are labeled **Hotdata** in the SQL connection picker.
83
+
84
+ ## Two-cell pattern
85
+
86
+ Keep the editor in one cell and consume `editor.result` in another. The editor caches the last successful run so downstream cells do not re-query the API on every refresh; click **Run on Hotdata** again after you change SQL. While a query is running, a Marimo status spinner is shown.
87
+
88
+ Marimo only shows **what you `return` from a cell**. Calling `mo.vstack(...)` or `hm.query_result(...)` without returning it produces no visible output.
89
+
90
+ See `examples/demo.py` for a full runnable notebook flow.
91
+
92
+ ## Examples
93
+
94
+ - `examples/demo.py` — tabbed explorer with workspace selection, connection health, recent results (selectable table), run history, and a native `mo.sql` cell.
95
+
96
+ Run locally (single-user machine):
97
+
98
+ ```bash
99
+ uv run marimo edit examples/demo.py --no-token
100
+ ```
101
+
102
+ On a **shared or networked host**, omit `--no-token` and use the access token printed in the terminal URL. Without it, anyone who can reach the Marimo port can run queries against your Hotdata workspace.
103
+
104
+ ## Layout
105
+
106
+ This repo is intentionally thin: **API client, env helpers, and result models** live in **hotdata-runtime**; **hotdata-marimo** only adds Marimo widgets (`sql_editor`, `table_browser`, `display` for tables/status/history, `workspace_selector`). Import `HotdataClient` / `QueryResult` / `from_env` from **`hotdata_marimo`** or directly from **`hotdata_runtime`**.
107
+
108
+ ## Development
109
+
110
+ This package depends on [**hotdata-runtime**](https://github.com/hotdata-dev/hotdata-runtime) (PyPI name `hotdata-runtime`). Development uses **uv**; keep a sibling checkout at `../hotdata-runtime` so the lockfile resolves the runtime from disk (see `[tool.uv.sources]` in `pyproject.toml`).
111
+
112
+ ```bash
113
+ uv sync --locked
114
+ uv run pytest
115
+ marimo edit examples/demo.py --no-token
116
+ ```
117
+
118
+ To pin **hotdata-runtime** from Git instead of the sibling path, remove the `[tool.uv.sources]` block, set the dependency line as needed, and run `uv lock` again.
119
+
120
+ For a **publishable** `uv.lock` (CI that only clones this repo), remove `[tool.uv.sources]`, point `hotdata-runtime` at PyPI or `git+https://…`, then `uv lock`.
121
+
122
+ The **`[project] name`** in [hotdata-runtime](https://github.com/hotdata-dev/hotdata-runtime) `pyproject.toml` is **`hotdata-runtime`** and the import package is **`hotdata_runtime`**.
123
+
124
+ Use **`--no-token`** for local development so the editor does not redirect to `/auth/login` (session auth is easy to hit with a global Marimo config). For a public or shared machine, omit it and use the printed URL with an access token instead.
@@ -0,0 +1,114 @@
1
+ # hotdata-marimo
2
+
3
+ Marimo UI helpers for [Hotdata](https://hotdata.dev): run SQL from a notebook, browse catalog metadata, and render results as tables.
4
+
5
+ ## Features
6
+
7
+ - **Workspace-aware setup** — build a `HotdataClient` from environment variables, or use `workspace_selector_from_env()` to choose a workspace interactively when no workspace is pinned.
8
+ - **Connection health** — show a compact status callout with API, workspace, and optional sandbox context.
9
+ - **Catalog browsing** — browse Hotdata connections, schemas, tables, and columns from Marimo UI controls.
10
+ - **SQL editor widget** — run SQL against Hotdata, cache the latest successful result, and render results in downstream reactive cells.
11
+ - **Native `mo.sql` engine** — register `HotdataMarimoEngine` so Marimo SQL cells can execute through a live `HotdataClient` with `engine=client`.
12
+ - **Result display helpers** — render query results, recent results, and run history as notebook-friendly UI.
13
+ - **Marimo UI aliases** — importing `hotdata_marimo` attaches helpers such as `mo.ui.hotdata_sql_editor` and `mo.ui.hotdata_table_browser` for discoverability.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ uv pip install hotdata-marimo
19
+ # or: pip install hotdata-marimo
20
+ ```
21
+
22
+ Requires Python 3.10+, **Marimo**, and [**hotdata-runtime**](https://github.com/hotdata-dev/hotdata-runtime) (Hotdata SDK + runtime/session semantics — pulled in automatically when you `pip install hotdata-marimo`).
23
+
24
+ ## Environment
25
+
26
+ | Variable | Required | Description |
27
+ |----------|----------|-------------|
28
+ | `HOTDATA_API_KEY` | Yes | API key for the Hotdata API |
29
+ | `HOTDATA_API_URL` | No | API base URL (default: `https://api.hotdata.dev`) |
30
+ | `HOTDATA_WORKSPACE` | No | Workspace id; if unset, the first active workspace is used |
31
+ | `HOTDATA_SANDBOX` | No | Sandbox session id, passed through to the SDK |
32
+
33
+ ## Minimal notebook
34
+
35
+ ```python
36
+ import marimo as mo
37
+ import hotdata_marimo as hm
38
+
39
+ client = hm.from_env()
40
+ editor = hm.sql_editor(client, default_sql="SELECT 1 AS ok")
41
+ return editor.ui
42
+ ```
43
+
44
+ ```python
45
+ return hm.query_result(editor.result)
46
+ ```
47
+
48
+ Importing `hotdata_marimo` registers discoverability aliases on Marimo’s UI namespace, so you can also use `mo.ui.hotdata_sql_editor`, `mo.ui.hotdata_table_browser`, `mo.ui.hotdata_query_result`, and `mo.ui.hotdata_connection_status`.
49
+
50
+ Use `hm.connection_status(client)` (or `mo.ui.hotdata_connection_status(client)`) for a small API/workspace health callout.
51
+
52
+ ## Marimo SQL Cells
53
+
54
+ Register the Hotdata SQL engine once during setup, then pass a `HotdataClient` to Marimo SQL cells:
55
+
56
+ ```python
57
+ import hotdata_marimo as hm
58
+
59
+ hm.register_hotdata_sql_engine()
60
+ client = hm.from_env()
61
+ ```
62
+
63
+ ```python
64
+ _df = mo.sql(
65
+ """
66
+ SELECT 1 AS example_value
67
+ """,
68
+ engine=client,
69
+ )
70
+ ```
71
+
72
+ The engine also exposes Hotdata catalog metadata to Marimo's data-source UI. Hotdata connections are labeled **Hotdata** in the SQL connection picker.
73
+
74
+ ## Two-cell pattern
75
+
76
+ Keep the editor in one cell and consume `editor.result` in another. The editor caches the last successful run so downstream cells do not re-query the API on every refresh; click **Run on Hotdata** again after you change SQL. While a query is running, a Marimo status spinner is shown.
77
+
78
+ Marimo only shows **what you `return` from a cell**. Calling `mo.vstack(...)` or `hm.query_result(...)` without returning it produces no visible output.
79
+
80
+ See `examples/demo.py` for a full runnable notebook flow.
81
+
82
+ ## Examples
83
+
84
+ - `examples/demo.py` — tabbed explorer with workspace selection, connection health, recent results (selectable table), run history, and a native `mo.sql` cell.
85
+
86
+ Run locally (single-user machine):
87
+
88
+ ```bash
89
+ uv run marimo edit examples/demo.py --no-token
90
+ ```
91
+
92
+ On a **shared or networked host**, omit `--no-token` and use the access token printed in the terminal URL. Without it, anyone who can reach the Marimo port can run queries against your Hotdata workspace.
93
+
94
+ ## Layout
95
+
96
+ This repo is intentionally thin: **API client, env helpers, and result models** live in **hotdata-runtime**; **hotdata-marimo** only adds Marimo widgets (`sql_editor`, `table_browser`, `display` for tables/status/history, `workspace_selector`). Import `HotdataClient` / `QueryResult` / `from_env` from **`hotdata_marimo`** or directly from **`hotdata_runtime`**.
97
+
98
+ ## Development
99
+
100
+ This package depends on [**hotdata-runtime**](https://github.com/hotdata-dev/hotdata-runtime) (PyPI name `hotdata-runtime`). Development uses **uv**; keep a sibling checkout at `../hotdata-runtime` so the lockfile resolves the runtime from disk (see `[tool.uv.sources]` in `pyproject.toml`).
101
+
102
+ ```bash
103
+ uv sync --locked
104
+ uv run pytest
105
+ marimo edit examples/demo.py --no-token
106
+ ```
107
+
108
+ To pin **hotdata-runtime** from Git instead of the sibling path, remove the `[tool.uv.sources]` block, set the dependency line as needed, and run `uv lock` again.
109
+
110
+ For a **publishable** `uv.lock` (CI that only clones this repo), remove `[tool.uv.sources]`, point `hotdata-runtime` at PyPI or `git+https://…`, then `uv lock`.
111
+
112
+ The **`[project] name`** in [hotdata-runtime](https://github.com/hotdata-dev/hotdata-runtime) `pyproject.toml` is **`hotdata-runtime`** and the import package is **`hotdata_runtime`**.
113
+
114
+ Use **`--no-token`** for local development so the editor does not redirect to `/auth/login` (session auth is easy to hit with a global Marimo config). For a public or shared machine, omit it and use the printed URL with an access token instead.
@@ -0,0 +1,40 @@
1
+ # hotdata-marimo — next steps
2
+
3
+ ## Near term (build on MVP)
4
+
5
+ - [x] **Workspace selector** — When `HOTDATA_WORKSPACE` is unset and multiple workspaces exist, expose `mo.ui.dropdown` (or similar) and rebuild the client when the choice changes.
6
+ - [x] **Connection status** — Small status chip (API reachable, workspace id, optional sandbox) using a lightweight health or `workspaces`/`connections` probe.
7
+ - [ ] **Query cancel** — Wire cancel to the query-run API if/when exposed in the OpenAPI client; surface a Cancel control next to Run.
8
+ - [x] **`mo.ui.hotdata_*` aliases** — Re-export or thin wrappers: `hotdata_sql_editor`, `hotdata_table_browser`, `hotdata_query_result`, `hotdata_connection_picker` for discoverability.
9
+ - [x] **Tests** — Unit tests with mocked SDK responses; optional integration tests gated on `HOTDATA_API_KEY` (mirror sdk-python patterns).
10
+
11
+ ## SQL editor & execution
12
+
13
+ - [ ] **Schema-aware autocomplete** — anywidget (or CodeMirror) + Hotdata `information_schema` / column metadata for table/column suggestions.
14
+ - [x] **Async UX** — Progress text or spinner while polling query runs; optional configurable timeouts.
15
+ - [x] **Run history panel** — `QueryRunsApi.list_query_runs` + metadata (latency, touched tables, `result_id`) in a side panel or collapsible.
16
+ - [x] **Rerun / clear** — Explicit rerun without relying only on `run_button` semantics; optional “clear result” action.
17
+
18
+ ## Results
19
+
20
+ - [x] **Pagination / LIMIT guidance** — Surface row counts vs limit; warn when result is truncated if the API exposes it.
21
+ - [ ] **Export** — CSV / Parquet download links or buttons (align with Hotdata results/export APIs when available).
22
+ - [x] **Cached result reuse** — Prefer `get_result(result_id)` over re-running identical SQL when `result_id` is known.
23
+ - [ ] **Materialization / cache status** — Show persistence state when the API returns it (`processing` / `ready` / errors).
24
+
25
+ ## Data discovery
26
+
27
+ - [x] **Connection picker** — Filter catalog by connection; map display names to ids consistently.
28
+ - [x] **Schema search** — Text filter over tables/columns without loading full dropdowns for huge catalogs.
29
+ - [ ] **Table preview** — `LIMIT` preview query from browser selection (optional second query).
30
+ - [ ] **Generated queries** — Starters beyond `SELECT *`: profile/join templates from selected tables.
31
+
32
+ ## App mode
33
+
34
+ - [ ] **Deployable Marimo app** — Layout recipe (sidebar explorer + editor + result) and docs for `marimo run` / WASM constraints.
35
+ - [ ] **Auth in apps** — Document env injection for deployed apps; avoid embedding API keys in notebooks.
36
+
37
+ ## Docs & packaging
38
+
39
+ - [x] **README** — Install, env vars, minimal notebook example, and “two-cell” pattern for `editor.result` + `mo.stop`.
40
+ - [x] **Changelog** — Keep `CHANGELOG.md` once versions ship.
@@ -0,0 +1,85 @@
1
+ import marimo
2
+
3
+ __generated_with = "0.23.5"
4
+ app = marimo.App()
5
+
6
+
7
+ @app.cell
8
+ def _():
9
+ import os
10
+
11
+ import marimo as mo
12
+
13
+ import hotdata_marimo as hm
14
+
15
+ hm.register_hotdata_sql_engine()
16
+ return hm, mo, os
17
+
18
+
19
+ @app.cell
20
+ def _(hm, mo, os):
21
+ mo.stop(
22
+ not os.environ.get("HOTDATA_API_KEY"),
23
+ mo.callout(
24
+ mo.md(
25
+ "Add **HOTDATA_API_KEY** to your environment "
26
+ "to run this example."
27
+ ),
28
+ kind="warn",
29
+ ),
30
+ )
31
+ workspace = hm.workspace_selector_from_env()
32
+ return (workspace,)
33
+
34
+
35
+ @app.cell
36
+ def _(hm, workspace):
37
+ client = workspace.client
38
+ status = hm.connections_panel(client)
39
+ recent = hm.recent_results(client, limit=20)
40
+ history = hm.run_history(client, limit=10)
41
+ return client, history, recent, status
42
+
43
+
44
+ @app.cell
45
+ def _(mo):
46
+ mo.md(r"""
47
+ ## HotData explorer
48
+ Use the tabs below to switch between workspaces, connection status, recent results, and run history.
49
+
50
+ On a shared or networked host, run Marimo **without** `--no-token` and open the printed URL
51
+ with its access token so only you can use this notebook.
52
+ """)
53
+ return
54
+
55
+
56
+ @app.cell
57
+ def _(recent):
58
+ recent_tab = recent.tab_ui
59
+ return (recent_tab,)
60
+
61
+
62
+ @app.cell
63
+ def _(history, mo, recent_tab, status, workspace):
64
+ mo.ui.tabs({
65
+ "Workspaces": workspace.ui,
66
+ "Connections": status,
67
+ "Recent results": recent_tab,
68
+ "Run history": history,
69
+ })
70
+ return
71
+
72
+
73
+ @app.cell
74
+ def _(client, mo):
75
+ _df = mo.sql(
76
+ """
77
+ SELECT 1 AS example_value
78
+ """,
79
+ engine=client
80
+ )
81
+ return
82
+
83
+
84
+ if __name__ == "__main__":
85
+ app.run()
@@ -0,0 +1,80 @@
1
+ """Marimo-native UI and helpers for Hotdata (built on hotdata-runtime)."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ try:
6
+ __version__ = version("hotdata-marimo")
7
+ except PackageNotFoundError:
8
+ __version__ = "0.0.0+unknown"
9
+
10
+ from hotdata_runtime import HotdataClient, QueryResult, from_env
11
+
12
+ from hotdata_marimo.display import (
13
+ RecentResults,
14
+ connection_status,
15
+ connections_panel,
16
+ query_result,
17
+ recent_results,
18
+ run_history,
19
+ )
20
+ from hotdata_marimo.sql_engine import (
21
+ HotdataMarimoEngine,
22
+ register_hotdata_sql_engine,
23
+ unregister_hotdata_sql_engine,
24
+ )
25
+ from hotdata_marimo.sql_editor import SqlEditor, sql_editor
26
+ from hotdata_marimo.table_browser import TableBrowser, connection_picker, table_browser
27
+ from hotdata_marimo.workspace_selector import WorkspaceSelector, workspace_selector_from_env
28
+
29
+ __all__ = [
30
+ "__version__",
31
+ "HotdataClient",
32
+ "HotdataMarimoEngine",
33
+ "QueryResult",
34
+ "RecentResults",
35
+ "SqlEditor",
36
+ "TableBrowser",
37
+ "WorkspaceSelector",
38
+ "connection_picker",
39
+ "connection_status",
40
+ "connections_panel",
41
+ "from_env",
42
+ "hotdata_connection_picker",
43
+ "hotdata_query_result",
44
+ "hotdata_recent_results",
45
+ "hotdata_sql_editor",
46
+ "hotdata_table_browser",
47
+ "hotdata_workspace_selector",
48
+ "query_result",
49
+ "recent_results",
50
+ "register_hotdata_sql_engine",
51
+ "register_mo_ui_hotdata_aliases",
52
+ "run_history",
53
+ "sql_editor",
54
+ "table_browser",
55
+ "unregister_hotdata_sql_engine",
56
+ "workspace_selector_from_env",
57
+ ]
58
+
59
+ hotdata_sql_editor = sql_editor
60
+ hotdata_table_browser = table_browser
61
+ hotdata_query_result = query_result
62
+ hotdata_connection_picker = connection_picker
63
+ hotdata_workspace_selector = workspace_selector_from_env
64
+ hotdata_recent_results = recent_results
65
+
66
+
67
+ def register_mo_ui_hotdata_aliases() -> None:
68
+ """Attach Hotdata helpers to ``marimo.ui`` for discoverability (``mo.ui.hotdata_*``)."""
69
+ import marimo as mo
70
+
71
+ mo.ui.hotdata_sql_editor = hotdata_sql_editor # type: ignore[attr-defined]
72
+ mo.ui.hotdata_table_browser = hotdata_table_browser # type: ignore[attr-defined]
73
+ mo.ui.hotdata_query_result = hotdata_query_result # type: ignore[attr-defined]
74
+ mo.ui.hotdata_connection_status = connection_status # type: ignore[attr-defined]
75
+ mo.ui.hotdata_connection_picker = hotdata_connection_picker # type: ignore[attr-defined]
76
+ mo.ui.hotdata_workspace_selector = hotdata_workspace_selector # type: ignore[attr-defined]
77
+ mo.ui.hotdata_recent_results = hotdata_recent_results # type: ignore[attr-defined]
78
+
79
+
80
+ register_mo_ui_hotdata_aliases()
@@ -0,0 +1,107 @@
1
+ """Shared dropdown option helpers for Marimo UI widgets."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+ from typing import Any
7
+
8
+ import marimo as mo
9
+
10
+ from hotdata_runtime import HotdataClient
11
+
12
+
13
+ def unique_label_options(
14
+ pairs: list[tuple[str, str]],
15
+ *,
16
+ disambiguate: Callable[[str, str, int], str] | None = None,
17
+ ) -> dict[str, str]:
18
+ """Build a label→value map, suffixing repeated labels when needed."""
19
+ counts: dict[str, int] = {}
20
+ options: dict[str, str] = {}
21
+ for label, value in pairs:
22
+ count = counts.get(label, 0)
23
+ counts[label] = count + 1
24
+ if count == 0:
25
+ key = label
26
+ elif disambiguate is not None:
27
+ key = disambiguate(label, value, count)
28
+ else:
29
+ key = f"{label} ({count + 1})"
30
+ options[key] = value
31
+ return options
32
+
33
+
34
+ def empty_dropdown(
35
+ *,
36
+ label: str,
37
+ message: str,
38
+ full_width: bool = True,
39
+ ):
40
+ return mo.ui.dropdown(
41
+ options={message: ""},
42
+ label=label,
43
+ full_width=full_width,
44
+ )
45
+
46
+
47
+ def connection_options(conns: list[Any]) -> dict[str, str]:
48
+ pairs = [(str(c.name), str(c.id)) for c in conns]
49
+ return unique_label_options(
50
+ pairs,
51
+ disambiguate=lambda label, value, count: f"{label} ({value})",
52
+ )
53
+
54
+
55
+ def connection_picker_from_connections(
56
+ conns: list[Any],
57
+ *,
58
+ label: str = "Connection",
59
+ full_width: bool = True,
60
+ ):
61
+ if not conns:
62
+ return empty_dropdown(
63
+ label=label,
64
+ message="(no connections)",
65
+ full_width=full_width,
66
+ )
67
+ return mo.ui.dropdown(
68
+ options=connection_options(conns),
69
+ label=label,
70
+ full_width=full_width,
71
+ )
72
+
73
+
74
+ def connection_picker(
75
+ client: HotdataClient,
76
+ *,
77
+ label: str = "Connection",
78
+ full_width: bool = True,
79
+ ):
80
+ conns = client.connections().list_connections().connections
81
+ return connection_picker_from_connections(
82
+ conns,
83
+ label=label,
84
+ full_width=full_width,
85
+ )
86
+
87
+
88
+ def resolve_connection_picker(
89
+ client: HotdataClient,
90
+ *,
91
+ label: str = "Connection",
92
+ full_width: bool = True,
93
+ ) -> tuple[Any | None, str | None]:
94
+ """Return ``(dropdown_or_none, implicit_connection_id)`` for table browsers."""
95
+ conns = client.connections().list_connections().connections
96
+ if not conns:
97
+ return None, ""
98
+ if len(conns) == 1:
99
+ return None, conns[0].id
100
+ return (
101
+ connection_picker_from_connections(
102
+ conns,
103
+ label=label,
104
+ full_width=full_width,
105
+ ),
106
+ None,
107
+ )