Version history of dash-flows, rendered from CHANGELOG.md.

Changelog

Version history of dash-flows. The timeline on this page is rendered from CHANGELOG.md, reproduced below.


All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

Changed

12+13). lib/analytics_tracker.py now delegates bot classification to the package's classify() instead of its own year-old User-Agent list (which filed ClaudeBot — Anthropic's training crawler — under "search"). Reporting consequence: human_hits DROPS and bot_hits RISES on the day this deploys, because UA-less and library clients (httpx, Go-http-client, node-fetch, an empty UA) move from human to crawler. That is the number becoming true, not a regression — the hub's day-over-day view will show the step.

AnalyticsTracker.record_read): one row per corpus document the package serves (tier, verdict, bytes, verified vendor). lib/ traffic_rollup.py's rollup gains an additive vendors[] block on any day with reads, and a new /admin/traffic page shows this host its own vendor × day / vendor → tier ledger, gated exactly like the control board.

.github/workflows/cd.yml's deploy job as a fast-forward push after the CI matrix is green (sync item 13). A push to main is a candidate, not a deploy; verify now runs only when deploy succeeds and re-checks /healthz build == $GITHUB_SHA itself before smoke testing. See DIVERGENCES.md's posture fence (deploy: release-branch).

default** (sync item 15): run.py's RobotsConfig(block_ai_training= False, …) — since sync item 12 every corpus read is a ledger row this app keeps, so a training-crawler fetch is now recorded and priceable instead of walled outright. robots.txt carries no Disallow stanza for them at all; the package's bot middleware stops 403ing / and /healthz for them too. allow_ai_search / allow_traditional are unchanged.

