Skip to content

refactor: extract graph layout from svg renderer - #26

Merged
astravys merged 2 commits into
devfrom
feat/extract-graph-layout
Jun 13, 2026
Merged

astravys merged 2 commits into
devfrom
feat/extract-graph-layout

Conversation

@astravys

@astravys astravys commented Jun 13, 2026 •

Copy link
Copy Markdown
Owner

Summary

Extract reusable Graph family node measurement and deterministic placement from the Database Schema SVG renderer. The existing renderer now converts the validated database model through databaseToGraphModel(), calls layoutGraph(), and draws from the shared layout result.

Closes #8

Architecture

Before:

ValidatedDatabaseModel → Database-specific renderer layout → SVG

After:

ValidatedDatabaseModel → databaseToGraphModel() → layoutGraph() → existing SVG drawing/routing → SVG

Files and packages changed

  • Added Graph layout contracts and implementation in packages/family-graph/src/layout.ts.
  • Exported the layout API from @orbit/family-graph.
  • Migrated @orbit/renderer-svg to depend on @orbit/family-graph and @orbit/validator.
  • Replaced renderer-owned box measurement and placement with Graph layout consumption.
  • Added focused Graph layout tests and expanded renderer regression assertions.
  • Added the intentional review artifact at artifacts/review/issue-8-customer-database.svg.

Layout contracts and algorithm

Added:

  • GraphLayout
  • GraphLayoutBounds
  • GraphLayoutNode
  • GraphLayoutRow
  • GraphLayoutPort
  • GraphLayoutOptions
  • layoutGraph(model, options)

The algorithm is deterministic and stateless. It measures node width from titles, compartment titles, rows, and badges with a 300px compatibility minimum; calculates header/documentation/row heights; places primary nodes in a configurable fixed-column grid; optionally places secondary node kinds in a stable secondary column; exposes absolute row and port locations; and calculates total bounds.

The Database renderer identifies database.enum as a secondary node kind to preserve the existing table-grid/enum placement.

Renderer migration

renderDatabaseSvg(model) remains the public API. It now:

  1. Calls databaseToGraphModel(model).
  2. Calls layoutGraph(graph, { secondaryNodeKinds: ["database.enum"] }).
  3. Draws boxes and rows from GraphLayoutNode and GraphLayoutRow coordinates.
  4. Uses layout node/row locations for the existing orthogonal relationship paths.

Relationship routing itself remains renderer-local and unchanged in scope.

Removed obsolete code

Removed the renderer-private Box/BoxRow compatibility model, layout(), tableBox(), enumBox(), table/enum placement loops, duplicate width/height calculations, and duplicate layout constants. No second Database Schema layout path remains.

Explicit non-goals

Validation

Passed:

  • corepack pnpm install
  • corepack pnpm build
  • corepack pnpm typecheck
  • corepack pnpm test (7 files, 16 tests)
  • corepack pnpm lint
  • corepack pnpm check
  • corepack pnpm orbit validate docs/examples/customer-database.orbit
  • corepack pnpm orbit parse docs/examples/customer-database.orbit --json
  • corepack pnpm orbit render docs/examples/customer-database.orbit --output artifacts/review/issue-8-customer-database.svg

corepack pnpm format:check still reports pre-existing formatting issues in docs/architecture.md and docs/implementation-families.md; this PR does not modify those files.

Review artifact

artifacts/review/issue-8-customer-database.svg

The committed review SVG was generated from the actual branch implementation through the CLI render path. It is valid, non-empty, and 3,280 bytes.

Visual comparison

Compared against a fresh render from dev before the extraction:

  • Old dimensions: 780×434
  • New dimensions: 780×434
  • Node order/IDs: unchanged (users, orders, order_status)
  • Relationship endpoints and path coordinates: unchanged
  • Serialized SVG: byte-for-byte identical for the canonical example
  • Intentional visual differences: none

Copilot review

Copilot raised one valid edge case: secondary-only graphs with document documentation did not apply the document header offset. Commit 244c531 applies the offset, adds a regression test, reruns all validation, and regenerates the unchanged review artifact. The thread was replied to and resolved; no unresolved conversations remain.

Dead/obsolete code audit

Reviewed the complete diff and searched for the old layout helper names and geometry constants. No obsolete renderer layout implementation, unused imports, temporary debug code, or accidental generated output remains. The only committed generated file is the intentional review SVG.

Copilot AI review requested due to automatic review settings June 13, 2026 10:06

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extracts deterministic node measurement and placement from the database SVG renderer into a reusable @orbit/family-graph layout module, then updates the renderer to consume that shared layout output while keeping relationship routing renderer-local (per issue #8’s scope).

Changes:

  • Added layoutGraph() and associated GraphLayout* contracts in @orbit/family-graph, with focused layout tests.
  • Migrated @orbit/renderer-svg to build a GraphModel via databaseToGraphModel() and render using layoutGraph() results.
  • Expanded renderer regression assertions and committed a review SVG artifact.

Reviewed changes

Copilot reviewed 7 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/renderer.test.ts Updates renderer regression fixture and adds assertions for emitted IDs and XML header.
tests/graph-layout.test.ts Adds deterministic/non-overlapping layout tests and minimal-bounds coverage for layoutGraph().
pnpm-lock.yaml Adds workspace deps needed by @orbit/renderer-svg.
packages/renderer-svg/tsconfig.json Adds project references to family-graph and validator.
packages/renderer-svg/src/renderer.ts Replaces renderer-owned layout with databaseToGraphModel() + layoutGraph() consumption.
packages/renderer-svg/package.json Adds dependencies on @orbit/family-graph and @orbit/validator.
packages/family-graph/src/layout.ts Introduces shared graph layout contracts + deterministic placement implementation.
packages/family-graph/src/index.ts Exports the new layout API from @orbit/family-graph.
artifacts/review/issue-8-customer-database.svg Adds the intentional review SVG artifact generated by the CLI render path.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/family-graph/src/layout.ts
@astravys
astravys merged commit ce344e9 into dev Jun 13, 2026
2 checks passed
@astravys
astravys deleted the feat/extract-graph-layout branch June 13, 2026 11:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants