How to Use Focus, Reach, and Deep-Linking Features in the Archify Viewer

Archify's viewer provides three core navigation primitives—focus(), reach(), and deepLink()—that let you programmatically center the camera on nodes, smoothly bring targets into view, and generate shareable URLs that restore exact view states.

The archify repository implements a lightweight, client-side diagram viewer where all navigation state lives in the browser. These three APIs in archify/viewer.mjs give developers precise control over camera positioning and user guidance without requiring server-side coordination.

Focus: Center and Highlight Specific Nodes

The focus() method centers the camera on a target node and optionally animates a highlight effect around it. This is the most aggressive navigation option—use it when you want to direct full attention to a specific element.

Under the hood, focus() performs four steps:

  • Looks up the node's world-space bounding box via viewer.graph.getNodeBounds(nodeId)
  • Computes a target camera position that centers that box
  • Animates the transition using the built-in tween engine (viewer.tween)
  • Adds a temporary visual highlight layer through viewer.overlay.addHighlight(nodeId)
// Focus on a node with full animation and highlight
const nodeId = 'product-42';
viewer.focus(nodeId, { animate: true, highlight: true });

The method signature in archify/viewer.mjs (lines 112-138) accepts an options object with:

  • animate – whether to tween the camera movement (default: false)
  • highlight – whether to flash a visual indicator around the node (default: false)

Reach: Subtle Navigation That Respects Current View

The reach() method moves the camera just enough to bring the target node into the visible viewport. Unlike focus(), it does not force the node to the center, making it ideal for subtle navigation that preserves the user's spatial context.

The key difference lies in the camera calculation: instead of centering the node, reach() computes the minimal translation that makes the node intersect the visible rectangle, respecting an optional inset margin in pixels.

// Bring a node into view with 30px padding from edges
const nodeId = 'order-7';
viewer.reach(nodeId, { inset: 30 });

As implemented in archify/viewer.mjs (lines 140-165), this method is particularly useful for:

  • Responding to search results without disorienting the user
  • Showing related nodes while maintaining the current zoom level
  • Progressive disclosure flows where you want gentle camera nudges

Deep-Linking: Create Permanent, Shareable View URLs

The deepLink() method generates a URL that encodes the current camera state and selected node identifier. These fragments enable any view state to be reopened later or shared between users.

The serialization process in archify/viewer.mjs (lines 167-190) packs:

  • The target nodeId as the focus parameter
  • Camera position via viewer.camera.toJSON() (x, y coordinates)
  • Current zoom level
// Generate a shareable URL for the current view
const nodeId = 'customer-3';
const link = viewer.deepLink(nodeId);
console.log('Share this URL:', link);
// Output: https://archify.example.com/viewer.html#focus=customer-3&zoom=2.0&x=845&y=312

Restoring Deep-Linked Views

When a page loads with a hash fragment, archify/boot.mjs (lines 45-58) automatically parses and restores the view:

// Boot sequence handles fragment restoration automatically
viewer.on('ready', () => {
  const fragment = new URLSearchParams(window.location.hash.slice(1));
  if (fragment.has('focus')) {
    const nodeId = fragment.get('focus');
    viewer.focus(nodeId, { animate: false });
  }
});

No server-side storage is required—everything lives in the URL fragment, making deep-linking work with static hosting or embedded deployments.

Architecture Overview: How Navigation Works Together

All three methods share a common foundation in archify/camera.mjs, which abstracts positioning, zoom, and serialization. The camera object is manipulated directly by focus() and reach(), while deepLink() relies on its toJSON() method for state persistence.

Component Responsibility Key Methods
archify/viewer.mjs High-level navigation API focus(), reach(), deepLink()
archify/camera.mjs Position and zoom management toJSON(), setPosition(), setZoom()
archify/graph.mjs Node geometry lookup getNodeBounds(nodeId)
archify/overlay.mjs Visual feedback addHighlight(nodeId)
archify/boot.mjs URL fragment parsing Hash detection on initialization

Practical Integration Patterns

Search Result Navigation

Combine reach() with search to show results without jarring camera jumps:

function onSearchResult(nodeId) {
  // Highlight the result list item
  highlightSearchResult(nodeId);
  // Gently bring node into view
  viewer.reach(nodeId, { inset: 50 });
}

Expose deepLink() through a share button:

document.getElementById('share-btn').addEventListener('click', () => {
  const selectedNode = viewer.selection.active;
  if (selectedNode) {
    const url = viewer.deepLink(selectedNode.id);
    navigator.clipboard.writeText(url);
    showToast('Link copied to clipboard');
  }
});

Initial Load with Context

Pre-focus based on URL parameters when embedding the viewer in dashboards:

const params = new URLSearchParams(window.location.search);
const initialNode = params.get('node');
if (initialNode) {
  viewer.once('ready', () => viewer.focus(initialNode, { animate: true }));
}

Summary

  • focus(nodeId, options) – Centers camera on node with optional animation and highlight; use for intentional, attention-directing navigation
  • reach(nodeId, options) – Minimally moves camera to bring node into viewport with configurable inset; use for subtle, context-preserving navigation
  • deepLink(nodeId) – Serializes camera state and node identifier to URL fragment; enables shareable, reproducible views without server dependency

All three APIs are implemented in archify/viewer.mjs and depend on archify/camera.mjs for state management and archify/graph.mjs for geometry queries.

Frequently Asked Questions

What is the difference between focus() and reach() in Archify?

focus() centers the target node in the viewport and optionally adds a highlight effect, making it ideal when you want to direct full user attention. reach() performs minimal camera movement—just enough to bring the node into view—preserving the user's current spatial context and zoom level. Choose focus() for intentional navigation like clicking a search result; use reach() for subtle guidance like showing related nodes.

How does deep-linking work without a server?

Archify encodes all view state directly into the URL hash fragment using viewer.camera.toJSON() and the target node identifier. When the page loads, archify/boot.mjs parses this fragment and calls viewer.focus() to restore the exact camera position. This client-only approach works with static file hosting and requires no database or backend session management.

Can I disable animation when focusing on a node?

Yes—pass animate: false in the options object: viewer.focus(nodeId, { animate: false }). This is useful when restoring deep-linked views where you want immediate positioning rather than transitional movement, as implemented in archify/boot.mjs during fragment restoration.

What happens if the node ID passed to focus() does not exist?

The viewer.graph.getNodeBounds(nodeId) lookup in archify/viewer.mjs will return undefined for invalid IDs, and the focus() method silently returns without modifying the camera. You can check node existence beforehand using viewer.graph.hasNode(nodeId) if you need explicit error handling.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →