first commit
Some checks failed
Backend Tests / Static Checks (push) Has been cancelled
Backend Tests / Tests (other) (push) Has been cancelled
Backend Tests / Tests (plugin) (push) Has been cancelled
Backend Tests / Tests (server) (push) Has been cancelled
Backend Tests / Tests (store) (push) Has been cancelled
Build Canary Image / build-frontend (push) Has been cancelled
Build Canary Image / build-push (linux/amd64) (push) Has been cancelled
Build Canary Image / build-push (linux/arm64) (push) Has been cancelled
Build Canary Image / merge (push) Has been cancelled
Frontend Tests / Lint (push) Has been cancelled
Frontend Tests / Build (push) Has been cancelled
Proto Linter / Lint Protos (push) Has been cancelled
Some checks failed
Backend Tests / Static Checks (push) Has been cancelled
Backend Tests / Tests (other) (push) Has been cancelled
Backend Tests / Tests (plugin) (push) Has been cancelled
Backend Tests / Tests (server) (push) Has been cancelled
Backend Tests / Tests (store) (push) Has been cancelled
Build Canary Image / build-frontend (push) Has been cancelled
Build Canary Image / build-push (linux/amd64) (push) Has been cancelled
Build Canary Image / build-push (linux/arm64) (push) Has been cancelled
Build Canary Image / merge (push) Has been cancelled
Frontend Tests / Lint (push) Has been cancelled
Frontend Tests / Build (push) Has been cancelled
Proto Linter / Lint Protos (push) Has been cancelled
This commit is contained in:
130
web/src/components/MemoContent/PositionedImageView.tsx
Normal file
130
web/src/components/MemoContent/PositionedImageView.tsx
Normal file
@@ -0,0 +1,130 @@
|
||||
import { useMemo } from "react";
|
||||
import type { Attachment } from "@/types/proto/api/v1/attachment_service_pb";
|
||||
import { getAttachmentUrl } from "@/utils/attachment";
|
||||
|
||||
interface PositionedImageViewProps {
|
||||
imageId: string;
|
||||
className?: string;
|
||||
style?: React.CSSProperties | { cssText: string };
|
||||
alt?: string;
|
||||
}
|
||||
|
||||
// Simplified positioned image view for memo display (read-only)
|
||||
export const PositionedImageView = ({ imageId, className, style, alt }: PositionedImageViewProps) => {
|
||||
// Debug: log component props
|
||||
// console.log('PositionedImageView props:', { imageId, style });
|
||||
// Construct the URL directly using Memos standard URL structure
|
||||
// Format: /file/{attachment-name}/{filename}
|
||||
const imageUrl = useMemo(() => {
|
||||
// Extract filename from imageId if it contains path info
|
||||
// For simple IDs, we'll use a generic filename
|
||||
const filename = imageId.includes('/') ? imageId.split('/').pop() || 'image' : 'image.jpg';
|
||||
return `${window.location.origin}/file/${imageId}/${filename}`;
|
||||
}, [imageId]);
|
||||
|
||||
const containerStyle = useMemo(() => {
|
||||
const baseStyles: React.CSSProperties = {
|
||||
display: 'block',
|
||||
};
|
||||
|
||||
// Handle different style input types
|
||||
if (style) {
|
||||
if ('cssText' in style) {
|
||||
// Handle string-based styles
|
||||
const styleStr = style.cssText;
|
||||
if (styleStr.includes('text-align: left')) baseStyles.textAlign = 'left';
|
||||
if (styleStr.includes('text-align: center')) baseStyles.textAlign = 'center';
|
||||
if (styleStr.includes('text-align: right')) baseStyles.textAlign = 'right';
|
||||
if (styleStr.includes('float: left')) baseStyles.float = 'left';
|
||||
if (styleStr.includes('float: right')) baseStyles.float = 'right';
|
||||
|
||||
// Handle margin - only set if not already defined
|
||||
if (styleStr.includes('margin:') && !('margin' in baseStyles)) {
|
||||
const marginMatch = styleStr.match(/margin:\s*([^;]+)/);
|
||||
if (marginMatch) baseStyles.margin = marginMatch[1].trim();
|
||||
}
|
||||
|
||||
// Set default margin-bottom only if no margin is specified
|
||||
if (!styleStr.includes('margin:') && !('margin' in baseStyles) && !('marginBottom' in baseStyles)) {
|
||||
baseStyles.marginBottom = '1rem';
|
||||
}
|
||||
} else {
|
||||
// Handle CSSProperties object
|
||||
Object.assign(baseStyles, style);
|
||||
|
||||
// Set default margin-bottom only if no margin properties exist
|
||||
if (!('margin' in baseStyles) && !('marginBottom' in baseStyles)) {
|
||||
baseStyles.marginBottom = '1rem';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return baseStyles;
|
||||
}, [style]);
|
||||
|
||||
const imageStyle = useMemo(() => {
|
||||
const baseStyles: React.CSSProperties = {
|
||||
maxWidth: '100%',
|
||||
height: 'auto',
|
||||
borderRadius: '0.5rem',
|
||||
};
|
||||
|
||||
// Extract width/height from style if present
|
||||
if (style) {
|
||||
if ('cssText' in style) {
|
||||
const styleStr = style.cssText;
|
||||
const widthMatch = styleStr.match(/width:\s*([^;]+)/);
|
||||
const heightMatch = styleStr.match(/height:\s*([^;]+)/);
|
||||
|
||||
if (widthMatch) baseStyles.width = widthMatch[1].trim();
|
||||
if (heightMatch) baseStyles.height = heightMatch[1].trim();
|
||||
}
|
||||
}
|
||||
|
||||
return baseStyles;
|
||||
}, [style]);
|
||||
|
||||
// Debug: log image URL
|
||||
// console.log('Image URL:', imageUrl);
|
||||
|
||||
// Fallback to showing the tag if image URL cannot be constructed
|
||||
if (!imageUrl || imageUrl.includes('undefined')) {
|
||||
return (
|
||||
<span className="inline-block bg-yellow-100 text-yellow-800 px-1 rounded text-sm">
|
||||
[img:{imageId}]
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`positioned-image-wrapper ${className || ''}`}
|
||||
style={containerStyle}
|
||||
data-img-id={imageId}
|
||||
>
|
||||
<img
|
||||
src={imageUrl}
|
||||
alt={alt || `Image ${imageId}`}
|
||||
style={imageStyle}
|
||||
className="rounded-lg shadow-sm"
|
||||
loading="lazy"
|
||||
onError={(e) => {
|
||||
// Fallback if image fails to load
|
||||
const target = e.target as HTMLImageElement;
|
||||
target.style.display = 'none';
|
||||
const fallback = document.createElement('span');
|
||||
fallback.className = 'inline-block bg-red-100 text-red-800 px-1 rounded text-sm';
|
||||
fallback.textContent = `[img:${imageId}] (failed to load)`;
|
||||
target.parentNode?.appendChild(fallback);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Alternative approach: Create a hook to get attachments for the current memo
|
||||
export const useMemoAttachments = () => {
|
||||
// This would need to be implemented based on how memos are loaded
|
||||
// For now, return empty array
|
||||
return [];
|
||||
};
|
||||
Reference in New Issue
Block a user