ObsiGate/IMAGE_RENDERING_GUIDE.md

9.8 KiB

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)

    [<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

    ![[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

    ![[image.svg]]
    
    • Filename only, no path
    • Resolved using attachment index built at startup
  4. Standard Markdown image

    ![alt text](path/to/image.png)
    
    • 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:

# 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:

![My Image](images/test.png)

Expected:

  • Image resolves via multi-strategy resolution
  • Displays with proper styling
  • Shows placeholder if not found

Markdown:

![[Assets/Images/diagram.svg]]

Expected:

  • Resolves relative to vault root
  • SVG renders inline
  • Maintains aspect ratio

Markdown:

![[logo.png]]

Expected:

  • Searches attachment index
  • Resolves to unique match if only one exists
  • Shows placeholder if not found or ambiguous

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:

![External](https://example.com/image.png)

Expected:

  • URL preserved unchanged
  • Image loaded from external source
  • No resolution attempted

Test Case 6: Missing Image

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:

VAULT_1_ATTACHMENTS_PATH=Attachments

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:

    docker logs obsigate | grep "indexed.*attachments"
    
  2. Check attachment stats:

    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:

    VAULT_N_SCAN_ATTACHMENTS=false
    
  2. Use specific attachments folder to reduce scan scope:

    VAULT_N_ATTACHMENTS_PATH=Images
    
  3. Monitor memory usage:

    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

  • All 4 image syntaxes render correctly in markdown preview
  • Startup scan completes without blocking UI (async/background)
  • Images with filename-only wiki-links resolve via index
  • Config attachmentsPath used as priority lookup
  • Unresolved images show visible placeholder, not broken icon
  • No regression on standard markdown image syntax ![]()
  • 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.