跳转到内容

仪表盘(Dashboard,.dash) ​

English

.dash 文件以数据描述仪表盘:source 块为数据文件命名,query 块存放 SQL,plot 块绘制查询结果,filter 块让点击一张图就能筛选其他图。不需要编写代码,人和 AI agent 都可以编写这类文件,通过 diff 审阅,并在打开前校验。

.dash 文件可以根据保存的查询绘制常规图表、表格、数字和地图。纯文本格式也方便与 AI agent 协作:agent 编写或修改文件,用 ducklocal check 验证能否运行,你再在图表上写评论,提出下一步修改意见。

完整示例 ​

这个仪表盘读取教程样例文件。把 sales.dash 下载到 sales.csv 所在的文件夹,再打开它。

hcl
source "sales" {
  path = "sales.csv"
}

query "totals" {
  sql = <<SQL
    SELECT round(sum(revenue), 2) AS revenue, sum(orders) AS orders,
           count(DISTINCT region) AS regions
    FROM sales
    WHERE $channel
  SQL
}

query "by_channel" {
  sql = <<SQL
    SELECT channel, round(sum(revenue), 2) AS revenue
    FROM sales
    GROUP BY channel
    ORDER BY revenue DESC
  SQL
}

query "daily" {
  sql = <<SQL
    SELECT date, region, round(sum(revenue), 2) AS revenue
    FROM sales
    WHERE $channel
    GROUP BY date, region
    ORDER BY date
  SQL
}

plot "revenue" {
  type  = "card"
  query = query.totals
  value = revenue
  title = "Revenue"
  width = 4
}

plot "orders" {
  type  = "card"
  query = query.totals
  value = orders
  title = "Orders"
  width = 4
}

plot "regions" {
  type  = "card"
  query = query.totals
  value = regions
  title = "Regions"
  width = 4
}

plot "revenue_by_channel" {
  type  = "bar"
  query = query.by_channel
  x     = channel
  y     = revenue
  title = "Revenue by channel"
  width = 5
}

plot "daily_revenue" {
  type   = "line"
  query  = query.daily
  x      = date
  y      = revenue
  series = region
  title  = "Daily revenue, per region"
  width  = 7
}

plot "daily_table" {
  type  = "table"
  query = query.daily
  title = "The numbers behind it"
}

filter "channel" {
  plot = plot.revenue_by_channel
}

打开后,最上面一排是三个数字,下面柱状图和折线图并排,再下面是表格。每个 query 只执行一次,不管有多少个 plot 用它:totals 供给三张卡片,daily 同时供给折线图和表格。在 Revenue by channel 里点一根柱子,卡片、折线图和表格就只看这个渠道;柱状图本身保持完整,可以接着点别的柱子。

打开仪表盘 ​

以下任一方式都能把 .dash 文件打开成仪表盘标签页,和查询标签页并列:

  • 标签栏末尾的 +,再选 打开 Dashboard;
  • 把文件拖到窗口上;
  • 在命令行里指定:ducklocal sales.dash;
  • 从脚本或 agent 打开到已经在运行的窗口里:ducklocal open sales.dash(见 CLI 指南)。

plot 排在 12 列的网格上:每个 plot 占它的 width 列,从左往右排满一行,放不下的就换到下一行。拖动行之间的分隔条可以调整高度;仪表盘比标签页高时会滚动。画不出来的 plot(查询失败,或缺少某列)会在自己的格子里显示原因,其余部分照常绘制。打开的仪表盘下次启动时会恢复,最近打开过的会列在侧栏的 仪表盘 下。

鼠标移到 plot 上,右上角会出现操作按钮:

  • SQL 把 plot 翻过来,显示它的查询上一次执行的 SQL(已经填好 source 和当前的筛选值,可以原样运行),附有 复制 和 在查询页打开;图表 再翻回去。
  • 对话气泡打开这个 plot 的评论。
  • 放大这张图 让它占满仪表盘标签页;点 放回看板(Esc) 或按 Esc 回到网格。

