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,347 @@
1
+ """Single-chart render utility shared by sizing pass and render pass."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ from dataface.core.compile.models.chart.compiled import (
8
+ Chart,
9
+ )
10
+ from dataface.core.compile.models.face.compiled import VariableValues
11
+ from dataface.core.render.svg_utils import extract_svg_dimensions
12
+
13
+ if TYPE_CHECKING:
14
+ from dataface.core.compile.chart_resolved import ResolvedChart
15
+ from dataface.core.compile.models.style.merged import MergedStyle
16
+ from dataface.core.execute.executor import Executor
17
+
18
+ # Type alias for the SVG render cache used across sizing and render passes.
19
+ # Maps (chart_id, slot_width, slot_height) → (svg_string, actual_height_px).
20
+ RenderCache = dict[tuple[str, float, float], tuple[str, float]]
21
+
22
+
23
+ def render_chart_to_svg(
24
+ chart: Chart,
25
+ executor: Executor,
26
+ variables: VariableValues,
27
+ width: float,
28
+ height: float | None = None,
29
+ resolved_style: MergedStyle | None = None,
30
+ padding: dict[str, Any] | None = None,
31
+ face_level: int = 1,
32
+ vega_config: dict[str, Any] | None = None,
33
+ ) -> tuple[str, float, float]:
34
+ """Render a Vega-Lite chart and return (svg_string, actual_width, actual_height).
35
+
36
+ Executes the query, resolves the chart, and calls vl-convert.
37
+ When height=None, Vega auto-sizes vertically (used for initial sizing pass).
38
+ When height is given, the chart is constrained to that height (used for
39
+ cols alignment re-renders).
40
+
41
+ Shared by both Vega-family and SVG-family chart render paths.
42
+ Does NOT wrap the SVG in data-attribute groups — that stays in rendering.py.
43
+
44
+ ``face_level`` is the heading level of the parent face (root=1, nested=2, …).
45
+ Chart title uses face_level + 1.
46
+ """
47
+ from dataface.core.compile.jinja import resolve_jinja_template
48
+ from dataface.core.render.chart.pipeline import resolve_chart
49
+ from dataface.core.render.chart.vega_lite import render_chart as vega_render
50
+ from dataface.core.render.placeholder import is_placeholder_needed
51
+ from dataface.core.render.utils import resolve_field_names
52
+
53
+ data = [] if chart.type == "callout" else executor.execute_chart(chart, variables)
54
+
55
+ # Per-layer datasets for layered charts
56
+ datasets: dict[str, list[dict[str, Any]]] | None = None
57
+ if chart.type == "layered" and chart.layers:
58
+ layer_query_names: set[str] = set()
59
+ for layer in chart.layers:
60
+ q = layer.query
61
+ if isinstance(q, str):
62
+ layer_query_names.add(q)
63
+ if layer_query_names:
64
+ datasets = {}
65
+ if chart.query_name:
66
+ datasets[chart.query_name] = data
67
+ for qn in layer_query_names:
68
+ if qn not in datasets:
69
+ datasets[qn] = executor.execute_query(qn, variables)
70
+
71
+ use_placeholder = is_placeholder_needed(chart, data)
72
+
73
+ updates: dict[str, str] = {}
74
+ if chart.title:
75
+ updates["title"] = resolve_jinja_template(chart.title, variables, strict=False)
76
+ if chart.label:
77
+ updates["label"] = resolve_jinja_template(chart.label, variables, strict=False)
78
+ if chart.subtitle:
79
+ updates["subtitle"] = resolve_jinja_template(
80
+ chart.subtitle, variables, strict=False
81
+ )
82
+ if chart.message:
83
+ updates["message"] = resolve_jinja_template(
84
+ chart.message, variables, strict=False
85
+ )
86
+ # KPI link is a static URL template resolved against variables (e.g.
87
+ # "/region/{{ region }}"). Vega-family charts resolve link per datum at
88
+ # render time via _build_href_calc_expr — do not pre-resolve those.
89
+ if chart.type == "kpi" and chart.link:
90
+ updates["link"] = resolve_jinja_template(chart.link, variables, strict=False)
91
+ resolved_chart = chart.model_copy(update=updates) if updates else chart
92
+ resolved_chart = resolve_field_names(resolved_chart, data)
93
+
94
+ detect_data = data
95
+ if use_placeholder and not data:
96
+ from dataface.core.render.placeholder import generate_placeholder_data
97
+
98
+ detect_data = generate_placeholder_data("bar", resolved_chart)
99
+
100
+ col_descs = (
101
+ executor.get_column_descriptions(chart.query_name) if chart.query_name else None
102
+ )
103
+ presentation_chart = resolve_chart(
104
+ resolved_chart, detect_data, col_descs, board_style=resolved_style
105
+ )
106
+
107
+ # Attached-table dispatch — arc charts (only "pie" survives the
108
+ # normalizer; "donut" → "pie + style.inner_radius") at a tier ×
109
+ # cardinality where direct labels can't fit cleanly render as donut
110
+ # SVG + swatch-keyed table SVG composed into one outer <svg>. See
111
+ # arc_attached_table.py for the trigger rule.
112
+ if resolved_style is not None and chart.type == "pie" and data:
113
+ from dataface.core.render.chart.arc_attached_table import (
114
+ compute_shares,
115
+ should_attach_table,
116
+ )
117
+
118
+ shares = compute_shares(chart.theta or "", data)
119
+ if should_attach_table(width, shares):
120
+ return _render_arc_with_attached_table(
121
+ presentation_chart=presentation_chart,
122
+ chart=chart,
123
+ data=data,
124
+ shares=shares,
125
+ width=width,
126
+ height=height,
127
+ is_placeholder=use_placeholder,
128
+ datasets=datasets,
129
+ variables=variables,
130
+ padding=padding,
131
+ resolved_style=resolved_style,
132
+ face_level=face_level,
133
+ vega_config=vega_config,
134
+ )
135
+
136
+ # height=None → Vega auto-sizes vertically; actual height read back from SVG.
137
+ # height=<value> → fixed height constraint (used for cols alignment re-renders).
138
+ chart_svg = vega_render(
139
+ presentation_chart,
140
+ data,
141
+ format="svg",
142
+ width=width,
143
+ height=height,
144
+ is_placeholder=use_placeholder,
145
+ datasets=datasets,
146
+ variables=variables,
147
+ padding=padding,
148
+ resolved_style=resolved_style,
149
+ face_level=face_level,
150
+ effective_vega_config=vega_config,
151
+ )
152
+
153
+ dims = extract_svg_dimensions(chart_svg)
154
+ return chart_svg, dims.width, dims.height
155
+
156
+
157
+ def _render_arc_with_attached_table(
158
+ presentation_chart: ResolvedChart,
159
+ chart: Chart,
160
+ data: list[dict[str, Any]],
161
+ shares: list[float],
162
+ width: float,
163
+ height: float | None,
164
+ is_placeholder: bool,
165
+ datasets: dict[str, list[dict[str, Any]]] | None,
166
+ variables: VariableValues,
167
+ padding: dict[str, Any] | None,
168
+ resolved_style: MergedStyle,
169
+ face_level: int,
170
+ vega_config: dict[str, Any] | None,
171
+ ) -> tuple[str, float, float]:
172
+ """Render donut + attached-table composition.
173
+
174
+ Donut renders without direct labels (table carries them). Table uses
175
+ the swatch column + header-hidden config from the β-1 foundation.
176
+ Palette is read from ``presentation_chart.resolved_style.palette``
177
+ (chart-local merged style) so any per-chart palette override survives
178
+ into the swatches — same source the arc layer paints from.
179
+
180
+ The ``height`` parameter is intentionally ignored — the composed
181
+ SVG's natural height (donut + gap + table) flows back through the
182
+ data-aware sizer so the surrounding layout grows the card to fit.
183
+ A cols-alignment re-render that tried to constrain height here
184
+ would clip the table.
185
+ """
186
+ import dataclasses
187
+ from types import SimpleNamespace
188
+
189
+ from dataface.core.compile.models.style.authored import (
190
+ ChartStylePatch,
191
+ TableChartStylePatch,
192
+ )
193
+ from dataface.core.compile.style_cascade import build_resolved_style
194
+ from dataface.core.render.chart.arc_attached_table import (
195
+ build_attached_table_columns,
196
+ compose_attached_table_svg,
197
+ )
198
+ from dataface.core.render.chart.table import render_table_svg
199
+ from dataface.core.render.chart.vega_lite import render_chart as vega_render
200
+
201
+ # Donut: replace labels with None so the labels layer doesn't emit.
202
+ # The arc `order` encoding still fires (data_override + order are
203
+ # hoisted out of the labels branch in profile.py) so wedge order
204
+ # stays pinned to data insertion.
205
+ donut_chart = dataclasses.replace(presentation_chart, labels=None)
206
+ donut_svg = vega_render(
207
+ donut_chart,
208
+ data,
209
+ format="svg",
210
+ width=width,
211
+ height=width, # donut renders square at the slot width
212
+ is_placeholder=is_placeholder,
213
+ datasets=datasets,
214
+ variables=variables,
215
+ padding=padding,
216
+ resolved_style=resolved_style,
217
+ face_level=face_level,
218
+ effective_vega_config=vega_config,
219
+ )
220
+ donut_dims = extract_svg_dimensions(donut_svg)
221
+
222
+ # Series rows for the table — palette from chart-local resolved
223
+ # style (same source the arc layer paints from), pre-computed shares
224
+ # from the trigger gate (no duplicate arithmetic).
225
+ series_rows = _extract_arc_rows_for_table(presentation_chart, data, shares)
226
+ # Pre-format share as "42%" string — sidesteps the three-lane
227
+ # numeric layout's %-suffix-drop bug (filed as follow-up).
228
+ table_data = [
229
+ {
230
+ "swatch": r["swatch"],
231
+ "share": f"{round(r['share'] * 100)}%",
232
+ "name": r["name"],
233
+ "value": r["value"],
234
+ }
235
+ for r in series_rows
236
+ ]
237
+ # Value format — read directly from the chart's format hint
238
+ # (``str | FormatConfig | None``). ``TableColumnConfig.format``
239
+ # accepts all three shapes; passing the raw value through preserves
240
+ # any authored prefix/suffix on a FormatConfig.
241
+ value_format = presentation_chart.format
242
+
243
+ # Width-sizing uses the same font the table will render with —
244
+ # threaded from the resolved table style (theme-cascaded) so a
245
+ # theme that retunes table type doesn't desync measurement.
246
+ table_style = resolved_style.charts.table
247
+ table_font_size = (
248
+ float(table_style.font.size) if table_style.font.size is not None else 11.0
249
+ )
250
+ table_font_family = table_style.font.family or "Inter Variable"
251
+ columns, table_target_width = build_attached_table_columns(
252
+ table_data,
253
+ value_format=value_format,
254
+ font_size=table_font_size,
255
+ font_family=table_font_family,
256
+ )
257
+ table_patch = ChartStylePatch(
258
+ table=TableChartStylePatch.model_validate(
259
+ {
260
+ "header": {"visible": False},
261
+ "pagination": {"enabled": False},
262
+ "columns": columns,
263
+ }
264
+ )
265
+ )
266
+ # SimpleNamespace stand-in — render_table_svg reads chart attributes
267
+ # by attribute access. Stdlib ad-hoc carrier; no mock framework in
268
+ # production code. ``chart.id`` is guaranteed on a normalized Chart
269
+ # so we read it directly.
270
+ table_chart = SimpleNamespace(
271
+ id=f"{chart.id}__attached_table",
272
+ title=None,
273
+ subtitle=None,
274
+ style=table_patch,
275
+ columns=None,
276
+ column_defaults=None,
277
+ link=None,
278
+ )
279
+
280
+ table_resolved = build_resolved_style(resolved_style, table_patch)
281
+ table_render_width = min(table_target_width, float(width))
282
+ table_svg = render_table_svg(
283
+ table_chart,
284
+ table_data,
285
+ width=table_render_width,
286
+ resolved_style=table_resolved,
287
+ board_style=resolved_style,
288
+ )
289
+ table_dims = extract_svg_dimensions(table_svg)
290
+
291
+ composed, w, h = compose_attached_table_svg(
292
+ donut_svg=donut_svg,
293
+ table_svg=table_svg,
294
+ donut_width=donut_dims.width,
295
+ donut_height=donut_dims.height,
296
+ table_width=table_dims.width,
297
+ table_height=table_dims.height,
298
+ )
299
+ return composed, w, h
300
+
301
+
302
+ def _extract_arc_rows_for_table(
303
+ presentation_chart: ResolvedChart,
304
+ data: list[dict[str, Any]],
305
+ shares: list[float],
306
+ ) -> list[dict[str, Any]]:
307
+ """Build per-wedge rows for the attached table.
308
+
309
+ Palette source is the **chart-local** merged style
310
+ (``presentation_chart.resolved_style.palette``) — same source the
311
+ arc layer paints wedges from. Reading the board-level palette
312
+ instead would silently desync swatches from wedges whenever a chart
313
+ overrides ``style.palette`` or ``style.range.category``.
314
+
315
+ Shares are passed in (already computed at the trigger gate) rather
316
+ than recomputed here. ``data`` and ``shares`` are expected to have
317
+ the same length (``compute_shares`` returns one share per data row);
318
+ ``zip(..., strict=True)`` enforces that invariant.
319
+ """
320
+ from dataface.core.render.errors import ChartDataError
321
+
322
+ theta_field = presentation_chart.theta or ""
323
+ color_ch = presentation_chart.resolved_channels.get("color")
324
+ color_field = (
325
+ color_ch.data_field
326
+ if color_ch is not None and color_ch.data_field is not None
327
+ else None
328
+ )
329
+ palette = list(presentation_chart.resolved_style.palette)
330
+ if not palette:
331
+ raise ChartDataError(
332
+ f"Arc chart {presentation_chart.id!r} attached-table render "
333
+ "needs a resolved palette, but the chart-local style produced "
334
+ "an empty list.",
335
+ chart_id=presentation_chart.id,
336
+ )
337
+ rows: list[dict[str, Any]] = []
338
+ for idx, (row, share) in enumerate(zip(data, shares, strict=True)):
339
+ rows.append(
340
+ {
341
+ "swatch": palette[idx % len(palette)],
342
+ "share": share,
343
+ "name": row.get(color_field) if color_field else None,
344
+ "value": row.get(theta_field, 0) or 0,
345
+ }
346
+ )
347
+ return rows
@@ -0,0 +1,193 @@
1
+ """Renderer registry for resolved charts."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+ from dataclasses import dataclass, field
7
+ from typing import TYPE_CHECKING, Any, Protocol
8
+
9
+ if TYPE_CHECKING:
10
+ from dataface.core.compile.models.style.merged import MergedStyle
11
+
12
+ from dataface.core.compile.chart_resolved import ResolvedChart
13
+ from dataface.core.render.chart.artifacts import RenderArtifact
14
+ from dataface.core.render.chart.callout import render_callout_chart_svg
15
+ from dataface.core.render.chart.kpi import render_kpi_svg
16
+ from dataface.core.render.chart.serialization import build_dataface_json
17
+ from dataface.core.render.chart.spark_bar import render_spark_bar_svg
18
+ from dataface.core.render.chart.standard_renderer import render_standard_vega_spec
19
+ from dataface.core.render.chart.table import render_table_svg
20
+
21
+ SVG_RENDERERS: dict[str, Callable[..., str]] = {
22
+ "table": render_table_svg,
23
+ "spark_bar": render_spark_bar_svg,
24
+ "kpi": render_kpi_svg,
25
+ "callout": render_callout_chart_svg,
26
+ }
27
+
28
+ # SVG chart types that consume `variables` for pagination state.
29
+ _VARIABLE_AWARE_SVG_TYPES = {"table"}
30
+
31
+
32
+ class ChartRenderer(Protocol):
33
+ def render(
34
+ self,
35
+ chart: ResolvedChart,
36
+ data: list[dict[str, Any]],
37
+ width: float | None = None,
38
+ height: float | None = None,
39
+ is_placeholder: bool = False,
40
+ datasets: dict[str, list[dict[str, Any]]] | None = None,
41
+ variables: dict[str, Any] | None = None,
42
+ padding: dict[str, Any] | None = None,
43
+ face_level: int = 1,
44
+ ) -> RenderArtifact: ...
45
+
46
+
47
+ @dataclass
48
+ class StandardVegaLiteRenderer:
49
+ def render(
50
+ self,
51
+ chart: ResolvedChart,
52
+ data: list[dict[str, Any]],
53
+ width: float | None = None,
54
+ height: float | None = None,
55
+ is_placeholder: bool = False,
56
+ datasets: dict[str, list[dict[str, Any]]] | None = None,
57
+ variables: dict[str, Any] | None = None,
58
+ padding: dict[str, Any] | None = None,
59
+ face_level: int = 1,
60
+ resolved_style: MergedStyle | None = None,
61
+ effective_vega_config: dict[str, Any] | None = None,
62
+ ) -> RenderArtifact:
63
+ return RenderArtifact(
64
+ kind="vega_spec",
65
+ payload=render_standard_vega_spec(
66
+ chart,
67
+ data,
68
+ width,
69
+ height,
70
+ datasets=datasets,
71
+ padding=padding,
72
+ resolved_style=resolved_style,
73
+ face_level=face_level,
74
+ effective_vega_config=effective_vega_config,
75
+ ),
76
+ )
77
+
78
+
79
+ @dataclass
80
+ class SvgRenderer:
81
+ def render(
82
+ self,
83
+ chart: ResolvedChart,
84
+ data: list[dict[str, Any]],
85
+ width: float | None = None,
86
+ height: float | None = None,
87
+ is_placeholder: bool = False,
88
+ datasets: dict[str, list[dict[str, Any]]] | None = None,
89
+ variables: dict[str, Any] | None = None,
90
+ padding: (
91
+ dict[str, Any] | None
92
+ ) = None, # intentionally unused: SVG-family uses SVG translate
93
+ face_level: int = 1,
94
+ resolved_style: MergedStyle | None = None,
95
+ ) -> RenderArtifact:
96
+ # KPI/spark/spark_bar/table take chart-level MergedChartsStyle as
97
+ # ``resolved_style``; callout takes the board-level MergedStyle for
98
+ # callout card styling. The two surfaces don't overlap on a single
99
+ # call, so a per-type swap is enough — no separate "chart_style" kwarg.
100
+ kwargs: dict[str, Any] = {
101
+ "width": width,
102
+ "height": height,
103
+ "is_placeholder": is_placeholder,
104
+ "face_level": face_level,
105
+ }
106
+ if chart.chart_type == "callout":
107
+ assert (
108
+ resolved_style is not None
109
+ ), f"{chart.chart_type} renderer requires resolved_style"
110
+ kwargs["resolved_style"] = resolved_style
111
+ else:
112
+ if resolved_style is None:
113
+ raise ValueError(
114
+ f"SvgRenderer.render requires resolved_style (board-level MergedStyle) "
115
+ f"for chart type '{chart.chart_type}'. "
116
+ "Thread the face's MergedStyle from the render pipeline."
117
+ )
118
+ kwargs["resolved_style"] = chart.resolved_style
119
+ kwargs["board_style"] = resolved_style
120
+ if chart.chart_type in _VARIABLE_AWARE_SVG_TYPES:
121
+ kwargs["variables"] = variables
122
+ svg = SVG_RENDERERS[chart.chart_type](chart, data, **kwargs)
123
+ return RenderArtifact(kind="svg", payload=svg)
124
+
125
+
126
+ @dataclass
127
+ class RendererRegistry:
128
+ standard_renderer: ChartRenderer = field(default_factory=StandardVegaLiteRenderer)
129
+ geo_renderer: ChartRenderer = field(default_factory=StandardVegaLiteRenderer)
130
+ svg_renderer: ChartRenderer = field(default_factory=SvgRenderer)
131
+
132
+ def select(self, chart: ResolvedChart) -> ChartRenderer:
133
+ if chart.renderer_family == "svg":
134
+ return self.svg_renderer
135
+ if chart.renderer_family == "geo":
136
+ return self.geo_renderer
137
+ return self.standard_renderer
138
+
139
+ def render(
140
+ self,
141
+ chart: ResolvedChart,
142
+ data: list[dict[str, Any]],
143
+ width: float | None = None,
144
+ height: float | None = None,
145
+ is_placeholder: bool = False,
146
+ datasets: dict[str, list[dict[str, Any]]] | None = None,
147
+ variables: dict[str, Any] | None = None,
148
+ padding: dict[str, Any] | None = None,
149
+ resolved_style: MergedStyle | None = None,
150
+ face_level: int = 1,
151
+ effective_vega_config: dict[str, Any] | None = None,
152
+ ) -> RenderArtifact:
153
+ renderer = self.select(chart)
154
+ # SVG-family always needs resolved_style; VL/geo families take it
155
+ # too so the chart's halo/knockout/spec background match the face's
156
+ # *actual* resolved background rather than re-deriving from theme name
157
+ # (which can mismatch when the face has chart-local style overrides).
158
+ extra: dict[str, Any] = (
159
+ {"resolved_style": resolved_style}
160
+ if chart.renderer_family in ("svg", "vega", "geo")
161
+ else {}
162
+ )
163
+ if (
164
+ chart.renderer_family in ("vega", "geo")
165
+ and effective_vega_config is not None
166
+ ):
167
+ extra["effective_vega_config"] = effective_vega_config
168
+ return renderer.render(
169
+ chart,
170
+ data,
171
+ width,
172
+ height,
173
+ is_placeholder,
174
+ datasets=datasets,
175
+ variables=variables,
176
+ padding=padding,
177
+ face_level=face_level,
178
+ **extra,
179
+ )
180
+
181
+
182
+ def build_chart_json(
183
+ chart: ResolvedChart,
184
+ data: list[dict[str, Any]],
185
+ width: float | None = None,
186
+ height: float | None = None,
187
+ ) -> RenderArtifact:
188
+ return RenderArtifact(
189
+ kind="json",
190
+ payload=build_dataface_json(
191
+ chart.source_chart, data, width=width, height=height
192
+ ),
193
+ )