From 8d73f664f5b88c6860bbe071a602c595898ff8c5 Mon Sep 17 00:00:00 2001 From: JetSquirrel Date: Thu, 1 Oct 2026 02:47:12 +0800 Subject: [PATCH 1/2] Cross-filter dashboards, map what to query, and a five-command CLI Dashboards: - A `filter` block makes a bar chart or a table pickable; a query reads the pick as `$name`, a predicate that is TRUE while nothing is picked. The picking plot's own query is never narrowed, only queries whose SQL changed re-run, and the picks show as chips above the plots. - `source` blocks name data files relative to the .dash file, as CTEs in front of each query that reads them. - A 12-column grid (`width`), `card` plots, and maps sized, coloured and labelled by column, over an optional OpenStreetMap base map. - A wheel over a table plot scrolls the table or the dashboard, not both. CLI: - `ducklocal schema` lists the relations under PATHs or in a database, with their columns and the SQL that reads each, summarised past 12 relations; `--stats` is the per-column profile, which `profile` now only aliases. - `ducklocal open` shows SQL, a spec or data in the running window; `open --state` reads back what the window shows, for an agent to pick up where a person left off. - Errors carry a `hint` when there is an obvious next step. - Help lists query, schema, open, check and export; `profile` and `lsp` still work. Co-Authored-By: Claude Opus 5.5 (1M context) --- Cargo.lock | 2 + Cargo.toml | 4 + README.md | 24 +- README.zh-CN.md | 24 +- examples/analysis_app/dashboard.dash | 54 +- skills/ducklocal/SKILL.md | 20 +- skills/ducklocal/references/cli.md | 48 +- src/app.rs | 84 +++- src/cli.rs | 166 +++++- src/i18n.rs | 29 ++ src/main.rs | 4 + src/remote.rs | 676 +++++++++++++++++++++++++ src/schema_cli.rs | 473 +++++++++++++++++ src/spec/complete.rs | 124 ++++- src/spec/filter.rs | 205 ++++++++ src/spec/lsp.rs | 36 +- src/spec/mod.rs | 44 +- src/spec/model.rs | 697 ++++++++++++++++++++++++- src/spec/plot.rs | 70 ++- src/spec/prepare.rs | 153 +++++- src/spec/source.rs | 230 +++++++++ src/spec/view.rs | 725 +++++++++++++++++++++++++-- src/spec/wheel.rs | 238 +++++++++ src/ui/chart.rs | 6 + src/ui/geo.rs | 464 +++++++++++++++-- src/ui/mod.rs | 11 + src/ui/results.rs | 36 ++ src/ui/tiles.rs | 421 ++++++++++++++++ src/ui/workspace.rs | 61 ++- tests/cli.rs | 289 +++++++++++ tests/lsp.rs | 17 +- 31 files changed, 5262 insertions(+), 173 deletions(-) create mode 100644 src/remote.rs create mode 100644 src/schema_cli.rs create mode 100644 src/spec/filter.rs create mode 100644 src/spec/source.rs create mode 100644 src/spec/wheel.rs create mode 100644 src/ui/tiles.rs diff --git a/Cargo.lock b/Cargo.lock index a83da54..caf6f48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2236,10 +2236,12 @@ dependencies = [ "chrono", "directories", "duckdb", + "getrandom 0.3.4", "gpui-component-shell", "gpui-kit", "gpui-shell", "hmac 0.12.1", + "image", "lsp-server", "lsp-types", "quick-xml 0.36.2", diff --git a/Cargo.toml b/Cargo.toml index 0297eef..85bc955 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,10 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } ureq = { version = "3", default-features = false, features = ["rustls"] } hmac = "0.12" sha2 = "0.10" +getrandom = "0.3" +# Map tiles: the same `image` GPUI decodes with, only the formats tile +# servers send. +image = { version = "0.25", default-features = false, features = ["png", "jpeg"] } quick-xml = { version = "0.36", features = ["serialize"] } unicode-width = "0.2" diff --git a/README.md b/README.md index 42eca69..64e1037 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ ducklocal warehouse.duckdb # or an existing DuckDB database Each CSV, TSV, Parquet, JSON, or Excel file becomes queryable as the window opens. You can also drag files or folders onto the window, or pick them from the file dialog — and they stay registered, so the next launch starts with the same workspace. One open request attaches at most 256 files. -Your data stays on your machine: nothing is uploaded, and there is no account. +Your data stays on your machine: nothing is uploaded, and there is no account. Map charts can draw an OpenStreetMap base map under their points, off until you turn it on (View → Online base map): its tile requests reveal the area being viewed, never your rows. ## Features @@ -43,7 +43,15 @@ ducklocal query --sql "SELECT * FROM 'sales.csv'" --limit 20 ducklocal query --database warehouse.duckdb --sql "SHOW TABLES" ``` -Results are structured JSON, with explicit truncation and precision-preserving value encodings; `--format md` prints the same values as a Markdown table for a document to quote. File databases default to read-only; writes require `--read-write`. Read-only is not a filesystem/network sandbox: `COPY` can still write files. +Results are structured JSON, with explicit truncation and precision-preserving value encodings; `--format md` prints the same values as a Markdown table for a document to quote. File databases default to read-only; writes require `--read-write`. Read-only is not a filesystem/network sandbox: `COPY` can still write files. An error is JSON too, and carries a `hint` when there is an obvious next step — the flag that was meant, `--read-write` for a refused write, `ducklocal schema` for a table that does not exist. + +`ducklocal schema` maps what there is to query in one call — every file under the PATHs, or a database's tables and views, with their columns and the SQL that reads each. A large catalog comes back as a summary sized for a prompt, and `--stats` gives one relation's exact per-column statistics: + +```bash +ducklocal schema ./data/ +ducklocal schema --database warehouse.duckdb --table orders +ducklocal schema sales.csv --stats # nulls, distinct, min/max, median, day coverage +``` An analysis app can be exported as one standalone HTML file — the statements its `query()` calls issued, with their results — for sharing with someone who does not have DuckLocal: @@ -51,7 +59,7 @@ An analysis app can be exported as one standalone HTML file — the statements i ducklocal export --html examples/analysis_app ``` -A dashboard can also be declared as data: a `.dash` file of query and plot blocks, opened as a tab like an app, editable right there in the GUI. `ducklocal check` validates a spec without a window, and `ducklocal lsp` gives any LSP-capable editor the same diagnostics, completion, hover and go-to-definition: +A dashboard can also be declared as data: a `.dash` file of query and plot blocks, opened as a tab like an app, editable right there in the GUI. A `filter` block makes a bar chart or a table clickable: the value picked there narrows every query that reads it as `$name` — cross-filtering, with the picking plot itself left whole. `ducklocal check` validates a spec without a window, and `ducklocal lsp` gives any LSP-capable editor the same diagnostics, completion, hover and go-to-definition: ```bash ducklocal dashboard.dash # open as a dashboard tab @@ -59,6 +67,16 @@ ducklocal check dashboard.dash # validate; JSON diagnostics, exit 2 o ducklocal lsp # language server over stdio, for editors ``` +When the work is for a person to see, `ducklocal open` hands it to the window that is already running — starting one if none is — as a tab in front. It runs in the window's own session, so they can carry on from it: + +```bash +ducklocal open --title "Revenue" --run --sql "SELECT channel, sum(amount) FROM 'orders.csv' GROUP BY 1" +ducklocal open dashboard.dash # a dashboard tab; data files and folders attach +ducklocal open --no-launch --sql-file q.sql # exit 1, kind not_running, if no window is up +``` + +The other direction works too: `ducklocal open --state` reports what the running window shows — its tabs, each query's SQL and result, each dashboard's picked filters — so an agent can carry on from where the person left off. + See the [CLI guide](https://ducklocal.app/docs/cli) for conversion, stdin, output, app export, and safety details. The [official agent skill](skills/ducklocal/SKILL.md) teaches schema-first exploration, SQL analysis, and verified file conversion. Copy it into your target project's supported skills directory; for example, from this checkout: diff --git a/README.zh-CN.md b/README.zh-CN.md index b707360..ff0cb2f 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -19,7 +19,7 @@ ducklocal warehouse.duckdb # 或已有的 DuckDB 数据库 每个 CSV / TSV / Parquet / JSON / Excel 文件在窗口打开时即可查询。也可以把文件或文件夹拖进窗口,或用文件对话框选择;它们会被记住,下次启动直接回到同一个工作区。一次打开请求最多挂载 256 个文件。 -数据只留在这台机器上:不上传、不需要账号。 +数据只留在这台机器上:不上传、不需要账号。地图图表可以在点下方绘制 OpenStreetMap 底图,默认关闭,需在「视图 → 在线底图」中开启:瓦片请求会暴露正在查看的区域,但绝不包含你的数据行。 ## 功能 @@ -43,7 +43,15 @@ ducklocal query --sql "SELECT * FROM 'sales.csv'" --limit 20 ducklocal query --database warehouse.duckdb --sql "SHOW TABLES" ``` -结果为结构化 JSON,显式标记截断并保留数值精度;`--format md` 把同样的值输出为 Markdown 表格,便于写进文档引用。文件数据库默认只读,写入需 `--read-write`;只读不是文件系统/网络沙箱,`COPY` 仍可写文件。 +结果为结构化 JSON,显式标记截断并保留数值精度;`--format md` 把同样的值输出为 Markdown 表格,便于写进文档引用。文件数据库默认只读,写入需 `--read-write`;只读不是文件系统/网络沙箱,`COPY` 仍可写文件。出错时同样输出 JSON;有明确下一步时会带 `hint`,比如拼错的参数本来是哪个、写入被拒时加 `--read-write`、表不存在时先跑 `ducklocal schema`。 + +`ducklocal schema` 一次列出所有可查询的东西:PATH 下的每个文件,或数据库的表和视图,连同列和读取它的 SQL。目录很大时返回适合放进提示词的摘要;`--stats` 给出单个关系精确的逐列统计: + +```bash +ducklocal schema ./data/ +ducklocal schema --database warehouse.duckdb --table orders +ducklocal schema sales.csv --stats # 空值、去重数、最值、中位数、日期覆盖 +``` 分析应用可以导出为一个独立 HTML 文件——应用 `query()` 发出的语句及其结果——方便发给没有 DuckLocal 的人: @@ -51,7 +59,7 @@ ducklocal query --database warehouse.duckdb --sql "SHOW TABLES" ducklocal export --html examples/analysis_app ``` -dashboard 也可以声明为数据:一个由 query 和 plot block 组成的 `.dash` 文件,像应用一样以标签页打开,并可直接在 GUI 里编辑。`ducklocal check` 无窗口校验规格文件,`ducklocal lsp` 则把同样的诊断、补全、悬停和跳转定义提供给任何支持 LSP 的编辑器: +dashboard 也可以声明为数据:一个由 query 和 plot block 组成的 `.dash` 文件,像应用一样以标签页打开,并可直接在 GUI 里编辑。`filter` block 让柱状图或表格可以点选:选中的值会筛选所有以 `$name` 引用它的查询,也就是交叉筛选,做选择的那张图本身保持不变。`ducklocal check` 无窗口校验规格文件,`ducklocal lsp` 则把同样的诊断、补全、悬停和跳转定义提供给任何支持 LSP 的编辑器: ```bash ducklocal dashboard.dash # 以 dashboard 标签页打开 @@ -59,6 +67,16 @@ ducklocal check dashboard.dash # 校验;JSON 诊断,有错误时 ducklocal lsp # 面向编辑器的语言服务器(stdio) ``` +如果结果是给人看的,`ducklocal open` 会把它交给已经在运行的窗口(没有就先启动一个),作为前台的新标签页打开。它在窗口自己的会话里运行,人可以直接接着改: + +```bash +ducklocal open --title "Revenue" --run --sql "SELECT channel, sum(amount) FROM 'orders.csv' GROUP BY 1" +ducklocal open dashboard.dash # 打开为 dashboard 标签页;数据文件和文件夹会被挂载 +ducklocal open --no-launch --sql-file q.sql # 没有窗口时退出码 1,kind 为 not_running +``` + +反方向也可以:`ducklocal open --state` 报告正在运行的窗口里显示的内容,包括各个标签页、每个查询的 SQL 和结果、每个 dashboard 当前选中的筛选。这样 agent 可以从人停下的地方接着做。 + 转换、stdin、输出编码、应用导出和安全说明见 [CLI 指南](https://ducklocal.app/docs/zh/cli)。 [官方 agent skill](skills/ducklocal/SKILL.md) 教 AI 先查 schema,再进行 SQL 分析及验证格式转换。复制到目标项目支持的 skill 目录即可,例如在本仓库执行: diff --git a/examples/analysis_app/dashboard.dash b/examples/analysis_app/dashboard.dash index a82ff52..5350889 100644 --- a/examples/analysis_app/dashboard.dash +++ b/examples/analysis_app/dashboard.dash @@ -1,5 +1,5 @@ // The analysis_app panel's data as a dashboard spec: same orders.csv, -// declared rather than scripted. Check it from the repository root: +// declared rather than scripted. Check it from anywhere: // // ducklocal check examples/analysis_app/dashboard.dash // @@ -7,13 +7,26 @@ // // ducklocal examples/analysis_app/dashboard.dash // -// Paths in SQL are relative to the working directory, like every other -// ducklocal command. +// The source's path is relative to this file, so the spec and its data move +// together; the queries read it by name. + +source "orders" { + path = "orders.csv" +} + +query "totals" { + sql = <