SQL 视图带语法高亮。右键点击仪表盘表格的单元格,可复制该单元格、用制表符分隔的整行,或按显示顺序复制整列。

点 table 图表的表头可以排序:先降序,再升序,再回到查询原来的顺序。数值按数值排,NULL 排在最后;被筛选选中的行,排序后仍保持选中。

工具栏的 查看源码 会把标签页切换成该文件的编辑器,带语法高亮(heredoc 里的 SQL 与 SQL 编辑器同样着色)、补全和行内诊断。保存 或 ⌘S 写回文件并重新绘制。如果保存的文件未通过校验,标签页会保留上一次能用的仪表盘,并在上方显示错误。其他程序修改了文件时也是如此。返回视图 回到图表。重新加载 会重读文件并重新执行所有查询。

用评论审阅 ​

有了评论,审阅仪表盘就像审阅文档一样,还能把修改交给 AI agent。关于这套设计背后的产品思考,可阅读《Claude Dashboards 给 DuckLocal 的启发》。

在窗口里,鼠标移到 plot 上,点对话气泡。仪表盘上方会弹出一张卡片:顶部引用 plot 的标题,下面是已有的讨论,再下面是输入框。写下要改的地方(比如“按收入从高到低排”),点 评论(或按 ⌘/Ctrl+Enter)。每条讨论都有 回复 和 解决;解决了的讨论会变淡但仍然保留,有人回复时重新打开。有未解决讨论的 plot 会一直显示一个带数字的气泡,哪里还有事待办一眼就能看到。

agent 用 ducklocal comments 读取并回复评论,不需要窗口:

bash
ducklocal comments sales.dash
json
{
  "file": "sales.dash",
  "comments_file": "sales.dash.comments.json",
  "open": 1,
  "threads": [
    {
      "id": "c1",
      "plot": "revenue_by_channel",
      "plot_title": "Revenue by channel",
      "plot_query": "by_channel",
      "plot_lines": [68, 75],
      "plot_missing": false,
      "resolved": false,
      "comments": [
        { "author": "user", "text": "按收入从高到低排", "at": "2026-10-10T14:03:00+08:00" }
      ]
    }
  ]
}

每条讨论都写明了它所在的 plot、plot 用的查询,以及 plot block 在文件里的起止行,agent 由此知道该改哪里。它修改规格文件,跑一遍 ducklocal check,然后回复:

bash
ducklocal comments sales.dash --reply c1 --text "已按收入降序排列" --resolve

窗口同时盯着这两个文件。一秒之内,plot 就按改过的规格重新绘制,agent 的回复也出现在讨论里,标着 Agent。

评论保存在仪表盘旁边的 名称.dash.comments.json 里,和它的 source 文件一样跟着仪表盘走。它们是工作笔记,不是仪表盘的一部分:别提交进版本库,审阅完就可以删掉。

官方 agent skill 教过这套流程,装了它的 agent 只要听到一句“处理 sales.dash 上的评论”就知道怎么做。

0.4.0 新增

agent 可以提议修改,而不是直接改文件。标签页上会出现 审阅,显示差异和检查结果,只有你能应用。

看见 agent 正在做什么 ​

agent 对当前打开的仪表盘运行 check、comments 或 presence 时,角落会显示它的名字,带着它所属厂商的标志(Claude、OpenAI、Gemini、Cursor、DeepSeek),认不出的就显示首字母;点击可查看这次工作的记录。文件被修改后,改动和新增的 plot 会保持标记(Claude 改过、Claude 新增),直到你把它们放在视野里看过片刻;被删除的 plot 留下一条可手动关闭的提示。

agent 可以先运行 ducklocal presence sales.dash --doing editing --plot revenue_by_channel,说明接下来要改哪里。已有的 plot 会加上边框;尚不存在的名字会在仪表盘下方显示占位提示。30 秒没有活动,或显式传入 --doing idle 后,这次会话结束。agent 干完了、但它回复的评论还在等你回应时,它会留在角落,头像上显示等待的条数。评论会记下是哪个 agent 写的,并显示它的标志。参数见 CLI 参考。这些标记用于展示本机活动,不会自动向 agent 派发任务。

