No description
  • TypeScript 64%
  • Svelte 27.7%
  • Gherkin 7.3%
  • JavaScript 0.5%
  • Dockerfile 0.3%
  • Other 0.1%
Find a file
nocyphr d13b036f81
All checks were successful
CI / ci (push) Successful in 19s
CI / publish (push) Successful in 1m0s
Cap the opening viewport at the canvas max zoom
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>
2026-06-12 15:59:35 +03:00
.forgejo/workflows CI can be dispatched manually 2026-06-11 07:51:21 +03:00
features The open edit window lives in the URL (?artifact=classifier:name) 2026-06-11 05:38:10 +03:00
frontend Cap the opening viewport at the canvas max zoom 2026-06-12 15:59:35 +03:00
vendor Vendor ara-cli 0.2.0 wheel, base install only: image 2.4GB -> 1.1GB 2026-06-11 10:45:24 +03:00
workspaces Workspace frontend shell: Svelte 5 + fzf fuzzy search in Docker Compose (#563) 2026-06-10 14:35:59 +03:00
.dockerignore Vendor ara-cli 0.2.0 wheel, base install only: image 2.4GB -> 1.1GB 2026-06-11 10:45:24 +03:00
.gitignore Workspace frontend shell: Svelte 5 + fzf fuzzy search in Docker Compose (#563) 2026-06-10 14:35:59 +03:00
docker-compose.dev.yml Forgejo CI builds and pushes the prod image; compose splits into dev (build) and prod (pull-and-run) 2026-06-11 07:41:25 +03:00
docker-compose.yml commentary 2026-06-11 08:14:10 +03:00
Dockerfile Vendor ara-cli 0.2.0 wheel, base install only: image 2.4GB -> 1.1GB 2026-06-11 10:45:24 +03:00
Dockerfile.prod Multi-stage prod image: 1.18GB -> 496MB 2026-06-11 11:52:55 +03:00
README.md Restore README as authored 2026-06-11 12:48:16 +03:00

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.

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:

  1. 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)
  2. 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.