ObsiViewer/docs/GRAPH_SETTINGS.md

9.8 KiB

Graph Settings Feature

Complete implementation of Obsidian-compatible graph settings panel for ObsiViewer.

Overview

This feature adds a comprehensive settings panel for the graph view, matching Obsidian's interface and functionality. Settings are stored in .obsidian/graph.json and synchronized in real-time with the graph rendering.

Features

  • ⚙️ Settings Button: Gear icon in top-right of graph view
  • 🎨 Settings Panel: Slide-over panel with four collapsible sections
  • 💾 Persistence: Auto-save to .obsidian/graph.json with 250ms debounce
  • 🔄 Live Sync: Watch for external file changes and reload
  • 📱 Responsive: Desktop slide-over, mobile full-screen
  • Accessible: Keyboard navigation, ARIA labels, focus management

Architecture

Files Created

src/app/graph/
├── graph-settings.types.ts          # Type definitions & utilities
├── graph-settings.service.ts        # Config read/write/watch service
├── graph-runtime-adapter.ts         # Apply settings to graph engine
└── ui/
    ├── settings-button.component.ts # Gear icon button
    ├── settings-panel.component.ts  # Main panel with sections
    └── sections/
        ├── filters-section.component.ts
        ├── groups-section.component.ts
        ├── display-section.component.ts
        └── forces-section.component.ts

server/
└── index.mjs                        # API endpoints added

Configuration Structure

graph.json Format

{
  "collapse-filter": false,
  "search": "",
  "showTags": false,
  "showAttachments": false,
  "hideUnresolved": false,
  "showOrphans": true,
  
  "collapse-color-groups": false,
  "colorGroups": [
    {
      "query": "tag:#markdown",
      "color": { "a": 1, "rgb": 14701138 }
    }
  ],
  
  "collapse-display": false,
  "showArrow": false,
  "textFadeMultiplier": 0,
  "nodeSizeMultiplier": 1,
  "lineSizeMultiplier": 1,
  
  "collapse-forces": false,
  "centerStrength": 0.5,
  "repelStrength": 10,
  "linkStrength": 1,
  "linkDistance": 250,
  
  "scale": 1,
  "close": false
}

Settings Sections

1. Filters

Controls which nodes appear in the graph.

Control JSON Key Type Description
Search search string Filter nodes by text search
Tags showTags boolean Show/hide tag nodes
Attachments showAttachments boolean Show/hide attachment files
Existing files only hideUnresolved boolean Hide unresolved links (inverted)
Orphans showOrphans boolean Show/hide orphan nodes

2. Groups

Color nodes based on queries.

  • Color Groups: Array of { query, color } objects
  • Query Types:
    • tag:#tagname - Match notes with tag
    • file:filename - Match by filename
    • path:folder - Match by folder path
  • Color Format: { a: number, rgb: number } (RGB as integer 0-16777215)
  • Actions: New group, Duplicate, Delete, Reorder (drag & drop)

3. Display

Visual appearance settings.

Control JSON Key Range Step Description
Arrows showArrow boolean - Show directional arrows on links
Text fade threshold textFadeMultiplier -3 to 3 0.1 When to fade node labels
Node size nodeSizeMultiplier 0.25 to 3 0.05 Size multiplier for nodes
Link thickness lineSizeMultiplier 0.25 to 3 0.05 Thickness multiplier for links

4. Forces

Physics simulation parameters.

Control JSON Key Range Step Description
Center force centerStrength 0 to 2 0.01 Pull toward center
Repel force repelStrength 0 to 20 0.5 Push nodes apart
Link force linkStrength 0 to 2 0.01 Pull connected nodes together
Link distance linkDistance 20 to 300 1 Target distance between linked nodes

API Endpoints

GET /api/vault/graph

Load graph configuration.

Response:

{
  "config": { /* GraphConfig */ },
  "rev": "abc123-456"
}

PUT /api/vault/graph

Save graph configuration.

Headers:

  • If-Match: Optional revision for conflict detection

Body: Complete GraphConfig object

Response:

{
  "rev": "def456-789"
}

Status Codes:

  • 200 - Success
  • 409 - Conflict (file modified externally)
  • 500 - Server error

Color Conversion

RGB Integer Format

Obsidian stores colors as integers (0-16777215):

// HEX to Integer
const hex = "#E06C75";
const rgb = hexToInt(hex); // 14707829

// Integer to RGBA
const { r, g, b, a } = intToRgba(14707829, 1);
// r: 224, g: 108, b: 117, a: 1

// To CSS
const css = colorToCss({ a: 1, rgb: 14707829 });
// "rgba(224, 108, 117, 1)"

Runtime Adapter

The GraphRuntimeAdapter translates config to runtime options:

Filters Application

