ObsiViewer/docs/GRAPH_SETTINGS.md

390 lines
9.8 KiB
Markdown

# 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<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
```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.