Connection lost. Reconnecting… attempt 1 of 8
Paused. Your work is held on the server.
Could not reconnect.
This session has expired on the server.
An unhandled error has occurred.
Sedna.UI
v0.31.0 · main

Graph plugins

tier 3 — a surface

What a graph does beyond drawing and reading: a minimap for a large one, links the reader draws, groups that fold and groups that are outlined, the drawing as a file, and two more layouts. Each loads the first time a graph on the page uses it, from inside the package, so a page that uses none downloads none. How a reader moves through a graph is on Exploring a graph.

The minimap

Live — zoom in on one domain, then press or drag in the minimap. <canvas class="graph-minimap" data-graph-minimap aria-hidden="true">, written inside the graph, draws the whole drawing in miniature and frames the part in view. Pressing it moves the view there, dragging pans, and the wheel zooms about the point under the pointer. It is Sedna.UI's own, in the canvas's colours, and while the view pans only the frame is drawn again.

Write one when a graph is larger than its frame, so that zooming in on a part does not lose the whole. It is hidden on a phone, where the frame is the whole screen, and from assistive technology: the keyboard already pans and zooms, with Shift + arrows, + − and 0 to fit.

Storefront Ordering Payments Fulfilment Customers Platform
  • Storefront
  • Product pages
  • Search pages
  • Account pages
  • Basket view
  • Checkout pages
  • Content blocks
  • Image resizer
  • Page cache
  • Consent banner
  • Store locator
  • Wish lists
  • Ordering
  • Checkout API
  • Basket service
  • Pricing
  • Vouchers
  • Tax rules
  • Orders API
  • Order history
  • Invoices
  • Returns
  • Order events
  • Order database
  • Payments
  • Payments API
  • Card gateway
  • Wallets
  • Refunds
  • Fraud screening
  • Payouts
  • Ledger
  • Reconciliation
  • Chargebacks
  • Payment events
  • Ledger database
  • Fulfilment
  • Warehouse sync
  • Pick lists
  • Packing app
  • Shipping API
  • Carrier bridge
  • Tracking
  • Label printing
  • Stock levels
  • Replenishment
  • Returns desk
  • Stock database
  • Customers
  • Accounts API
  • Sign-in
  • Profiles
  • Addresses
  • Preferences
  • Loyalty points
  • Newsletter
  • Reviews
  • Support tickets
  • Chat assistant
  • Customer database
  • Platform
  • API gateway
  • Service mesh
  • Message bus
  • Feature flags
  • Secrets store
  • Metrics
  • Log store
  • Tracing
  • Build pipeline
  • Deploy service
  • Status page
  • Storefront runs Product pages
  • Storefront runs Search pages
  • Storefront runs Account pages
  • Storefront runs Basket view
  • Storefront runs Checkout pages
  • Storefront runs Content blocks
  • Storefront runs Image resizer
  • Storefront runs Page cache
  • Storefront runs Consent banner
  • Storefront runs Store locator
  • Storefront runs Wish lists
  • Ordering runs Checkout API
  • Ordering runs Basket service
  • Ordering runs Pricing
  • Ordering runs Vouchers
  • Ordering runs Tax rules
  • Ordering runs Orders API
  • Ordering runs Order history
  • Ordering runs Invoices
  • Ordering runs Returns
  • Ordering runs Order events
  • Ordering runs Order database
  • Payments runs Payments API
  • Payments runs Card gateway
  • Payments runs Wallets
  • Payments runs Refunds
  • Payments runs Fraud screening
  • Payments runs Payouts
  • Payments runs Ledger
  • Payments runs Reconciliation
  • Payments runs Chargebacks
  • Payments runs Payment events
  • Payments runs Ledger database
  • Fulfilment runs Warehouse sync
  • Fulfilment runs Pick lists
  • Fulfilment runs Packing app
  • Fulfilment runs Shipping API
  • Fulfilment runs Carrier bridge
  • Fulfilment runs Tracking
  • Fulfilment runs Label printing
  • Fulfilment runs Stock levels
  • Fulfilment runs Replenishment
  • Fulfilment runs Returns desk
  • Fulfilment runs Stock database
  • Customers runs Accounts API
  • Customers runs Sign-in
  • Customers runs Profiles
  • Customers runs Addresses
  • Customers runs Preferences
  • Customers runs Loyalty points
  • Customers runs Newsletter
  • Customers runs Reviews
  • Customers runs Support tickets
  • Customers runs Chat assistant
  • Customers runs Customer database
  • Platform runs API gateway
  • Platform runs Service mesh
  • Platform runs Message bus
  • Platform runs Feature flags
  • Platform runs Secrets store
  • Platform runs Metrics
  • Platform runs Log store
  • Platform runs Tracing
  • Platform runs Build pipeline
  • Platform runs Deploy service
  • Platform runs Status page
  • Product pages shows Pricing
  • Search pages checks Stock levels
  • Basket view calls Basket service
  • Checkout pages calls Checkout API
  • Account pages calls Accounts API
  • Account pages shows Order history
  • Checkout API reads Basket service
  • Checkout API charges Payments API
  • Checkout API places Orders API
  • Checkout API redeems Vouchers
  • Pricing applies Tax rules
  • Orders API writes Order database
  • Orders API publishes Order events
  • Order events feeds Warehouse sync
  • Order events feeds Invoices
  • Order events feeds Newsletter
  • Returns asks for Refunds
  • Returns books Returns desk
  • Payments API authorises Card gateway
  • Payments API screens Fraud screening
  • Payments API charges Wallets
  • Payments API publishes Payment events
  • Payment events feeds Ledger
  • Ledger writes Ledger database
  • Reconciliation reads Ledger
  • Chargebacks raises Refunds
  • Payouts reads Ledger
  • Warehouse sync prints Pick lists
  • Pick lists feeds Packing app
  • Packing app prints Label printing
  • Shipping API books Carrier bridge
  • Carrier bridge feeds Tracking
  • Stock levels reads Stock database
  • Replenishment reads Stock levels
  • Packing app calls Shipping API
  • Sign-in checks Accounts API
  • Profiles writes Customer database
  • Addresses writes Customer database
  • Loyalty points listens to Order events
  • Reviews shown on Product pages
  • Support tickets reads Order history
  • Chat assistant opens Support tickets
  • Preferences controls Newsletter
  • API gateway routes Checkout API
  • API gateway routes Accounts API
  • API gateway routes Payments API
  • Service mesh carries API gateway
  • Message bus carries Order events
  • Message bus carries Payment events
  • Feature flags configures Checkout pages
  • Secrets store holds keys for Card gateway
  • Metrics watches API gateway
  • Tracing reads Service mesh
  • Metrics writes Log store
  • Build pipeline hands over to Deploy service
  • Status page reads Metrics
  • Page cache caches Product pages
  • Image resizer serves Content blocks
  • Consent banner asks for Newsletter
  • Store locator checks Stock levels
  • Wish lists writes Customer database
<div class="graph-frame" data-graph-frame>
    <div class="toolbar">
        <div class="graph-legend">
            <span class="graph-key"><span class="graph-swatch series-2"></span> <span>Storefront</span></span>
            <span class="graph-key"><span class="graph-swatch series-3"></span> <span>Ordering</span></span>
            <span class="graph-key"><span class="graph-swatch series-1"></span> <span>Payments</span></span>
            <span class="graph-key"><span class="graph-swatch series-5"></span> <span>Fulfilment</span></span>
            <span class="graph-key"><span class="graph-swatch series-6"></span> <span>Customers</span></span>
            <span class="graph-key"><span class="graph-swatch series-4"></span> <span>Platform</span></span>
        </div>
        <span class="toolbar-count" data-graph-stats="{nodes} services · {edges} links"></span>
    </div>
    <div class="card">
        <div class="graph graph--lg" data-graph id="pl-estate" data-graph-layout="islands" data-graph-arrows="target"
             aria-label="Every service of the web shop, in its domain">
            <ul class="graph-data" data-graph-data>
                <li data-node="storefront" data-group="storefront" data-tone="2" data-icon="ri-store-2-line" data-meta="Domain">Storefront</li>
                <li data-node="product-pages" data-group="storefront" data-tone="2">Product pages</li>
                <li data-node="search-pages" data-group="storefront" data-tone="2">Search pages</li>
                <li data-node="account-pages" data-group="storefront" data-tone="2">Account pages</li>
                <li data-node="basket-view" data-group="storefront" data-tone="2">Basket view</li>
                <li data-node="checkout-pages" data-group="storefront" data-tone="2">Checkout pages</li>
                <li data-node="content-blocks" data-group="storefront" data-tone="2">Content blocks</li>
                <li data-node="image-resizer" data-group="storefront" data-tone="2">Image resizer</li>
                <li data-node="page-cache" data-group="storefront" data-tone="2">Page cache</li>
                <li data-node="consent-banner" data-group="storefront" data-tone="2">Consent banner</li>
                <li data-node="store-locator" data-group="storefront" data-tone="2">Store locator</li>
                <li data-node="wish-lists" data-group="storefront" data-tone="2">Wish lists</li>
                <li data-node="ordering" data-group="ordering" data-tone="3" data-icon="ri-shopping-cart-2-line" data-meta="Domain">Ordering</li>
                <li data-node="checkout-api" data-group="ordering" data-tone="3">Checkout API</li>
                <li data-node="basket-service" data-group="ordering" data-tone="3">Basket service</li>
                <li data-node="pricing" data-group="ordering" data-tone="3">Pricing</li>
                <li data-node="vouchers" data-group="ordering" data-tone="3">Vouchers</li>
                <li data-node="tax-rules" data-group="ordering" data-tone="3">Tax rules</li>
                <li data-node="orders-api" data-group="ordering" data-tone="3">Orders API</li>
                <li data-node="order-history" data-group="ordering" data-tone="3">Order history</li>
                <li data-node="invoices" data-group="ordering" data-tone="3">Invoices</li>
                <li data-node="returns" data-group="ordering" data-tone="3">Returns</li>
                <li data-node="order-events" data-group="ordering" data-tone="3" data-shape="diamond">Order events</li>
                <li data-node="order-database" data-group="ordering" data-tone="3" data-shape="square">Order database</li>
                <li data-node="payments" data-group="payments" data-tone="1" data-icon="ri-bank-card-line" data-meta="Domain">Payments</li>
                <li data-node="payments-api" data-group="payments" data-tone="1">Payments API</li>
                <li data-node="card-gateway" data-group="payments" data-tone="1">Card gateway</li>
                <li data-node="wallets" data-group="payments" data-tone="1">Wallets</li>
                <li data-node="refunds" data-group="payments" data-tone="1">Refunds</li>
                <li data-node="fraud-screening" data-group="payments" data-tone="1">Fraud screening</li>
                <li data-node="payouts" data-group="payments" data-tone="1">Payouts</li>
                <li data-node="ledger" data-group="payments" data-tone="1">Ledger</li>
                <li data-node="reconciliation" data-group="payments" data-tone="1">Reconciliation</li>
                <li data-node="chargebacks" data-group="payments" data-tone="1">Chargebacks</li>
                <li data-node="payment-events" data-group="payments" data-tone="1" data-shape="diamond">Payment events</li>
                <li data-node="ledger-database" data-group="payments" data-tone="1" data-shape="square">Ledger database</li>
                <li data-node="fulfilment" data-group="fulfilment" data-tone="5" data-icon="ri-truck-line" data-meta="Domain">Fulfilment</li>
                <li data-node="warehouse-sync" data-group="fulfilment" data-tone="5">Warehouse sync</li>
                <li data-node="pick-lists" data-group="fulfilment" data-tone="5">Pick lists</li>
                <li data-node="packing-app" data-group="fulfilment" data-tone="5">Packing app</li>
                <li data-node="shipping-api" data-group="fulfilment" data-tone="5">Shipping API</li>
                <li data-node="carrier-bridge" data-group="fulfilment" data-tone="5">Carrier bridge</li>
                <li data-node="tracking" data-group="fulfilment" data-tone="5">Tracking</li>
                <li data-node="label-printing" data-group="fulfilment" data-tone="5">Label printing</li>
                <li data-node="stock-levels" data-group="fulfilment" data-tone="5">Stock levels</li>
                <li data-node="replenishment" data-group="fulfilment" data-tone="5">Replenishment</li>
                <li data-node="returns-desk" data-group="fulfilment" data-tone="5">Returns desk</li>
                <li data-node="stock-database" data-group="fulfilment" data-tone="5" data-shape="square">Stock database</li>
                <li data-node="customers" data-group="customers" data-tone="6" data-icon="ri-user-heart-line" data-meta="Domain">Customers</li>
                <li data-node="accounts-api" data-group="customers" data-tone="6">Accounts API</li>
                <li data-node="sign-in" data-group="customers" data-tone="6">Sign-in</li>
                <li data-node="profiles" data-group="customers" data-tone="6">Profiles</li>
                <li data-node="addresses" data-group="customers" data-tone="6">Addresses</li>
                <li data-node="preferences" data-group="customers" data-tone="6">Preferences</li>
                <li data-node="loyalty-points" data-group="customers" data-tone="6">Loyalty points</li>
                <li data-node="newsletter" data-group="customers" data-tone="6">Newsletter</li>
                <li data-node="reviews" data-group="customers" data-tone="6">Reviews</li>
                <li data-node="support-tickets" data-group="customers" data-tone="6">Support tickets</li>
                <li data-node="chat-assistant" data-group="customers" data-tone="6">Chat assistant</li>
                <li data-node="customer-database" data-group="customers" data-tone="6" data-shape="square">Customer database</li>
                <li data-node="platform" data-group="platform" data-tone="4" data-icon="ri-stack-line" data-meta="Domain">Platform</li>
                <li data-node="api-gateway" data-group="platform" data-tone="4">API gateway</li>
                <li data-node="service-mesh" data-group="platform" data-tone="4">Service mesh</li>
                <li data-node="message-bus" data-group="platform" data-tone="4" data-shape="diamond">Message bus</li>
                <li data-node="feature-flags" data-group="platform" data-tone="4">Feature flags</li>
                <li data-node="secrets-store" data-group="platform" data-tone="4">Secrets store</li>
                <li data-node="metrics" data-group="platform" data-tone="4">Metrics</li>
                <li data-node="log-store" data-group="platform" data-tone="4">Log store</li>
                <li data-node="tracing" data-group="platform" data-tone="4">Tracing</li>
                <li data-node="build-pipeline" data-group="platform" data-tone="4">Build pipeline</li>
                <li data-node="deploy-service" data-group="platform" data-tone="4">Deploy service</li>
                <li data-node="status-page" data-group="platform" data-tone="4">Status page</li>

                <li data-edge data-source="storefront" data-target="product-pages" data-label="runs" data-weight="light">Storefront runs Product pages</li>
                <li data-edge data-source="storefront" data-target="search-pages" data-label="runs" data-weight="light">Storefront runs Search pages</li>
                <li data-edge data-source="storefront" data-target="account-pages" data-label="runs" data-weight="light">Storefront runs Account pages</li>
                <li data-edge data-source="storefront" data-target="basket-view" data-label="runs" data-weight="light">Storefront runs Basket view</li>
                <li data-edge data-source="storefront" data-target="checkout-pages" data-label="runs" data-weight="light">Storefront runs Checkout pages</li>
                <li data-edge data-source="storefront" data-target="content-blocks" data-label="runs" data-weight="light">Storefront runs Content blocks</li>
                <li data-edge data-source="storefront" data-target="image-resizer" data-label="runs" data-weight="light">Storefront runs Image resizer</li>
                <li data-edge data-source="storefront" data-target="page-cache" data-label="runs" data-weight="light">Storefront runs Page cache</li>
                <li data-edge data-source="storefront" data-target="consent-banner" data-label="runs" data-weight="light">Storefront runs Consent banner</li>
                <li data-edge data-source="storefront" data-target="store-locator" data-label="runs" data-weight="light">Storefront runs Store locator</li>
                <li data-edge data-source="storefront" data-target="wish-lists" data-label="runs" data-weight="light">Storefront runs Wish lists</li>
                <li data-edge data-source="ordering" data-target="checkout-api" data-label="runs" data-weight="light">Ordering runs Checkout API</li>
                <li data-edge data-source="ordering" data-target="basket-service" data-label="runs" data-weight="light">Ordering runs Basket service</li>
                <li data-edge data-source="ordering" data-target="pricing" data-label="runs" data-weight="light">Ordering runs Pricing</li>
                <li data-edge data-source="ordering" data-target="vouchers" data-label="runs" data-weight="light">Ordering runs Vouchers</li>
                <li data-edge data-source="ordering" data-target="tax-rules" data-label="runs" data-weight="light">Ordering runs Tax rules</li>
                <li data-edge data-source="ordering" data-target="orders-api" data-label="runs" data-weight="light">Ordering runs Orders API</li>
                <li data-edge data-source="ordering" data-target="order-history" data-label="runs" data-weight="light">Ordering runs Order history</li>
                <li data-edge data-source="ordering" data-target="invoices" data-label="runs" data-weight="light">Ordering runs Invoices</li>
                <li data-edge data-source="ordering" data-target="returns" data-label="runs" data-weight="light">Ordering runs Returns</li>
                <li data-edge data-source="ordering" data-target="order-events" data-label="runs" data-weight="light">Ordering runs Order events</li>
                <li data-edge data-source="ordering" data-target="order-database" data-label="runs" data-weight="light">Ordering runs Order database</li>
                <li data-edge data-source="payments" data-target="payments-api" data-label="runs" data-weight="light">Payments runs Payments API</li>
                <li data-edge data-source="payments" data-target="card-gateway" data-label="runs" data-weight="light">Payments runs Card gateway</li>
                <li data-edge data-source="payments" data-target="wallets" data-label="runs" data-weight="light">Payments runs Wallets</li>
                <li data-edge data-source="payments" data-target="refunds" data-label="runs" data-weight="light">Payments runs Refunds</li>
                <li data-edge data-source="payments" data-target="fraud-screening" data-label="runs" data-weight="light">Payments runs Fraud screening</li>
                <li data-edge data-source="payments" data-target="payouts" data-label="runs" data-weight="light">Payments runs Payouts</li>
                <li data-edge data-source="payments" data-target="ledger" data-label="runs" data-weight="light">Payments runs Ledger</li>
                <li data-edge data-source="payments" data-target="reconciliation" data-label="runs" data-weight="light">Payments runs Reconciliation</li>
                <li data-edge data-source="payments" data-target="chargebacks" data-label="runs" data-weight="light">Payments runs Chargebacks</li>
                <li data-edge data-source="payments" data-target="payment-events" data-label="runs" data-weight="light">Payments runs Payment events</li>
                <li data-edge data-source="payments" data-target="ledger-database" data-label="runs" data-weight="light">Payments runs Ledger database</li>
                <li data-edge data-source="fulfilment" data-target="warehouse-sync" data-label="runs" data-weight="light">Fulfilment runs Warehouse sync</li>
                <li data-edge data-source="fulfilment" data-target="pick-lists" data-label="runs" data-weight="light">Fulfilment runs Pick lists</li>
                <li data-edge data-source="fulfilment" data-target="packing-app" data-label="runs" data-weight="light">Fulfilment runs Packing app</li>
                <li data-edge data-source="fulfilment" data-target="shipping-api" data-label="runs" data-weight="light">Fulfilment runs Shipping API</li>
                <li data-edge data-source="fulfilment" data-target="carrier-bridge" data-label="runs" data-weight="light">Fulfilment runs Carrier bridge</li>
                <li data-edge data-source="fulfilment" data-target="tracking" data-label="runs" data-weight="light">Fulfilment runs Tracking</li>
                <li data-edge data-source="fulfilment" data-target="label-printing" data-label="runs" data-weight="light">Fulfilment runs Label printing</li>
                <li data-edge data-source="fulfilment" data-target="stock-levels" data-label="runs" data-weight="light">Fulfilment runs Stock levels</li>
                <li data-edge data-source="fulfilment" data-target="replenishment" data-label="runs" data-weight="light">Fulfilment runs Replenishment</li>
                <li data-edge data-source="fulfilment" data-target="returns-desk" data-label="runs" data-weight="light">Fulfilment runs Returns desk</li>
                <li data-edge data-source="fulfilment" data-target="stock-database" data-label="runs" data-weight="light">Fulfilment runs Stock database</li>
                <li data-edge data-source="customers" data-target="accounts-api" data-label="runs" data-weight="light">Customers runs Accounts API</li>
                <li data-edge data-source="customers" data-target="sign-in" data-label="runs" data-weight="light">Customers runs Sign-in</li>
                <li data-edge data-source="customers" data-target="profiles" data-label="runs" data-weight="light">Customers runs Profiles</li>
                <li data-edge data-source="customers" data-target="addresses" data-label="runs" data-weight="light">Customers runs Addresses</li>
                <li data-edge data-source="customers" data-target="preferences" data-label="runs" data-weight="light">Customers runs Preferences</li>
                <li data-edge data-source="customers" data-target="loyalty-points" data-label="runs" data-weight="light">Customers runs Loyalty points</li>
                <li data-edge data-source="customers" data-target="newsletter" data-label="runs" data-weight="light">Customers runs Newsletter</li>
                <li data-edge data-source="customers" data-target="reviews" data-label="runs" data-weight="light">Customers runs Reviews</li>
                <li data-edge data-source="customers" data-target="support-tickets" data-label="runs" data-weight="light">Customers runs Support tickets</li>
                <li data-edge data-source="customers" data-target="chat-assistant" data-label="runs" data-weight="light">Customers runs Chat assistant</li>
                <li data-edge data-source="customers" data-target="customer-database" data-label="runs" data-weight="light">Customers runs Customer database</li>
                <li data-edge data-source="platform" data-target="api-gateway" data-label="runs" data-weight="light">Platform runs API gateway</li>
                <li data-edge data-source="platform" data-target="service-mesh" data-label="runs" data-weight="light">Platform runs Service mesh</li>
                <li data-edge data-source="platform" data-target="message-bus" data-label="runs" data-weight="light">Platform runs Message bus</li>
                <li data-edge data-source="platform" data-target="feature-flags" data-label="runs" data-weight="light">Platform runs Feature flags</li>
                <li data-edge data-source="platform" data-target="secrets-store" data-label="runs" data-weight="light">Platform runs Secrets store</li>
                <li data-edge data-source="platform" data-target="metrics" data-label="runs" data-weight="light">Platform runs Metrics</li>
                <li data-edge data-source="platform" data-target="log-store" data-label="runs" data-weight="light">Platform runs Log store</li>
                <li data-edge data-source="platform" data-target="tracing" data-label="runs" data-weight="light">Platform runs Tracing</li>
                <li data-edge data-source="platform" data-target="build-pipeline" data-label="runs" data-weight="light">Platform runs Build pipeline</li>
                <li data-edge data-source="platform" data-target="deploy-service" data-label="runs" data-weight="light">Platform runs Deploy service</li>
                <li data-edge data-source="platform" data-target="status-page" data-label="runs" data-weight="light">Platform runs Status page</li>
                <li data-edge data-source="product-pages" data-target="pricing" data-label="shows">Product pages shows Pricing</li>
                <li data-edge data-source="search-pages" data-target="stock-levels" data-label="checks">Search pages checks Stock levels</li>
                <li data-edge data-source="basket-view" data-target="basket-service" data-label="calls">Basket view calls Basket service</li>
                <li data-edge data-source="checkout-pages" data-target="checkout-api" data-label="calls">Checkout pages calls Checkout API</li>
                <li data-edge data-source="account-pages" data-target="accounts-api" data-label="calls">Account pages calls Accounts API</li>
                <li data-edge data-source="account-pages" data-target="order-history" data-label="shows">Account pages shows Order history</li>
                <li data-edge data-source="checkout-api" data-target="basket-service" data-label="reads">Checkout API reads Basket service</li>
                <li data-edge data-source="checkout-api" data-target="payments-api" data-label="charges">Checkout API charges Payments API</li>
                <li data-edge data-source="checkout-api" data-target="orders-api" data-label="places">Checkout API places Orders API</li>
                <li data-edge data-source="checkout-api" data-target="vouchers" data-label="redeems">Checkout API redeems Vouchers</li>
                <li data-edge data-source="pricing" data-target="tax-rules" data-label="applies">Pricing applies Tax rules</li>
                <li data-edge data-source="orders-api" data-target="order-database" data-label="writes">Orders API writes Order database</li>
                <li data-edge data-source="orders-api" data-target="order-events" data-label="publishes">Orders API publishes Order events</li>
                <li data-edge data-source="order-events" data-target="warehouse-sync" data-label="feeds">Order events feeds Warehouse sync</li>
                <li data-edge data-source="order-events" data-target="invoices" data-label="feeds">Order events feeds Invoices</li>
                <li data-edge data-source="order-events" data-target="newsletter" data-label="feeds">Order events feeds Newsletter</li>
                <li data-edge data-source="returns" data-target="refunds" data-label="asks for">Returns asks for Refunds</li>
                <li data-edge data-source="returns" data-target="returns-desk" data-label="books">Returns books Returns desk</li>
                <li data-edge data-source="payments-api" data-target="card-gateway" data-label="authorises">Payments API authorises Card gateway</li>
                <li data-edge data-source="payments-api" data-target="fraud-screening" data-label="screens">Payments API screens Fraud screening</li>
                <li data-edge data-source="payments-api" data-target="wallets" data-label="charges">Payments API charges Wallets</li>
                <li data-edge data-source="payments-api" data-target="payment-events" data-label="publishes">Payments API publishes Payment events</li>
                <li data-edge data-source="payment-events" data-target="ledger" data-label="feeds">Payment events feeds Ledger</li>
                <li data-edge data-source="ledger" data-target="ledger-database" data-label="writes">Ledger writes Ledger database</li>
                <li data-edge data-source="reconciliation" data-target="ledger" data-label="reads">Reconciliation reads Ledger</li>
                <li data-edge data-source="chargebacks" data-target="refunds" data-label="raises">Chargebacks raises Refunds</li>
                <li data-edge data-source="payouts" data-target="ledger" data-label="reads">Payouts reads Ledger</li>
                <li data-edge data-source="warehouse-sync" data-target="pick-lists" data-label="prints">Warehouse sync prints Pick lists</li>
                <li data-edge data-source="pick-lists" data-target="packing-app" data-label="feeds">Pick lists feeds Packing app</li>
                <li data-edge data-source="packing-app" data-target="label-printing" data-label="prints">Packing app prints Label printing</li>
                <li data-edge data-source="shipping-api" data-target="carrier-bridge" data-label="books">Shipping API books Carrier bridge</li>
                <li data-edge data-source="carrier-bridge" data-target="tracking" data-label="feeds">Carrier bridge feeds Tracking</li>
                <li data-edge data-source="stock-levels" data-target="stock-database" data-label="reads">Stock levels reads Stock database</li>
                <li data-edge data-source="replenishment" data-target="stock-levels" data-label="reads">Replenishment reads Stock levels</li>
                <li data-edge data-source="packing-app" data-target="shipping-api" data-label="calls">Packing app calls Shipping API</li>
                <li data-edge data-source="sign-in" data-target="accounts-api" data-label="checks">Sign-in checks Accounts API</li>
                <li data-edge data-source="profiles" data-target="customer-database" data-label="writes">Profiles writes Customer database</li>
                <li data-edge data-source="addresses" data-target="customer-database" data-label="writes">Addresses writes Customer database</li>
                <li data-edge data-source="loyalty-points" data-target="order-events" data-label="listens to">Loyalty points listens to Order events</li>
                <li data-edge data-source="reviews" data-target="product-pages" data-label="shown on">Reviews shown on Product pages</li>
                <li data-edge data-source="support-tickets" data-target="order-history" data-label="reads">Support tickets reads Order history</li>
                <li data-edge data-source="chat-assistant" data-target="support-tickets" data-label="opens">Chat assistant opens Support tickets</li>
                <li data-edge data-source="preferences" data-target="newsletter" data-label="controls">Preferences controls Newsletter</li>
                <li data-edge data-source="api-gateway" data-target="checkout-api" data-label="routes">API gateway routes Checkout API</li>
                <li data-edge data-source="api-gateway" data-target="accounts-api" data-label="routes">API gateway routes Accounts API</li>
                <li data-edge data-source="api-gateway" data-target="payments-api" data-label="routes">API gateway routes Payments API</li>
                <li data-edge data-source="service-mesh" data-target="api-gateway" data-label="carries">Service mesh carries API gateway</li>
                <li data-edge data-source="message-bus" data-target="order-events" data-label="carries">Message bus carries Order events</li>
                <li data-edge data-source="message-bus" data-target="payment-events" data-label="carries">Message bus carries Payment events</li>
                <li data-edge data-source="feature-flags" data-target="checkout-pages" data-label="configures">Feature flags configures Checkout pages</li>
                <li data-edge data-source="secrets-store" data-target="card-gateway" data-label="holds keys for">Secrets store holds keys for Card gateway</li>
                <li data-edge data-source="metrics" data-target="api-gateway" data-label="watches">Metrics watches API gateway</li>
                <li data-edge data-source="tracing" data-target="service-mesh" data-label="reads">Tracing reads Service mesh</li>
                <li data-edge data-source="metrics" data-target="log-store" data-label="writes">Metrics writes Log store</li>
                <li data-edge data-source="build-pipeline" data-target="deploy-service" data-label="hands over to">Build pipeline hands over to Deploy service</li>
                <li data-edge data-source="status-page" data-target="metrics" data-label="reads">Status page reads Metrics</li>
                <li data-edge data-source="page-cache" data-target="product-pages" data-label="caches">Page cache caches Product pages</li>
                <li data-edge data-source="image-resizer" data-target="content-blocks" data-label="serves">Image resizer serves Content blocks</li>
                <li data-edge data-source="consent-banner" data-target="newsletter" data-label="asks for">Consent banner asks for Newsletter</li>
                <li data-edge data-source="store-locator" data-target="stock-levels" data-label="checks">Store locator checks Stock levels</li>
                <li data-edge data-source="wish-lists" data-target="customer-database" data-label="writes">Wish lists writes Customer database</li>
            </ul>
            <div class="graph-canvas"></div>
            <div class="graph-tools" role="toolbar" aria-label="View">
                <button class="btn btn--ghost btn--sm btn--icon" type="button" data-graph-action="zoom-in" aria-label="Zoom in" data-tip="Zoom in"><i class="ri-zoom-in-line"></i></button>
                <button class="btn btn--ghost btn--sm btn--icon" type="button" data-graph-action="zoom-out" aria-label="Zoom out" data-tip="Zoom out"><i class="ri-zoom-out-line"></i></button>
                <button class="btn btn--ghost btn--sm btn--icon" type="button" data-graph-action="fit" aria-label="Fit" data-tip="Fit"><i class="ri-focus-3-line"></i></button>
                <span class="graph-tools-sep"></span>
                <button class="btn btn--ghost btn--sm btn--icon" type="button" data-graph-action="fullscreen" aria-pressed="false" aria-label="Full screen" data-tip="Full screen">
                    <i class="ri-fullscreen-line graph-when-off"></i><i class="ri-fullscreen-exit-line graph-when-on"></i>
                </button>
            </div>
            <canvas class="graph-minimap" data-graph-minimap aria-hidden="true"></canvas>
            <div class="graph-wait skeleton skeleton-block"></div>
        </div>
    </div>