Block ​

文件由一连串 block 组成,每个 block 是类型、带引号的名字和主体:

hcl
source "name" { ... }
query "name" { ... }
plot "name" { ... }
filter "name" { ... }

同一类中名字必须唯一:两个 query 不能同名,两个 plot 也不能,但 query 和 plot 之间可以同名。

source ​

属性必填取值
path是一个 .csv、.tsv、.txt、.parquet、.json、.jsonl 或 .ndjson 文件,或者匹配多个文件的通配符(exports/orders-*.csv),合起来当作一张表读

source 给数据文件起一个名字,供查询读取:写了 source "sales",查询里就写 FROM sales。路径相对于 .dash 文件,而不是 DuckLocal 启动时所在的目录,所以仪表盘和数据可以一起挪;~ 开头的路径和绝对路径也可以。

source 不会动连接:它会变成每个用到它的查询前面的一段 WITH sales AS (…),所以侧栏里不会多出东西,标签页关掉也不留痕迹;同名的表只在这个仪表盘的查询里被遮住。名字必须是普通标识符(字母、数字和 _)。工作簿不能当 source,先在 DuckLocal 里打开它,再查询它变成的表。

query ​

属性必填取值
sql是一条只读 SQL 语句,写成 heredoc 或字符串

query block 只包含 sql。SQL 里可以用 $name 读取某个 filter。

plot ​

属性适用取值
type全部,必填"line"、"bar"、"stacked_bar"、"area"、"scatter"、"pie"、"heatmap"、"histogram"、"map"、"table"、"card" 或 "text"——带引号的字符串
query除 text 外全部必填指向某个 query block 的引用:query.by_channel
x图表,必填横轴上的列;pie 的扇区;histogram 要分箱的数值列。table 显示所有列,不需要 x
y图表,必填要画的列,必须是数值类型;pie 各扇区的大小。histogram 不需要 y
seriesline、bar、stacked_bar、area、scatter、heatmap按这一列的取值拆分行,每个取值一条序列;heatmap 的行
title全部plot 的标题,带引号的字符串;卡片的标签
width全部在 12 列网格里占几列,1–12。默认 12,卡片默认 3
valuecard要显示的列,取第一行。默认第一列
notecard数值下方的说明;带引号的字符串直接显示,裸列名则读取第一行对应列的值
tonecard"accent"、"good"、"warning" 或 "critical",设置顶部横线与数值的颜色
format图表和 card数字的含义,见数字格式与变化。map 和 table 不支持
deltacard存放变化量的数值列,显示在数值下方
delta_formatcard变化量的格式。默认与卡片相同
down_is_goodcard设为 true 时,下降显示绿色、上升显示红色,适合成本、延迟、流失率
description全部,0.4.0 起这张图显示什么、怎么读;鼠标移到标题旁的 ⓘ 上时显示
texttext正文,字符串或 heredoc;title 与 text 至少写一个,不写 query
lat、lngmap纬度和经度列,单位是度。数值列的列名表明它是纬度或经度时可以不写,比如 lat、latitude、geo_lat,或 lng、lon、longitude
colormap按这一列给点着色;不写时,如果有取值不多的文本列,会自动用它
sizemap按这一数值列决定点的大小
size_scalemap"sqrt"(默认)或 "log",决定 size 怎样缩放
tooltipmap点的提示框里显示哪些列:[name, passengers]

列属性指的是查询结果中的列,匹配时不区分大小写。普通列名直接写(x = date);不是普通标识符的列名加引号(y = "revenue (EUR)")。某种类型用不上的属性,比如折线图的 lat、地图的 x、饼图的 series,会被当成错误,而不是悄悄忽略。

filter ​

属性必填取值
plot是在哪张图上点选:plot.revenue_by_channel。只能是 bar、stacked_bar 或 table
columntable 必填点选时取哪一列的值。bar 和 stacked_bar 不写时就是它的 x

filter 的作用见筛选。

图表类型 ​

类型画出
line每条序列一条线,不填充
area每条序列一块填充面积
bar每行一根柱子,每条序列一种颜色。带 series 时,每组里各序列的柱子并排。只有一条序列时,每根柱子上标出数值
stacked_bar每个 x 一根柱子,各 series 堆叠:正值从零向上,负值向下,顶部和提示框里显示合计
heatmapx 横排、series 竖排,每个格子按 y 用同一种蓝色着色,值越大越深(深色模式下越浅)。查询没有返回的组合留空
histogram按数值列 x 的区间统计行数,箱宽取整。查询要直接返回行本身,不要先分组
scatter每个点一个圆点,互不相连,每条序列一种颜色
pie每个 x 值一个扇区,大小按 y,从大到小;超过 8 个时最小的几个合并成"其他"。画成环形,中间显示合计;点图例项可以单独突出一个扇区,再点一次恢复
text标题、说明文字,或两者兼有;不执行查询
map每行一个点,位置按 lat 和 lng;见地图
table把查询结果显示为表格,列宽按内容调整;点表头排序
card一个大号数字,title 是它的标签。按千位分组,过长的小数会舍入(11439704.020000005 → 11,439,704.02),放不下时缩小字号而不是截断

鼠标移到图表上可以查看数值,格式与坐标轴一致(显示 4.4M,不显示 4400000)。

柱状图会标出每一个类别。标签在柱子下面放不下时,图会横过来,标签排在左边一列。只有时间轴会抽稀标签,在不重叠的前提下尽量多标,第一个和最后一个总会标出;按 DATE 列画的柱状图,会把查询里没有数据的日子补成空位,一段冷清的日子看起来是个缺口,而不是直接消失。折线图、面积图和散点图的轴也这样抽稀标签。

所有图表类型的 x 轴都是分类轴:各个值按查询返回的顺序等距排开,不按数值间距排。所以用两个数值列画 scatter,得到的并不是真正的 x–y 散点图。

行按查询返回的顺序绘制,所以给图表供数的查询要以 ORDER BY 结尾。

0.4.0 新增

仪表盘里按时间画的 line 和 scatter 会按实际时间定位,间隔不均匀时看起来也不均匀,并且不管 ORDER BY 如何都按时间顺序画。时间为 NULL 的行不画,并有提示。查询返回的 NULL 读数会让线在那里断开。area 仍然等距排开,并会注明。序列按名称保持颜色;查询被截断时,每种图表都会注明。

卡片显示查询的第一行。查询返回多行时,数字下面会注明;一行都没有时显示 —。

卡片与文本面板 ​

卡片的 note 用来解释数值,tone 用颜色表达状态。同一行的卡片共享数值字号。文本面板可以添加标题或说明,不执行 SQL:

hcl
plot "revenue_summary" {
  type  = "card"
  query = query.totals
  value = revenue
  title = "收入"
  note  = "当前选中渠道"
  tone  = "accent"
  width = 4
}

plot "reading_guide" {
  type  = "text"
  title = "如何阅读这张仪表盘"
  text  = "点击渠道,筛选每日图表和收入卡片。"
  width = 8
}

文本面板只接受 type、title、text 和 width。较长的说明可以写成 text = <<TEXT,以单独一行 TEXT 结束。图表、饼图和地图使用八种不同色相,并为当前主题调整明暗;多条序列会显示图例。

数字格式与变化 ​

format 决定卡片数值、数值轴、柱子标签和提示框的显示方式:

取值显示为
"num0"、"num1"、"num2"带相应小数位的数字
"pct"(等于 "pct1")、"pct0"、"pct2"比例:0.421 显示为 42.1%,所以查询要返回小数,不要乘以 100
"usd"、"eur"、"gbp"、"cny"货币,两位小数
"usd0"、"eur0"、"gbp0"、"cny0"、"jpy"货币,取整

卡片用 delta 显示变化,在数值下方显示为 ▲ 12.3% 或 ▼ $125。向好时为绿色,否则为红色,后面的 note 说明是跟什么比。变化量要在卡片的查询里算好:

