dataface 0.1.2__py3-none-any.whl

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.
Files changed (455) hide show
  1. d3_format/__init__.py +14 -0
  2. d3_format/errors.py +19 -0
  3. d3_format/format.py +551 -0
  4. d3_format/spec.py +159 -0
  5. dataface/DATAFACE_SYNTAX.md +1135 -0
  6. dataface/__init__.py +93 -0
  7. dataface/_docs_site.py +20 -0
  8. dataface/_install_hint.py +26 -0
  9. dataface/agent_api/__init__.py +79 -0
  10. dataface/agent_api/_init_templates/__init__.py +0 -0
  11. dataface/agent_api/_init_templates/agents_dft_snippet.md +26 -0
  12. dataface/agent_api/_init_templates/dataface.yml +15 -0
  13. dataface/agent_api/_init_templates/faces-dataface.yml +144 -0
  14. dataface/agent_api/_init_templates/index.md +24 -0
  15. dataface/agent_api/_paths.py +118 -0
  16. dataface/agent_api/_project_agents_md.py +43 -0
  17. dataface/agent_api/_session_store.py +486 -0
  18. dataface/agent_api/_state.py +28 -0
  19. dataface/agent_api/chat.py +221 -0
  20. dataface/agent_api/dashboards.py +257 -0
  21. dataface/agent_api/describe.py +366 -0
  22. dataface/agent_api/describe_query.py +120 -0
  23. dataface/agent_api/docs/__init__.py +25 -0
  24. dataface/agent_api/docs/_loader.py +292 -0
  25. dataface/agent_api/docs/yaml-reference.md +2757 -0
  26. dataface/agent_api/file_refs.py +118 -0
  27. dataface/agent_api/init.py +126 -0
  28. dataface/agent_api/inspect.py +128 -0
  29. dataface/agent_api/mcp_install.py +170 -0
  30. dataface/agent_api/query.py +274 -0
  31. dataface/agent_api/schema.py +658 -0
  32. dataface/agent_api/schema_search.py +284 -0
  33. dataface/agent_api/search.py +270 -0
  34. dataface/agent_api/skill_install.py +141 -0
  35. dataface/agent_api/skill_render.py +90 -0
  36. dataface/agent_api/skills.py +293 -0
  37. dataface/agent_api/surface_aliases.yaml +128 -0
  38. dataface/agent_api/validate.py +175 -0
  39. dataface/agent_api/validate_query.py +84 -0
  40. dataface/ai/__init__.py +39 -0
  41. dataface/ai/agent.py +139 -0
  42. dataface/ai/context.py +45 -0
  43. dataface/ai/events.py +62 -0
  44. dataface/ai/external_mcp.py +610 -0
  45. dataface/ai/generate_sql.py +96 -0
  46. dataface/ai/llm.py +403 -0
  47. dataface/ai/mcp/__init__.py +51 -0
  48. dataface/ai/mcp/server.py +289 -0
  49. dataface/ai/memories.py +85 -0
  50. dataface/ai/prompts.py +177 -0
  51. dataface/ai/schema_context.py +138 -0
  52. dataface/ai/skills/before-after-comparison/SKILL.md +102 -0
  53. dataface/ai/skills/before-after-comparison/examples/before-after-comparison.yml +24 -0
  54. dataface/ai/skills/dashboard-build/SKILL.md +212 -0
  55. dataface/ai/skills/dashboard-build/examples/_smoke.yml +15 -0
  56. dataface/ai/skills/dashboard-design/SKILL.md +182 -0
  57. dataface/ai/skills/dashboard-review/SKILL.md +113 -0
  58. dataface/ai/skills/dashboard-structural-review/SKILL.md +173 -0
  59. dataface/ai/skills/dashboard-visual-review/SKILL.md +139 -0
  60. dataface/ai/skills/dataface-mcp-setup/SKILL.md +177 -0
  61. dataface/ai/skills/dataface-troubleshooting/SKILL.md +225 -0
  62. dataface/ai/skills/drill-down-link/SKILL.md +112 -0
  63. dataface/ai/skills/drill-down-link/examples/drill-down-link.yml +27 -0
  64. dataface/ai/skills/faceted-small-multiples/SKILL.md +116 -0
  65. dataface/ai/skills/faceted-small-multiples/examples/faceted-small-multiples.yml +33 -0
  66. dataface/ai/skills/filter-bar-with-variables/SKILL.md +105 -0
  67. dataface/ai/skills/filter-bar-with-variables/examples/filter-bar-with-variables.yml +49 -0
  68. dataface/ai/skills/kpi-row/SKILL.md +101 -0
  69. dataface/ai/skills/kpi-row/examples/kpi-row.yml +55 -0
  70. dataface/ai/skills/report-design/SKILL.md +184 -0
  71. dataface/ai/skills/single-metric-bignum/SKILL.md +90 -0
  72. dataface/ai/skills/single-metric-bignum/examples/single-metric-bignum.yml +27 -0
  73. dataface/ai/skills/table-heavy-ops-dashboard/SKILL.md +114 -0
  74. dataface/ai/skills/table-heavy-ops-dashboard/examples/table-heavy-ops-dashboard.yml +48 -0
  75. dataface/ai/skills/time-series-trend/SKILL.md +93 -0
  76. dataface/ai/skills/time-series-trend/examples/time-series-trend.yml +26 -0
  77. dataface/ai/skills/top-n-with-detail/SKILL.md +98 -0
  78. dataface/ai/skills/top-n-with-detail/examples/top-n-with-detail.yml +45 -0
  79. dataface/ai/skills/two-by-two-grid-overview/SKILL.md +78 -0
  80. dataface/ai/skills/two-by-two-grid-overview/examples/two-by-two-grid-overview.yml +64 -0
  81. dataface/ai/tool_schemas.py +132 -0
  82. dataface/ai/tools/__init__.py +312 -0
  83. dataface/ai/yaml_utils.py +57 -0
  84. dataface/cli/__init__.py +3 -0
  85. dataface/cli/_console.py +48 -0
  86. dataface/cli/_error_format.py +83 -0
  87. dataface/cli/_extras.py +190 -0
  88. dataface/cli/_json_output.py +8 -0
  89. dataface/cli/_parsing.py +17 -0
  90. dataface/cli/_version_info.py +56 -0
  91. dataface/cli/commands/__init__.py +3 -0
  92. dataface/cli/commands/_agent_input.py +205 -0
  93. dataface/cli/commands/_agent_server.py +115 -0
  94. dataface/cli/commands/chat.py +645 -0
  95. dataface/cli/commands/describe.py +107 -0
  96. dataface/cli/commands/docs.py +131 -0
  97. dataface/cli/commands/extension.py +179 -0
  98. dataface/cli/commands/init.py +240 -0
  99. dataface/cli/commands/inspect.py +94 -0
  100. dataface/cli/commands/mcp_init.py +167 -0
  101. dataface/cli/commands/query.py +386 -0
  102. dataface/cli/commands/render.py +291 -0
  103. dataface/cli/commands/schema.py +411 -0
  104. dataface/cli/commands/search.py +49 -0
  105. dataface/cli/commands/serve.py +114 -0
  106. dataface/cli/commands/skills.py +133 -0
  107. dataface/cli/commands/skills_init.py +161 -0
  108. dataface/cli/commands/validate.py +63 -0
  109. dataface/cli/main.py +1501 -0
  110. dataface/core/__init__.py +75 -0
  111. dataface/core/compile/__init__.py +244 -0
  112. dataface/core/compile/_jinja_helpers.py +78 -0
  113. dataface/core/compile/channel.py +222 -0
  114. dataface/core/compile/chart_focus.py +101 -0
  115. dataface/core/compile/chart_resolved.py +169 -0
  116. dataface/core/compile/chart_type_detection.py +489 -0
  117. dataface/core/compile/chart_update.py +261 -0
  118. dataface/core/compile/colors.py +64 -0
  119. dataface/core/compile/compiler.py +904 -0
  120. dataface/core/compile/config.py +823 -0
  121. dataface/core/compile/custom_chart_types.py +208 -0
  122. dataface/core/compile/data_table_attachment.py +1287 -0
  123. dataface/core/compile/detect.py +110 -0
  124. dataface/core/compile/errors.py +302 -0
  125. dataface/core/compile/filter_injection.py +319 -0
  126. dataface/core/compile/introspection.py +527 -0
  127. dataface/core/compile/jinja.py +511 -0
  128. dataface/core/compile/labels_env.py +52 -0
  129. dataface/core/compile/markdown.py +154 -0
  130. dataface/core/compile/meta.py +388 -0
  131. dataface/core/compile/models/__init__.py +0 -0
  132. dataface/core/compile/models/chart/__init__.py +0 -0
  133. dataface/core/compile/models/chart/authored.py +2137 -0
  134. dataface/core/compile/models/chart/compiled.py +398 -0
  135. dataface/core/compile/models/config.py +347 -0
  136. dataface/core/compile/models/face/__init__.py +0 -0
  137. dataface/core/compile/models/face/authored.py +659 -0
  138. dataface/core/compile/models/face/compiled.py +522 -0
  139. dataface/core/compile/models/factories.py +201 -0
  140. dataface/core/compile/models/markers.py +40 -0
  141. dataface/core/compile/models/palette.py +36 -0
  142. dataface/core/compile/models/primitives.py +415 -0
  143. dataface/core/compile/models/query/__init__.py +0 -0
  144. dataface/core/compile/models/query/authored.py +246 -0
  145. dataface/core/compile/models/query/compiled.py +710 -0
  146. dataface/core/compile/models/refs.py +137 -0
  147. dataface/core/compile/models/source.py +611 -0
  148. dataface/core/compile/models/style/__init__.py +0 -0
  149. dataface/core/compile/models/style/authored.py +481 -0
  150. dataface/core/compile/models/style/compiled.py +3399 -0
  151. dataface/core/compile/models/style/merged.py +1682 -0
  152. dataface/core/compile/models/theme.py +362 -0
  153. dataface/core/compile/models/variable/__init__.py +0 -0
  154. dataface/core/compile/models/variable/authored.py +254 -0
  155. dataface/core/compile/models/vega_lite/__init__.py +0 -0
  156. dataface/core/compile/models/vega_lite/config.py +510 -0
  157. dataface/core/compile/models/vega_lite/contracts.py +171 -0
  158. dataface/core/compile/normalize_charts.py +494 -0
  159. dataface/core/compile/normalize_layout.py +1000 -0
  160. dataface/core/compile/normalize_queries.py +297 -0
  161. dataface/core/compile/normalize_variables.py +489 -0
  162. dataface/core/compile/normalizer.py +543 -0
  163. dataface/core/compile/palette.py +1100 -0
  164. dataface/core/compile/parameterized.py +658 -0
  165. dataface/core/compile/parser.py +228 -0
  166. dataface/core/compile/schema.py +20 -0
  167. dataface/core/compile/schema_renderers/__init__.py +0 -0
  168. dataface/core/compile/schema_renderers/json_schema.py +163 -0
  169. dataface/core/compile/schema_renderers/prompt.py +152 -0
  170. dataface/core/compile/schema_renderers/vscode_schema.py +301 -0
  171. dataface/core/compile/sizing.py +2126 -0
  172. dataface/core/compile/sources.py +518 -0
  173. dataface/core/compile/sql_authoring_lint.py +56 -0
  174. dataface/core/compile/style_cascade.py +471 -0
  175. dataface/core/compile/typography.py +299 -0
  176. dataface/core/compile/validator.py +301 -0
  177. dataface/core/compile/variables.py +53 -0
  178. dataface/core/compile/vega_config.py +98 -0
  179. dataface/core/compile/vega_lite/__init__.py +6 -0
  180. dataface/core/compile/vega_lite/validation.py +95 -0
  181. dataface/core/compile/yaml_error_formatter.py +838 -0
  182. dataface/core/connections.py +38 -0
  183. dataface/core/dashboard.py +358 -0
  184. dataface/core/defaults/default_config.yml +101 -0
  185. dataface/core/defaults/palettes/categorical/category-10-dark.yml +32 -0
  186. dataface/core/defaults/palettes/categorical/category-10-light.yml +43 -0
  187. dataface/core/defaults/palettes/categorical/category-10.yml +31 -0
  188. dataface/core/defaults/palettes/categorical/category-6-tonal-blue.yml +22 -0
  189. dataface/core/defaults/palettes/categorical/category-6-tonal-brown.yml +29 -0
  190. dataface/core/defaults/palettes/categorical/category-6-tonal-green.yml +20 -0
  191. dataface/core/defaults/palettes/categorical/category-6-tonal-orange.yml +21 -0
  192. dataface/core/defaults/palettes/categorical/category-6-tonal-purple.yml +20 -0
  193. dataface/core/defaults/palettes/categorical/editorial-10-dark.yml +32 -0
  194. dataface/core/defaults/palettes/categorical/editorial-10.yml +40 -0
  195. dataface/core/defaults/palettes/categorical/hero-6.yml +17 -0
  196. dataface/core/defaults/palettes/categorical/single-blue.yml +11 -0
  197. dataface/core/defaults/palettes/categorical/tableau.yml +20 -0
  198. dataface/core/defaults/palettes/data/xkcd_colors.json +3803 -0
  199. dataface/core/defaults/palettes/diverging/blue-red.yml +25 -0
  200. dataface/core/defaults/palettes/diverging/coolwarm.yml +24 -0
  201. dataface/core/defaults/palettes/diverging/crimson-green.yml +23 -0
  202. dataface/core/defaults/palettes/diverging/orange-teal.yml +23 -0
  203. dataface/core/defaults/palettes/diverging/sunset.yml +24 -0
  204. dataface/core/defaults/palettes/scaffold/dft-creams.yml +38 -0
  205. dataface/core/defaults/palettes/scaffold/dft-grays.yml +53 -0
  206. dataface/core/defaults/palettes/sequential/amber.yml +22 -0
  207. dataface/core/defaults/palettes/sequential/blue.yml +22 -0
  208. dataface/core/defaults/palettes/sequential/brown.yml +22 -0
  209. dataface/core/defaults/palettes/sequential/gray.yml +22 -0
  210. dataface/core/defaults/palettes/sequential/green.yml +22 -0
  211. dataface/core/defaults/palettes/sequential/purple.yml +22 -0
  212. dataface/core/defaults/palettes/sequential/rust.yml +22 -0
  213. dataface/core/defaults/palettes/sequential/teal.yml +22 -0
  214. dataface/core/defaults/palettes/tone/negative.yml +32 -0
  215. dataface/core/defaults/palettes/tone/positive.yml +22 -0
  216. dataface/core/defaults/palettes/tone/warning.yml +22 -0
  217. dataface/core/defaults/themes/_base.yaml +786 -0
  218. dataface/core/defaults/themes/bi.yaml +16 -0
  219. dataface/core/defaults/themes/carbong100.yaml +41 -0
  220. dataface/core/defaults/themes/cream.yaml +122 -0
  221. dataface/core/defaults/themes/dark.yaml +40 -0
  222. dataface/core/defaults/themes/diagnostics-title-angle-extreme.yaml +9 -0
  223. dataface/core/defaults/themes/diagnostics-title-baseline-extreme.yaml +9 -0
  224. dataface/core/defaults/themes/diagnostics-title-baseline.yaml +24 -0
  225. dataface/core/defaults/themes/diagnostics-title-center.yaml +8 -0
  226. dataface/core/defaults/themes/diagnostics-title-color-extreme.yaml +24 -0
  227. dataface/core/defaults/themes/diagnostics-title-font-extreme.yaml +25 -0
  228. dataface/core/defaults/themes/diagnostics-title-left.yaml +8 -0
  229. dataface/core/defaults/themes/diagnostics-title-offset-extreme.yaml +9 -0
  230. dataface/core/defaults/themes/diagnostics-title-size-extreme.yaml +24 -0
  231. dataface/core/defaults/themes/diagnostics-title-weight-extreme.yaml +24 -0
  232. dataface/core/defaults/themes/editorial.yaml +147 -0
  233. dataface/core/defaults/themes/light.yaml +30 -0
  234. dataface/core/defaults/themes/looker.yaml +17 -0
  235. dataface/core/defaults/themes/stark.yaml +134 -0
  236. dataface/core/errors/__init__.py +67 -0
  237. dataface/core/errors/codes_compile.py +56 -0
  238. dataface/core/errors/codes_execute.py +177 -0
  239. dataface/core/errors/codes_render.py +106 -0
  240. dataface/core/errors/codes_unknown.py +15 -0
  241. dataface/core/errors/hints.py +74 -0
  242. dataface/core/errors/registry.py +42 -0
  243. dataface/core/errors/structured.py +92 -0
  244. dataface/core/execute/__init__.py +91 -0
  245. dataface/core/execute/adapters/__init__.py +49 -0
  246. dataface/core/execute/adapters/adapter_registry.py +400 -0
  247. dataface/core/execute/adapters/base.py +245 -0
  248. dataface/core/execute/adapters/csv_adapter.py +239 -0
  249. dataface/core/execute/adapters/dbt_adapter.py +283 -0
  250. dataface/core/execute/adapters/dbt_adapter_factory.py +212 -0
  251. dataface/core/execute/adapters/dbt_macro_loader.py +95 -0
  252. dataface/core/execute/adapters/dbt_utils.py +150 -0
  253. dataface/core/execute/adapters/http_adapter.py +224 -0
  254. dataface/core/execute/adapters/metricflow_adapter.py +94 -0
  255. dataface/core/execute/adapters/schema_resolver_adapter.py +144 -0
  256. dataface/core/execute/adapters/sql_adapter.py +710 -0
  257. dataface/core/execute/adapters/values_adapter.py +58 -0
  258. dataface/core/execute/batch.py +744 -0
  259. dataface/core/execute/cache_backend.py +135 -0
  260. dataface/core/execute/cache_keys.py +66 -0
  261. dataface/core/execute/dbt_jinja.py +21 -0
  262. dataface/core/execute/dialects/__init__.py +121 -0
  263. dataface/core/execute/dialects/athena.py +75 -0
  264. dataface/core/execute/dialects/base.py +302 -0
  265. dataface/core/execute/dialects/bigquery.py +38 -0
  266. dataface/core/execute/dialects/databricks.py +68 -0
  267. dataface/core/execute/dialects/duckdb.py +35 -0
  268. dataface/core/execute/dialects/mysql.py +68 -0
  269. dataface/core/execute/dialects/postgres.py +39 -0
  270. dataface/core/execute/dialects/redshift.py +12 -0
  271. dataface/core/execute/dialects/snowflake.py +51 -0
  272. dataface/core/execute/dialects/sqlserver.py +92 -0
  273. dataface/core/execute/duckdb_cache.py +712 -0
  274. dataface/core/execute/duckdb_config.py +26 -0
  275. dataface/core/execute/errors.py +213 -0
  276. dataface/core/execute/executor.py +1249 -0
  277. dataface/core/execute/parallel.py +162 -0
  278. dataface/core/execute/setup_sql.py +58 -0
  279. dataface/core/execute/source_registry.py +72 -0
  280. dataface/core/execute/source_resolver.py +255 -0
  281. dataface/core/execute/sql_guard.py +387 -0
  282. dataface/core/execute/sql_literals.py +199 -0
  283. dataface/core/fonts.py +52 -0
  284. dataface/core/inspect/__init__.py +32 -0
  285. dataface/core/inspect/cache_factory.py +98 -0
  286. dataface/core/inspect/db_types.py +162 -0
  287. dataface/core/inspect/dbt_schema.py +96 -0
  288. dataface/core/inspect/defaults.yml +37 -0
  289. dataface/core/inspect/fanout_risk.py +109 -0
  290. dataface/core/inspect/manifest_utils.py +77 -0
  291. dataface/core/inspect/partials/categorical.yml +40 -0
  292. dataface/core/inspect/partials/date.yml +40 -0
  293. dataface/core/inspect/partials/numeric.yml +55 -0
  294. dataface/core/inspect/partition_types.py +38 -0
  295. dataface/core/inspect/query_validator.py +975 -0
  296. dataface/core/inspect/renderer.py +354 -0
  297. dataface/core/inspect/resolver.py +808 -0
  298. dataface/core/inspect/search.py +461 -0
  299. dataface/core/inspect/sources/__init__.py +32 -0
  300. dataface/core/inspect/sources/dbt.py +738 -0
  301. dataface/core/inspect/sources/duckdb_utils.py +66 -0
  302. dataface/core/inspect/templates/__init__.py +1 -0
  303. dataface/core/inspect/templates/categorical_column.yml +196 -0
  304. dataface/core/inspect/templates/charts.yml +109 -0
  305. dataface/core/inspect/templates/date_column.yml +248 -0
  306. dataface/core/inspect/templates/model.yml +138 -0
  307. dataface/core/inspect/templates/numeric_column.yml +261 -0
  308. dataface/core/inspect/templates/quality.yml +80 -0
  309. dataface/core/inspect/templates/string_column.yml +263 -0
  310. dataface/core/project_roots.py +165 -0
  311. dataface/core/render/__init__.py +87 -0
  312. dataface/core/render/board_links.py +176 -0
  313. dataface/core/render/chart/__init__.py +27 -0
  314. dataface/core/render/chart/arc_attached_table.py +251 -0
  315. dataface/core/render/chart/artifacts.py +16 -0
  316. dataface/core/render/chart/callout.py +225 -0
  317. dataface/core/render/chart/decisions.py +358 -0
  318. dataface/core/render/chart/geo.py +700 -0
  319. dataface/core/render/chart/kpi.py +916 -0
  320. dataface/core/render/chart/labels.py +76 -0
  321. dataface/core/render/chart/pipeline.py +818 -0
  322. dataface/core/render/chart/presentation.py +36 -0
  323. dataface/core/render/chart/profile.py +3438 -0
  324. dataface/core/render/chart/render_single.py +347 -0
  325. dataface/core/render/chart/renderers.py +193 -0
  326. dataface/core/render/chart/rendering.py +565 -0
  327. dataface/core/render/chart/serialization.py +90 -0
  328. dataface/core/render/chart/spark.py +496 -0
  329. dataface/core/render/chart/spark_bar.py +370 -0
  330. dataface/core/render/chart/spec_builders.py +154 -0
  331. dataface/core/render/chart/standard_renderer.py +2645 -0
  332. dataface/core/render/chart/table.py +2957 -0
  333. dataface/core/render/chart/table_support.py +1452 -0
  334. dataface/core/render/chart/tick_values.py +66 -0
  335. dataface/core/render/chart/time_unit_detect.py +809 -0
  336. dataface/core/render/chart/title_overflow.py +157 -0
  337. dataface/core/render/chart/type_inference.py +122 -0
  338. dataface/core/render/chart/validation.py +99 -0
  339. dataface/core/render/chart/vega_lite.py +125 -0
  340. dataface/core/render/chart/vega_lite_types.py +268 -0
  341. dataface/core/render/chart/vl_field_maps.py +346 -0
  342. dataface/core/render/chart_interactivity.py +24 -0
  343. dataface/core/render/control_registry.py +287 -0
  344. dataface/core/render/converters/__init__.py +24 -0
  345. dataface/core/render/converters/chart.py +276 -0
  346. dataface/core/render/converters/html.py +98 -0
  347. dataface/core/render/converters/pdf.py +40 -0
  348. dataface/core/render/converters/png.py +41 -0
  349. dataface/core/render/errors.py +144 -0
  350. dataface/core/render/face_api.py +160 -0
  351. dataface/core/render/faces.py +1194 -0
  352. dataface/core/render/font_measurement.py +48 -0
  353. dataface/core/render/font_support.py +197 -0
  354. dataface/core/render/fonts/DFTSansTabular-Regular.ttf +0 -0
  355. dataface/core/render/fonts/DFTSansTabular-Regular.woff2 +0 -0
  356. dataface/core/render/fonts/DFTSerifOldstyleProportional-Regular.ttf +0 -0
  357. dataface/core/render/fonts/DFTSerifOldstyleTabular-Regular.ttf +0 -0
  358. dataface/core/render/fonts/InterVariable.ttf +0 -0
  359. dataface/core/render/fonts/InterVariable.woff2 +0 -0
  360. dataface/core/render/fonts/NOTO_COLOR_EMOJI_LICENSE.txt +93 -0
  361. dataface/core/render/fonts/NOTO_EMOJI_LICENSE.txt +93 -0
  362. dataface/core/render/fonts/NotoColorEmoji-Regular.ttf +0 -0
  363. dataface/core/render/fonts/NotoColorEmoji-Regular.woff2 +0 -0
  364. dataface/core/render/fonts/NotoEmoji-Regular.ttf +0 -0
  365. dataface/core/render/fonts/NotoEmoji-Regular.woff2 +0 -0
  366. dataface/core/render/fonts/SOURCE_CODE_PRO_LICENSE.txt +93 -0
  367. dataface/core/render/fonts/SOURCE_SERIF_4_LICENSE.txt +98 -0
  368. dataface/core/render/fonts/SourceCodePro-Regular.ttf +0 -0
  369. dataface/core/render/fonts/SourceSerif4-Regular.ttf +0 -0
  370. dataface/core/render/fonts/_emoji_font_face.css +43 -0
  371. dataface/core/render/fonts/source-serif-4-variable-latin.woff2 +0 -0
  372. dataface/core/render/format_utils.py +329 -0
  373. dataface/core/render/geo_defaults.yml +28 -0
  374. dataface/core/render/json_format.py +146 -0
  375. dataface/core/render/layout_sizing.py +865 -0
  376. dataface/core/render/layouts.py +541 -0
  377. dataface/core/render/markdown_defaults.yml +16 -0
  378. dataface/core/render/missing_vars_prompt.py +79 -0
  379. dataface/core/render/placeholder.py +389 -0
  380. dataface/core/render/render_result.py +14 -0
  381. dataface/core/render/renderer.py +467 -0
  382. dataface/core/render/script_embedding.py +16 -0
  383. dataface/core/render/svg_utils.py +212 -0
  384. dataface/core/render/template_loader.py +69 -0
  385. dataface/core/render/templates/controls/_styles.css +606 -0
  386. dataface/core/render/templates/controls/checkbox.html +16 -0
  387. dataface/core/render/templates/controls/date.html +16 -0
  388. dataface/core/render/templates/controls/number.html +19 -0
  389. dataface/core/render/templates/controls/readonly.html +9 -0
  390. dataface/core/render/templates/controls/select.html +21 -0
  391. dataface/core/render/templates/controls/slider.html +22 -0
  392. dataface/core/render/templates/controls/text.html +16 -0
  393. dataface/core/render/templates/scripts/chart_interactivity.js +191 -0
  394. dataface/core/render/templates/scripts/variables.js +976 -0
  395. dataface/core/render/templates/svg/grid_pattern.svg +3 -0
  396. dataface/core/render/templates/svg/styles.css +51 -0
  397. dataface/core/render/terminal.py +311 -0
  398. dataface/core/render/terminal_charts.py +563 -0
  399. dataface/core/render/terminal_defaults.yml +2 -0
  400. dataface/core/render/terminal_layouts.py +299 -0
  401. dataface/core/render/terminal_text.py +31 -0
  402. dataface/core/render/text/__init__.py +1 -0
  403. dataface/core/render/text/case.py +113 -0
  404. dataface/core/render/text_format.py +129 -0
  405. dataface/core/render/utils.py +106 -0
  406. dataface/core/render/variable_controls.py +946 -0
  407. dataface/core/render/variable_input_refinement.py +140 -0
  408. dataface/core/render/warnings/__init__.py +15 -0
  409. dataface/core/render/warnings/bar_color_1_to_1_with_x.py +80 -0
  410. dataface/core/render/warnings/base.py +44 -0
  411. dataface/core/render/warnings/fanout_risk.py +15 -0
  412. dataface/core/render/warnings/from_query_diagnostic.py +56 -0
  413. dataface/core/render/warnings/missing_join_predicate.py +13 -0
  414. dataface/core/render/warnings/query_parse_error.py +14 -0
  415. dataface/core/render/warnings/query_returned_zero_rows.py +42 -0
  416. dataface/core/render/warnings/reaggregation.py +14 -0
  417. dataface/core/render/warnings/registry.py +45 -0
  418. dataface/core/render/warnings/suppression.py +46 -0
  419. dataface/core/render/warnings/temporal_single_point.py +63 -0
  420. dataface/core/render/warnings/unreferenced_chart.py +15 -0
  421. dataface/core/render/warnings/y_encoding_mostly_null.py +76 -0
  422. dataface/core/render/yaml_format.py +167 -0
  423. dataface/core/resolve_face.py +195 -0
  424. dataface/core/schema/__init__.py +0 -0
  425. dataface/core/schema/guidance.py +151 -0
  426. dataface/core/scoped_paths.py +59 -0
  427. dataface/core/serve/__init__.py +14 -0
  428. dataface/core/serve/bootstrap.py +39 -0
  429. dataface/core/serve/embedded.py +57 -0
  430. dataface/core/serve/port.py +129 -0
  431. dataface/core/serve/server.py +938 -0
  432. dataface/core/serve/templates/__init__.py +0 -0
  433. dataface/core/serve/templates/directory.yml +6 -0
  434. dataface/core/serve/templates/error.html.j2 +217 -0
  435. dataface/core/utils.py +121 -0
  436. dataface/core/validate.py +64 -0
  437. dataface/integrations/__init__.py +0 -0
  438. dataface/integrations/highlighting.py +351 -0
  439. dataface/integrations/markdown.py +537 -0
  440. dataface/py.typed +0 -0
  441. dataface-0.1.2.dist-info/METADATA +375 -0
  442. dataface-0.1.2.dist-info/RECORD +455 -0
  443. dataface-0.1.2.dist-info/WHEEL +4 -0
  444. dataface-0.1.2.dist-info/entry_points.txt +2 -0
  445. dataface-0.1.2.dist-info/licenses/LICENSE +202 -0
  446. mdsvg/__init__.py +168 -0
  447. mdsvg/fonts.py +656 -0
  448. mdsvg/images.py +299 -0
  449. mdsvg/parser.py +629 -0
  450. mdsvg/playground.py +284 -0
  451. mdsvg/py.typed +2 -0
  452. mdsvg/renderer.py +1623 -0
  453. mdsvg/style.py +355 -0
  454. mdsvg/types.py +200 -0
  455. mdsvg/utils.py +86 -0