</div>

Where the minimap sits

Bottom right by default. .graph-minimap--start moves it to the start edge and .graph-minimap--top to the top, and the two combine. Keep it clear of the tools: .graph-tools--stack stands them in a column, .graph-tools--bottom moves them to the bottom and .graph-tools--start to the start edge. In a .graph--sm the minimap is smaller.

The two graphs read one list: data-graph-data="#pl-bike-parts" on a graph names a .graph-data list anywhere on the page, so two views of the same records share one text alternative.

  • Touring bicycle
  • Frame set
  • Frame
  • Fork
  • Headset
  • Seat post
  • Saddle
  • Seat clamp
  • Mudguards
  • Kickstand
  • Front wheel
  • Front rim
  • Front hub
  • Front spokes
  • Front tyre
  • Front inner tube
  • Front rim tape
  • Front axle
  • Rear wheel
  • Rear rim
  • Rear hub
  • Rear spokes
  • Rear tyre
  • Rear inner tube
  • Rear rim tape
  • Rear axle
  • Drivetrain
  • Crankset
  • Chainring
  • Bottom bracket
  • Chain
  • Cassette
  • Rear derailleur
  • Shifter
  • Pedals
  • Brakes
  • Brake levers
  • Front caliper
  • Rear caliper
  • Brake pads
  • Front rotor
  • Rear rotor
  • Brake hoses
  • Cockpit
  • Handlebar
  • Stem
  • Grips
  • Bell
  • Front light
  • Rear light
  • The frame set is part of the bicycle
  • The front wheel is part of the bicycle
  • The rear wheel is part of the bicycle
  • The drivetrain is part of the bicycle
  • The brakes is part of the bicycle
  • The cockpit is part of the bicycle
  • The frame is part of the frame set
  • The fork is part of the frame set
  • The headset is part of the frame set
  • The seat post is part of the frame set
  • The saddle is part of the frame set
  • The seat clamp is part of the frame set
  • The mudguards is part of the frame set
  • The kickstand is part of the frame set
  • The front rim is part of the front wheel
  • The front hub is part of the front wheel
  • The front spokes is part of the front wheel
  • The front tyre is part of the front wheel
  • The front inner tube is part of the front wheel
  • The front rim tape is part of the front wheel
  • The front axle is part of the front wheel
  • The rear rim is part of the rear wheel
  • The rear hub is part of the rear wheel
  • The rear spokes is part of the rear wheel
  • The rear tyre is part of the rear wheel
  • The rear inner tube is part of the rear wheel
  • The rear rim tape is part of the rear wheel
  • The rear axle is part of the rear wheel
  • The crankset is part of the drivetrain
  • The chainring is part of the drivetrain
  • The bottom bracket is part of the drivetrain
  • The chain is part of the drivetrain
  • The cassette is part of the drivetrain
  • The rear derailleur is part of the drivetrain
  • The shifter is part of the drivetrain
  • The pedals is part of the drivetrain
  • The brake levers is part of the brakes
  • The front caliper is part of the brakes
  • The rear caliper is part of the brakes
  • The brake pads is part of the brakes
  • The front rotor is part of the brakes
  • The rear rotor is part of the brakes
  • The brake hoses is part of the brakes
  • The handlebar is part of the cockpit
  • The stem is part of the cockpit
  • The grips is part of the cockpit
  • The bell is part of the cockpit
  • The front light is part of the cockpit
  • The rear light is part of the cockpit
  • The rear hub carries the cassette
  • The front hub carries the front rotor
  • The rear hub carries the rear rotor
  • The shifter moves the rear derailleur
  • The brake levers press the brake hoses
  • The frame holds the bottom bracket
  • The fork holds the front caliper
  • The handlebar holds the shifter
  • The handlebar holds the brake levers