hcl
plot "revenue_total" {
  type         = "card"
  query        = query.totals
  value        = revenue
  format       = "usd0"
  delta        = growth
  delta_format = "pct1"
  note         = "比上周"
}

布局 ​

width 是 plot 在仪表盘的 12 列里占几列。plot 按写的顺序从左往右排,下一个放不下就换行,所以四张卡片(各占 3)排成一行数字,两张 width = 6 的图在下面并排。一行不满 12 列时,剩下的地方空着。全是卡片的一行起始高度较矮;全是表格的一行,起始高度刚好放下最长那张表的所有行(最多一张图的高度);全是文本面板的一行按文字内容确定高度;其他行起始高度是一张图的高度。鼠标悬停时才显示行间的调整手柄。

地图 ​

hcl
plot "stations" {
  type       = "map"
  query      = query.stations
  lat        = geo_lat
  lng        = geo_lng
  color      = line
  size       = passengers
  size_scale = "log"
  tooltip    = [name, passengers]
}

地图把每一行画成一个点,用 Web Mercator 投影,范围自动贴合这些点,背景是标了度数的经纬网。

  • color:最多五个取值各有一种颜色;取值更多时,最常见的四个各有颜色,其余共用"其他"。图例标出各颜色对应的值。不写时,如果结果里有取值不多的文本列,就按它着色。
  • size:点的面积跟着数值走,数值翻倍,面积也翻倍。数值跨好几个数量级时用 size_scale = "log"。角落的图例可以读回大小对应的数值。
  • tooltip:不写时,提示框先列 size 列和其他数值列,再列坐标。

点的下面可以画一层 OpenStreetMap 底图。它默认关闭,要在 显示 → 在线底图(OpenStreetMap) 里打开,因为这是 DuckLocal 唯一会自己联网的地方。发出去的只是要哪些地图瓦片,大致能看出你在看哪片区域,但不会带上任何一行数据。瓦片在磁盘上缓存一周(见设置与应用数据);关闭或离线时,地图只画经纬网。查询结果 图表 标签页里的地图也受这个开关控制。

筛选 ​

filter 让一张图可以点选,并用选中的值筛选其他查询:

hcl
filter "channel" {
  plot = plot.revenue_by_channel     # 点一根柱子,选中它的 channel
}

filter "day" {
  plot   = plot.days                 # 表格:选中一行,取……
  column = day                       # ……这一列的值
}

query "daily" {
  sql = "SELECT date, sum(orders) AS orders FROM sales WHERE $channel AND $day GROUP BY 1"
}

查询里的 $name 是一个条件,写在该写条件的地方。什么都没选时它是 TRUE,所以仪表盘打开时和没有这个 filter 完全一样。选中一个值后,它变成 ("channel" = 'Web'),也就是 filter 的 column 列等于选中的值;选中显示为 NULL 的单元格时,会匹配 SQL NULL 或文本 NULL。多个 filter 怎么组合,由查询自己的写法决定。

  • 柱状图:点一根柱子就选中它,它保持原色,其他柱子变淡。再点一次取消。
  • 表格:选中一行,点击或用方向键都可以。
  • 当前生效的选择以小标签显示在所有图的上方;点标签上的 ✕ 清除这个 filter,全部清除 清除所有选择。
  • 做选择的那张图不会被自己的 filter 筛选。 即使选中了某个渠道,它的查询里 $channel 仍然是 TRUE;否则点一下,柱状图就只剩刚点的那根,没法再点别的。这就是交叉筛选,和 Mosaic、crossfilter 的做法一样。
  • 只重跑变了的查询。 一次选择只重跑 SQL 因此改变的查询;不读任何 filter 的查询保留原来的结果。

每个 filter 同时只保留一个值。可点选的 plot 会在标题下显示点击筛选提示;筛选导致部分分组消失时,序列仍保持原来的颜色。使用 $channel 的查询,必须在该条件所在的位置能访问筛选器指定的列;ducklocal check 还会绑定一次带选中值的查询,在第一次点击前发现缺失的列。

