fix: Editeur Excalidraw - diagramme vide (CSS + appState) et auto-save pendant l'edition (BUG-064, BUG-065)

This commit is contained in:
2026-09-18 08:59:54 -04:00
parent 7f0f64a42e
commit 2bd9dd7535
19 changed files with 436 additions and 75 deletions
+17 -14
View File
@@ -5,8 +5,11 @@
* excalidraw-editor.html. Communication between parent and iframe
* via postMessage:
*
* Parent → Iframe: init {data, theme} | theme {theme} | requestSave
* Parent → Iframe: init {data, theme} | theme {theme}
* Iframe → Parent: ready | save {data} | modified {dirty}
*
* Saves are explicit only (in-editor Save button / Ctrl+S): no autosave, so
* writing the file never triggers an `index_updated` reload of the viewer.
*/
import { api } from './auth.js';
@@ -35,7 +38,15 @@ export function renderExcalidraw(container, data, vaultName, filePath, opts = {}
iframe.src = '/static/excalidraw-editor.html?v=' + Date.now();
iframe.sandbox.add('allow-scripts');
iframe.sandbox.add('allow-same-origin');
// Let the editor's own Fullscreen button work (native Fullscreen API inside
// the sandboxed iframe).
iframe.setAttribute('allow', 'fullscreen');
iframe.setAttribute('allowfullscreen', '');
iframe.style.cssText = 'width:100%;height:100%;border:none;';
// Identify the owning document so sync.js can avoid re-rendering (and thus
// reloading) this iframe after its own save triggers an `index_updated`.
iframe.dataset.excalidrawVault = vaultName;
iframe.dataset.excalidrawPath = filePath;
// Clean container and insert iframe
container.innerHTML = '';
@@ -49,7 +60,6 @@ export function renderExcalidraw(container, data, vaultName, filePath, opts = {}
path: filePath,
isDirty: false,
ready: false,
saveTimer: null,
};
_activeEditors.set(editorId, editorState);
@@ -94,16 +104,11 @@ export function renderExcalidraw(container, data, vaultName, filePath, opts = {}
break;
case 'modified':
// No autosave: saving writes the file, which emits `index_updated` and
// reloads the viewer (visible page refresh) — and can interrupt the
// user mid-drawing. The scene is saved explicitly via the in-editor
// Save button or Ctrl+S. We only track the dirty state.
editorState.isDirty = msg.dirty === true;
// If dirty, start auto-save timer (2s debounce)
if (editorState.isDirty) {
if (editorState.saveTimer) clearTimeout(editorState.saveTimer);
editorState.saveTimer = setTimeout(() => {
if (editorState.isDirty && editorState.ready) {
iframe.contentWindow.postMessage({ type: 'requestSave' }, '*');
}
}, 2000);
}
break;
}
});
@@ -154,9 +159,7 @@ export function notifyExcalidrawThemeChange(theme) {
* Clean up an editor instance (e.g., when tab is closed).
*/
export function destroyExcalidrawEditor(editorId) {
const state = _activeEditors.get(editorId);
if (state) {
if (state.saveTimer) clearTimeout(state.saveTimer);
if (_activeEditors.has(editorId)) {
_activeEditors.delete(editorId);
}
}
+7
View File
@@ -693,6 +693,13 @@ async function reloadExternalWrite(vault, path, force = false) {
}
// Not editing: refresh the read view when it shows the written document.
if (state.currentVault === vault && state.currentPath === path) {
// Excalidraw owns its iframe: re-rendering would recreate it (visible page
// refresh) and discard the in-editor scene. Its own save already persisted
// the file, so there is nothing to reload here.
const openExcalidraw = Array.from(
document.querySelectorAll("iframe[data-excalidraw-path]")
).some((f) => f.dataset.excalidrawVault === vault && f.dataset.excalidrawPath === path);
if (openExcalidraw) return;
_invalidateActiveTabCache(vault, path);
openFile(vault, path);
}