Add extensive project documentation including analysis review, image rendering changelog and guide, contributing guidelines, hidden files configuration guide, PWA documentation suite, roadmap, and dashboard specification.
381 lines
9.8 KiB
Markdown
381 lines
9.8 KiB
Markdown
# Obsidian Image Rendering - Implementation Guide
|
|
|
|
## Overview
|
|
|
|
ObsiGate now supports comprehensive Obsidian-compatible image rendering with intelligent multi-strategy path resolution. This document provides implementation details, testing guidance, and troubleshooting tips.
|
|
|
|
---
|
|
|
|
## Features Implemented
|
|
|
|
### ✅ Supported Image Syntaxes
|
|
|
|
1. **Standard Markdown with HTML attributes** (Obsidian-compatible)
|
|
```markdown
|
|
[<img width="180" height="60" src="path/to/image.svg"/>](https://example.com)
|
|
```
|
|
- Preserves `width` and `height` attributes
|
|
- Maintains clickable link wrapper
|
|
- Resolves `src` through the resolution pipeline
|
|
|
|
2. **Obsidian wiki-link embed with full path**
|
|
```markdown
|
|
![[06_Boite_a_Outils/6.2_Attachments/image.svg]]
|
|
```
|
|
- Full vault-relative path
|
|
- Resolves relative to vault root
|
|
|
|
3. **Obsidian wiki-link embed with filename only**
|
|
```markdown
|
|
![[image.svg]]
|
|
```
|
|
- Filename only, no path
|
|
- Resolved using attachment index built at startup
|
|
|
|
4. **Standard Markdown image**
|
|
```markdown
|
|

|
|
```
|
|
- Goes through multi-strategy resolution pipeline
|
|
- External URLs (http://, https://) are preserved unchanged
|
|
|
|
### ✅ Attachment Index
|
|
|
|
- **Startup scan**: Asynchronous scan of all vaults for image files
|
|
- **Supported formats**: `.png`, `.jpg`, `.jpeg`, `.gif`, `.svg`, `.webp`, `.bmp`, `.ico`
|
|
- **Index structure**: `{vault_name: {filename_lower: [absolute_path, ...]}}`
|
|
- **Resolution cache**: Results cached per vault + filename for performance
|
|
- **Logging**: Number of attachments indexed per vault logged at startup
|
|
|
|
### ✅ Multi-Strategy Path Resolution
|
|
|
|
Priority order (stops at first successful resolution):
|
|
|
|
| Priority | Strategy | Description |
|
|
|----------|----------|-------------|
|
|
| 1 | Absolute path | If path is absolute and file exists |
|
|
| 2 | Config attachments folder | Resolve relative to `VAULT_N_ATTACHMENTS_PATH` |
|
|
| 3 | Startup index (unique match) | Lookup filename in index; use if only one match |
|
|
| 4 | Same directory | Resolve relative to current markdown file's directory |
|
|
| 5 | Vault root relative | Resolve relative to vault root |
|
|
| 6 | Startup index (closest match) | If multiple matches, pick best path match |
|
|
| 7 | Fallback | Display styled placeholder with tooltip |
|
|
|
|
### ✅ Configuration Schema
|
|
|
|
New environment variables per vault:
|
|
|
|
```bash
|
|
# Required
|
|
VAULT_1_NAME=MyVault
|
|
VAULT_1_PATH=/vaults/MyVault
|
|
|
|
# Optional - Image configuration
|
|
VAULT_1_ATTACHMENTS_PATH=06_Boite_a_Outils/6.2_Attachments # Relative path
|
|
VAULT_1_SCAN_ATTACHMENTS=true # Default: true
|
|
```
|
|
|
|
### ✅ API Endpoints
|
|
|
|
**Serve Image**
|
|
```
|
|
GET /api/image/{vault_name}?path=relative/path/to/image.png
|
|
```
|
|
- Returns image with proper MIME type
|
|
- Supports all common image formats
|
|
- Path traversal protection
|
|
|
|
**Rescan Vault Attachments**
|
|
```
|
|
POST /api/attachments/rescan/{vault_name}
|
|
```
|
|
- Clears cache for the vault
|
|
- Re-scans vault directory for images
|
|
- Returns attachment count
|
|
|
|
**Attachment Statistics**
|
|
```
|
|
GET /api/attachments/stats?vault={vault_name}
|
|
```
|
|
- Returns attachment counts per vault
|
|
- Optional vault filter
|
|
|
|
### ✅ Frontend Styling
|
|
|
|
**Image Rendering**
|
|
- Images displayed with `max-width: 100%` for responsiveness
|
|
- Rounded corners and subtle shadow
|
|
- Hover effect on linked images
|
|
|
|
**Placeholder for Missing Images**
|
|
- Styled error box with dashed border
|
|
- Shows filename in monospace font
|
|
- Tooltip displays full path
|
|
- Red color scheme for visibility
|
|
|
|
---
|
|
|
|
## Testing Guide
|
|
|
|
### Test Case 1: Standard Markdown Image
|
|
|
|
**Markdown:**
|
|
```markdown
|
|

|
|
```
|
|
|
|
**Expected:**
|
|
- Image resolves via multi-strategy resolution
|
|
- Displays with proper styling
|
|
- Shows placeholder if not found
|
|
|
|
### Test Case 2: Wiki-link with Full Path
|
|
|
|
**Markdown:**
|
|
```markdown
|
|
![[Assets/Images/diagram.svg]]
|
|
```
|
|
|
|
**Expected:**
|
|
- Resolves relative to vault root
|
|
- SVG renders inline
|
|
- Maintains aspect ratio
|
|
|
|
### Test Case 3: Wiki-link with Filename Only
|
|
|
|
**Markdown:**
|
|
```markdown
|
|
![[logo.png]]
|
|
```
|
|
|
|
**Expected:**
|
|
- Searches attachment index
|
|
- Resolves to unique match if only one exists
|
|
- Shows placeholder if not found or ambiguous
|
|
|
|
### Test Case 4: HTML Image in Link
|
|
|
|
**Markdown:**
|
|
```markdown
|
|
[<img width="200" height="100" src="banner.jpg"/>](https://example.com)
|
|
```
|
|
|
|
**Expected:**
|
|
- Preserves width and height attributes
|
|
- Image is clickable and links to URL
|
|
- Resolves banner.jpg through resolution pipeline
|
|
|
|
### Test Case 5: External Image URL
|
|
|
|
**Markdown:**
|
|
```markdown
|
|

|
|
```
|
|
|
|
**Expected:**
|
|
- URL preserved unchanged
|
|
- Image loaded from external source
|
|
- No resolution attempted
|
|
|
|
### Test Case 6: Missing Image
|
|
|
|
**Markdown:**
|
|
```markdown
|
|
![[nonexistent.png]]
|
|
```
|
|
|
|
**Expected:**
|
|
- Displays: `[image not found: nonexistent.png]`
|
|
- Styled with red dashed border
|
|
- Tooltip shows full attempted path
|
|
|
|
### Test Case 7: Attachments Path Priority
|
|
|
|
**Setup:**
|
|
```bash
|
|
VAULT_1_ATTACHMENTS_PATH=Attachments
|
|
```
|
|
|
|
**Markdown:**
|
|
```markdown
|
|
![[photo.jpg]]
|
|
```
|
|
|
|
**Expected:**
|
|
- Checks `Attachments/photo.jpg` first (strategy 2)
|
|
- Falls back to index search if not found
|
|
- Logs resolution strategy used
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Images Not Displaying
|
|
|
|
**Symptom:** Images show as placeholders even though files exist
|
|
|
|
**Checks:**
|
|
1. Verify attachment index was built at startup:
|
|
```bash
|
|
docker logs obsigate | grep "indexed.*attachments"
|
|
```
|
|
|
|
2. Check attachment stats:
|
|
```bash
|
|
curl http://localhost:2020/api/attachments/stats
|
|
```
|
|
|
|
3. Verify file permissions (Docker must be able to read images)
|
|
|
|
4. Check if image extension is supported (see `IMAGE_EXTENSIONS` in `attachment_indexer.py`)
|
|
|
|
**Solution:**
|
|
- Rescan attachments: `curl -X POST http://localhost:2020/api/attachments/rescan/VaultName`
|
|
- Check Docker volume mounts in `docker-compose.yml`
|
|
- Verify `VAULT_N_SCAN_ATTACHMENTS` is not set to `false`
|
|
|
|
### Attachment Scan Disabled
|
|
|
|
**Symptom:** Log shows "attachment scanning disabled"
|
|
|
|
**Cause:** `VAULT_N_SCAN_ATTACHMENTS=false` in environment
|
|
|
|
**Solution:**
|
|
- Remove the variable or set to `true`
|
|
- Restart container: `docker-compose restart obsigate`
|
|
|
|
### Wrong Image Resolved (Multiple Matches)
|
|
|
|
**Symptom:** Image with common filename resolves to wrong file
|
|
|
|
**Cause:** Multiple files with same name in different directories
|
|
|
|
**Solution:**
|
|
1. Use full path syntax: `![[folder/subfolder/image.png]]`
|
|
2. Configure `VAULT_N_ATTACHMENTS_PATH` to prioritize specific folder
|
|
3. Rename files to be unique
|
|
|
|
### Performance Issues with Large Vaults
|
|
|
|
**Symptom:** Slow startup or high memory usage
|
|
|
|
**Cause:** Large number of images being indexed
|
|
|
|
**Optimization:**
|
|
1. Disable scanning for vaults without images:
|
|
```bash
|
|
VAULT_N_SCAN_ATTACHMENTS=false
|
|
```
|
|
|
|
2. Use specific attachments folder to reduce scan scope:
|
|
```bash
|
|
VAULT_N_ATTACHMENTS_PATH=Images
|
|
```
|
|
|
|
3. Monitor memory usage:
|
|
```bash
|
|
docker stats obsigate
|
|
```
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
### Module Structure
|
|
|
|
```
|
|
backend/
|
|
├── attachment_indexer.py # Image scanning and indexing
|
|
├── image_processor.py # Markdown preprocessing
|
|
├── indexer.py # Vault indexing (updated)
|
|
└── main.py # API endpoints (updated)
|
|
```
|
|
|
|
### Data Flow
|
|
|
|
```
|
|
Startup:
|
|
├─ indexer.build_index()
|
|
│ ├─ Scans markdown files
|
|
│ └─ Calls attachment_indexer.build_attachment_index()
|
|
└─ attachment_indexer builds image index per vault
|
|
|
|
Rendering:
|
|
├─ User requests markdown file
|
|
├─ main._render_markdown() called
|
|
│ ├─ image_processor.preprocess_images()
|
|
│ │ ├─ Detects all 4 image syntaxes
|
|
│ │ ├─ Calls resolve_image_path() for each
|
|
│ │ └─ Transforms to /api/image/{vault}?path=...
|
|
│ └─ mistune renders to HTML
|
|
└─ Frontend displays with styled images
|
|
|
|
Image Serving:
|
|
├─ Browser requests /api/image/{vault}?path=...
|
|
├─ main.api_image() validates and resolves path
|
|
├─ Determines MIME type
|
|
└─ Returns image bytes with proper content-type
|
|
```
|
|
|
|
### Resolution Cache
|
|
|
|
- **Key:** `(vault_name, image_src)`
|
|
- **Value:** `Optional[Path]` (resolved absolute path or None)
|
|
- **Invalidation:** On vault rescan
|
|
- **Thread-safe:** Protected by `_attachment_lock`
|
|
|
|
---
|
|
|
|
## Performance Characteristics
|
|
|
|
| Operation | Complexity | Notes |
|
|
|-----------|-----------|-------|
|
|
| Attachment scan | O(n) | n = number of files in vault |
|
|
| Image resolution (cached) | O(1) | Hash table lookup |
|
|
| Image resolution (uncached) | O(k) | k = number of strategies (max 7) |
|
|
| Rescan vault | O(n) | Rebuilds index for one vault |
|
|
|
|
**Memory Usage:**
|
|
- ~100 bytes per indexed image (filename + path)
|
|
- Resolution cache grows with unique image references
|
|
- Cache cleared on rescan
|
|
|
|
---
|
|
|
|
## Future Enhancements
|
|
|
|
Potential improvements for future versions:
|
|
|
|
1. **Lazy loading**: Only index images when first accessed
|
|
2. **Image thumbnails**: Generate and cache thumbnails for large images
|
|
3. **Image metadata**: Extract and display EXIF data
|
|
4. **Batch rescan**: Rescan all vaults with one command
|
|
5. **File watcher**: Auto-rescan on filesystem changes
|
|
6. **Image optimization**: Compress images on-the-fly
|
|
7. **CDN support**: Serve images from external CDN
|
|
|
|
---
|
|
|
|
## Acceptance Criteria Status
|
|
|
|
- [x] All 4 image syntaxes render correctly in markdown preview
|
|
- [x] Startup scan completes without blocking UI (async/background)
|
|
- [x] Images with filename-only wiki-links resolve via index
|
|
- [x] Config `attachmentsPath` used as priority lookup
|
|
- [x] Unresolved images show visible placeholder, not broken icon
|
|
- [x] No regression on standard markdown image syntax `![]()`
|
|
- [x] Rescan command works and updates display without restart
|
|
|
|
---
|
|
|
|
## Version Information
|
|
|
|
**Implementation Date:** 2025
|
|
**ObsiGate Version:** 1.2.0 (pending)
|
|
**Python Version:** 3.11+
|
|
**Dependencies:** No new dependencies required
|
|
|
|
---
|
|
|
|
*For questions or issues, refer to the main README.md or open an issue on the project repository.*
|