ObsiGate/docs/IMAGE_RENDERING_GUIDE.md
Bruno Charest 5280dc7a50 Add comprehensive documentation and analysis files
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.
2026-05-26 08:35:58 -04:00

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.