forgegraph 0.5.0__tar.gz → 0.6.1__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.5.0 → forgegraph-0.6.1}/.gitignore +3 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/CHANGELOG.md +29 -0
- forgegraph-0.6.1/CITATION.cff +16 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/CONTRIBUTING.md +15 -3
- {forgegraph-0.5.0 → forgegraph-0.6.1}/PKG-INFO +11 -9
- {forgegraph-0.5.0 → forgegraph-0.6.1}/README.md +10 -8
- forgegraph-0.6.1/SECURITY.md +9 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/design/architecture.md +40 -35
- forgegraph-0.6.1/docs/examples/acme_polarization.yaml +24 -0
- forgegraph-0.6.1/docs/examples/assay_summary.yaml +33 -0
- forgegraph-0.6.1/docs/examples/cells.layout.yaml +26 -0
- forgegraph-0.6.1/docs/examples/co2.yaml +28 -0
- forgegraph-0.6.1/docs/examples/dose_response.yaml +33 -0
- forgegraph-0.6.1/docs/examples/durability.yaml +26 -0
- forgegraph-0.6.1/docs/examples/lab_polarization.yaml +24 -0
- forgegraph-0.6.1/docs/examples/packs/lab/formats/poster.yaml +11 -0
- forgegraph-0.6.1/docs/examples/packs/lab/pack.yaml +5 -0
- forgegraph-0.6.1/docs/examples/packs/lab/themes/lab_theme.yaml +13 -0
- forgegraph-0.6.1/docs/examples/penguins.yaml +29 -0
- forgegraph-0.6.1/docs/examples/polarization.yaml +24 -0
- forgegraph-0.6.1/docs/examples/savings.yaml +58 -0
- forgegraph-0.6.1/docs/examples/tips_side_by_side.yaml +46 -0
- forgegraph-0.6.1/docs/examples/tips_stacked.yaml +42 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/packs/acme/layouts/dashboard.md +1 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/packs/acme/pack.md +1 -1
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/packs/acme/themes/acme.md +1 -1
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/packs/forgegraph_default/formats/compact_format.md +2 -17
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/packs/forgegraph_default/formats/wide_format.md +3 -32
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/packs/forgegraph_default/graphspecs/graphspec_example.md +4 -4
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/packs/forgegraph_default/layouts/layout.md +3 -3
- forgegraph-0.6.1/docs/packs/forgegraph_default/pack.md +9 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/packs/forgegraph_default/schedules/schedule.md +1 -1
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/packs/forgegraph_default/themes/light_theme.md +2 -2
- forgegraph-0.6.1/docs/site/gallery.md +123 -0
- forgegraph-0.6.1/docs/site/get-started/installation.md +13 -0
- forgegraph-0.6.1/docs/site/get-started/quickstart.md +54 -0
- forgegraph-0.6.1/docs/site/get-started/skill.md +17 -0
- forgegraph-0.6.1/docs/site/guides/configs.md +38 -0
- forgegraph-0.6.1/docs/site/guides/graphs.md +74 -0
- forgegraph-0.6.1/docs/site/guides/layers.md +57 -0
- forgegraph-0.6.1/docs/site/guides/layouts.md +66 -0
- forgegraph-0.6.1/docs/site/guides/provenance.md +41 -0
- forgegraph-0.6.1/docs/site/guides/schedules.md +42 -0
- forgegraph-0.6.1/docs/site/guides/series.md +92 -0
- forgegraph-0.6.1/docs/site/guides/themes-formats-packs.md +70 -0
- forgegraph-0.6.1/docs/site/help/troubleshooting.md +133 -0
- forgegraph-0.6.1/docs/site/index.md +46 -0
- forgegraph-0.6.1/docs/site/reference/python-api.md +77 -0
- forgegraph-0.6.1/docs/site/release-notes.md +3 -0
- forgegraph-0.6.1/docs/site/stylesheets/extra.css +83 -0
- forgegraph-0.6.1/docs/specifications/config.md +6 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/specifications/format.md +1 -1
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/specifications/graphspec_example.md +3 -3
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/specifications/graphtype_xy.md +1 -1
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/specifications/layer_error_bar.md +3 -3
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/specifications/layer_reference.md +4 -33
- forgegraph-0.6.1/docs/specifications/layout_example.md +31 -0
- forgegraph-0.6.1/docs/specifications/pack.md +9 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/specifications/provenance.md +2 -2
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/specifications/schedule.md +1 -1
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/specifications/theme.md +2 -2
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/lab_throughput.layout.md +1 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/ops/formats/dashboard.md +1 -1
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/ops/formats/dashboard.yaml +1 -1
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/paper/journal/formats/journal-double.md +1 -1
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/paper/journal/formats/journal-double.yaml +1 -1
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/packs/acme/pack.yaml +1 -1
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/packs/acme/themes/acme.yaml +1 -1
- forgegraph-0.6.1/mkdocs.yml +87 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/pyproject.toml +6 -1
- forgegraph-0.6.1/scripts/docs_site.py +339 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawing/layers.py +31 -6
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawing/markup.py +17 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawing/page.py +221 -5
- forgegraph-0.6.1/src/forgegraph/drawing/readout.py +269 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawing/xy.py +15 -4
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/forgemark.py +2 -2
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/graph.py +3 -1
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/layouts/__init__.py +2 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/packs/forgegraph_default/formats/compact_format.yaml +1 -1
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/packs/forgegraph_default/formats/wide_format.yaml +1 -1
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/packs/forgegraph_default/graphspecs/graphspec_example.yaml +3 -3
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/packs/forgegraph_default/layouts/layout.yaml +3 -3
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/packs/forgegraph_default/pack.yaml +3 -3
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/packs/forgegraph_default/schedules/schedule.yaml +1 -1
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/packs/forgegraph_default/themes/light_theme.yaml +2 -2
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/skill/forgegraph/SKILL.md +4 -3
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/skill/forgegraph/references/requests.md +1 -1
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/specifications/format.yaml +1 -1
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/specifications/graphspec_example.yaml +3 -3
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/specifications/graphtype_xy.yaml +1 -1
- forgegraph-0.6.1/src/forgegraph/specifications/layer_error_bar.yaml +11 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/specifications/layer_reference.yaml +2 -2
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/specifications/layout_example.yaml +3 -3
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/specifications/pack.yaml +3 -3
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/specifications/provenance.yaml +2 -2
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/specifications/schedule.yaml +1 -1
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/specifications/theme.yaml +2 -2
- forgegraph-0.6.1/tests/test_docs_site.py +27 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_layers.py +49 -1
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_layout.py +31 -2
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_markup.py +15 -1
- forgegraph-0.6.1/tests/test_readout.py +280 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/uv.lock +569 -4
- forgegraph-0.5.0/docs/design/todo.md +0 -10
- forgegraph-0.5.0/docs/packs/forgegraph_default/pack.md +0 -33
- forgegraph-0.5.0/docs/specifications/config.md +0 -24
- forgegraph-0.5.0/docs/specifications/layout_example.md +0 -88
- forgegraph-0.5.0/docs/specifications/pack.md +0 -25
- forgegraph-0.5.0/docs/specs_and_packs_revisions/notes.docx +0 -0
- forgegraph-0.5.0/src/forgegraph/specifications/layer_error_bar.yaml +0 -11
- {forgegraph-0.5.0 → forgegraph-0.6.1}/.pre-commit-config.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/LICENSE +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/TRADEMARKS.md +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/design/initial-specification.md +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/packs/acme/formats/slide.md +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/packs/acme/graphspecs/polarization-curve.md +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/packs/acme/schedules/workday.md +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/packs/forgegraph_default/catalogs/formats/compact_format.md +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/packs/forgegraph_default/catalogs/formats/wide_format.md +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/packs/forgegraph_default/catalogs/themes/light_theme.md +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/specifications/catalogs/graphtype_xy.md +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/specifications/catalogs/layer_error_bar.md +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/specifications/catalogs/layer_note.md +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/specifications/catalogs/layer_reference.md +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/specifications/layer_note.md +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/docs/specifications/styles.md +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/README.md +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/forgegraph.toml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/lab_throughput.layout.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/lost_runs.parquet +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/lost_runs.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/ops/pack.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/prep_lab_dashboard.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/run_time.parquet +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/run_time.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/runs_gc_ms_01.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/runs_hplc_01.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/runs_hplc_02.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/runs_icp_oes_01.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/samples_weekly.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/throughput_weekly.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/weekly_gc_ms_01.parquet +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/weekly_hplc_01.parquet +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/weekly_hplc_02.parquet +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/weekly_icp_oes_01.parquet +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/weekly_runs.parquet +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/dashboard/weekly_samples.parquet +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/paper/cell_durability.parquet +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/paper/cell_performance.layout.md +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/paper/cell_performance.layout.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/paper/cell_polarization.parquet +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/paper/forgegraph.toml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/paper/hfr_time.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/paper/journal/pack.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/paper/polarization.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/paper/prep_cells.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/layouts/paper/voltage_time.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/packs/acme/formats/slide.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/packs/acme/graphspecs/polarization-curve.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/packs/acme/layouts/dashboard.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/packs/acme/references/house.md +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/packs/acme/schedules/workday.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/README.md +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/air_passengers.csv +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/air_passengers_by_month.csv +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/anscombe.csv +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/assay_summary.csv +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/cell_durability.csv +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/cell_polarization.csv +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/co2_monthly.csv +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/diamonds.csv +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/dose_response.csv +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/economics.csv +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/enzyme_kinetics.csv +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/fmri.csv +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/gapminder.csv +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/geyser.csv +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/growth.csv +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/iris.csv +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/lab_export.xlsx +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/lab_runs.xlsx +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/penguins.csv +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/seaice.csv +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/sensor_log_eu.csv +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/stocks.csv +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/tips.csv +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/examples/sample-data/weather.xlsx +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/scripts/build_example_data.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/scripts/build_recreations.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/scripts/build_sample_data.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/scripts/check_denylist.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/scripts/forgemark.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/scripts/mirror_specs.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/scripts/report_skill_evals.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/scripts/run_skill_evals.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/scripts/smoke_installed.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/scripts/visual_changelog.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/__init__.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/__main__.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/canvas.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/catalog_build/__init__.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/catalog_build/build.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/catalog_build/cards.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/catalog_build/examples.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/catalog_build/files.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/catalog_build/page.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/cli/__init__.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/cli/catalog.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/cli/graphs.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/cli/jobs.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/cli/schema.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/cli/skill.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/data.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawing/__init__.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawing/axes.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawing/colorbar.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawing/dates.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawing/errors.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawing/export.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawing/figure.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawing/key.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawing/labels.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawing/layout.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawing/limits.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawing/lint.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawing/marks.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawing/spacing.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawing/style.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawing/text.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawing/ticks.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/drawn.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/fonts/IBMPlexSerif-Bold.ttf +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/fonts/IBMPlexSerif-Light.ttf +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/fonts/IBMPlexSerif-Medium.ttf +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/fonts/IBMPlexSerif-Regular.ttf +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/fonts/IBMPlexSerif-SemiBold.ttf +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/fonts/OFL-IBMPlexSerif.txt +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/fonts/OFL-Roboto.txt +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/fonts/OFL-SourceSans3.md +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/fonts/Roboto-Bold.ttf +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/fonts/Roboto-Italic.ttf +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/fonts/Roboto-Light.ttf +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/fonts/Roboto-Medium.ttf +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/fonts/Roboto-Regular.ttf +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/fonts/Roboto-SemiBold.ttf +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/fonts/SourceSans3-Bold.ttf +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/fonts/SourceSans3-It.ttf +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/fonts/SourceSans3-Light.ttf +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/fonts/SourceSans3-Medium.ttf +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/fonts/SourceSans3-Regular.ttf +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/fonts/SourceSans3-Semibold.ttf +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/fonts/__init__.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/jobs/__init__.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/jobs/run.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/jobs/status.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/layouts/separators.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/packs/__init__.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/packs/forgegraph_default/catalogs/formats/compact_format.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/packs/forgegraph_default/catalogs/formats/wide_format.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/packs/forgegraph_default/catalogs/themes/light_theme.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/packs/forgegraph_default/graphspecs/dated.parquet +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/packs/forgegraph_default/graphspecs/fanned.parquet +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/packs/forgegraph_default/graphspecs/polarization.parquet +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/packs/forgegraph_default/graphspecs/reinforced.parquet +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/skill/__init__.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/skill/forgegraph/examples/bars.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/skill/forgegraph/examples/figure.layout.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/skill/forgegraph/examples/figure.schedule.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/skill/forgegraph/examples/layers.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/skill/forgegraph/examples/xy.parquet +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/skill/forgegraph/examples/xy.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/skill/forgegraph/references/themes_formats_configs.md +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/spec/__init__.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/spec/access.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/spec/common.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/spec/explain.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/spec/layers.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/spec/layout.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/spec/loaded.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/spec/parameters.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/spec/provenance.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/spec/registry.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/spec/resolve.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/spec/schedule.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/spec/series.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/spec/tags.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/spec/validate.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/specifications/catalogs/graphtype_xy.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/specifications/catalogs/layer_error_bar.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/specifications/catalogs/layer_note.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/specifications/catalogs/layer_reference.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/specifications/config.toml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/specifications/layer_note.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/src/forgegraph/specifications/styles.yaml +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/baselines/crowded_runs.png +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/baselines/flow_sweep_wide.png +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/baselines/fuel_by_size.png +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/baselines/layout_anode_layers.png +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/baselines/layout_screening_and_rate.png +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/baselines/polarization.png +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/baselines/polarization_narrow.png +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/baselines/precipitation_loglog.png +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/baselines/replicates.png +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/baselines/screening_scatter.png +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/baselines/separator_colorbar.png +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/conftest.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/fixtures/catalog_comments.html +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/pack_helpers.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/recreations.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/render_helpers.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/spec_mirror.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_axes.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_blank.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_catalog.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_catalog_files.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_catalog_inputs.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_catalog_page.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_cli.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_dates.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_denylist.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_jobs.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_key_position.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_labels.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_lint.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_marks.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_packaging.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_packs.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_perturbation.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_regression.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_render.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_schedule.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_series.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_smoke.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_spec_comments.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_spec_mirrors.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_specs.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_styles.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_tags.py +0 -0
- {forgegraph-0.5.0 → forgegraph-0.6.1}/tests/test_text.py +0 -0
|
@@ -2,6 +2,35 @@
|
|
|
2
2
|
|
|
3
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
4
|
|
|
5
|
+
## 0.6.1 (2026-10-31)
|
|
6
|
+
|
|
7
|
+
### Changes
|
|
8
|
+
|
|
9
|
+
- **A reference line for each look.** With `per_series`, a reference layer now draws a line for each look its series are drawn in: each color, marker shape, marker fill and line style. Before, groups that differed only in marker shape, fill or line style shared one line. Lines that `split` draws, such as replicates, still share one.
|
|
10
|
+
- **Guides on the documentation site.** The site now has a guide to each part of ForgeGraph, a gallery of worked examples, help with every lint issue and common error, and the release notes.
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
- `per_series` with series colored on a scale is now an error. Before, it drew a single line over all the rows in the palette's first color.
|
|
15
|
+
|
|
16
|
+
## 0.6.0 (2026-10-30)
|
|
17
|
+
|
|
18
|
+
### New Features
|
|
19
|
+
|
|
20
|
+
- **Values on hover.** Pointing at a data point on a graph in an HTML page, or tapping it, shows its values: each axis's value, with its error bar where it has one, and its series' group. A value with an error bar is rounded to the error's last digit, as in 8.0 ± 0.4 (95%CI). It works on markers, lines and bars, and in every tile of a layout.
|
|
21
|
+
- **Feature requests from the agent.** When the agent makes a graph ForgeGraph can't draw, or one that's watermarked, it asks whether you'd like to file a feature request for what was missing.
|
|
22
|
+
- **A documentation site.** Getting started, and a reference for every specification, command and Python function, built from the repository.
|
|
23
|
+
|
|
24
|
+
### Changes
|
|
25
|
+
|
|
26
|
+
- **HTML pages are larger.** Each HTML page now carries its graphs' points and the font their values are written in, about 0.2 MB more for the font.
|
|
27
|
+
- **Issue forms.** Feature requests and bug reports on GitHub now have forms, and the agent writes requests under their headings.
|
|
28
|
+
|
|
29
|
+
### Bug Fixes
|
|
30
|
+
|
|
31
|
+
- A negative error bar distance is now an error when the spec is read, instead of when the graph is drawn.
|
|
32
|
+
- The specifications' descriptions now match what ForgeGraph does.
|
|
33
|
+
|
|
5
34
|
## 0.5.0 (2026-10-29)
|
|
6
35
|
|
|
7
36
|
### New Features
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: If you use ForgeGraph in your work, please cite it as below.
|
|
3
|
+
title: ForgeGraph
|
|
4
|
+
abstract: Fast, consistent, publication-quality graphs from pandas DataFrames, for people and AI agents.
|
|
5
|
+
type: software
|
|
6
|
+
authors:
|
|
7
|
+
- given-names: Steven
|
|
8
|
+
family-names: Kaye
|
|
9
|
+
repository-code: https://github.com/sskaye/forgegraph
|
|
10
|
+
url: https://pypi.org/project/forgegraph/
|
|
11
|
+
license: MIT
|
|
12
|
+
keywords:
|
|
13
|
+
- data visualization
|
|
14
|
+
- plotting
|
|
15
|
+
- matplotlib
|
|
16
|
+
- scientific visualization
|
|
@@ -31,14 +31,26 @@ uv run python scripts/build_recreations.py --baselines
|
|
|
31
31
|
|
|
32
32
|
`uv run python scripts/visual_changelog.py main` then shows every image that changed.
|
|
33
33
|
|
|
34
|
+
## The documentation site
|
|
35
|
+
|
|
36
|
+
The site is built with MkDocs from docs/site. Its Reference pages are generated as it's built: the commands from the CLI, the specifications and the built-in pack from their files, and the Python API from the docstrings (scripts/docs_site.py). The guides' and gallery's graphs are drawn as it's built, from the GraphSpecs, layouts and pack in docs/examples and the layouts in examples/layouts, with data from examples/sample-data, so an example that can't be drawn standard and lint-clean fails the build. To build it and read it while you work:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
uv run --group docs mkdocs serve
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
CI builds it on every pull request, and a broken link fails the build. Publishing a GitHub release deploys it to GitHub Pages.
|
|
43
|
+
|
|
34
44
|
## Requesting a graph type
|
|
35
45
|
|
|
36
46
|
Open a GitHub issue describing the graph you need, ideally with an example image or sketch and a little sample data. No code is needed.
|
|
37
47
|
|
|
38
48
|
## Releasing
|
|
39
49
|
|
|
40
|
-
1. Set the new version in pyproject.toml
|
|
50
|
+
1. Set the new version in pyproject.toml, in the built-in pack's pack.yaml and in the definitions' (src/forgegraph/specifications/pack.yaml), and for a new minor version, their `requires` (`">=0.4, <0.5"` for 0.4).
|
|
41
51
|
2. Rebuild the catalog and mirrors (above).
|
|
42
52
|
3. Write the CHANGELOG entry: the key features a user would want to know were added, changed or removed, and the bugs fixed.
|
|
43
|
-
4.
|
|
44
|
-
5.
|
|
53
|
+
4. Write the visual changelog: `uv run python scripts/visual_changelog.py v<previous version>`, which saves docs/visual-changelog/<version>.html.
|
|
54
|
+
5. Merge with CI green, and tag the merge commit `v<version>`.
|
|
55
|
+
6. Run the Publish workflow, then create the GitHub release with the CHANGELOG entry as its notes.
|
|
56
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: forgegraph
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.1
|
|
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
|
|
@@ -39,8 +39,9 @@ You describe a graph in a **GraphSpec**. ForgeGraph draws it in a house style se
|
|
|
39
39
|
- **Themes:** fixes the look of the graph (fonts, palette, lines, keys, etc.).
|
|
40
40
|
- **Formats:** sets the destination: figure size, base font size, file types and resolution, e.g. a slide, a journal column or a dashboard tile.
|
|
41
41
|
- **GraphTypes:** defines the type of graph and its parameters. E.g. `xy` (lines, markers and bars).
|
|
42
|
-
- **Layers:** features drawn over a graph. E.g. reference lines, notes, & error bars.
|
|
42
|
+
- **Layers:** features drawn over a graph. E.g. reference lines and bands, notes, & error bars.
|
|
43
43
|
- **GraphSpecs:** specify the data, theme, format, and all customizable properties.
|
|
44
|
+
- **Series:** each series in a graph has its own marker, line and bar properties, and can show a summary of its data, such as the mean, a percentile or a confidence interval.
|
|
44
45
|
- **Layouts:** combine multiple graphs into a larger figure or multi-tab dashboard.
|
|
45
46
|
- **Scheduled renders:** re-render graphs and dashboards on a schedule set with cron, launchd, or by an agent.
|
|
46
47
|
- **Provenance:** every saved file contains its specifications as metadata, enabling inspection of how the graph was made and reproduction of it.
|
|
@@ -48,7 +49,7 @@ You describe a graph in a **GraphSpec**. ForgeGraph draws it in a house style se
|
|
|
48
49
|
- **Configs:** control which packs may be used, per user or folder. Used to enforce use of style pack(s) and watermark non-standard graphs.
|
|
49
50
|
- **Lint:** Graphs are linted on rendering, with warnings for issues including overlapping or clipped text, a key covering data, or fonts too small.
|
|
50
51
|
|
|
51
|
-
**Status:** 0.
|
|
52
|
+
**Status:** Until 1.0, a new minor version may change how graphs look or how specs are written; the [changelog](https://github.com/sskaye/forgegraph/blob/main/CHANGELOG.md) lists what changed in each release. `forgegraph catalog` lists the graph types, layers and packs you have. More graph types are planned. If there are specific ones you want, [open an issue](https://github.com/sskaye/forgegraph/issues).
|
|
52
53
|
|
|
53
54
|
## Install
|
|
54
55
|
|
|
@@ -131,7 +132,7 @@ forgegraph render voltage.yaml
|
|
|
131
132
|
|
|
132
133
|
## Packs and configs
|
|
133
134
|
|
|
134
|
-
Every named theme, format, GraphSpec, layout and schedule comes from a **pack**, a folder or pip package of specs, and is named `pack/name`. ForgeGraph's built-in pack, `forgegraph_default`, is stored in src/forgegraph/packs
|
|
135
|
+
Every named theme, format, GraphSpec, layout and schedule comes from a **pack**, a folder or pip package of specs, and is named `pack/name`. ForgeGraph's built-in pack, `forgegraph_default`, is stored in src/forgegraph/packs. It has a theme and formats, so a graph can always be drawn, and specs to extend; `forgegraph catalog` lists them.
|
|
135
136
|
|
|
136
137
|
A **config** lists the packs that may be used:
|
|
137
138
|
|
|
@@ -145,11 +146,13 @@ On macOS, a scheduled render that writes into a cloud-synced folder needs Full D
|
|
|
145
146
|
|
|
146
147
|
## Commands
|
|
147
148
|
|
|
149
|
+
`forgegraph --help` lists every command. The main ones:
|
|
150
|
+
|
|
148
151
|
- `forgegraph render <graphspec or layout>`: draw and save, and report lint.
|
|
149
152
|
- `forgegraph explain <graphspec or layout>`: every value it's drawn with, and which file set it.
|
|
150
153
|
- `forgegraph catalog`: the packs, graph types, layer types, themes, formats, GraphSpecs, layouts, schedules and reference notes available here.
|
|
151
154
|
- `forgegraph schema graphspec --graph-type <type> --theme <theme> --format <format>`: every parameter a GraphSpec can set.
|
|
152
|
-
- `forgegraph schema <kind>`: every parameter of a theme, format, layer, layout, schedule, pack or config.
|
|
155
|
+
- `forgegraph schema <kind>`: every parameter of a theme, format, graph type, layer, layout, schedule, pack or config.
|
|
153
156
|
- `forgegraph pack-notes`: the reference notes of the packs available here.
|
|
154
157
|
- `forgegraph load <file>`: a saved graph's record, and its GraphSpec.
|
|
155
158
|
- `forgegraph job run <schedule>`: render a schedule's GraphSpecs and layouts once.
|
|
@@ -169,13 +172,12 @@ ForgeGraph is [MIT-licensed](https://github.com/sskaye/forgegraph/blob/main/LICE
|
|
|
169
172
|
|
|
170
173
|
## Documentation
|
|
171
174
|
|
|
175
|
+
- [Documentation site](https://sskaye.github.io/forgegraph/): getting started, and a reference for every specification, command and Python function.
|
|
172
176
|
- [Specifications](https://github.com/sskaye/forgegraph/blob/main/docs/specifications): the file that defines each kind of spec, every parameter with its default, range and description.
|
|
173
177
|
- [Built-in pack](https://github.com/sskaye/forgegraph/blob/main/docs/packs/forgegraph_default): forgegraph_default's specs.
|
|
174
178
|
- [Changelog](https://github.com/sskaye/forgegraph/blob/main/CHANGELOG.md).
|
|
175
179
|
- [Contributing](https://github.com/sskaye/forgegraph/blob/main/CONTRIBUTING.md): working on ForgeGraph itself.
|
|
176
|
-
-
|
|
177
|
-
- [Architecture](https://github.com/sskaye/forgegraph/blob/main/docs/design/architecture.md): how ForgeGraph is designed, and why.
|
|
178
|
-
- [To do](https://github.com/sskaye/forgegraph/blob/main/docs/design/todo.md): what's still planned.
|
|
180
|
+
- [Architecture](https://github.com/sskaye/forgegraph/blob/main/docs/design/architecture.md): how ForgeGraph is designed, and why.
|
|
179
181
|
|
|
180
182
|
## Contributing
|
|
181
183
|
|
|
@@ -196,5 +198,5 @@ Feature requests and bug reports go to [GitHub issues](https://github.com/sskaye
|
|
|
196
198
|
- **examples/**: sample data; `packs/acme`, an example pack in a deliberately different style; and `layouts/`, a paper figure and a dashboard.
|
|
197
199
|
- **evals/skill/**: the skill's eval cases, run by `scripts/run_skill_evals.py`.
|
|
198
200
|
- **tests/**, **scripts/**: tests, and build and maintenance scripts.
|
|
199
|
-
- **docs/**: the specifications' and packs' pages, the design record and the visual changelogs. **review/**: each build phase's
|
|
201
|
+
- **docs/**: the specifications' and packs' pages, the design record and the visual changelogs. **review/**: review pages: each build phase's, the audit's and the refactoring's, and the open issues list.
|
|
200
202
|
|
|
@@ -9,8 +9,9 @@ You describe a graph in a **GraphSpec**. ForgeGraph draws it in a house style se
|
|
|
9
9
|
- **Themes:** fixes the look of the graph (fonts, palette, lines, keys, etc.).
|
|
10
10
|
- **Formats:** sets the destination: figure size, base font size, file types and resolution, e.g. a slide, a journal column or a dashboard tile.
|
|
11
11
|
- **GraphTypes:** defines the type of graph and its parameters. E.g. `xy` (lines, markers and bars).
|
|
12
|
-
- **Layers:** features drawn over a graph. E.g. reference lines, notes, & error bars.
|
|
12
|
+
- **Layers:** features drawn over a graph. E.g. reference lines and bands, notes, & error bars.
|
|
13
13
|
- **GraphSpecs:** specify the data, theme, format, and all customizable properties.
|
|
14
|
+
- **Series:** each series in a graph has its own marker, line and bar properties, and can show a summary of its data, such as the mean, a percentile or a confidence interval.
|
|
14
15
|
- **Layouts:** combine multiple graphs into a larger figure or multi-tab dashboard.
|
|
15
16
|
- **Scheduled renders:** re-render graphs and dashboards on a schedule set with cron, launchd, or by an agent.
|
|
16
17
|
- **Provenance:** every saved file contains its specifications as metadata, enabling inspection of how the graph was made and reproduction of it.
|
|
@@ -18,7 +19,7 @@ You describe a graph in a **GraphSpec**. ForgeGraph draws it in a house style se
|
|
|
18
19
|
- **Configs:** control which packs may be used, per user or folder. Used to enforce use of style pack(s) and watermark non-standard graphs.
|
|
19
20
|
- **Lint:** Graphs are linted on rendering, with warnings for issues including overlapping or clipped text, a key covering data, or fonts too small.
|
|
20
21
|
|
|
21
|
-
**Status:** 0.
|
|
22
|
+
**Status:** Until 1.0, a new minor version may change how graphs look or how specs are written; the [changelog](https://github.com/sskaye/forgegraph/blob/main/CHANGELOG.md) lists what changed in each release. `forgegraph catalog` lists the graph types, layers and packs you have. More graph types are planned. If there are specific ones you want, [open an issue](https://github.com/sskaye/forgegraph/issues).
|
|
22
23
|
|
|
23
24
|
## Install
|
|
24
25
|
|
|
@@ -101,7 +102,7 @@ forgegraph render voltage.yaml
|
|
|
101
102
|
|
|
102
103
|
## Packs and configs
|
|
103
104
|
|
|
104
|
-
Every named theme, format, GraphSpec, layout and schedule comes from a **pack**, a folder or pip package of specs, and is named `pack/name`. ForgeGraph's built-in pack, `forgegraph_default`, is stored in src/forgegraph/packs
|
|
105
|
+
Every named theme, format, GraphSpec, layout and schedule comes from a **pack**, a folder or pip package of specs, and is named `pack/name`. ForgeGraph's built-in pack, `forgegraph_default`, is stored in src/forgegraph/packs. It has a theme and formats, so a graph can always be drawn, and specs to extend; `forgegraph catalog` lists them.
|
|
105
106
|
|
|
106
107
|
A **config** lists the packs that may be used:
|
|
107
108
|
|
|
@@ -115,11 +116,13 @@ On macOS, a scheduled render that writes into a cloud-synced folder needs Full D
|
|
|
115
116
|
|
|
116
117
|
## Commands
|
|
117
118
|
|
|
119
|
+
`forgegraph --help` lists every command. The main ones:
|
|
120
|
+
|
|
118
121
|
- `forgegraph render <graphspec or layout>`: draw and save, and report lint.
|
|
119
122
|
- `forgegraph explain <graphspec or layout>`: every value it's drawn with, and which file set it.
|
|
120
123
|
- `forgegraph catalog`: the packs, graph types, layer types, themes, formats, GraphSpecs, layouts, schedules and reference notes available here.
|
|
121
124
|
- `forgegraph schema graphspec --graph-type <type> --theme <theme> --format <format>`: every parameter a GraphSpec can set.
|
|
122
|
-
- `forgegraph schema <kind>`: every parameter of a theme, format, layer, layout, schedule, pack or config.
|
|
125
|
+
- `forgegraph schema <kind>`: every parameter of a theme, format, graph type, layer, layout, schedule, pack or config.
|
|
123
126
|
- `forgegraph pack-notes`: the reference notes of the packs available here.
|
|
124
127
|
- `forgegraph load <file>`: a saved graph's record, and its GraphSpec.
|
|
125
128
|
- `forgegraph job run <schedule>`: render a schedule's GraphSpecs and layouts once.
|
|
@@ -139,13 +142,12 @@ ForgeGraph is [MIT-licensed](https://github.com/sskaye/forgegraph/blob/main/LICE
|
|
|
139
142
|
|
|
140
143
|
## Documentation
|
|
141
144
|
|
|
145
|
+
- [Documentation site](https://sskaye.github.io/forgegraph/): getting started, and a reference for every specification, command and Python function.
|
|
142
146
|
- [Specifications](https://github.com/sskaye/forgegraph/blob/main/docs/specifications): the file that defines each kind of spec, every parameter with its default, range and description.
|
|
143
147
|
- [Built-in pack](https://github.com/sskaye/forgegraph/blob/main/docs/packs/forgegraph_default): forgegraph_default's specs.
|
|
144
148
|
- [Changelog](https://github.com/sskaye/forgegraph/blob/main/CHANGELOG.md).
|
|
145
149
|
- [Contributing](https://github.com/sskaye/forgegraph/blob/main/CONTRIBUTING.md): working on ForgeGraph itself.
|
|
146
|
-
-
|
|
147
|
-
- [Architecture](https://github.com/sskaye/forgegraph/blob/main/docs/design/architecture.md): how ForgeGraph is designed, and why.
|
|
148
|
-
- [To do](https://github.com/sskaye/forgegraph/blob/main/docs/design/todo.md): what's still planned.
|
|
150
|
+
- [Architecture](https://github.com/sskaye/forgegraph/blob/main/docs/design/architecture.md): how ForgeGraph is designed, and why.
|
|
149
151
|
|
|
150
152
|
## Contributing
|
|
151
153
|
|
|
@@ -166,5 +168,5 @@ Feature requests and bug reports go to [GitHub issues](https://github.com/sskaye
|
|
|
166
168
|
- **examples/**: sample data; `packs/acme`, an example pack in a deliberately different style; and `layouts/`, a paper figure and a dashboard.
|
|
167
169
|
- **evals/skill/**: the skill's eval cases, run by `scripts/run_skill_evals.py`.
|
|
168
170
|
- **tests/**, **scripts/**: tests, and build and maintenance scripts.
|
|
169
|
-
- **docs/**: the specifications' and packs' pages, the design record and the visual changelogs. **review/**: each build phase's
|
|
171
|
+
- **docs/**: the specifications' and packs' pages, the design record and the visual changelogs. **review/**: review pages: each build phase's, the audit's and the refactoring's, and the open issues list.
|
|
170
172
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
Security fixes go into the latest minor release. Upgrade to it to get them.
|
|
6
|
+
|
|
7
|
+
## Reporting a vulnerability
|
|
8
|
+
|
|
9
|
+
Please report a vulnerability privately, through the repository's [Security tab](https://github.com/sskaye/forgegraph/security/advisories/new), not in a public issue. Say what's affected, how to reproduce it, and what it lets someone do. You'll get a reply there, and the fix is released with an advisory once it's ready.
|
|
@@ -46,7 +46,6 @@ Also:
|
|
|
46
46
|
| Studio (D14) | Tableau-like drag and drop plus a house-style editor; built after everything else works |
|
|
47
47
|
| Stability (D15) | Versioned specs, library-level semver, visual changelog, pinning via lockfiles |
|
|
48
48
|
| Licence and name (§9) | MIT; the name kept by Steven Kaye via `TRADEMARKS.md` |
|
|
49
|
-
| What's planned | [To do](todo.md) |
|
|
50
49
|
|
|
51
50
|
---
|
|
52
51
|
|
|
@@ -57,7 +56,7 @@ Also:
|
|
|
57
56
|
- **Fixed style**: house style, set by the theme and format (by the org, journal or user), never by an individual graph. Each value is a **parameter**, e.g. `plot_area.border.sides` or `series.colors`; a theme may also leave some parameters variable, for graphs to change.
|
|
58
57
|
- **Options**: what an individual graph may vary. Each is documented with a default, except the data itself (the data file and the required mappings such as `x` and `y`), which every graph must specify.
|
|
59
58
|
- **Escape hatch**: raw matplotlib on a themed figure, always marked non-standard.
|
|
60
|
-
3. **One source per fact; everything else is generated.** Each kind of document is defined by its definition file (§8): every parameter with its default, range and description. The code reads them; `forgegraph schema`, the catalog pages and the docs/
|
|
59
|
+
3. **One source per fact; everything else is generated.** Each kind of document is defined by its definition file (§8): every parameter with its default, range and description. The code reads them; `forgegraph schema`, the catalog pages and the docs/specifications mirrors are generated from them, and the skill reads them at run time. Nothing generated is edited by hand.
|
|
61
60
|
4. **Renderer code holds no style constants.** Every visual value is read from the resolved spec. Editing a default in YAML changes the output without editing code, and tests check that each value actually changes something (§7).
|
|
62
61
|
5. **Extensible without forking.** Anything an organisation would want to customise can be supplied by a pack.
|
|
63
62
|
6. **Reproducible by default.** A rendered file carries its own spec and provenance. A saved spec plus a data reference re-renders the same graph tomorrow, or with tomorrow's data.
|
|
@@ -83,6 +82,7 @@ Also:
|
|
|
83
82
|
|
|
84
83
|
## 3. Architecture
|
|
85
84
|
|
|
85
|
+
|
|
86
86
|
```
|
|
87
87
|
Interfaces Python API · CLI (forgegraph) · Agent Skill · Studio desktop app (later) · OS scheduler
|
|
88
88
|
│ all build / read
|
|
@@ -97,12 +97,13 @@ Also:
|
|
|
97
97
|
Registry graph types · layer types · packs (themes · formats · GraphSpecs · layouts · schedules · notes)
|
|
98
98
|
▲
|
|
99
99
|
├── which packs may be used: the main config, then the folder config
|
|
100
|
-
└── values: graph type
|
|
100
|
+
└── values: graph type · theme · format, each through its extends chain ◄ the GraphSpec and those it extends
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
-
**Resolve** merges, in order: the graph type's defaults, then the theme, the format, any spec named in `extends:`, and finally the spec itself. Each field has a defined merge rule (replace by default; `layers` merge by name, so a GraphSpec adds layers or changes an inherited one by naming it), set in the GraphSpec specification. Resolve records where each value came from. `forgegraph explain my.yaml` prints the resolved spec with an origin for every value (like `git config --show-origin`). When someone asks "why is my legend frameless?", the answer is one command away.
|
|
104
103
|
|
|
105
|
-
**
|
|
104
|
+
**Resolve** finds each value of the graph type, the theme and the format through that file's own extends chain, then replaces the variable ones with what the GraphSpec sets, itself or through the GraphSpecs it extends. The nearest file that sets a parameter decides it, except that `series` and `layers` merge by name, so a GraphSpec adds a series or layer, or changes an inherited one, by naming it. Resolve records where each value came from. `forgegraph explain my.yaml` prints the resolved spec with an origin for every value (like `git config --show-origin`). When someone asks "why is my legend frameless?", the answer is one command away.
|
|
105
|
+
|
|
106
|
+
**Read** takes the DataFrame passed in, or reads the Parquet file the GraphSpec's `data.file` names. **Validate** checks the frame against the graph type's columns and fails with messages an agent can act on ("series.voltage.column: no column 'Voltage' in the data; columns are …"). **Lint** inspects the rendered figure as part of every render (D9). **Export** writes the files and embeds provenance.
|
|
106
107
|
|
|
107
108
|
### 3.1 What it looks like to use
|
|
108
109
|
|
|
@@ -199,7 +200,7 @@ Interactivity is not part of the MVP. If it becomes a requirement later, the opt
|
|
|
199
200
|
|
|
200
201
|
Every size in a theme is a multiple of the format's base font ("tick labels = 0.9 × base"), so a theme renders correctly in any format. That matters because graphs go to many destinations, not mainly slides (so no `.pptx` export: the examples were collected on slides only for convenience).
|
|
201
202
|
|
|
202
|
-
**The default theme** (forgegraph_default/light_theme): Roboto, semibold titles, a full box, outward ticks, the Okabe–Ito palette with a grey benchmark, a frameless key, direct labels where they fit. It fixes the font, the palette and its lists, the ticks' direction and algorithms, the
|
|
203
|
+
**The default theme** (forgegraph_default/light_theme): Roboto, semibold titles, a full box, outward ticks, the Okabe–Ito palette with a grey benchmark, a frameless key, direct labels where they fit. It fixes the font, the palette and its lists, the ticks' direction, sides and algorithms, the minor ticks and the grid. The `acme` example pack has a *deliberately different* house style (IBM Plex Serif, two sides of the box, a grid, Paul Tol's muted palette), so it shows that a pack can change the look completely.
|
|
203
204
|
|
|
204
205
|
**Fonts and PDFs:**
|
|
205
206
|
|
|
@@ -207,7 +208,7 @@ Every size in a theme is a multiple of the format's base font ("tick labels = 0.
|
|
|
207
208
|
- Maths in labels uses the theme font, upright rather than italic.
|
|
208
209
|
- PDF and PostScript output embed TrueType (Type 42) fonts rather than matplotlib's default Type 3, which many journal submission checks reject.
|
|
209
210
|
|
|
210
|
-
*The
|
|
211
|
+
*The definition files are the specification (§8).*
|
|
211
212
|
|
|
212
213
|
### D4. Packs: extension without forking
|
|
213
214
|
|
|
@@ -234,14 +235,14 @@ acme-forgegraph/
|
|
|
234
235
|
- **Distribution:** a local folder, or a pip package that declares a `forgegraph.packs` entry point. Pip already handles git URLs (`pip install git+https://…`) and versions, so ForgeGraph has no fetching or caching of its own.
|
|
235
236
|
- **Pinning:** pip packs are pinned like any dependency, in the project's lockfile; folder packs by git commit. Scheduled dashboards therefore don't change looks by surprise.
|
|
236
237
|
- **Skill integration:** `forgegraph catalog` lists the allowed packs' specs, `forgegraph pack-notes` prints their reference notes, each under its pack, and requests about a pack's specs go to the address in its pack.yaml's `issues`.
|
|
237
|
-
- **Data only, never executed.** Packs hold YAML, Markdown and fonts, never Python. Graph types and layers live only in the core library. An org that needs a new graph type requests it upstream (D10); an org-specific variant of an existing type is a GraphSpec to extend.
|
|
238
|
+
- **Data only, never executed.** Packs hold YAML, Markdown, Parquet data and fonts, never Python. Graph types and layers live only in the core library. An org that needs a new graph type requests it upstream (D10); an org-specific variant of an existing type is a GraphSpec to extend.
|
|
238
239
|
- **Loading:** ForgeGraph finds a pip pack's files through package metadata without importing it, and loads YAML with a safe loader.
|
|
239
240
|
- **Reference notes:** the skill treats them as reference data, not as instructions.
|
|
240
241
|
- **Trust:** installing a pip package is still a trust decision, as for any package.
|
|
241
242
|
|
|
242
243
|
### D5. Graph types and layer types: one definition file each
|
|
243
244
|
|
|
244
|
-
A graph type is defined by its own file in `src/forgegraph/specifications/`, starting with `graphtype_xy.yaml`, and drawn by drawing/xy.py; a layer type by its file there (`layer_<type>.yaml`), drawn by drawing/layers.py. A category's brackets say what the code needs to know 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. Descriptions are for people, and no code reads them. Its parameters are what a GraphSpec chooses for one graph: the data column for each axis, the
|
|
245
|
+
A graph type is defined by its own file in `src/forgegraph/specifications/`, starting with `graphtype_xy.yaml`, and drawn by drawing/xy.py; a layer type by its file there (`layer_<type>.yaml`), drawn by drawing/layers.py. A category's brackets say what the code needs to know 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. Descriptions are for people, and no code reads them. Its parameters are what a GraphSpec chooses for one graph: the data column for each axis, the arrangement, the series and their marks, the benchmark and the legend. Things drawn on top of the graph, including those computed from its data (error bars, a reference line at the mean, a fitted curve), are layers. A parameter defined as None with no None in its range is required: a GraphSpec must set it. Style values never live in a graph type: the renderer reads them from the theme, so the theme stays the single place where style values live. The graph code receives a DataFrame; loading and preparing data happens before it.
|
|
245
246
|
|
|
246
247
|
**Series labels come from a set of labelling methods.** Each method either places every label cleanly or reports that it can't. The theme lists the methods it allows, in order of preference, and a graph's `legend.method: auto` uses the first that fits. A method chosen by name that doesn't fit, or `auto` with nothing that fits, is an error that says what to change: ForgeGraph never draws labels over data as a fallback. The first methods are:
|
|
247
248
|
|
|
@@ -257,8 +258,8 @@ Placement is deterministic. A direct label sits inside the plot, beside its own
|
|
|
257
258
|
|
|
258
259
|
- `forgegraph catalog build` generates one **HTML catalog** per pack, with a tab per kind (themes, formats, graph types, layers). Each spec shows its generated table, its baseline, and one image per variable parameter, with that parameter changed. A rule chooses each image's value and the baseline it's drawn on, not a hand-picked list, so the catalog files hold only generic baselines. Every parameter gets such a card, fixed ones included, and the perturbation tests draw them all; a format's output and provenance parameters, which change only the saved files, and a graph type's layer.types, which only permits layers, get none. Forgemark supports per-tab comments in HTML.
|
|
259
260
|
- **Comments survive rebuilds.**
|
|
260
|
-
- The build gives every section, table row and figure a stable `id` (`#xy-param-
|
|
261
|
-
- It then re-attaches each comment by its recorded anchor text, using Forgemark's own `reattach`. Only comments whose text actually changed stay orphaned.
|
|
261
|
+
- The build gives every section, table row and figure a stable `id` (`#graphtype-xy-param-legend-method`, `#graphtype-xy-baseline-legend-method`), and carries the trailing `forgemark-comments` block across verbatim.
|
|
262
|
+
- It then re-attaches each comment by its element's id or its recorded anchor text, using Forgemark's own `reattach`. Only comments whose text actually changed stay orphaned.
|
|
262
263
|
- `forgemark lint` reports any orphans.
|
|
263
264
|
- **Size and freshness.**
|
|
264
265
|
- Catalog images are embedded at screen resolution, which keeps the file small enough to commit.
|
|
@@ -285,7 +286,7 @@ The GraphSpec's `data` category:
|
|
|
285
286
|
- **`data.file`**: the Parquet file: an absolute path, a path starting with `~`, or a path relative to the folder of the GraphSpec file that sets it. None: the DataFrame is passed through the Python API. A DataFrame passed in is drawn whatever `data.file` says.
|
|
286
287
|
- **`data.preprocessing`**: optional; where the code that made the data is (a path, a URL, a note). ForgeGraph records it in provenance and never reads it.
|
|
287
288
|
|
|
288
|
-
Drawing reads the DataFrame and checks it against the GraphSpec: the named columns exist, axis
|
|
289
|
+
Drawing reads the DataFrame and checks it against the GraphSpec: the named columns exist, each axis column suits its axis's data.type (numbers, dates or categories), a log axis has no values of 0 or below. A missing data file, a file that isn't Parquet, or a GraphSpec with no data file and no DataFrame is an error naming the parameter.
|
|
289
290
|
|
|
290
291
|
### D8. Text: lightweight markup for scientific labels
|
|
291
292
|
|
|
@@ -313,11 +314,12 @@ When a request isn't in the catalog, the agent says so plainly, then:
|
|
|
313
314
|
|
|
314
315
|
1. **Compose first** when the request is a variation of something supported: the closest graph type + layers.
|
|
315
316
|
2. **Draw it anyway** when nothing is close, e.g. a violin plot when only `xy` exists. The *agent* writes raw matplotlib on a themed canvas (`fg.blank(theme, format)`), so the result is at least on-style. The requester never writes code.
|
|
316
|
-
3. **Watermark it.** Every non-standard output gets a prominent diagonal, semi-transparent "NON-STANDARD" watermark across the plot area, plus `nonstandard: true` in provenance, and a warning to the user. The watermark is **always on
|
|
317
|
-
4. **Offer a request.** With the user's OK, the agent files a feature request or bug report as a GitHub issue, as the skill's reference file says. Graph types live only in the core (D4), so
|
|
317
|
+
3. **Watermark it.** Every non-standard output gets a prominent diagonal, semi-transparent "NON-STANDARD" watermark across the plot area, plus `nonstandard: true` in provenance, and a warning to the user. The watermark is **always on**. Its look is the theme's: the default theme leaves it variable, so a GraphSpec can restyle it, even out of sight, and an organisation that wants it to show fixes it in its own theme. This also covers changes made after rendering: whatever draws or saves the figure, ForgeGraph or matplotlib directly, first checks that it still has the artists it had when rendering finished, drawing the same pixels. If anything was drawn or changed through `.axes` or `.figure`, the output is marked non-standard, because its embedded spec no longer reproduces it, and it stays non-standard.
|
|
318
|
+
4. **Offer a request.** With the user's OK, the agent files a feature request or bug report as a GitHub issue, as the skill's reference file says. Graph types live only in the core (D4), so a request for one goes upstream, where the maintainer handles it; a request about a pack's specs goes to the address under `issues` in its pack.yaml.
|
|
318
319
|
|
|
319
320
|
### D11. Layouts: multi-panel figures and dashboards are one feature
|
|
320
321
|
|
|
322
|
+
|
|
321
323
|
```yaml
|
|
322
324
|
name: lab_throughput
|
|
323
325
|
version: 0
|
|
@@ -328,7 +330,7 @@ parameters:
|
|
|
328
330
|
format: ops/dashboard
|
|
329
331
|
separators:
|
|
330
332
|
line:
|
|
331
|
-
|
|
333
|
+
style: solid
|
|
332
334
|
tabs:
|
|
333
335
|
Overview:
|
|
334
336
|
grid:
|
|
@@ -351,16 +353,17 @@ parameters:
|
|
|
351
353
|
icp_oes_01: runs_icp_oes_01.yaml
|
|
352
354
|
```
|
|
353
355
|
|
|
354
|
-
|
|
356
|
+
|
|
357
|
+
**A layout arranges GraphSpecs.** A layout file (`*.layout.yaml`, docs/specifications/layout_example.md) holds only what's particular to several graphs together: the arrangement, what separates the graphs, and the look of the tab bar on its HTML page. Text, colors, fonts and the look of each graph stay in each tile's GraphSpec and its theme, so a tile drawn alone and in a layout is the same graph. A layout has no per-tile settings, and its only text is its tab names.
|
|
355
358
|
|
|
356
359
|
- **Format, no theme.** The layout's format (by default the default pack's wide_format: the compact format at 300 mm) sets the size and output of the whole figure, and replaces each tile's own format, so every tile is drawn at one base size. The layout can set the format's variable parameters, such as its file types. Each tile keeps its own theme.
|
|
357
360
|
- **The grid.** grid.areas draws the grid as rows of tile names, as CSS `grid-template-areas`, matplotlib's `subplot_mosaic` and patchwork do: a name repeated over a rectangle of cells spans it, and `.` is a blank cell. column_widths and row_heights are ratios of plot boxes.
|
|
358
361
|
- **Aligned plot boxes.** A tab's tiles are laid out together, as patchwork, egg and cowplot do: each column's and row's margins are the most room any of its tiles needs around its plot box, measured to the letters, so plot boxes line up across tiles whatever their tick labels. A spanning tile's plot box runs across the margins and gaps inside it. With the format's height auto, each row is as tall as its tiles' aspects need. Tiles are never shrunk to fit; a figure too small for them is an error naming them.
|
|
359
|
-
- **Separators.** The
|
|
360
|
-
- **Tabs.** Each tab is a figure of its own, at the format's size. html saves one page with a
|
|
362
|
+
- **Separators.** The gaps between and around the tiles, in the first tile's theme's background color; optionally a line along the middle of each gap between two tiles, and around the tiles (separators.border); and the corner radius of each tile's background.
|
|
363
|
+
- **Tabs.** A tab holds a grid and its tiles, or tabs of its own, to any depth. Each tab with a grid is a figure of its own, at the format's size. html saves one page with a row of tabs for each level, which works where the page's scripts don't run; where they run, each tab also has its own link (#by-instrument, or a nested tab's path, #platinum/voltage). pdf saves a page per tab; other file types, a file per tab (lab_throughput-overview.png, or lab_throughput-platinum-voltage.png for a nested tab). A layout's tabs replace those it inherits.
|
|
361
364
|
- **Data:** each tile draws from its own data.file.
|
|
362
365
|
- **HTML** is each figure as SVG, its text as outlines, in a minimal page; a figure shrinks to fit a narrow window without reflowing. A GraphSpec can be saved as a page too. Scheduled runs add the "data as of" stamp and the stale-banner script (D12).
|
|
363
|
-
- **Rendering:** each tab renders as one matplotlib figure, each tile drawn into its own Axes in its own theme's settings.
|
|
366
|
+
- **Rendering:** each tab with a grid renders as one matplotlib figure, each tile drawn into its own Axes in its own theme's settings.
|
|
364
367
|
- **What's given up:** pages don't reflow on phones. Panel labels, shared axes and one legend for several tiles aren't drawn yet.
|
|
365
368
|
|
|
366
369
|
Paper figures and ops dashboards differ only in their format and defaults; examples/layouts has one of each.
|
|
@@ -372,7 +375,7 @@ A scheduled job is a **schedule file** (`*.schedule.yaml`, docs/specifications/s
|
|
|
372
375
|
- **Cadence.** `interval.count` counts `interval.unit` (minute, hour, day, week, month or year), and `at` says when in each unit, one time or a list: ":30", "06:00", "mon 06:00", "1 06:00", "Jan 1 06:00". `interval.count` divides 60, 24 and 12 for minutes, hours and months, and is 1 otherwise, so every form maps exactly onto every scheduler. Times are the machine's local time.
|
|
373
376
|
- **`forgegraph job run lab.schedule.yaml`** runs a schedule once. Whatever the environment offers schedules that command: the machine's scheduler (launchd, systemd, cron, Task Scheduler), a GitHub Actions workflow, a Claude scheduled task. ForgeGraph writes no scheduler entries itself, so every user's path is the same.
|
|
374
377
|
- **Each run** takes a lock, renders every target (a failing one is recorded and the others still render), writes the status file, and runs `on_failure` or `on_success` without a shell.
|
|
375
|
-
- **Run status.** Every run writes `<name>.status.json`
|
|
378
|
+
- **Run status.** Every run writes its status file whole (by default `<name>.status.json` in the output folder, or beside the schedule file when it has none): last attempt, last success, consecutive failures, and for each target its outputs, rows, data files and their times, lint count and errors. `job run` also writes a log, rotated so it can't grow without limit.
|
|
376
379
|
- **Freshness without a monitor.** Every output shows a "data as of" footnote **in the image itself** (the oldest data file's modification time, and the cadence in words), drawn in the theme's footnote look. It's the data's time, not the clock's, so the same data gives the same image.
|
|
377
380
|
- **HTML pages** show a stale banner on their own when the data is older than `freshness.stale_after_hours` (by default, the longest gap between runs plus 25 %), so a job that silently stopped can't look current. This works where the page's script runs (a local browser or a web host). File-sharing previews often don't run scripts, so the in-image footnote is what always shows.
|
|
378
381
|
- **The skill** has the agent write the schedule file and schedule `forgegraph job run <schedule>` by the method of its choice.
|
|
@@ -416,7 +419,7 @@ Every output embeds a provenance record, at its format's provenance.level (full,
|
|
|
416
419
|
|
|
417
420
|
**Build:** a local web app (`forgegraph studio`: a Python server running the library, plus a frontend with drag-and-drop), packaged as a desktop app. The UI framework is chosen in its UI plan, trying a Python UI framework (e.g. NiceGUI) first before committing to a separate TypeScript frontend. It's still a GUI over specs: schema-described options are what make it feasible.
|
|
418
421
|
|
|
419
|
-
**When:** after everything else works
|
|
422
|
+
**When:** after everything else works.
|
|
420
423
|
|
|
421
424
|
- **Ready for it from the start:** options are described by the schema.
|
|
422
425
|
- **Built with Studio:** the shelf-mapping code and its round-trip tests.
|
|
@@ -426,8 +429,8 @@ Every output embeds a provenance record, at its format's provenance.level (full,
|
|
|
426
429
|
### D15. Stability for a public library
|
|
427
430
|
|
|
428
431
|
- **Specs are versioned.** Every spec file has a `version`, and provenance records it, with a pack spec's pack version. `forgegraph migrate` is written when a spec change needs it.
|
|
429
|
-
- **One version number: the library's**, using semver. The built-in
|
|
430
|
-
- **Major:** a breaking change to a spec format.
|
|
432
|
+
- **One version number: the library's**, using semver. The built-in pack is released with it and carries its version; other packs have their own, like any package.
|
|
433
|
+
- **Major:** a breaking change to a spec format. Before 1.0, a minor release may also change how specs are written.
|
|
431
434
|
- **Minor:** releases may change how graphs look.
|
|
432
435
|
- **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
436
|
- **Pinning:** projects and scheduled renders pin versions through their lockfile, so a dashboard can't change appearance overnight.
|
|
@@ -451,14 +454,14 @@ Every output embeds a provenance record, at its format's provenance.level (full,
|
|
|
451
454
|
| color by a numeric column, with a color bar | built |
|
|
452
455
|
| wide data (several y columns) | built |
|
|
453
456
|
| bars: grouped, stacked, horizontal, with value labels | built |
|
|
454
|
-
| reference lines, notes and error bars | built |
|
|
457
|
+
| reference lines and bands (target regions, fill between), notes and error bars | built |
|
|
455
458
|
| multi-panel figures and dashboards (layouts) | built |
|
|
456
|
-
|
|
|
459
|
+
| stacked decomposition | on request |
|
|
457
460
|
| annotations: arrows, braces, highlights, leader callouts | on request |
|
|
458
461
|
| the distribution graph type (box plus strip, categorical x) | planned |
|
|
459
|
-
| iso-curves (x·y = c)
|
|
462
|
+
| iso-curves (x·y = c) | planned |
|
|
460
463
|
|
|
461
|
-
**Annotations** aren't
|
|
464
|
+
**Annotations** aren't built yet; they're built when someone asks for them. Producing them deterministically at good quality is hard, so the problem is split in two. The *drawing* is deterministic: `arrow`, `brace`, `ellipse`, `text` and `delta` would be layers anchored in data coordinates, stored in the spec, and re-render identically. The *judgement* — what to call out and where to put it — is the agent's, checked by lint for overlaps. That makes annotations reproducible and reviewable while still using the agent for what it's good at.
|
|
462
465
|
|
|
463
466
|
---
|
|
464
467
|
|
|
@@ -493,7 +496,7 @@ Repo `sskaye/forgegraph`; distribution, import and CLI `forgegraph`. The README'
|
|
|
493
496
|
|
|
494
497
|
## 8. Specifications
|
|
495
498
|
|
|
496
|
-
Each kind of document is specified by its definition file: every parameter with its default, range and description. The code reads it, and tests check the code against it, including the edges of every range. docs/specifications/ mirrors each file for review, and docs/packs/
|
|
499
|
+
Each kind of document is specified by its definition file: every parameter with its default, range and description. The code reads it, and tests check the code against it, including the edges of every range. docs/specifications/ mirrors each file for review, and docs/packs/ the built-in pack's and the acme example pack's.
|
|
497
500
|
|
|
498
501
|
| Specification | Definition file |
|
|
499
502
|
| --- | --- |
|
|
@@ -532,7 +535,7 @@ Each kind of document is specified by its definition file: every parameter with
|
|
|
532
535
|
| House-style changes silently alter scheduled outputs | Version pinning through lockfiles; visual changelog |
|
|
533
536
|
| Someone else ships a product called ForgeGraph | `TRADEMARKS.md`; held namespaces; optional registration |
|
|
534
537
|
| A pack is malicious or compromised | Packs are data only and never imported; YAML loaded safely; reference notes treated as data by the skill |
|
|
535
|
-
| Real data leaks into public requests | The
|
|
538
|
+
| Real data leaks into public requests | The agent files a request only when the user asks for one |
|
|
536
539
|
| Studio and the agent drift into incompatible specs | Options described by the schema; round-trip tests when Studio is built |
|
|
537
540
|
| Internal details leak in published figures | `minimal` provenance leaves out the data file and preprocessing reference |
|
|
538
541
|
| Confidential material enters the repo | Example PDFs gitignored; synthetic data rescaled; a deny-list of internal terms checked on every commit |
|
|
@@ -548,6 +551,7 @@ Each kind of document is specified by its definition file: every parameter with
|
|
|
548
551
|
|
|
549
552
|
- interactive backend and live-filter dashboards (post-MVP)
|
|
550
553
|
- responsive HTML dashboards (D11); `forgegraph migrate` (until a spec change needs it)
|
|
554
|
+
- a fit layer
|
|
551
555
|
- in layouts: panel labels, axes shared between tiles, and one legend for several tiles (D11)
|
|
552
556
|
- stat or KPI tiles for ops dashboards
|
|
553
557
|
- MCP server, for agents that can't run `forgegraph` themselves (e.g. cloud chat apps)
|
|
@@ -570,11 +574,11 @@ Decisions made while building whose reasons aren't given above.
|
|
|
570
574
|
- **Text is measured to its letters.** matplotlib spaces text by a box sized for the font's tallest letters and deepest descenders, so its gaps aren't the gaps you see. Following CSS's `text-box: trim-both cap alphabetic`, Figma's vertical trim and D3's axis offsets, a text's edges run from its capital height to its baseline, growing only where its letters reach further. ForgeGraph lays the figure out itself, with no matplotlib layout engine.
|
|
571
575
|
- **No font hinting.** Text has the same width at every resolution, so what's laid out is what's saved.
|
|
572
576
|
- **Roboto is the base font,** chosen from a comparison of 13 open fonts.
|
|
573
|
-
- **Ticks come from published algorithms** (drawing/ticks.py), which a theme chooses: the
|
|
577
|
+
- **Ticks come from published algorithms** (drawing/ticks.py), which a theme chooses: the default theme's are extended Wilkinson for linear axes and R's scales::breaks_log for log axes. A rule of ForgeGraph's own let labels run together on short axes. On every axis the labels must fit: the algorithm is asked for one tick fewer while they would overlap.
|
|
574
578
|
- **The layout always fits its tick labels.** Tick labels can keep a layout from settling: a taller plot box gets more, wider labels, which make it shorter again. After 6 unsettled passes it lays out once more with, on each side, the most room either of the last two passes needed, so whichever labels the final draw has fit.
|
|
575
579
|
- **Date axes use one calendar unit per axis.** matplotlib's automatic date ticks mix units (the 1st and 15th, labelled "Aug" and "15") and name the shared context in a corner. As in d3 and Vega-Lite, ticks are a calendar unit's boundaries, never finer than the data's spacing, with the next unit up on a second line where it changes, as Plotly and Datawrapper do. Weekly ticks fall on Mondays even when the data starts on another day, which reads more naturally than counting from the data's first day. ForgeGraph writes %-d, %-m and %-H itself, since strftime's differ by platform.
|
|
576
580
|
- **The default palette is Okabe–Ito.** With 4 or more groups, color_blind's colors ran together in the skill evals: two blues and two greys. Okabe and Ito's palette keeps its first four colors distinct for color-blind readers.
|
|
577
|
-
- **The default theme leaves marker sizes and opacity to the GraphSpec.** Agents in the skill evals needed smaller markers for dense data.
|
|
581
|
+
- **The default theme leaves marker sizes and opacity to the GraphSpec.** Agents in the skill evals needed smaller markers for dense data. A key's samples are drawn at their series' marker size.
|
|
578
582
|
|
|
579
583
|
### Names
|
|
580
584
|
|
|
@@ -588,7 +592,7 @@ Decisions made while building whose reasons aren't given above.
|
|
|
588
592
|
- **The grid is a list of tile names per row,** written one row under the other so it reads as a picture of the figure, as CSS grids, matplotlib's subplot_mosaic and patchwork do. A list of tiles with spans can't express a tile spanning rows.
|
|
589
593
|
- **A layout's tabs replace those it inherits,** rather than merging by name as a GraphSpec's layers do: a set of tabs is one design.
|
|
590
594
|
- **Spacing between tiles is the layout's,** not the theme's: it exists only in layouts, and a layout has no theme. A pack sets its house spacing in a base layout that its others extend.
|
|
591
|
-
- **The layout's format is twice the
|
|
595
|
+
- **The layout's format is twice the compact format's width,** so two columns are each as wide as one compact graph at the same text size. At the compact width, two columns were too small for their labels in the skill evals.
|
|
592
596
|
- **A layout can set its format's variable parameters,** as a GraphSpec can, so a dashboard can be saved as HTML without a format of its own.
|
|
593
597
|
- **html is a file type for any graph,** not only dashboards: a graph linked from a wiki, or a scheduled graph whose page shows the stale banner. Figures are SVG with their text as outlines, so pages look the same in every browser.
|
|
594
598
|
- **Example formats stay examples.** Journal and dashboard sizes differ by journal and screen, and organizations keep their formats in packs.
|
|
@@ -604,9 +608,9 @@ Decisions made while building whose reasons aren't given above.
|
|
|
604
608
|
### Packs and configs
|
|
605
609
|
|
|
606
610
|
- **Packs are the only source of named specs,** so the specs a person or organization requires are kept in one place, and a folder can't pick up stray themes and formats beside a GraphSpec.
|
|
607
|
-
- **A pack only a folder config lists is non-standard, not an error,** so someone who can't change their organization's main config can still make and try a pack, while a pack used by accident is labelled, not silent. A folder config can't
|
|
611
|
+
- **A pack only a folder config lists is non-standard, not an error,** so someone who can't change their organization's main config can still make and try a pack, while a pack used by accident is labelled, not silent. A folder config can't give a pack's name to another folder when the main config or a built-in pack already has it, which would let a folder swap an organization's pack for its own copy without being marked non-standard.
|
|
608
612
|
- **The main config is ~/.config/forgegraph/config.toml on every platform,** Windows included: one path is the easiest to document. `FORGEGRAPH_CONFIG` names another file, which keeps evals and tests away from the machine's own and lets an organization point at a managed one.
|
|
609
|
-
- **The main config is written on first use,** listing the built-in
|
|
613
|
+
- **The main config is written on first use,** listing the built-in pack, since pip can't run code when it installs a package. It's never overwritten.
|
|
610
614
|
- **forgegraph.toml is a folder config,** and the CLI's option is `--folder`: calling it a project's would assume how the folder is used.
|
|
611
615
|
- **The record names each pack spec's pack version,** so a file shows which release of a house style drew it.
|
|
612
616
|
- **`schema graphspec` shows any spec ForgeGraph can find,** while `catalog` and `pack-notes` show only the allowed packs, so an agent can see how a spec it builds on is written.
|
|
@@ -614,8 +618,9 @@ Decisions made while building whose reasons aren't given above.
|
|
|
614
618
|
|
|
615
619
|
### Requests and the repository
|
|
616
620
|
|
|
617
|
-
- **The
|
|
621
|
+
- **The MVP is small.** Features come from specific requests, not from guessing what users will want; annotations are built when someone asks for them.
|
|
618
622
|
- **Requests go through the skill,** which points to references/requests.md: agents already know how to write a feature request or bug report, so there's no issue form.
|
|
619
623
|
- **The public repository starts from one commit.** The earlier history held a confidential term, so it's kept privately.
|
|
620
624
|
- **Forgemark's tool is pinned,** downloaded once and checked by SHA-256. Problems found in Forgemark are reported to its maintainer, not worked around.
|
|
621
625
|
- **Review pages and catalog pages are exempt from the large-file check,** since they embed their images, and the deny-list check skips base64 image data, which can hold a short deny-listed term by chance.
|
|
626
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: acme_polarization
|
|
2
|
+
version: 0
|
|
3
|
+
description: The polarization curves in the example acme pack's theme and slide format.
|
|
4
|
+
extends: None
|
|
5
|
+
parameters: !var
|
|
6
|
+
specifications:
|
|
7
|
+
graph_type: xy
|
|
8
|
+
theme: acme/acme
|
|
9
|
+
format: acme/slide
|
|
10
|
+
text:
|
|
11
|
+
title: Polarization curves
|
|
12
|
+
data:
|
|
13
|
+
file: cell_polarization.parquet
|
|
14
|
+
preprocessing: examples/sample-data/cell_polarization.csv, unchanged
|
|
15
|
+
axis:
|
|
16
|
+
x:
|
|
17
|
+
data:
|
|
18
|
+
column: current_density_a_cm2
|
|
19
|
+
title: Current density (A/cm^2)
|
|
20
|
+
y:
|
|
21
|
+
title: Voltage (V)
|
|
22
|
+
series:
|
|
23
|
+
voltage_v:
|
|
24
|
+
color: cell
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: assay_summary
|
|
2
|
+
version: 0
|
|
3
|
+
description: Each compound's mean response at each dose, with error bars from the standard deviations in the data.
|
|
4
|
+
extends: None
|
|
5
|
+
parameters: !var
|
|
6
|
+
specifications:
|
|
7
|
+
graph_type: xy
|
|
8
|
+
theme: forgegraph_default/light_theme
|
|
9
|
+
format: forgegraph_default/compact_format
|
|
10
|
+
text:
|
|
11
|
+
title: Dose response
|
|
12
|
+
data:
|
|
13
|
+
file: assay_summary.parquet
|
|
14
|
+
preprocessing: examples/sample-data/assay_summary.csv, unchanged
|
|
15
|
+
axis:
|
|
16
|
+
x:
|
|
17
|
+
data:
|
|
18
|
+
column: dose_uM
|
|
19
|
+
type: categorical
|
|
20
|
+
title: Dose (µM)
|
|
21
|
+
y:
|
|
22
|
+
title: Response (%)
|
|
23
|
+
series:
|
|
24
|
+
mean_response_pct:
|
|
25
|
+
color: compound
|
|
26
|
+
layers:
|
|
27
|
+
spread:
|
|
28
|
+
type: error_bar
|
|
29
|
+
value:
|
|
30
|
+
lower: sd_pct
|
|
31
|
+
upper: sd_pct
|
|
32
|
+
aggregation:
|
|
33
|
+
function: None
|