# 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 ```json { "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:** ```json { "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:** ```json { "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): ```typescript // 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 ```typescript const filtered = GraphRuntimeAdapter.applyFilters( baseGraphData, config, allNotes ); ``` Applies: - Text search on node labels - Tag/attachment/orphan filtering - Unresolved link filtering ### Display Options Conversion ```typescript 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 ```typescript const nodeColors = GraphRuntimeAdapter.applyColorGroups( nodes, config, allNotes ); ``` Returns `Map` for node coloring. ## State Management ### Service Layer **GraphSettingsService**: - `config: Signal` - 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 ```typescript 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](https://obsidian.md/) graph view interface and behavior.