<div class="sedna-col sedna-gap-3">
    <ul class="graph-data" id="pl-bike-parts">
        <li data-node="bicycle" data-tone="1" data-shape="star" data-meta="Finished product" data-root>Touring bicycle</li>
        <li data-node="frame-set" data-tone="2" data-shape="square" data-meta="Assembly" data-hub>Frame set</li>
        <li data-node="frame" data-tone="2" data-meta="Part">Frame</li>
        <li data-node="fork" data-tone="2" data-meta="Part">Fork</li>
        <li data-node="headset" data-tone="2" data-meta="Part">Headset</li>
        <li data-node="seat-post" data-tone="2" data-meta="Part">Seat post</li>
        <li data-node="saddle" data-tone="2" data-meta="Part">Saddle</li>
        <li data-node="seat-clamp" data-tone="2" data-meta="Part">Seat clamp</li>
        <li data-node="mudguards" data-tone="2" data-meta="Part">Mudguards</li>
        <li data-node="kickstand" data-tone="2" data-meta="Part">Kickstand</li>
        <li data-node="front-wheel" data-tone="3" data-shape="square" data-meta="Assembly" data-hub>Front wheel</li>
        <li data-node="front-rim" data-tone="3" data-meta="Part">Front rim</li>
        <li data-node="front-hub" data-tone="3" data-meta="Part">Front hub</li>
        <li data-node="front-spokes" data-tone="3" data-meta="Part">Front spokes</li>
        <li data-node="front-tyre" data-tone="3" data-meta="Part">Front tyre</li>
        <li data-node="front-inner-tube" data-tone="3" data-meta="Part">Front inner tube</li>
        <li data-node="front-rim-tape" data-tone="3" data-meta="Part">Front rim tape</li>
        <li data-node="front-axle" data-tone="3" data-meta="Part">Front axle</li>
        <li data-node="rear-wheel" data-tone="3" data-shape="square" data-meta="Assembly" data-hub>Rear wheel</li>
        <li data-node="rear-rim" data-tone="3" data-meta="Part">Rear rim</li>
        <li data-node="rear-hub" data-tone="3" data-meta="Part">Rear hub</li>
        <li data-node="rear-spokes" data-tone="3" data-meta="Part">Rear spokes</li>
        <li data-node="rear-tyre" data-tone="3" data-meta="Part">Rear tyre</li>
        <li data-node="rear-inner-tube" data-tone="3" data-meta="Part">Rear inner tube</li>
        <li data-node="rear-rim-tape" data-tone="3" data-meta="Part">Rear rim tape</li>
        <li data-node="rear-axle" data-tone="3" data-meta="Part">Rear axle</li>
        <li data-node="drivetrain" data-tone="5" data-shape="square" data-meta="Assembly" data-hub>Drivetrain</li>
        <li data-node="crankset" data-tone="5" data-meta="Part">Crankset</li>
        <li data-node="chainring" data-tone="5" data-meta="Part">Chainring</li>
        <li data-node="bottom-bracket" data-tone="5" data-meta="Part">Bottom bracket</li>
        <li data-node="chain" data-tone="5" data-meta="Part">Chain</li>
        <li data-node="cassette" data-tone="5" data-meta="Part">Cassette</li>
        <li data-node="rear-derailleur" data-tone="5" data-meta="Part">Rear derailleur</li>
        <li data-node="shifter" data-tone="5" data-meta="Part">Shifter</li>
        <li data-node="pedals" data-tone="5" data-meta="Part">Pedals</li>
        <li data-node="brakes" data-tone="6" data-shape="square" data-meta="Assembly" data-hub>Brakes</li>
        <li data-node="brake-levers" data-tone="6" data-meta="Part">Brake levers</li>
        <li data-node="front-caliper" data-tone="6" data-meta="Part">Front caliper</li>
        <li data-node="rear-caliper" data-tone="6" data-meta="Part">Rear caliper</li>
        <li data-node="brake-pads" data-tone="6" data-meta="Part">Brake pads</li>
        <li data-node="front-rotor" data-tone="6" data-meta="Part">Front rotor</li>
        <li data-node="rear-rotor" data-tone="6" data-meta="Part">Rear rotor</li>
        <li data-node="brake-hoses" data-tone="6" data-meta="Part">Brake hoses</li>
        <li data-node="cockpit" data-tone="4" data-shape="square" data-meta="Assembly" data-hub>Cockpit</li>
        <li data-node="handlebar" data-tone="4" data-meta="Part">Handlebar</li>
        <li data-node="stem" data-tone="4" data-meta="Part">Stem</li>
        <li data-node="grips" data-tone="4" data-meta="Part">Grips</li>
        <li data-node="bell" data-tone="4" data-meta="Part">Bell</li>
        <li data-node="front-light" data-tone="4" data-meta="Part">Front light</li>
        <li data-node="rear-light" data-tone="4" data-meta="Part">Rear light</li>

        <li data-edge data-source="frame-set" data-target="bicycle" data-label="part of">The frame set is part of the bicycle</li>
        <li data-edge data-source="front-wheel" data-target="bicycle" data-label="part of">The front wheel is part of the bicycle</li>
        <li data-edge data-source="rear-wheel" data-target="bicycle" data-label="part of">The rear wheel is part of the bicycle</li>
        <li data-edge data-source="drivetrain" data-target="bicycle" data-label="part of">The drivetrain is part of the bicycle</li>
        <li data-edge data-source="brakes" data-target="bicycle" data-label="part of">The brakes is part of the bicycle</li>
        <li data-edge data-source="cockpit" data-target="bicycle" data-label="part of">The cockpit is part of the bicycle</li>
        <li data-edge data-source="frame" data-target="frame-set" data-label="part of">The frame is part of the frame set</li>
        <li data-edge data-source="fork" data-target="frame-set" data-label="part of">The fork is part of the frame set</li>
        <li data-edge data-source="headset" data-target="frame-set" data-label="part of">The headset is part of the frame set</li>
        <li data-edge data-source="seat-post" data-target="frame-set" data-label="part of">The seat post is part of the frame set</li>
        <li data-edge data-source="saddle" data-target="frame-set" data-label="part of">The saddle is part of the frame set</li>
        <li data-edge data-source="seat-clamp" data-target="frame-set" data-label="part of">The seat clamp is part of the frame set</li>
        <li data-edge data-source="mudguards" data-target="frame-set" data-label="part of">The mudguards is part of the frame set</li>
        <li data-edge data-source="kickstand" data-target="frame-set" data-label="part of">The kickstand is part of the frame set</li>
        <li data-edge data-source="front-rim" data-target="front-wheel" data-label="part of">The front rim is part of the front wheel</li>
        <li data-edge data-source="front-hub" data-target="front-wheel" data-label="part of">The front hub is part of the front wheel</li>
        <li data-edge data-source="front-spokes" data-target="front-wheel" data-label="part of">The front spokes is part of the front wheel</li>
        <li data-edge data-source="front-tyre" data-target="front-wheel" data-label="part of">The front tyre is part of the front wheel</li>
        <li data-edge data-source="front-inner-tube" data-target="front-wheel" data-label="part of">The front inner tube is part of the front wheel</li>
        <li data-edge data-source="front-rim-tape" data-target="front-wheel" data-label="part of">The front rim tape is part of the front wheel</li>
        <li data-edge data-source="front-axle" data-target="front-wheel" data-label="part of">The front axle is part of the front wheel</li>
        <li data-edge data-source="rear-rim" data-target="rear-wheel" data-label="part of">The rear rim is part of the rear wheel</li>
        <li data-edge data-source="rear-hub" data-target="rear-wheel" data-label="part of">The rear hub is part of the rear wheel</li>
        <li data-edge data-source="rear-spokes" data-target="rear-wheel" data-label="part of">The rear spokes is part of the rear wheel</li>
        <li data-edge data-source="rear-tyre" data-target="rear-wheel" data-label="part of">The rear tyre is part of the rear wheel</li>
        <li data-edge data-source="rear-inner-tube" data-target="rear-wheel" data-label="part of">The rear inner tube is part of the rear wheel</li>
        <li data-edge data-source="rear-rim-tape" data-target="rear-wheel" data-label="part of">The rear rim tape is part of the rear wheel</li>
        <li data-edge data-source="rear-axle" data-target="rear-wheel" data-label="part of">The rear axle is part of the rear wheel</li>
        <li data-edge data-source="crankset" data-target="drivetrain" data-label="part of">The crankset is part of the drivetrain</li>
        <li data-edge data-source="chainring" data-target="drivetrain" data-label="part of">The chainring is part of the drivetrain</li>
        <li data-edge data-source="bottom-bracket" data-target="drivetrain" data-label="part of">The bottom bracket is part of the drivetrain</li>
        <li data-edge data-source="chain" data-target="drivetrain" data-label="part of">The chain is part of the drivetrain</li>
        <li data-edge data-source="cassette" data-target="drivetrain" data-label="part of">The cassette is part of the drivetrain</li>
        <li data-edge data-source="rear-derailleur" data-target="drivetrain" data-label="part of">The rear derailleur is part of the drivetrain</li>
        <li data-edge data-source="shifter" data-target="drivetrain" data-label="part of">The shifter is part of the drivetrain</li>
        <li data-edge data-source="pedals" data-target="drivetrain" data-label="part of">The pedals is part of the drivetrain</li>
        <li data-edge data-source="brake-levers" data-target="brakes" data-label="part of">The brake levers is part of the brakes</li>
        <li data-edge data-source="front-caliper" data-target="brakes" data-label="part of">The front caliper is part of the brakes</li>
        <li data-edge data-source="rear-caliper" data-target="brakes" data-label="part of">The rear caliper is part of the brakes</li>
        <li data-edge data-source="brake-pads" data-target="brakes" data-label="part of">The brake pads is part of the brakes</li>
        <li data-edge data-source="front-rotor" data-target="brakes" data-label="part of">The front rotor is part of the brakes</li>
        <li data-edge data-source="rear-rotor" data-target="brakes" data-label="part of">The rear rotor is part of the brakes</li>
        <li data-edge data-source="brake-hoses" data-target="brakes" data-label="part of">The brake hoses is part of the brakes</li>
        <li data-edge data-source="handlebar" data-target="cockpit" data-label="part of">The handlebar is part of the cockpit</li>
        <li data-edge data-source="stem" data-target="cockpit" data-label="part of">The stem is part of the cockpit</li>
        <li data-edge data-source="grips" data-target="cockpit" data-label="part of">The grips is part of the cockpit</li>
        <li data-edge data-source="bell" data-target="cockpit" data-label="part of">The bell is part of the cockpit</li>
        <li data-edge data-source="front-light" data-target="cockpit" data-label="part of">The front light is part of the cockpit</li>
        <li data-edge data-source="rear-light" data-target="cockpit" data-label="part of">The rear light is part of the cockpit</li>
        <li data-edge data-source="rear-hub" data-target="cassette" data-label="carries" data-line="dashed">The rear hub carries the cassette</li>
        <li data-edge data-source="front-hub" data-target="front-rotor" data-label="carries" data-line="dashed">The front hub carries the front rotor</li>
        <li data-edge data-source="rear-hub" data-target="rear-rotor" data-label="carries" data-line="dashed">The rear hub carries the rear rotor</li>
        <li data-edge data-source="shifter" data-target="rear-derailleur" data-label="moves" data-line="dashed">The shifter moves the rear derailleur</li>
        <li data-edge data-source="brake-levers" data-target="brake-hoses" data-label="press" data-line="dashed">The brake levers press the brake hoses</li>
        <li data-edge data-source="frame" data-target="bottom-bracket" data-label="holds" data-line="dashed">The frame holds the bottom bracket</li>
        <li data-edge data-source="fork" data-target="front-caliper" data-label="holds" data-line="dashed">The fork holds the front caliper</li>
        <li data-edge data-source="handlebar" data-target="shifter" data-label="holds" data-line="dashed">The handlebar holds the shifter</li>
        <li data-edge data-source="handlebar" data-target="brake-levers" data-label="holds" data-line="dashed">The handlebar holds the brake levers</li>
    </ul>
    <div class="sedna-grid sedna-grid--fit sedna-grid--lg">
        <div class="card">
            <div class="graph graph--sm" data-graph id="pl-corner-start" data-graph-data="#pl-bike-parts" data-graph-arrows="target"
                 aria-label="The parts of a touring bicycle">
                <div class="graph-canvas"></div>
                <div class="graph-tools graph-tools--stack" role="toolbar" aria-label="View">
                    <button class="btn btn--ghost btn--sm btn--icon" type="button" data-graph-action="zoom-in" aria-label="Zoom in" data-tip="Zoom in"><i class="ri-zoom-in-line"></i></button>
                    <button class="btn btn--ghost btn--sm btn--icon" type="button" data-graph-action="zoom-out" aria-label="Zoom out" data-tip="Zoom out"><i class="ri-zoom-out-line"></i></button>
                    <span class="graph-tools-sep"></span>
                    <button class="btn btn--ghost btn--sm btn--icon" type="button" data-graph-action="fit" aria-label="Fit" data-tip="Fit"><i class="ri-focus-3-line"></i></button>
                </div>
                <canvas class="graph-minimap graph-minimap--start" data-graph-minimap aria-hidden="true"></canvas>
                <div class="graph-wait skeleton skeleton-block"></div>
            </div>
        </div>
        <div class="card">
            <div class="graph graph--sm" data-graph id="pl-corner-top" data-graph-data="#pl-bike-parts" data-graph-arrows="target"
                 aria-label="The parts of a touring bicycle, again">
                <div class="graph-canvas"></div>
                <div class="graph-tools graph-tools--bottom" role="toolbar" aria-label="View">
                    <button class="btn btn--ghost btn--sm btn--icon" type="button" data-graph-action="zoom-in" aria-label="Zoom in" data-tip="Zoom in"><i class="ri-zoom-in-line"></i></button>
                    <button class="btn btn--ghost btn--sm btn--icon" type="button" data-graph-action="zoom-out" aria-label="Zoom out" data-tip="Zoom out"><i class="ri-zoom-out-line"></i></button>
                    <span class="graph-tools-sep"></span>
                    <button class="btn btn--ghost btn--sm btn--icon" type="button" data-graph-action="fit" aria-label="Fit" data-tip="Fit"><i class="ri-focus-3-line"></i></button>
                </div>
                <canvas class="graph-minimap graph-minimap--top graph-minimap--start" data-graph-minimap aria-hidden="true"></canvas>
                <div class="graph-wait skeleton skeleton-block"></div>
            </div>
        </div>
    </div>
