From 9804cf9a6dfc5538cd8f5ac4b3a8e188d2e4b233 Mon Sep 17 00:00:00 2001 From: bruno Date: Wed, 29 Jul 2026 16:08:03 -0400 Subject: [PATCH] feat(excalidraw): support .excalidraw.md (Obsidian plugin format) with lz-string decompression --- backend/indexer.py | 1 + backend/main.py | 24 +++++++++++++++++++++++- frontend/excalidraw-editor.html | 25 ++++++++++++++++++++----- frontend/js/excalidraw-viewer.js | 11 ++++++----- frontend/js/utils.js | 5 +++++ 5 files changed, 55 insertions(+), 11 deletions(-) diff --git a/backend/indexer.py b/backend/indexer.py index 2b6384e..edbc326 100644 --- a/backend/indexer.py +++ b/backend/indexer.py @@ -64,6 +64,7 @@ SUPPORTED_EXTENSIONS = { ".php", ".sql", ".r", ".m", ".swift", ".kt", ".dockerfile", ".makefile", ".cmake", ".excalidraw", + ".excalidraw.md", } diff --git a/backend/main.py b/backend/main.py index d67ad16..6cffc0f 100644 --- a/backend/main.py +++ b/backend/main.py @@ -124,6 +124,7 @@ class FileContentResponse(BaseModel): is_json: bool | None = Field(default=None, description="True for JSON files") is_excalidraw: bool | None = Field(default=None, description="True for Excalidraw diagram files") excalidraw_data: dict[str, Any] | None = Field(default=None, description="Excalidraw diagram data (elements, appState, files)") + excalidraw_data_compressed: str | None = Field(default=None, description="Compressed Excalidraw data for .excalidraw.md files") pdf_metadata: dict[str, Any] | None = Field(default=None, description="PDF metadata") pdf_toc: list[dict[str, Any]] | None = Field(default=None, description="PDF table of contents") image_mime: str | None = Field(default=None, description="MIME type for image files") @@ -2505,7 +2506,28 @@ async def api_file(vault_name: str, path: str = Query(..., description="Relative "is_markdown": False, "is_json": True, } - # === Excalidraw: diagram editor === + # === Excalidraw .excalidraw.md (Obsidian plugin format) === + if path.lower().endswith(".excalidraw.md"): + import re as re_mod + raw_lower = file_path.read_text(encoding="utf-8", errors="replace") + # Check for excalidraw-plugin in frontmatter or body + if "excalidraw-plugin:" in raw_lower: + # Extract compressed JSON block + match = re_mod.search(r'```compressed-json\n(.*?)\n```', raw_lower, re_mod.DOTALL) + if match: + compressed = match.group(1).strip() + return { + "vault": vault_name, "path": path, + "title": file_path.name.replace(".excalidraw.md", ""), + "tags": [], "frontmatter": {}, + "html": "", "raw_length": len(raw_lower), + "extension": ".excalidraw.md", + "is_markdown": False, + "is_excalidraw": True, + "excalidraw_data_compressed": compressed, + } + # Fallback: treat as regular markdown + raw = raw_lower if ext == ".excalidraw": import json as json_mod try: diff --git a/frontend/excalidraw-editor.html b/frontend/excalidraw-editor.html index 375dfbe..6cea14e 100644 --- a/frontend/excalidraw-editor.html +++ b/frontend/excalidraw-editor.html @@ -107,6 +107,7 @@