Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,30 @@ and paints it with `layoutIr` + `renderSvg` (`src/render.ts`); the SPA inlines t
SVG and wires click-inspect by `data-node-id` against the IR. pinhole's layout is
dagre — pure JS, no native dependency.

Where an official mark exists, a node paints it instead of a generic glyph — a
Deployment gets the Kubernetes wheel-and-helm heptagon, a Kustomization the
Flux mark, a `Helm::Release` the Helm wheel. The corpus is vendored under
`web/icons/` (30 kubernetes/community SVGs, 3 cncf/artwork marks for
Flux/Argo/Helm, licensing in `THIRD_PARTY.md`) and mapped kind by kind in
`src/icon-packs.ts`; a kind with no official icon falls through to pinhole's
keyword heuristic rather than a wrong picture.

dagre's layout is a good first draft, not a final one: drag a card to move
it, grab a containment box's corner to resize it, and both survive a reload.
What persists is a delta — `{dx,dy}` for a card, `{dw,dh}` for a box, never
an absolute position — keyed by `behold.layout.<project>.<lens>`
(`web/layout-store.js`), so the graph stays chant's and the arrangement on
top of it is yours. `↺ layout` sits beside `⤢ fit` and shows up only once
something on the current lens is hand-placed.

Type splits by purpose: mono (system stacks — ui-monospace, SF Mono,
Cascadia, JetBrains, IBM Plex) carries node ids, ARNs, statuses and counts;
sans carries labels only. Colour comes from 552 Ghostty terminal palettes run
through an OKLCH-derived token pipeline (`src/theme.ts`), so a theme switch
re-derives the whole chrome, not just the graph — and a node whose drift
status just changed pulses once in the colour it became, off under
`prefers-reduced-motion`.

## Local development

`just` lists everything. The core loop:
Expand Down
28 changes: 16 additions & 12 deletions example-argo-estate/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,22 @@ no Docker. It copies out, `npm install`s, and serves. What to look at:
both Applications to the AppProject they name (behold#222) — the join chant
lints as ARGO002, so an estate whose lint passes cannot be drawn wrong.
The entity graph carries them at the attributes tier (`?detail=3`).
- The **logical lens** on the control plane alone
(`behold serve control-plane`, zoom: logical): `namespace app-a` and
`namespace app-b` drawn as boxes even though the control plane declares no
`Namespace` object anywhere. They come from each Application's
`spec.destination.namespace` — a namespace Argo will own objects in is a
namespace the estate is committed to. Serve all three members and the apps'
Deployments and Services sit inside those boxes.

That last one is the Argo/Flux difference worth noticing: flux-estate's control
plane must declare the app namespaces itself (a Kustomization's
`targetNamespace` must already exist), while here `CreateNamespace=true` means
Argo makes them — so the box exists on the strength of the destination alone.
- The **logical lens** (zoom: logical): cluster ⊃ `namespace app-a` and
`namespace app-b`, each holding that app's Deployment + Service, plus
`namespace argocd` holding both Applications and the AppProject — drawn
even though the control plane declares no `Namespace` object anywhere. The
app namespaces come from each Application's `spec.destination.namespace`; a
namespace Argo will own objects in is a namespace the estate is committed
to, and behold#224 taught the composed estate to draw that projection.
Serve `control-plane` alone (`behold serve control-plane`, zoom: logical)
and the same boxes appear empty — the destination harvest still names them,
but there is nothing from app-a/app-b in the graph to fill them with.

That alone-vs-composed split is the Argo/Flux difference worth noticing:
flux-estate's control plane must declare the app namespaces itself (a
Kustomization's `targetNamespace` must already exist), while here
`CreateNamespace=true` means Argo makes them — so the box exists on the
strength of the destination alone.

Argo's ordering is the `argocd.argoproj.io/sync-wave` annotation, so unlike
Flux's `dependsOn` (behold#223) there is no edge to draw for it: app-a is wave
Expand Down
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@
"prepublishOnly": "npm run build"
},
"dependencies": {
"@hono/node-server": "^1.13.0",
"@hono/node-server": "^2.0.5",
"@intentius/chant": "^0.38.0",
"@intentius/pinhole": "^0.3.1",
"hono": "^4.6.0"
"hono": "^4.12.34"
},
"devDependencies": {
"@types/node": "^22.0.0",
Expand Down
Loading