</div>

Live — turn drawing on and give Campaign results a feed. The connect toggle — or data-graph-connect on the graph, to start drawing — turns a drag from one record to another into a link, and the graph dispatches sedna-graph-connect with { source, target }. While it is on, the graph carries data-graph-drawing and the pointer is a crosshair; a toggle's two icons are .graph-when-off and .graph-when-on.

The line drawn is removed again, and the app adds the link to its own data, the way a drop in drag and drop is the app's to make: it can check it, save it, or refuse it. This page has no app behind it, so data-graph-managed keeps what is drawn instead. In Blazor the handler is @onsedna-graph-connect with SednaGraphConnectEventArgs, on Graph in Blazor.

Turn drawing on, then drag from one record to another.
  • Orders database
  • Web analytics
  • Customer export
  • Stock feed
  • Nightly load
  • Clickstream job
  • Stock sync
  • Sales facts
  • Customer table
  • Product table
  • Visits
  • Stock snapshot
  • Weekly sales
  • Margin by category
  • Stock cover
  • Campaign results
  • The orders database feeds the nightly load
  • The customer export feeds the nightly load
  • The nightly load fills the sales facts
  • The nightly load fills the customer table
  • The nightly load fills the product table
  • Web analytics feeds the clickstream job
  • The clickstream job fills the visits
  • The stock feed feeds the stock sync
  • The stock sync fills the stock snapshot
  • The sales facts feed weekly sales
  • The sales facts feed margin by category
  • The product table feeds margin by category
  • The stock snapshot feeds stock cover
  • The sales facts feed stock cover
Source Job Table Report Feeds
<div class="graph-frame" data-graph-frame>
    <div class="toolbar">
        <span class="text-sm text-muted">Turn drawing on, then drag from one record to another.</span>
        <span class="toolbar-count" data-graph-stats="{edges} feeds between {nodes} records"></span>
    </div>
    <div class="card">
        <div class="graph" data-graph id="pl-draw" data-graph-managed data-graph-arrows="target"
             aria-label="Where the sales reports get their data">
            <ul class="graph-data" data-graph-data>
                <li data-node="orders-db" data-kind="source" data-tone="4" data-shape="square" data-icon="ri-database-2-line" data-meta="Source">Orders database</li>
                <li data-node="clickstream" data-kind="source" data-tone="4" data-shape="square" data-icon="ri-cursor-line" data-meta="Source">Web analytics</li>
                <li data-node="crm" data-kind="source" data-tone="4" data-shape="square" data-icon="ri-contacts-book-line" data-meta="Source">Customer export</li>
                <li data-node="stock-feed" data-kind="source" data-tone="4" data-shape="square" data-icon="ri-archive-stack-line" data-meta="Source">Stock feed</li>
                <li data-node="nightly-load" data-kind="job" data-tone="5" data-shape="diamond" data-icon="ri-timer-line" data-meta="Runs at 02:00">Nightly load</li>
                <li data-node="click-job" data-kind="job" data-tone="5" data-shape="diamond" data-icon="ri-timer-line" data-meta="Runs hourly">Clickstream job</li>
                <li data-node="stock-sync" data-kind="job" data-tone="5" data-shape="diamond" data-icon="ri-timer-line" data-meta="Runs every 15 minutes">Stock sync</li>
                <li data-node="sales" data-kind="table" data-tone="2" data-icon="ri-table-line" data-meta="Table">Sales facts</li>
                <li data-node="customers" data-kind="table" data-tone="2" data-icon="ri-table-line" data-meta="Table">Customer table</li>
                <li data-node="products" data-kind="table" data-tone="2" data-icon="ri-table-line" data-meta="Table">Product table</li>
                <li data-node="visits" data-kind="table" data-tone="2" data-icon="ri-table-line" data-meta="Table">Visits</li>
                <li data-node="stock" data-kind="table" data-tone="2" data-icon="ri-table-line" data-meta="Table">Stock snapshot</li>
                <li data-node="weekly-sales" data-kind="report" data-tone="3" data-shape="triangle" data-icon="ri-bar-chart-2-line" data-meta="Report">Weekly sales</li>
                <li data-node="margin" data-kind="report" data-tone="3" data-shape="triangle" data-icon="ri-bar-chart-2-line" data-meta="Report">Margin by category</li>
                <li data-node="stock-cover" data-kind="report" data-tone="3" data-shape="triangle" data-icon="ri-bar-chart-2-line" data-meta="Report">Stock cover</li>
                <li data-node="campaigns" data-kind="report" data-tone="3" data-shape="triangle" data-icon="ri-bar-chart-2-line" data-meta="Report, no data yet" data-hub>Campaign results</li>

                <li data-edge data-source="orders-db" data-target="nightly-load" data-label="feeds">The orders database feeds the nightly load</li>
                <li data-edge data-source="crm" data-target="nightly-load" data-label="feeds">The customer export feeds the nightly load</li>
                <li data-edge data-source="nightly-load" data-target="sales" data-label="fills">The nightly load fills the sales facts</li>
                <li data-edge data-source="nightly-load" data-target="customers" data-label="fills">The nightly load fills the customer table</li>
                <li data-edge data-source="nightly-load" data-target="products" data-label="fills">The nightly load fills the product table</li>
                <li data-edge data-source="clickstream" data-target="click-job" data-label="feeds">Web analytics feeds the clickstream job</li>
                <li data-edge data-source="click-job" data-target="visits" data-label="fills">The clickstream job fills the visits</li>
                <li data-edge data-source="stock-feed" data-target="stock-sync" data-label="feeds">The stock feed feeds the stock sync</li>
                <li data-edge data-source="stock-sync" data-target="stock" data-label="fills">The stock sync fills the stock snapshot</li>
                <li data-edge data-source="sales" data-target="weekly-sales" data-label="feeds">The sales facts feed weekly sales</li>
                <li data-edge data-source="sales" data-target="margin" data-label="feeds">The sales facts feed margin by category</li>
                <li data-edge data-source="products" data-target="margin" data-label="feeds">The product table feeds margin by category</li>
                <li data-edge data-source="stock" data-target="stock-cover" data-label="feeds">The stock snapshot feeds stock cover</li>
                <li data-edge data-source="sales" data-target="stock-cover" data-label="feeds">The sales facts feed stock cover</li>
            </ul>
            <div class="graph-canvas"></div>
            <div class="graph-tools" role="toolbar" aria-label="Drawing">
                <button class="btn btn--ghost btn--sm btn--icon" type="button" data-graph-action="connect" aria-pressed="false" aria-label="Draw links" data-tip="Draw links">
                    <i class="ri-link graph-when-off"></i><i class="ri-check-line graph-when-on"></i>
                </button>
                <span class="graph-tools-sep"></span>
                <button class="btn btn--ghost btn--sm btn--icon" type="button" data-graph-action="fit" aria-label="Fit" data-tip="Fit"><i class="ri-focus-3-line"></i></button>
            </div>
            <div class="graph-wait skeleton skeleton-block"></div>
        </div>
    </div>
    <div class="graph-legend">
        <span class="graph-key"><span class="graph-swatch graph-swatch--square series-4"></span> <span>Source</span></span>
        <span class="graph-key"><span class="graph-swatch graph-swatch--diamond series-5"></span> <span>Job</span></span>
        <span class="graph-key"><span class="graph-swatch series-2"></span> <span>Table</span></span>
        <span class="graph-key"><span class="graph-swatch graph-swatch--triangle series-3"></span> <span>Report</span></span>
        <span class="graph-key"><span class="graph-line graph-line--arrow"></span> <span>Feeds</span></span>
    </div>
