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,522 @@
|
|
|
1
|
+
"""Compiled face types — output representation.
|
|
2
|
+
|
|
3
|
+
Stage: COMPILE (Output) / EXECUTE and RENDER (Input)
|
|
4
|
+
Purpose: Layout, Face, and resolved frozen dataclasses.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import Any, Literal, TypeGuard
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
11
|
+
|
|
12
|
+
from dataface.core.compile.models.chart.compiled import Chart
|
|
13
|
+
from dataface.core.compile.models.face.authored import LayoutType
|
|
14
|
+
from dataface.core.compile.models.query.compiled import AnyQuery
|
|
15
|
+
from dataface.core.compile.models.style.authored import StylePatch
|
|
16
|
+
from dataface.core.compile.models.style.merged import MergedStyle
|
|
17
|
+
from dataface.core.compile.models.variable.authored import SingleRowBoolProbe, Variable
|
|
18
|
+
|
|
19
|
+
# ============================================================================
|
|
20
|
+
# TYPE ALIASES
|
|
21
|
+
# ============================================================================
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Resolved variable values (not Variable objects)
|
|
25
|
+
# Used for Jinja template resolution - values can be any Python type
|
|
26
|
+
VariableValues = dict[str, Any]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# ============================================================================
|
|
30
|
+
# UNIFIED LAYOUT
|
|
31
|
+
# ============================================================================
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class LayoutItem(BaseModel):
|
|
35
|
+
"""A single item in a layout.
|
|
36
|
+
|
|
37
|
+
Represents either a chart or a nested face in the layout.
|
|
38
|
+
The unified structure makes iteration simple:
|
|
39
|
+
|
|
40
|
+
for item in layout.items:
|
|
41
|
+
if item.type == "chart":
|
|
42
|
+
render_chart(item.chart)
|
|
43
|
+
else:
|
|
44
|
+
render_face(item.face)
|
|
45
|
+
|
|
46
|
+
Attributes:
|
|
47
|
+
type: Either "chart" or "face"
|
|
48
|
+
chart: Chart if type == "chart"
|
|
49
|
+
face: Face if type == "face"
|
|
50
|
+
|
|
51
|
+
Grid-specific (optional):
|
|
52
|
+
row, col: Grid position (0-based)
|
|
53
|
+
row_span, col_span: Grid span (in grid units)
|
|
54
|
+
|
|
55
|
+
Calculated dimensions (set by sizing module):
|
|
56
|
+
width_fraction: What fraction of parent width (0.0-1.0)
|
|
57
|
+
width: Pixel width
|
|
58
|
+
height: Pixel height
|
|
59
|
+
x: X position in pixels
|
|
60
|
+
y: Y position in pixels
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
type: Literal["chart", "face"] = Field(description="Item type: 'chart' or 'face'.")
|
|
64
|
+
chart: "Chart | None" = Field(
|
|
65
|
+
default=None, description="Compiled chart, present when type == 'chart'."
|
|
66
|
+
)
|
|
67
|
+
face: "Face | None" = Field(
|
|
68
|
+
default=None, description="Compiled face, present when type == 'face'."
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Grid positioning (optional) - 0-based, in grid units
|
|
72
|
+
row: int | None = Field(
|
|
73
|
+
default=None, description="Grid row position (0-based; grid layouts only)."
|
|
74
|
+
)
|
|
75
|
+
col: int | None = Field(
|
|
76
|
+
default=None, description="Grid column position (0-based; grid layouts only)."
|
|
77
|
+
)
|
|
78
|
+
row_span: int | None = Field(
|
|
79
|
+
default=None, description="Number of rows to span (grid layouts only)."
|
|
80
|
+
)
|
|
81
|
+
col_span: int | None = Field(
|
|
82
|
+
default=None, description="Number of columns to span (grid layouts only)."
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Authored dimensions (from YAML, e.g. "30%" or "200px").
|
|
86
|
+
# user_width: width fraction/px authored on a cols item.
|
|
87
|
+
# layout_height: slot height authored on a rows/wrapper item; resolved to
|
|
88
|
+
# LayoutItem.height (float px) by the sizing pass.
|
|
89
|
+
user_width: str | None = Field(
|
|
90
|
+
default=None,
|
|
91
|
+
description="User-specified width from YAML (e.g., '30%' or '200px').",
|
|
92
|
+
)
|
|
93
|
+
layout_height: str | None = Field(
|
|
94
|
+
default=None,
|
|
95
|
+
description="Layout-wrapper height from YAML (e.g., '30%' or '200px'); "
|
|
96
|
+
"drives slot allocation in the sizing pass.",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Calculated dimensions (set by sizing)
|
|
100
|
+
width_fraction: float = Field(
|
|
101
|
+
default=1.0, description="Fraction of parent width (0.0–1.0); set by sizing."
|
|
102
|
+
)
|
|
103
|
+
width: float = Field(default=0.0, description="Pixel width; set by sizing.")
|
|
104
|
+
height: float = Field(default=0.0, description="Pixel height; set by sizing.")
|
|
105
|
+
x: float = Field(default=0.0, description="X position in pixels; set by sizing.")
|
|
106
|
+
y: float = Field(default=0.0, description="Y position in pixels; set by sizing.")
|
|
107
|
+
|
|
108
|
+
# Layout visibility — evaluated at render time; None means always visible.
|
|
109
|
+
# Accepts: bool (static), str (variable name or Jinja expression),
|
|
110
|
+
# or SingleRowBoolProbe (single-row boolean probe).
|
|
111
|
+
visible: bool | str | SingleRowBoolProbe | None = Field(
|
|
112
|
+
default=None,
|
|
113
|
+
description=(
|
|
114
|
+
"Visibility condition for this layout item. "
|
|
115
|
+
"None/True: always shown. False: always hidden. "
|
|
116
|
+
"str: variable name (truthy check) or Jinja boolean expression. "
|
|
117
|
+
"{query, column}: single-row boolean probe (same contract as variables.disabled)."
|
|
118
|
+
),
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Details (collapsible section) metadata
|
|
122
|
+
details_variable: str | None = Field(
|
|
123
|
+
default=None,
|
|
124
|
+
description="Variable name controlling expanded state (collapsible sections).",
|
|
125
|
+
)
|
|
126
|
+
details_summary: str | None = Field(
|
|
127
|
+
default=None,
|
|
128
|
+
description="Summary text shown when collapsed (collapsible sections).",
|
|
129
|
+
)
|
|
130
|
+
details_expanded_summary: str | None = Field(
|
|
131
|
+
default=None,
|
|
132
|
+
description="Summary text shown when expanded (collapsible sections).",
|
|
133
|
+
)
|
|
134
|
+
description: str | None = Field(
|
|
135
|
+
default=None, description="Optional metadata for AI/context tooltips."
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Render-ready sizing (calculated during normalization/sizing)
|
|
139
|
+
calculated_width: float | None = Field(
|
|
140
|
+
default=None,
|
|
141
|
+
description="Calculated pixel width based on 1200px default; set by sizing.",
|
|
142
|
+
)
|
|
143
|
+
calculated_height: float | None = Field(
|
|
144
|
+
default=None, description="Calculated pixel height; set by sizing."
|
|
145
|
+
)
|
|
146
|
+
aspect_ratio: float | None = Field(
|
|
147
|
+
default=None,
|
|
148
|
+
description="Width/height ratio for responsive rendering; set by sizing.",
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
model_config = ConfigDict(extra="forbid")
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class Layout(BaseModel):
|
|
155
|
+
"""Unified layout structure.
|
|
156
|
+
|
|
157
|
+
Stage: COMPILE output
|
|
158
|
+
|
|
159
|
+
All layout types (rows, cols, grid, tabs) are represented with this
|
|
160
|
+
unified structure, making downstream processing simpler.
|
|
161
|
+
|
|
162
|
+
The type field indicates the original layout type for rendering
|
|
163
|
+
(e.g., rows render vertically, cols horizontally).
|
|
164
|
+
|
|
165
|
+
Attributes:
|
|
166
|
+
type: Layout type (rows, cols, grid, tabs)
|
|
167
|
+
items: List of LayoutItem (charts or nested boards)
|
|
168
|
+
|
|
169
|
+
Grid-specific:
|
|
170
|
+
columns: Number of columns in grid
|
|
171
|
+
gap: Gap between items
|
|
172
|
+
|
|
173
|
+
Tabs-specific:
|
|
174
|
+
tab_titles: Titles for each tab
|
|
175
|
+
default_tab: Index of default active tab
|
|
176
|
+
|
|
177
|
+
Calculated:
|
|
178
|
+
width, height: Total dimensions (set by sizing)
|
|
179
|
+
content_width, content_height: Inner dimensions after padding/margin
|
|
180
|
+
|
|
181
|
+
Example:
|
|
182
|
+
>>> layout = compiled_board.layout
|
|
183
|
+
>>> print(f"Layout type: {layout.type}")
|
|
184
|
+
>>> for item in layout.items:
|
|
185
|
+
... print(f" - {item.type}: {item.chart.id if item.chart else item.face.id}")
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
type: LayoutType = Field(description="Layout type (rows, cols, grid, tabs).")
|
|
189
|
+
items: list[LayoutItem] = Field(
|
|
190
|
+
default_factory=list,
|
|
191
|
+
description="List of layout items (charts or nested faces).",
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
# Grid-specific
|
|
195
|
+
columns: int | None = Field(
|
|
196
|
+
default=None, description="Number of columns for grid layouts."
|
|
197
|
+
)
|
|
198
|
+
gap: str | None = Field(
|
|
199
|
+
default=None, description="Gap between items for grid layouts (CSS value)."
|
|
200
|
+
)
|
|
201
|
+
row_height: str | None = Field(
|
|
202
|
+
default=None, description="Default row height for grid layouts (CSS value)."
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
# Tabs-specific
|
|
206
|
+
tab_titles: list[str] | None = Field(
|
|
207
|
+
default=None, description="Titles for each tab (tabs layout only)."
|
|
208
|
+
)
|
|
209
|
+
tab_slugs: list[str] | None = Field(
|
|
210
|
+
default=None,
|
|
211
|
+
description="Slugified titles used as URL values (tabs layout only).",
|
|
212
|
+
)
|
|
213
|
+
tab_variable: str | None = Field(
|
|
214
|
+
default=None,
|
|
215
|
+
description="Variable name controlling the active tab (tabs layout only).",
|
|
216
|
+
)
|
|
217
|
+
default_tab: int | None = Field(
|
|
218
|
+
default=None, description="Index of the default active tab (tabs layout only)."
|
|
219
|
+
)
|
|
220
|
+
tab_position: Literal["top", "left"] | None = Field(
|
|
221
|
+
default=None,
|
|
222
|
+
description="Tab bar position: 'top' or 'left' (tabs layout only).",
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
# Calculated dimensions (outer dimensions including padding/margin)
|
|
226
|
+
width: float = Field(
|
|
227
|
+
default=0.0, description="Total calculated pixel width; set by sizing."
|
|
228
|
+
)
|
|
229
|
+
height: float = Field(
|
|
230
|
+
default=0.0, description="Total calculated pixel height; set by sizing."
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
# Calculated content dimensions (inner dimensions after padding/margin)
|
|
234
|
+
content_width: float = Field(
|
|
235
|
+
default=0.0,
|
|
236
|
+
description="Inner pixel width after padding/margin; set by sizing.",
|
|
237
|
+
)
|
|
238
|
+
content_height: float = Field(
|
|
239
|
+
default=0.0,
|
|
240
|
+
description="Inner pixel height after padding/margin; set by sizing.",
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
model_config = ConfigDict(extra="forbid")
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
# ============================================================================
|
|
247
|
+
# COMPILED FACE
|
|
248
|
+
# ============================================================================
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class Face(BaseModel):
|
|
252
|
+
"""Compiled face ready for execution and rendering.
|
|
253
|
+
|
|
254
|
+
Stage: COMPILE output / EXECUTE and RENDER input
|
|
255
|
+
|
|
256
|
+
This is the main output of compilation. It contains:
|
|
257
|
+
- Resolved definitions (variables, queries, charts)
|
|
258
|
+
- Unified layout structure
|
|
259
|
+
- Calculated dimensions
|
|
260
|
+
- Applied defaults and metadata
|
|
261
|
+
|
|
262
|
+
Guarantees:
|
|
263
|
+
- id: Always set
|
|
264
|
+
- title: Always set (empty string if not provided)
|
|
265
|
+
- layout: Always present (unified structure)
|
|
266
|
+
- All charts resolved to Chart objects
|
|
267
|
+
- All queries resolved to query objects
|
|
268
|
+
|
|
269
|
+
Attributes:
|
|
270
|
+
id: Unique identifier for this face
|
|
271
|
+
title: Display title
|
|
272
|
+
description: Description text
|
|
273
|
+
variables: Variable definitions (Dict[str, Variable])
|
|
274
|
+
queries: Compiled queries (Dict[str, AnyQuery])
|
|
275
|
+
charts: Compiled charts (Dict[str, Chart])
|
|
276
|
+
layout: Unified layout structure
|
|
277
|
+
variable_defaults: Resolved default values for variables
|
|
278
|
+
style: Face styling
|
|
279
|
+
Example:
|
|
280
|
+
>>> result = compile(yaml_content)
|
|
281
|
+
>>> face = result.face
|
|
282
|
+
>>> print(face.id) # "sales-dataface"
|
|
283
|
+
>>> print(face.title) # "Sales Dataface"
|
|
284
|
+
>>> print(face.layout.type) # "rows"
|
|
285
|
+
>>> for item in face.layout.items:
|
|
286
|
+
... chart = item.chart
|
|
287
|
+
... print(f"Chart: {chart.id}, Query: {chart.query_name}")
|
|
288
|
+
"""
|
|
289
|
+
|
|
290
|
+
id: str = Field(description="Unique identifier for this face.")
|
|
291
|
+
title: str = Field(
|
|
292
|
+
default="", description="Display title. Empty string if not provided."
|
|
293
|
+
)
|
|
294
|
+
description: str = Field(
|
|
295
|
+
default="", description="Description text. Empty string if not provided."
|
|
296
|
+
)
|
|
297
|
+
tags: list[str] = Field(
|
|
298
|
+
default_factory=list, description="Tags for categorization and search."
|
|
299
|
+
)
|
|
300
|
+
text: str = Field(
|
|
301
|
+
default="", description="Markdown text content for text-only faces."
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
# Definitions
|
|
305
|
+
variables: dict[str, Variable] = Field(
|
|
306
|
+
default_factory=dict,
|
|
307
|
+
description="Local variable definitions for UI controls.",
|
|
308
|
+
)
|
|
309
|
+
queries: dict[str, AnyQuery] = Field(
|
|
310
|
+
default_factory=dict,
|
|
311
|
+
description="Compiled query objects by name.",
|
|
312
|
+
)
|
|
313
|
+
charts: dict[str, Chart] = Field(
|
|
314
|
+
default_factory=dict,
|
|
315
|
+
description="Compiled chart objects by name.",
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
# Source configurations (for resolving named source references)
|
|
319
|
+
sources: dict[str, dict[str, Any]] = Field(
|
|
320
|
+
default_factory=dict,
|
|
321
|
+
description="Source configurations for resolving named source references (e.g., {'profiles': {'type': 'duckdb'}}).",
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
# Unified layout
|
|
325
|
+
layout: Layout = Field(
|
|
326
|
+
description="Unified layout structure containing all charts and nested faces.",
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
# Pre-computed variable defaults from variable_registry
|
|
330
|
+
variable_defaults: VariableValues = Field(
|
|
331
|
+
default_factory=dict,
|
|
332
|
+
description="Pre-computed default values for all variables; only set on root board.",
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
# Global variable registry (only set on root face, contains all variables from entire tree)
|
|
336
|
+
variable_registry: dict[str, Variable] | None = Field(
|
|
337
|
+
default=None,
|
|
338
|
+
description="All variables from the entire face tree; only set on root face.",
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
# Vega-Lite theme (e.g., "dark", "stark", "carbong100")
|
|
342
|
+
theme: str | None = Field(
|
|
343
|
+
default=None,
|
|
344
|
+
description="Theme name (e.g., 'editorial', 'cream', 'stark'). Inherited by nested faces.",
|
|
345
|
+
)
|
|
346
|
+
# Authored style patch — None when the face has no style: block.
|
|
347
|
+
authored_style: StylePatch | None = Field(
|
|
348
|
+
default=None,
|
|
349
|
+
description="Authored face style patch (background, padding, border, etc.).",
|
|
350
|
+
)
|
|
351
|
+
# Board-scoped resolved style — authoritative, produced at compile time.
|
|
352
|
+
resolved_style: MergedStyle = Field(
|
|
353
|
+
description="Board-scoped resolved style; produced at compile time. Render reads this instead of re-merging patches.",
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
@property
|
|
357
|
+
def visible_variables(self) -> "dict[str, Variable]":
|
|
358
|
+
"""Return variables with visible=True (available for UI controls)."""
|
|
359
|
+
return {k: v for k, v in self.variables.items() if v.visible}
|
|
360
|
+
|
|
361
|
+
# Card gap toggle (from board YAML)
|
|
362
|
+
card_gap: bool = Field(
|
|
363
|
+
default=False,
|
|
364
|
+
description="When True, adds gap between cards and adjusts page margin.",
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
# Semantic heading level: count of titled ancestors (not structural nesting depth).
|
|
368
|
+
# A titled root face is level=1; a titled face nested under a bare wrapper is still
|
|
369
|
+
# level=2 (only one titled ancestor). Bare wrappers without a title do not advance
|
|
370
|
+
# the counter. Required — every Face is constructed by the normalizer (or by a
|
|
371
|
+
# call site that derives level from parent_level), so a missing value is a bug,
|
|
372
|
+
# not a default. Can be overridden via face.style.title.level.
|
|
373
|
+
level: int = Field(
|
|
374
|
+
description=(
|
|
375
|
+
"Heading level for this face's title (matches H{level}; root face = 1 when "
|
|
376
|
+
"titled, 0 when untitled). Computed from titled-ancestor count; can be "
|
|
377
|
+
"overridden via face.style.title.level."
|
|
378
|
+
),
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
# Compilation metadata
|
|
382
|
+
meta: dict[str, Any] = Field(
|
|
383
|
+
default_factory=dict,
|
|
384
|
+
description="Compilation metadata (warnings, diagnostics, source info) populated by the normalizer.",
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
model_config = ConfigDict(extra="forbid")
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
# Resolve forward references (Chart and Face are now in scope)
|
|
391
|
+
LayoutItem.model_rebuild()
|
|
392
|
+
Layout.model_rebuild()
|
|
393
|
+
Face.model_rebuild()
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
# ============================================================================
|
|
397
|
+
# RESOLVED TYPES (produced by resolve_face() — no lookups needed downstream)
|
|
398
|
+
# ============================================================================
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
@dataclass(frozen=True)
|
|
402
|
+
class ResolvedLayoutItem:
|
|
403
|
+
"""Layout item with static dimension estimates — no executor, no data access."""
|
|
404
|
+
|
|
405
|
+
type: str # "chart" | "face" | "text" | "markdown" | ...
|
|
406
|
+
chart: Chart | None
|
|
407
|
+
# nested face is a forward reference — resolved recursively
|
|
408
|
+
face: "MergedFace | None"
|
|
409
|
+
x: float
|
|
410
|
+
y: float
|
|
411
|
+
width: float
|
|
412
|
+
height: float
|
|
413
|
+
# Carry-through optional fields from LayoutItem
|
|
414
|
+
details_variable: str | None = None
|
|
415
|
+
details_summary: str | None = None
|
|
416
|
+
details_expanded_summary: str | None = None
|
|
417
|
+
description: str | None = None
|
|
418
|
+
visible: "bool | str | SingleRowBoolProbe | None" = None
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
@dataclass(frozen=True)
|
|
422
|
+
class ResolvedLayout:
|
|
423
|
+
"""Layout tree with static dimension estimates.
|
|
424
|
+
|
|
425
|
+
Dimensions come from compile-time static sizing. The render phase applies
|
|
426
|
+
data-aware sizing (executor-driven row counts and Vega render-first heights)
|
|
427
|
+
on top of these estimates.
|
|
428
|
+
"""
|
|
429
|
+
|
|
430
|
+
type: str # "rows" | "cols" | "grid" | "tabs"
|
|
431
|
+
items: tuple["ResolvedLayoutItem", ...]
|
|
432
|
+
width: float
|
|
433
|
+
height: float
|
|
434
|
+
content_width: float
|
|
435
|
+
content_height: float
|
|
436
|
+
# Grid-specific
|
|
437
|
+
columns: int | None = None
|
|
438
|
+
gap: float = 0.0
|
|
439
|
+
# Tabs-specific
|
|
440
|
+
tab_titles: tuple[str, ...] = ()
|
|
441
|
+
tab_slugs: tuple[str, ...] = ()
|
|
442
|
+
tab_variable: str | None = None
|
|
443
|
+
default_tab: int = 0
|
|
444
|
+
tab_position: str | None = None
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
@dataclass(frozen=True)
|
|
448
|
+
class MergedFace:
|
|
449
|
+
"""Pure resolved face — deterministic transform from Face + config.
|
|
450
|
+
|
|
451
|
+
Produced by resolve_face() — never constructed directly.
|
|
452
|
+
No executor, no data access, no render-phase artifacts.
|
|
453
|
+
The render layer performs data-aware sizing on top of these static estimates.
|
|
454
|
+
|
|
455
|
+
Board-level config fields (page_padding, card_padding, card_gap) are None
|
|
456
|
+
for nested faces — those values only apply to the root renderable face.
|
|
457
|
+
"""
|
|
458
|
+
|
|
459
|
+
# Identity
|
|
460
|
+
id: str
|
|
461
|
+
title: str
|
|
462
|
+
description: str
|
|
463
|
+
tags: tuple[str, ...]
|
|
464
|
+
text: str
|
|
465
|
+
level: int # Heading level (1 = root face, matches H{level})
|
|
466
|
+
|
|
467
|
+
# Style (theme + face overrides already merged).
|
|
468
|
+
style: MergedStyle
|
|
469
|
+
|
|
470
|
+
# Baked board config constants — None for nested faces (root only).
|
|
471
|
+
# config.style.board.margin / card_padding / card_gap
|
|
472
|
+
page_padding: float | None
|
|
473
|
+
card_padding: float | None
|
|
474
|
+
card_gap: float | None
|
|
475
|
+
|
|
476
|
+
# Static dimension estimates (from compile-time layout tree)
|
|
477
|
+
# Render phase applies data-aware sizing on top.
|
|
478
|
+
width: float
|
|
479
|
+
height: float
|
|
480
|
+
|
|
481
|
+
# Resolved Vega-Lite config (pure: theme name → compiled VL config dict)
|
|
482
|
+
vega_config: dict[str, Any]
|
|
483
|
+
|
|
484
|
+
# Layout tree with static item estimates
|
|
485
|
+
layout: ResolvedLayout
|
|
486
|
+
|
|
487
|
+
# Data (unchanged from Face)
|
|
488
|
+
charts: dict[str, Chart]
|
|
489
|
+
variables: dict[str, Variable]
|
|
490
|
+
queries: dict[str, AnyQuery]
|
|
491
|
+
variable_defaults: VariableValues
|
|
492
|
+
|
|
493
|
+
@property
|
|
494
|
+
def visible_variables(self) -> "dict[str, Variable]":
|
|
495
|
+
"""Variables with visible=True — available for UI controls."""
|
|
496
|
+
return {k: v for k, v in self.variables.items() if v.visible}
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
# ============================================================================
|
|
500
|
+
# TYPE GUARD
|
|
501
|
+
# ============================================================================
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
def is_face(item: Any) -> TypeGuard[Face]:
|
|
505
|
+
"""Type guard for Face.
|
|
506
|
+
|
|
507
|
+
After compilation, all faces are Face objects.
|
|
508
|
+
Use this for type narrowing in conditional blocks.
|
|
509
|
+
|
|
510
|
+
Args:
|
|
511
|
+
item: Item to check
|
|
512
|
+
|
|
513
|
+
Returns:
|
|
514
|
+
True if item is a Face
|
|
515
|
+
|
|
516
|
+
Example:
|
|
517
|
+
>>> for item in layout.items:
|
|
518
|
+
... if item.face and is_face(item.face):
|
|
519
|
+
... # Type checker knows item.face is Face
|
|
520
|
+
... print(item.face.layout.type)
|
|
521
|
+
"""
|
|
522
|
+
return isinstance(item, Face)
|