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,49 @@
1
+ """CLI command for `dft search`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import typer
8
+
9
+ from dataface.agent_api.search import SearchResult, search_dashboards
10
+
11
+
12
+ def search_command(
13
+ query: str,
14
+ json_output: bool = False,
15
+ limit: int = 10,
16
+ project_dir: Path | None = None,
17
+ ) -> None:
18
+ """Search dashboards by keyword with ranked results."""
19
+ result = search_dashboards(
20
+ query=query,
21
+ project_dir=(project_dir or Path(".")).resolve(),
22
+ limit=limit,
23
+ )
24
+
25
+ if json_output:
26
+ typer.echo(result.model_dump_json(indent=2, exclude_none=True))
27
+ if not result.success:
28
+ raise typer.Exit(1)
29
+ return
30
+
31
+ _print_rich(result)
32
+ if not result.success:
33
+ raise typer.Exit(1)
34
+
35
+
36
+ def _print_rich(result: SearchResult) -> None:
37
+ if not result.success:
38
+ for e in result.errors:
39
+ typer.echo(f"Error: {e}", err=True)
40
+ return
41
+
42
+ if not result.results:
43
+ typer.echo("No results found.")
44
+ return
45
+
46
+ for hit in result.results:
47
+ typer.echo(f" [{hit.match_score:.2f}] {hit.title} {hit.source_path}")
48
+ if hit.summary:
49
+ typer.echo(f" {hit.summary}")
@@ -0,0 +1,114 @@
1
+ """Serve command implementation."""
2
+
3
+ import os
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ import typer
8
+ import uvicorn
9
+
10
+
11
+ def serve_command(
12
+ port: int | None = None,
13
+ host: str = "localhost",
14
+ project_dir: Path | None = None,
15
+ connection: str | None = None,
16
+ dialect: str | None = None,
17
+ target: str | None = None,
18
+ ) -> None:
19
+ """Start unified Dataface server.
20
+
21
+ This command starts a FastAPI server that serves:
22
+ - Profile templates at /profile/{template}/?model=X&column=Y
23
+ - Any face file at /{path}/?var=value (path.yml → /path/)
24
+ - Health check at /health
25
+
26
+ Example URLs:
27
+ http://localhost:9876/health
28
+ http://localhost:9876/profile/model/?model=fct_orders&theme=dark
29
+ http://localhost:9876/profile/numeric_column/?model=fct_orders&column=revenue
30
+ http://localhost:9876/sales/?region=West
31
+
32
+ Args:
33
+ port: Port number to serve on (auto-resolved if not specified)
34
+ host: Host address to bind to
35
+ project_dir: Project directory for resolving face file paths
36
+ connection: Database connection string
37
+ dialect: SQL dialect (auto-detected from dbt profile, or duckdb)
38
+ target: dbt target name (defaults to DBT_TARGET env var, then profile default)
39
+
40
+ Returns:
41
+ None (exits with code 0 on success, 1 on errors)
42
+ """
43
+ from dataface.core.project_roots import (
44
+ discover_project_root,
45
+ infer_dialect_from_dbt,
46
+ )
47
+ from dataface.core.serve.bootstrap import apply_default_theme_from_env
48
+ from dataface.core.serve.port import resolve_port
49
+ from dataface.core.serve.server import create_server
50
+
51
+ # Discover project root when not explicitly provided
52
+ resolved_dir = project_dir if project_dir else discover_project_root()
53
+
54
+ # Apply DFT_DEFAULT_THEME before anything else — fails fast on invalid names.
55
+ try:
56
+ apply_default_theme_from_env()
57
+ except ValueError as e:
58
+ typer.echo(f"Error: {e}", err=True)
59
+ raise typer.Exit(1) from None
60
+
61
+ # Resolve port: --port > DFT_PORT > dataface.yml > hash(project_dir)
62
+ port = resolve_port(
63
+ explicit_port=port,
64
+ project_dir=resolved_dir,
65
+ host=host,
66
+ )
67
+
68
+ # Resolve dbt target: CLI flag > DBT_TARGET env > profile default
69
+ effective_target = target or os.environ.get("DBT_TARGET") or None
70
+
71
+ # Infer dialect from dbt profile when not explicitly provided
72
+ effective_dialect = dialect
73
+ if effective_dialect is None:
74
+ inferred = infer_dialect_from_dbt(resolved_dir, effective_target)
75
+ if inferred:
76
+ typer.echo(f" Auto-detected dialect: {inferred} (from dbt profile)")
77
+ effective_dialect = inferred or "duckdb"
78
+
79
+ # Create FastAPI app
80
+ app = create_server(
81
+ project_dir=resolved_dir,
82
+ connection=connection,
83
+ dialect=effective_dialect,
84
+ target=effective_target,
85
+ )
86
+
87
+ # Start server
88
+ typer.echo("🚀 Starting dashboard server")
89
+ typer.echo(f" URL: http://{host}:{port}")
90
+ typer.echo(f" Health: http://{host}:{port}/health")
91
+ typer.echo("")
92
+ typer.echo(" Routes:")
93
+ typer.echo(f" http://{host}:{port}/profile/model/?model=<table>")
94
+ typer.echo(
95
+ f" http://{host}:{port}/profile/numeric_column/?model=<table>&column=<col>"
96
+ )
97
+ typer.echo(f" http://{host}:{port}/<name>/?var=value")
98
+ typer.echo("")
99
+ typer.echo(" Press CTRL+C to stop")
100
+
101
+ try:
102
+ uvicorn.run(
103
+ app,
104
+ host=host,
105
+ port=port,
106
+ log_level="info",
107
+ timeout_graceful_shutdown=3,
108
+ )
109
+ except KeyboardInterrupt:
110
+ typer.echo("\n👋 Server stopped")
111
+ sys.exit(0)
112
+ except Exception as e: # noqa: BLE001
113
+ typer.echo(f"Error starting server: {e}", err=True)
114
+ raise typer.Exit(1) from None
@@ -0,0 +1,133 @@
1
+ """Skills command — thin wrapper over dataface.agent_api.skills."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+
7
+ import typer
8
+ from rich.padding import Padding
9
+ from rich.table import Table
10
+
11
+ from dataface.agent_api import skills as _api
12
+ from dataface.cli._console import dft_console
13
+ from dataface.cli._json_output import print_json_result
14
+
15
+ err_console = dft_console(stderr=True)
16
+
17
+
18
+ def skills_command(
19
+ name: str | None = None,
20
+ search: str | None = None,
21
+ limit: int = 10,
22
+ as_json: bool = False,
23
+ ) -> None:
24
+ """List packaged agent skills, search them, or show one by name — selected from the input args."""
25
+ if name is not None and search is not None:
26
+ err_console.print(
27
+ "[red]Error:[/red] cannot combine a skill name with --search; use one or the other."
28
+ )
29
+ raise typer.Exit(1)
30
+
31
+ if search is not None:
32
+ result = _api.search_skills(search, limit=limit, surface="cli")
33
+ if as_json:
34
+ print_json_result(result)
35
+ return
36
+ _print_search_results(result)
37
+ return
38
+
39
+ if name is None:
40
+ listing = _api.list_skills(surface="cli")
41
+ if as_json:
42
+ print_json_result(listing)
43
+ return
44
+ _print_skills_table(listing)
45
+ return
46
+
47
+ try:
48
+ skill = _api.get_skill(name, surface="cli")
49
+ except _api.SkillNotFound as exc:
50
+ print(str(exc), file=sys.stderr)
51
+ sys.exit(1)
52
+ if as_json:
53
+ print_json_result(skill)
54
+ else:
55
+ print(skill.body)
56
+
57
+
58
+ def _print_skills_table(result: _api.SkillList) -> None:
59
+ if not result.skills:
60
+ typer.echo("No skills available.")
61
+ return
62
+
63
+ console = dft_console()
64
+
65
+ workflows = [s for s in result.skills if s.kind == "workflow"]
66
+ patterns = [s for s in result.skills if s.kind == "pattern"]
67
+
68
+ typer.echo(
69
+ "Agent skills — workflows and layout patterns for building Dataface dashboards."
70
+ )
71
+ typer.echo("YAML field reference is `dft docs`, not skills.\n")
72
+
73
+ name_width = max(len(s.name) for s in result.skills)
74
+
75
+ if workflows:
76
+ console.print("[bold]Workflows[/bold]")
77
+ console.print(Padding(_skill_table(workflows, name_width), (0, 0, 0, 2)))
78
+ console.print()
79
+
80
+ if patterns:
81
+ console.print("[bold]Patterns[/bold]")
82
+ console.print(Padding(_skill_table(patterns, name_width), (0, 0, 0, 2)))
83
+ console.print()
84
+
85
+ typer.echo("Next steps")
86
+ typer.echo(
87
+ " dft skills <name> Read the full skill body and copy the example YAML"
88
+ )
89
+ typer.echo(" dft docs YAML syntax reference")
90
+ typer.echo("")
91
+ typer.echo('Find a skill: dft skills -s "<query>"')
92
+ typer.echo('YAML syntax: dft docs -s "<query>"')
93
+
94
+
95
+ def _skill_table(skills: list[_api.Skill], name_width: int) -> Table:
96
+ t = Table(show_header=False, box=None, padding=(0, 2, 0, 0))
97
+ t.add_column("Name", style="bold", no_wrap=True, width=name_width)
98
+ t.add_column("Description", overflow="fold")
99
+ for skill in skills:
100
+ suffix = r" [dim]\[scaffold][/dim]" if skill.has_examples else ""
101
+ t.add_row(skill.name, f"{_first_sentence(skill.description)}{suffix}")
102
+ return t
103
+
104
+
105
+ def _print_search_results(result: _api.SkillSearchResult) -> None:
106
+ if not result.hits:
107
+ typer.echo(f'Agent skills — search results for "{result.query}"\n')
108
+ typer.echo(
109
+ 'No skills matched. Try `dft skills -s "kpi"` or `dft skills` (full list).'
110
+ )
111
+ return
112
+
113
+ console = dft_console()
114
+ typer.echo(f'Agent skills — search results for "{result.query}"\n')
115
+ for hit in result.hits:
116
+ scaffold = r" \[scaffold]" if hit.has_examples else ""
117
+ console.print(
118
+ f" [bold]{hit.name}[/bold] \\[{hit.kind}]{scaffold} "
119
+ f"[dim]score {hit.score:.2f}[/dim]"
120
+ )
121
+ console.print(f" {_first_sentence(hit.description)}")
122
+ typer.echo("")
123
+ typer.echo(
124
+ f"{len(result.hits)} matches. "
125
+ "Read one: `dft skills <name>` — copy the example YAML directly."
126
+ )
127
+
128
+
129
+ def _first_sentence(text: str) -> str:
130
+ """Collapse a YAML block-scalar description to a single sentence."""
131
+ flat = " ".join(text.split())
132
+ head, sep, _ = flat.partition(".")
133
+ return head + sep if sep else flat
@@ -0,0 +1,161 @@
1
+ """`dft init skills` thin wrapper: parse args, call agent_api.skill_install."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import typer
8
+
9
+ from dataface.agent_api import skill_install
10
+ from dataface.core.project_roots import find_dataface_project, find_git_root
11
+
12
+ _VALID_TARGETS = skill_install.SKILL_INSTALL_TARGETS
13
+
14
+
15
+ def _resolve_project_dir(start: Path) -> Path:
16
+ """Return the directory where skills should be installed.
17
+
18
+ Skills are agent tooling that belong at the git root, not the operational
19
+ project root. When a .git directory exists above the dataface.yml, install
20
+ at the git root so monorepos with nested dataface projects don't accumulate
21
+ divergent skill copies.
22
+ """
23
+ project_root = find_dataface_project(start)
24
+ if project_root is None:
25
+ project_root = start.resolve()
26
+ git_root = find_git_root(project_root)
27
+ if git_root is not None:
28
+ return git_root
29
+ return project_root
30
+
31
+
32
+ def _resolve_targets(
33
+ target: str | None,
34
+ *,
35
+ all_targets: bool,
36
+ dir_override: Path | None,
37
+ project_root: Path,
38
+ ) -> list[Path]:
39
+ if dir_override is not None:
40
+ return [dir_override.resolve()]
41
+
42
+ if target is not None:
43
+ key = target.lower()
44
+ if key not in _VALID_TARGETS:
45
+ typer.echo(
46
+ f"Unknown target: {target}. "
47
+ f"Supported: {', '.join(sorted(_VALID_TARGETS))}",
48
+ err=True,
49
+ )
50
+ raise typer.Exit(1)
51
+ return [skill_install.target_dir_for(key, project_root=project_root)]
52
+
53
+ detected = skill_install.detect_skill_targets(project_root)
54
+ if all_targets:
55
+ if not detected:
56
+ typer.echo(
57
+ "No agent skill targets detected (.cursor/, AGENTS.md, CLAUDE.md).\n"
58
+ "Pass an explicit target: dft init skills agents | claude",
59
+ err=True,
60
+ )
61
+ raise typer.Exit(1)
62
+ keys = detected
63
+ elif detected:
64
+ keys = detected
65
+ else:
66
+ typer.echo(
67
+ "No agent skill targets detected (.cursor/, AGENTS.md, CLAUDE.md).\n"
68
+ "Specify a target or pass --dir:\n"
69
+ " dft init skills agents\n"
70
+ " dft init skills claude\n"
71
+ " dft init skills --all\n"
72
+ " dft init skills --dir .agents/skills",
73
+ err=True,
74
+ )
75
+ raise typer.Exit(1)
76
+
77
+ return [skill_install.target_dir_for(k, project_root=project_root) for k in keys]
78
+
79
+
80
+ def _format_legacy_note(legacy_dirs: list[Path], project_root: Path) -> str | None:
81
+ if not legacy_dirs:
82
+ return None
83
+ first = legacy_dirs[0]
84
+ try:
85
+ rel = first.relative_to(project_root)
86
+ except ValueError:
87
+ rel = first
88
+ return (
89
+ f"Legacy skill install detected at {rel} — "
90
+ "re-run `dft init skills agents` and remove the legacy dir manually."
91
+ )
92
+
93
+
94
+ def run_init_skills(
95
+ target: str | None,
96
+ all_targets: bool,
97
+ dir_override: Path | None,
98
+ force: bool,
99
+ check: bool,
100
+ project_dir: Path | None = None,
101
+ ) -> None:
102
+ """Shared implementation for ``dft init skills``."""
103
+ if target is not None and (all_targets or dir_override is not None):
104
+ typer.echo(
105
+ "Specify one of: a target name, --all, or --dir — not combined.",
106
+ err=True,
107
+ )
108
+ raise typer.Exit(1)
109
+ if all_targets and dir_override is not None:
110
+ typer.echo("Specify either --all or --dir, not both.", err=True)
111
+ raise typer.Exit(1)
112
+
113
+ project_root = _resolve_project_dir(
114
+ project_dir if project_dir is not None else Path.cwd()
115
+ )
116
+ target_dirs = _resolve_targets(
117
+ target,
118
+ all_targets=all_targets,
119
+ dir_override=dir_override,
120
+ project_root=project_root,
121
+ )
122
+
123
+ prefix = "Would install" if check else "Installed"
124
+ any_legacy = False
125
+ for target_dir in target_dirs:
126
+ result = skill_install.install_skills(
127
+ target_dir=target_dir,
128
+ project_root=project_root,
129
+ force=force,
130
+ check=check,
131
+ )
132
+ try:
133
+ rel = target_dir.relative_to(project_root)
134
+ except ValueError:
135
+ rel = target_dir
136
+ if result.installed:
137
+ typer.echo(f" {prefix} workflow skills ({len(result.installed)}) → {rel}/")
138
+ for name in sorted(result.installed):
139
+ typer.echo(f" ✓ {name}")
140
+ elif result.skipped_existing:
141
+ typer.echo(
142
+ f" Workflow skills unchanged ({len(result.skipped_existing)})"
143
+ f" → {rel}/"
144
+ )
145
+ if result.retired_removed:
146
+ typer.echo(f" Removed retired: {', '.join(result.retired_removed)}")
147
+ if result.skipped_existing:
148
+ typer.echo(f" Unchanged (identical): {', '.join(result.skipped_existing)}")
149
+ legacy_note = _format_legacy_note(result.legacy_dirs_detected, project_root)
150
+ if legacy_note:
151
+ any_legacy = True
152
+ typer.echo(f" {legacy_note}")
153
+
154
+ if check:
155
+ typer.echo("")
156
+ typer.echo(" Dry run — no files written.")
157
+ elif any_legacy:
158
+ typer.echo("")
159
+ typer.echo(
160
+ " Re-run after removing legacy .cursor/skills or .codex/skills dirs."
161
+ )
@@ -0,0 +1,63 @@
1
+ """validate command — thin wrapper over dataface.agent_api.validate."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ import typer
9
+
10
+ from dataface.agent_api.validate import ValidateResult
11
+ from dataface.cli._console import dft_console
12
+ from dataface.cli._error_format import print_render_warnings, print_structured_errors
13
+
14
+ _console = dft_console()
15
+
16
+
17
+ def validate_command(
18
+ paths: list[Path] | None,
19
+ project_dir: Path | None = None,
20
+ json_output: bool = False,
21
+ strict: bool = False,
22
+ ) -> None:
23
+ from dataface.agent_api.validate import validate_paths
24
+
25
+ results = validate_paths(paths, project_dir=project_dir)
26
+ _emit(results, json_output=json_output, strict=strict)
27
+
28
+
29
+ def _emit(
30
+ results: list[ValidateResult],
31
+ *,
32
+ json_output: bool,
33
+ strict: bool,
34
+ ) -> None:
35
+ if json_output:
36
+ if len(results) == 1:
37
+ typer.echo(results[0].model_dump_json(exclude_none=True, indent=2))
38
+ else:
39
+ typer.echo(
40
+ json.dumps(
41
+ [r.model_dump(mode="json", exclude_none=True) for r in results],
42
+ indent=2,
43
+ )
44
+ )
45
+ has_errors = any(r.errors for r in results)
46
+ raise typer.Exit(0 if not has_errors else 1)
47
+
48
+ has_errors = False
49
+ has_warnings = False
50
+ for r in results:
51
+ if r.errors:
52
+ has_errors = True
53
+ print_structured_errors(r.errors)
54
+ if r.warnings:
55
+ has_warnings = True
56
+ print_render_warnings(r.warnings, path=r.path)
57
+ if not r.errors and not r.warnings:
58
+ _console.print(f"✓ {r.path} OK")
59
+
60
+ if has_errors:
61
+ raise typer.Exit(1)
62
+ if strict and has_warnings:
63
+ raise typer.Exit(1)