</div>

The event is an ordinary bubbling DOM event. Save the link, then add an item to the list: the list is the data, so a new item is a new line on the canvas.

<!-- The graph has no data-graph-managed: the page decides whether a drawn link is kept. -->
<div class="graph" data-graph id="lineage" data-graph-connect aria-label="Where the sales reports get their data">
    <ul class="graph-data" data-graph-data>
        <li data-node="sales">Sales facts</li>
        <li data-node="campaigns">Campaign results</li>
    </ul>
    <div class="graph-canvas"></div>
    <div class="graph-wait skeleton skeleton-block"></div>
</div>

<script>
    const graph = document.getElementById('lineage');

    graph.addEventListener('sedna-graph-connect', async event => {
        const { source, target } = event.detail;

        // Save it first. The line the reader drew is already gone again.
        const saved = await fetch('/api/lineage/links', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ source, target }),
        });
        if (!saved.ok) return;

        // The list is the data: a new item is a new link on the canvas.
        const item = document.createElement('li');
        item.dataset.edge = '';
        item.dataset.source = source;
        item.dataset.target = target;
        item.dataset.label = 'feeds';
        item.textContent = `${source} feeds ${target}`;
        graph.querySelector('[data-graph-data]').append(item);
    });
</script>

Folding groups

Live — open every module, or right-click one. A record with data-parent is drawn inside that record, which becomes a group. data-graph-collapse gives every group a fold cue in its corner while it is selected — click a group, then its cue — and data-collapsed on a group starts it folded, as Shared is here. A folded group is one record, and the links of what is inside it end on it.

expand-all and collapse-all act on every group. expand and collapse act on one — a button's value, else the menu's record, else the selection — and in a menu each hides itself where it does not apply. Folding dispatches sedna-graph-collapse and unfolding sedna-graph-expand, each with { id, label }. An unfolded group opens where it is and the others stay put; arrange lays the drawing out again.

  • Checkout
  • Basket view
  • Address form
  • Payment form
  • Order summary
  • Checkout state
  • Catalogue
  • Product list
  • Product page
  • Filters
  • Price tag
  • Account
  • Sign-in form
  • Profile page
  • Order list
  • Shared
  • Button
  • Form field
  • Money format
  • HTTP client
  • Icon
  • The basket view uses the price tag
  • The basket view reads the checkout state
  • The address form uses the form field
  • The address form writes the checkout state
  • The payment form uses the form field
  • The payment form uses the HTTP client
  • The payment form writes the checkout state
  • The order summary uses the money format
  • The order summary uses the button
  • The product list uses the price tag
  • The product list uses the filters
  • The product page uses the price tag
  • The product page uses the button
  • The product page uses the HTTP client
  • The filters use the icon
  • The price tag uses the money format
  • The sign-in form uses the form field
  • The sign-in form uses the HTTP client
  • The profile page uses the form field
  • The order list uses the money format
  • The order list uses the HTTP client
  • The button uses the icon
<div class="graph-frame" data-graph-frame>
    <div class="toolbar">
        <div class="toolbar-filters">
            <button class="btn btn--sm" type="button" data-graph-action="expand-all"><i class="ri-add-box-line"></i> <span>Open every module</span></button>
            <button class="btn btn--sm" type="button" data-graph-action="collapse-all"><i class="ri-checkbox-indeterminate-line"></i> <span>Fold every module</span></button>
            <button class="btn btn--ghost btn--sm" type="button" data-graph-action="arrange"><i class="ri-magic-line"></i> <span>Arrange again</span></button>
        </div>
        <span class="toolbar-count" data-graph-stats="{nodes} shown of {totalNodes}"></span>
    </div>
    <div class="card">
        <div class="graph" data-graph id="pl-fold" data-graph-layout="fcose" data-graph-collapse data-graph-arrows="target"
             aria-label="The web shop's code, by module, and what each part uses">
            <ul class="graph-data" data-graph-data>
                <li data-node="checkout" data-tone="2" data-meta="Module">Checkout</li>
                <li data-node="basket-view" data-parent="checkout" data-tone="2" data-meta="Component">Basket view</li>
                <li data-node="address-form" data-parent="checkout" data-tone="2" data-meta="Component">Address form</li>
                <li data-node="payment-form" data-parent="checkout" data-tone="2" data-meta="Component">Payment form</li>
                <li data-node="order-summary" data-parent="checkout" data-tone="2" data-meta="Component">Order summary</li>
                <li data-node="checkout-state" data-parent="checkout" data-tone="2" data-shape="diamond" data-meta="State">Checkout state</li>

                <li data-node="catalogue" data-tone="3" data-meta="Module">Catalogue</li>
                <li data-node="product-list" data-parent="catalogue" data-tone="3" data-meta="Component">Product list</li>
                <li data-node="product-page" data-parent="catalogue" data-tone="3" data-meta="Component">Product page</li>
                <li data-node="filters" data-parent="catalogue" data-tone="3" data-meta="Component">Filters</li>
                <li data-node="price-tag" data-parent="catalogue" data-tone="3" data-meta="Component">Price tag</li>

                <li data-node="account" data-tone="5" data-meta="Module">Account</li>
                <li data-node="sign-in-form" data-parent="account" data-tone="5" data-meta="Component">Sign-in form</li>
                <li data-node="profile-page" data-parent="account" data-tone="5" data-meta="Component">Profile page</li>
                <li data-node="order-list" data-parent="account" data-tone="5" data-meta="Component">Order list</li>

                <li data-node="shared" data-tone="4" data-meta="Module" data-collapsed>Shared</li>
                <li data-node="button" data-parent="shared" data-tone="4" data-meta="Component">Button</li>
                <li data-node="form-field" data-parent="shared" data-tone="4" data-meta="Component">Form field</li>
                <li data-node="money" data-parent="shared" data-tone="4" data-shape="hexagon" data-meta="Helper">Money format</li>
                <li data-node="http" data-parent="shared" data-tone="4" data-shape="hexagon" data-meta="Helper">HTTP client</li>
                <li data-node="icons" data-parent="shared" data-tone="4" data-meta="Component">Icon</li>

                <li data-edge data-source="basket-view" data-target="price-tag" data-label="uses">The basket view uses the price tag</li>
                <li data-edge data-source="basket-view" data-target="checkout-state" data-label="reads">The basket view reads the checkout state</li>
                <li data-edge data-source="address-form" data-target="form-field" data-label="uses">The address form uses the form field</li>
                <li data-edge data-source="address-form" data-target="checkout-state" data-label="writes">The address form writes the checkout state</li>
                <li data-edge data-source="payment-form" data-target="form-field" data-label="uses">The payment form uses the form field</li>
                <li data-edge data-source="payment-form" data-target="http" data-label="uses">The payment form uses the HTTP client</li>
                <li data-edge data-source="payment-form" data-target="checkout-state" data-label="writes">The payment form writes the checkout state</li>
                <li data-edge data-source="order-summary" data-target="money" data-label="uses">The order summary uses the money format</li>
                <li data-edge data-source="order-summary" data-target="button" data-label="uses">The order summary uses the button</li>
                <li data-edge data-source="product-list" data-target="price-tag" data-label="uses">The product list uses the price tag</li>
                <li data-edge data-source="product-list" data-target="filters" data-label="uses">The product list uses the filters</li>
                <li data-edge data-source="product-page" data-target="price-tag" data-label="uses">The product page uses the price tag</li>
                <li data-edge data-source="product-page" data-target="button" data-label="uses">The product page uses the button</li>
                <li data-edge data-source="product-page" data-target="http" data-label="uses">The product page uses the HTTP client</li>
                <li data-edge data-source="filters" data-target="icons" data-label="uses">The filters use the icon</li>
                <li data-edge data-source="price-tag" data-target="money" data-label="uses">The price tag uses the money format</li>
                <li data-edge data-source="sign-in-form" data-target="form-field" data-label="uses">The sign-in form uses the form field</li>
                <li data-edge data-source="sign-in-form" data-target="http" data-label="uses">The sign-in form uses the HTTP client</li>
                <li data-edge data-source="profile-page" data-target="form-field" data-label="uses">The profile page uses the form field</li>
                <li data-edge data-source="order-list" data-target="money" data-label="uses">The order list uses the money format</li>
                <li data-edge data-source="order-list" data-target="http" data-label="uses">The order list uses the HTTP client</li>
                <li data-edge data-source="button" data-target="icons" data-label="uses">The button uses the icon</li>
            </ul>
            <div class="graph-canvas"></div>
            <div class="menu graph-menu" data-graph-menu role="menu" hidden>
                <span class="menu-label" data-graph-field="label"></span>
                <button class="menu-item" role="menuitem" type="button" data-graph-action="expand"><i class="ri-add-box-line"></i> <span>Open the module</span></button>
                <button class="menu-item" role="menuitem" type="button" data-graph-action="collapse"><i class="ri-checkbox-indeterminate-line"></i> <span>Fold the module</span></button>
                <button class="menu-item" role="menuitem" type="button" data-graph-action="focus"><i class="ri-focus-2-line"></i> <span>Show what it touches</span></button>
                <button class="menu-item" role="menuitem" type="button" data-graph-action="unfocus"><i class="ri-fullscreen-exit-line"></i> <span>Show everything</span></button>
            </div>
            <div class="graph-wait skeleton skeleton-block"></div>
        </div>
    </div>
</div>

Outlining groups

Live. data-graph-hulls — or the hulls toggle — draws a soft outline around every data-group of two or more records, in the group's tone and routed around the records that are not in it. The records are laid out by their links, so the outline is how the reader sees who owns what on a map shaped by something else. It follows a record that is dragged, and fades while a record is lit.

A group's key is .graph-swatch--group in its .series-* colour. Its tone is its heart's — the record whose id is the group's name — else the one most of its members wear. A data-group is not a container: for records drawn inside another, use data-parent, as in the two examples either side of this one.

  • Search
  • Recommendations
  • Product pages
  • Reviews
  • Wish lists
  • Basket
  • Checkout
  • Vouchers
  • Payments
  • Fraud screening
  • Orders
  • Warehouse sync
  • Shipping
  • Tracking
  • Returns
  • Gateway
  • Message bus
  • Feature flags
  • Metrics
  • Sign-in
  • The gateway routes to the product pages
  • The gateway routes to checkout
  • The gateway checks sign-in
  • The product pages call search
  • The product pages show reviews
  • The product pages show recommendations
  • Recommendations read past orders
  • Wish lists check sign-in
  • Wish lists link to the product pages
  • The basket applies vouchers
  • Checkout reads the basket
  • Checkout charges through payments
  • Payments screens with fraud screening
  • Checkout places orders
  • Orders publish to the message bus
  • The message bus feeds the warehouse sync
  • Orders book shipping
  • The warehouse sync hands parcels over to shipping
  • Returns book collection with shipping
  • Shipping feeds tracking
  • Returns update orders
  • Returns refund through payments
  • Feature flags configure checkout
  • Feature flags configure search
  • Metrics watches the gateway
  • Metrics watches the message bus
  • The gateway reads feature flags
  • Sign-in reads feature flags
Discovery squad Conversion squad Delivery squad Platform squad
<div class="graph-frame" data-graph-frame>
    <div class="card">
        <div class="graph" data-graph id="pl-hulls" data-graph-hulls data-graph-arrows="target"
             aria-label="Services, and the squad that owns each">
            <ul class="graph-data" data-graph-data>
                <li data-node="search" data-group="discovery" data-tone="2" data-meta="Discovery squad">Search</li>
                <li data-node="recommendations" data-group="discovery" data-tone="2" data-meta="Discovery squad">Recommendations</li>
                <li data-node="product-pages" data-group="discovery" data-tone="2" data-meta="Discovery squad" data-hub>Product pages</li>
                <li data-node="reviews" data-group="discovery" data-tone="2" data-meta="Discovery squad">Reviews</li>
                <li data-node="wish-lists" data-group="discovery" data-tone="2" data-meta="Discovery squad">Wish lists</li>
                <li data-node="basket" data-group="conversion" data-tone="3" data-meta="Conversion squad">Basket</li>
                <li data-node="checkout" data-group="conversion" data-tone="3" data-meta="Conversion squad" data-hub>Checkout</li>
                <li data-node="vouchers" data-group="conversion" data-tone="3" data-meta="Conversion squad">Vouchers</li>
                <li data-node="payments" data-group="conversion" data-tone="3" data-meta="Conversion squad">Payments</li>
                <li data-node="fraud" data-group="conversion" data-tone="3" data-meta="Conversion squad">Fraud screening</li>
                <li data-node="orders" data-group="delivery" data-tone="5" data-meta="Delivery squad" data-hub>Orders</li>
                <li data-node="warehouse" data-group="delivery" data-tone="5" data-meta="Delivery squad">Warehouse sync</li>
                <li data-node="shipping" data-group="delivery" data-tone="5" data-meta="Delivery squad">Shipping</li>
                <li data-node="tracking" data-group="delivery" data-tone="5" data-meta="Delivery squad">Tracking</li>
                <li data-node="returns" data-group="delivery" data-tone="5" data-meta="Delivery squad">Returns</li>
                <li data-node="gateway" data-group="platform" data-tone="4" data-shape="hexagon" data-meta="Platform squad" data-hub>Gateway</li>
                <li data-node="bus" data-group="platform" data-tone="4" data-shape="diamond" data-meta="Platform squad">Message bus</li>
                <li data-node="flags" data-group="platform" data-tone="4" data-shape="hexagon" data-meta="Platform squad">Feature flags</li>
                <li data-node="metrics" data-group="platform" data-tone="4" data-shape="hexagon" data-meta="Platform squad">Metrics</li>
                <li data-node="sign-in" data-group="platform" data-tone="4" data-shape="hexagon" data-meta="Platform squad">Sign-in</li>

                <li data-edge data-source="gateway" data-target="product-pages" data-label="routes">The gateway routes to the product pages</li>
                <li data-edge data-source="gateway" data-target="checkout" data-label="routes">The gateway routes to checkout</li>
                <li data-edge data-source="gateway" data-target="sign-in" data-label="checks">The gateway checks sign-in</li>
                <li data-edge data-source="product-pages" data-target="search" data-label="calls">The product pages call search</li>
                <li data-edge data-source="product-pages" data-target="reviews" data-label="shows">The product pages show reviews</li>
                <li data-edge data-source="product-pages" data-target="recommendations" data-label="shows">The product pages show recommendations</li>
                <li data-edge data-source="recommendations" data-target="orders" data-label="reads">Recommendations read past orders</li>
                <li data-edge data-source="wish-lists" data-target="sign-in" data-label="checks">Wish lists check sign-in</li>
                <li data-edge data-source="wish-lists" data-target="product-pages" data-label="links to">Wish lists link to the product pages</li>
                <li data-edge data-source="basket" data-target="vouchers" data-label="applies">The basket applies vouchers</li>
                <li data-edge data-source="checkout" data-target="basket" data-label="reads">Checkout reads the basket</li>
                <li data-edge data-source="checkout" data-target="payments" data-label="charges">Checkout charges through payments</li>
                <li data-edge data-source="payments" data-target="fraud" data-label="screens">Payments screens with fraud screening</li>
                <li data-edge data-source="checkout" data-target="orders" data-label="places">Checkout places orders</li>
                <li data-edge data-source="orders" data-target="bus" data-label="publishes">Orders publish to the message bus</li>
                <li data-edge data-source="bus" data-target="warehouse" data-label="feeds">The message bus feeds the warehouse sync</li>
                <li data-edge data-source="orders" data-target="shipping" data-label="books">Orders book shipping</li>
                <li data-edge data-source="warehouse" data-target="shipping" data-label="hands over">The warehouse sync hands parcels over to shipping</li>
                <li data-edge data-source="returns" data-target="shipping" data-label="books">Returns book collection with shipping</li>
                <li data-edge data-source="shipping" data-target="tracking" data-label="feeds">Shipping feeds tracking</li>
                <li data-edge data-source="returns" data-target="orders" data-label="updates">Returns update orders</li>
                <li data-edge data-source="returns" data-target="payments" data-label="refunds">Returns refund through payments</li>
                <li data-edge data-source="flags" data-target="checkout" data-label="configures">Feature flags configure checkout</li>
                <li data-edge data-source="flags" data-target="search" data-label="configures">Feature flags configure search</li>
                <li data-edge data-source="metrics" data-target="gateway" data-label="watches">Metrics watches the gateway</li>
                <li data-edge data-source="metrics" data-target="bus" data-label="watches">Metrics watches the message bus</li>
                <li data-edge data-source="gateway" data-target="flags" data-label="reads">The gateway reads feature flags</li>
                <li data-edge data-source="sign-in" data-target="flags" data-label="reads">Sign-in reads feature flags</li>
            </ul>
            <div class="graph-canvas"></div>
            <div class="graph-tools" role="toolbar" aria-label="View">
                <button class="btn btn--ghost btn--sm btn--icon" type="button" data-graph-action="hulls" aria-pressed="true" aria-label="Outline the squads" data-tip="Outline the squads">
                    <i class="ri-shape-line"></i>
                </button>
                <button class="btn btn--ghost btn--sm btn--icon" type="button" data-graph-action="arrange" aria-label="Arrange again" data-tip="Arrange again"><i class="ri-magic-line"></i></button>
                <button class="btn btn--ghost btn--sm btn--icon" type="button" data-graph-action="fit" aria-label="Fit" data-tip="Fit"><i class="ri-focus-3-line"></i></button>
            </div>
            <div class="graph-wait skeleton skeleton-block"></div>
        </div>
    </div>
    <div class="graph-legend">
        <span class="graph-key"><span class="graph-swatch graph-swatch--group series-2"></span> <span>Discovery squad</span></span>
        <span class="graph-key"><span class="graph-swatch graph-swatch--group series-3"></span> <span>Conversion squad</span></span>
        <span class="graph-key"><span class="graph-swatch graph-swatch--group series-5"></span> <span>Delivery squad</span></span>
        <span class="graph-key"><span class="graph-swatch graph-swatch--group series-4"></span> <span>Platform squad</span></span>
    </div>
</div>

Exporting the drawing

Live — both buttons download. export-svg and export-png save the whole drawing — every record shown, not only the part in view — in the colours of the theme it is shown in, so a dark page exports dark. data-graph-filename on the button names the file; without it the file is named after the graph's id.

The SVG is Sedna.UI's own: each record a group with a <title>, the text as text, and every colour the one the canvas resolved from the tokens. The PNG is the engine's, at twice the resolution. From script, graph.export('svg') resolves a Blob; from C#, ExportAsync returns a data: URL and DownloadAsync saves the file.

The whole drawing, not only the part in view, in the theme it is shown in.
  • Browser
  • Content network
  • API gateway
  • Storefront
  • Checkout API
  • Payments API
  • Card processor
  • Orders API
  • Orders database
  • Order events
  • Warehouse sync
  • Order e-mails
  • The browser loads pages from the content network
  • The browser calls the API gateway
  • The content network asks the storefront on a miss
  • The gateway routes to the checkout API
  • The checkout API charges through the payments API
  • The payments API authorises the card
  • The checkout API places the order
  • The orders API writes the orders database
  • The orders API publishes an event
  • The event starts picking in the warehouse
  • The event sends the order confirmation
<div class="graph-frame" data-graph-frame>
    <div class="toolbar">
        <span class="text-sm text-muted">The whole drawing, not only the part in view, in the theme it is shown in.</span>
        <div class="toolbar-actions">
            <button class="btn btn--sm" type="button" data-graph-action="export-svg" data-graph-filename="order-flow"><i class="ri-file-code-line"></i> <span>Download SVG</span></button>
            <button class="btn btn--sm" type="button" data-graph-action="export-png" data-graph-filename="order-flow"><i class="ri-image-line"></i> <span>Download PNG</span></button>
        </div>
    </div>
    <div class="card">
        <div class="graph" data-graph id="pl-export" data-graph-layout="dagre" data-graph-direction="LR" data-graph-nodes="box" data-graph-arrows="target"
             aria-label="How an order placed on the web shop travels through its systems">
            <ul class="graph-data" data-graph-data>
                <li data-node="browser" data-tone="muted" data-icon="ri-global-line" data-meta="The shopper's browser">Browser</li>
                <li data-node="cdn" data-tone="6" data-icon="ri-cloud-line" data-meta="Static files and pages">Content network</li>
                <li data-node="gateway" data-tone="6" data-icon="ri-door-open-line" data-meta="gateway-01">API gateway</li>
                <li data-node="storefront" data-tone="2" data-icon="ri-store-2-line" data-meta="web-front-01">Storefront</li>
                <li data-node="checkout" data-tone="2" data-icon="ri-shopping-cart-2-line" data-meta="checkout-api-02">Checkout API</li>
                <li data-node="payments" data-tone="2" data-icon="ri-bank-card-line" data-meta="payments-api-01">Payments API</li>
                <li data-node="card-processor" data-tone="muted" data-icon="ri-global-line" data-meta="api.example.net">Card processor</li>
                <li data-node="orders" data-tone="2" data-icon="ri-file-list-3-line" data-meta="orders-console-01">Orders API</li>
                <li data-node="orders-db" data-tone="4" data-icon="ri-database-2-line" data-meta="src-db-14">Orders database</li>
                <li data-node="events" data-tone="5" data-icon="ri-stack-line" data-meta="bus-broker-01">Order events</li>
                <li data-node="warehouse" data-tone="3" data-icon="ri-inbox-archive-line" data-meta="warehouse-sync-01">Warehouse sync</li>
                <li data-node="mail" data-tone="3" data-icon="ri-mail-send-line" data-meta="notify-worker-02">Order e-mails</li>

                <li data-edge data-source="browser" data-target="cdn" data-label="pages">The browser loads pages from the content network</li>
                <li data-edge data-source="browser" data-target="gateway" data-label="API calls">The browser calls the API gateway</li>
                <li data-edge data-source="cdn" data-target="storefront" data-label="on a miss">The content network asks the storefront on a miss</li>
                <li data-edge data-source="gateway" data-target="checkout" data-label="routes">The gateway routes to the checkout API</li>
                <li data-edge data-source="checkout" data-target="payments" data-label="charges">The checkout API charges through the payments API</li>
                <li data-edge data-source="payments" data-target="card-processor" data-label="authorises">The payments API authorises the card</li>
                <li data-edge data-source="checkout" data-target="orders" data-label="places">The checkout API places the order</li>
                <li data-edge data-source="orders" data-target="orders-db" data-label="writes">The orders API writes the orders database</li>
                <li data-edge data-source="orders" data-target="events" data-label="publishes">The orders API publishes an event</li>
                <li data-edge data-source="events" data-target="warehouse" data-label="picks">The event starts picking in the warehouse</li>
                <li data-edge data-source="events" data-target="mail" data-label="confirms">The event sends the order confirmation</li>
            </ul>
            <div class="graph-canvas"></div>
            <div class="graph-wait skeleton skeleton-block"></div>
        </div>
    </div>