navigation contract (sync item 16): the sidebar's sections now come from each docs page's frontmatter (category: + order:) against a fork-owned CATEGORY_ORDER, never a hand-edited page_order list; the top bar gains an Other Apps hover menu (the network's primary applications, from lib/network_directory.py) and a version badge; the sidebar's Resources section is third-party only (dmc + React Flow); a new /changelog page renders this file as a timeline; a new /api page documents every dash_flows component's props, generated from the installed package's metadata.json; /admin/traffic gains a dmc.DatePickerInput day picker (replacing a bare dcc.Dropdown) and a People** section separating the v3 human numbers from the crawler ledger below it; pages/home.py renders through markdown2dash instead of dcc.Markdown. Admin pages (/admin/control-board, /admin/traffic) are hidden from the sidebar and search unless the viewer is the owner. See DIVERGENCES.md divergence 11 for the two lines this fork kept (the wordmark, the SPA page-view beacon).

(sync item 17): scripts/network_smoke.py's UA now leads with a Chrome/AppleWebKit token before the internal token — at dash-improve-my-llms ≥ 2.8 a UA with no browser engine token is crawler-lane, so the old bare-internal-token default made every default-UA check (the manifest link, the social card) read the prerendered crawler document instead of the browser one.

frontmatter gains a nav: short-sidebar-label field (separate from name:, which stays the <title>/og:title/llms.txt heading) and sidebar links show a lock icon on auth/admin-tier pages; /api's sitemap lastmod now reads the committed dash_flows/api_metadata.json extract's generated stamp (built by python scripts/build_api_metadata.py — re-run it when a component's props change) instead of always being absent; /changelog parses every heading shape the fleet writes (hyphen, en dash, em dash, bare or bracketed version, prose-first releases with no ### sections) and gains a sitemap lastmod of its own; the AppShell gains a keyboard skip-link to #main-content; lib/constants.py gains LOGO_ICON/WORDMARK_COLOR/WORDMARK_VISIBLE_FROM, lifting the header's identity out of components/header.py.

Fixed

reached the browser only** (sync item 18 contract highlight 7-amended, muicharts' finding): a markdown2dash directive renders Dash components, so its table exists only in the React tree — the machine lane (/api-reference/llms.txt, the crawler document) and the non-JS prerender are built from the markdown source, where the directive line is stripped. Measured before the fix: zero markdown table rows anywhere outside a JavaScript browser, across all five .. kwargs:: directives on that page. pages/markdown.py now expands .. kwargs:: fence-aware into a real markdown table, the same treatment it already gave .. source::, through a new lib.directives.kwargs.props_markdown() — one parse (props_for()) for both the directive's own table and the expansion, so the two lanes cannot drift apart again.

section at the foot of this CHANGELOG parsed as a release (the 1.6.41 heading match accepts unbracketed ## label — date, and free text came through with it), rendering an extra Timeline card with no version number. pages/changelog.py now requires an UNBRACKETED ## heading to look like a release — a version, an ISO date, or Unreleased — before treating it as one; bracketed headings (## [x.y.z]) stay trusted as intent.

is now resolved per CALL SITE instead of per file — the file-scoped substring check could pass a file whose headers= sat on an unrelated code path while its actual .test_client() calls stayed bare. tests/test_excluded_links_hidden.py's admin-leak sweep now covers /llms-small.txt and /llms-full.txt, not only /llms.txt.

batteries as crawler traffic** (sync 1.6.43 item 1): record_read — the on_document_read hook the 2.8.0 floor added — never learned the internal-traffic contract track_visit has held since it existed. A request carrying lib.constants.INTERNAL_UA_TOKEN now drops before any field is read, keyed on the event's ua field (not user_agent, which EVENT_FIELDS does not have).

(sync item 10): this host served dash where the fleet key set names dash_version, and no backend at all. Every value on the wire was correct, but the hub's hourly sweep, the F4 battery, cd.yml's build-match wait and scripts/network_smoke.py all read the payload BY KEY NAME, so a renamed key is indistinguishable from a missing one. dash_version and backend are now served BESIDE dash (additive — an extra costs nothing, a substitute costs a red cell). DIVERGENCES.md §3 corrected in the same change: it had recorded this key set as a deliberate divergence, which is what kept several rounds of syncs from fixing it — the file layout is the real divergence, the key set never was.

[1.3.0] - 2026-08-01

Changed — flows.2plot.dev joins the 2plot network standard

The docs site now matches the pattern proven on 2plot.ai, 2plot.dev, boilerplate.2plot.dev, leaflet.2plot.dev and email.2plot.dev. What that means in practice:

SITE_BRAND = "dash-flows — React Flow node graphs for Dash" (package name leads — the library rule; "Pip Install Python" is the byline, never the name), and it now reaches the <title>, the /llms.txt H1, the llms viewer's brand chip, the home prose, the README and every share-card headline. tests/test_site_identity.py pins all of them, because every one of these falls back to a framework default silently.

below the floor) and replaces lib/seo.py entirely: /robots.txt, /sitemap.xml, /llms.txt, /<page>/llms.txt, the crawler prerender, canonical/og:url and the cross-host ## Network directory (lib/network_directory.py) all come from the package. The one thing lib/seo.py did that the package does not — re-pointing canonical/og:url on client-side navigation — moved into templates/index.html as the network-standard History-API script.

into the standard lib/analytics_tracker.py (which drops internal-network and /healthz traffic at WRITE time — the internal-traffic contract's inbound half), lib/traffic_rollup.py, lib/satellite_reporter.py (flock lease so one worker reports per interval) and lib/bulletin.py. This app's own SPA page-view beacon — the reason its session numbers are honest when every other Dash site reports single-page visits — survives the split as lib/pageview_beacon.py. The outbound half of the contract is new here: ad_client and the traffic reporter now send the 2plot-internal UA, so this site's readers stop being counted as bot traffic on the hub.

package name — across AD_APP_ID, SATELLITE_APP_KEY and the bulletin.

image_url= before, so Dash emitted og:image="" on every page — an empty tag that unfurls worse than none, because scrapers treat it as the declared image and render a blank card. All pages now point at cdn.2plot.ai/github_assets/flows.2plot.dev.png (1200x630, rendered by scripts/make_social_card.py), the template declares only the auxiliary tags Dash omits, and scripts/smoke_live.py reads the CDN object's IHDR bytes after every deploy.

smuggling). markdown2dash 0.1.2 pins gunicorn<22, so it now installs with --no-deps everywhere (Dockerfile, CI, requirements-docs.txt lists its real dependencies) and CI asserts the gunicorn version inside the built image.

Added — CI/CD, from nothing

This repo had no .github/workflows at all. It now has the boilerplate's proven shape: ci.yml (actionlint first — an invalid workflow file is the one defect CI structurally cannot report; flake8; the secretless pytest suite — zero secrets in CI on purpose, so fail-closed postures are provable; the real Docker image built, version-fingerprinted inside, booted and probed by scripts/network_smoke.py — the same battery CD runs against production; an examples matrix across Dash 4.2.0/4.4.1 and Python 3.10–3.13; a wheel build proving the dash>=4.1.0 floor in a clean venv with nothing but Dash) and cd.yml (owns main, requires CI, then 120s settle + five consecutive healthy probes — Render swaps instances, so a single 200 can come from the dying instance — then the battery plus scripts/smoke_live.py against https://flows.2plot.dev, where peer checks warn and own-host checks fail).

Changed — the wheel states its real floor

setup.py now declares install_requires=['dash>=4.1.0'] (the first release with the multi-backend constructor this component targets). CI installs the wheel with exactly dash==4.1.0 and nothing else, and asserts top_level.txt == ["dash_flows"] so no docs-site directory can ever leak into the package.

Changed

React 18, so no component-side React migration was required. Multi-backend (Flask/FastAPI/Quart) and websocket callbacks are available to consuming apps.

(React Flow requires ^4.4.0).

Added

components/, lib/) seeded from the Dash Documentation Boilerplate. Each component is documented in docs/<topic>/<topic>.md with live .. exec:: demos, .. source:: code, and .. kwargs:: prop tables generated from the existing examples/.

an embeddable twin (docs/<topic>/exNN.py, ids namespaced exNN-) rendered live via .. exec::, plus the full source and a "How it works" breakdown on its category page. 23_callback_stress_test is intentionally source-only (performance harness).

smoke_runtime.py (WSGI runtime gate: /, /_dash-layout, /_dash-dependencies), and validate_docs.py (docs render gate). Results matrix in SMOKE_RESULTS.md (35/35 import + runtime). Browser-fidelity test in tests/test_examples_render.py.

Removed

Removed R/, man/, src/jl/, src/DashFlows.jl, NAMESPACE, DESCRIPTION, Project.toml, .Rbuildignore, and the duplicate JS bundle copies under deps/ and inst/. build:backends no longer generates R/Julia (--r-prefix/--jl-prefix removed).

review_checklist.md, REACT_FLOW_COMPARISON.md, and the tracked .idea/ IDE config.

python run.py), the old JS dev demo (src/demo/, root index.html, webpack.serve.config.js, the npm start script, webpack-dev-server), the stale boilerplate tests tests/test_usage.py/tests/test_handle.py (replaced by tests/test_examples_render.py), unreferenced assets/logo.svg/assets/group-node.png, the unused webpack require in webpack.config.js, and the deprecated babel-eslint parser (now @babel/eslint-parser; run npm install once to refresh the lockfile).

Fixed

setProps effects in DashFlows.react.js now guard on a serialized diff (mirroring the nodeConnections effect), so React Flow's asynchronous node re-measurement can no longer drive a setPropssetNodes loop.

ResizableNode now renders a default target (top) + source (bottom) handle pair when data.handles is omitted or empty (matching DefaultNode), so edges bind to it instead of failing. The data.handles prop is now optional.

colorMode-based background override so the MiniMap follows the --df-minimap-* CSS variables, which track both React Flow's .dark class and Mantine's data-mantine-color-scheme (correct even for colorMode="system").

colors on cards/panels/buttons with Mantine CSS variables; converted native html.Buttons to dmc.Button (ex15/ex18/ex19); synced the ex20/ex21 canvases' colorMode to the page theme via color-scheme-storage; and themed the ex14 metric-card node text so it stays legible on dark canvases.

Roadmap (carried over from REACT_FLOW_COMPARISON.md)

(useSmartHandles, useFlowState, useConnectionValidation, useViewportActions, useImageExport, useCopyPaste, useElkLayout).

layoutOptions use.

[1.2.0] - 2026-04-03

Added

React Flow 12.10.1 Pass-Through Props

Floating Edges

Helper Lines (Alignment Guides)

Add Node on Edge Drop

Node Connections Tracking

Undo/Redo System

Computing Flows (Graph Traversal)

Sub-flows (Collapsible Group Nodes)

ViewportPortal (Floating Annotations)

Animated Layout Transitions

Accessibility (ARIA Support)

Resize Constraints

Glass Connection Line

Type-Colored MiniMap

EdgeToolbar on ButtonEdge

Smart Handle Positioning

Delete Elements Action

New Examples

New Python Wrapper Components (auto-generated)

Changed

Fixed


[1.1.0] - 2025-12-15

Added

Custom Icons with DashIconify

Node Layout System

Enhanced Node Data Props

New Example

Changed

Fixed

New Examples

Changed

Fixed

Technical


[1.0.0] - 2024-12-10

Added

New Node Types

New Edge Types

New UI Components

Theming System

Status Indicators

Handle Configuration

Examples

Changed

Fixed

Technical

[0.0.4] - 2024-11-XX

Added

Changed

[0.0.3] - 2024-XX-XX

Added


Migration Guide

From 0.0.x to 1.0.0

  1. Update import statements: Component names remain the same, but ensure you're importing from dash_flows:
   from dash_flows import DashFlows, DefaultNode, InputNode, OutputNode
  1. Node status indicators: The status indicator CSS classes have been updated. If you were using custom CSS targeting the old classes, update to:
  1. Theming: The new theming system uses CSS custom properties. Apply theme classes to your container:
   DashFlows(
       id='flow',
       className='df-theme-glass df-scheme-ocean',
       ...
   )
  1. Handle configuration: Handles are now configured via the node's data.handles array instead of separate props.

Note for AI agents: This is the static, prerendered view of an interactive Dash application served because we detected a non-JS user agent. Full prose docs: