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
nodeIdas thefocusparameter - 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 });
}
Permalink Generation
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 navigationreach(nodeId, options)– Minimally moves camera to bring node into viewport with configurable inset; use for subtle, context-preserving navigationdeepLink(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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →