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,8 @@
1
+ """Shared JSON output helper for dft verbs."""
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ def print_json_result(result: BaseModel) -> None:
7
+ """Write result as JSON to stdout — stable wire contract for all dft verbs."""
8
+ print(result.model_dump_json(exclude_none=True, indent=2))
@@ -0,0 +1,17 @@
1
+ """Parse repeatable `--flag key=value` options into a dict."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterable
6
+
7
+ import typer
8
+
9
+
10
+ def parse_kv_pairs(items: Iterable[str], flag: str) -> dict[str, str]:
11
+ out: dict[str, str] = {}
12
+ for item in items:
13
+ if "=" not in item:
14
+ raise typer.BadParameter(f"{flag} expects key=value, got: {item!r}")
15
+ k, v = item.split("=", 1)
16
+ out[k] = v
17
+ return out
@@ -0,0 +1,56 @@
1
+ """Compose the `dft --version` line: version, install path, Python, editable flag."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import platform
7
+ import sys
8
+ from dataclasses import dataclass
9
+ from importlib import metadata
10
+ from pathlib import Path
11
+
12
+ import dataface
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class VersionInfo:
17
+ version: str
18
+ package_dir: Path
19
+ python_version: str
20
+ python_executable: str
21
+ editable: bool
22
+
23
+ def render(self) -> str:
24
+ editable = " (editable)" if self.editable else ""
25
+ return (
26
+ f"dft {self.version} from {self.package_dir}{editable} "
27
+ f"(Python {self.python_version}, {self.python_executable})"
28
+ )
29
+
30
+
31
+ def _read_direct_url_json() -> str | None:
32
+ try:
33
+ return metadata.distribution("dataface").read_text("direct_url.json")
34
+ except metadata.PackageNotFoundError:
35
+ return None
36
+
37
+
38
+ def _is_editable() -> bool:
39
+ raw = _read_direct_url_json()
40
+ if not raw:
41
+ return False
42
+ try:
43
+ data = json.loads(raw)
44
+ except json.JSONDecodeError:
45
+ return False
46
+ return bool(data.get("dir_info", {}).get("editable"))
47
+
48
+
49
+ def collect() -> VersionInfo:
50
+ return VersionInfo(
51
+ version=dataface.__version__,
52
+ package_dir=Path(dataface.__file__).parent,
53
+ python_version=platform.python_version(),
54
+ python_executable=sys.executable,
55
+ editable=_is_editable(),
56
+ )
@@ -0,0 +1,3 @@
1
+ """CLI command implementations."""
2
+
3
+ __all__ = []
@@ -0,0 +1,205 @@
1
+ """Input layer abstraction for dft chat.
2
+
3
+ Two concrete implementations:
4
+ - PromptToolkitInput: pinned prompt bar, multi-line edit, autocompletion,
5
+ arrow-key history. Used when stdin is a real TTY and TERM is not dumb.
6
+ - StdlibInput: wraps typer.prompt + readline history. Fallback for piped
7
+ stdin, dumb terminals, and environments where prompt_toolkit is unavailable.
8
+
9
+ select_input_layer() picks the right layer at runtime.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import os
15
+ import sys
16
+ from pathlib import Path
17
+ from typing import Any, Protocol, runtime_checkable
18
+
19
+ import typer
20
+
21
+
22
+ @runtime_checkable
23
+ class InputLayer(Protocol):
24
+ """One method: read a prompt, return a string. EOFError on Ctrl+D."""
25
+
26
+ def read(self, prompt: str) -> str:
27
+ """Read one user turn.
28
+
29
+ Args:
30
+ prompt: The prompt string to display.
31
+
32
+ Returns:
33
+ The user's input as a string.
34
+
35
+ Raises:
36
+ EOFError: When the user presses Ctrl+D (end of input).
37
+ """
38
+ ...
39
+
40
+
41
+ class StdlibInput:
42
+ """Stdlib input: typer.prompt + readline history file."""
43
+
44
+ def __init__(self, hist_path: Path) -> None:
45
+ self._hist_path = hist_path
46
+ self._setup_readline()
47
+
48
+ def _setup_readline(self) -> None:
49
+ try:
50
+ import readline # noqa: PLC0415 # type: ignore[import]
51
+
52
+ self._hist_path.parent.mkdir(parents=True, exist_ok=True)
53
+ if self._hist_path.exists():
54
+ readline.read_history_file(str(self._hist_path))
55
+ readline.set_history_length(1000)
56
+ import atexit # noqa: PLC0415
57
+
58
+ atexit.register(readline.write_history_file, str(self._hist_path))
59
+ except (ImportError, OSError):
60
+ pass
61
+
62
+ def read(self, prompt: str) -> str:
63
+ return typer.prompt(prompt.rstrip(": "))
64
+
65
+
66
+ class DftCompleter:
67
+ """Completer for dft chat: slash commands and @-path completions."""
68
+
69
+ def __init__(self, slash_commands: set[str]) -> None:
70
+ self._slash_commands = sorted(slash_commands)
71
+
72
+ def get_completions(self, document: Any, complete_event: Any) -> list[Any]:
73
+ from prompt_toolkit.completion import Completion # noqa: PLC0415
74
+
75
+ # document is a prompt_toolkit Document
76
+ text: str = document.text_before_cursor
77
+
78
+ # Slash-command completions: only when buffer starts with /
79
+ if text.startswith("/"):
80
+ typed_cmd = text[1:] # strip leading /
81
+ completions = []
82
+ for cmd in self._slash_commands:
83
+ if cmd.startswith(typed_cmd):
84
+ # Completion.text is what gets appended to the buffer
85
+ completions.append(
86
+ Completion(cmd[len(typed_cmd) :], display=f"/{cmd}")
87
+ )
88
+ return completions
89
+
90
+ # @-path completions: delegate to PathCompleter when buffer contains @
91
+ if "@" in text:
92
+ at_pos = text.rfind("@")
93
+ before_at = text[:at_pos]
94
+ # Only trigger if @ is at word boundary (not inside a word like email)
95
+ if before_at and before_at[-1].isalnum():
96
+ return []
97
+ partial_path = text[at_pos + 1 :]
98
+ from prompt_toolkit.completion import PathCompleter # noqa: PLC0415
99
+ from prompt_toolkit.document import Document # noqa: PLC0415
100
+
101
+ # Build a sub-document for PathCompleter using just the path portion
102
+ sub_doc = Document(partial_path, len(partial_path))
103
+ path_completions = []
104
+ pc = PathCompleter(min_input_len=0)
105
+ for c in pc.get_completions(
106
+ sub_doc, complete_event
107
+ ): # complete_event is CompleteEvent from prompt_toolkit
108
+ path_completions.append(
109
+ Completion(
110
+ c.text, start_position=c.start_position, display=c.display
111
+ )
112
+ )
113
+ return path_completions
114
+
115
+ return []
116
+
117
+
118
+ class PromptToolkitInput:
119
+ """prompt_toolkit input: pinned prompt, multi-line, history, completions.
120
+
121
+ Enter submits; Escape+Enter (Meta+Enter) inserts a newline.
122
+ The default for multiline=True is the opposite, so we override with
123
+ explicit KeyBindings — matching Claude Code / ChatGPT / Slack behavior.
124
+ """
125
+
126
+ def __init__(self, hist_path: Path, slash_commands: set[str]) -> None:
127
+ from prompt_toolkit import PromptSession # noqa: PLC0415
128
+ from prompt_toolkit.history import FileHistory # noqa: PLC0415
129
+ from prompt_toolkit.key_binding import KeyBindings # noqa: PLC0415
130
+
131
+ hist_path.parent.mkdir(parents=True, exist_ok=True)
132
+
133
+ kb = KeyBindings()
134
+
135
+ @kb.add("enter")
136
+ def _submit(event: Any) -> None:
137
+ event.current_buffer.validate_and_handle()
138
+
139
+ @kb.add("escape", "enter")
140
+ def _newline_meta(event: Any) -> None:
141
+ event.current_buffer.insert_text("\n")
142
+
143
+ self._session: PromptSession = PromptSession(
144
+ history=FileHistory(str(hist_path)),
145
+ # DftCompleter is duck-typed against the Completer protocol; we
146
+ # don't inherit because prompt_toolkit imports are deferred to
147
+ # keep it an optional dep.
148
+ completer=DftCompleter(slash_commands), # type: ignore[arg-type]
149
+ multiline=True,
150
+ key_bindings=kb,
151
+ )
152
+ # Single-slot timestamp store for double-tap-exit. prompt_toolkit
153
+ # intercepts SIGINT and raises KeyboardInterrupt in the read thread,
154
+ # so the chat loop's signal handler never fires while we own the
155
+ # prompt. We track Ctrl+C state here instead.
156
+ self._last_sigint_ts: list[float] = []
157
+
158
+ def read(self, prompt: str) -> str:
159
+ try:
160
+ return self._session.prompt(prompt)
161
+ except KeyboardInterrupt:
162
+ import time # noqa: PLC0415
163
+
164
+ now = time.monotonic()
165
+ if self._last_sigint_ts and (now - self._last_sigint_ts[0]) < 1.0:
166
+ # Double-tap within 1s — end the session.
167
+ raise EOFError from None
168
+ if self._last_sigint_ts:
169
+ self._last_sigint_ts[0] = now
170
+ else:
171
+ self._last_sigint_ts.append(now)
172
+ print(
173
+ "(Press Ctrl+C again within 1s to exit, or Ctrl+D)",
174
+ file=sys.stderr,
175
+ )
176
+ return ""
177
+
178
+
179
+ def select_input_layer(
180
+ hist_path: Path, slash_commands: set[str] | None = None
181
+ ) -> InputLayer:
182
+ """Select the right input layer for the current environment.
183
+
184
+ Returns PromptToolkitInput when:
185
+ - stdin is a TTY
186
+ - TERM is not "dumb"
187
+ - prompt_toolkit can be imported
188
+
189
+ Otherwise returns StdlibInput.
190
+ """
191
+ if slash_commands is None:
192
+ slash_commands = set()
193
+
194
+ if not sys.stdin.isatty():
195
+ return StdlibInput(hist_path)
196
+
197
+ if os.environ.get("TERM") == "dumb":
198
+ return StdlibInput(hist_path)
199
+
200
+ try:
201
+ import prompt_toolkit # noqa: PLC0415, F401
202
+ except ImportError:
203
+ return StdlibInput(hist_path)
204
+
205
+ return PromptToolkitInput(hist_path, slash_commands)
@@ -0,0 +1,115 @@
1
+ """Embedded HTTP server lifecycle for `dft chat`.
2
+
3
+ `dft chat` runs the same FastAPI app that `dft mcp serve` exposes alongside
4
+ its stdio MCP transport, so that URLs the model emits via `render_dashboard`
5
+ (e.g. `http://localhost:<port>/<face>`) actually open a live preview. Without
6
+ this, those URLs 404.
7
+
8
+ The agent's chat loop is sync; uvicorn needs asyncio. We host the server on a
9
+ dedicated background thread + asyncio loop owned by these helpers — separate
10
+ from the external-MCP manager's loop so neither lifecycle blocks the other.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import asyncio
16
+ import contextlib
17
+ import threading
18
+ from concurrent.futures import Future
19
+ from dataclasses import dataclass
20
+
21
+ import uvicorn
22
+
23
+ from dataface.core.serve.embedded import build_embedded_server
24
+
25
+
26
+ @dataclass
27
+ class EmbeddedServerHandle:
28
+ """Handle for the agent's embedded preview HTTP server."""
29
+
30
+ server: uvicorn.Server
31
+ port: int
32
+ loop: asyncio.AbstractEventLoop
33
+ thread: threading.Thread
34
+ serve_future: Future[None]
35
+
36
+
37
+ def start_http_server(*, port_hint: int = 8765) -> EmbeddedServerHandle:
38
+ """Start the dataface FastAPI server on a background thread.
39
+
40
+ Resolves a free port starting at *port_hint* via `find_available_port` so
41
+ the agent doesn't collide with a `dft mcp serve` already on 8765.
42
+
43
+ Returns a handle the caller hands to `stop_http_server` for teardown.
44
+ Surfaces `ImportError` (uvicorn missing — install `dataface[mcp]`) and
45
+ `RuntimeError` (no free port near *port_hint*) directly from
46
+ `build_embedded_server`. If uvicorn binds but then raises (TOCTTOU race),
47
+ the underlying `OSError` is raised here too — no silent swallows.
48
+ """
49
+ server, resolved = build_embedded_server(port_hint=port_hint)
50
+
51
+ loop = asyncio.new_event_loop()
52
+ thread = threading.Thread(
53
+ target=loop.run_forever, daemon=True, name="dft-chat-http"
54
+ )
55
+ thread.start()
56
+
57
+ serve_future = asyncio.run_coroutine_threadsafe(server.serve(), loop)
58
+
59
+ # Wait until uvicorn signals it has started OR the serve coroutine raises.
60
+ # Polling both protects against `serve()` failing to bind (in which case
61
+ # serve_future completes with an OSError) — without this, a bind failure
62
+ # would just look like a 5-second timeout with the real cause swallowed.
63
+ deadline = threading.Event()
64
+
65
+ async def _wait_started() -> None:
66
+ while not server.started:
67
+ await asyncio.sleep(0.05)
68
+ deadline.set()
69
+
70
+ asyncio.run_coroutine_threadsafe(_wait_started(), loop)
71
+
72
+ import time
73
+
74
+ deadline_ts = time.monotonic() + 5.0
75
+ while not deadline.wait(timeout=0.05):
76
+ if serve_future.done():
77
+ # serve() returned/raised before started flipped — surface the
78
+ # underlying error rather than the generic timeout below.
79
+ exc = serve_future.exception()
80
+ if exc is not None:
81
+ raise exc
82
+ raise RuntimeError(
83
+ f"HTTP server exited before signaling started on port {resolved}"
84
+ )
85
+ if time.monotonic() >= deadline_ts:
86
+ server.should_exit = True
87
+ raise RuntimeError(
88
+ f"HTTP server failed to start within 5s on port {resolved}"
89
+ )
90
+
91
+ return EmbeddedServerHandle(
92
+ server=server,
93
+ port=resolved,
94
+ loop=loop,
95
+ thread=thread,
96
+ serve_future=serve_future,
97
+ )
98
+
99
+
100
+ def stop_http_server(handle: EmbeddedServerHandle) -> None:
101
+ """Stop the HTTP server started by `start_http_server`.
102
+
103
+ The handle's attributes are guaranteed by `start_http_server` (which is
104
+ the only constructor) — no defensive `getattr` needed.
105
+ """
106
+ handle.server.should_exit = True
107
+
108
+ # Best-effort: if serve() raises on shutdown, continue teardown anyway.
109
+ with contextlib.suppress(Exception):
110
+ handle.serve_future.result(timeout=5.0)
111
+
112
+ if not handle.loop.is_closed():
113
+ handle.loop.call_soon_threadsafe(handle.loop.stop)
114
+
115
+ handle.thread.join(timeout=5.0)