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 tagfile:filename
- Match by filenamepath: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
- Success409
- 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
→ absolutenodeSize
(base: 5px)lineSizeMultiplier
→ absolutelinkThickness
(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 configurationload()
- Load from APIsave(patch)
- Save with debounce (250ms)watch(callback)
- Subscribe to changesresetToDefaults()
- Reset all settingsresetSection(section)
- Reset specific sectiontoggleCollapse(section)
- Toggle section expand/collapse
Reactive Updates
All settings changes trigger:
- Immediate UI update (signal update)
- Debounced file write (250ms)
- 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 panelEnter
/Space
- Activate buttons/togglesTab
- 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
- Drag & Drop: Groups reordering not yet implemented
- Scale/Close: Preserved but not exposed in UI
- Advanced Queries: Only basic query types supported
- 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.