dataface 0.3.2.dev22__py3-none-any.whl → 0.3.2.dev111__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.
- dataface/__init__.py +70 -23
- dataface/agent_api/__init__.py +162 -68
- dataface/agent_api/_paths.py +9 -3
- dataface/agent_api/docs/yaml-reference.md +240 -106
- dataface/agent_api/init.py +1 -1
- dataface/agent_api/project_session.py +2 -1
- dataface/agent_api/serve.py +115 -0
- dataface/ai/agent.py +28 -2
- dataface/cli/_project.py +100 -1
- dataface/cli/commands/board_artifact.py +5 -2
- dataface/cli/commands/describe.py +7 -3
- dataface/cli/commands/docs.py +12 -3
- dataface/cli/commands/init.py +7 -14
- dataface/cli/commands/inspect.py +6 -1
- dataface/cli/commands/mcp_init.py +23 -43
- dataface/cli/commands/migrate.py +8 -3
- dataface/cli/commands/query.py +11 -8
- dataface/cli/commands/render.py +20 -9
- dataface/cli/commands/search.py +8 -2
- dataface/cli/commands/serve.py +26 -76
- dataface/cli/commands/skills_init.py +2 -19
- dataface/cli/commands/validate.py +7 -2
- dataface/cli/filesystem_project.py +5 -4
- dataface/cli/main.py +2 -19
- dataface/core/__init__.py +73 -28
- dataface/core/{compile/colors.py → colors.py} +20 -5
- dataface/core/compile/compiler.py +97 -34
- dataface/core/compile/data_table.py +7 -2
- dataface/core/compile/enrich.py +18 -44
- dataface/core/compile/face_warnings.py +46 -8
- dataface/core/compile/format.py +47 -1
- dataface/core/compile/inherit_graph.py +3 -3
- dataface/core/compile/inherit_registry.yaml +1 -1
- dataface/core/compile/migrations.py +112 -9
- dataface/core/compile/models/chart/normalized/kpi.py +0 -4
- dataface/core/compile/models/chart/resolved/_base.py +28 -10
- dataface/core/compile/models/chart/resolved/callout.py +2 -3
- dataface/core/compile/models/chart/resolved/kpi.py +7 -3
- dataface/core/compile/models/chart/resolved/table.py +16 -2
- dataface/core/compile/models/markers.py +1 -1
- dataface/core/compile/models/primitives.py +175 -2
- dataface/core/compile/models/source.py +5 -5
- dataface/core/compile/models/style/authored/__init__.py +32 -8
- dataface/core/compile/models/style/authored/_base.py +126 -69
- dataface/core/compile/models/style/authored/table.py +61 -3
- dataface/core/compile/models/style/resolved/__init__.py +12 -2
- dataface/core/compile/models/style/resolved/_base.py +142 -57
- dataface/core/compile/models/style/resolved/_cartesian.py +6 -11
- dataface/core/compile/models/style/resolved/_cascade.py +141 -54
- dataface/core/compile/models/style/resolved/area.py +1 -1
- dataface/core/compile/models/style/resolved/bar.py +1 -1
- dataface/core/compile/models/style/resolved/line.py +1 -1
- dataface/core/compile/models/style/resolved/scatter.py +3 -2
- dataface/core/compile/models/style/theme/__init__.py +36 -14
- dataface/core/compile/models/style/theme/_chart_base.py +17 -9
- dataface/core/compile/models/style/theme/axis.py +432 -312
- dataface/core/compile/models/style/theme/charts.py +10 -6
- dataface/core/compile/models/style/theme/kpi.py +2 -1
- dataface/core/compile/models/style/theme/page.py +5 -2
- dataface/core/compile/models/style/theme/table.py +2 -2
- dataface/core/compile/models/vega_lite/config.py +1 -1
- dataface/core/compile/normalize_charts.py +6 -2
- dataface/core/compile/normalize_queries.py +21 -7
- dataface/core/compile/resolve/__init__.py +2 -0
- dataface/core/compile/resolve/_axes.py +147 -44
- dataface/core/compile/resolve/_channels.py +1 -1
- dataface/core/compile/resolve/_dispatch.py +1 -1
- dataface/core/compile/resolve/_domain.py +49 -32
- dataface/core/compile/resolve/_kwargs.py +39 -12
- dataface/core/compile/resolve/_marks.py +8 -3
- dataface/core/compile/resolve/_palette.py +7 -3
- dataface/core/compile/resolve/_table.py +124 -33
- dataface/core/compile/resolve/area.py +48 -18
- dataface/core/compile/resolve/bar.py +245 -64
- dataface/core/compile/resolve/geo.py +10 -3
- dataface/core/compile/resolve/heatmap.py +16 -8
- dataface/core/compile/resolve/line.py +35 -12
- dataface/core/compile/resolve/pie.py +4 -1
- dataface/core/compile/resolve/pie_attachment.py +1 -1
- dataface/core/compile/resolve/scatter.py +25 -13
- dataface/core/compile/resolve/simple.py +25 -2
- dataface/core/compile/schema_renderers/prompt.py +1 -1
- dataface/core/compile/schema_renderers/textmate_grammar.py +8 -1
- dataface/core/compile/source_map.py +211 -28
- dataface/core/compile/style_cascade.py +126 -55
- dataface/core/compile/yaml_error_formatter.py +1 -1
- dataface/core/connections.py +10 -3
- dataface/core/dashboard.py +24 -18
- dataface/core/defaults/themes/_base.yaml +49 -39
- dataface/core/defaults/themes/cream.yaml +4 -4
- dataface/core/defaults/themes/diagnostics-title-baseline.yaml +1 -1
- dataface/core/defaults/themes/diagnostics-title-color-extreme.yaml +1 -1
- dataface/core/defaults/themes/diagnostics-title-font-extreme.yaml +1 -1
- dataface/core/defaults/themes/diagnostics-title-size-extreme.yaml +1 -1
- dataface/core/defaults/themes/diagnostics-title-weight-extreme.yaml +1 -1
- dataface/core/defaults/themes/editorial.yaml +6 -6
- dataface/core/defaults/themes/neon.yaml +15 -8
- dataface/core/defaults/themes/plain.yaml +6 -4
- dataface/core/defaults/themes/stark.yaml +16 -7
- dataface/core/defaults/themes/vivid.yaml +15 -8
- dataface/core/diagnostics/__init__.py +2 -2
- dataface/core/diagnostics/codes_compile.py +13 -31
- dataface/core/diagnostics/codes_execute.py +79 -0
- dataface/core/diagnostics/codes_render.py +87 -28
- dataface/core/diagnostics/diagnostic.py +30 -0
- dataface/core/diagnostics/hints.py +19 -0
- dataface/core/dialects/base.py +17 -0
- dataface/core/dialects/postgres.py +14 -0
- dataface/core/dialects/redshift.py +5 -0
- dataface/core/execute/__init__.py +59 -14
- dataface/core/execute/adapters/dbt_adapter.py +4 -1
- dataface/core/execute/adapters/dbt_adapter_factory.py +19 -0
- dataface/core/execute/adapters/dbt_utils.py +84 -20
- dataface/core/execute/adapters/postgres_connection_manager.py +48 -0
- dataface/core/execute/adapters/sql_adapter.py +72 -4
- dataface/core/execute/dbt_jinja.py +65 -10
- dataface/core/execute/executor.py +69 -16
- dataface/core/font_measure.py +28 -0
- dataface/core/fonts.py +8 -5
- dataface/core/inspect/__init__.py +46 -6
- dataface/core/project.py +13 -1
- dataface/core/render/chart/data_table_attachment.py +14 -12
- dataface/core/render/chart/emitters/_cartesian.py +21 -18
- dataface/core/render/chart/emitters/_channels.py +56 -8
- dataface/core/render/chart/emitters/_label_overlap.py +18 -15
- dataface/core/render/chart/emitters/_measured_label_padding.py +4 -4
- dataface/core/render/chart/emitters/area.py +3 -3
- dataface/core/render/chart/emitters/bar.py +60 -35
- dataface/core/render/chart/emitters/geo.py +8 -13
- dataface/core/render/chart/emitters/heatmap.py +20 -26
- dataface/core/render/chart/emitters/line.py +2 -2
- dataface/core/render/chart/emitters/scatter.py +7 -30
- dataface/core/render/chart/features/baseline.py +22 -9
- dataface/core/render/chart/features/endpoint_labels.py +55 -32
- dataface/core/render/chart/features/mirror_axis.py +12 -9
- dataface/core/render/chart/features/value_labels.py +1 -1
- dataface/core/render/chart/kpi.py +11 -90
- dataface/core/render/chart/presentation.py +6 -8
- dataface/core/render/chart/rendering.py +3 -12
- dataface/core/render/chart/session.py +10 -0
- dataface/core/render/chart/spec.py +3 -0
- dataface/core/render/chart/spec_builders.py +18 -18
- dataface/core/render/chart/table.py +81 -36
- dataface/core/render/chart/table_support.py +2 -53
- dataface/core/render/chart/time_unit_detect.py +3 -2
- dataface/core/render/chart/translate.py +82 -44
- dataface/core/render/chart/type_inference.py +45 -25
- dataface/core/render/chart/validation.py +91 -0
- dataface/core/render/chart/vega_lite.py +30 -11
- dataface/core/render/chart/vl_field_maps.py +111 -76
- dataface/core/render/converters/chart.py +16 -6
- dataface/core/render/faces.py +3 -2
- dataface/core/render/fonts/DFTSerifOldstyleProportional-Regular.woff2 +0 -0
- dataface/core/render/fonts/DFTSerifOldstyleTabular-Regular.woff2 +0 -0
- dataface/core/render/fonts/SOURCE_CODE_PRO_LICENSE.txt +93 -93
- dataface/core/render/fonts/_font_face.css +10 -0
- dataface/core/render/layout_sizing.py +267 -238
- dataface/core/render/numeral_expr.py +80 -0
- dataface/core/render/warnings/bar_band_width_too_narrow.py +64 -6
- dataface/core/render/warnings/layered_chart_shared_y_axis_scale_mismatch.py +25 -3
- dataface/core/render/warnings/likely_currency_or_percent_missing_formatter.py +12 -6
- dataface/core/render/warnings/palette_unsupported.py +6 -0
- dataface/core/render/warnings/pie_dominant_segment.py +1 -0
- dataface/core/render/warnings/pie_too_many_segments.py +1 -0
- dataface/core/render/warnings/point_map_out_of_projection.py +1 -0
- dataface/core/render/warnings/query_returned_zero_rows.py +1 -0
- dataface/core/render/warnings/redundant_encoding.py +20 -1
- dataface/core/render/warnings/registry.py +7 -3
- dataface/core/render/warnings/temporal_single_point.py +1 -0
- dataface/core/render/warnings/too_many_color_categories.py +1 -0
- dataface/core/render/warnings/too_many_x_categories.py +18 -2
- dataface/core/render/warnings/value_labels_crowd_width.py +9 -0
- dataface/core/render/warnings/y_encoding_mostly_null.py +1 -0
- dataface/core/render_format.py +18 -0
- dataface/core/text/format_d3.py +56 -0
- dataface/core/text/numeral_scale.py +218 -0
- dataface/core/utils.py +103 -10
- dataface/data/schemas/board-resolved/board-resolved.schema.json +9487 -7978
- dataface/data/schemas/yaml/0.3.2.json +1 -0
- dataface/data/schemas/yaml/manifest.json +7 -0
- {dataface-0.3.2.dev22.dist-info → dataface-0.3.2.dev111.dist-info}/METADATA +1 -1
- {dataface-0.3.2.dev22.dist-info → dataface-0.3.2.dev111.dist-info}/RECORD +185 -177
- {dataface-0.3.2.dev22.dist-info → dataface-0.3.2.dev111.dist-info}/WHEEL +0 -0
- {dataface-0.3.2.dev22.dist-info → dataface-0.3.2.dev111.dist-info}/entry_points.txt +0 -0
- {dataface-0.3.2.dev22.dist-info → dataface-0.3.2.dev111.dist-info}/licenses/LICENSE +0 -0
dataface/core/connections.py
CHANGED
|
@@ -14,9 +14,12 @@ looking for, so routing through an adapter would mean inventing a sentinel
|
|
|
14
14
|
dataset just to construct one. The BigQuery client needs no dataset at all.
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
-
from
|
|
17
|
+
from __future__ import annotations
|
|
18
18
|
|
|
19
|
-
from
|
|
19
|
+
from typing import TYPE_CHECKING, Any
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from dataface.core.compile.models.source import BigQuerySourceConfig, SourceConfig
|
|
20
23
|
|
|
21
24
|
|
|
22
25
|
def _import_bigquery(module_name: str) -> Any:
|
|
@@ -78,6 +81,8 @@ def list_datasets(source_config: SourceConfig) -> list[str]:
|
|
|
78
81
|
this is what the connection form calls before the user can know which
|
|
79
82
|
datasets exist.
|
|
80
83
|
"""
|
|
84
|
+
from dataface.core.compile.models.source import BigQuerySourceConfig
|
|
85
|
+
|
|
81
86
|
if not isinstance(source_config, BigQuerySourceConfig):
|
|
82
87
|
raise ValueError(
|
|
83
88
|
f"list_datasets is BigQuery-only, got {source_config.type!r}. "
|
|
@@ -97,6 +102,8 @@ def dataset_location(source_config: SourceConfig) -> str:
|
|
|
97
102
|
resources without a location, so filling it for every listed dataset would
|
|
98
103
|
be an N+1 across potentially hundreds of them.
|
|
99
104
|
"""
|
|
105
|
+
from dataface.core.compile.models.source import BigQuerySourceConfig
|
|
106
|
+
|
|
100
107
|
if not isinstance(source_config, BigQuerySourceConfig):
|
|
101
108
|
raise ValueError(
|
|
102
109
|
f"dataset_location is BigQuery-only, got {source_config.type!r}."
|
|
@@ -133,7 +140,7 @@ def test_connection(source_config: SourceConfig) -> tuple[bool, str]:
|
|
|
133
140
|
try:
|
|
134
141
|
adapter = build_adapter(creds)
|
|
135
142
|
with adapter.connection_named("test"):
|
|
136
|
-
adapter.execute("SELECT 1", auto_begin=
|
|
143
|
+
adapter.execute("SELECT 1", auto_begin=False, fetch=True)
|
|
137
144
|
return True, "Connection successful"
|
|
138
145
|
except (
|
|
139
146
|
Exception # noqa: BLE001
|
dataface/core/dashboard.py
CHANGED
|
@@ -21,6 +21,7 @@ from __future__ import annotations
|
|
|
21
21
|
import importlib.util as _importlib_util
|
|
22
22
|
import logging
|
|
23
23
|
import time
|
|
24
|
+
from datetime import datetime
|
|
24
25
|
from pathlib import PurePosixPath
|
|
25
26
|
from typing import Any, Literal, get_args
|
|
26
27
|
|
|
@@ -81,10 +82,7 @@ from dataface.core.render import RenderError, render
|
|
|
81
82
|
from dataface.core.render.board_links import LinkContext
|
|
82
83
|
from dataface.core.render.dir_context import lazy_dir_context
|
|
83
84
|
from dataface.core.render.face_to_dict import NO_ROW_CAP
|
|
84
|
-
|
|
85
|
-
RenderFormat = Literal[
|
|
86
|
-
"svg", "html", "png", "pdf", "terminal", "json", "text", "yaml", "data"
|
|
87
|
-
]
|
|
85
|
+
from dataface.core.render_format import RenderFormat
|
|
88
86
|
|
|
89
87
|
# "failed" — no output produced (face_error, or validation_errors from a failed
|
|
90
88
|
# compile).
|
|
@@ -120,9 +118,9 @@ class RenderedDashboard(BaseModel):
|
|
|
120
118
|
# concept of. Cloud resolves agent-facing dashboard URLs through its own
|
|
121
119
|
# router instead (`search_dashboards`).
|
|
122
120
|
url: str | None = None
|
|
123
|
-
#
|
|
124
|
-
# Cloud's snapshot writer to compute expires_at
|
|
125
|
-
|
|
121
|
+
# Per-query (name, data_as_of, policy) records populated on success. Consumed
|
|
122
|
+
# by Cloud's snapshot writer to compute expires_at anchored on actual data age.
|
|
123
|
+
board_query_data_ages: list[tuple[str, datetime, CachePolicy]] = []
|
|
126
124
|
|
|
127
125
|
|
|
128
126
|
def raise_on_dashboard_failure(result: RenderedDashboard) -> None:
|
|
@@ -394,19 +392,29 @@ def render_dashboard(
|
|
|
394
392
|
# the compile-time source map here: render never re-parses the
|
|
395
393
|
# authored text, it reuses the map compile already built.
|
|
396
394
|
stamp_diagnostics(
|
|
397
|
-
render_result.chart_errors,
|
|
395
|
+
render_result.chart_errors,
|
|
396
|
+
result.source_map,
|
|
397
|
+
result.sql_blocks,
|
|
398
|
+
result.container_paths,
|
|
399
|
+
)
|
|
400
|
+
stamp_diagnostics(
|
|
401
|
+
render_result.warnings,
|
|
402
|
+
result.source_map,
|
|
403
|
+
result.sql_blocks,
|
|
404
|
+
result.container_paths,
|
|
398
405
|
)
|
|
399
|
-
stamp_diagnostics(render_result.warnings, result.source_map, result.sql_blocks)
|
|
400
406
|
stamp_diagnostics(
|
|
401
407
|
render_result.suppressed_warnings,
|
|
402
408
|
result.source_map,
|
|
403
409
|
result.sql_blocks,
|
|
410
|
+
result.container_paths,
|
|
404
411
|
)
|
|
405
412
|
if render_result.face_error is not None:
|
|
406
413
|
stamp_diagnostics(
|
|
407
414
|
[render_result.face_error],
|
|
408
415
|
result.source_map,
|
|
409
416
|
result.sql_blocks,
|
|
417
|
+
result.container_paths,
|
|
410
418
|
)
|
|
411
419
|
_logger.debug(
|
|
412
420
|
"render_dashboard %s compile=%.0fms execute+render=%.0fms total=%.0fms cache=%s",
|
|
@@ -472,7 +480,12 @@ def render_dashboard(
|
|
|
472
480
|
fatal_diagnostic = e.to_diagnostic(
|
|
473
481
|
file=file_path.relpath if file_path is not None else None
|
|
474
482
|
)
|
|
475
|
-
stamp_diagnostics(
|
|
483
|
+
stamp_diagnostics(
|
|
484
|
+
[fatal_diagnostic],
|
|
485
|
+
result.source_map,
|
|
486
|
+
result.sql_blocks,
|
|
487
|
+
result.container_paths,
|
|
488
|
+
)
|
|
476
489
|
return RenderedDashboard(
|
|
477
490
|
status="failed",
|
|
478
491
|
face_error=fatal_diagnostic,
|
|
@@ -495,12 +508,5 @@ def render_dashboard(
|
|
|
495
508
|
*render_result.suppressed_warnings,
|
|
496
509
|
],
|
|
497
510
|
url=url,
|
|
498
|
-
|
|
499
|
-
# `#`-anchored refs are normalized straight into the registry and never
|
|
500
|
-
# get a `queries:` entry, so keying off the named section hands Cloud a
|
|
501
|
-
# short map — or an empty one for a board authored entirely inline,
|
|
502
|
-
# which its `min(..., default=inf)` reads as "nothing expires".
|
|
503
|
-
board_cache_policies={
|
|
504
|
-
name: q.cache for name, q in result.query_registry.items()
|
|
505
|
-
},
|
|
511
|
+
board_query_data_ages=executor.query_data_ages,
|
|
506
512
|
)
|
|
@@ -145,27 +145,25 @@ style:
|
|
|
145
145
|
radius: 0.0
|
|
146
146
|
|
|
147
147
|
axis:
|
|
148
|
-
|
|
148
|
+
line:
|
|
149
149
|
visible: false
|
|
150
150
|
width: 1
|
|
151
151
|
ticks:
|
|
152
152
|
visible: false
|
|
153
|
-
|
|
153
|
+
length: 6 # default tick length cascades to all axes; matches VL's geometric idiom
|
|
154
154
|
grid:
|
|
155
155
|
visible: true
|
|
156
156
|
opacity: 1
|
|
157
157
|
width: 1.5
|
|
158
|
-
|
|
159
|
-
width: 2
|
|
160
|
-
label:
|
|
158
|
+
labels:
|
|
161
159
|
font:
|
|
162
160
|
size: &size_label 11
|
|
163
161
|
padding: 6
|
|
164
162
|
overlap: # default: all strategies enabled; the ONE home for this setting
|
|
165
163
|
tilt: true
|
|
166
164
|
skip: true
|
|
167
|
-
|
|
168
|
-
# bound, flush, offset
|
|
165
|
+
min_gap: 16 # 16px minimum gap between adjacent labels
|
|
166
|
+
# bound, flush, offset intentionally absent — VL per-axis defaults apply.
|
|
169
167
|
title:
|
|
170
168
|
visible: false
|
|
171
169
|
font:
|
|
@@ -173,13 +171,6 @@ style:
|
|
|
173
171
|
weight: &weight_bold 600
|
|
174
172
|
case: title
|
|
175
173
|
padding: 10
|
|
176
|
-
# fill is semantically only active on axis_x (ordinal bucketed-time);
|
|
177
|
-
# required on all AxisStyle instances since the model enforces it.
|
|
178
|
-
fill: "null"
|
|
179
|
-
# fiscal_year_start_month: same story as fill — only meaningful on
|
|
180
|
-
# axis_x (year/yearquarter bucketing), required everywhere the model
|
|
181
|
-
# enforces it. 1 = January = calendar-quarter convention.
|
|
182
|
-
fiscal_year_start_month: 1
|
|
183
174
|
|
|
184
175
|
axis_x:
|
|
185
176
|
# Only intentional differences from `axis` — everything else cascades.
|
|
@@ -188,7 +179,8 @@ style:
|
|
|
188
179
|
ticks:
|
|
189
180
|
visible: true
|
|
190
181
|
width: 1
|
|
191
|
-
|
|
182
|
+
offset: 1
|
|
183
|
+
labels:
|
|
192
184
|
# Tilt angle candidates for the "tilt" step in the label overlap
|
|
193
185
|
# strategy walk. Picker chooses the first angle where labels fit.
|
|
194
186
|
# When no angle fits, the strategy walk continues to the next step
|
|
@@ -196,7 +188,6 @@ style:
|
|
|
196
188
|
tilt_increments: [0, -30, -45, -60, -90]
|
|
197
189
|
scale:
|
|
198
190
|
padding: 0 # band/point padding around categorical x-axis values
|
|
199
|
-
offset: 1
|
|
200
191
|
# Engine fills missing time buckets on ordinal bucketed-time x-axes.
|
|
201
192
|
# Bar/line/area inherit this global default. Scatter pins null under
|
|
202
193
|
# style.charts.scatter.axis_x (layer 3.5) — scatter should never zero-fill or interpolate.
|
|
@@ -205,23 +196,28 @@ style:
|
|
|
205
196
|
|
|
206
197
|
axis_y:
|
|
207
198
|
# Y-specific tweaks only — orientation and tick width. Empty
|
|
208
|
-
# grid/
|
|
199
|
+
# grid/line/labels/title cascade from the canonical `axis`
|
|
209
200
|
# block via the InheritSlot graph at resolve time, so face-level patches on
|
|
210
201
|
# `style.axis.*` reach axis_y through the same path.
|
|
211
202
|
# "auto" → "right" by default; flips to "left" when endpoint labels are
|
|
212
203
|
# on the right edge (line charts with style.line.endpoint_labels.visible).
|
|
204
|
+
# Also serves the categorical-orient role for horizontal bar's swapped
|
|
205
|
+
# categorical axis (categorical_orient was deleted 2026-08 trim).
|
|
213
206
|
position: auto
|
|
214
|
-
categorical_orient: left
|
|
215
207
|
ticks:
|
|
216
208
|
width: 1.5
|
|
217
|
-
|
|
218
|
-
|
|
209
|
+
grid:
|
|
210
|
+
# zero-baseline gridline — measure axis only. Moved from axis.grid.zero
|
|
211
|
+
# (BaseAxisGridStyle has no zero; only MeasureGridStyle does).
|
|
212
|
+
zero:
|
|
213
|
+
width: 2
|
|
219
214
|
scale:
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
215
|
+
continuous:
|
|
216
|
+
# Measure-axis domain stays pinned to the chart query's full unfiltered
|
|
217
|
+
# extent across filter-variable changes (editorial default — a filter
|
|
218
|
+
# changes data, not axes). "auto" opts a chart back into rescaling the
|
|
219
|
+
# domain to the current filtered subset on every variable change.
|
|
220
|
+
domain_strategy: stable
|
|
225
221
|
# Editorial default (settled 2026-07-16): data marks sit ~8% from
|
|
226
222
|
# the visible axis edges. Zero-anchored: domain_max = data_max * 1.08.
|
|
227
223
|
# Zoomed axes: both edges expand by 8% of the visible span, symmetric.
|
|
@@ -234,16 +230,14 @@ style:
|
|
|
234
230
|
# tick width matching axis_y. Colors cascade from `axis` via the inherit graph.
|
|
235
231
|
ticks:
|
|
236
232
|
width: 1.5
|
|
237
|
-
|
|
233
|
+
labels:
|
|
238
234
|
font: {}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
# Authors override per-chart via chart.format or style.axis_y.format.
|
|
246
|
-
format: "number_default"
|
|
235
|
+
# References the shared number_default alias (.3~s) so quantitative axes
|
|
236
|
+
# AND the value labels that inherit this format read a bounded 3-sig-fig
|
|
237
|
+
# SI number (9.18, 21.5M) — not the unbounded 6-sig-fig 9.18039 a bare ~s
|
|
238
|
+
# gives on real data. Axis ticks are round, so the bound is invisible there.
|
|
239
|
+
# Authors override per-chart via chart.format or style.axis_y.labels.format.
|
|
240
|
+
format: "number_default"
|
|
247
241
|
|
|
248
242
|
tooltip:
|
|
249
243
|
format: ",.2~f"
|
|
@@ -563,7 +557,8 @@ style:
|
|
|
563
557
|
# "auto" runs the Dataface smart-zero heuristic at render time.
|
|
564
558
|
# Set to true/false to pin the zero-baseline for all bar charts.
|
|
565
559
|
scale:
|
|
566
|
-
|
|
560
|
+
continuous:
|
|
561
|
+
zero: auto
|
|
567
562
|
endpoint_labels:
|
|
568
563
|
visible: false
|
|
569
564
|
label_offset: 6.0
|
|
@@ -573,12 +568,13 @@ style:
|
|
|
573
568
|
visible: false
|
|
574
569
|
ticks:
|
|
575
570
|
visible: false
|
|
576
|
-
|
|
571
|
+
labels:
|
|
577
572
|
padding: 6.0
|
|
578
573
|
|
|
579
574
|
line:
|
|
580
575
|
scale:
|
|
581
|
-
|
|
576
|
+
continuous:
|
|
577
|
+
zero: auto
|
|
582
578
|
endpoint_labels:
|
|
583
579
|
visible: false
|
|
584
580
|
label_offset: 6.0
|
|
@@ -588,7 +584,8 @@ style:
|
|
|
588
584
|
# Authors who want a stacked area chart opt in via ``chart.style.stack: "zero"``.
|
|
589
585
|
stack: none
|
|
590
586
|
scale:
|
|
591
|
-
|
|
587
|
+
continuous:
|
|
588
|
+
zero: auto
|
|
592
589
|
endpoint_labels:
|
|
593
590
|
visible: false
|
|
594
591
|
label_offset: 6.0
|
|
@@ -739,7 +736,8 @@ style:
|
|
|
739
736
|
# _ChartStyleBase, so mypy can't verify zero-arg construction. These stay required.
|
|
740
737
|
scatter:
|
|
741
738
|
scale:
|
|
742
|
-
|
|
739
|
+
continuous:
|
|
740
|
+
zero: auto
|
|
743
741
|
# Scatter always breaks at missing buckets (honest dots). Pinned here so a
|
|
744
742
|
# future change to style.charts.axis_x.fill does not connect gaps on point marks.
|
|
745
743
|
# Bar is intentionally NOT pinned: an author who opts into fill: linear on a bar
|
|
@@ -811,6 +809,18 @@ style:
|
|
|
811
809
|
axis_y:
|
|
812
810
|
grid:
|
|
813
811
|
visible: false
|
|
812
|
+
# Heatmap's color channel carries the whole measure — a reader can't
|
|
813
|
+
# decode dark-vs-light without a key. Keep this a family default so
|
|
814
|
+
# themes that globally suppress legends (editorial direct-labeling)
|
|
815
|
+
# still inherit an honest gradient legend here, just as scatter opts
|
|
816
|
+
# back in.
|
|
817
|
+
legend:
|
|
818
|
+
visible: true
|
|
819
|
+
# The click-to-mute toggle mutes every mark whose color differs from
|
|
820
|
+
# the clicked one; a heatmap's color is its measure, near-unique per
|
|
821
|
+
# cell, so one click drops the whole chart to muted opacity — and a
|
|
822
|
+
# gradient key has no symbols to click.
|
|
823
|
+
interactive_legend: false
|
|
814
824
|
|
|
815
825
|
# Series-label primitive. Typography for Cartesian text marks that name a
|
|
816
826
|
# data series — endpoint labels and direct stack labels. Placement is
|
|
@@ -62,15 +62,13 @@ style:
|
|
|
62
62
|
font:
|
|
63
63
|
color: dft-creams.heading
|
|
64
64
|
axis:
|
|
65
|
-
|
|
65
|
+
line:
|
|
66
66
|
color: dft-creams.grid
|
|
67
67
|
grid:
|
|
68
68
|
color: dft-creams.grid
|
|
69
|
-
zero:
|
|
70
|
-
color: dft-creams.subtitle
|
|
71
69
|
ticks:
|
|
72
70
|
color: dft-creams.grid
|
|
73
|
-
|
|
71
|
+
labels:
|
|
74
72
|
font:
|
|
75
73
|
color: dft-creams.muted
|
|
76
74
|
title:
|
|
@@ -82,6 +80,8 @@ style:
|
|
|
82
80
|
axis_y:
|
|
83
81
|
grid:
|
|
84
82
|
color: dft-creams.grid-emphasis # softer grid for the value axis (overrides default gray)
|
|
83
|
+
zero:
|
|
84
|
+
color: dft-creams.subtitle
|
|
85
85
|
ticks:
|
|
86
86
|
color: dft-creams.grid-emphasis
|
|
87
87
|
axis_quantitative:
|
|
@@ -69,7 +69,7 @@ style:
|
|
|
69
69
|
# dataface/src/dataface/core/defaults/palettes/categorical/editorial-10.yml.
|
|
70
70
|
palette: editorial-10
|
|
71
71
|
axis:
|
|
72
|
-
|
|
72
|
+
labels:
|
|
73
73
|
font:
|
|
74
74
|
color: dft-grays.muted
|
|
75
75
|
title:
|
|
@@ -80,15 +80,15 @@ style:
|
|
|
80
80
|
color: dft-grays.subtitle
|
|
81
81
|
grid:
|
|
82
82
|
color: dft-grays.grid
|
|
83
|
-
zero:
|
|
84
|
-
color: dft-grays.subtitle
|
|
85
83
|
ticks:
|
|
86
84
|
color: dft-grays.grid
|
|
87
|
-
|
|
85
|
+
line:
|
|
88
86
|
color: dft-grays.grid
|
|
89
87
|
axis_y:
|
|
90
88
|
grid:
|
|
91
89
|
color: dft-grays.grid-emphasis
|
|
90
|
+
zero:
|
|
91
|
+
color: dft-grays.subtitle
|
|
92
92
|
ticks:
|
|
93
93
|
color: dft-grays.grid-emphasis
|
|
94
94
|
axis_quantitative:
|
|
@@ -152,12 +152,12 @@ style:
|
|
|
152
152
|
# left/right for a horizontal bar — vertical bars' bottom labels have
|
|
153
153
|
# no edge to resolve against and stay untouched.
|
|
154
154
|
axis_x:
|
|
155
|
-
|
|
155
|
+
labels:
|
|
156
156
|
align: inward
|
|
157
157
|
heatmap:
|
|
158
158
|
# Inward-align the row labels (heatmap's y is always the nominal row axis).
|
|
159
159
|
axis_y:
|
|
160
|
-
|
|
160
|
+
labels:
|
|
161
161
|
align: inward
|
|
162
162
|
line:
|
|
163
163
|
endpoint_labels:
|
|
@@ -64,16 +64,14 @@ style:
|
|
|
64
64
|
bar:
|
|
65
65
|
background: *faint
|
|
66
66
|
axis:
|
|
67
|
-
|
|
67
|
+
line:
|
|
68
68
|
color: *faint
|
|
69
69
|
grid:
|
|
70
70
|
color: *faint
|
|
71
71
|
width: 1
|
|
72
|
-
zero:
|
|
73
|
-
color: &value dft-grays.canvas # baseline anchors at max contrast
|
|
74
72
|
ticks:
|
|
75
73
|
color: *faint
|
|
76
|
-
|
|
74
|
+
labels:
|
|
77
75
|
font:
|
|
78
76
|
color: *mid
|
|
79
77
|
title:
|
|
@@ -82,8 +80,12 @@ style:
|
|
|
82
80
|
axis_x:
|
|
83
81
|
grid:
|
|
84
82
|
visible: false
|
|
83
|
+
axis_y:
|
|
84
|
+
grid:
|
|
85
|
+
zero:
|
|
86
|
+
color: &value dft-grays.canvas # baseline anchors at max contrast
|
|
85
87
|
axis_quantitative:
|
|
86
|
-
|
|
88
|
+
labels:
|
|
87
89
|
font:
|
|
88
90
|
color: *value # numeric readouts POP
|
|
89
91
|
ticks:
|
|
@@ -104,9 +106,14 @@ style:
|
|
|
104
106
|
visible: true
|
|
105
107
|
bar:
|
|
106
108
|
legend:
|
|
107
|
-
|
|
109
|
+
# A bar that direct-labels has this suppressed again by resolve. It is
|
|
110
|
+
# load-bearing for the bars that cannot — grouped, or stacked but
|
|
111
|
+
# disqualified by `multiples:`, a negative measure, or a series absent
|
|
112
|
+
# from the last column — which would otherwise carry no series key at
|
|
113
|
+
# all, since the theme hides legends globally.
|
|
114
|
+
visible: true
|
|
108
115
|
axis_x: # inward-align horizontal-bar row labels (resolve scopes to left/right edge)
|
|
109
|
-
|
|
116
|
+
labels:
|
|
110
117
|
align: inward
|
|
111
118
|
scatter:
|
|
112
119
|
legend:
|
|
@@ -141,7 +148,7 @@ style:
|
|
|
141
148
|
gradient:
|
|
142
149
|
palette: viridis
|
|
143
150
|
axis_y: # inward-align heatmap row labels (y is always nominal)
|
|
144
|
-
|
|
151
|
+
labels:
|
|
145
152
|
align: inward
|
|
146
153
|
table:
|
|
147
154
|
border:
|
|
@@ -35,20 +35,22 @@ style:
|
|
|
35
35
|
single_series_palette: ['editorial-10.1'] # editorial denim single series
|
|
36
36
|
# Generic BI chrome: medium-gray, visible — nothing recedes dramatically.
|
|
37
37
|
axis:
|
|
38
|
-
|
|
38
|
+
line:
|
|
39
39
|
color: dft-grays.grid-emphasis
|
|
40
40
|
grid:
|
|
41
41
|
color: dft-grays.grid-emphasis # visible, medium (the BI-default look)
|
|
42
|
-
zero:
|
|
43
|
-
color: dft-grays.subtitle
|
|
44
42
|
ticks:
|
|
45
43
|
color: dft-grays.grid-emphasis
|
|
46
|
-
|
|
44
|
+
labels:
|
|
47
45
|
font:
|
|
48
46
|
color: dft-grays.muted
|
|
49
47
|
title:
|
|
50
48
|
font:
|
|
51
49
|
color: dft-grays.subtitle
|
|
50
|
+
axis_y:
|
|
51
|
+
grid:
|
|
52
|
+
zero:
|
|
53
|
+
color: dft-grays.subtitle
|
|
52
54
|
# Soft light tooltip, mirroring editorial's look -- stark (the structural
|
|
53
55
|
# root plain extends directly) now sets a sharp/thick/no-shadow/triangle
|
|
54
56
|
# tooltip for its own utilitarian voice; plain wants the same soft BI-report
|
|
@@ -45,20 +45,22 @@ style:
|
|
|
45
45
|
# inherit. dark/editorial/cream override.
|
|
46
46
|
single_series_palette: [dft-grays.black]
|
|
47
47
|
axis:
|
|
48
|
-
|
|
48
|
+
line:
|
|
49
49
|
color: *color_ink
|
|
50
50
|
ticks:
|
|
51
51
|
color: *color_ink
|
|
52
52
|
grid:
|
|
53
53
|
color: *color_ink
|
|
54
|
-
|
|
55
|
-
color: *color_ink
|
|
56
|
-
label:
|
|
54
|
+
labels:
|
|
57
55
|
font:
|
|
58
56
|
color: *color_ink
|
|
59
57
|
title:
|
|
60
58
|
font:
|
|
61
59
|
color: *color_ink
|
|
60
|
+
axis_y:
|
|
61
|
+
grid:
|
|
62
|
+
zero:
|
|
63
|
+
color: *color_ink
|
|
62
64
|
|
|
63
65
|
legend:
|
|
64
66
|
label:
|
|
@@ -155,12 +157,12 @@ style:
|
|
|
155
157
|
color: *color_ink
|
|
156
158
|
|
|
157
159
|
axis_quantitative:
|
|
158
|
-
|
|
160
|
+
labels:
|
|
159
161
|
font:
|
|
160
162
|
family: '''DFT Sans Tabular'', Inter, system-ui, sans-serif'
|
|
161
163
|
|
|
162
|
-
# Direct-label multi-series lines/areas at their endpoints rather
|
|
163
|
-
# leaning on a side legend. Set on the structural root so the plain
|
|
164
|
+
# Direct-label multi-series lines/areas/bars at their endpoints rather
|
|
165
|
+
# than leaning on a side legend. Set on the structural root so the plain
|
|
164
166
|
# theme inherits it; the crafted themes (editorial/vivid/neon) already
|
|
165
167
|
# set their own true.
|
|
166
168
|
line:
|
|
@@ -169,6 +171,13 @@ style:
|
|
|
169
171
|
area:
|
|
170
172
|
endpoint_labels:
|
|
171
173
|
visible: true
|
|
174
|
+
# A vertical bar's labels anchor to the last column's per-series stack
|
|
175
|
+
# midpoints; a horizontal bar's take the top-row rail instead. Resolve
|
|
176
|
+
# turns this back off wherever the rail cannot name every series — see
|
|
177
|
+
# _bar_endpoint_labels_for_stack.
|
|
178
|
+
bar:
|
|
179
|
+
endpoint_labels:
|
|
180
|
+
visible: true
|
|
172
181
|
|
|
173
182
|
pie:
|
|
174
183
|
total:
|
|
@@ -35,16 +35,14 @@ style:
|
|
|
35
35
|
categorical:
|
|
36
36
|
single_series_palette: ['vivid-10.1'] # brand-blue single series
|
|
37
37
|
axis:
|
|
38
|
-
|
|
38
|
+
line:
|
|
39
39
|
color: *faint
|
|
40
40
|
grid:
|
|
41
41
|
color: *faint
|
|
42
42
|
width: 1
|
|
43
|
-
zero:
|
|
44
|
-
color: &value dft-grays.ink # baseline anchors at max contrast
|
|
45
43
|
ticks:
|
|
46
44
|
color: *faint
|
|
47
|
-
|
|
45
|
+
labels:
|
|
48
46
|
font:
|
|
49
47
|
color: *mid # category/time labels = MID
|
|
50
48
|
title:
|
|
@@ -53,9 +51,13 @@ style:
|
|
|
53
51
|
axis_x:
|
|
54
52
|
grid:
|
|
55
53
|
visible: false
|
|
54
|
+
axis_y:
|
|
55
|
+
grid:
|
|
56
|
+
zero:
|
|
57
|
+
color: &value dft-grays.ink # baseline anchors at max contrast
|
|
56
58
|
# Numeric axis readouts POP at the max VALUE tier (TradingView scale-text).
|
|
57
59
|
axis_quantitative:
|
|
58
|
-
|
|
60
|
+
labels:
|
|
59
61
|
font:
|
|
60
62
|
color: *value
|
|
61
63
|
ticks:
|
|
@@ -76,13 +78,18 @@ style:
|
|
|
76
78
|
visible: true
|
|
77
79
|
bar:
|
|
78
80
|
legend:
|
|
79
|
-
|
|
81
|
+
# A bar that direct-labels has this suppressed again by resolve. It is
|
|
82
|
+
# load-bearing for the bars that cannot — grouped, or stacked but
|
|
83
|
+
# disqualified by `multiples:`, a negative measure, or a series absent
|
|
84
|
+
# from the last column — which would otherwise carry no series key at
|
|
85
|
+
# all, since the theme hides legends globally.
|
|
86
|
+
visible: true
|
|
80
87
|
axis_x: # inward-align horizontal-bar row labels (resolve scopes to left/right edge)
|
|
81
|
-
|
|
88
|
+
labels:
|
|
82
89
|
align: inward
|
|
83
90
|
heatmap: # inward-align heatmap row labels (y is always nominal)
|
|
84
91
|
axis_y:
|
|
85
|
-
|
|
92
|
+
labels:
|
|
86
93
|
align: inward
|
|
87
94
|
scatter:
|
|
88
95
|
legend:
|
|
@@ -20,7 +20,6 @@ from dataface.core.diagnostics.codes_compile import (
|
|
|
20
20
|
ERR_EXTRA_FIELD,
|
|
21
21
|
ERR_FILE_NOT_FOUND,
|
|
22
22
|
ERR_JINJA_ERROR,
|
|
23
|
-
ERR_LABEL_VALUES_MEASURE_AXIS,
|
|
24
23
|
ERR_LAYERS_AMBIGUOUS_Y_DOMAIN,
|
|
25
24
|
ERR_LINE_Y_NOT_NUMERIC,
|
|
26
25
|
ERR_LOG_SCALE_REQUIRES_POSITIVE_DATA,
|
|
@@ -114,6 +113,7 @@ from dataface.core.diagnostics.codes_unknown import ERR_INTERNAL
|
|
|
114
113
|
from dataface.core.diagnostics.diagnostic import (
|
|
115
114
|
ColumnSpan,
|
|
116
115
|
Diagnostic,
|
|
116
|
+
RelatedLocation,
|
|
117
117
|
SourceRange,
|
|
118
118
|
build_diagnostic,
|
|
119
119
|
display_message,
|
|
@@ -137,6 +137,7 @@ __all__ = [
|
|
|
137
137
|
"Diagnostic",
|
|
138
138
|
"SourceRange",
|
|
139
139
|
"ColumnSpan",
|
|
140
|
+
"RelatedLocation",
|
|
140
141
|
"build_diagnostic",
|
|
141
142
|
"display_message",
|
|
142
143
|
"build_doc_url",
|
|
@@ -166,7 +167,6 @@ __all__ = [
|
|
|
166
167
|
"ERR_LOG_SCALE_REQUIRES_POSITIVE_DATA",
|
|
167
168
|
"ERR_AREA_STACKED_LOG_SCALE_NOT_SUPPORTED",
|
|
168
169
|
"ERR_FILE_NOT_FOUND",
|
|
169
|
-
"ERR_LABEL_VALUES_MEASURE_AXIS",
|
|
170
170
|
"ERR_TICKS_INTERVAL_MEASURE_AXIS",
|
|
171
171
|
# Compile warnings
|
|
172
172
|
"WARN_UNREFERENCED_CHART",
|