forgegraph 0.4.0__tar.gz → 0.5.0__tar.gz
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.
- {forgegraph-0.4.0 → forgegraph-0.5.0}/CHANGELOG.md +38 -28
- {forgegraph-0.4.0 → forgegraph-0.5.0}/CONTRIBUTING.md +2 -2
- {forgegraph-0.4.0 → forgegraph-0.5.0}/PKG-INFO +1 -1
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/design/architecture.md +1 -1
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/packs/forgegraph_default/layouts/layout.md +9 -2
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/packs/forgegraph_default/pack.md +7 -3
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/packs/forgegraph_default/themes/light_theme.md +1 -1
- forgegraph-0.5.0/docs/specifications/layout_example.md +88 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/specifications/theme.md +6 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/pyproject.toml +1 -1
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/cli/schema.py +10 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/drawing/axes.py +8 -7
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/drawing/layers.py +31 -9
- forgegraph-0.5.0/src/forgegraph/drawing/page.py +434 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/drawing/spacing.py +56 -31
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/drawing/style.py +4 -4
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/fonts/__init__.py +10 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/layouts/__init__.py +68 -23
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/layouts/separators.py +14 -4
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/packs/forgegraph_default/layouts/layout.yaml +8 -1
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/packs/forgegraph_default/pack.yaml +2 -2
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/packs/forgegraph_default/themes/light_theme.yaml +1 -1
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/spec/layout.py +94 -22
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/spec/parameters.py +19 -1
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/spec/validate.py +22 -1
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/specifications/layout_example.yaml +4 -1
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/specifications/theme.yaml +6 -0
- forgegraph-0.5.0/tests/baselines/crowded_runs.png +0 -0
- forgegraph-0.5.0/tests/baselines/flow_sweep_wide.png +0 -0
- forgegraph-0.5.0/tests/baselines/fuel_by_size.png +0 -0
- forgegraph-0.5.0/tests/baselines/layout_anode_layers.png +0 -0
- forgegraph-0.5.0/tests/baselines/layout_screening_and_rate.png +0 -0
- forgegraph-0.5.0/tests/baselines/polarization.png +0 -0
- forgegraph-0.5.0/tests/baselines/polarization_narrow.png +0 -0
- forgegraph-0.5.0/tests/baselines/precipitation_loglog.png +0 -0
- forgegraph-0.5.0/tests/baselines/replicates.png +0 -0
- forgegraph-0.5.0/tests/baselines/screening_scatter.png +0 -0
- forgegraph-0.5.0/tests/baselines/separator_colorbar.png +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_layers.py +34 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_layout.py +252 -4
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_specs.py +49 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_styles.py +26 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_text.py +21 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/uv.lock +1 -1
- forgegraph-0.4.0/docs/specifications/layout_example.md +0 -28
- forgegraph-0.4.0/src/forgegraph/drawing/page.py +0 -219
- forgegraph-0.4.0/tests/baselines/crowded_runs.png +0 -0
- forgegraph-0.4.0/tests/baselines/flow_sweep_wide.png +0 -0
- forgegraph-0.4.0/tests/baselines/fuel_by_size.png +0 -0
- forgegraph-0.4.0/tests/baselines/layout_anode_layers.png +0 -0
- forgegraph-0.4.0/tests/baselines/layout_screening_and_rate.png +0 -0
- forgegraph-0.4.0/tests/baselines/polarization.png +0 -0
- forgegraph-0.4.0/tests/baselines/polarization_narrow.png +0 -0
- forgegraph-0.4.0/tests/baselines/precipitation_loglog.png +0 -0
- forgegraph-0.4.0/tests/baselines/replicates.png +0 -0
- forgegraph-0.4.0/tests/baselines/screening_scatter.png +0 -0
- forgegraph-0.4.0/tests/baselines/separator_colorbar.png +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/.gitignore +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/.pre-commit-config.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/LICENSE +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/README.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/TRADEMARKS.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/design/initial-specification.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/design/todo.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/packs/acme/formats/slide.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/packs/acme/graphspecs/polarization-curve.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/packs/acme/layouts/dashboard.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/packs/acme/pack.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/packs/acme/schedules/workday.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/packs/acme/themes/acme.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/packs/forgegraph_default/catalogs/formats/compact_format.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/packs/forgegraph_default/catalogs/formats/wide_format.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/packs/forgegraph_default/catalogs/themes/light_theme.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/packs/forgegraph_default/formats/compact_format.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/packs/forgegraph_default/formats/wide_format.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/packs/forgegraph_default/graphspecs/graphspec_example.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/packs/forgegraph_default/schedules/schedule.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/specifications/catalogs/graphtype_xy.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/specifications/catalogs/layer_error_bar.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/specifications/catalogs/layer_note.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/specifications/catalogs/layer_reference.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/specifications/config.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/specifications/format.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/specifications/graphspec_example.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/specifications/graphtype_xy.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/specifications/layer_error_bar.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/specifications/layer_note.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/specifications/layer_reference.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/specifications/pack.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/specifications/provenance.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/specifications/schedule.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/specifications/styles.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/docs/specs_and_packs_revisions/notes.docx +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/README.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/forgegraph.toml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/lab_throughput.layout.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/lab_throughput.layout.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/lost_runs.parquet +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/lost_runs.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/ops/formats/dashboard.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/ops/formats/dashboard.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/ops/pack.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/prep_lab_dashboard.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/run_time.parquet +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/run_time.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/runs_gc_ms_01.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/runs_hplc_01.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/runs_hplc_02.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/runs_icp_oes_01.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/samples_weekly.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/throughput_weekly.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/weekly_gc_ms_01.parquet +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/weekly_hplc_01.parquet +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/weekly_hplc_02.parquet +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/weekly_icp_oes_01.parquet +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/weekly_runs.parquet +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/dashboard/weekly_samples.parquet +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/paper/cell_durability.parquet +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/paper/cell_performance.layout.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/paper/cell_performance.layout.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/paper/cell_polarization.parquet +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/paper/forgegraph.toml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/paper/hfr_time.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/paper/journal/formats/journal-double.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/paper/journal/formats/journal-double.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/paper/journal/pack.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/paper/polarization.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/paper/prep_cells.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/layouts/paper/voltage_time.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/packs/acme/formats/slide.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/packs/acme/graphspecs/polarization-curve.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/packs/acme/layouts/dashboard.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/packs/acme/pack.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/packs/acme/references/house.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/packs/acme/schedules/workday.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/packs/acme/themes/acme.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/README.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/air_passengers.csv +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/air_passengers_by_month.csv +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/anscombe.csv +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/assay_summary.csv +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/cell_durability.csv +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/cell_polarization.csv +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/co2_monthly.csv +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/diamonds.csv +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/dose_response.csv +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/economics.csv +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/enzyme_kinetics.csv +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/fmri.csv +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/gapminder.csv +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/geyser.csv +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/growth.csv +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/iris.csv +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/lab_export.xlsx +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/lab_runs.xlsx +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/penguins.csv +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/seaice.csv +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/sensor_log_eu.csv +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/stocks.csv +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/tips.csv +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/examples/sample-data/weather.xlsx +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/scripts/build_example_data.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/scripts/build_recreations.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/scripts/build_sample_data.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/scripts/check_denylist.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/scripts/forgemark.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/scripts/mirror_specs.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/scripts/report_skill_evals.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/scripts/run_skill_evals.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/scripts/smoke_installed.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/scripts/visual_changelog.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/__init__.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/__main__.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/canvas.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/catalog_build/__init__.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/catalog_build/build.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/catalog_build/cards.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/catalog_build/examples.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/catalog_build/files.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/catalog_build/page.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/cli/__init__.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/cli/catalog.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/cli/graphs.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/cli/jobs.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/cli/skill.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/data.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/drawing/__init__.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/drawing/colorbar.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/drawing/dates.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/drawing/errors.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/drawing/export.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/drawing/figure.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/drawing/key.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/drawing/labels.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/drawing/layout.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/drawing/limits.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/drawing/lint.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/drawing/marks.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/drawing/markup.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/drawing/text.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/drawing/ticks.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/drawing/xy.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/drawn.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/fonts/IBMPlexSerif-Bold.ttf +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/fonts/IBMPlexSerif-Light.ttf +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/fonts/IBMPlexSerif-Medium.ttf +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/fonts/IBMPlexSerif-Regular.ttf +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/fonts/IBMPlexSerif-SemiBold.ttf +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/fonts/OFL-IBMPlexSerif.txt +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/fonts/OFL-Roboto.txt +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/fonts/OFL-SourceSans3.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/fonts/Roboto-Bold.ttf +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/fonts/Roboto-Italic.ttf +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/fonts/Roboto-Light.ttf +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/fonts/Roboto-Medium.ttf +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/fonts/Roboto-Regular.ttf +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/fonts/Roboto-SemiBold.ttf +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/fonts/SourceSans3-Bold.ttf +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/fonts/SourceSans3-It.ttf +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/fonts/SourceSans3-Light.ttf +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/fonts/SourceSans3-Medium.ttf +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/fonts/SourceSans3-Regular.ttf +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/fonts/SourceSans3-Semibold.ttf +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/forgemark.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/graph.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/jobs/__init__.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/jobs/run.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/jobs/status.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/packs/__init__.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/packs/forgegraph_default/catalogs/formats/compact_format.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/packs/forgegraph_default/catalogs/formats/wide_format.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/packs/forgegraph_default/catalogs/themes/light_theme.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/packs/forgegraph_default/formats/compact_format.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/packs/forgegraph_default/formats/wide_format.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/packs/forgegraph_default/graphspecs/dated.parquet +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/packs/forgegraph_default/graphspecs/fanned.parquet +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/packs/forgegraph_default/graphspecs/graphspec_example.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/packs/forgegraph_default/graphspecs/polarization.parquet +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/packs/forgegraph_default/graphspecs/reinforced.parquet +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/packs/forgegraph_default/schedules/schedule.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/skill/__init__.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/skill/forgegraph/SKILL.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/skill/forgegraph/examples/bars.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/skill/forgegraph/examples/figure.layout.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/skill/forgegraph/examples/figure.schedule.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/skill/forgegraph/examples/layers.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/skill/forgegraph/examples/xy.parquet +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/skill/forgegraph/examples/xy.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/skill/forgegraph/references/requests.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/skill/forgegraph/references/themes_formats_configs.md +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/spec/__init__.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/spec/access.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/spec/common.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/spec/explain.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/spec/layers.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/spec/loaded.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/spec/provenance.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/spec/registry.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/spec/resolve.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/spec/schedule.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/spec/series.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/spec/tags.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/specifications/catalogs/graphtype_xy.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/specifications/catalogs/layer_error_bar.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/specifications/catalogs/layer_note.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/specifications/catalogs/layer_reference.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/specifications/config.toml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/specifications/format.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/specifications/graphspec_example.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/specifications/graphtype_xy.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/specifications/layer_error_bar.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/specifications/layer_note.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/specifications/layer_reference.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/specifications/pack.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/specifications/provenance.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/specifications/schedule.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/src/forgegraph/specifications/styles.yaml +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/conftest.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/fixtures/catalog_comments.html +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/pack_helpers.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/recreations.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/render_helpers.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/spec_mirror.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_axes.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_blank.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_catalog.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_catalog_files.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_catalog_inputs.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_catalog_page.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_cli.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_dates.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_denylist.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_jobs.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_key_position.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_labels.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_lint.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_marks.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_markup.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_packaging.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_packs.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_perturbation.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_regression.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_render.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_schedule.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_series.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_smoke.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_spec_comments.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_spec_mirrors.py +0 -0
- {forgegraph-0.4.0 → forgegraph-0.5.0}/tests/test_tags.py +0 -0
|
@@ -1,40 +1,52 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
ForgeGraph follows [semantic versioning](https://semver.org). Before 1.0, a minor version may change how graphs look or how specs are written.
|
|
3
|
+
ForgeGraph follows [semantic versioning](https://semver.org). Before 1.0, a minor version may change how graphs look or how specs are written.
|
|
4
|
+
|
|
5
|
+
## 0.5.0 (2026-10-29)
|
|
6
|
+
|
|
7
|
+
### New Features
|
|
8
|
+
|
|
9
|
+
- **Nested tabs.** A tab in a layout can now hold tabs of its own, to any depth. The HTML page shows a row of tabs for each level, and switching to another group keeps the same chart showing.
|
|
10
|
+
- **Tick labels per axis.** `axis.x.tick.label` and `axis.y.tick.label` set the size, weight and color of each axis's tick labels separately. Anything they don't set comes from `axis.tick.label`.
|
|
11
|
+
- **Tabs without scripts.** The tabs on a layout's HTML page now work where the page's scripts don't run, such as in a browser with JavaScript turned off. Before, such a page showed every tab, one below another.
|
|
12
|
+
- **Tab bar style.** A layout's `tab_bar.label` and `tab_bar.line` set the look of the tab names and of the line under them on the HTML page. By default they follow the first graph's theme.
|
|
13
|
+
|
|
14
|
+
### Changes
|
|
15
|
+
|
|
16
|
+
- **Titles.** Titles now sit `title.gap` above the plot, measured from the line the letters sit on, so titles side by side line up whatever their letters. The default theme's `title.gap` is now 0.41, which keeps its titles where they were. A theme that sets its own `title.gap` will see its titles sit a little higher than in 0.4.
|
|
17
|
+
- **Notes above the plot.** A note placed outside the plot at the top now goes between the title and the plot, instead of above the title.
|
|
18
|
+
- **Tab names on the page.** The tab names are written in the theme's font and color, instead of the browser's font in dark grey.
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
- Tick labels now use the theme's tick label font and weight. Before, only their size and color were applied.
|
|
23
|
+
- A y-axis title reading upward now sits the same distance from the tick labels whatever its letters.
|
|
4
24
|
|
|
5
25
|
## 0.4.0 (2026-10-27)
|
|
6
26
|
|
|
7
|
-
|
|
27
|
+
### New Features
|
|
8
28
|
|
|
9
|
-
|
|
29
|
+
- **Multiple Series.** GraphSpecs can now have independent Series, enabling full control over the marker, line, and bar properties and not just variation by a palette.
|
|
30
|
+
- **Reference bands.** The `reference_line` layer is now `reference` and has been augmented to also draw lines per element of the series and bands. It also can now compute a value plus offset (e.g. a line at mean + 3 s.d.). This enables drawing of error bands, confidence intervals of model fits, and other more complex references.
|
|
31
|
+
- **More aggregations.** Series, reference lines, and error bars can show the mean, median, percentile, standard deviation, standard error or confidence intervals.
|
|
32
|
+
- **Stepped and smooth lines.** `line.interpolate` can now be steps or a smooth curve in addition to a direct line.
|
|
33
|
+
- **Dates as values.** Dates, such as `2025-08-25`, can now be used directly in all relevant parameters.
|
|
10
34
|
|
|
11
|
-
|
|
12
|
-
- The specification files, which define each kind, are in `src/forgegraph/specifications/`, and their pages in `docs/specifications/`; no spec can name or extend them. The built-in pack's pages are in `docs/packs/forgegraph_default/`, and the catalog page is `catalog/forgegraph_default.html`.
|
|
13
|
-
- A category's brackets in a specification file say what the code reads about it, from a fixed set: its style, `same as <sibling>` for a category with a sibling's parameters, and `look from <theme category>` for a layer category whose look comes from the theme. Anything else there is an error, and descriptions are never read, so rewording one changes nothing.
|
|
14
|
-
- A style's member that no file tags, itself or through a category inside `parameters`, is fixed or variable as its general parameter is: a fixed `text.font` fixes every text style's font, even where the theme sets the style's own.
|
|
15
|
-
- `forgegraph schema <kind>` no longer says whether a parameter is fixed: it shows the kind's specification, which isn't what graphs are drawn with. `forgegraph schema graphspec` lists what a GraphSpec can set, and `forgegraph explain` shows what's fixed.
|
|
16
|
-
- A reference layer's `fill.color` may be None: two lines with values make a band only when it isn't.
|
|
17
|
-
- **Breaking for existing specs:** a GraphSpec lists its series by name under `series`, each drawn from its own column (by default, the column of its name) with its own settings: `aggregation`, `split`, `order`, `color`, `color_scale`, `line`, `marker`, `bar` and `value_label`, and the theme's variable series parameters it sets for itself. They replace `axis.*.data.aggregation`, `encoding.*` and `mark.*`; the value axis's `data.column` is None. `arrangement.base_axis`, `arrangement.stacking`, `arrangement.offset` and `arrangement.total.show` replace `series.base_axis`, `series.aggregation`, `series.offset` and `series.value_label.total.show`. A series' color, line style, marker shape and fill take `first`, `series`, a value or a column; series take palette colors in the order they're listed.
|
|
18
|
-
- **Breaking for existing specs:** the `reference` layer replaces `reference_line`: `line_1` and `line_2`, each at a value or from a column, with its own `aggregation` and `offset` (aggregation styles), `scope`, line and label (with `label.inside`), and the `fill` of a band between them. The theme's `layer.reference` replaces `layer.reference_line`.
|
|
19
|
-
- An aggregation style (`function`, `multiplier`, `percentile`) for a series, a reference line and error bars; functions include `percentile`, `stddev`, `stderr` and `ci`.
|
|
20
|
-
- `line.interpolate` draws a series' line straight, in steps (`step_after`, `step_before`, `step_center`) or as a smooth curve that never overshoots its points.
|
|
21
|
-
- A note and a reference layer's lines, labels and fill may set the theme's variable look for themselves.
|
|
22
|
-
- A style key (one from `encoding.line.style.column`, `encoding.marker.shape.column` or `encoding.marker.fill.column`) follows `legend.key.title.text`, as the series' key does: `column_name`, the default, gives each key its own column's name, so default graphs are unchanged; `None` removes every key's title; a text titles one key, and with more than one key drawn it's an error. With no `encoding.color.column`, a style key's samples are drawn in the one series color, not the text color. `render --json` reports `style_keys`, the column each style key drawn explains (for a layout, each tile's), beside `labelling`.
|
|
23
|
-
- A range type `date`: a date, or a date and time, as YAML's bare date (`2025-08-25`, `2025-08-25 06:00:00`) or ISO 8601 text (`"2025-08-25T06:00"`); a time with a zone is at its clock time, as the data's are. `axis.x.min`, `axis.x.max`, `axis.y.min` and `axis.y.max`, a reference line's `value` and `benchmark.value` take it: on a datetime axis or column, a date is drawn exactly where its day number is, and numbers keep working as day numbers. A date on an axis that isn't dates is an error naming the axis's type. Text that reads as a date is a reference line's column only if the data has a column of that name. Provenance records write dates as ISO 8601 text.
|
|
24
|
-
- On a date value axis, what would draw nonsense is an error that says what to do: stacking (`series.aggregation` sum or percent), adding up rows (`axis.*.data.aggregation` sum), bars (`mark.bar.show`, which would start at 1970), and value labels without a date format. A reference line at a date column's aggregate is on a date axis only, at its mean, median, min or max, with multiplier 1. A date axis's `tick.label.format` must have a date directive.
|
|
35
|
+
### Changes
|
|
25
36
|
|
|
26
|
-
|
|
37
|
+
- **One built-in pack, `forgegraph_default`.** `forgegraph_base` is renamed `forgegraph_default` and its specifications have been renamed. `forgegraph_base_2` has been removed. Please update your config and any GraphSpecs or packs referencing the old packs. The default theme has been tuned slightly to fix more parameters, reducing complexity of graph design.
|
|
38
|
+
- **Themes, formats and GraphSpecs are written differently.** Each has one `parameters` tree, and `!fix` and `!var` tags are used to set what a GraphSpec can change, in place of the `fixed_parameters` and `variable_parameters` blocks. A theme sets the parameters of all text and all lines once, under `text` and `line`, and each category that uses them, such as the title or the tick labels, only needs to modify the parameters that are different from that default.
|
|
39
|
+
- **Simplified Specifications.** Numerous simplifications to the way specifications are written, making them easier to read and modify.
|
|
27
40
|
|
|
28
|
-
|
|
29
|
-
- The `outside-limits` and `log-axis-values` lint checks count only data points. A reference line's ends and an error bar's ends no longer count as points outside a set axis limit, or as values of 0 or below on a log axis.
|
|
30
|
-
- A style key on the benchmark's column matches the drawing: its entries are the groups the series are styled by, so a value after the benchmark no longer gets the next value's style, and the benchmark, drawn in its own style, has no style sample and is named once, by `legend.method`. When `legend.method: key` doesn't fit, the error says why: every position in `legend.key.positions` is taken or covers data, naming the position each style key took.
|
|
31
|
-
- YAML's null (`null`, `~` or nothing) is None everywhere a spec can say None. `extends: null` failed with `KeyError: None`, and a file extending null wasn't required to list every parameter.
|
|
32
|
-
- Computed values are compared with an axis's limits allowing for float noise: a value is past a limit only if it's past by more than 1e-9 of the axis's span, measured in powers of ten on a log axis. A stack that totals `axis.y.max` but for its last digits isn't reported by `outside-limits`; a reference line at a computed value that equals a set limit isn't an error; bars whose values are 0 but for float noise still stand on the axis; and a tick a hair past the end of the view, which matplotlib draws, is measured for layout and lint and counts when log labels choose between plain numbers and powers of ten.
|
|
33
|
-
- Dates and times draw the same on y as on x: every value placed on an axis (the data, its aggregates, stacks, reference lines, error bars and limits) becomes a number the same way, so a date y axis has date ticks, a reference line at a date column's aggregate lands on it, and error bars from a date column's spread are in days. With value labels off, a date `tick.label.format` such as `"%H:%M"` on y is no longer refused as a number format; value labels and a reference line's value on a date axis are written in a date format.
|
|
41
|
+
### Bug Fixes
|
|
34
42
|
|
|
35
|
-
|
|
43
|
+
- Dates on the y axis draw as they do on x: date ticks, reference lines at a date column's mean or median, and error bars in days.
|
|
44
|
+
- The lint no longer counts reference lines and error bars as data points outside the axis limits, or as values of 0 or below on a log axis.
|
|
45
|
+
- Values that differ from an axis limit only by rounding are no longer reported as outside it.
|
|
46
|
+
- In a theme with a light font weight, powers of ten on a log axis no longer give a `font-fallback` lint issue.
|
|
47
|
+
- `null` and `~` are accepted everywhere a spec can say None.
|
|
36
48
|
|
|
37
|
-
|
|
49
|
+
## 0.3.0 (2026-10-25)
|
|
38
50
|
|
|
39
51
|
### Added
|
|
40
52
|
|
|
@@ -78,8 +90,6 @@ Images that changed: the [visual changelog](docs/visual-changelog/0.3.0.html).
|
|
|
78
90
|
|
|
79
91
|
## 0.2.0 (2026-10-24)
|
|
80
92
|
|
|
81
|
-
Images that changed: the [visual changelog](docs/visual-changelog/0.2.0.html).
|
|
82
|
-
|
|
83
93
|
### Changed
|
|
84
94
|
|
|
85
95
|
- Error bars are black by default in both built-in themes, and their color (`layer.error_bar.color`) is a variable parameter: a GraphSpec may set `series` or any hex color.
|
|
@@ -9,7 +9,7 @@ uv sync
|
|
|
9
9
|
uv run pre-commit install
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
While you work, `uv run pytest` runs the development suite: every test not marked slow, in parallel. The full suite adds the slow ones (every catalog card drawn, the image baselines, the catalog pages, and any test over a second). CI runs it on every pull request, and it's the one to run before a release:
|
|
12
|
+
While you work, `uv run pytest` runs the development suite: every test not marked slow, in parallel. The full suite adds the slow ones (every catalog card drawn, the image baselines, the catalog pages, and any test over a second). CI runs it on every pull request that changes more than pages no test reads (`review/`, `CHANGELOG.md`, `CONTRIBUTING.md`, and `docs/` other than the spec and pack pages), and it's the one to run before a release:
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
15
|
uv run pytest --full && uv run ruff check . && uv run ruff format --check && uv run pyright
|
|
@@ -39,6 +39,6 @@ Open a GitHub issue describing the graph you need, ideally with an example image
|
|
|
39
39
|
|
|
40
40
|
1. Set the new version in pyproject.toml and in the built-in pack's pack.yaml, and for a new minor version, its `requires` (`">=0.4, <0.5"` for 0.4).
|
|
41
41
|
2. Rebuild the catalog and mirrors (above).
|
|
42
|
-
3. Write the
|
|
42
|
+
3. Write the CHANGELOG entry: the key features a user would want to know were added, changed or removed, and the bugs fixed.
|
|
43
43
|
4. Merge with CI green, and tag the merge commit `v<version>`.
|
|
44
44
|
5. Run the Publish workflow.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: forgegraph
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Fast, consistent, publication-quality graphs from pandas DataFrames, usable by people and AI agents.
|
|
5
5
|
Project-URL: Repository, https://github.com/sskaye/forgegraph
|
|
6
6
|
Project-URL: Issues, https://github.com/sskaye/forgegraph/issues
|
|
@@ -429,7 +429,7 @@ Every output embeds a provenance record, at its format's provenance.level (full,
|
|
|
429
429
|
- **One version number: the library's**, using semver. The built-in packs are released with it and carry its version; other packs have their own, like any package.
|
|
430
430
|
- **Major:** a breaking change to a spec format.
|
|
431
431
|
- **Minor:** releases may change how graphs look.
|
|
432
|
-
- **Visual changelog:**
|
|
432
|
+
- **Visual changelog:** `scripts/visual_changelog.py` shows before and after images of every catalog card and regression graph that changed between two commits, for checking how a change alters the look.
|
|
433
433
|
- **Pinning:** projects and scheduled renders pin versions through their lockfile, so a dashboard can't change appearance overnight.
|
|
434
434
|
- **No telemetry.** Feature requests come from the request flow and GitHub issues.
|
|
435
435
|
|
|
@@ -21,11 +21,18 @@ parameters: !var *# !var/!fix sets whether a layout may chang
|
|
|
21
21
|
border:\
|
|
22
22
|
show: false *# \[true, false\]; Whether to also draw the line around the outside of the tiles, through the middle of the inner gap.*\
|
|
23
23
|
corner_radius: 0 *# \[float ≥ 0\]; The radius of the corners of each tile's background, × base size.*\
|
|
24
|
-
|
|
24
|
+
tab_bar: *# The row of tab names at the top of the HTML page of a layout with more than one tab.*\
|
|
25
|
+
label: *# \[text style\]; The tab names. Its defaults are the first tile's theme's text. The selected tab is underlined in its color.*\
|
|
26
|
+
alignment: left *# \[left, center, right\]; The text alignment.*\
|
|
27
|
+
line: *# \[line style\]; The line under the tab bar, and the border's top when the border is drawn. Its defaults are the first tile's theme's line.*\
|
|
28
|
+
style: None *# \[None, solid, dashed, dotted\]; The line style. If None, no line is drawn.*\
|
|
29
|
+
width: 0.05 *# \[float ≥ 0\]; The width, × base size. If 0, no line is drawn.*\
|
|
30
|
+
color: "#D9D9D9" *# \[hex color\]; The color.*\
|
|
31
|
+
tabs: *# The layout's tabs, in order, each under the name shown on its tab. A tab holds a grid and its tiles, or tabs of its own, to any depth. A layout's tabs replace those it inherits.*\
|
|
25
32
|
main:\
|
|
26
33
|
grid:\
|
|
27
34
|
areas: \[\[a\]\] *# \[text grid\]; A list of tiles for each row. A . leaves a cell blank. A name repeated over a rectangle of cells makes that tile span them, e.g. \[a, a\] makes tile a span two columns.*\
|
|
28
35
|
column_widths: None *# \[None, floats > 0\]; The relative widths of the columns' plot boxes. If None, they're equal.*\
|
|
29
36
|
row_heights: None *# \[None, floats > 0\]; The relative heights of the rows' plot boxes. If None, they're equal, or as tall as the tiles need.*\
|
|
30
37
|
tiles: *# The GraphSpec drawn in each area of the grid, under the area's name.*\
|
|
31
|
-
a: None *# \[text\]; The GraphSpec file: an absolute path, a path starting with
|
|
38
|
+
a: None *# \[text\]; The GraphSpec file: an absolute path, a path starting with ~, or a path relative to this file. A layout must set it.*
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
This page mirrors src/forgegraph/packs/forgegraph_default/pack.yaml, which is the specification. A test fails if they ever differ.
|
|
4
4
|
|
|
5
5
|
name: forgegraph_default *# \[text\]; The pack's name, either a folder where the pack is stored or a pip pack's entry point.*\
|
|
6
|
-
version: 0.
|
|
6
|
+
version: 0.5.0 *# \[text\]; The pack's release version.*\
|
|
7
7
|
description: ForgeGraph's built-in default pack. So it’s always possible to create a graph. *# \[text\]; One line, describing what the pack is.*\
|
|
8
8
|
issues: https://github.com/sskaye/forgegraph/issues *# \[text\]; Where to file feature requests and bug reports about the pack.*\
|
|
9
|
-
requires: <!-- fmc:1 -->">=0.
|
|
9
|
+
requires: <!-- fmc:1 -->">=0.5, <0.6"<!-- /fmc:1 --> *# \[None, version specifier\]; The ForgeGraph version required for the pack. pip format, e.g. ">=0.6, <1". If None, it works with any version.*
|
|
10
10
|
|
|
11
11
|
<!-- forgemark-comments
|
|
12
12
|
- id: 1
|
|
13
|
-
anchor_text: "\">=0.
|
|
13
|
+
anchor_text: "\">=0.5, <0.6\""
|
|
14
14
|
context_before: "xt\\]; Where to file feature requests and bug reports about the pack.*\\ requires:"
|
|
15
15
|
context_after: "# \\[None, version specifier\\]; The ForgeGraph version required for the pack."
|
|
16
16
|
author: "Steven Kaye"
|
|
@@ -26,4 +26,8 @@ requires: <!-- fmc:1 -->">=0.4, <0.5"<!-- /fmc:1 --> *# \[Non
|
|
|
26
26
|
timestamp: 2026-10-28T03:30:17Z
|
|
27
27
|
body: |
|
|
28
28
|
With the 0.4.0 release, this now reads ">=0.4, <0.5" and the pack version is 0.4.0.
|
|
29
|
+
- author: Claude
|
|
30
|
+
timestamp: 2026-10-29T05:13:44Z
|
|
31
|
+
body: |
|
|
32
|
+
With the 0.5.0 release, this now reads ">=0.5, <0.6" and the pack version is 0.5.0.
|
|
29
33
|
-->
|
|
@@ -26,7 +26,7 @@ parameters: !var *# !var/!fix sets whether a GraphSpec may ch
|
|
|
26
26
|
title: *# \[text style\]; The graph's title.*\
|
|
27
27
|
size: 1.05 *# \[float > 0\]; The size, × base size.*\
|
|
28
28
|
weight: semibold *# \[light, regular, medium, semibold, bold\]; The weight.*\
|
|
29
|
-
gap: 0.
|
|
29
|
+
gap: 0.41 *# \[float ≥ 0\]; The gap between the text and whatever is around it, × base size.*\
|
|
30
30
|
axis:\
|
|
31
31
|
title: *# \[text style\]; The axis titles.*\
|
|
32
32
|
size: 1.05 *# \[float > 0\]; The size, × base size.*\
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Layout specification: layout_example
|
|
2
|
+
|
|
3
|
+
This page mirrors src/forgegraph/specifications/layout_example.yaml, which is the specification. A test fails if they ever differ.
|
|
4
|
+
|
|
5
|
+
name: layout_example *# \[text\]; The name of the layout.*\
|
|
6
|
+
version: 0 *# \[int ≥ 0\]; The version number of the specification.*\
|
|
7
|
+
description: An example layout to define the specification. *# \[text\]; The description of the layout.*\
|
|
8
|
+
extends: None *# \[None, layout\]; Each parameter not listed is inherited from this layout. If None, this file must list every parameter.*\
|
|
9
|
+
\
|
|
10
|
+
parameters: !var *# !var/!fix sets whether a layout may change a property. Subcategories/parameters inherit the setting of the one above them unless explicitly overridden.*\
|
|
11
|
+
specifications:\
|
|
12
|
+
format: forgegraph_default/wide_format *# \[format\]; The format that sets the layout's size and output, in place of its tiles' formats.*\
|
|
13
|
+
separators: *# The space and lines between and around the figures. The gaps use the background.color of the first tile's theme.*\
|
|
14
|
+
gap:\
|
|
15
|
+
inner: 1.5 *# \[float ≥ 0\]; The space between the figure's edges and the outer tiles, × base size.*\
|
|
16
|
+
outer: 1.5 *# \[float ≥ 0\]; The space between neighboring tiles, from the outermost text or line of one to that of the next, × base size.*\
|
|
17
|
+
line: *# \[line style\]; The line along the middle of each gap between tiles. Its defaults are the first tile's theme's line.*\
|
|
18
|
+
border:\
|
|
19
|
+
show: false *# \[true, false\]; Whether to also draw the line around the outside of the tiles, through the middle of the inner gap.*\
|
|
20
|
+
corner_radius: 0 *# \[float ≥ 0\]; The radius of the corners of each tile's background, × base size.*\
|
|
21
|
+
tab_bar: *# The row of tab names at the top of the HTML page of a layout with more than one tab.*\
|
|
22
|
+
label: *# \[text style\]; The tab names. Its defaults are the first tile's theme's text. The selected tab is underlined in its color.*\
|
|
23
|
+
line: *# \[line style\]; The line under the tab bar, and the border's top when the border is drawn. Its defaults are the first tile's theme's line.*\
|
|
24
|
+
tabs: *# The layout's tabs, in order, <!-- fmc:1 -->each under the name shown on its tab<!-- /fmc:1 -->. A tab holds a grid and its tiles, or tabs of its own, to any depth. A layout's tabs replace those it inherits.*\
|
|
25
|
+
main:\
|
|
26
|
+
grid:\
|
|
27
|
+
areas: \[\[a\]\] *# \[text grid\]; A list of tiles for each row. A . leaves a cell blank. A name repeated over a rectangle of cells makes that tile span them, e.g. \[a, a\] makes tile a span two columns.*\
|
|
28
|
+
column_widths: None *# \[None, floats > 0\]; The relative widths of the columns' plot boxes. If None, they're equal.*\
|
|
29
|
+
row_heights: None *# \[None, floats > 0\]; The relative heights of the rows' plot boxes. If None, they're equal, or as tall as the tiles need.*\
|
|
30
|
+
tiles: *# The GraphSpec drawn in each area of the grid, under the area's name.*\
|
|
31
|
+
a: None *# \[text\]; The GraphSpec file: an absolute path, a path starting with ~, or a path relative to this file. A layout must set it.*
|
|
32
|
+
|
|
33
|
+
<!-- forgemark-comments
|
|
34
|
+
- id: 1
|
|
35
|
+
anchor_text: "each under the name shown on its tab"
|
|
36
|
+
context_before: "faults are the first tile's theme's line.*\\ tabs: # The layout's tabs, in order,"
|
|
37
|
+
context_after: ". A tab holds a grid and its tiles, or tabs of its own, to any depth."
|
|
38
|
+
author: Claude
|
|
39
|
+
timestamp: 2026-10-29T03:24:33Z
|
|
40
|
+
resolved: false
|
|
41
|
+
body: |
|
|
42
|
+
Proposal for the HTML page's tab bar, with no new parameter.
|
|
43
|
+
|
|
44
|
+
Problem: the page draws the tab names in the browser's font and in dark grey (#333333), whatever the theme. A layout with a dark background gets an unreadable tab bar, and the tab names never match the figures' text.
|
|
45
|
+
|
|
46
|
+
Proposed change: the tab bar takes its look from what the layout already has, as the gaps already take the first tile's theme's background.color.
|
|
47
|
+
1. Each tab's name, as written under tabs, is drawn in the first tile's theme's general text: its font, weight and color, at text.size × the layout's base size.
|
|
48
|
+
2. The selected tab is underlined in that text color.
|
|
49
|
+
3. The line under the tab bar is separators.line, whose defaults are the first tile's theme's line.
|
|
50
|
+
4. The page embeds the font, since a browser shows a typeface only if the viewer has it installed or the page carries it.
|
|
51
|
+
|
|
52
|
+
Nothing is added to this specification or the theme's. The tabs description would end: "Their names are written in the first tile's theme's text."
|
|
53
|
+
|
|
54
|
+
Alternative: a tab_label text style here, with the first tile's theme's text as its defaults, as separators.line takes the theme's line. It would let a layout style its tab names apart from the figures' text, but nothing has asked for that.
|
|
55
|
+
|
|
56
|
+
Recommendation: no new parameter.
|
|
57
|
+
replies:
|
|
58
|
+
- author: Steven
|
|
59
|
+
timestamp: "2026-10-29T03:40:06.714Z"
|
|
60
|
+
body: |-
|
|
61
|
+
Better to have the control.
|
|
62
|
+
Proposal:
|
|
63
|
+
- Under tabs, add the param “tab_label” (or whatever the standard name is for the text that appears in a tab), which should be text_style, enabling setting of the params of the tab title text.
|
|
64
|
+
- The selected tab is underlined in that text color
|
|
65
|
+
- Add an additional parameter for the line under the tab bar. Again, name whatever that’s normally called and make it line style.
|
|
66
|
+
- The page embeds the font, since a browser shows a typeface only if the viewer has it installed or the page carries it.
|
|
67
|
+
- author: Claude
|
|
68
|
+
timestamp: 2026-10-29T03:41:14Z
|
|
69
|
+
body: |
|
|
70
|
+
Agreed, with one change to where the parameters go and suggested names.
|
|
71
|
+
|
|
72
|
+
1. They can't go under tabs. tabs is a mapping of the author's own tab names, so a parameter there would read as a tab named tab_label, and a layout couldn't name a tab that. They need a category of their own beside tabs and separators.
|
|
73
|
+
2. Names: the usual name for the strip of tabs is the "tab bar", for the text in a tab its "label", and for the line under the bar a "divider". "line" matches separators.line, so I'd use it over divider.
|
|
74
|
+
|
|
75
|
+
Proposed lines:
|
|
76
|
+
|
|
77
|
+
tab_bar: # The row of tab names at the top of the HTML page of a layout with more than one tab.
|
|
78
|
+
label: # [text style]; The tab names. Its defaults are the first tile's theme's text. The selected tab is underlined in its color.
|
|
79
|
+
line: # [line style]; The line under the tab bar. Its defaults are the first tile's theme's line.
|
|
80
|
+
|
|
81
|
+
3. The selected tab is marked only by its underline. Today it's also drawn semibold; dropping that lets label.weight decide every tab's weight.
|
|
82
|
+
4. With nested tabs (#67), every level's tab bar uses the same style.
|
|
83
|
+
5. These change only the HTML page, since images and PDFs have no tab bar.
|
|
84
|
+
- author: Claude
|
|
85
|
+
timestamp: 2026-10-29T05:16:29Z
|
|
86
|
+
body: |
|
|
87
|
+
Built in sskaye/forgegraph#75 as agreed: tab_bar.label and tab_bar.line, below on this page. The selected tab is underlined in the label's color, the label's alignment places the names and its gap spaces them, the page embeds the label's font, and the line can be solid, dashed or dotted. The default pack's layout keeps no line under the bar.
|
|
88
|
+
-->
|
|
@@ -39,6 +39,12 @@ parameters: !var *# !var/!fix sets whether a GraphSpec may ch
|
|
|
39
39
|
minor: *# \[line style\]; The minor tick marks.*\
|
|
40
40
|
count: 0 *# \[int ≥ 0\]; The number of minor ticks between major ticks. If 0, there are none.*\
|
|
41
41
|
length: 0.15 *# \[float ≥ 0\]; The minor tick length, × base size.*\
|
|
42
|
+
x:\
|
|
43
|
+
tick:\
|
|
44
|
+
label: *# \[text style, look from axis.tick.label\]; The x-axis tick labels. Each parameter not set here is axis.tick.label's.*\
|
|
45
|
+
y:\
|
|
46
|
+
tick:\
|
|
47
|
+
label: *# \[text style, look from axis.tick.label\]; The y-axis tick labels. Each parameter not set here is axis.tick.label's.*\
|
|
42
48
|
max_extension: 0.15 *# \[0-1\]; The most an axis may be extended to fit direct labels, notes and line labels inside the plot area, × its range. If 0, it's never extended.*\
|
|
43
49
|
plot_area:\
|
|
44
50
|
border: *# \[line style\]; The plot area's border.*\
|
|
@@ -111,6 +111,16 @@ def schema(
|
|
|
111
111
|
}
|
|
112
112
|
for p in [*grid.values(), tile]
|
|
113
113
|
]
|
|
114
|
+
out[k].append(
|
|
115
|
+
{
|
|
116
|
+
"parameter": "tabs.<tab>.tabs.<tab>",
|
|
117
|
+
"default": None,
|
|
118
|
+
"range": "a tab",
|
|
119
|
+
"required": False,
|
|
120
|
+
"description": "A tab's own tabs, each under its name, in place of its grid "
|
|
121
|
+
"and tiles; they may hold tabs too.",
|
|
122
|
+
}
|
|
123
|
+
)
|
|
114
124
|
|
|
115
125
|
def text() -> None:
|
|
116
126
|
for note in out.pop("notes", []):
|
|
@@ -361,7 +361,7 @@ def ticks_and_labels(
|
|
|
361
361
|
raise DrawError(f"{prefix}.scale: log, but the axis is categorical")
|
|
362
362
|
t = style.theme
|
|
363
363
|
order = str(t["axis.tick.label.date_order"])
|
|
364
|
-
gap = style.pt("
|
|
364
|
+
gap = style.pt(f"{prefix}.tick.label.gap")
|
|
365
365
|
locator: Locator
|
|
366
366
|
if algorithm == "all":
|
|
367
367
|
locator = FixedLocator(sorted(set(float(v) for v in values if not np.isnan(v))))
|
|
@@ -375,7 +375,7 @@ def ticks_and_labels(
|
|
|
375
375
|
locator = LogTicks(
|
|
376
376
|
str(t["axis.tick.log_algorithm"]),
|
|
377
377
|
str(t["axis.tick.algorithm"]),
|
|
378
|
-
|
|
378
|
+
gap,
|
|
379
379
|
integer=kind == "integer",
|
|
380
380
|
)
|
|
381
381
|
elif kind == "date":
|
|
@@ -415,7 +415,7 @@ def ticks_and_labels(
|
|
|
415
415
|
formatter = Wrapped(
|
|
416
416
|
formatter or axis.get_major_formatter(),
|
|
417
417
|
int(t["axis.tick.label.max_wrap"]),
|
|
418
|
-
|
|
418
|
+
gap,
|
|
419
419
|
)
|
|
420
420
|
if formatter is not None:
|
|
421
421
|
axis.set_major_formatter(formatter)
|
|
@@ -431,11 +431,12 @@ def _spacing(values: np.ndarray) -> float | None:
|
|
|
431
431
|
|
|
432
432
|
|
|
433
433
|
def check_fit(ax: Axes, gt: dict[str, Any], style: Style) -> None:
|
|
434
|
-
"""Tick labels that run together, or come within the theme's
|
|
435
|
-
other, are an error: all and manual are never
|
|
436
|
-
labels by their height, not their width, so long
|
|
437
|
-
|
|
434
|
+
"""Tick labels that run together, or come within the theme's tick label gap of each
|
|
435
|
+
other (axis.x.tick.label.gap or axis.y's), are an error: all and manual are never
|
|
436
|
+
thinned, and auto's algorithms count labels by their height, not their width, so long
|
|
437
|
+
labels can still meet."""
|
|
438
438
|
for name, axis in (("x", ax.xaxis), ("y", ax.yaxis)):
|
|
439
|
+
gap = style.pt(f"axis.{name}.tick.label.gap") * ax.figure.dpi / 72
|
|
439
440
|
algorithm = gt[f"axis.{name}.tick.algorithm"]
|
|
440
441
|
# get_majorticklabels updates the ticks first: before a draw they're stale
|
|
441
442
|
if _run_together([t for t in axis.get_majorticklabels() if t.get_text()], gap):
|
|
@@ -42,6 +42,7 @@ from matplotlib.transforms import Bbox
|
|
|
42
42
|
from forgegraph.drawing import labels, limits, marks
|
|
43
43
|
from forgegraph.drawing.axes import number_text, numbers, on_axis
|
|
44
44
|
from forgegraph.drawing.errors import DrawError
|
|
45
|
+
from forgegraph.drawing.key import keys
|
|
45
46
|
from forgegraph.drawing.markup import markup
|
|
46
47
|
from forgegraph.drawing.style import Style, unset
|
|
47
48
|
from forgegraph.drawing.text import measure
|
|
@@ -726,9 +727,28 @@ def _draw_bars(
|
|
|
726
727
|
caps.set_transform(transform)
|
|
727
728
|
|
|
728
729
|
|
|
730
|
+
TOP_NOTE = "_forgegraph_top_note" # on a note above the plot box: its gap, in points
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
def _under_top_notes(ax: Axes, renderer: Any) -> float:
|
|
734
|
+
"""How far above the plot box a note outside at the top starts, in display pixels: past
|
|
735
|
+
the x-axis's tick labels on top, and past a key outside at the top."""
|
|
736
|
+
plot = ax.get_window_extent(renderer)
|
|
737
|
+
reach = 0.0
|
|
738
|
+
if any(t.label2.get_visible() and t.label2.get_text() for t in ax.xaxis.get_major_ticks()):
|
|
739
|
+
box = ax.xaxis.get_tightbbox(renderer)
|
|
740
|
+
reach = max(reach, box.y1 - plot.y1) if box is not None else reach
|
|
741
|
+
for k in keys(ax):
|
|
742
|
+
if k.outside and k.position and k.position.startswith("top"):
|
|
743
|
+
room = k.height + k.style.pt("legend.key.gap.outer")
|
|
744
|
+
reach = max(reach, room * ax.figure.dpi / 72) # type: ignore[union-attr]
|
|
745
|
+
return reach
|
|
746
|
+
|
|
747
|
+
|
|
729
748
|
def notes_outside(ax: Axes, layers: dict[str, dict[str, Any]], style: Style) -> None:
|
|
730
|
-
"""Notes outside the plot area:
|
|
731
|
-
layout, which makes room
|
|
749
|
+
"""Notes outside the plot area: at the top, between the title and the plot box (above a
|
|
750
|
+
key outside at the top), or below the x-axis title. Drawn before layout, which makes room
|
|
751
|
+
for them; the title moves up over a note at the top."""
|
|
732
752
|
for name, layer in layers.items():
|
|
733
753
|
if layer["type"] != "note" or layer["inside"]:
|
|
734
754
|
continue
|
|
@@ -743,19 +763,19 @@ def notes_outside(ax: Axes, layers: dict[str, dict[str, Any]], style: Style) ->
|
|
|
743
763
|
own = look(style, layer)
|
|
744
764
|
gap = own.pt("layer.note.gap")
|
|
745
765
|
top = vertical == "top"
|
|
746
|
-
anchor = ax.title if top else ax.xaxis.label
|
|
747
766
|
|
|
748
|
-
def edge(renderer: Any,
|
|
767
|
+
def edge(renderer: Any, top: bool = top) -> Bbox:
|
|
749
768
|
plot = ax.get_window_extent(renderer)
|
|
750
|
-
if
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
769
|
+
if top:
|
|
770
|
+
reach = _under_top_notes(ax, renderer)
|
|
771
|
+
return Bbox.from_extents(plot.x0, plot.y0, plot.x1, plot.y1 + reach)
|
|
772
|
+
if ax.xaxis.label.get_text():
|
|
773
|
+
box = ax.xaxis.label.get_window_extent(renderer)
|
|
754
774
|
else:
|
|
755
775
|
box = ax.xaxis.get_tightbbox(renderer) or plot
|
|
756
776
|
return Bbox.from_extents(plot.x0, box.y0, plot.x1, box.y1)
|
|
757
777
|
|
|
758
|
-
_note(
|
|
778
|
+
note = _note(
|
|
759
779
|
ax,
|
|
760
780
|
layer["text"],
|
|
761
781
|
(fraction, 1.0 if top else 0.0),
|
|
@@ -765,6 +785,8 @@ def notes_outside(ax: Axes, layers: dict[str, dict[str, Any]], style: Style) ->
|
|
|
765
785
|
"bottom" if top else "top",
|
|
766
786
|
own,
|
|
767
787
|
)
|
|
788
|
+
if top:
|
|
789
|
+
setattr(note, TOP_NOTE, gap)
|
|
768
790
|
|
|
769
791
|
|
|
770
792
|
def _note(
|