Part of #68 (Roadmap to 1.0.0).
Three small UI/UX polish items, grouped. Each is independently shippable; the per-file coverage gate (npm test) applies to all.
1. Prettier EXPLAIN output (pretty = 1 + compact = 1, version-gated)
Problem. The plan views render ClickHouse's raw indentation-based EXPLAIN. ClickHouse ≥ 26.3 added EXPLAIN … pretty = 1 (line-drawing tree, human-readable expressions, output columns, JOIN annotations; improved again in 26.4), and compact = 1 collapses noisy Expression steps — together they read far better. We just emit EXPLAIN <inner> today (src/core/explain.js:90).
Catch. pretty is unknown on servers < 26.3 (Unknown setting pretty), so it must be gated on the server version — which the app does not currently know (#74's stamp is the app build, not the server).
Scope.
- Capture server version once — add a
SELECT version() (alongside or right after loadSchema in src/net/ch-client.js), store on state (e.g. state.serverVersion).
src/core/: pure version helper (100% covered) — e.g. supportsExplainPretty(versionString) parsing major.minor and returning ≥ 26.3. Robust to empty/malformed version strings (→ false).
buildExplainQuery(inner, viewId, opts) — add an opts.pretty flag (kept pure/testable). When true, decorate:
- plain:
EXPLAIN pretty = 1, compact = 1 <inner>
- indexes:
EXPLAIN indexes = 1, pretty = 1, compact = 1 <inner>
- projections:
EXPLAIN projections = 1, pretty = 1, compact = 1 <inner>
- pipeline (
graph = 1) and estimate: unchanged.
When false (older server), emit exactly today's queries.
- Caller (
src/ui/app.js) passes pretty: supportsExplainPretty(state.serverVersion).
Acceptance.
Note (out of scope): since CH ≥ 25.9, EXPLAIN indexes = 1 only shows reasonable output with SETTINGS use_query_condition_cache = 0, use_skip_indexes_on_data_read = 0. Separate concern — flag as a possible follow-up.
2. Sort underscore-prefixed tables to the end of each database
Problem. Tables named _… (internal/temp/staging) clutter the top of each database's list in the schema sidebar. Ordering is purely SQL (ORDER BY database, name, src/net/ch-client.js:117); there's no client-side sort.
Scope.
- Change the ORDER BY to push underscore-prefixed names last, alphabetised within each group:
ORDER BY database, startsWith(name, '_'), name
(Use startsWith(name, '_'), not name LIKE '_%' — _ is a LIKE single-char wildcard and would match every name.)
- Apply the same to the lineage query (
src/net/ch-client.js:150).
Acceptance.
3. Loading spinner for the table-detail / CREATE TABLE pane
Problem. openNodeDetail (src/ui/app.js:605) awaits loadTableDetail (columns + partitions + create_table_query) before mounting the pane, so on a slow fetch nothing appears — no feedback that the DDL is loading.
Scope. Mirror the existing columns = 'loading' sentinel and the Icon.spinner() + .placeholder.starting pattern already used for the schema graph (src/ui/results.js:108):
- Mount the detail pane immediately in a loading state (spinner + "Loading table…").
- Await
loadTableDetail, then populate / re-render the pane with columns, partitions, and DDL.
- Render branch in
src/ui/schema-detail.js (+ its test); wiring in src/ui/app.js.
Acceptance.
Part of #68 (Roadmap to 1.0.0).
Three small UI/UX polish items, grouped. Each is independently shippable; the per-file coverage gate (
npm test) applies to all.1. Prettier EXPLAIN output (
pretty = 1+compact = 1, version-gated)Problem. The plan views render ClickHouse's raw indentation-based EXPLAIN. ClickHouse ≥ 26.3 added
EXPLAIN … pretty = 1(line-drawing tree, human-readable expressions, output columns, JOIN annotations; improved again in 26.4), andcompact = 1collapses noisyExpressionsteps — together they read far better. We just emitEXPLAIN <inner>today (src/core/explain.js:90).Catch.
prettyis unknown on servers < 26.3 (Unknown setting pretty), so it must be gated on the server version — which the app does not currently know (#74's stamp is the app build, not the server).Scope.
SELECT version()(alongside or right afterloadSchemainsrc/net/ch-client.js), store on state (e.g.state.serverVersion).src/core/: pure version helper (100% covered) — e.g.supportsExplainPretty(versionString)parsingmajor.minorand returning≥ 26.3. Robust to empty/malformed version strings (→ false).buildExplainQuery(inner, viewId, opts)— add anopts.prettyflag (kept pure/testable). When true, decorate:EXPLAIN pretty = 1, compact = 1 <inner>EXPLAIN indexes = 1, pretty = 1, compact = 1 <inner>EXPLAIN projections = 1, pretty = 1, compact = 1 <inner>graph = 1) and estimate: unchanged.When false (older server), emit exactly today's queries.
src/ui/app.js) passespretty: supportsExplainPretty(state.serverVersion).Acceptance.
pretty = 1, compact = 1.pretty/compact), no failed requests.Note (out of scope): since CH ≥ 25.9,
EXPLAIN indexes = 1only shows reasonable output withSETTINGS use_query_condition_cache = 0, use_skip_indexes_on_data_read = 0. Separate concern — flag as a possible follow-up.2. Sort underscore-prefixed tables to the end of each database
Problem. Tables named
_…(internal/temp/staging) clutter the top of each database's list in the schema sidebar. Ordering is purely SQL (ORDER BY database, name,src/net/ch-client.js:117); there's no client-side sort.Scope.
startsWith(name, '_'), notname LIKE '_%'—_is a LIKE single-char wildcard and would match every name.)src/net/ch-client.js:150).Acceptance.
3. Loading spinner for the table-detail / CREATE TABLE pane
Problem.
openNodeDetail(src/ui/app.js:605)awaitsloadTableDetail(columns + partitions +create_table_query) before mounting the pane, so on a slow fetch nothing appears — no feedback that the DDL is loading.Scope. Mirror the existing
columns = 'loading'sentinel and theIcon.spinner()+.placeholder.startingpattern already used for the schema graph (src/ui/results.js:108):loadTableDetail, then populate / re-render the pane with columns, partitions, and DDL.src/ui/schema-detail.js(+ its test); wiring insrc/ui/app.js.Acceptance.
npm testgreen at the per-file coverage gate (new version helper + explain + schema-detail branches at 100%).