diff --git a/src/renderer/components/Editor/Editor.css b/src/renderer/components/Editor/Editor.css index 09e7d34..5cc95ae 100644 --- a/src/renderer/components/Editor/Editor.css +++ b/src/renderer/components/Editor/Editor.css @@ -337,6 +337,28 @@ color: var(--vscode-descriptionForeground); } +.media-preview-image { + max-width: 100%; + max-height: 100%; + display: flex; + align-items: center; + justify-content: center; + padding: 16px; +} + +.media-preview-image img { + max-width: 100%; + max-height: 100%; + object-fit: contain; + border-radius: 4px; +} + +.media-preview-image.has-error::after { + content: 'Image not found'; + color: var(--vscode-descriptionForeground); + font-size: 14px; +} + .media-details { width: 320px; display: flex; diff --git a/src/renderer/components/Editor/Editor.tsx b/src/renderer/components/Editor/Editor.tsx index 6603b51..076d470 100644 --- a/src/renderer/components/Editor/Editor.tsx +++ b/src/renderer/components/Editor/Editor.tsx @@ -672,11 +672,17 @@ const MediaEditor: React.FC<{ mediaId: string }> = ({ mediaId }) => {
{item.mimeType.startsWith('image/') ? ( -
- - - - {item.originalName} +
+ {item.alt { + // Fallback to placeholder if image fails to load + const target = e.target as HTMLImageElement; + target.style.display = 'none'; + target.parentElement?.classList.add('has-error'); + }} + />
) : (
diff --git a/src/renderer/components/Sidebar/Sidebar.css b/src/renderer/components/Sidebar/Sidebar.css index d274201..7b2d479 100644 --- a/src/renderer/components/Sidebar/Sidebar.css +++ b/src/renderer/components/Sidebar/Sidebar.css @@ -207,6 +207,13 @@ background-color: var(--vscode-input-background); border-radius: 4px; flex-shrink: 0; + overflow: hidden; +} + +.media-thumbnail img { + width: 100%; + height: 100%; + object-fit: cover; } .media-item-info { diff --git a/src/renderer/components/Sidebar/Sidebar.tsx b/src/renderer/components/Sidebar/Sidebar.tsx index 5dd5503..04ef53d 100644 --- a/src/renderer/components/Sidebar/Sidebar.tsx +++ b/src/renderer/components/Sidebar/Sidebar.tsx @@ -580,10 +580,14 @@ const MediaList: React.FC = () => { > {item.mimeType.startsWith('image/') ? (
- {/* Would load actual image in production */} - - - + {item.alt { + const target = e.target as HTMLImageElement; + target.style.display = 'none'; + }} + />
) : (
diff --git a/src/renderer/components/WysiwygEditor/WysiwygEditor.tsx b/src/renderer/components/WysiwygEditor/WysiwygEditor.tsx index 58670bd..d6e3f49 100644 --- a/src/renderer/components/WysiwygEditor/WysiwygEditor.tsx +++ b/src/renderer/components/WysiwygEditor/WysiwygEditor.tsx @@ -22,6 +22,8 @@ export const WysiwygEditor: React.FC = ({ }) => { // Track if we're updating from internal changes vs external prop changes const isInternalChange = useRef(false); + // Track if we're in the middle of a programmatic content sync (shouldn't trigger onChange) + const isSyncingContent = useRef(false); const lastExternalContent = useRef(content); const editor = useEditor({ @@ -55,6 +57,10 @@ export const WysiwygEditor: React.FC = ({ ], content, onUpdate: ({ editor }) => { + // Don't call onChange during programmatic content sync + if (isSyncingContent.current) { + return; + } isInternalChange.current = true; const markdown = editor.storage.markdown.getMarkdown(); onChange(markdown); @@ -67,7 +73,13 @@ export const WysiwygEditor: React.FC = ({ if (editor && content !== lastExternalContent.current) { // This is an external change (e.g., switching posts) if (!isInternalChange.current) { + // Mark that we're syncing to prevent onChange from firing + isSyncingContent.current = true; editor.commands.setContent(content); + // Reset sync flag after a microtask to ensure onUpdate has fired + Promise.resolve().then(() => { + isSyncingContent.current = false; + }); } lastExternalContent.current = content; isInternalChange.current = false; diff --git a/src/renderer/index.html b/src/renderer/index.html index b7393ca..9ac4b40 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -3,7 +3,7 @@ - + Blogging Desktop Server