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,38 @@
|
|
|
1
|
+
"""Public dft-core connection API.
|
|
2
|
+
|
|
3
|
+
Single exported symbol: test_connection(source_config) → (bool, str).
|
|
4
|
+
|
|
5
|
+
All DB connection machinery lives in execute/adapters/dbt_adapter_factory.py.
|
|
6
|
+
Cloud and other consumers call this, not dbt.adapters directly.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_connection(source_config: dict[str, Any]) -> tuple[bool, str]:
|
|
13
|
+
"""Verify that source_config can reach the database.
|
|
14
|
+
|
|
15
|
+
Constructs a fresh adapter, pings with SELECT 1, and lets GC clean up
|
|
16
|
+
when the local reference drops at function exit. The temp target dir is
|
|
17
|
+
removed by the weakref.finalize registered in build_adapter.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
source_config: Dict with a 'type' key and warehouse-specific fields.
|
|
21
|
+
Example: {"type": "duckdb", "path": ":memory:"}
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
(True, "Connection successful") on success.
|
|
25
|
+
(False, "<error message>") on any failure — driver missing, bad creds,
|
|
26
|
+
network unreachable, unsupported type, etc.
|
|
27
|
+
"""
|
|
28
|
+
from dataface.core.execute.adapters.dbt_adapter_factory import build_adapter
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
adapter = build_adapter(source_config)
|
|
32
|
+
with adapter.connection_named("test"):
|
|
33
|
+
adapter.execute("SELECT 1", auto_begin=True, fetch=True)
|
|
34
|
+
return True, "Connection successful"
|
|
35
|
+
except (
|
|
36
|
+
Exception # noqa: BLE001
|
|
37
|
+
) as e: # broad on purpose — surfaces driver-level errors as messages
|
|
38
|
+
return False, str(e) or type(e).__name__
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
"""Compile + execute + render orchestrator that returns a typed envelope.
|
|
2
|
+
|
|
3
|
+
Counterpart to `core.render.face_api.render_face`, which raises on failure
|
|
4
|
+
and returns raw output for external integrations (MkDocs, Sphinx). This
|
|
5
|
+
module's `render_dashboard` packages compile errors, chart errors, and
|
|
6
|
+
warnings into a `RenderedDashboard` Pydantic model — the shape the CLI
|
|
7
|
+
render verb, the MCP tool, and the embedded HTTP server all consume.
|
|
8
|
+
|
|
9
|
+
The agent_api dashboards module re-exports the public names so callers
|
|
10
|
+
reach them via `dataface.agent_api.dashboards` (the typed-verb surface).
|
|
11
|
+
The implementation lives here — top-level under `core` rather than under
|
|
12
|
+
`core/render/` — so `core.serve.server` can call it without inverting the
|
|
13
|
+
layer stack, and so the `dataface.core.render` function-vs-submodule name
|
|
14
|
+
collision doesn't break `mock.patch("dataface.core.X.dashboard.Y")` on
|
|
15
|
+
Python 3.10 (a real CI failure we hit when this module lived at
|
|
16
|
+
`core/render/dashboard.py`).
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import importlib.util as _importlib_util
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Any, Literal, get_args
|
|
24
|
+
|
|
25
|
+
from pydantic import BaseModel
|
|
26
|
+
|
|
27
|
+
from dataface.core.compile import compile, compile_file, get_project_warnings_ignore
|
|
28
|
+
from dataface.core.compile.errors import DatafaceError
|
|
29
|
+
from dataface.core.errors import (
|
|
30
|
+
DF_RENDER_FORMAT_UNSUPPORTED,
|
|
31
|
+
DF_RENDER_INPUT_INVALID,
|
|
32
|
+
DF_UNKNOWN_INTERNAL,
|
|
33
|
+
StructuredError,
|
|
34
|
+
)
|
|
35
|
+
from dataface.core.errors.structured import NextCommand
|
|
36
|
+
from dataface.core.execute import ExecutionError, Executor
|
|
37
|
+
from dataface.core.execute.adapters import AdapterRegistry
|
|
38
|
+
from dataface.core.execute.duckdb_cache import DuckDBCache
|
|
39
|
+
from dataface.core.render.warnings.base import RenderWarning
|
|
40
|
+
from dataface.core.render.warnings.suppression import partition as _partition_warnings
|
|
41
|
+
|
|
42
|
+
# Probe once at import time: avoids repeated find_spec calls and avoids
|
|
43
|
+
# the `try/except ImportError` antipattern on our own modules.
|
|
44
|
+
_SUPER_SCHEMA_AVAILABLE = _importlib_util.find_spec("dataface_super_schema") is not None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _enrich_fanout_warnings_if_available(result: Any) -> None:
|
|
48
|
+
"""Enrich compiled queries with fanout warnings when private pkg is installed."""
|
|
49
|
+
if not _SUPER_SCHEMA_AVAILABLE:
|
|
50
|
+
return
|
|
51
|
+
from dataface_super_schema.inspect.relationship_warnings import ( # noqa: PLC0415
|
|
52
|
+
enrich_dashboard_fanout_warnings,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
enrich_dashboard_fanout_warnings(result)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
from dataface.core.render import RenderError, render
|
|
59
|
+
from dataface.core.render.board_links import LinkContext
|
|
60
|
+
from dataface.core.scoped_paths import project_root_for, resolve_scoped_path
|
|
61
|
+
|
|
62
|
+
RenderFormat = Literal["svg", "html", "png", "pdf", "terminal", "json", "text", "yaml"]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class RenderedDashboard(BaseModel):
|
|
66
|
+
success: bool
|
|
67
|
+
# bytes for binary formats (png/pdf); str for text-based; dict for json.
|
|
68
|
+
# MCP callers should not request binary formats — `model_dump_json()` /
|
|
69
|
+
# `json.dumps()` in the MCP dispatcher will fail on bytes at the wire
|
|
70
|
+
# boundary, which is correct behaviour.
|
|
71
|
+
data: dict[str, Any] | str | bytes | None = None
|
|
72
|
+
validation_errors: list[StructuredError] = []
|
|
73
|
+
warnings: list[RenderWarning] = []
|
|
74
|
+
suppressed_warnings: list[RenderWarning] = []
|
|
75
|
+
chart_errors: list[StructuredError] = []
|
|
76
|
+
face_error: StructuredError | None = None
|
|
77
|
+
url: str | None = None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _view_url(
|
|
81
|
+
path: Path,
|
|
82
|
+
variables: dict[str, Any] | None = None,
|
|
83
|
+
*,
|
|
84
|
+
port: int | None,
|
|
85
|
+
project_dir: Path | None = None,
|
|
86
|
+
) -> str | None:
|
|
87
|
+
if port is None:
|
|
88
|
+
return None
|
|
89
|
+
from urllib.parse import urlencode
|
|
90
|
+
|
|
91
|
+
route_path = path
|
|
92
|
+
if path.is_absolute():
|
|
93
|
+
if project_dir is None:
|
|
94
|
+
return None
|
|
95
|
+
root = project_dir.resolve()
|
|
96
|
+
try:
|
|
97
|
+
route_path = path.resolve().relative_to(root)
|
|
98
|
+
except ValueError:
|
|
99
|
+
return None
|
|
100
|
+
|
|
101
|
+
url_path = route_path.with_suffix("").as_posix()
|
|
102
|
+
if url_path.startswith("faces/"):
|
|
103
|
+
url_path = url_path.removeprefix("faces/")
|
|
104
|
+
qs = "?" + urlencode(variables) if variables else ""
|
|
105
|
+
return f"http://localhost:{port}/{url_path}{qs}"
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _stamp_file_commands(
|
|
109
|
+
errors: list[StructuredError],
|
|
110
|
+
file_path: Path,
|
|
111
|
+
) -> list[StructuredError]:
|
|
112
|
+
"""Return errors with a 'dft validate <file>' next_command added to each.
|
|
113
|
+
|
|
114
|
+
The compile path produces StructuredErrors without file context. When we
|
|
115
|
+
know the file that was compiled, stamp each error so the serve/HTML layer
|
|
116
|
+
can render the validate button.
|
|
117
|
+
"""
|
|
118
|
+
cmd = NextCommand(label="Validate", command=f"dft validate {file_path}")
|
|
119
|
+
return [
|
|
120
|
+
(
|
|
121
|
+
err.model_copy(update={"next_commands": [cmd]})
|
|
122
|
+
if not err.next_commands
|
|
123
|
+
else err
|
|
124
|
+
)
|
|
125
|
+
for err in errors
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _input_error(message: str) -> RenderedDashboard:
|
|
130
|
+
"""Build a RenderedDashboard failure from a plain input-validation message.
|
|
131
|
+
|
|
132
|
+
Stamped with DF-RENDER-INPUT-INVALID so consumers can distinguish user-supplied
|
|
133
|
+
bad arguments from internal failures.
|
|
134
|
+
"""
|
|
135
|
+
return RenderedDashboard(
|
|
136
|
+
success=False,
|
|
137
|
+
validation_errors=[
|
|
138
|
+
DatafaceError.from_code(
|
|
139
|
+
DF_RENDER_INPUT_INVALID, message=message
|
|
140
|
+
).to_structured()
|
|
141
|
+
],
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def render_dashboard(
|
|
146
|
+
path: Path | None = None,
|
|
147
|
+
yaml_content: str | None = None,
|
|
148
|
+
project_dir: Path | None = None,
|
|
149
|
+
variables: dict[str, Any] | None = None,
|
|
150
|
+
*,
|
|
151
|
+
adapter_registry: AdapterRegistry | None = None,
|
|
152
|
+
format: str = "json",
|
|
153
|
+
server_port: int | None = None,
|
|
154
|
+
use_cache: bool = True,
|
|
155
|
+
scale: float | None = None,
|
|
156
|
+
as_link: bool = False,
|
|
157
|
+
link_context: LinkContext | None = None,
|
|
158
|
+
duckdb_cache: DuckDBCache | None = None,
|
|
159
|
+
ignore_codes: set[str] | None = None,
|
|
160
|
+
) -> RenderedDashboard:
|
|
161
|
+
"""Validate, compile, and render a Dataface dashboard."""
|
|
162
|
+
# as_link=True: compile-only path that returns a preview URL without
|
|
163
|
+
# executing queries — the cheap counterpart to a full render. Requires
|
|
164
|
+
# `path` (the URL points at the saved face on disk).
|
|
165
|
+
if as_link:
|
|
166
|
+
if not path:
|
|
167
|
+
return _input_error("as_link=True requires 'path'")
|
|
168
|
+
try:
|
|
169
|
+
link_file_path = resolve_scoped_path(path, project_dir)
|
|
170
|
+
except ValueError as exc:
|
|
171
|
+
return _input_error(str(exc))
|
|
172
|
+
if not link_file_path.exists():
|
|
173
|
+
return _input_error(f"File not found: {path}")
|
|
174
|
+
result = compile_file(link_file_path)
|
|
175
|
+
if not result.success:
|
|
176
|
+
return RenderedDashboard(
|
|
177
|
+
success=False,
|
|
178
|
+
validation_errors=_stamp_file_commands(result.errors, link_file_path),
|
|
179
|
+
warnings=list(result.warnings),
|
|
180
|
+
suppressed_warnings=list(result.suppressed_warnings),
|
|
181
|
+
)
|
|
182
|
+
return RenderedDashboard(
|
|
183
|
+
success=True,
|
|
184
|
+
warnings=list(result.warnings),
|
|
185
|
+
suppressed_warnings=list(result.suppressed_warnings),
|
|
186
|
+
url=_view_url(
|
|
187
|
+
link_file_path, variables, port=server_port, project_dir=project_dir
|
|
188
|
+
),
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
if adapter_registry is None:
|
|
192
|
+
return _input_error(
|
|
193
|
+
"adapter_registry is required for full render "
|
|
194
|
+
"(or pass as_link=True with a path for link-only)"
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
allowed_formats = frozenset(get_args(RenderFormat))
|
|
198
|
+
if format not in allowed_formats:
|
|
199
|
+
return RenderedDashboard(
|
|
200
|
+
success=False,
|
|
201
|
+
validation_errors=[
|
|
202
|
+
DatafaceError.from_code(
|
|
203
|
+
DF_RENDER_FORMAT_UNSUPPORTED, format=format
|
|
204
|
+
).to_structured()
|
|
205
|
+
],
|
|
206
|
+
)
|
|
207
|
+
if not path and not yaml_content:
|
|
208
|
+
return _input_error("Must provide either 'path' or 'yaml_content'")
|
|
209
|
+
if path and yaml_content:
|
|
210
|
+
return _input_error("Provide only one of 'path' or 'yaml_content', not both")
|
|
211
|
+
|
|
212
|
+
file_path: Path | None = None
|
|
213
|
+
if path:
|
|
214
|
+
try:
|
|
215
|
+
file_path = resolve_scoped_path(path, project_dir)
|
|
216
|
+
except ValueError as exc:
|
|
217
|
+
return _input_error(str(exc))
|
|
218
|
+
if not file_path.exists():
|
|
219
|
+
# No `file=` — `dft validate <missing-file>` would itself fail; don't
|
|
220
|
+
# surface that as a next-command suggestion.
|
|
221
|
+
return _input_error(f"File not found: {path}")
|
|
222
|
+
result = compile_file(file_path)
|
|
223
|
+
else:
|
|
224
|
+
if yaml_content is None:
|
|
225
|
+
return _input_error("yaml_content is required when no path is provided")
|
|
226
|
+
resolved_base = project_root_for(project_dir)
|
|
227
|
+
result = compile(
|
|
228
|
+
yaml_content,
|
|
229
|
+
base_dir=resolved_base,
|
|
230
|
+
project_dir=resolved_base,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
if not result.success:
|
|
234
|
+
errs = (
|
|
235
|
+
_stamp_file_commands(result.errors, file_path)
|
|
236
|
+
if file_path is not None
|
|
237
|
+
else list(result.errors)
|
|
238
|
+
)
|
|
239
|
+
return RenderedDashboard(
|
|
240
|
+
success=False,
|
|
241
|
+
validation_errors=errs,
|
|
242
|
+
warnings=list(result.warnings),
|
|
243
|
+
suppressed_warnings=list(result.suppressed_warnings),
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
_enrich_fanout_warnings_if_available(result)
|
|
247
|
+
face = result.face
|
|
248
|
+
if face is None:
|
|
249
|
+
return RenderedDashboard(
|
|
250
|
+
success=False,
|
|
251
|
+
face_error=DatafaceError.from_code(
|
|
252
|
+
DF_UNKNOWN_INTERNAL,
|
|
253
|
+
message="Compilation did not produce a face",
|
|
254
|
+
).to_structured(file=str(file_path) if file_path else None),
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# Honor DFT_CACHE_PATH unless the caller passed an owned cache.
|
|
258
|
+
# When `duckdb_cache` is provided, the caller manages its lifecycle
|
|
259
|
+
# (e.g. dft serve's module-level singleton) — don't close it here.
|
|
260
|
+
from dataface.core.execute.duckdb_cache import open_cache_from_env
|
|
261
|
+
from dataface.core.render.render_result import RenderResult as _RenderResult
|
|
262
|
+
|
|
263
|
+
own_cache = duckdb_cache is None
|
|
264
|
+
cache = duckdb_cache if duckdb_cache is not None else open_cache_from_env()
|
|
265
|
+
render_result: _RenderResult
|
|
266
|
+
try:
|
|
267
|
+
executor = Executor(
|
|
268
|
+
face,
|
|
269
|
+
adapter_registry=adapter_registry,
|
|
270
|
+
query_registry=result.query_registry,
|
|
271
|
+
use_cache=use_cache,
|
|
272
|
+
duckdb_cache=cache,
|
|
273
|
+
)
|
|
274
|
+
render_options: dict[str, Any] = {}
|
|
275
|
+
if scale is not None:
|
|
276
|
+
render_options["scale"] = scale
|
|
277
|
+
if link_context is not None:
|
|
278
|
+
render_options["link_context"] = link_context
|
|
279
|
+
render_result = render(
|
|
280
|
+
face,
|
|
281
|
+
executor,
|
|
282
|
+
format=format,
|
|
283
|
+
variables=variables or {},
|
|
284
|
+
ignore_codes=ignore_codes,
|
|
285
|
+
project_dir=project_dir,
|
|
286
|
+
**render_options,
|
|
287
|
+
)
|
|
288
|
+
# Compile-side warnings haven't passed through the render-time partition,
|
|
289
|
+
# so --ignore-warning / dataface.yml / per-chart codes have no effect on
|
|
290
|
+
# them yet. Apply the same three-layer partition here for symmetry.
|
|
291
|
+
compile_active, compile_suppressed = _partition_warnings(
|
|
292
|
+
list(result.warnings),
|
|
293
|
+
cli_codes=ignore_codes or set(),
|
|
294
|
+
project_codes=set(get_project_warnings_ignore(project_dir)),
|
|
295
|
+
per_chart_codes={
|
|
296
|
+
chart_id: set(chart.warnings_ignore)
|
|
297
|
+
for chart_id, chart in face.charts.items()
|
|
298
|
+
if chart.warnings_ignore
|
|
299
|
+
},
|
|
300
|
+
)
|
|
301
|
+
if render_result.face_error is not None:
|
|
302
|
+
return RenderedDashboard(
|
|
303
|
+
success=False,
|
|
304
|
+
face_error=render_result.face_error,
|
|
305
|
+
chart_errors=render_result.chart_errors,
|
|
306
|
+
warnings=[*compile_active, *render_result.render_warnings],
|
|
307
|
+
suppressed_warnings=[
|
|
308
|
+
*result.suppressed_warnings,
|
|
309
|
+
*compile_suppressed,
|
|
310
|
+
*render_result.suppressed_warnings,
|
|
311
|
+
],
|
|
312
|
+
)
|
|
313
|
+
rendered_output = render_result.output
|
|
314
|
+
if rendered_output is None:
|
|
315
|
+
# face_error guard above returns early; this is unreachable in practice.
|
|
316
|
+
return RenderedDashboard(
|
|
317
|
+
success=False,
|
|
318
|
+
face_error=DatafaceError.from_code(
|
|
319
|
+
DF_UNKNOWN_INTERNAL, message="Render produced no output"
|
|
320
|
+
).to_structured(),
|
|
321
|
+
)
|
|
322
|
+
if format == "json":
|
|
323
|
+
import json
|
|
324
|
+
|
|
325
|
+
data: dict[str, Any] | str | bytes = json.loads(rendered_output)
|
|
326
|
+
elif isinstance(rendered_output, bytes):
|
|
327
|
+
# Binary formats (png, pdf) — pass bytes through unchanged.
|
|
328
|
+
data = rendered_output
|
|
329
|
+
else:
|
|
330
|
+
data = rendered_output
|
|
331
|
+
except (ExecutionError, RenderError) as e:
|
|
332
|
+
# Executor creation failures and MissingRequiredVariablesError (a RenderError
|
|
333
|
+
# subclass) reach here — these are face-level fatals, not per-chart errors.
|
|
334
|
+
return RenderedDashboard(
|
|
335
|
+
success=False,
|
|
336
|
+
face_error=e.to_structured(file=str(file_path) if file_path else None),
|
|
337
|
+
)
|
|
338
|
+
finally:
|
|
339
|
+
if own_cache and cache is not None:
|
|
340
|
+
cache.close()
|
|
341
|
+
|
|
342
|
+
url = (
|
|
343
|
+
_view_url(file_path, variables, port=server_port, project_dir=project_dir)
|
|
344
|
+
if file_path is not None
|
|
345
|
+
else None
|
|
346
|
+
)
|
|
347
|
+
return RenderedDashboard(
|
|
348
|
+
success=True,
|
|
349
|
+
data=data,
|
|
350
|
+
chart_errors=render_result.chart_errors,
|
|
351
|
+
warnings=[*compile_active, *render_result.render_warnings],
|
|
352
|
+
suppressed_warnings=[
|
|
353
|
+
*result.suppressed_warnings,
|
|
354
|
+
*compile_suppressed,
|
|
355
|
+
*render_result.suppressed_warnings,
|
|
356
|
+
],
|
|
357
|
+
url=url,
|
|
358
|
+
)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Default Dataface Configuration
|
|
2
|
+
# ==============================
|
|
3
|
+
#
|
|
4
|
+
# STRUCTURE:
|
|
5
|
+
# style: Resolved from the base theme (authorable visual config)
|
|
6
|
+
# [everything else] Engine internals — not per-face authorable
|
|
7
|
+
#
|
|
8
|
+
# All visual/authorable config lives in themes/ files.
|
|
9
|
+
# See: themes/stark.yaml for the structural root style; themes/editorial.yaml is
|
|
10
|
+
# the shipped default and adds editorial voice on top.
|
|
11
|
+
|
|
12
|
+
# ============================================================================
|
|
13
|
+
# STYLE — resolved from theme files
|
|
14
|
+
# ============================================================================
|
|
15
|
+
# All visual/authorable properties live in themes/.
|
|
16
|
+
# The theme cascade: theme defaults → face style: → chart style:
|
|
17
|
+
|
|
18
|
+
style:
|
|
19
|
+
extends: editorial
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# ============================================================================
|
|
23
|
+
# VEGA-LITE
|
|
24
|
+
# ============================================================================
|
|
25
|
+
# Engine internals for the Vega-Lite output channel. The schema URL is a
|
|
26
|
+
# Python constant (dataface.core.compile.vega_lite.VEGA_LITE_SCHEMA_URL) —
|
|
27
|
+
# bumping it requires code changes, so it is not authorable here.
|
|
28
|
+
|
|
29
|
+
vega:
|
|
30
|
+
default_theme: editorial
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# ============================================================================
|
|
34
|
+
# CHART RENDERING CONSTANTS
|
|
35
|
+
# ============================================================================
|
|
36
|
+
# Algorithm constants for chart layout. Not per-face authorable — these
|
|
37
|
+
# control axis label sizing, tooltip formatting, and rendering heuristics.
|
|
38
|
+
|
|
39
|
+
chart_rendering:
|
|
40
|
+
default_width: 600
|
|
41
|
+
kpi:
|
|
42
|
+
# Spacing between value baseline and the title block. Title and support
|
|
43
|
+
# form a fixed 3-line block at one consistent rhythm — see
|
|
44
|
+
# ``_resolve_kpi_layout`` in ``core/render/chart/kpi.py``. Inner edge
|
|
45
|
+
# padding (top/bottom/left/right) all derive from
|
|
46
|
+
# ``style.charts.kpi.content_padding`` for a unified inset.
|
|
47
|
+
minimum_title_value_gap: 14.0
|
|
48
|
+
spark_bar:
|
|
49
|
+
title_height: 24.0
|
|
50
|
+
title_baseline_y: 16.0
|
|
51
|
+
more_rows_font_size: 10.0
|
|
52
|
+
more_rows_offset_y: 12.0
|
|
53
|
+
more_rows_bottom_padding: 8.0
|
|
54
|
+
text_baseline_offset: 4.0
|
|
55
|
+
side_padding: 4.0
|
|
56
|
+
avg_char_width_px: 7.0
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# ============================================================================
|
|
60
|
+
# RENDERING / EXPORT DEFAULTS
|
|
61
|
+
# ============================================================================
|
|
62
|
+
|
|
63
|
+
rendering:
|
|
64
|
+
timestamp_visible: true
|
|
65
|
+
timestamp_format: "%Y-%m-%d %H:%M"
|
|
66
|
+
timestamp:
|
|
67
|
+
font:
|
|
68
|
+
size: 11
|
|
69
|
+
color: "#999999"
|
|
70
|
+
y: 14
|
|
71
|
+
|
|
72
|
+
# Page footer — right-aligned attribution at the bottom of every SVG-rendered
|
|
73
|
+
# face. Set footer_text to "" to disable. The terminal renderer does not emit
|
|
74
|
+
# a footer.
|
|
75
|
+
footer_text: "made with dataface"
|
|
76
|
+
footer:
|
|
77
|
+
font:
|
|
78
|
+
size: 11
|
|
79
|
+
color: "#999999"
|
|
80
|
+
y_offset: 14
|
|
81
|
+
rule:
|
|
82
|
+
color: "#e5e5e5"
|
|
83
|
+
stroke_width: 0.5
|
|
84
|
+
|
|
85
|
+
svg:
|
|
86
|
+
preserve_aspect_ratio: "xMidYMid meet"
|
|
87
|
+
background: null
|
|
88
|
+
|
|
89
|
+
png:
|
|
90
|
+
scale: 1.0
|
|
91
|
+
background: null
|
|
92
|
+
|
|
93
|
+
pdf:
|
|
94
|
+
background: null
|
|
95
|
+
page_size: "auto"
|
|
96
|
+
|
|
97
|
+
html:
|
|
98
|
+
background: "#ffffff"
|
|
99
|
+
|
|
100
|
+
terminal:
|
|
101
|
+
background: null
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Categorical palette — category-10-dark
|
|
2
|
+
#
|
|
3
|
+
# Dark companion to category-10. Each stop is the darker counterpart of the
|
|
4
|
+
# same slot in `category-10.yml` — used for direct labels that need higher
|
|
5
|
+
# contrast against light backgrounds (e.g. right-pane endpoint labels on
|
|
6
|
+
# multi-series lines, segment names on stacked bars).
|
|
7
|
+
#
|
|
8
|
+
# Pairing is positional: stop N here is the dark companion of stop N in
|
|
9
|
+
# category-10. Reviewers edit the trio (`category-10`, `category-10-dark`,
|
|
10
|
+
# `category-10-light`) together when the editorial palette changes — the
|
|
11
|
+
# light companion (`category-10-light.yml`) handles de-emphasis fading;
|
|
12
|
+
# this file handles direct-label inking.
|
|
13
|
+
|
|
14
|
+
name: category-10-dark
|
|
15
|
+
description: |
|
|
16
|
+
Dark companion to category-10. Ten slots, each darker than the matching
|
|
17
|
+
category-10 stop. Designed for direct-label color (chart endpoint labels,
|
|
18
|
+
right-pane segment names) where bright fills need a darker text counterpart
|
|
19
|
+
against light backgrounds. Pairing is positional: slot N here pairs with
|
|
20
|
+
slot N in category-10.
|
|
21
|
+
|
|
22
|
+
colors:
|
|
23
|
+
- "#235a8a" # 1 — blue (companion to #2d74b3)
|
|
24
|
+
- "#2f9fbd" # 2 — cyan (companion to #41c6e7)
|
|
25
|
+
- "#3d7a5e" # 3 — green (companion to #40a97b)
|
|
26
|
+
- "#684e33" # 4 — brown (companion to #7a5531)
|
|
27
|
+
- "#98772c" # 5 — gold (companion to #dea628)
|
|
28
|
+
- "#82568d" # 6 — purple (companion to #a46fb2)
|
|
29
|
+
- "#ad562d" # 7 — orange (companion to #da5a23)
|
|
30
|
+
- "#526836" # 8 — moss (companion to #6f9041)
|
|
31
|
+
- "#6c7685" # 9 — gray (companion to #949daa)
|
|
32
|
+
- "#404852" # 10 — charcoal (companion to #505b6b)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Categorical palette — category-10-light
|
|
2
|
+
#
|
|
3
|
+
# Light de-emphasis companion to category-10. Each stop is the lighter,
|
|
4
|
+
# desaturated counterpart of the same slot in `category-10.yml` — used
|
|
5
|
+
# for fading out-of-focus members of a series so the highlighted member
|
|
6
|
+
# (rendered in its base color) reads forward. The intended use case is
|
|
7
|
+
# editorial highlight-columns / dim-the-chorus emphasis, where typically
|
|
8
|
+
# 1 series is emphasized and 1–2 are de-emphasized; this palette is sized
|
|
9
|
+
# to that case, not to a 10-bar chorus.
|
|
10
|
+
#
|
|
11
|
+
# Pairing is positional: stop N here is the light companion of stop N in
|
|
12
|
+
# category-10. Reviewers edit the trio (`category-10`, `category-10-dark`,
|
|
13
|
+
# `category-10-light`) together when the editorial palette changes.
|
|
14
|
+
#
|
|
15
|
+
# Derivation: option-B uniform OKLCH lift, settled at L=0.86 with chroma
|
|
16
|
+
# multiplied by 0.32 (per-slot hue preserved). Slot 6 (purple) is the
|
|
17
|
+
# calibration anchor — it lands at L=0.861 C=0.037 H=318.8, ~7° off from
|
|
18
|
+
# the source lavender swatch (#d3c8db, L=0.847 C=0.029 H=311.3) but on
|
|
19
|
+
# the same purple axis as base #a46fb2. Slot 10 (charcoal) is held at
|
|
20
|
+
# L=0.74 (main L − 0.12) so it stays visually distinct from slot 9
|
|
21
|
+
# (gray) — without that override the two near-neutral slots collapse
|
|
22
|
+
# into a single value at the uniform target L.
|
|
23
|
+
|
|
24
|
+
name: category-10-light
|
|
25
|
+
description: |
|
|
26
|
+
Light de-emphasis companion to category-10. Ten slots, each lighter and
|
|
27
|
+
more desaturated than the matching category-10 stop. Designed for fading
|
|
28
|
+
out-of-focus members of a series in highlight-columns / dim-the-chorus
|
|
29
|
+
emphasis design, where the highlighted member renders in its base color
|
|
30
|
+
and 1–2 others recede into the same hue at lower visual weight. Pairing
|
|
31
|
+
is positional: slot N here pairs with slot N in category-10.
|
|
32
|
+
|
|
33
|
+
colors:
|
|
34
|
+
- "#bed4ea" # 1 — blue (companion to #2d74b3)
|
|
35
|
+
- "#b6d8e2" # 2 — cyan (companion to #41c6e7)
|
|
36
|
+
- "#bcd9c9" # 3 — green (companion to #40a97b)
|
|
37
|
+
- "#dccec2" # 4 — brown (companion to #7a5531)
|
|
38
|
+
- "#e0cfaf" # 5 — gold (companion to #dea628)
|
|
39
|
+
- "#dccae1" # 6 — purple (companion to #a46fb2)
|
|
40
|
+
- "#f2c6b6" # 7 — orange (companion to #da5a23)
|
|
41
|
+
- "#cad6bd" # 8 — moss (companion to #6f9041)
|
|
42
|
+
- "#ced1d6" # 9 — gray (companion to #949daa)
|
|
43
|
+
- "#a7abb1" # 10 — charcoal (companion to #505b6b)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Categorical palette — category-10
|
|
2
|
+
#
|
|
3
|
+
# Migrated from dataface/core/defaults/chart_defaults.yml > palettes > category-10
|
|
4
|
+
# Categorical palettes have no LUT, no surface variants, no interpolation.
|
|
5
|
+
# Stops are what you get — exactly as authored.
|
|
6
|
+
#
|
|
7
|
+
# Companions (positional pairing — slot N of the companion pairs with slot N
|
|
8
|
+
# here; reviewers edit the trio together when the editorial palette changes):
|
|
9
|
+
# - `category-10-dark.yml` — darker twin, used to ink direct labels (e.g.
|
|
10
|
+
# endpoint labels on multi-series lines) so the label sits a notch
|
|
11
|
+
# darker than its line.
|
|
12
|
+
# - `category-10-light.yml` — lighter, desaturated twin, used to fade
|
|
13
|
+
# out-of-focus members in highlight-columns / dim-the-chorus emphasis.
|
|
14
|
+
|
|
15
|
+
name: category-10
|
|
16
|
+
description: |
|
|
17
|
+
Default 10-color categorical palette for qualitative data encoding.
|
|
18
|
+
Blue-first; calm editorial posture; earth-and-neutral expansion after
|
|
19
|
+
the first five hues. Pre-M2 palette, retained unchanged.
|
|
20
|
+
|
|
21
|
+
colors:
|
|
22
|
+
- "#2d74b3" # 1 — blue (default single-series)
|
|
23
|
+
- "#41c6e7" # 2 — cyan
|
|
24
|
+
- "#40a97b" # 3 — green
|
|
25
|
+
- "#7a5531" # 4 — brown
|
|
26
|
+
- "#dea628" # 5 — gold
|
|
27
|
+
- "#a46fb2" # 6 — purple
|
|
28
|
+
- "#da5a23" # 7 — orange
|
|
29
|
+
- "#6f9041" # 8 — moss
|
|
30
|
+
- "#949daa" # 9 — gray
|
|
31
|
+
- "#505b6b" # 10 — charcoal
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Categorical palette — category-6-tonal-blue
|
|
2
|
+
#
|
|
3
|
+
# Single-hue categorical palette anchored on category-10 slot 0 (blue, H≈248.8°
|
|
4
|
+
# in OKLCH). Strict-safe core of 4 stops (color-alone CVD-safe under Leonardo
|
|
5
|
+
# ΔE ≥ 11) plus 2 extended stops that REQUIRE redundant form encoding
|
|
6
|
+
# (dash/marker/pattern) past slot 4. See docs/docs/guides/tonal-categorical-palette.md.
|
|
7
|
+
|
|
8
|
+
name: category-6-tonal-blue
|
|
9
|
+
description: |
|
|
10
|
+
Tonal monochromatic blue palette. 6 stops at fixed hue (H≈248.8°), all in
|
|
11
|
+
the blue family. Use for editorial / brand-restrained dashboards that want
|
|
12
|
+
one hue throughout. Strict-safe core: slots 0-3 (Leonardo ΔE ≥ 11 pairwise).
|
|
13
|
+
Extended: slots 4-5 (require dash, marker, or pattern redundancy past slot 4).
|
|
14
|
+
See docs/docs/guides/tonal-categorical-palette.md.
|
|
15
|
+
|
|
16
|
+
colors:
|
|
17
|
+
- "#0375c4" # 0 — bluish (anchor / default single-series)
|
|
18
|
+
- "#81b3e5" # 1 — carolina blue (light tint)
|
|
19
|
+
- "#004a7f" # 2 — prussian blue (dark saturated)
|
|
20
|
+
- "#002341" # 3 — marine (very dark)
|
|
21
|
+
- "#6f89a3" # 4 — blue/grey (extended; form-redundant)
|
|
22
|
+
- "#c2d3e4" # 5 — light blue grey (extended; form-redundant)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Categorical palette — category-6-tonal-brown
|
|
2
|
+
#
|
|
3
|
+
# Single-hue categorical palette anchored on category-10 slot 3 (brown, H≈64°
|
|
4
|
+
# in OKLCH). Variant of the F template with a hue-wobble in extended slots
|
|
5
|
+
# (4-5) toward H≈78° to land in dft-creams territory — i.e., the lightest
|
|
6
|
+
# extended stops visually bridge into the cream theme's chrome.
|
|
7
|
+
|
|
8
|
+
name: category-6-tonal-brown
|
|
9
|
+
description: |
|
|
10
|
+
Tonal monochromatic brown palette. 6 stops in the brown family, designed
|
|
11
|
+
to bridge into the dft-creams scaffold for cream-themed dashboards.
|
|
12
|
+
Slots 0-3 are saturated browns at H≈64° (sepia, mushroom, brown, dark brown);
|
|
13
|
+
slots 4-5 wobble +14° to H≈78° to land between cream-30 and cream-60 in
|
|
14
|
+
perceptual space — the extended stops feel continuous with the cream-theme
|
|
15
|
+
chrome rather than as separate "near-neutrals."
|
|
16
|
+
|
|
17
|
+
Strict-safe core: slots 0-3 (Leonardo ΔE ≥ 11 pairwise).
|
|
18
|
+
Extended: slots 4-5 (require form redundancy past slot 4; live in cream
|
|
19
|
+
territory and may have low contrast against cream-025 / cream-theme
|
|
20
|
+
backgrounds — that's the bridge, not a bug).
|
|
21
|
+
See docs/docs/guides/tonal-categorical-palette.md.
|
|
22
|
+
|
|
23
|
+
colors:
|
|
24
|
+
- "#9a642b" # 0 — sepia (anchor)
|
|
25
|
+
- "#c9a687" # 1 — mushroom (light tan tint)
|
|
26
|
+
- "#683b01" # 2 — brown (dark saturated)
|
|
27
|
+
- "#321c04" # 3 — dark brown (very dark)
|
|
28
|
+
- "#908472" # 4 — brown grey (extended; cream-bridge)
|
|
29
|
+
- "#d8d0c3" # 5 — light grey (extended; cream-bridge)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Categorical palette — category-6-tonal-green
|
|
2
|
+
#
|
|
3
|
+
# Single-hue categorical palette anchored on category-10 slot 2 (green, H≈161.3°
|
|
4
|
+
# in OKLCH). Same architecture as category-6-tonal-blue.
|
|
5
|
+
|
|
6
|
+
name: category-6-tonal-green
|
|
7
|
+
description: |
|
|
8
|
+
Tonal monochromatic green palette. 6 stops at fixed hue (H≈161.3°), all in
|
|
9
|
+
the green family. Use for editorial / brand-restrained dashboards that want
|
|
10
|
+
one hue throughout. Strict-safe core: slots 0-3 (Leonardo ΔE ≥ 11 pairwise).
|
|
11
|
+
Extended: slots 4-5 (require dash, marker, or pattern redundancy past slot 4).
|
|
12
|
+
See docs/docs/guides/tonal-categorical-palette.md.
|
|
13
|
+
|
|
14
|
+
colors:
|
|
15
|
+
- "#00875a" # 0 — dark sea green (anchor)
|
|
16
|
+
- "#78c09c" # 1 — pale teal (light tint)
|
|
17
|
+
- "#005638" # 2 — spruce (dark saturated)
|
|
18
|
+
- "#002a19" # 3 — dark forest green (very dark)
|
|
19
|
+
- "#6c907d" # 4 — slate green (extended; form-redundant)
|
|
20
|
+
- "#c1d7cb" # 5 — light grey (extended; form-redundant)
|