const filtered = GraphRuntimeAdapter.applyFilters(
  baseGraphData,
  config,
  allNotes
);

Applies:

  • Text search on node labels
  • Tag/attachment/orphan filtering
  • Unresolved link filtering

Display Options Conversion

const displayOptions = GraphRuntimeAdapter.configToDisplayOptions(config);

Converts:

  • textFadeMultiplier (-3 to 3) → textFadeThreshold (0 to 100)
  • nodeSizeMultiplier → absolute nodeSize (base: 5px)
  • lineSizeMultiplier → absolute linkThickness (base: 1px)
  • repelStrength (0 to 20) → chargeStrength (0 to -200)

Color Groups Application

const nodeColors = GraphRuntimeAdapter.applyColorGroups(
  nodes,
  config,
  allNotes
);

Returns Map<nodeId, cssColor> for node coloring.

State Management

Service Layer

GraphSettingsService:

  • config: Signal<GraphConfig> - Current configuration
  • load() - Load from API
  • save(patch) - Save with debounce (250ms)
  • watch(callback) - Subscribe to changes
  • resetToDefaults() - Reset all settings
  • resetSection(section) - Reset specific section
  • toggleCollapse(section) - Toggle section expand/collapse

Reactive Updates

All settings changes trigger:

  1. Immediate UI update (signal update)
  2. Debounced file write (250ms)
  3. Graph re-render (via computed signals)

External Change Detection

Polls every 2 seconds to detect external file modifications and reload config.

Usage Examples

Open Settings Panel

Click the gear icon (⚙️) in top-right of graph view.

Keyboard Shortcuts

  • Esc - Close settings panel
  • Enter/Space - Activate buttons/toggles
  • Tab - Navigate controls

Programmatic Access

import { GraphSettingsService } from './app/graph/graph-settings.service';

// Inject service
constructor(private settings: GraphSettingsService) {}

// Get current config
const config = this.settings.config();

// Update settings
this.settings.save({
  showArrow: true,
  nodeSizeMultiplier: 1.5
});

// Watch for changes
this.settings.watch(config => {
  console.log('Config updated:', config);
});

// Reset
this.settings.resetToDefaults();
this.settings.resetSection('display');

Testing Checklist

Manual Tests

  • Click gear icon → panel slides in
  • All sections expand/collapse correctly
  • Each control modifies corresponding JSON key
  • Settings persist after page reload
  • External file edit → UI updates
  • Search filter works on node labels
  • Tags toggle hides/shows tag nodes
  • Existing files only hides unresolved links
  • Orphans toggle hides/shows orphan nodes
  • New color group adds with default color
  • Color picker updates group color
  • Query input filters matching nodes
  • Delete group removes from list
  • Duplicate group creates copy
  • Arrows toggle shows/hides link arrows
  • All sliders update in real-time
  • Animate button restarts simulation
  • Force sliders affect node positions
  • Reset all restores defaults
  • Reset section restores section defaults
  • Close button closes panel
  • Esc key closes panel
  • Mobile: full-screen panel
  • Dark mode: correct theming

Integration Tests

  • graph.json created on first run
  • Atomic writes prevent corruption
  • Backup file created (.bak)
  • Conflict detection works (409 response)
  • Polling detects external changes
  • Debounce prevents excessive writes
  • Invalid JSON handled gracefully
  • Missing file uses defaults

Performance

  • Debounce: 250ms write delay
  • Polling: 2-second interval
  • Atomic Writes: Temp file + rename
  • Validation: Clamps values to bounds
  • Signal-based: Minimal re-renders

Browser Compatibility

  • Chrome/Edge: Full support
  • Firefox: Full support
  • Safari: Full support
  • Mobile browsers: Responsive design

Known Limitations

  1. Drag & Drop: Groups reordering not yet implemented
  2. Scale/Close: Preserved but not exposed in UI
  3. Advanced Queries: Only basic query types supported
  4. Undo/Redo: Not implemented

Future Enhancements

  • Drag & drop for color group reordering
  • Advanced query builder UI
  • Preset configurations
  • Export/import settings
  • Undo/redo for settings changes
  • Keyboard shortcuts for common actions
  • Animation presets
  • Graph layout algorithms selector

Troubleshooting

Settings Not Saving

  • Check browser console for errors
  • Verify .obsidian directory exists
  • Check file permissions
  • Ensure server is running

Panel Not Opening

  • Check for JavaScript errors
  • Verify all components imported correctly
  • Clear browser cache

External Changes Not Detected

  • Polling interval is 2 seconds
  • Check file watcher is enabled
  • Verify file path is correct

Performance Issues

  • Reduce polling frequency
  • Increase debounce delay
  • Disable external change detection

Credits

Designed to match Obsidian graph view interface and behavior.