</div>

Hierarchies

data-graph-layout="dagre" lays a graph out in ranks — an org chart, a flow, a dependency tree — in the data-graph-direction given: TB, the default, LR, BT or RL. Boxes and data-graph-curve="taxi" draw the right angles of an org chart. Every layout, and switching between them, is on Graph layouts.

  • Northwind Retail
  • Engineering
  • Finance
  • Operations
  • People
  • Platform team
  • Checkout team
  • Data team
  • Accounts payable
  • Controlling
  • Warehouse
  • Customer service
  • Recruiting
  • Engineering reports to the managing director
  • Finance reports to the managing director
  • Operations reports to the managing director
  • People reports to the managing director
  • The platform team is part of engineering
  • The checkout team is part of engineering
  • The data team is part of engineering
  • Accounts payable is part of finance
  • Controlling is part of finance
  • The warehouse is part of operations
  • Customer service is part of operations
  • Recruiting is part of people
<div class="card">
    <div class="graph" data-graph id="pl-dagre" data-graph-layout="dagre" data-graph-nodes="box" data-graph-curve="taxi" data-graph-edge-labels="none"
         aria-label="How the company is organised">
        <ul class="graph-data" data-graph-data>
            <li data-node="director" data-tone="1" data-icon="ri-building-line" data-meta="Managing director" data-root>Northwind Retail</li>
            <li data-node="engineering" data-tone="2" data-icon="ri-code-s-slash-line" data-meta="Alex Fischer">Engineering</li>
            <li data-node="finance" data-tone="3" data-icon="ri-money-euro-circle-line" data-meta="Priya Nair">Finance</li>
            <li data-node="operations" data-tone="5" data-icon="ri-truck-line" data-meta="Jordan Weiss">Operations</li>
            <li data-node="people" data-tone="4" data-icon="ri-team-line" data-meta="Rin Nakamura">People</li>
            <li data-node="platform" data-tone="2">Platform team</li>
            <li data-node="checkout-team" data-tone="2">Checkout team</li>
            <li data-node="data-team" data-tone="2">Data team</li>
            <li data-node="payables" data-tone="3">Accounts payable</li>
            <li data-node="controlling" data-tone="3">Controlling</li>
            <li data-node="warehouse" data-tone="5">Warehouse</li>
            <li data-node="service" data-tone="5">Customer service</li>
            <li data-node="recruiting" data-tone="4">Recruiting</li>

            <li data-edge data-source="director" data-target="engineering">Engineering reports to the managing director</li>
            <li data-edge data-source="director" data-target="finance">Finance reports to the managing director</li>
            <li data-edge data-source="director" data-target="operations">Operations reports to the managing director</li>
            <li data-edge data-source="director" data-target="people">People reports to the managing director</li>
            <li data-edge data-source="engineering" data-target="platform">The platform team is part of engineering</li>
            <li data-edge data-source="engineering" data-target="checkout-team">The checkout team is part of engineering</li>
            <li data-edge data-source="engineering" data-target="data-team">The data team is part of engineering</li>
            <li data-edge data-source="finance" data-target="payables">Accounts payable is part of finance</li>
            <li data-edge data-source="finance" data-target="controlling">Controlling is part of finance</li>
            <li data-edge data-source="operations" data-target="warehouse">The warehouse is part of operations</li>
            <li data-edge data-source="operations" data-target="service">Customer service is part of operations</li>
            <li data-edge data-source="people" data-target="recruiting">Recruiting is part of people</li>
        </ul>
        <div class="graph-canvas"></div>
        <div class="graph-wait skeleton skeleton-block"></div>
    </div>
</div>

Nested springs

data-graph-layout="fcose" is springs that also lay out groups inside groups: hosts in racks, racks in a room, all from data-parent. Boxes keep every name inside its group's edge, which suits a diagram of a few dozen records. More on Graph layouts.

  • Server room
  • Rack A
  • Rack B
  • orders-console-01
  • src-db-14
  • src-db-11
  • build-runner-04
  • build-runner-05
  • cache-03
  • Comms room
  • firewall-01
  • core-switch-01
  • Backup vault
  • backup-store-02
  • src-db-24
  • standby-gateway-01
  • The firewall connects to the core switch
  • The core switch connects orders-console-01
  • The core switch connects src-db-14
  • orders-console-01 reads src-db-14
  • build-runner-04 reads src-db-11
  • build-runner-04 caches in cache-03
  • build-runner-05 caches in cache-03
  • src-db-14 replicates to src-db-24 in the backup vault
  • src-db-14 is backed up to backup-store-02
  • src-db-11 is backed up to backup-store-02
  • The firewall fails over to the standby gateway
<div class="card">
    <div class="graph" data-graph id="pl-fcose" data-graph-layout="fcose" data-graph-nodes="box" data-graph-arrows="target"
         aria-label="Hosts in their racks and rooms, and what connects them">
        <ul class="graph-data" data-graph-data>
            <li data-node="server-room" data-tone="2" data-meta="Room">Server room</li>
            <li data-node="rack-a" data-parent="server-room" data-tone="2" data-meta="Rack">Rack A</li>
            <li data-node="rack-b" data-parent="server-room" data-tone="2" data-meta="Rack">Rack B</li>
            <li data-node="orders-console-01" data-parent="rack-a" data-tone="2" data-icon="ri-computer-line" data-meta="192.0.2.121">orders-console-01</li>
            <li data-node="src-db-14" data-parent="rack-a" data-tone="4" data-icon="ri-database-2-line" data-meta="192.0.2.114">src-db-14</li>
            <li data-node="src-db-11" data-parent="rack-a" data-tone="4" data-icon="ri-database-2-line" data-meta="192.0.2.111">src-db-11</li>
            <li data-node="build-runner-04" data-parent="rack-b" data-tone="3" data-icon="ri-server-line" data-meta="192.0.2.104">build-runner-04</li>
            <li data-node="build-runner-05" data-parent="rack-b" data-tone="3" data-icon="ri-server-line" data-meta="192.0.2.105">build-runner-05</li>
            <li data-node="cache-03" data-parent="rack-b" data-tone="4" data-icon="ri-flashlight-line" data-meta="192.0.2.103">cache-03</li>

            <li data-node="comms-room" data-tone="6" data-meta="Room">Comms room</li>
            <li data-node="firewall-01" data-parent="comms-room" data-tone="6" data-icon="ri-shield-keyhole-line" data-meta="192.0.2.1">firewall-01</li>
            <li data-node="core-switch-01" data-parent="comms-room" data-tone="6" data-icon="ri-git-merge-line" data-meta="192.0.2.10">core-switch-01</li>

            <li data-node="vault" data-tone="5" data-meta="Room at the backup site">Backup vault</li>
            <li data-node="backup-store-02" data-parent="vault" data-tone="5" data-icon="ri-archive-drawer-line" data-meta="198.51.100.32">backup-store-02</li>
            <li data-node="src-db-24" data-parent="vault" data-tone="4" data-icon="ri-database-2-line" data-meta="198.51.100.24">src-db-24</li>
            <li data-node="standby-gateway-01" data-parent="vault" data-tone="6" data-icon="ri-door-open-line" data-meta="198.51.100.1">standby-gateway-01</li>

            <li data-edge data-source="firewall-01" data-target="core-switch-01" data-label="trunk">The firewall connects to the core switch</li>
            <li data-edge data-source="core-switch-01" data-target="orders-console-01" data-label="access">The core switch connects orders-console-01</li>
            <li data-edge data-source="core-switch-01" data-target="src-db-14" data-label="access">The core switch connects src-db-14</li>
            <li data-edge data-source="orders-console-01" data-target="src-db-14" data-label="reads">orders-console-01 reads src-db-14</li>
            <li data-edge data-source="build-runner-04" data-target="src-db-11" data-label="reads">build-runner-04 reads src-db-11</li>
            <li data-edge data-source="build-runner-04" data-target="cache-03" data-label="caches in">build-runner-04 caches in cache-03</li>
            <li data-edge data-source="build-runner-05" data-target="cache-03" data-label="caches in">build-runner-05 caches in cache-03</li>
            <li data-edge data-source="src-db-14" data-target="src-db-24" data-label="replicates" data-line="dashed">src-db-14 replicates to src-db-24 in the backup vault</li>
            <li data-edge data-source="src-db-14" data-target="backup-store-02" data-label="backs up" data-line="dotted">src-db-14 is backed up to backup-store-02</li>
            <li data-edge data-source="src-db-11" data-target="backup-store-02" data-label="backs up" data-line="dotted">src-db-11 is backed up to backup-store-02</li>
            <li data-edge data-source="firewall-01" data-target="standby-gateway-01" data-label="fails over to" data-line="dashed">The firewall fails over to the standby gateway</li>
        </ul>
        <div class="graph-canvas"></div>
        <div class="graph-wait skeleton skeleton-block"></div>
    </div>
</div>

What loads, and under which licence

Everything below ships inside the package under _content/Sedna.UI/lib/cytoscape/, with each licence beside the files it covers in licenses/. Nothing is fetched from anywhere else, and a plugin that fails to load is reported in the console while the graph goes on drawing. The pinned versions are in VENDORED.txt there and in THIRD-PARTY-NOTICES.md.

ForPackageLicenceLoaded
The canvas, and the PNG exportcytoscapeMITWith the first graph on a page.
The minimap, the SVG exportSedna.UI's ownApache-2.0With the graph module, which the first graph brings.
Drawing linkscytoscape-edgehandles, with lodash.memoize and lodash.throttleMITWhen drawing is first turned on.
Folding groupscytoscape-expand-collapseMITFor a graph with data-graph-collapse or a data-collapsed group, or at the first fold.
Outlining groupscytoscape-bubblesets, with bubblesets-js, cytoscape-layers and lodash.throttleMITWhen outlines are first turned on.
Hierarchiescytoscape-dagre, which bundles @dagrejs/dagre and @dagrejs/graphlibMITWhen the dagre layout first runs.
Nested springscytoscape-fcose, with cose-base and layout-baseMITWhen the fcose layout first runs.
Left out on purpose: cytoscape-svg It exports SVG as well, and it is licensed under GPL-3.0, which an Apache-2.0 package cannot carry without passing its terms on to every app that installs it. The SVG export is Sedna.UI's own code instead, and a copyleft plugin is out of scope whatever it offers.

Actions, events and calls

PluginOn the graphActionEventC#
Minimapcanvas[data-graph-minimap] inside it———
Drawing linksdata-graph-connect, data-graph-managedconnectsedna-graph-connectSetDrawingAsync
Folding groupsdata-graph-collapse; data-collapsed on a groupexpand, collapse, expand-all, collapse-allsedna-graph-expand, sedna-graph-collapseExpandAsync, CollapseAsync
Outlining groupsdata-graph-hullshulls—SetHullsAsync
Export—export-svg, export-png, with data-graph-filename—ExportAsync, DownloadAsync
Hierarchies, nested springsdata-graph-layout="dagre", "fcose"——LayoutAsync

From script, the handle sednaUi.graph.get(id) resolves has connect(on), collapse(id?), expand(id?), hulls(on), export(format) and download(format, filename).