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.
- d3_format/__init__.py +14 -0
- d3_format/errors.py +19 -0
- d3_format/format.py +551 -0
- d3_format/spec.py +159 -0
- dataface/DATAFACE_SYNTAX.md +1135 -0
- dataface/__init__.py +93 -0
- dataface/_docs_site.py +20 -0
- dataface/_install_hint.py +26 -0
- dataface/agent_api/__init__.py +79 -0
- dataface/agent_api/_init_templates/__init__.py +0 -0
- dataface/agent_api/_init_templates/agents_dft_snippet.md +26 -0
- dataface/agent_api/_init_templates/dataface.yml +15 -0
- dataface/agent_api/_init_templates/faces-dataface.yml +144 -0
- dataface/agent_api/_init_templates/index.md +24 -0
- dataface/agent_api/_paths.py +118 -0
- dataface/agent_api/_project_agents_md.py +43 -0
- dataface/agent_api/_session_store.py +486 -0
- dataface/agent_api/_state.py +28 -0
- dataface/agent_api/chat.py +221 -0
- dataface/agent_api/dashboards.py +257 -0
- dataface/agent_api/describe.py +366 -0
- dataface/agent_api/describe_query.py +120 -0
- dataface/agent_api/docs/__init__.py +25 -0
- dataface/agent_api/docs/_loader.py +292 -0
- dataface/agent_api/docs/yaml-reference.md +2757 -0
- dataface/agent_api/file_refs.py +118 -0
- dataface/agent_api/init.py +126 -0
- dataface/agent_api/inspect.py +128 -0
- dataface/agent_api/mcp_install.py +170 -0
- dataface/agent_api/query.py +274 -0
- dataface/agent_api/schema.py +658 -0
- dataface/agent_api/schema_search.py +284 -0
- dataface/agent_api/search.py +270 -0
- dataface/agent_api/skill_install.py +141 -0
- dataface/agent_api/skill_render.py +90 -0
- dataface/agent_api/skills.py +293 -0
- dataface/agent_api/surface_aliases.yaml +128 -0
- dataface/agent_api/validate.py +175 -0
- dataface/agent_api/validate_query.py +84 -0
- dataface/ai/__init__.py +39 -0
- dataface/ai/agent.py +139 -0
- dataface/ai/context.py +45 -0
- dataface/ai/events.py +62 -0
- dataface/ai/external_mcp.py +610 -0
- dataface/ai/generate_sql.py +96 -0
- dataface/ai/llm.py +403 -0
- dataface/ai/mcp/__init__.py +51 -0
- dataface/ai/mcp/server.py +289 -0
- dataface/ai/memories.py +85 -0
- dataface/ai/prompts.py +177 -0
- dataface/ai/schema_context.py +138 -0
- dataface/ai/skills/before-after-comparison/SKILL.md +102 -0
- dataface/ai/skills/before-after-comparison/examples/before-after-comparison.yml +24 -0
- dataface/ai/skills/dashboard-build/SKILL.md +212 -0
- dataface/ai/skills/dashboard-build/examples/_smoke.yml +15 -0
- dataface/ai/skills/dashboard-design/SKILL.md +182 -0
- dataface/ai/skills/dashboard-review/SKILL.md +113 -0
- dataface/ai/skills/dashboard-structural-review/SKILL.md +173 -0
- dataface/ai/skills/dashboard-visual-review/SKILL.md +139 -0
- dataface/ai/skills/dataface-mcp-setup/SKILL.md +177 -0
- dataface/ai/skills/dataface-troubleshooting/SKILL.md +225 -0
- dataface/ai/skills/drill-down-link/SKILL.md +112 -0
- dataface/ai/skills/drill-down-link/examples/drill-down-link.yml +27 -0
- dataface/ai/skills/faceted-small-multiples/SKILL.md +116 -0
- dataface/ai/skills/faceted-small-multiples/examples/faceted-small-multiples.yml +33 -0
- dataface/ai/skills/filter-bar-with-variables/SKILL.md +105 -0
- dataface/ai/skills/filter-bar-with-variables/examples/filter-bar-with-variables.yml +49 -0
- dataface/ai/skills/kpi-row/SKILL.md +101 -0
- dataface/ai/skills/kpi-row/examples/kpi-row.yml +55 -0
- dataface/ai/skills/report-design/SKILL.md +184 -0
- dataface/ai/skills/single-metric-bignum/SKILL.md +90 -0
- dataface/ai/skills/single-metric-bignum/examples/single-metric-bignum.yml +27 -0
- dataface/ai/skills/table-heavy-ops-dashboard/SKILL.md +114 -0
- dataface/ai/skills/table-heavy-ops-dashboard/examples/table-heavy-ops-dashboard.yml +48 -0
- dataface/ai/skills/time-series-trend/SKILL.md +93 -0
- dataface/ai/skills/time-series-trend/examples/time-series-trend.yml +26 -0
- dataface/ai/skills/top-n-with-detail/SKILL.md +98 -0
- dataface/ai/skills/top-n-with-detail/examples/top-n-with-detail.yml +45 -0
- dataface/ai/skills/two-by-two-grid-overview/SKILL.md +78 -0
- dataface/ai/skills/two-by-two-grid-overview/examples/two-by-two-grid-overview.yml +64 -0
- dataface/ai/tool_schemas.py +132 -0
- dataface/ai/tools/__init__.py +312 -0
- dataface/ai/yaml_utils.py +57 -0
- dataface/cli/__init__.py +3 -0
- dataface/cli/_console.py +48 -0
- dataface/cli/_error_format.py +83 -0
- dataface/cli/_extras.py +190 -0
- dataface/cli/_json_output.py +8 -0
- dataface/cli/_parsing.py +17 -0
- dataface/cli/_version_info.py +56 -0
- dataface/cli/commands/__init__.py +3 -0
- dataface/cli/commands/_agent_input.py +205 -0
- dataface/cli/commands/_agent_server.py +115 -0
- dataface/cli/commands/chat.py +645 -0
- dataface/cli/commands/describe.py +107 -0
- dataface/cli/commands/docs.py +131 -0
- dataface/cli/commands/extension.py +179 -0
- dataface/cli/commands/init.py +240 -0
- dataface/cli/commands/inspect.py +94 -0
- dataface/cli/commands/mcp_init.py +167 -0
- dataface/cli/commands/query.py +386 -0
- dataface/cli/commands/render.py +291 -0
- dataface/cli/commands/schema.py +411 -0
- dataface/cli/commands/search.py +49 -0
- dataface/cli/commands/serve.py +114 -0
- dataface/cli/commands/skills.py +133 -0
- dataface/cli/commands/skills_init.py +161 -0
- dataface/cli/commands/validate.py +63 -0
- dataface/cli/main.py +1501 -0
- dataface/core/__init__.py +75 -0
- dataface/core/compile/__init__.py +244 -0
- dataface/core/compile/_jinja_helpers.py +78 -0
- dataface/core/compile/channel.py +222 -0
- dataface/core/compile/chart_focus.py +101 -0
- dataface/core/compile/chart_resolved.py +169 -0
- dataface/core/compile/chart_type_detection.py +489 -0
- dataface/core/compile/chart_update.py +261 -0
- dataface/core/compile/colors.py +64 -0
- dataface/core/compile/compiler.py +904 -0
- dataface/core/compile/config.py +823 -0
- dataface/core/compile/custom_chart_types.py +208 -0
- dataface/core/compile/data_table_attachment.py +1287 -0
- dataface/core/compile/detect.py +110 -0
- dataface/core/compile/errors.py +302 -0
- dataface/core/compile/filter_injection.py +319 -0
- dataface/core/compile/introspection.py +527 -0
- dataface/core/compile/jinja.py +511 -0
- dataface/core/compile/labels_env.py +52 -0
- dataface/core/compile/markdown.py +154 -0
- dataface/core/compile/meta.py +388 -0
- dataface/core/compile/models/__init__.py +0 -0
- dataface/core/compile/models/chart/__init__.py +0 -0
- dataface/core/compile/models/chart/authored.py +2137 -0
- dataface/core/compile/models/chart/compiled.py +398 -0
- dataface/core/compile/models/config.py +347 -0
- dataface/core/compile/models/face/__init__.py +0 -0
- dataface/core/compile/models/face/authored.py +659 -0
- dataface/core/compile/models/face/compiled.py +522 -0
- dataface/core/compile/models/factories.py +201 -0
- dataface/core/compile/models/markers.py +40 -0
- dataface/core/compile/models/palette.py +36 -0
- dataface/core/compile/models/primitives.py +415 -0
- dataface/core/compile/models/query/__init__.py +0 -0
- dataface/core/compile/models/query/authored.py +246 -0
- dataface/core/compile/models/query/compiled.py +710 -0
- dataface/core/compile/models/refs.py +137 -0
- dataface/core/compile/models/source.py +611 -0
- dataface/core/compile/models/style/__init__.py +0 -0
- dataface/core/compile/models/style/authored.py +481 -0
- dataface/core/compile/models/style/compiled.py +3399 -0
- dataface/core/compile/models/style/merged.py +1682 -0
- dataface/core/compile/models/theme.py +362 -0
- dataface/core/compile/models/variable/__init__.py +0 -0
- dataface/core/compile/models/variable/authored.py +254 -0
- dataface/core/compile/models/vega_lite/__init__.py +0 -0
- dataface/core/compile/models/vega_lite/config.py +510 -0
- dataface/core/compile/models/vega_lite/contracts.py +171 -0
- dataface/core/compile/normalize_charts.py +494 -0
- dataface/core/compile/normalize_layout.py +1000 -0
- dataface/core/compile/normalize_queries.py +297 -0
- dataface/core/compile/normalize_variables.py +489 -0
- dataface/core/compile/normalizer.py +543 -0
- dataface/core/compile/palette.py +1100 -0
- dataface/core/compile/parameterized.py +658 -0
- dataface/core/compile/parser.py +228 -0
- dataface/core/compile/schema.py +20 -0
- dataface/core/compile/schema_renderers/__init__.py +0 -0
- dataface/core/compile/schema_renderers/json_schema.py +163 -0
- dataface/core/compile/schema_renderers/prompt.py +152 -0
- dataface/core/compile/schema_renderers/vscode_schema.py +301 -0
- dataface/core/compile/sizing.py +2126 -0
- dataface/core/compile/sources.py +518 -0
- dataface/core/compile/sql_authoring_lint.py +56 -0
- dataface/core/compile/style_cascade.py +471 -0
- dataface/core/compile/typography.py +299 -0
- dataface/core/compile/validator.py +301 -0
- dataface/core/compile/variables.py +53 -0
- dataface/core/compile/vega_config.py +98 -0
- dataface/core/compile/vega_lite/__init__.py +6 -0
- dataface/core/compile/vega_lite/validation.py +95 -0
- dataface/core/compile/yaml_error_formatter.py +838 -0
- dataface/core/connections.py +38 -0
- dataface/core/dashboard.py +358 -0
- dataface/core/defaults/default_config.yml +101 -0
- dataface/core/defaults/palettes/categorical/category-10-dark.yml +32 -0
- dataface/core/defaults/palettes/categorical/category-10-light.yml +43 -0
- dataface/core/defaults/palettes/categorical/category-10.yml +31 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-blue.yml +22 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-brown.yml +29 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-green.yml +20 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-orange.yml +21 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-purple.yml +20 -0
- dataface/core/defaults/palettes/categorical/editorial-10-dark.yml +32 -0
- dataface/core/defaults/palettes/categorical/editorial-10.yml +40 -0
- dataface/core/defaults/palettes/categorical/hero-6.yml +17 -0
- dataface/core/defaults/palettes/categorical/single-blue.yml +11 -0
- dataface/core/defaults/palettes/categorical/tableau.yml +20 -0
- dataface/core/defaults/palettes/data/xkcd_colors.json +3803 -0
- dataface/core/defaults/palettes/diverging/blue-red.yml +25 -0
- dataface/core/defaults/palettes/diverging/coolwarm.yml +24 -0
- dataface/core/defaults/palettes/diverging/crimson-green.yml +23 -0
- dataface/core/defaults/palettes/diverging/orange-teal.yml +23 -0
- dataface/core/defaults/palettes/diverging/sunset.yml +24 -0
- dataface/core/defaults/palettes/scaffold/dft-creams.yml +38 -0
- dataface/core/defaults/palettes/scaffold/dft-grays.yml +53 -0
- dataface/core/defaults/palettes/sequential/amber.yml +22 -0
- dataface/core/defaults/palettes/sequential/blue.yml +22 -0
- dataface/core/defaults/palettes/sequential/brown.yml +22 -0
- dataface/core/defaults/palettes/sequential/gray.yml +22 -0
- dataface/core/defaults/palettes/sequential/green.yml +22 -0
- dataface/core/defaults/palettes/sequential/purple.yml +22 -0
- dataface/core/defaults/palettes/sequential/rust.yml +22 -0
- dataface/core/defaults/palettes/sequential/teal.yml +22 -0
- dataface/core/defaults/palettes/tone/negative.yml +32 -0
- dataface/core/defaults/palettes/tone/positive.yml +22 -0
- dataface/core/defaults/palettes/tone/warning.yml +22 -0
- dataface/core/defaults/themes/_base.yaml +786 -0
- dataface/core/defaults/themes/bi.yaml +16 -0
- dataface/core/defaults/themes/carbong100.yaml +41 -0
- dataface/core/defaults/themes/cream.yaml +122 -0
- dataface/core/defaults/themes/dark.yaml +40 -0
- dataface/core/defaults/themes/diagnostics-title-angle-extreme.yaml +9 -0
- dataface/core/defaults/themes/diagnostics-title-baseline-extreme.yaml +9 -0
- dataface/core/defaults/themes/diagnostics-title-baseline.yaml +24 -0
- dataface/core/defaults/themes/diagnostics-title-center.yaml +8 -0
- dataface/core/defaults/themes/diagnostics-title-color-extreme.yaml +24 -0
- dataface/core/defaults/themes/diagnostics-title-font-extreme.yaml +25 -0
- dataface/core/defaults/themes/diagnostics-title-left.yaml +8 -0
- dataface/core/defaults/themes/diagnostics-title-offset-extreme.yaml +9 -0
- dataface/core/defaults/themes/diagnostics-title-size-extreme.yaml +24 -0
- dataface/core/defaults/themes/diagnostics-title-weight-extreme.yaml +24 -0
- dataface/core/defaults/themes/editorial.yaml +147 -0
- dataface/core/defaults/themes/light.yaml +30 -0
- dataface/core/defaults/themes/looker.yaml +17 -0
- dataface/core/defaults/themes/stark.yaml +134 -0
- dataface/core/errors/__init__.py +67 -0
- dataface/core/errors/codes_compile.py +56 -0
- dataface/core/errors/codes_execute.py +177 -0
- dataface/core/errors/codes_render.py +106 -0
- dataface/core/errors/codes_unknown.py +15 -0
- dataface/core/errors/hints.py +74 -0
- dataface/core/errors/registry.py +42 -0
- dataface/core/errors/structured.py +92 -0
- dataface/core/execute/__init__.py +91 -0
- dataface/core/execute/adapters/__init__.py +49 -0
- dataface/core/execute/adapters/adapter_registry.py +400 -0
- dataface/core/execute/adapters/base.py +245 -0
- dataface/core/execute/adapters/csv_adapter.py +239 -0
- dataface/core/execute/adapters/dbt_adapter.py +283 -0
- dataface/core/execute/adapters/dbt_adapter_factory.py +212 -0
- dataface/core/execute/adapters/dbt_macro_loader.py +95 -0
- dataface/core/execute/adapters/dbt_utils.py +150 -0
- dataface/core/execute/adapters/http_adapter.py +224 -0
- dataface/core/execute/adapters/metricflow_adapter.py +94 -0
- dataface/core/execute/adapters/schema_resolver_adapter.py +144 -0
- dataface/core/execute/adapters/sql_adapter.py +710 -0
- dataface/core/execute/adapters/values_adapter.py +58 -0
- dataface/core/execute/batch.py +744 -0
- dataface/core/execute/cache_backend.py +135 -0
- dataface/core/execute/cache_keys.py +66 -0
- dataface/core/execute/dbt_jinja.py +21 -0
- dataface/core/execute/dialects/__init__.py +121 -0
- dataface/core/execute/dialects/athena.py +75 -0
- dataface/core/execute/dialects/base.py +302 -0
- dataface/core/execute/dialects/bigquery.py +38 -0
- dataface/core/execute/dialects/databricks.py +68 -0
- dataface/core/execute/dialects/duckdb.py +35 -0
- dataface/core/execute/dialects/mysql.py +68 -0
- dataface/core/execute/dialects/postgres.py +39 -0
- dataface/core/execute/dialects/redshift.py +12 -0
- dataface/core/execute/dialects/snowflake.py +51 -0
- dataface/core/execute/dialects/sqlserver.py +92 -0
- dataface/core/execute/duckdb_cache.py +712 -0
- dataface/core/execute/duckdb_config.py +26 -0
- dataface/core/execute/errors.py +213 -0
- dataface/core/execute/executor.py +1249 -0
- dataface/core/execute/parallel.py +162 -0
- dataface/core/execute/setup_sql.py +58 -0
- dataface/core/execute/source_registry.py +72 -0
- dataface/core/execute/source_resolver.py +255 -0
- dataface/core/execute/sql_guard.py +387 -0
- dataface/core/execute/sql_literals.py +199 -0
- dataface/core/fonts.py +52 -0
- dataface/core/inspect/__init__.py +32 -0
- dataface/core/inspect/cache_factory.py +98 -0
- dataface/core/inspect/db_types.py +162 -0
- dataface/core/inspect/dbt_schema.py +96 -0
- dataface/core/inspect/defaults.yml +37 -0
- dataface/core/inspect/fanout_risk.py +109 -0
- dataface/core/inspect/manifest_utils.py +77 -0
- dataface/core/inspect/partials/categorical.yml +40 -0
- dataface/core/inspect/partials/date.yml +40 -0
- dataface/core/inspect/partials/numeric.yml +55 -0
- dataface/core/inspect/partition_types.py +38 -0
- dataface/core/inspect/query_validator.py +975 -0
- dataface/core/inspect/renderer.py +354 -0
- dataface/core/inspect/resolver.py +808 -0
- dataface/core/inspect/search.py +461 -0
- dataface/core/inspect/sources/__init__.py +32 -0
- dataface/core/inspect/sources/dbt.py +738 -0
- dataface/core/inspect/sources/duckdb_utils.py +66 -0
- dataface/core/inspect/templates/__init__.py +1 -0
- dataface/core/inspect/templates/categorical_column.yml +196 -0
- dataface/core/inspect/templates/charts.yml +109 -0
- dataface/core/inspect/templates/date_column.yml +248 -0
- dataface/core/inspect/templates/model.yml +138 -0
- dataface/core/inspect/templates/numeric_column.yml +261 -0
- dataface/core/inspect/templates/quality.yml +80 -0
- dataface/core/inspect/templates/string_column.yml +263 -0
- dataface/core/project_roots.py +165 -0
- dataface/core/render/__init__.py +87 -0
- dataface/core/render/board_links.py +176 -0
- dataface/core/render/chart/__init__.py +27 -0
- dataface/core/render/chart/arc_attached_table.py +251 -0
- dataface/core/render/chart/artifacts.py +16 -0
- dataface/core/render/chart/callout.py +225 -0
- dataface/core/render/chart/decisions.py +358 -0
- dataface/core/render/chart/geo.py +700 -0
- dataface/core/render/chart/kpi.py +916 -0
- dataface/core/render/chart/labels.py +76 -0
- dataface/core/render/chart/pipeline.py +818 -0
- dataface/core/render/chart/presentation.py +36 -0
- dataface/core/render/chart/profile.py +3438 -0
- dataface/core/render/chart/render_single.py +347 -0
- dataface/core/render/chart/renderers.py +193 -0
- dataface/core/render/chart/rendering.py +565 -0
- dataface/core/render/chart/serialization.py +90 -0
- dataface/core/render/chart/spark.py +496 -0
- dataface/core/render/chart/spark_bar.py +370 -0
- dataface/core/render/chart/spec_builders.py +154 -0
- dataface/core/render/chart/standard_renderer.py +2645 -0
- dataface/core/render/chart/table.py +2957 -0
- dataface/core/render/chart/table_support.py +1452 -0
- dataface/core/render/chart/tick_values.py +66 -0
- dataface/core/render/chart/time_unit_detect.py +809 -0
- dataface/core/render/chart/title_overflow.py +157 -0
- dataface/core/render/chart/type_inference.py +122 -0
- dataface/core/render/chart/validation.py +99 -0
- dataface/core/render/chart/vega_lite.py +125 -0
- dataface/core/render/chart/vega_lite_types.py +268 -0
- dataface/core/render/chart/vl_field_maps.py +346 -0
- dataface/core/render/chart_interactivity.py +24 -0
- dataface/core/render/control_registry.py +287 -0
- dataface/core/render/converters/__init__.py +24 -0
- dataface/core/render/converters/chart.py +276 -0
- dataface/core/render/converters/html.py +98 -0
- dataface/core/render/converters/pdf.py +40 -0
- dataface/core/render/converters/png.py +41 -0
- dataface/core/render/errors.py +144 -0
- dataface/core/render/face_api.py +160 -0
- dataface/core/render/faces.py +1194 -0
- dataface/core/render/font_measurement.py +48 -0
- dataface/core/render/font_support.py +197 -0
- dataface/core/render/fonts/DFTSansTabular-Regular.ttf +0 -0
- dataface/core/render/fonts/DFTSansTabular-Regular.woff2 +0 -0
- dataface/core/render/fonts/DFTSerifOldstyleProportional-Regular.ttf +0 -0
- dataface/core/render/fonts/DFTSerifOldstyleTabular-Regular.ttf +0 -0
- dataface/core/render/fonts/InterVariable.ttf +0 -0
- dataface/core/render/fonts/InterVariable.woff2 +0 -0
- dataface/core/render/fonts/NOTO_COLOR_EMOJI_LICENSE.txt +93 -0
- dataface/core/render/fonts/NOTO_EMOJI_LICENSE.txt +93 -0
- dataface/core/render/fonts/NotoColorEmoji-Regular.ttf +0 -0
- dataface/core/render/fonts/NotoColorEmoji-Regular.woff2 +0 -0
- dataface/core/render/fonts/NotoEmoji-Regular.ttf +0 -0
- dataface/core/render/fonts/NotoEmoji-Regular.woff2 +0 -0
- dataface/core/render/fonts/SOURCE_CODE_PRO_LICENSE.txt +93 -0
- dataface/core/render/fonts/SOURCE_SERIF_4_LICENSE.txt +98 -0
- dataface/core/render/fonts/SourceCodePro-Regular.ttf +0 -0
- dataface/core/render/fonts/SourceSerif4-Regular.ttf +0 -0
- dataface/core/render/fonts/_emoji_font_face.css +43 -0
- dataface/core/render/fonts/source-serif-4-variable-latin.woff2 +0 -0
- dataface/core/render/format_utils.py +329 -0
- dataface/core/render/geo_defaults.yml +28 -0
- dataface/core/render/json_format.py +146 -0
- dataface/core/render/layout_sizing.py +865 -0
- dataface/core/render/layouts.py +541 -0
- dataface/core/render/markdown_defaults.yml +16 -0
- dataface/core/render/missing_vars_prompt.py +79 -0
- dataface/core/render/placeholder.py +389 -0
- dataface/core/render/render_result.py +14 -0
- dataface/core/render/renderer.py +467 -0
- dataface/core/render/script_embedding.py +16 -0
- dataface/core/render/svg_utils.py +212 -0
- dataface/core/render/template_loader.py +69 -0
- dataface/core/render/templates/controls/_styles.css +606 -0
- dataface/core/render/templates/controls/checkbox.html +16 -0
- dataface/core/render/templates/controls/date.html +16 -0
- dataface/core/render/templates/controls/number.html +19 -0
- dataface/core/render/templates/controls/readonly.html +9 -0
- dataface/core/render/templates/controls/select.html +21 -0
- dataface/core/render/templates/controls/slider.html +22 -0
- dataface/core/render/templates/controls/text.html +16 -0
- dataface/core/render/templates/scripts/chart_interactivity.js +191 -0
- dataface/core/render/templates/scripts/variables.js +976 -0
- dataface/core/render/templates/svg/grid_pattern.svg +3 -0
- dataface/core/render/templates/svg/styles.css +51 -0
- dataface/core/render/terminal.py +311 -0
- dataface/core/render/terminal_charts.py +563 -0
- dataface/core/render/terminal_defaults.yml +2 -0
- dataface/core/render/terminal_layouts.py +299 -0
- dataface/core/render/terminal_text.py +31 -0
- dataface/core/render/text/__init__.py +1 -0
- dataface/core/render/text/case.py +113 -0
- dataface/core/render/text_format.py +129 -0
- dataface/core/render/utils.py +106 -0
- dataface/core/render/variable_controls.py +946 -0
- dataface/core/render/variable_input_refinement.py +140 -0
- dataface/core/render/warnings/__init__.py +15 -0
- dataface/core/render/warnings/bar_color_1_to_1_with_x.py +80 -0
- dataface/core/render/warnings/base.py +44 -0
- dataface/core/render/warnings/fanout_risk.py +15 -0
- dataface/core/render/warnings/from_query_diagnostic.py +56 -0
- dataface/core/render/warnings/missing_join_predicate.py +13 -0
- dataface/core/render/warnings/query_parse_error.py +14 -0
- dataface/core/render/warnings/query_returned_zero_rows.py +42 -0
- dataface/core/render/warnings/reaggregation.py +14 -0
- dataface/core/render/warnings/registry.py +45 -0
- dataface/core/render/warnings/suppression.py +46 -0
- dataface/core/render/warnings/temporal_single_point.py +63 -0
- dataface/core/render/warnings/unreferenced_chart.py +15 -0
- dataface/core/render/warnings/y_encoding_mostly_null.py +76 -0
- dataface/core/render/yaml_format.py +167 -0
- dataface/core/resolve_face.py +195 -0
- dataface/core/schema/__init__.py +0 -0
- dataface/core/schema/guidance.py +151 -0
- dataface/core/scoped_paths.py +59 -0
- dataface/core/serve/__init__.py +14 -0
- dataface/core/serve/bootstrap.py +39 -0
- dataface/core/serve/embedded.py +57 -0
- dataface/core/serve/port.py +129 -0
- dataface/core/serve/server.py +938 -0
- dataface/core/serve/templates/__init__.py +0 -0
- dataface/core/serve/templates/directory.yml +6 -0
- dataface/core/serve/templates/error.html.j2 +217 -0
- dataface/core/utils.py +121 -0
- dataface/core/validate.py +64 -0
- dataface/integrations/__init__.py +0 -0
- dataface/integrations/highlighting.py +351 -0
- dataface/integrations/markdown.py +537 -0
- dataface/py.typed +0 -0
- dataface-0.1.2.dist-info/METADATA +375 -0
- dataface-0.1.2.dist-info/RECORD +455 -0
- dataface-0.1.2.dist-info/WHEEL +4 -0
- dataface-0.1.2.dist-info/entry_points.txt +2 -0
- dataface-0.1.2.dist-info/licenses/LICENSE +202 -0
- mdsvg/__init__.py +168 -0
- mdsvg/fonts.py +656 -0
- mdsvg/images.py +299 -0
- mdsvg/parser.py +629 -0
- mdsvg/playground.py +284 -0
- mdsvg/py.typed +2 -0
- mdsvg/renderer.py +1623 -0
- mdsvg/style.py +355 -0
- mdsvg/types.py +200 -0
- mdsvg/utils.py +86 -0
|
@@ -0,0 +1,1135 @@
|
|
|
1
|
+
# Dataface YAML Syntax
|
|
2
|
+
|
|
3
|
+
Authoring reference for Dataface face YAML. Every option in this file is enforced by the compiler (`extra="forbid"` is set on every model — unknown keys are schema errors).
|
|
4
|
+
|
|
5
|
+
Browse with `dft docs` (run with no args for the topic catalog, `dft docs <topic>` for one section, `dft docs all` for the whole file).
|
|
6
|
+
|
|
7
|
+
## Getting Started
|
|
8
|
+
|
|
9
|
+
Dataface workflow — from a dbt project to a running dashboard.
|
|
10
|
+
|
|
11
|
+
### Step 1 — Validate your YAML
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
dft validate faces/my_dashboard.yml
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`dft validate` performs YAML schema + cross-reference validation. It checks that every field, chart reference, query name, and variable is correctly structured. **No database connection is required.**
|
|
18
|
+
|
|
19
|
+
To verify that your database connection works, run a simple query:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
dft query 'SELECT 1' --source <your_source>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
A successful result means your data source is reachable.
|
|
26
|
+
|
|
27
|
+
### Step 2 — Browse your schema
|
|
28
|
+
|
|
29
|
+
Use `dft schema` to explore available tables and columns before writing SQL:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
dft schema # list configured data sources
|
|
33
|
+
dft schema mydb # list schemas in that source
|
|
34
|
+
dft schema mydb main # list tables in that schema
|
|
35
|
+
dft schema mydb main orders # profile the orders table (columns, types, samples)
|
|
36
|
+
dft schema mydb main orders id # profile one column (distribution, sample values)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Never invent column names — always verify them with `dft schema` first.
|
|
40
|
+
|
|
41
|
+
### Step 3 — Build one chart
|
|
42
|
+
|
|
43
|
+
Write the minimal YAML needed for a single chart:
|
|
44
|
+
|
|
45
|
+
```yaml
|
|
46
|
+
source: mydb
|
|
47
|
+
|
|
48
|
+
queries:
|
|
49
|
+
revenue: SELECT month, SUM(amount) AS total FROM orders GROUP BY 1 ORDER BY 1
|
|
50
|
+
|
|
51
|
+
charts:
|
|
52
|
+
revenue_trend:
|
|
53
|
+
query: revenue
|
|
54
|
+
type: line
|
|
55
|
+
x: month
|
|
56
|
+
y: total
|
|
57
|
+
|
|
58
|
+
rows:
|
|
59
|
+
- revenue_trend
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Then validate and render:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
dft validate faces/my_dashboard.yml
|
|
66
|
+
dft render faces/my_dashboard.yml
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Add each additional chart after the previous one validates and renders cleanly.
|
|
70
|
+
|
|
71
|
+
### Step 4 — Serve locally
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
dft serve
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Opens a live-preview server. Edit the YAML and reload the browser to see changes.
|
|
78
|
+
|
|
79
|
+
**See also:** `dft docs cheatsheet` (minimal examples), `dft docs queries`, `dft docs charts`, `dft docs variables`.
|
|
80
|
+
|
|
81
|
+
## Cheatsheet
|
|
82
|
+
|
|
83
|
+
One screen of essentials. Each topic below has a dedicated H2 (`dft docs face`, `dft docs queries`, …) for full coverage.
|
|
84
|
+
|
|
85
|
+
### Minimal face
|
|
86
|
+
|
|
87
|
+
```yaml
|
|
88
|
+
source: my_profile
|
|
89
|
+
|
|
90
|
+
queries:
|
|
91
|
+
revenue: SELECT month, SUM(amount) AS total FROM orders GROUP BY 1 ORDER BY 1
|
|
92
|
+
|
|
93
|
+
charts:
|
|
94
|
+
revenue_trend:
|
|
95
|
+
query: revenue
|
|
96
|
+
type: line
|
|
97
|
+
x: month
|
|
98
|
+
y: total
|
|
99
|
+
|
|
100
|
+
rows:
|
|
101
|
+
- revenue_trend
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Top-level face fields
|
|
105
|
+
|
|
106
|
+
- `title`, `description`, `tags`
|
|
107
|
+
- `source` / `sources` — default and named data connections
|
|
108
|
+
- `variables` — interactive filter controls
|
|
109
|
+
- `queries` — named SQL / CSV / HTTP / dbt / inline queries
|
|
110
|
+
- `charts` — named chart definitions
|
|
111
|
+
- Exactly one of `rows`, `cols`, `grid`, `tabs` (or `text:` for a text-only face)
|
|
112
|
+
- `theme`, `style`, `id`, `width`, `height` — presentation
|
|
113
|
+
|
|
114
|
+
### Queries (named, parameterized, inline)
|
|
115
|
+
|
|
116
|
+
```yaml
|
|
117
|
+
queries:
|
|
118
|
+
# Bare-string form — SQL inherits the face-level source
|
|
119
|
+
revenue: SELECT month, SUM(amount) AS total FROM orders GROUP BY 1 ORDER BY 1
|
|
120
|
+
|
|
121
|
+
# Long form with options
|
|
122
|
+
filtered:
|
|
123
|
+
sql: "SELECT * FROM orders WHERE region = '{{ region }}'"
|
|
124
|
+
source: warehouse
|
|
125
|
+
|
|
126
|
+
# Inline data (no DB needed)
|
|
127
|
+
targets:
|
|
128
|
+
columns: [region, target]
|
|
129
|
+
values:
|
|
130
|
+
- [US, 100]
|
|
131
|
+
- [EU, 80]
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Variables
|
|
135
|
+
|
|
136
|
+
```yaml
|
|
137
|
+
variables:
|
|
138
|
+
region:
|
|
139
|
+
input: select # See `dft docs variables` for all 14 input types
|
|
140
|
+
options: { static: [US, EU, APAC] }
|
|
141
|
+
default: US
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Reference variables inside queries with bare `{{ region }}` — no `variables.` prefix.
|
|
145
|
+
|
|
146
|
+
### Charts
|
|
147
|
+
|
|
148
|
+
```yaml
|
|
149
|
+
charts:
|
|
150
|
+
revenue_trend:
|
|
151
|
+
query: revenue # Named query reference
|
|
152
|
+
type: line # See `dft docs charts` for all 29 chart types
|
|
153
|
+
x: month
|
|
154
|
+
y: total
|
|
155
|
+
color: segment
|
|
156
|
+
|
|
157
|
+
quick_chart:
|
|
158
|
+
query: "SELECT month, revenue FROM orders" # Bare SQL shorthand (no named query needed)
|
|
159
|
+
type: bar
|
|
160
|
+
x: month
|
|
161
|
+
y: revenue
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Layout
|
|
165
|
+
|
|
166
|
+
Pick exactly one of:
|
|
167
|
+
|
|
168
|
+
- `rows: [chart_a, chart_b]` — vertical stack
|
|
169
|
+
- `cols: [chart_a, chart_b]` — horizontal arrangement
|
|
170
|
+
- `grid: { columns: 24, items: [...] }` — CSS-grid placement
|
|
171
|
+
- `tabs: { items: [{title: ..., rows: [...]}] }` — tabbed navigation
|
|
172
|
+
|
|
173
|
+
**See also:** `dft docs face` (full top-level reference),
|
|
174
|
+
`dft docs queries`, `dft docs charts`, `dft docs variables`, `dft docs layout`,
|
|
175
|
+
`dft docs errors` (common error codes), `dft docs all` (whole reference).
|
|
176
|
+
|
|
177
|
+
## Face
|
|
178
|
+
|
|
179
|
+
The top-level YAML mapping is a face. Exactly one layout key (`rows`, `cols`, `grid`, `tabs`) must be present unless `text:` is set.
|
|
180
|
+
|
|
181
|
+
```yaml-schema
|
|
182
|
+
title: "Sales Overview"
|
|
183
|
+
description: "Monthly KPIs and trend"
|
|
184
|
+
tags: [sales, weekly]
|
|
185
|
+
|
|
186
|
+
source: my_profile # Shorthand: default source for every query
|
|
187
|
+
# sources: { default: my_profile } # Equivalent long form
|
|
188
|
+
# sources: { default: warehouse, local_csv: { type: csv, file: data.csv } }
|
|
189
|
+
|
|
190
|
+
variables: # Optional — interactive controls
|
|
191
|
+
queries: # Named queries
|
|
192
|
+
charts: # Named charts
|
|
193
|
+
rows: [ ... ] # Or cols:, grid:, tabs: (pick one)
|
|
194
|
+
|
|
195
|
+
theme: dark # Vega-Lite theme; inherited by nested faces
|
|
196
|
+
|
|
197
|
+
# Nesting / layout primitives (mostly for nested faces inside rows/cols)
|
|
198
|
+
id: my_face # Auto-generated from filename if omitted
|
|
199
|
+
style: { padding: 16, background: "#f8fafc" }
|
|
200
|
+
width: 400 # Pixels or "50%" when nested
|
|
201
|
+
height: 300
|
|
202
|
+
|
|
203
|
+
card_gap: false # When true, adds gap between cards
|
|
204
|
+
chart_focus: revenue_trend # Render only one chart with its dependent variables
|
|
205
|
+
|
|
206
|
+
details: "Click to expand" # Collapsible section
|
|
207
|
+
expanded_title: "Hide details"
|
|
208
|
+
expanded: false
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Top-level fields (23 total):
|
|
212
|
+
|
|
213
|
+
| Field | Type | Notes |
|
|
214
|
+
|-------|------|-------|
|
|
215
|
+
| `title` | string | Display title |
|
|
216
|
+
| `description` | string | Description text |
|
|
217
|
+
| `tags` | list[string] | Tags for categorization/search |
|
|
218
|
+
| `text` | string | Markdown body for text-only faces |
|
|
219
|
+
| `source` | string | Default source shorthand (equivalent to `sources.default`) |
|
|
220
|
+
| `sources` | object | `{default: name, <name>: {type: ...}}` |
|
|
221
|
+
| `variables` | object | See [Variables](#variables) |
|
|
222
|
+
| `queries` | object | See [Queries](#queries) |
|
|
223
|
+
| `charts` | object | See [Charts](#charts) |
|
|
224
|
+
| `rows` | list | Vertical layout — chart names or inline blocks |
|
|
225
|
+
| `cols` | list | Horizontal layout — chart names or inline blocks |
|
|
226
|
+
| `grid` | object | CSS-grid layout (see [Layout](#layout)) |
|
|
227
|
+
| `tabs` | object | Tabbed layout (see [Layout](#layout)) |
|
|
228
|
+
| `card_gap` | bool | Add visible gap between cards (default `false`) |
|
|
229
|
+
| `chart_focus` | string | Render only this chart (with its variables) |
|
|
230
|
+
| `details` | string | Collapsible-section summary text |
|
|
231
|
+
| `expanded_title` | string | Header text when expanded |
|
|
232
|
+
| `expanded` | bool | Default expanded state |
|
|
233
|
+
| `id` | string | Explicit face ID (auto-generated from filename) |
|
|
234
|
+
| `style` | object | Board-style block (see [Board style](#board-style)) |
|
|
235
|
+
| `width` | string \| int | Width when nested (`"50%"` or pixels) |
|
|
236
|
+
| `height` | string \| int | Height when nested |
|
|
237
|
+
| `theme` | string | Vega-Lite theme name (e.g. `dark`, `editorial`, `carbong100`) — inherited by nested faces |
|
|
238
|
+
|
|
239
|
+
`face:` as a top-level key is rejected. Put face properties (title, rows, queries, …) directly at the YAML root.
|
|
240
|
+
|
|
241
|
+
### Board style
|
|
242
|
+
|
|
243
|
+
`style:` on a face, nested face, or layout section accepts a fixed set of keys — not arbitrary CSS:
|
|
244
|
+
|
|
245
|
+
```yaml
|
|
246
|
+
style:
|
|
247
|
+
padding: "16px"
|
|
248
|
+
margin: "0 0 12px 0"
|
|
249
|
+
background: "#f8fafc"
|
|
250
|
+
color: "#0f172a"
|
|
251
|
+
gap: 12
|
|
252
|
+
border:
|
|
253
|
+
width: 1
|
|
254
|
+
color: "#cbd5e1"
|
|
255
|
+
radius: 8
|
|
256
|
+
text:
|
|
257
|
+
align: left
|
|
258
|
+
column:
|
|
259
|
+
number: 2
|
|
260
|
+
gap: 24
|
|
261
|
+
rule: "1px solid #cbd5e1"
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
CSS-only keys like `border-radius`, `border-left`, `margin-top` are not board-style fields and are rejected.
|
|
265
|
+
|
|
266
|
+
#### Title heading levels
|
|
267
|
+
|
|
268
|
+
`style.title.level` controls the H-level (H1–H6) used for face and chart titles.
|
|
269
|
+
|
|
270
|
+
```yaml
|
|
271
|
+
# Default — compute from count of titled ancestors (the recommended default).
|
|
272
|
+
# A titled root face is H1, a titled child section is H2, and so on.
|
|
273
|
+
# Bare layout wrappers (no title) do not advance the counter.
|
|
274
|
+
style:
|
|
275
|
+
title:
|
|
276
|
+
level: auto
|
|
277
|
+
|
|
278
|
+
# Lock level to a specific heading — useful for embedded dashboards where
|
|
279
|
+
# H1/H2 are reserved by the outer page.
|
|
280
|
+
style:
|
|
281
|
+
title:
|
|
282
|
+
level: 3 # this face and all descendants start from H3
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
When set to an integer, it cascades to descendants: titled children render at
|
|
286
|
+
`level + 1`, bare wrappers inherit the locked level unchanged.
|
|
287
|
+
|
|
288
|
+
**See also:** `dft docs queries` (data layer), `dft docs charts` (display layer),
|
|
289
|
+
`dft docs layout` (composition), `dft docs cheatsheet` (one-page essentials).
|
|
290
|
+
|
|
291
|
+
## Queries
|
|
292
|
+
|
|
293
|
+
Queries are the data layer. Charts reference queries by name; queries never embed display logic.
|
|
294
|
+
|
|
295
|
+
```yaml
|
|
296
|
+
source: my_profile # Optional: default source for every query below
|
|
297
|
+
|
|
298
|
+
queries:
|
|
299
|
+
# SQL — the default. `type: sql` is implicit when `sql:` is present.
|
|
300
|
+
revenue: SELECT month, SUM(amount) AS total FROM orders GROUP BY 1 ORDER BY 1
|
|
301
|
+
|
|
302
|
+
# SQL with metadata
|
|
303
|
+
filtered:
|
|
304
|
+
description: Monthly revenue filtered by region
|
|
305
|
+
sql: SELECT * FROM orders WHERE {{ filter('region', region) }}
|
|
306
|
+
source: warehouse # Override face-level source
|
|
307
|
+
setup_sql: CREATE TEMP FUNCTION norm(x FLOAT64) AS (x / 100.0);
|
|
308
|
+
|
|
309
|
+
# CSV file
|
|
310
|
+
targets:
|
|
311
|
+
type: csv
|
|
312
|
+
file: data/targets.csv # Relative to the face YAML file
|
|
313
|
+
columns: [region, target]
|
|
314
|
+
delimiter: ","
|
|
315
|
+
encoding: utf-8
|
|
316
|
+
|
|
317
|
+
# HTTP / REST API
|
|
318
|
+
customers:
|
|
319
|
+
type: http
|
|
320
|
+
url: https://api.example.com/customers
|
|
321
|
+
method: GET # GET | POST | PUT | DELETE | PATCH
|
|
322
|
+
headers: { X-Token: "{{ secrets.token }}" }
|
|
323
|
+
params: { status: active }
|
|
324
|
+
body: { ... }
|
|
325
|
+
json_path: $.data
|
|
326
|
+
|
|
327
|
+
# dbt model — read columns directly from a compiled dbt model
|
|
328
|
+
customers_model:
|
|
329
|
+
type: dbt_model
|
|
330
|
+
model: fct_customers
|
|
331
|
+
columns: [customer_id, arr, region]
|
|
332
|
+
|
|
333
|
+
# MetricFlow / dbt Semantic Layer
|
|
334
|
+
revenue_by_region:
|
|
335
|
+
type: metricflow # Optional — also implied by `metrics:` presence
|
|
336
|
+
metrics: [revenue]
|
|
337
|
+
dimensions: [region]
|
|
338
|
+
time_grain: month # day | week | month | quarter | year
|
|
339
|
+
|
|
340
|
+
# Inline values (no database)
|
|
341
|
+
sample:
|
|
342
|
+
type: values # Optional — also implied by `values:` presence
|
|
343
|
+
columns: [name, score]
|
|
344
|
+
values:
|
|
345
|
+
- [Alice, 92.4]
|
|
346
|
+
- [Bob, 87.1]
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Query types (`type:` literals): `sql`, `csv`, `http`, `dbt_model`, `metricflow`, `values`. `schema_resolver` is internal-only and not part of the authored surface.
|
|
350
|
+
|
|
351
|
+
Common fields (all query types):
|
|
352
|
+
|
|
353
|
+
| Field | Description |
|
|
354
|
+
|-------|-------------|
|
|
355
|
+
| `description` | Metadata sentence for AI search and tooltips |
|
|
356
|
+
| `source` | Source reference (string) or inline source config |
|
|
357
|
+
| `target` | dbt target name (defaults to `dev`) |
|
|
358
|
+
| `filters` | Post-execution result filters |
|
|
359
|
+
| `limit` | Maximum rows returned |
|
|
360
|
+
| `pivot` | Table-rendering cross-tab hint: `{column, value}` |
|
|
361
|
+
| `ignore` | Diagnostic codes to suppress (e.g. `["fanout_risk"]`) |
|
|
362
|
+
|
|
363
|
+
SQL fields: `sql`, `setup_sql`.
|
|
364
|
+
MetricFlow fields: `metrics`, `dimensions`, `time_grain`.
|
|
365
|
+
HTTP fields: `url`, `method`, `headers`, `params`, `body`, `path`.
|
|
366
|
+
CSV fields: `file`, `filter`.
|
|
367
|
+
dbt-model fields: `model`, `columns`.
|
|
368
|
+
Inline-values fields: `columns`, `values` (or `rows` for record-shape).
|
|
369
|
+
|
|
370
|
+
Source types (configured via `sources:` or in the project config): `postgres`, `snowflake`, `bigquery`, `redshift`, `mysql`, `duckdb`, `csv`, `json`, `parquet`, `http`, `dbt_profile`.
|
|
371
|
+
|
|
372
|
+
### Jinja variable injection
|
|
373
|
+
|
|
374
|
+
Queries are Jinja-rendered before execution. Reference variables as bare names — `{{ region }}` — not with a `variables.` namespace prefix.
|
|
375
|
+
|
|
376
|
+
```yaml
|
|
377
|
+
variables:
|
|
378
|
+
region: { input: select, options: { static: [US, EU, APAC] } }
|
|
379
|
+
|
|
380
|
+
queries:
|
|
381
|
+
sales:
|
|
382
|
+
sql: |
|
|
383
|
+
SELECT month, revenue
|
|
384
|
+
FROM orders
|
|
385
|
+
WHERE {{ filter('region', region) }}
|
|
386
|
+
AND {{ filter_date_range('month', period) }}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
For multiline SQL, always use block scalar (`sql: |`). Never use `"SELECT\n…"` or `'SELECT\n…'` — escaped newlines make diffs unreadable and confuse agents learning from examples. Single-line SQL in double quotes is fine: `sql: "SELECT 1 AS n"`.
|
|
390
|
+
|
|
391
|
+
`filter()` and `filter_date_range()` are helper macros that emit safe SQL predicates for select/multiselect and daterange variables respectively.
|
|
392
|
+
|
|
393
|
+
**Common multiselect mistake** — `tojson` produces double-quoted strings; most SQL dialects (including DuckDB) treat `"trial"` as a *column reference*, not a string literal. The query silently returns an empty result and `dft render` exits 0.
|
|
394
|
+
|
|
395
|
+
```sql
|
|
396
|
+
-- WRONG: produces WHERE plan IN ("trial", "pro") — treated as column refs, not strings
|
|
397
|
+
WHERE plan IN ({{ plans | map('tojson') | join(', ') }})
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Use the `filter()` macro instead — it emits correctly quoted predicates:
|
|
401
|
+
|
|
402
|
+
```sql
|
|
403
|
+
-- CORRECT
|
|
404
|
+
WHERE {{ filter('plan', plans) }}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
The `filter()` macro handles `select` (single value → `=`) and `multiselect` (list → `IN (...)`) automatically and quotes all string literals correctly.
|
|
408
|
+
|
|
409
|
+
### Inline query in a chart
|
|
410
|
+
|
|
411
|
+
A chart can carry its own one-off query instead of referencing a named one. Three equivalent forms:
|
|
412
|
+
|
|
413
|
+
```yaml
|
|
414
|
+
charts:
|
|
415
|
+
rev_chart:
|
|
416
|
+
# Shorthand — bare SQL string (simplest form)
|
|
417
|
+
query: "SELECT month, revenue FROM orders"
|
|
418
|
+
type: bar
|
|
419
|
+
x: month
|
|
420
|
+
y: revenue
|
|
421
|
+
|
|
422
|
+
rev_chart2:
|
|
423
|
+
# Explicit inline dict
|
|
424
|
+
query:
|
|
425
|
+
sql: "SELECT month, revenue FROM orders"
|
|
426
|
+
type: bar
|
|
427
|
+
x: month
|
|
428
|
+
y: revenue
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
The bare-string shorthand works whenever the value contains a SQL keyword (`SELECT`, `WITH`, `INSERT`, etc.) and is not already a named query. Use `query: {sql: ...}` when you need additional query options (`source:`, `description:`, etc.).
|
|
432
|
+
|
|
433
|
+
Inline queries are not reusable. Prefer named queries when more than one chart consumes the data.
|
|
434
|
+
|
|
435
|
+
**See also:** `dft docs variables` (use `{{ var }}` in SQL),
|
|
436
|
+
`dft docs charts` (charts reference queries by name),
|
|
437
|
+
`dft docs errors` (query-execution error codes).
|
|
438
|
+
|
|
439
|
+
## Charts
|
|
440
|
+
|
|
441
|
+
Each chart binds a query to a chart type and an encoding. Unknown chart fields are rejected.
|
|
442
|
+
|
|
443
|
+
```yaml
|
|
444
|
+
charts:
|
|
445
|
+
revenue_trend:
|
|
446
|
+
query: revenue # Name of a query (or inline query def)
|
|
447
|
+
type: line # See chart types below
|
|
448
|
+
title: "Revenue"
|
|
449
|
+
subtitle: "Last 30 days"
|
|
450
|
+
description: "AI/tooltip metadata about what this chart answers."
|
|
451
|
+
|
|
452
|
+
# Data mapping (the channels)
|
|
453
|
+
x: month # column name
|
|
454
|
+
y: total # column name OR [col, col, ...] for multi-series
|
|
455
|
+
color: segment # column | {value: "#ccc"} | {field, scale} | {field, when}
|
|
456
|
+
|
|
457
|
+
# Sizing — live at chart root, NOT under style:
|
|
458
|
+
height: 400 # exact px; bypasses aspect_ratio and min/max clamps
|
|
459
|
+
aspect_ratio: 2.0 # shape without a fixed size; height = width / aspect_ratio
|
|
460
|
+
# height and aspect_ratio are ignored on kpi, table, callout, spark_bar
|
|
461
|
+
|
|
462
|
+
# Style + behavior
|
|
463
|
+
sort: { by: total, order: desc }
|
|
464
|
+
stack: zero # false | zero | normalize | center
|
|
465
|
+
format: integer # named alias or raw D3 spec (e.g. ",.0f")
|
|
466
|
+
x_label: "Month"
|
|
467
|
+
y_label: "Revenue (USD)"
|
|
468
|
+
link: "/orders?month={{ month }}" # Click-through URL template (drill-down)
|
|
469
|
+
filters: { ... } # Result filters
|
|
470
|
+
|
|
471
|
+
style: # Chart-local style patch (typed; not raw CSS) — paint only
|
|
472
|
+
orientation: vertical # style: does NOT accept height or aspect_ratio
|
|
473
|
+
stack: true
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
### Chart types (29 total)
|
|
477
|
+
|
|
478
|
+
Set `type:` to one of:
|
|
479
|
+
|
|
480
|
+
**Basic** (one mark per chart) — `bar`, `line`, `area`, `scatter`, `pie`, `donut`, `kpi`, `table`.
|
|
481
|
+
|
|
482
|
+
**Statistical** — `boxplot`, `errorbar`, `errorband`, `histogram`, `heatmap`.
|
|
483
|
+
|
|
484
|
+
**Geographic** — `geoshape`, `map`, `point_map`, `bubble_map`.
|
|
485
|
+
|
|
486
|
+
**Marks** (direct Vega-Lite mark passthroughs; used inside `layered` mostly) — `circle`, `square`, `tick`, `rule`, `trail`, `rect`, `arc`, `image`.
|
|
487
|
+
|
|
488
|
+
**Composition** — `layered` (see [Composition](#composition)).
|
|
489
|
+
|
|
490
|
+
**Sparklines** — `spark_bar` (compact horizontal bars used in profiler cards).
|
|
491
|
+
|
|
492
|
+
**Auxiliary** — `callout` (message card with a `style.tone:` field; `message:` required), `auto` (auto-detect from data — internal-only; not surfaced in UI dropdowns).
|
|
493
|
+
|
|
494
|
+
Aliases that map to underlying marks: `scatter` → `circle`, `heatmap` → `rect`, `pie` / `donut` → `arc`, `histogram` → `bar` with binning, `map` → `geoshape`.
|
|
495
|
+
|
|
496
|
+
### Shared chart fields
|
|
497
|
+
|
|
498
|
+
All chart types accept the channels and style fields below — but each type rejects fields that don't belong to it (e.g. `theta` on a bar chart, `x` on a pie chart).
|
|
499
|
+
|
|
500
|
+
| Field | Type | Description |
|
|
501
|
+
|-------|------|-------------|
|
|
502
|
+
| `x` | string | X-axis field |
|
|
503
|
+
| `y` | string \| list[string] | Y-axis field(s) — list for multi-series |
|
|
504
|
+
| `color` | string \| object | Field name, `{value}`, `{field, scale}`, or `{field, when}` |
|
|
505
|
+
| `size` | string | Field for size encoding |
|
|
506
|
+
| `shape` | string | Field for shape encoding |
|
|
507
|
+
| `opacity` | string \| object | Field name or `{field, scale}` |
|
|
508
|
+
| `stroke` | object | `{color, width}` — each accepts field/scale/when |
|
|
509
|
+
| `theta` | string | Angular field (pie/donut/arc) |
|
|
510
|
+
| `style.inner_radius` | float 0–1 | Donut hole ratio (hole/outer disk; pie/donut only); `type: donut` sets it to `0.6` automatically |
|
|
511
|
+
| `total` | object | `{label, format}` — center total for donut |
|
|
512
|
+
| `labels` | object | `{template, where}` — per-row Jinja annotations near anchors |
|
|
513
|
+
| `x_label` | string | X-axis title override |
|
|
514
|
+
| `y_label` | string | Y-axis title override |
|
|
515
|
+
| `format` | string \| object | D3 format string, preset name, or FormatConfig |
|
|
516
|
+
| `message` | string | Static text for `type: callout` |
|
|
517
|
+
| `geo` | string \| object | GeoJSON field or inline spec (geoshape) |
|
|
518
|
+
| `geo_source` | string | Named geographic data source |
|
|
519
|
+
| `lookup` | string | Field that joins to the geographic source key |
|
|
520
|
+
| `value` | string | KPI: column reference (string column name); map: data field for fill color |
|
|
521
|
+
| `projection` | string \| object | Projection name (e.g. `mercator`, `albersUsa`) or VL projection config |
|
|
522
|
+
| `latitude` | string | Latitude field (point/bubble map) |
|
|
523
|
+
| `longitude` | string | Longitude field (point/bubble map) |
|
|
524
|
+
| `background` | string \| object | Background channel — color, `{value}`, `{field, scale/when}`, or map layer |
|
|
525
|
+
| `sort` | object | `{by, order}` — categorical sort |
|
|
526
|
+
| `stack` | bool \| enum | `false`, `zero`, `normalize`, or `center` |
|
|
527
|
+
| `link` | string | Click-through URL template for drill-down links |
|
|
528
|
+
| `filters` | object | Post-execution row filters: `{col: var_name}` (implicit `eq`) or `{col: {op: var}}` where op is one of `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `like`, `ilike`, `in`, `not_in`, `between`. Implicit-eq values may also be Jinja templates (e.g. `"{{ region }}"`) resolved at execute time. A filter is skipped when the variable is `None`, `""`, or renders to `"none"`. |
|
|
529
|
+
| `layers` | list | Layer definitions for `type: layered` (see [Composition](#composition)) |
|
|
530
|
+
| `conditional_formatting` | object | Discrete style rules by column (see [Conditional formatting](#conditional-formatting)) |
|
|
531
|
+
| `data_table` | list | Attached mini-table beneath bar/line/area/layered (see [Composition](#composition)) |
|
|
532
|
+
| `height` | int \| float | Exact pixel height. Wins over `aspect_ratio` and theme defaults. Bypasses `min_height`/`max_height`. Not valid on `kpi`, `table`, `callout`, `spark_bar`. |
|
|
533
|
+
| `aspect_ratio` | float | Chart shape: `height = width / aspect_ratio`. Theme default is `1.5`. Not valid on `kpi`, `table`, `callout`, `spark_bar`. |
|
|
534
|
+
| `min_height` | float | Height floor for this chart only; overrides `style.charts.min_height`. Ignored when `height` is set. |
|
|
535
|
+
| `max_height` | float | Height ceiling for this chart only; overrides `style.charts.max_height`. Ignored when `height` is set. |
|
|
536
|
+
|
|
537
|
+
`height` and `aspect_ratio` live at **chart root** — they are rejected under `style:`. `style:` is paint only (colors, fonts, marks).
|
|
538
|
+
|
|
539
|
+
KPI-only fields: `value`, `label`, `support`. KPI uses `label:` for the header text — `title:` is rejected on KPI charts. `glyph` and `tone` moved into the style namespace: use `style.glyph.character` and `style.tone`. To override glyph/value color, use `style.kpi.glyph.font.color` / `style.kpi.value.font.color`.
|
|
540
|
+
|
|
541
|
+
Top-level chart fields shared by all types: `id`, `query`, `type`, `title`, `subtitle`, `description`, `height`, `aspect_ratio`, `style`, `link`, `filters`, `conditional_formatting`.
|
|
542
|
+
|
|
543
|
+
### Chart-type cheatsheet
|
|
544
|
+
|
|
545
|
+
Each block below shows the minimum-viable shape for one chart type. They are stacked for compactness; in a face, each chart sits under its own key in `charts:`.
|
|
546
|
+
|
|
547
|
+
```yaml-schema
|
|
548
|
+
# bar — categorical x, numeric y
|
|
549
|
+
type: bar
|
|
550
|
+
x: category
|
|
551
|
+
y: value
|
|
552
|
+
color: group # Optional second dimension; stacks by default
|
|
553
|
+
|
|
554
|
+
# line — temporal/ordered x, numeric y
|
|
555
|
+
type: line
|
|
556
|
+
x: date
|
|
557
|
+
y: revenue
|
|
558
|
+
color: segment # Optional: one line per segment
|
|
559
|
+
|
|
560
|
+
# area — same encoding as line; filled below
|
|
561
|
+
type: area
|
|
562
|
+
x: date
|
|
563
|
+
y: value
|
|
564
|
+
stack: normalize # 100% stacked
|
|
565
|
+
|
|
566
|
+
# scatter — x and y numeric
|
|
567
|
+
type: scatter
|
|
568
|
+
x: spend
|
|
569
|
+
y: revenue
|
|
570
|
+
color: region
|
|
571
|
+
size: volume # Optional bubble size
|
|
572
|
+
|
|
573
|
+
# pie / donut — pre-aggregated; one row per segment
|
|
574
|
+
type: pie
|
|
575
|
+
theta: revenue
|
|
576
|
+
color: segment
|
|
577
|
+
style:
|
|
578
|
+
inner_radius: 0.6 # 0 = solid pie; >0 = donut (type: donut sets 0.6 by default)
|
|
579
|
+
total:
|
|
580
|
+
label: Total
|
|
581
|
+
format: integer
|
|
582
|
+
labels:
|
|
583
|
+
template: "{{ segment }}\n{{ revenue | format(',.0f') }}"
|
|
584
|
+
|
|
585
|
+
# kpi — requires exactly 1 row; value: is a column reference
|
|
586
|
+
type: kpi
|
|
587
|
+
value: total_revenue # column name (always a column reference)
|
|
588
|
+
label: "Total Revenue" # NOT `title:` — `title:` is rejected on KPI
|
|
589
|
+
style:
|
|
590
|
+
glyph:
|
|
591
|
+
name: "▲" # glyph character; moved from chart root (ADR-001)
|
|
592
|
+
tone: positive # positive | negative | warning; moved from chart root (ADR-001)
|
|
593
|
+
# To override glyph or value color: style.kpi.glyph.font.color / style.kpi.value.font.color
|
|
594
|
+
support: # Optional support line (same shape: value/label/format/glyph/tone)
|
|
595
|
+
value: prev_revenue
|
|
596
|
+
label: "vs last month"
|
|
597
|
+
format: percent_delta
|
|
598
|
+
glyph: "▲"
|
|
599
|
+
tone: positive
|
|
600
|
+
|
|
601
|
+
# table — renders all query columns unless `style.columns` selects a subset
|
|
602
|
+
type: table
|
|
603
|
+
style:
|
|
604
|
+
columns:
|
|
605
|
+
- column: order_id
|
|
606
|
+
- column: amount
|
|
607
|
+
label: Amount
|
|
608
|
+
format: currency_whole
|
|
609
|
+
align: right # left | center | right
|
|
610
|
+
header_overflow: wrap-two # clip | truncate | wrap-two | wrap
|
|
611
|
+
header_link: "/columns/amount"
|
|
612
|
+
link: "/orders?id={{ order_id }}"
|
|
613
|
+
background: "#fafafa"
|
|
614
|
+
font: { color: "#0f172a", weight: "600" }
|
|
615
|
+
scale:
|
|
616
|
+
background:
|
|
617
|
+
palette: ["#f8fafc", "#bae6fd", "#0369a1"]
|
|
618
|
+
domain: data # currently only "data"
|
|
619
|
+
min: 0
|
|
620
|
+
max: 1000000
|
|
621
|
+
null_color: "#eeeeee"
|
|
622
|
+
hinge: auto # number | "auto"
|
|
623
|
+
arm_mode: asymmetric # asymmetric | symmetric
|
|
624
|
+
spark:
|
|
625
|
+
type: line # line | area | bar | bar-normalize | columns
|
|
626
|
+
color: "#0369a1"
|
|
627
|
+
height: 24
|
|
628
|
+
last_visible: true
|
|
629
|
+
width: 120 # int (px) or string
|
|
630
|
+
glyph: "!"
|
|
631
|
+
glyph_color: "#92400e"
|
|
632
|
+
|
|
633
|
+
# heatmap — pre-aggregated; one row per (x, y) cell
|
|
634
|
+
type: heatmap
|
|
635
|
+
x: day_of_week
|
|
636
|
+
y: hour
|
|
637
|
+
color: event_count
|
|
638
|
+
|
|
639
|
+
# histogram — pre-bin in SQL OR use Vega-Lite binning behavior
|
|
640
|
+
type: histogram
|
|
641
|
+
x: amount
|
|
642
|
+
|
|
643
|
+
# boxplot / errorbar / errorband — statistical primitives
|
|
644
|
+
type: boxplot
|
|
645
|
+
x: region
|
|
646
|
+
y: latency
|
|
647
|
+
|
|
648
|
+
# geoshape — choropleth via GeoJSON
|
|
649
|
+
type: geoshape
|
|
650
|
+
geo_source: us-states
|
|
651
|
+
lookup: state
|
|
652
|
+
value: revenue
|
|
653
|
+
|
|
654
|
+
# map — generic choropleth (alias for geoshape with named source)
|
|
655
|
+
type: map
|
|
656
|
+
geo_source: us-states
|
|
657
|
+
lookup: state
|
|
658
|
+
value: revenue
|
|
659
|
+
|
|
660
|
+
# point_map / bubble_map — lat/lng points (optional size for bubble)
|
|
661
|
+
type: point_map
|
|
662
|
+
latitude: lat
|
|
663
|
+
longitude: lng
|
|
664
|
+
|
|
665
|
+
type: bubble_map
|
|
666
|
+
latitude: lat
|
|
667
|
+
longitude: lng
|
|
668
|
+
size: events
|
|
669
|
+
color: severity
|
|
670
|
+
|
|
671
|
+
# spark_bar — compact horizontal bars (used inline in profiler cards)
|
|
672
|
+
type: spark_bar
|
|
673
|
+
x: rank
|
|
674
|
+
y: count
|
|
675
|
+
|
|
676
|
+
# Pure marks (typically inside layered):
|
|
677
|
+
# circle, square, tick, rule, trail, rect, arc, image
|
|
678
|
+
|
|
679
|
+
# callout — message card with a style.tone: field (negative | warning | positive)
|
|
680
|
+
type: callout
|
|
681
|
+
message: "Query is disabled in this environment."
|
|
682
|
+
style:
|
|
683
|
+
tone: warning # optional; defaults to negative
|
|
684
|
+
|
|
685
|
+
# auto — internal; engine picks the chart type from the data shape.
|
|
686
|
+
type: auto
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
### Layered charts
|
|
690
|
+
|
|
691
|
+
```yaml
|
|
692
|
+
type: layered
|
|
693
|
+
x: month # Chart-level shared x
|
|
694
|
+
layers:
|
|
695
|
+
- type: bar # bar | line | area | circle | square | tick | rule | trail | rect | image | scatter
|
|
696
|
+
y: actual
|
|
697
|
+
label: Actual
|
|
698
|
+
fill: "#0369a1" # static paint (use fill:, not color: {value:})
|
|
699
|
+
- type: line
|
|
700
|
+
y: target
|
|
701
|
+
label: Target
|
|
702
|
+
axis_y:
|
|
703
|
+
orient: right # left | right
|
|
704
|
+
title: "Target"
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
Each layer accepts: `type`, `query` (optional layer-specific query), `x`, `y`, `label`, `color` (data channel, bare field name), `fill` (static hex paint for fixed-color marks), `size`, `shape`, `axis_y`. Vega-Lite `encoding:` is not allowed inside a layer — use the typed channels.
|
|
708
|
+
|
|
709
|
+
### Conditional formatting
|
|
710
|
+
|
|
711
|
+
Discrete, rule-driven style overrides applied per column. Each entry under `conditional_formatting:` is keyed by column name and contains a `when:` list of rules.
|
|
712
|
+
|
|
713
|
+
```yaml
|
|
714
|
+
charts:
|
|
715
|
+
accounts_table:
|
|
716
|
+
type: table
|
|
717
|
+
query: accounts
|
|
718
|
+
conditional_formatting:
|
|
719
|
+
status:
|
|
720
|
+
when:
|
|
721
|
+
- in: [blocked, escalated] # predicate (exactly one per rule)
|
|
722
|
+
background: "#fee2e2" # style output (at least one required)
|
|
723
|
+
font:
|
|
724
|
+
color: "#991b1b"
|
|
725
|
+
weight: "600"
|
|
726
|
+
- is_null: true
|
|
727
|
+
glyph: "!"
|
|
728
|
+
glyph_color: "#92400e"
|
|
729
|
+
- default: true # must be LAST in the list when present
|
|
730
|
+
font: { color: "#334155" }
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
Predicates (exactly one per rule): `eq`, `ne`, `lt`, `lte`, `gt`, `gte`, `between` (`[low, high]`), `in` (non-empty list), `is_null` (bool), `default` (`true` only — terminal fallback).
|
|
734
|
+
|
|
735
|
+
Style outputs (at least one per rule): `background`, `font` (color/weight/style/decoration), `glyph` (with optional `glyph_color`).
|
|
736
|
+
|
|
737
|
+
If `default: true` is set, it must be the last rule in the `when` list. Earlier rules win in order.
|
|
738
|
+
|
|
739
|
+
### Composition
|
|
740
|
+
|
|
741
|
+
Dataface composes charts in three ways:
|
|
742
|
+
|
|
743
|
+
1. **`layers:` on `type: layered`** — multiple marks share one x-axis. Listed under [Layered charts](#layered-charts) above. Each layer can have its own `query:`; otherwise all layers share the chart's `query:` (required for `data_table`).
|
|
744
|
+
|
|
745
|
+
2. **`data_table:` attached to a chart** — a mini cross-tab strip rendered below the chart, columns aligned to the chart's x-axis ticks. Supported on `bar`, `line`, `area`, `layered` only.
|
|
746
|
+
|
|
747
|
+
```yaml
|
|
748
|
+
charts:
|
|
749
|
+
revenue_trend:
|
|
750
|
+
type: line
|
|
751
|
+
query: monthly
|
|
752
|
+
x: month
|
|
753
|
+
y: revenue
|
|
754
|
+
data_table:
|
|
755
|
+
- source: revenue # Read raw per-x value
|
|
756
|
+
label: "Revenue"
|
|
757
|
+
format: integer
|
|
758
|
+
- aggregate: sum # Per-x aggregate
|
|
759
|
+
source: orders
|
|
760
|
+
format: integer
|
|
761
|
+
label: "Orders"
|
|
762
|
+
- aggregate: avg
|
|
763
|
+
source: order_value
|
|
764
|
+
- per_series: revenue # One row per color: series (requires `color:` on the chart)
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
Each entry is one of three shapes (discriminated by which key is present):
|
|
768
|
+
- `source:` — read the raw per-x value (no aggregation).
|
|
769
|
+
- `aggregate:` + `source:` — apply an aggregate per x-group (`sum`, `avg`, `min`, `max`, `median`, `count`, `count_distinct` — exact names only, no aliases).
|
|
770
|
+
- `per_series:` — expand into one row per `color:` series (requires the chart to have a `color:` channel).
|
|
771
|
+
|
|
772
|
+
Optional per-entry fields: `format` (D3 format string), `label` (left-stub row label; not allowed on `per_series:`).
|
|
773
|
+
|
|
774
|
+
Constraint: data_table requires a single chart-level `x:`. Layered charts with per-layer `x:` differing from the chart-level x are rejected.
|
|
775
|
+
|
|
776
|
+
3. **Layout composition** — the `rows`, `cols`, `grid`, `tabs` structure in [Layout](#layout). Layout composes charts into a face; chart-level composition belongs to `layered:` and `data_table:`.
|
|
777
|
+
|
|
778
|
+
Non-goals (not part of the authored chart surface): Vega-Lite `encoding`, `mark`, `spec`, `config`, `transform`, `params`, `resolve`, `hconcat`, `vconcat`, `concat`, `repeat`. These keys are rejected at compile time. Use the typed channels (`x`, `y`, `color`, …) and `style:` instead; use the layout primitives for visual composition.
|
|
779
|
+
|
|
780
|
+
### Custom chart plugins
|
|
781
|
+
|
|
782
|
+
Project-local chart types can extend the engine. Drop a `chart_types/<name>.yml` file next to the face files and reference the name in `type:`.
|
|
783
|
+
|
|
784
|
+
```yaml
|
|
785
|
+
# chart_types/funnel.yml
|
|
786
|
+
name: funnel
|
|
787
|
+
description: Funnel chart for conversion visualization
|
|
788
|
+
mark: bar # Underlying Vega-Lite mark
|
|
789
|
+
fields:
|
|
790
|
+
stage:
|
|
791
|
+
required: true
|
|
792
|
+
description: Stage field
|
|
793
|
+
value:
|
|
794
|
+
required: true
|
|
795
|
+
description: Value field
|
|
796
|
+
encoding_overrides:
|
|
797
|
+
y:
|
|
798
|
+
type: nominal
|
|
799
|
+
sort: null
|
|
800
|
+
x:
|
|
801
|
+
type: quantitative
|
|
802
|
+
mark_properties:
|
|
803
|
+
cornerRadius: 4
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
Then in a face:
|
|
807
|
+
|
|
808
|
+
```yaml-schema
|
|
809
|
+
charts:
|
|
810
|
+
conversion_funnel:
|
|
811
|
+
query: stages
|
|
812
|
+
type: funnel # The plugin name; no `custom:` prefix
|
|
813
|
+
# The plugin's required fields must be present in the data.
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
Plugin file fields:
|
|
817
|
+
|
|
818
|
+
| Field | Required | Description |
|
|
819
|
+
|-------|----------|-------------|
|
|
820
|
+
| `name` | yes | Lowercase identifier (regex `[a-z][a-z0-9_]*`) — must not shadow a built-in |
|
|
821
|
+
| `mark` | yes | Vega-Lite mark name (must be a valid VL mark) |
|
|
822
|
+
| `description` | no | Used in docs and AI prompts |
|
|
823
|
+
| `fields` | no | `{<name>: {required: bool, description: string}}` |
|
|
824
|
+
| `encoding_overrides` | no | Per-channel Vega-Lite encoding properties merged on top of standard mapping |
|
|
825
|
+
| `mark_properties` | no | Extra properties merged into the mark dict |
|
|
826
|
+
|
|
827
|
+
Constraints:
|
|
828
|
+
- A plugin cannot shadow a built-in chart type name.
|
|
829
|
+
- The `mark` value must be a valid Vega-Lite mark (validated at load time).
|
|
830
|
+
- Unknown YAML keys in the plugin file are rejected — typos are caught up front.
|
|
831
|
+
|
|
832
|
+
**See also:** `dft docs queries` (charts reference queries by name),
|
|
833
|
+
`dft docs variables` (use `{{ var }}` in chart queries),
|
|
834
|
+
`dft docs layout` (compose charts on the page),
|
|
835
|
+
`dft docs cheatsheet` (one-page essentials).
|
|
836
|
+
|
|
837
|
+
## Variables
|
|
838
|
+
|
|
839
|
+
Variables are the interactive filter layer. Each variable has an `input:` widget type and optional defaults / options.
|
|
840
|
+
|
|
841
|
+
```yaml
|
|
842
|
+
variables:
|
|
843
|
+
region:
|
|
844
|
+
input: select
|
|
845
|
+
label: "Region"
|
|
846
|
+
description: "Restrict every query to one region."
|
|
847
|
+
options:
|
|
848
|
+
static: [US, EU, APAC]
|
|
849
|
+
default: US
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
Input types (14 total):
|
|
853
|
+
|
|
854
|
+
| Input | Widget |
|
|
855
|
+
|-------|--------|
|
|
856
|
+
| `auto` | Auto-detect from `options` shape (the default) |
|
|
857
|
+
| `select` | Single-value dropdown |
|
|
858
|
+
| `multiselect` | Multi-value dropdown |
|
|
859
|
+
| `input` | Plain text input (alias for `text` in some surfaces) |
|
|
860
|
+
| `text` | Free-text input |
|
|
861
|
+
| `textarea` | Multi-line text input |
|
|
862
|
+
| `number` | Numeric input |
|
|
863
|
+
| `slider` | Single-handle numeric slider |
|
|
864
|
+
| `range` | Two-handle numeric slider (returns `[low, high]`) |
|
|
865
|
+
| `date` | Single date picker (also `datepicker`) |
|
|
866
|
+
| `datepicker` | Single date picker |
|
|
867
|
+
| `daterange` | Date range picker (returns `[start, end]`) |
|
|
868
|
+
| `checkbox` | Boolean toggle |
|
|
869
|
+
| `radio` | Radio group |
|
|
870
|
+
|
|
871
|
+
Common variable fields:
|
|
872
|
+
|
|
873
|
+
| Field | Type | Description |
|
|
874
|
+
|-------|------|-------------|
|
|
875
|
+
| `input` | enum | One of the input types above |
|
|
876
|
+
| `label` | string | UI label |
|
|
877
|
+
| `description` | string | Helper text below the input |
|
|
878
|
+
| `default` | any | Default value when no URL param is set |
|
|
879
|
+
| `placeholder` | string | Placeholder text |
|
|
880
|
+
| `required` | bool | Block rendering until a value exists |
|
|
881
|
+
| `allow_null` | bool | `null` is a valid selection |
|
|
882
|
+
| `visible` | bool | Hidden when `false`; still settable via URL param |
|
|
883
|
+
| `disabled` | bool \| string \| `{query, column}` | Static, Jinja expr, or query-backed disable |
|
|
884
|
+
| `data_type` | string | Upstream type hint (informational; preserved through migrations) |
|
|
885
|
+
|
|
886
|
+
Slider / range fields: `min`, `max`, `step`.
|
|
887
|
+
|
|
888
|
+
Filter-generation field: `operator` — SQL operator used when generating predicates (e.g. `=`, `IN`, `LIKE`).
|
|
889
|
+
|
|
890
|
+
Options sources (for `select`, `multiselect`, `radio`):
|
|
891
|
+
|
|
892
|
+
```yaml
|
|
893
|
+
variables:
|
|
894
|
+
product:
|
|
895
|
+
input: select
|
|
896
|
+
options:
|
|
897
|
+
static: [All, Electronics, Clothing] # Hardcoded list
|
|
898
|
+
# OR
|
|
899
|
+
query: products_list # Query whose first column is the option list
|
|
900
|
+
column: product_name # Optional: which column in that query
|
|
901
|
+
label_column: product_label # Optional: separate label column
|
|
902
|
+
|
|
903
|
+
region:
|
|
904
|
+
input: select
|
|
905
|
+
column: orders.region # Auto-populate from a database column
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
Top-level option-source binding (alternative to `options:`): `column`, `query`, `dimension` (MetricFlow), `measure` (MetricFlow), `model` (dbt).
|
|
909
|
+
|
|
910
|
+
Disabled forms:
|
|
911
|
+
|
|
912
|
+
```yaml
|
|
913
|
+
variables:
|
|
914
|
+
# Static bool
|
|
915
|
+
closed: { input: checkbox, disabled: true }
|
|
916
|
+
|
|
917
|
+
# Jinja expression against current variable values
|
|
918
|
+
q4_only: { input: select, options: { static: [Q4] }, disabled: "{{ year < 2024 }}" }
|
|
919
|
+
|
|
920
|
+
# Query-backed (must return exactly 1 row with the named boolean column)
|
|
921
|
+
territory:
|
|
922
|
+
input: select
|
|
923
|
+
options: { query: territory_options }
|
|
924
|
+
disabled:
|
|
925
|
+
query: control_state
|
|
926
|
+
column: territory_disabled
|
|
927
|
+
```
|
|
928
|
+
|
|
929
|
+
**Multiselect SQL antipattern** — the first instinct for filtering a multiselect variable in SQL is `IN ({{ plans | map('tojson') | join(', ') }})`. This is wrong: `tojson` produces double-quoted strings (`"trial"`), which most SQL dialects (including DuckDB) treat as *column references*, not string literals. The query silently returns an empty result and `dft render` exits 0.
|
|
930
|
+
|
|
931
|
+
Use `{{ filter('plan', plans) }}` instead — it emits a correctly quoted `IN (...)` predicate for multiselect and a simple `=` predicate for single-select:
|
|
932
|
+
|
|
933
|
+
```sql
|
|
934
|
+
-- WRONG: silent empty result
|
|
935
|
+
WHERE plan IN ({{ plans | map('tojson') | join(', ') }})
|
|
936
|
+
|
|
937
|
+
-- CORRECT
|
|
938
|
+
WHERE {{ filter('plan', plans) }}
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
Common mistakes (the validator rejects these — listed here so authors don't hit them):
|
|
942
|
+
|
|
943
|
+
- `options.values: [...]` → use `options.static: [...]`.
|
|
944
|
+
- `default:` nested inside `options:` → put `default:` at the variable level.
|
|
945
|
+
- `options: [a, b]` (bare list) → use `options.static: [a, b]`.
|
|
946
|
+
- Using a `variables.` namespace prefix inside Jinja → drop it and reference the bare variable name (e.g. `{{ region }}`).
|
|
947
|
+
|
|
948
|
+
**See also:** `dft docs queries` (variables are injected into SQL),
|
|
949
|
+
`dft docs face` (`variables:` is a top-level face field),
|
|
950
|
+
`dft docs cheatsheet` (minimal variable example).
|
|
951
|
+
|
|
952
|
+
## Layout
|
|
953
|
+
|
|
954
|
+
Choose exactly one of `rows`, `cols`, `grid`, `tabs` at the face top level (or use `text:` for a text-only face). Layouts nest freely. The block below shows all four side by side; in a real face you pick one.
|
|
955
|
+
|
|
956
|
+
```yaml-schema
|
|
957
|
+
# rows — vertical stack
|
|
958
|
+
rows:
|
|
959
|
+
- cols: [kpi_1, kpi_2, kpi_3] # Equal-width row of charts
|
|
960
|
+
- cols: [big_chart, 2] # big_chart takes 2 fractional columns
|
|
961
|
+
- text: | # Markdown block as a row
|
|
962
|
+
## Trends
|
|
963
|
+
Revenue has been increasing since Q2.
|
|
964
|
+
- revenue_trend # Bare chart name = one chart per row
|
|
965
|
+
- cols: [breakdown_a, breakdown_b]
|
|
966
|
+
- detail_table
|
|
967
|
+
|
|
968
|
+
# cols — horizontal arrangement at the top level
|
|
969
|
+
cols:
|
|
970
|
+
- rows: [kpi_revenue, kpi_users]
|
|
971
|
+
- rows: [trend_chart]
|
|
972
|
+
|
|
973
|
+
# grid — CSS-grid placement with explicit positioning
|
|
974
|
+
grid:
|
|
975
|
+
columns: 24
|
|
976
|
+
default_width: 8
|
|
977
|
+
default_height: 1
|
|
978
|
+
row_height: "120px"
|
|
979
|
+
gap: md # sm | md | lg | xl
|
|
980
|
+
items:
|
|
981
|
+
- item: kpi_revenue
|
|
982
|
+
col: 0
|
|
983
|
+
row: 0
|
|
984
|
+
width: 8 # alias for col_span
|
|
985
|
+
height: 1 # alias for row_span
|
|
986
|
+
- item: trend_chart
|
|
987
|
+
col: 0
|
|
988
|
+
row: 1
|
|
989
|
+
width: 24
|
|
990
|
+
|
|
991
|
+
# tabs — tabbed navigation
|
|
992
|
+
tabs:
|
|
993
|
+
id: view # URL param + variable name (auto if omitted)
|
|
994
|
+
position: top # top | left
|
|
995
|
+
default: overview
|
|
996
|
+
items:
|
|
997
|
+
- title: Overview
|
|
998
|
+
icon: 📊
|
|
999
|
+
description: "KPIs and trend"
|
|
1000
|
+
rows: [kpi_revenue, trend_chart]
|
|
1001
|
+
- title: Details
|
|
1002
|
+
rows: [detail_table]
|
|
1003
|
+
- title: Notes
|
|
1004
|
+
text: |
|
|
1005
|
+
Operational notes go here.
|
|
1006
|
+
style: { padding: 16 }
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
### Section-level fields (per `rows` / `cols` entry)
|
|
1010
|
+
|
|
1011
|
+
```yaml
|
|
1012
|
+
rows:
|
|
1013
|
+
- title: "Revenue overview" # Section heading
|
|
1014
|
+
description: "AI/tooltip context for the section."
|
|
1015
|
+
text: "Monthly trend data." # Markdown narrative above charts
|
|
1016
|
+
details: # Collapsible
|
|
1017
|
+
summary: "Click to expand"
|
|
1018
|
+
expanded_title: "Hide"
|
|
1019
|
+
expanded: false
|
|
1020
|
+
cols: [rev_chart, units_chart] # Use one of: cols, rows, grid, or tabs
|
|
1021
|
+
```
|
|
1022
|
+
|
|
1023
|
+
### Layout visibility (`visible`)
|
|
1024
|
+
|
|
1025
|
+
Layout items and chart references support a `visible` field that omits the item
|
|
1026
|
+
from the rendered output when its condition is falsy.
|
|
1027
|
+
|
|
1028
|
+
Accepted forms:
|
|
1029
|
+
|
|
1030
|
+
| Form | Example | Behaviour |
|
|
1031
|
+
|------|---------|-----------|
|
|
1032
|
+
| Omitted | — | Always shown (default) |
|
|
1033
|
+
| Static bool | `visible: false` | Always hidden / always shown |
|
|
1034
|
+
| Variable name | `visible: show_panel` | Hidden when the variable is falsy; raises if the variable is absent (use `variables.show_panel.default` to ensure it is always defined) |
|
|
1035
|
+
| Jinja expression | `visible: "a and b"` | Evaluated as a boolean Jinja expression; no `{{ }}` required |
|
|
1036
|
+
| Query probe | `visible: {query: flags, column: show_warm}` | Executes the named query; must return **exactly 1 row** with a boolean-coercible value |
|
|
1037
|
+
|
|
1038
|
+
`visible` applies to:
|
|
1039
|
+
- Bare chart name items (`- chart_name`)
|
|
1040
|
+
- Inline chart items (`- query: ... type: ...`)
|
|
1041
|
+
- Nested face items (`rows:`, `cols:`, `text:`, etc.)
|
|
1042
|
+
|
|
1043
|
+
> **Layout reflow:** In `rows:` layouts, hidden items collapse and the next item
|
|
1044
|
+
> moves up. In `cols:`, `grid:`, and `tabs:` layouts, the slot keeps its
|
|
1045
|
+
> pre-computed position — hiding leaves a blank gap. Use `rows:` when you need
|
|
1046
|
+
> items to reflow around hidden entries.
|
|
1047
|
+
|
|
1048
|
+
> **Distinction from `variables.visible`:** `variables.<name>.visible: false` hides the
|
|
1049
|
+
> *input control* in the variable bar only — the variable is still active and URL-settable.
|
|
1050
|
+
> Layout-item `visible` controls whether the item renders in the face at all.
|
|
1051
|
+
|
|
1052
|
+
```yaml
|
|
1053
|
+
variables:
|
|
1054
|
+
show_details:
|
|
1055
|
+
input: checkbox
|
|
1056
|
+
default: false
|
|
1057
|
+
|
|
1058
|
+
rows:
|
|
1059
|
+
- summary_kpis
|
|
1060
|
+
|
|
1061
|
+
- visible: show_details
|
|
1062
|
+
rows:
|
|
1063
|
+
- region_map
|
|
1064
|
+
- region_table
|
|
1065
|
+
```
|
|
1066
|
+
|
|
1067
|
+
### Inline charts inside layout
|
|
1068
|
+
|
|
1069
|
+
A layout entry can carry a full chart definition instead of a chart name:
|
|
1070
|
+
|
|
1071
|
+
```yaml
|
|
1072
|
+
queries:
|
|
1073
|
+
revenue: SELECT month, total FROM rev_daily
|
|
1074
|
+
|
|
1075
|
+
rows:
|
|
1076
|
+
- revenue_line:
|
|
1077
|
+
query: revenue
|
|
1078
|
+
type: line
|
|
1079
|
+
x: month
|
|
1080
|
+
y: total
|
|
1081
|
+
```
|
|
1082
|
+
|
|
1083
|
+
The key under `rows:` becomes the chart's ID.
|
|
1084
|
+
|
|
1085
|
+
### Nested faces
|
|
1086
|
+
|
|
1087
|
+
A layout entry can be a fully nested face (its own rows/cols/charts and optional `id`, `style`, `width`, `height`, `theme`):
|
|
1088
|
+
|
|
1089
|
+
```yaml
|
|
1090
|
+
rows:
|
|
1091
|
+
- id: side_panel
|
|
1092
|
+
width: "30%"
|
|
1093
|
+
style: { padding: 12 }
|
|
1094
|
+
rows:
|
|
1095
|
+
- kpi_1
|
|
1096
|
+
- kpi_2
|
|
1097
|
+
```
|
|
1098
|
+
|
|
1099
|
+
**See also:** `dft docs charts` (chart references inside layouts),
|
|
1100
|
+
`dft docs face` (`rows`/`cols`/`grid`/`tabs` are top-level face fields),
|
|
1101
|
+
`dft docs cheatsheet` (minimal layout examples).
|
|
1102
|
+
|
|
1103
|
+
## Errors
|
|
1104
|
+
|
|
1105
|
+
Dataface errors carry a machine-readable code in the form `DF-<CATEGORY>-<SLUG>`. The error message includes the code and a pointer to docs.
|
|
1106
|
+
|
|
1107
|
+
```
|
|
1108
|
+
DatafaceError [DF-RENDER-KPI-MULTIROW]: KPI chart 'revenue' returned 12 rows but `value` is a column reference.
|
|
1109
|
+
Add LIMIT 1 or aggregate down to one row.
|
|
1110
|
+
```
|
|
1111
|
+
|
|
1112
|
+
Active code categories (see `dataface/core/errors/codes_*.py` for the authoritative registry):
|
|
1113
|
+
|
|
1114
|
+
| Prefix | Meaning |
|
|
1115
|
+
|--------|---------|
|
|
1116
|
+
| `DF-RENDER-*` | Chart rendering errors (wrong row counts, unknown chart types, format issues) |
|
|
1117
|
+
| `DF-EXECUTE-*` | Query execution errors (missing sources, inline-source policy, source typing) |
|
|
1118
|
+
| `DF-UNKNOWN-*` | Fallback codes for legacy string-message errors not yet migrated |
|
|
1119
|
+
|
|
1120
|
+
The registry is being filled out incrementally — many compile-time and variable-resolution errors still raise as plain `DatafaceError` without a structured code. Today's typed codes:
|
|
1121
|
+
|
|
1122
|
+
- **`DF-RENDER-KPI-MULTIROW`** — a KPI chart's query returned more than one row but `value` is a column reference. Add `LIMIT 1` or aggregate down to one row.
|
|
1123
|
+
- **`DF-RENDER-UNKNOWN-CHART-TYPE`** — `type:` is not a recognized chart type. See the [Charts](#charts) section for the list.
|
|
1124
|
+
- **`DF-RENDER-FORMAT-UNSUPPORTED`** — the render `format` argument is not one of `svg`/`html`/`png`.
|
|
1125
|
+
- **`DF-RENDER-NO-LAYOUT`** — the face has no `rows`/`cols`/`grid`/`tabs` and nothing to render.
|
|
1126
|
+
- **`DF-RENDER-INPUT-INVALID`** — the render call received structurally invalid input (e.g. both `path` and `yaml_content`).
|
|
1127
|
+
- **`DF-EXECUTE-SOURCE-NOT-FOUND`** / **`-NOT-FOUND-EMPTY`** — the query's `source:` (or default source) is not configured in `dataface.yml`.
|
|
1128
|
+
- **`DF-EXECUTE-NO-DEFAULT-SOURCE`** — multiple sources are defined and no `default_source` was set.
|
|
1129
|
+
- **`DF-EXECUTE-SOURCE-INLINE-FORBIDDEN`** / **`-CROSS-FILE-FORBIDDEN`** — inline source definitions are restricted by project policy.
|
|
1130
|
+
- **`DF-EXECUTE-SOURCE-INVALID-TYPE`** / **`-MISSING-TYPE`** — a source definition has an unknown or missing `type:` field.
|
|
1131
|
+
|
|
1132
|
+
Run `dft validate <face>` to validate a face and see structured error details.
|
|
1133
|
+
|
|
1134
|
+
**See also:** `dft docs queries`, `dft docs charts`, `dft docs variables` (where most errors originate),
|
|
1135
|
+
`dft docs all` (whole reference for context).
|