@@ -0,0 +1,102 @@
1
+ ---
2
+ name: before-after-comparison
3
+ kind: pattern
4
+ description: >
5
+ Pattern for placing two metric series on the same axis to compare baseline
6
+ vs current, plan vs actual, or A vs B. Use when the question is "how does
7
+ current compare to baseline?" for the same set of categories or time periods.
8
+ Triggers on: 'compare', 'baseline vs current', 'plan vs actual', 'before
9
+ after', 'A/B', 'target vs actual', 'layered bars'. Both series share the same
10
+ x-axis encoding. Do NOT use for time-series trend (use time-series-trend).
11
+ Do NOT use for more than 3 series (chart becomes unreadable).
12
+ metadata:
13
+ author: fivetran
14
+ ---
15
+
16
+ # Before-After Comparison
17
+
18
+ Two series rendered on the same bar or line chart, sharing the x-axis, so the
19
+ viewer can compare baseline vs current or plan vs actual across categories or
20
+ time periods. Use `y: [series_a, series_b]` for layered bars, or `color:` for
21
+ grouped multi-series.
22
+
23
+ ## When to reach for this
24
+
25
+ - "How does Q4 compare to Q3 for each region?"
26
+ - "Are we above or below plan by category?"
27
+ - The comparison dimension is the same set of x values for both series
28
+
29
+ ## When NOT to use this
30
+
31
+ - Trend over time on a single metric → `time-series-trend`
32
+ - More than 3 series → too cluttered; split into separate charts
33
+ - Composition (parts of a whole) → stacked area in `faceted-small-multiples`
34
+
35
+ ## The pattern
36
+
37
+ ```yaml
38
+ queries:
39
+ plan_vs_actual:
40
+ sql: |
41
+ SELECT category,
42
+ SUM(actual_revenue) AS actual,
43
+ SUM(planned_revenue) AS plan
44
+ FROM budget
45
+ GROUP BY category
46
+ ORDER BY actual DESC
47
+
48
+ charts:
49
+ comparison:
50
+ type: bar
51
+ query: plan_vs_actual
52
+ x: category
53
+ y: [actual, plan] # two series, same x
54
+ title: Actual vs Plan by Category
55
+ ```
56
+
57
+ For time-based comparisons, use two separate columns in one query and map with
58
+ `color:` instead:
59
+
60
+ ```yaml
61
+ queries:
62
+ by_month:
63
+ columns: [month, current, prior]
64
+ values: ...
65
+
66
+ charts:
67
+ trend_compare:
68
+ type: line
69
+ query: by_month
70
+ x: month
71
+ y: [current, prior]
72
+ title: Current vs Prior Period
73
+ ```
74
+
75
+ See `examples/before-after-comparison.yml` for the inline-data worked example.
76
+
77
+ ## Variations
78
+
79
+ | Variation | YAML knob | When |
80
+ |---|---|---|
81
+ | Layered bars | `y: [a, b]` | Side-by-side bars per category |
82
+ | Two line series | `y: [a, b]` on `type: line` | Time-based trend comparison |
83
+ | Multiple categories | keep `x:` categorical | Natural for department/region comparisons |
84
+ | Delta column | add a computed delta in SQL | Explicitly show the gap |
85
+
86
+ ## Common pitfalls
87
+
88
+ | Pitfall | Why it breaks | Fix |
89
+ |---|---|---|
90
+ | Series on different scales | Misleading visual; one series dwarfs the other | Use matching units or a secondary axis |
91
+ | Too many series (`y: [a, b, c, d]`) | Legend and bars both unreadable | Cap at 2–3 series; combine the rest |
92
+ | Different x-values per series | Chart gaps or misaligned bars | Ensure both series have a value for every x |
93
+ | Swapped series order | Baseline visually dominates current | Put the primary/current series first in `y:` |
94
+
95
+ ## Worked example
96
+
97
+ See `examples/before-after-comparison.yml` — four departments comparing
98
+ current vs baseline spend. Inline data, no warehouse needed.
99
+
100
+ ## YAML Reference
101
+
102
+ For syntax and field details: {{ s_yaml_reference_footer }}
@@ -0,0 +1,24 @@
1
+ title: Q4 Budget vs Actual
2
+ description: Before-after comparison pattern — two series (current and baseline) on the same bar chart
3
+
4
+ queries:
5
+ budget_vs_actual:
6
+ description: Current quarter spend vs prior-quarter baseline by department
7
+ columns: [department, current, baseline]
8
+ values:
9
+ - [Engineering, 420000, 380000]
10
+ - [Marketing, 185000, 210000]
11
+ - [Sales, 340000, 295000]
12
+ - [Support, 95000, 105000]
13
+
14
+ charts:
15
+ comparison_bar:
16
+ description: Current vs baseline spend — bars sit side by side per department
17
+ type: bar
18
+ query: budget_vs_actual
19
+ x: department
20
+ y: [current, baseline]
21
+ title: Q4 Actual vs Prior Quarter by Department
22
+
23
+ rows:
24
+ - comparison_bar
@@ -0,0 +1,212 @@
1
+ ---
2
+ name: dashboard-build
3
+ kind: workflow
4
+ description: >
5
+ Core workflow for building dashboards and reports with Dataface. Use when
6
+ creating new dashboards, editing existing YAML, adding charts, writing
7
+ queries, iterating on layout, or when the user says 'build a dashboard',
8
+ 'create a report', 'add a chart', 'write a query'. Covers the
9
+ build-test-iterate cycle, parameterized queries, caching, and incremental
10
+ development. Do NOT use for diagnosing errors after something breaks (use
11
+ dataface-troubleshooting). Do NOT use for design decisions about
12
+ chart types or layout patterns (use dashboard-design or
13
+ report-design).
14
+ metadata:
15
+ author: fivetran
16
+ ---
17
+
18
+ # Building Dataface Dashboards
19
+
20
+ Build dashboards and reports **incrementally** — one chart at a time, validating at every step. Never one-shot an entire dashboard.
21
+
22
+ ## Companion Skills
23
+
24
+ - **`{{ s_skill_design_dashboard }}`** — chart-type, layout, and color decisions
25
+ - **`{{ s_skill_design_report }}`** — narrative reports (text + charts)
26
+ - **`{{ s_skill_review }}`** — self-review checklist run at delivery
27
+ - **`{{ s_skill_troubleshooting }}`** — when something breaks
28
+
29
+ {{ s_docs_discovery }}
30
+
31
+ ## Metadata Requirement
32
+
33
+ Fill `description` on every named object — agents downstream rely on it for context and search:
34
+
35
+ - `queries.*.description` — what the query returns and why it exists
36
+ - `charts.*.description` — what question the chart answers
37
+ - `variables.*.description` — how the variable should be used
38
+ - Layout objects (`rows`/`cols`/`grid.items`/`tabs.items`) — section intent when useful
39
+
40
+ Keep each description short and factual (one sentence).
41
+
42
+ ## The Workflow
43
+
44
+ ### Step 1 — Explore the Data
45
+
46
+ {{ s_explore_schema_first }}
47
+
48
+ Use `{{ s_execute_query }}` to verify cardinality and sample values before writing any YAML:
49
+
50
+ ```sql
51
+ SELECT status, COUNT(*) FROM orders GROUP BY status;
52
+ ```
53
+
54
+ **Do not skip this step.** Writing SQL against assumed column names is the #1 source of errors.
55
+
56
+ ### Step 2 — Build One Chart
57
+
58
+ Write a minimal YAML with a single query and chart:
59
+
60
+ ```yaml
61
+ source: my_profile
62
+ queries:
63
+ revenue:
64
+ sql: SELECT date, SUM(amount) AS total FROM orders GROUP BY date ORDER BY date
65
+ charts:
66
+ revenue_trend:
67
+ query: revenue
68
+ type: line
69
+ x: date
70
+ y: total
71
+ rows:
72
+ - revenue_trend
73
+ ```
74
+
75
+ Then verify with two calls:
76
+
77
+ 1. `{{ s_execute_query }}` — confirm the data shape and columns
78
+ 2. `{{ s_render_dashboard }}` — compile + render to see the visual
79
+
80
+ ### Step 3 — Iterate on That Chart
81
+
82
+ Adjust SQL, chart type, labels, colors. {{ s_render_to_verify }}
83
+
84
+ ### Step 4 — Add the Next Chart
85
+
86
+ Repeat steps 2–3 for each additional chart. One at a time.
87
+
88
+ ### Step 5 — Compose the Layout
89
+
90
+ Once all charts work individually, arrange them in `rows:` / `cols:`. The layout is the easy part — getting the queries right is the hard part.
91
+
92
+ ```yaml
93
+ rows:
94
+ - cols: [kpi_revenue, kpi_users, kpi_orders]
95
+ - cols: [revenue_trend, 2] # 2 = column span
96
+ - cols: [by_region, by_product]
97
+ ```
98
+
99
+ ### Step 6 — Save the Final YAML
100
+
101
+ Write the YAML under `faces/` with your normal file-edit mechanism, then:
102
+
103
+ {{ s_validate_example }}
104
+
105
+ Fix any errors `{{ s_validate_dashboard }}` reports, then re-run until it passes.
106
+
107
+ ## Parameterized Queries — Use From the Start
108
+
109
+ Use `{{ variable_name }}` syntax for any configurable values, even during exploration:
110
+
111
+ ```sql
112
+ SELECT * FROM orders WHERE region = '{{ region }}' AND date >= '{{ start_date }}'
113
+ ```
114
+
115
+ Pass concrete values via the `variables` parameter when testing. When the query moves into dashboard YAML it works identically — and results are already cached.
116
+
117
+ **If you hardcode values during exploration and switch to variables later, the cache won't help because the SQL template changed.**
118
+
119
+ ## Validate Early and Often
120
+
121
+ | Tool | When | What It Catches |
122
+ |------|------|-----------------|
123
+ | `{{ s_execute_query }}` | While drafting raw SQL | SQL errors, missing tables, wrong column names |
124
+ | `{{ s_validate_dashboard }}` | After every YAML edit | YAML schema errors, unknown fields, broken chart/query/layout references |
125
+ | `{{ s_query_face }}` | After saving YAML with named queries | Actual named-query columns and sample rows |
126
+ | `{{ s_render_dashboard }}` | Before delivery | Query execution, render errors, layout issues, misleading visuals |
127
+
128
+ These tools are fast. Use them after every change, not just at the end.
129
+
130
+ ## Self-Review Before Delivering
131
+
132
+ Run the review skill as the final step before declaring the face done:
133
+
134
+ - {{ s_review_pointer }}
135
+
136
+ It orchestrates structural ({{ s_validate_dashboard }}) and visual (PNG + vision) passes and returns a ranked findings list. Fix each `blocker`, then re-run the review. Rendering and validation are cached — the loop is cheap.
137
+
138
+ ## Data Requirements Per Chart Type
139
+
140
+ Each chart type expects data in a specific shape. Dataface validates this and errors fast — no silent magic.
141
+
142
+ | Chart Type | Expected Data | Key Fields |
143
+ |------------|---------------|------------|
144
+ | `kpi` | **Exactly 1 row** with a value column | `value` (column reference) |
145
+ | `line` | Multiple rows, temporal x + numeric y | `x`, `y` |
146
+ | `bar` | One row per category, numeric y | `x`, `y` — categorical x auto-flips to horizontal; override with `style.orientation: vertical` only when needed |
147
+ | `area` | Multiple rows, temporal x + numeric y | `x`, `y` |
148
+ | `scatter` | Multiple rows, both x and y numeric | `x`, `y` |
149
+ | `pie` | One row per segment (pre-aggregated) | `theta` (numeric) + `color` (category) |
150
+ | `heatmap` | One row per cell (pre-aggregated) | `x`, `y`, `color` |
151
+ | `table` | Any number of rows and columns | Use `style.columns` for per-column formatting: `- column: amount` / `format: currency_whole` / `label: Amount` / `align: right` / `width: 120` |
152
+
153
+ **Critical rules:**
154
+
155
+ - **KPI charts require exactly 1 row.** Aggregate to a single row: `SELECT SUM(amount) AS total FROM orders`. Multiple KPIs need separate single-row queries (or one query with multiple columns).
156
+ - **Pie and heatmap expect pre-aggregated data.** Use `GROUP BY` in the query. Dataface does NOT aggregate for you.
157
+
158
+ ## Formatting Numbers
159
+
160
+ Dataface auto-detects number format from column name and DB type — omit `format:` when auto-detection would produce the correct result (e.g. a column named `conversion_rate` already renders as a percentage).
161
+
162
+ When you do need an explicit format, use a named preset instead of a raw D3 string:
163
+
164
+ | Preset | Example output |
165
+ |--------|---------------|
166
+ | `integer` | `1,234` |
167
+ | `currency_whole` | `$1,234` |
168
+ | `currency_compact` | `$1.2M` |
169
+ | `percent` | `2.3%` — input is decimal fraction (0.023) |
170
+ | `percent_number` | `2.3%` — input is whole-number percent (2.3) |
171
+ | `percent_delta` | `+2.3%` — input is decimal fraction |
172
+ | `percent_number_delta` | `+2.3%` — input is whole-number (2.3) |
173
+
174
+ Full preset list and auto-detection rules: see `dashboard-structural-review`.
175
+
176
+ ## Common Mistakes
177
+
178
+ | Mistake | Fix |
179
+ |---------|-----|
180
+ | Building the entire dashboard in one pass | Build one chart at a time |
181
+ | Writing SQL without checking column names | Use `{{ s_schema }}`, `{{ s_schema_search }}`, or `{{ s_execute_query }}` first |
182
+ | Hardcoding values during exploration | Use `{{ variables }}` from the start so the query cache survives |
183
+ | Skipping validation between changes | `{{ s_validate_dashboard }}` after every YAML edit |
184
+ | Too many charts (>8) | Split into multiple dashboards |
185
+ | Using chart-type names as keys in layout | `table:` or `bar:` as keys cause parse errors — use descriptive names like `revenue_table` |
186
+ | Referencing query names in layout | Layout references charts, not queries — create a chart that wraps the query |
187
+ | KPI query returns multiple rows | Use `SUM()`/`COUNT()`/`AVG()` to aggregate to 1 row |
188
+ | Pie with raw unaggregated data | `GROUP BY` in query to aggregate before charting |
189
+ | Putting `height:` or `aspect_ratio:` under `style:` | Move them to chart root: `charts.my_chart.height: 400` — `style:` is paint only |
190
+ | Writing raw D3 format strings (`"$,.2s"`, `".1%"`) | Use a named preset (`currency_compact`, `percent`) — clearer and theme-consistent |
191
+ | Setting `format:` when auto-detection already matches | Omit `format:` for columns like `*_pct`, `*_rate`, `*revenue*` — auto handles them |
192
+ | Nesting `columns`/`values` under `sql:` for inline data | `sql:` is a string — inline data lives directly under the query name: `queries.my_data.columns: [...]` and `queries.my_data.values: [[...]]` |
193
+ | Filtering a multiselect with `{{ plans \| map('tojson') \| join(', ') }}` | `tojson` produces double-quoted strings that SQL treats as column refs — silent empty result, `dft render` exits 0. Use `{{ filter('plan', plans) }}` instead. |
194
+
195
+ ## Rationalizations to Resist
196
+
197
+ | Excuse | Reality |
198
+ |--------|---------|
199
+ | "User asked for the whole dashboard at once" | Build incrementally anyway. Iterate to the result, don't one-shot it. |
200
+ | "I know what the columns are called" | You don't. Check with `{{ s_schema }}`, `{{ s_schema_search }}`, or `{{ s_execute_query }}`. |
201
+ | "Validation is slow, I'll do it at the end" | Validation is instant. Skipping it costs more time debugging later. |
202
+ | "It's just a quick chart, no need for variables" | Variables cost nothing and enable caching. Use them. |
203
+ | "User wants 15 charts on one dashboard" | 8 max. Propose splitting into focused dashboards. |
204
+
205
+ ## Red Flags — STOP
206
+
207
+ - About to write SQL without having explored the actual schema first
208
+ - Building more than one new chart before validating the previous one
209
+ - Dashboard has more than 8 visualizations
210
+ - A chart exists without a clear question it answers
211
+ - Using hardcoded values that should be variables
212
+ - Delivering without running the self-review checklist
@@ -0,0 +1,15 @@
1
+ title: Smoke Test Dashboard
2
+ queries:
3
+ revenue:
4
+ columns: [month, amount]
5
+ values:
6
+ - [Jan, 1000]
7
+ - [Feb, 1200]
8
+ - [Mar, 900]
9
+ charts:
10
+ kpi:
11
+ query: revenue
12
+ type: kpi
13
+ value: amount
14
+ rows:
15
+ - kpi
@@ -0,0 +1,182 @@
1
+ ---
2
+ name: dashboard-design
3
+ kind: workflow
4
+ description: >
5
+ Design principles for Dataface dashboards — at-a-glance monitoring displays.
6
+ Use when choosing chart types, designing dashboard layouts, picking colors,
7
+ deciding dashboard vs report, or when the user says 'what chart type',
8
+ 'how should I lay this out', 'dashboard design'. Covers information
9
+ hierarchy, chart selection, layout patterns, and visual design. Do NOT
10
+ use for reports or narrative analyses (use report-design). Do
11
+ NOT use for the build-test-iterate workflow (use
12
+ dashboard-build). Do NOT use for fixing errors (use
13
+ dataface-troubleshooting).
14
+ metadata:
15
+ author: fivetran
16
+ ---
17
+
18
+ # Dashboard Design
19
+
20
+ Design dashboards for **at-a-glance monitoring** — clear, scannable, action-oriented displays.
21
+
22
+ > Implementing in YAML? Switch to `{{ s_skill_build }}` for the build-test-iterate workflow.
23
+
24
+ ## Dashboard vs. Report
25
+
26
+ | | Dashboard | Report |
27
+ |--|-----------|--------|
28
+ | **Purpose** | Monitor status at a glance | Answer a question, tell a story |
29
+ | **Frequency** | Daily / continuous | One-time or periodic |
30
+ | **Text** | Minimal — titles and labels | Extensive — narrative + recommendations |
31
+ | **Charts** | Primary content | Supporting evidence for the narrative |
32
+ | **Interaction** | Scan in seconds | Read in minutes |
33
+
34
+ If the user needs a narrative analysis, use the `report-design` skill instead.
35
+
36
+ ## Core Principles
37
+
38
+ 1. **Single screen** — no scrolling. If it doesn't fit, split into multiple dashboards.
39
+ 2. **Scannable in seconds** — a glance should convey status.
40
+ 3. **Every element earns its place** — remove anything that doesn't aid understanding.
41
+ 4. **Context for every number** — a number without comparison (trend, target, prior period) is meaningless.
42
+ 5. **Purpose-driven** — every chart answers a specific question.
43
+
44
+ ## Metadata Requirement
45
+
46
+ Whenever you output or edit Dataface YAML, add `description` metadata:
47
+
48
+ - `queries.*.description` for query intent
49
+ - `charts.*.description` for chart intent
50
+ - `variables.*.description` for filter semantics
51
+ - Layout object `description` fields (`rows`/`cols`/`grid.items`/`tabs.items`) when they add context
52
+
53
+ Descriptions should be concise and optimized for AI retrieval.
54
+
55
+ ## Thinking Process
56
+
57
+ Before designing, work through these in order:
58
+
59
+ ### 1. Audience & Purpose
60
+
61
+ - **Who** views this? (executive, analyst, operator)
62
+ - **When** do they view it? (morning standup, weekly review, always-on monitor)
63
+ - **What decision** does this inform?
64
+
65
+ ### 2. Information Hierarchy
66
+
67
+ - What is the **#1 most important metric**? → top-left, largest
68
+ - What are the **3-5 supporting KPIs**? → KPI row
69
+ - What **trends** matter? → line/area charts
70
+ - What **comparisons** are useful? → bar charts
71
+ - What **detail** supports drill-down? → tables at the bottom
72
+
73
+ ### 3. Chart Selection
74
+
75
+ | Question | Chart Type | Notes |
76
+ |----------|-----------|-------|
77
+ | Current value? | `kpi` | Big number, prominent position |
78
+ | Trending over time? | `line` | Continuous data, time on x-axis |
79
+ | Category comparison? | `bar` | Categorical x-axis, include zero baseline |
80
+ | Composition over time? | `area` (stacked) | Parts of a whole changing |
81
+ | Portion of whole? | `pie` | **Only for 2-3 segments** — use bar for more |
82
+ | Relationship? | `scatter` | Two numeric variables |
83
+ | Precise values? | `table` | When exact numbers matter |
84
+
85
+ **Avoid:**
86
+ - Pie for >3 categories (humans compare angles poorly — use bar)
87
+ - Multiple chart types for visual variety (consistency aids scanning)
88
+ - Scrolling layouts (if it scrolls, it's a report)
89
+
90
+ ### 4. Layout — Inverted Pyramid
91
+
92
+ Most important information first, following Western reading pattern (top-left → bottom-right):
93
+
94
+ ```
95
+ ┌──────────────────────────────────────────────┐
96
+ │ KPI 1 │ KPI 2 │ KPI 3 │ KPI 4 │ ← HEADLINE
97
+ ├──────────────────────────────────────────────┤
98
+ │ Primary trend chart (line/area) │ ← STORY
99
+ ├──────────────────────┬───────────────────────┤
100
+ │ Comparison chart │ Comparison chart │ ← CONTEXT
101
+ ├──────────────────────┴───────────────────────┤
102
+ │ Detail table (if needed) │ ← EVIDENCE
103
+ └──────────────────────────────────────────────┘
104
+ ```
105
+
106
+ - **4-8 total visualizations maximum**
107
+ - Related metrics grouped by proximity
108
+ - Consistent styling throughout
109
+
110
+ ### 5. Color & Visual Design
111
+
112
+ - **Gray is default** — muted everything, ONE accent color for emphasis
113
+ - **Color carries meaning** — same color = same meaning everywhere
114
+ - **Direct labels** on charts when possible, not separate legends
115
+ - **Maximize data-ink ratio** — every pixel should represent data
116
+
117
+ ### 6. Numeric Display
118
+
119
+ Apply standard numeric-display judgment (two-numeral rule, no false precision, tabular numerals in columns). Dataface-specific defaults:
120
+
121
+ - **Use format aliases, not raw d3 specs.** `format: currency_whole`, `format: percent`, `format: percent_delta`, `format: integer`, `format: compact`. Raw d3 (`"$,.0f"`) only when no alias fits.
122
+ - **Drop cents above $10.** `format: currency_whole` is the dashboard default. Reserve `format: currency` (with cents) for reconciliation surfaces — billing, financial statements.
123
+ - **Notation family: analytic for chrome, narrative for prose.** Analytic (`$2.5 M`, space, uppercase K/M/B) for axes, KPIs, tables, tooltips. Narrative (`$2.5mn`, no space, lowercase) only for text cards, titles, annotations. Independent of theme choice.
124
+ - **Zero strips trailing decimals even when siblings have them.** A `$0` KPI uses `currency_whole` even if its partner uses `currency`. A `0%` KPI uses `percent_whole` even if its partner uses `.2%`. The rule generalizes to any unit.
125
+ - **Percent precision is a group decision.** Default by magnitude: ≥20% → `.0%`; 1–20% → `.1%`; <1% → `.2%`. **Modulate by surface:** a single KPI or short rail can afford one more decimal; a long table column or axis wants the simpler form. **Override:** when tenths carry signal regardless of magnitude (A/B rates, churn, conversion in a tight range), use `format: ".2%"`.
126
+ - **Compaction is a group decision, not per-value.** Compact when ≥4 similar-magnitude values exceed 10,000, or when surface density demands it. Adjacent surfaces showing the same metric may compact differently — a reconciliation table can show full precision while the headline KPI uses `currency_compact`.
127
+ - **NULL renders as `—` (em-dash), never `0`.** Different claims about the data.
128
+ - **Tables anchor the currency symbol.** Default `symbol_mode: anchors` — first row carries the `$`, rows below don't.
129
+
130
+ ### 7. Variables & Interactivity
131
+
132
+ Add variables when the dashboard serves different time windows or segments:
133
+
134
+ - Date ranges → `daterange` input
135
+ - Categorical filters → `select` input
136
+ - Set sensible defaults (last 30 days, most common category)
137
+ - **NOT for executive dashboards** — those show the single most important view
138
+
139
+ ## Dashboard Patterns
140
+
141
+ ### Executive Dashboard
142
+ 4-6 KPIs → main trend → 2-3 breakdowns. No interaction. Show the single most important view.
143
+
144
+ ### Operational Dashboard
145
+ Status KPIs with alerts → recent activity → issues table. Emphasize what needs attention now.
146
+
147
+ ### Analytical Dashboard
148
+ KPIs → trends with filters → comparisons across dimensions → detail table. Include `variables:` for interactive filtering.
149
+
150
+ ## Quality Checklist
151
+
152
+ Before delivering:
153
+
154
+ - [ ] Fits on one screen (≤8 visualizations, no scrolling)
155
+ - [ ] KPIs are first and most prominent (top row)
156
+ - [ ] Every number has context (comparison, trend, or target)
157
+ - [ ] Chart types match the analytical question
158
+ - [ ] Related metrics grouped by proximity
159
+ - [ ] Color is purposeful, not decorative
160
+ - [ ] Titles are informative ("Revenue by Region, Last 30 Days" not "Chart 1")
161
+ - [ ] User's most important question answered at a glance
162
+ - [ ] Query/chart/variable/layout `description` metadata is filled for AI context
163
+
164
+ ## Common Mistakes
165
+
166
+ | Mistake | Fix |
167
+ |---------|-----|
168
+ | Too many charts (>8) | Split into multiple focused dashboards |
169
+ | KPIs without context | Add comparison period, trend, or target |
170
+ | Pie chart with 7 segments | Use a bar chart instead |
171
+ | Decorative color | Color must encode data or meaning |
172
+ | Generic titles | Titles should state what the chart answers |
173
+ | Scrolling dashboard | Reduce charts or split dashboards |
174
+
175
+ ## Rationalizations to Resist
176
+
177
+ | Excuse | Reality |
178
+ |--------|---------|
179
+ | "User asked for 12 metrics on one page" | 8 max. Propose splitting into focused dashboards. |
180
+ | "Pie chart is fine for 6 categories" | It's not — humans compare angles poorly. Use bar. |
181
+ | "The legend explains the colors" | Direct labels are always better than legends. |
182
+ | "More charts = more value" | More charts = more noise. Each must earn its place. |
@@ -0,0 +1,113 @@
1
+ ---
2
+ name: dashboard-review
3
+ kind: workflow
4
+ description: >
5
+ Primary entry point for reviewing a Dataface face. Runs
6
+ `dashboard-structural-review` first, then runs `dashboard-visual-review` when
7
+ the user asked for visual review or when structural surfaced ambiguous
8
+ "feels wrong" findings. Synthesizes both passes into a single ranked,
9
+ deduplicated findings list. Use when asked to 'review this dashboard',
10
+ 'review this face', 'is this dashboard good', or after building / editing a
11
+ face before delivery. Do NOT use when only one pass is needed — invoke the
12
+ leaf skill directly. Do NOT use for comparing two versions of a face (use
13
+ the looker-compare-diff pattern).
14
+ metadata:
15
+ author: fivetran
16
+ ---
17
+
18
+ # Dashboard Review
19
+
20
+ Orchestrate `dashboard-structural-review` and `dashboard-visual-review` into a
21
+ single ranked findings list. This is the default entry point — most "review"
22
+ requests should land here, not on either leaf skill.
23
+
24
+ ## When to use
25
+
26
+ - Any open-ended "review this dashboard" request
27
+ - Pre-delivery final pass
28
+ - After a substantive edit (new chart, new query, layout change)
29
+
30
+ ## When NOT to use
31
+
32
+ - The user explicitly asked for only structural OR only visual — invoke that
33
+ leaf skill directly
34
+ - Comparing two versions of a face — use the `looker-compare-diff` pattern
35
+
36
+ ## Protocol
37
+
38
+ ### Step 1: Always run structural review
39
+
40
+ Invoke `dashboard-structural-review` first. It's cheap (no rendering, no
41
+ vision tokens) and catches every problem that's visible in the YAML.
42
+
43
+ ### Step 2: Decide whether to run visual review
44
+
45
+ Run `dashboard-visual-review` when **any** of the following is true:
46
+
47
+ - The user asked for visual review explicitly ("how does it look", "review the
48
+ rendered output", "visual review", "check the design")
49
+ - Structural review surfaced a finding that hints at a visual problem (KPI
50
+ precision, generic title that may collide with axis labels, dense chart
51
+ count that may crowd the canvas)
52
+ - Structural review returned `**No findings.**` and the user asked for a
53
+ thorough review — do the visual pass to confirm
54
+
55
+ Skip visual review when:
56
+
57
+ - Structural review surfaced `blocker` findings — fix those first; rendering
58
+ may be misleading until the YAML is correct
59
+ - The user asked for a quick / cheap review
60
+ - `{{ s_validate_dashboard }}` failed inside
61
+ structural review — there's nothing valid to render yet
62
+
63
+ ### Step 3: Synthesize
64
+
65
+ Merge findings from both passes into a single list:
66
+
67
+ 1. **Deduplicate.** If both passes surface the same issue (e.g. structural
68
+ notes the KPI has no format spec and visual notes the rendered number is
69
+ `1247392.74`), keep the more concrete finding.
70
+ 2. **Order by severity.** All `blocker` findings before `warning` before
71
+ `nit`. Within a severity, keep the order each pass produced.
72
+ 3. **Cap at 5** by default. If you have to drop findings, drop `nit` first,
73
+ then `warning`. Never drop a `blocker`.
74
+ 4. **Tag the source** so the user knows which pass surfaced what.
75
+
76
+ ## Output format
77
+
78
+ ```markdown
79
+ **Dashboard review**
80
+
81
+ - `blocker` [structural] `charts.revenue_kpi`: query returns 12 rows but
82
+ `type: kpi` requires 1. Aggregate with `SUM()`.
83
+ - `warning` [visual] `charts.regions_bar`: x-axis labels overlap at rendered
84
+ width. Rotate to -45° or shorten.
85
+ - `warning` [structural] `queries.orders`: no `description:` field. Add one
86
+ sentence stating intent.
87
+ - `nit` [visual] `charts.users_pie`: muted palette would reduce visual noise.
88
+
89
+ **Passes run:** structural, visual
90
+ **Skipped:** —
91
+ ```
92
+
93
+ If neither pass found anything: emit `**Dashboard review: no findings.**`.
94
+
95
+ If a pass was skipped, name it in `**Skipped:**` with a one-phrase reason
96
+ (`Skipped: visual — blockers in structural pass`).
97
+
98
+ ## Common mistakes
99
+
100
+ | Mistake | Fix |
101
+ |---|---|
102
+ | Running visual review before structural | Always structural first. Visual depends on a render that won't be meaningful until the YAML is valid. |
103
+ | Skipping structural because "it's just visual" | The YAML is the source of truth — never skip structural. |
104
+ | Padding the synthesized list to fill a cap | If both passes agreed there are only 2 findings, return 2. |
105
+ | Forgetting to tag pass source | The user needs `[structural]` / `[visual]` to know where to look. |
106
+
107
+ ## Rationalizations to resist
108
+
109
+ | Excuse | Reality |
110
+ |---|---|
111
+ | "User said 'review' — they probably only want a quick check" | Default to thorough. The user can ask for cheap explicitly. |
112
+ | "Visual review is slow, let's skip it" | If a render is fast on this face (most are), do both passes by default. Cost is a real concern only at scale. |
113
+ | "Both passes are saying the same thing, I'll keep both" | Dedupe. Showing the same issue twice erodes trust. |