fix: handling of images fixed
This commit is contained in:
@@ -337,6 +337,28 @@
|
|||||||
color: var(--vscode-descriptionForeground);
|
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 {
|
.media-details {
|
||||||
width: 320px;
|
width: 320px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -672,11 +672,17 @@ const MediaEditor: React.FC<{ mediaId: string }> = ({ mediaId }) => {
|
|||||||
<div className="editor-content media-editor">
|
<div className="editor-content media-editor">
|
||||||
<div className="media-preview">
|
<div className="media-preview">
|
||||||
{item.mimeType.startsWith('image/') ? (
|
{item.mimeType.startsWith('image/') ? (
|
||||||
<div className="media-preview-placeholder">
|
<div className="media-preview-image">
|
||||||
<svg width="64" height="64" viewBox="0 0 24 24" fill="currentColor" opacity="0.3">
|
<img
|
||||||
<path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
|
src={`bds-media://${item.id}`}
|
||||||
</svg>
|
alt={item.alt || item.originalName}
|
||||||
<span>{item.originalName}</span>
|
onError={(e) => {
|
||||||
|
// Fallback to placeholder if image fails to load
|
||||||
|
const target = e.target as HTMLImageElement;
|
||||||
|
target.style.display = 'none';
|
||||||
|
target.parentElement?.classList.add('has-error');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="media-preview-placeholder">
|
<div className="media-preview-placeholder">
|
||||||
|
|||||||
@@ -207,6 +207,13 @@
|
|||||||
background-color: var(--vscode-input-background);
|
background-color: var(--vscode-input-background);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-thumbnail img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.media-item-info {
|
.media-item-info {
|
||||||
|
|||||||
@@ -580,10 +580,14 @@ const MediaList: React.FC = () => {
|
|||||||
>
|
>
|
||||||
{item.mimeType.startsWith('image/') ? (
|
{item.mimeType.startsWith('image/') ? (
|
||||||
<div className="media-thumbnail">
|
<div className="media-thumbnail">
|
||||||
{/* Would load actual image in production */}
|
<img
|
||||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="currentColor" opacity="0.5">
|
src={`bds-media://${item.id}`}
|
||||||
<path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
|
alt={item.alt || item.originalName}
|
||||||
</svg>
|
onError={(e) => {
|
||||||
|
const target = e.target as HTMLImageElement;
|
||||||
|
target.style.display = 'none';
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="media-thumbnail">
|
<div className="media-thumbnail">
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ export const WysiwygEditor: React.FC<WysiwygEditorProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
// Track if we're updating from internal changes vs external prop changes
|
// Track if we're updating from internal changes vs external prop changes
|
||||||
const isInternalChange = useRef(false);
|
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 lastExternalContent = useRef(content);
|
||||||
|
|
||||||
const editor = useEditor({
|
const editor = useEditor({
|
||||||
@@ -55,6 +57,10 @@ export const WysiwygEditor: React.FC<WysiwygEditorProps> = ({
|
|||||||
],
|
],
|
||||||
content,
|
content,
|
||||||
onUpdate: ({ editor }) => {
|
onUpdate: ({ editor }) => {
|
||||||
|
// Don't call onChange during programmatic content sync
|
||||||
|
if (isSyncingContent.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
isInternalChange.current = true;
|
isInternalChange.current = true;
|
||||||
const markdown = editor.storage.markdown.getMarkdown();
|
const markdown = editor.storage.markdown.getMarkdown();
|
||||||
onChange(markdown);
|
onChange(markdown);
|
||||||
@@ -67,7 +73,13 @@ export const WysiwygEditor: React.FC<WysiwygEditorProps> = ({
|
|||||||
if (editor && content !== lastExternalContent.current) {
|
if (editor && content !== lastExternalContent.current) {
|
||||||
// This is an external change (e.g., switching posts)
|
// This is an external change (e.g., switching posts)
|
||||||
if (!isInternalChange.current) {
|
if (!isInternalChange.current) {
|
||||||
|
// Mark that we're syncing to prevent onChange from firing
|
||||||
|
isSyncingContent.current = true;
|
||||||
editor.commands.setContent(content);
|
editor.commands.setContent(content);
|
||||||
|
// Reset sync flag after a microtask to ensure onUpdate has fired
|
||||||
|
Promise.resolve().then(() => {
|
||||||
|
isSyncingContent.current = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
lastExternalContent.current = content;
|
lastExternalContent.current = content;
|
||||||
isInternalChange.current = false;
|
isInternalChange.current = false;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; style-src 'self' 'unsafe-inline'; img-src 'self' data: file: blob:; worker-src 'self' blob:; font-src 'self' data:;" />
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; style-src 'self' 'unsafe-inline'; img-src 'self' data: file: blob: bds-media:; worker-src 'self' blob:; font-src 'self' data:;" />
|
||||||
<title>Blogging Desktop Server</title>
|
<title>Blogging Desktop Server</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
Reference in New Issue
Block a user