0.4.0 新增

点选显示为 NULL 的单元格时,只选中 SQL NULL;文本 'NULL' 也只选中它自己。可点选的图表不再在标题下显示点击筛选提示。

选中的值以带引号的字面量写进 SQL,比较时由 DuckDB 转成列的类型,所以日期、数字和文本一样能用。选择在标签页开着期间一直有效,重新加载后只要 filter 还在文件里也会保留;关掉 DuckLocal 后不会记住。

取值 ​

形式示例用于
字符串"Revenue by channel"type、title、path、size_scale、tone、字面量 note、text、带引号的列名、简短的 sql
Heredoc<<SQL … SQLsql、text
引用query.daily、plot.revenue_by_channelplot 的 query、filter 的 plot
裸名字channel列属性
数字6width
列表[name, "Unique IPs"]tooltip

字符串在行尾结束,支持 \n、\t、\" 和 \\。

# 或 // 之后直到行尾都是注释。属性之间不需要分隔符;换行只是空白。

这就是全部语法:没有变量、没有函数、没有条件,除了 filter 的 $name 也没有别的插值。

Heredoc ​

heredoc 用来写跨行文本,实际上就是写 SQL:

hcl
  sql = <<SQL
    SELECT 1
  SQL
  • << 后面的分隔符可以是任意单词(按惯例用 SQL),同一行里它后面不能再有别的内容。
  • heredoc 在第一行内容恰好等于分隔符(允许前后有空格)的地方结束。
  • 每一行文本都会去掉结束行那么多的缩进,所以 SQL 可以跟着 block 缩进,缩进本身不会带进 SQL。

SQL 规则 ​

仪表盘在打开、重新加载、保存时,以及 DuckLocal 启动并恢复该标签页时都会执行查询,所以查询只能读:

  • 允许: SELECT、WITH …、FROM 开头的查询、VALUES、SHOW、DESCRIBE、SUMMARIZE、PIVOT 和 UNPIVOT。
  • 拒绝: 任何可能改动数据或文件的语句,如 CREATE、INSERT、UPDATE、DELETE、DROP、COPY、ATTACH、INSTALL、LOAD 等。打开别人发来的 .dash 文件不会改动你的数据。
  • 每个 query 一条语句。
  • 嵌在别的查询里的 PIVOT 要写明取值。 以 PIVOT 开头的查询可以让 DuckDB 自己找取值(PIVOT orders ON channel USING sum(amount));嵌在 SELECT 或 WITH 里的,要写出取值列表:ON channel IN ('Web', 'Store')。
  • $name 必须对应一个 filter block。 只读检查时,每个 $name 都按 TRUE 处理。字符串、带引号的标识符、注释和 $$ … $$ 字符串里的 $ 不算。

查询在窗口自己的连接上执行,所以能看到 SQL 编辑器能看到的一切:打开的文件、挂载的数据库,以及 TEMP 表等连接级状态。

SQL 里的路径 ​

从 v0.2.2 起,仪表盘 SQL 里的相对文件路径也会从 .dash 所在文件夹查找。例如,从别的目录打开仪表盘时,FROM 'sales.csv' 可以读取仪表盘旁的 CSV。窗口和 ducklocal check 都如此。

对于 SQL 里直接指定的单个文件,DuckDB 仍会先检查工作目录;那里有同名文件时,可能优先读到它。source block 会明确按仪表盘目录解析路径,可以避免这个歧义。仍可使用绝对路径或已注册的视图。

限制 ​

限制结果
每个 plot 12 条序列多出的序列不画,并有提示说明原本有多少条
每条序列 1,500 个点按分桶取平均压缩到上限以内
200 根柱子多出的柱子不画
饼图 8 个扇区最小的几个合并成一个"其他"
热力图 200 列、12 行多出的列和行不画
直方图 5–40 个箱约为行数的平方根,箱宽是 1、2、2.5 或 5 乘以 10 的整数次幂
地图 20,000 个点多出的点不画,并有提示

校验仪表盘 ​

