- TypeScript 64%
- Svelte 27.7%
- Gherkin 7.3%
- JavaScript 0.5%
- Dockerfile 0.3%
- Other 0.1%
The root-family framing computed zoom 2.7 on the 4k tree; Svelte Flow clamps an initialViewport scale to maxZoom (2) but keeps the translation computed for 2.7 — the camera opened ~40k px away from the businessgoal, showing nothing but a passing edge. Teleports were unaffected (setViewport does not clamp). - familyWidthFitViewport takes an optional maxZoom; capped, the family floats centered instead of touching the sides (the centering x-term reduces to the old edge-to-edge value when uncapped) - initialFamilyViewport caps at the new CANVAS_MAX_ZOOM (2), which is also pinned explicitly on SvelteFlow so cap and clamp cannot drift - verification now asserts the root card's bounding rect is inside the window — the previous check only counted mounted nodes, which is how the bug slipped through Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|---|---|---|
| .forgejo/workflows | ||
| features | ||
| frontend | ||
| vendor | ||
| workspaces | ||
| .dockerignore | ||
| .gitignore | ||
| docker-compose.dev.yml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| Dockerfile.prod | ||
| README.md | ||
open ARA Tree Viewer
An opinionated specification tree visualizer - Talsen Team flavor.
Drop your ARA workspaces into a folder, run one command, and see every artifact as a living, connected tree. File changes stream in real time. No fragile UI state, no dropped selections, no refresh button.
Why
One menu, three keystrokes, zero friction. Mount your workspaces folder and you're done. Selections survive edits, the tree stays consistent no matter how fast the files underneath it change, and everything updates live.
Quick start
# 1. Create a workspaces folder with your ARA projects
mkdir -p workspaces/my-project/ara
# 2. Start the viewer
docker compose up
# 3. Open http://localhost:5173
That's it. The container is ~50 MB compressed - it's done pulling before you finish reading this.
Features
Workspace browser
Ctrl+K opens the workspace menu. Fuzzy search via fzf - type a few characters and the list narrows. Arrow keys move through results, Enter selects, Escape backs out. The menu remembers your position when the list changes underneath you.
New folders appear. Deleted folders disappear. No refresh needed - a file watcher pushes every change to the browser in real time.
Tree visualization
Every artifact is a card. Cards are connected by their "Contributes to" links into a left-to-right value contribution chain: Business Goal, Vision, Capability, Key Feature, Feature, down to Tasks and Examples. Each type gets its own border color and single-letter badge. Status icons and priority colors are pulled from the artifact's tag line - change a status on disk and the card updates instantly.
Teleport
Press Enter on the tree to open the artifact search. Type a name, then arrow through the results - the viewport animates to center each match as you scroll. Escape snaps back to where you started. Enter opens the artifact for editing right there.
Artifact search
The Enter-search understands types and tags. Type a type name ("businessgoal", "task") to filter by type. Type @user, @status, @priority, or @tag to chain filters - pick a value from the active pool. Filters accumulate. Backspace removes them one layer at a time. If your type pill has no match for what you typed, Enter creates a new artifact of that type with that name.
Structured editing
Double-click any card to open the edit panel. Every artifact type gets a purpose-built form:
- Business Goal: title, in-order-to, as-a, i-want, description
- Vision: large description area with contributing fields
- Capability: description entry with contributes-to dropdown
- Epic / User Story: expandable rules list - each rule is its own editable field, Shift+Enter adds another
- Feature: Gherkin scenario blocks with structured "As a... I want..." fields
- Task: status dropdown, priority toggle, creator field, sub-todo checklist with Shift+Enter to add
The title field includes the type marker ("Task:", "Feature:") - editing it renames the artifact file on save. The contributes-to dropdown shows only valid parent types per the tree definition.
Artifacts with content the structured editor can't parse fall back to a lossless raw editor - your file is preserved exactly as-is with only the fields you touched changed.
Enter submits (blocked until required fields are filled). Escape discards and closes. Tab jumps between fields.
Connections
Hover any card - grab handles appear. Drag from one to another to create a Contributes-to link. Edges are always smoothstep with arrowheads, matching the auto-parsed ones. Hierarchy decides the direction, not the drag: connecting a Vision to a Business Goal always points Vision → Business Goal, regardless of which way you dragged.
Invalid connections are silently rejected. Each artifact has exactly one parent - re-parenting removes the old edge. Tasks are always leaves.
Multi-delete
Shift+drag to box-select multiple cards. Shift+click to add or remove individual ones. Press Delete, confirm, and they're gone.
Keyboard-only operation
| Key | Action |
|---|---|
| Ctrl+K | Open workspace menu |
| Enter | Select workspace / open artifact search / start tree |
| Escape | Close panel / discard changes / back out one layer |
| Arrow keys | Navigate search results / artifact search (teleport) |
| Tab | Jump between form fields |
| Delete | Delete selected artifacts (with confirmation) |
| Shift+drag/click | Multi-select on canvas |
URL state
Every workspace selection and open artifact lives in the URL. Share a link and the recipient opens the same workspace with the same artifact in the edit panel. Browser back/forward navigates through your session.
How it works
docker compose up
│
▼
┌─────────────────────────────────────┐
│ node:22-slim container (50 MB) │
│ │
│ src/server/main.ts │
│ ├─ sirv serves dist/ (built once)│
│ └─ API: workspaces, artifacts, │
│ fzf filtering, SSE watchers │
│ │
│ ara-cli (vendored wheel) │
│ └─ artifact CRUD on disk │
│ │
│ fzf + grep │
│ └─ server-side fuzzy search │
│ │
│ Svelte 5 + @xyflow/svelte │
│ └─ built to dist/, served static │
└─────────────────────────────────────┘
│ volume mount
▼
./workspaces/
├── project-a/
│ └── ara/
│ ├── businessgoal/
│ ├── vision/
│ └── ...
└── project-b/
└── ara/
└── ...
The server watches the workspaces directory with inotify. When any artifact file changes - created, deleted, renamed, edited - it recomputes the tree and pushes the update to the browser via Server-Sent Events. The Svelte Flow canvas redraws from the new data. Status changes from external tools, renames from the edit window, new artifacts from the CLI - everything appears without a page refresh.
Deployment
The production image is a two-stage Docker build:
- Build stage: compiles the Svelte frontend to static dist/ and prunes node_modules to production-only (connect, sirv, tsx - no Vite, no Svelte, no dev deps)
- Runtime stage: node:22-slim with fzf, python3, and the vendored ara-cli wheel
The node user (uid 1000) owns the process, so files created in the mounted workspaces volume belong to the host user, not root.
# docker-compose.yml
services:
artifact-viewer:
image: git.nocyphr.com/nocyphr/open-ara-tree-viewer:latest
restart: unless-stopped
ports:
- "5173:5173"
volumes:
- ./workspaces:/project/workspaces
Development
# Dev mode with Vite HMR
cd frontend
npm ci
npm run dev
# Run tests
npm test
# Build and run production server locally
npm run build
npm start
Tech stack: Svelte 5 (runes), TypeScript, Svelte Flow (@xyflow/svelte), fuse.js (client-side fuzzy search), connect + sirv (production server), Chai + Mocha (testing), Forgejo CI (builds and pushes the prod image on every commit).
About the name
"ARA" is the artifact tree format used at Talsen Team - specification artifacts linked by "Contributes to" relationships. This viewer renders that tree as an interactive mind map. It's opinionated because it makes choices: keyboard-first, file-first, real-time, minimal UI. If you want a different flavor, fork it.