ducklocal check 不开窗口就能校验文件。把仪表盘交给别人之前,先跑一遍:

bash
ducklocal check sales.dash
ducklocal check sales.dash --database warehouse.duckdb
ducklocal check sales.dash --summary

先做静态检查:语法、名字与引用、必填属性、每个 $name 是否有对应的 filter,以及用 DuckDB 自己的 parser 检查每个查询的 SQL。然后真正执行每个查询(只读,带着 source,什么都没选),核对每个 plot 用到的列是否确实是查询返回的列,包括 y、地图坐标和 size 是否为数值,以及每个 filter 的 column 是否是它那张图的查询返回的列。之所以要真正执行,是因为转换失败、通配符一个文件都没匹配到这类问题,要读到行时才会暴露。

  • 不带 --database 时在内存里执行,只读自己 source 文件的仪表盘这样就够了。读取了某张 source 没定义的表的查询,列在 unresolved 下而不算失败,它的 plot 也不核对列:它多半是写给某个数据库的。
  • 带 --database PATH 时,在该数据库上执行(只读打开)。查询读的是数据库里的表时,一定要带上它。

每个使用 filter 的查询,还会带一个选中值做绑定检查,不执行这个变体,以发现该查询中不存在的筛选列。有错的 block 不会阻止其余有效 block 继续检查;同一次运行会汇总规格错误和查询失败。

成功时输出 source、查询(附带结果列,以及各自返回的行数 rows)、filter 与 plot 的 JSON 摘要,外加 empty:执行了但一行都没返回的查询。这不算错误,但通常是正则、筛选条件或 join 什么都没匹配上,交出仪表盘之前逐个看一眼。--summary 在成功时只输出一行 JSON:数量统计,以及未解析和返回空结果的查询名。包含规格诊断时以状态 2 退出,一次列出所有问题,每条一行 文件:行号: 信息;仅有 SQL 失败、数据库或文件错误时以 1 退出。细节见 CLI 指南。

0.4.0 新增

ducklocal check 文件 --restricted --allow-read 路径 可以在受限条件下校验不是你写的仪表盘。见限制不是你写的 SQL。

常见信息 ​

信息解决
type is a string in quotes写 type = "line",而不是 type = line
Unknown plot type: "donut"; one of line, bar, area, scatter, pie, map, table, card, text使用列出的类型之一
plot block "p" requires y图表类型都需要 y;table、map、card 和 text 不需要
plot "p" draws query "q", which the file does not define补上这个 query block,或改正 query = query.… 里的名字
query names a query block: query = query.some_namequery 属性要写引用,不是字符串
plot "p" draws y = "channel" (VARCHAR), which is not numeric让 y 指向数值列,或改用 type = "table"
A map plot places points by lat and lng; it takes no x地图没有坐标轴:写 lat 和 lng,或者让列名来判断
width is how many of the dashboard's 12 columns the plot spans写 1 到 12 之间的整数
query "q" reads $chanel, but no filter block is named "chanel"改正名字,或补上这个 filter block
filter "f" picks on plot "p", a line; a value is picked on a bar or table plot让 filter 指向柱状图或表格
a dashboard query must be a single read-only statement写入操作放到 SQL 编辑器里做,或者给嵌套的 PIVOT 用 IN 写明取值;见 SQL 规则
Unknown block: "card"; a card is a plot: plot "x" { type = "card" … }每个面板都是 plot block,卡片和文字面板也一样
"minutes" is a SQL keyword, so as a column alias it needs AS写成 count(*) AS minutes,或者给别名加引号
Unterminated heredoc: no line is exactly SQL在单独一行写上结束分隔符

在你自己的编辑器里编辑 ​

ducklocal lsp 是 .dash 文件的 language server,提供:输入时即时诊断;block 类型、属性、图表类型以及 query.、plot. 后面名字的补全;悬停文档;从 query.name 或 plot.name 跳到对应 block 的定义跳转。Neovim 与 VS Code 的配置见 CLI 指南。

基于 Apache-2.0 许可开源发布。