fix: rebuild images with title
This commit is contained in:
@@ -958,6 +958,7 @@ export class MediaEngine extends EventEmitter {
|
|||||||
size: stats.size,
|
size: stats.size,
|
||||||
width: metadata.width,
|
width: metadata.width,
|
||||||
height: metadata.height,
|
height: metadata.height,
|
||||||
|
title: metadata.title,
|
||||||
alt: metadata.alt,
|
alt: metadata.alt,
|
||||||
caption: metadata.caption,
|
caption: metadata.caption,
|
||||||
filePath: mediaFilePath,
|
filePath: mediaFilePath,
|
||||||
@@ -973,6 +974,7 @@ export class MediaEngine extends EventEmitter {
|
|||||||
id: metadata.id,
|
id: metadata.id,
|
||||||
projectId: this.currentProjectId,
|
projectId: this.currentProjectId,
|
||||||
originalName: metadata.originalName,
|
originalName: metadata.originalName,
|
||||||
|
title: metadata.title,
|
||||||
alt: metadata.alt,
|
alt: metadata.alt,
|
||||||
caption: metadata.caption,
|
caption: metadata.caption,
|
||||||
tags: metadata.tags,
|
tags: metadata.tags,
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ import { parseMacros, getMacro } from '../../macros/registry';
|
|||||||
import { InsertModal } from '../InsertModal';
|
import { InsertModal } from '../InsertModal';
|
||||||
import './Editor.css';
|
import './Editor.css';
|
||||||
|
|
||||||
|
/** Get display name for media: prefer title over originalName */
|
||||||
|
function getMediaDisplayName(media: { title?: string; originalName: string }): string {
|
||||||
|
return media.title || media.originalName;
|
||||||
|
}
|
||||||
|
|
||||||
// Module-level AutoSaveManager for idle-time based auto-saving
|
// Module-level AutoSaveManager for idle-time based auto-saving
|
||||||
const autoSaveManager = new AutoSaveManager({
|
const autoSaveManager = new AutoSaveManager({
|
||||||
idleTimeMs: 3000, // Save after 3 seconds of idle time
|
idleTimeMs: 3000, // Save after 3 seconds of idle time
|
||||||
@@ -207,7 +212,7 @@ const hydrateGalleries = async (
|
|||||||
<img
|
<img
|
||||||
src="bds-media://${link.media.id}"
|
src="bds-media://${link.media.id}"
|
||||||
alt="${link.media.alt || link.media.originalName}"
|
alt="${link.media.alt || link.media.originalName}"
|
||||||
title="${link.media.originalName}"
|
title="${link.media.title || link.media.originalName}"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
`).join('');
|
`).join('');
|
||||||
@@ -558,7 +563,7 @@ function buildMonthGallery(
|
|||||||
<img
|
<img
|
||||||
src="bds-media://${img.id}"
|
src="bds-media://${img.id}"
|
||||||
alt="${img.alt || img.originalName}"
|
alt="${img.alt || img.originalName}"
|
||||||
title="${img.originalName}"
|
title="${img.title || img.originalName}"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
`).join('')}
|
`).join('')}
|
||||||
@@ -976,7 +981,7 @@ const PostEditor: React.FC<PostEditorProps> = ({ postId }) => {
|
|||||||
if (mediaItem) {
|
if (mediaItem) {
|
||||||
references.push({
|
references.push({
|
||||||
id: mediaItem.id,
|
id: mediaItem.id,
|
||||||
title: mediaItem.originalName,
|
title: getMediaDisplayName(mediaItem),
|
||||||
type: 'media',
|
type: 'media',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1642,7 +1647,7 @@ const MediaEditor: React.FC<{ mediaId: string }> = ({ mediaId }) => {
|
|||||||
// Show confirmation modal
|
// Show confirmation modal
|
||||||
showConfirmDeleteModal({
|
showConfirmDeleteModal({
|
||||||
itemType: 'media',
|
itemType: 'media',
|
||||||
itemTitle: item.originalName,
|
itemTitle: getMediaDisplayName(item),
|
||||||
references,
|
references,
|
||||||
onConfirm: async () => {
|
onConfirm: async () => {
|
||||||
try {
|
try {
|
||||||
@@ -1676,7 +1681,7 @@ const MediaEditor: React.FC<{ mediaId: string }> = ({ mediaId }) => {
|
|||||||
<div className="editor-header">
|
<div className="editor-header">
|
||||||
<div className="editor-tabs">
|
<div className="editor-tabs">
|
||||||
<div className="editor-tab active">
|
<div className="editor-tab active">
|
||||||
<span className="editor-tab-title">{item.originalName}</span>
|
<span className="editor-tab-title">{getMediaDisplayName(item)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="editor-actions">
|
<div className="editor-actions">
|
||||||
|
|||||||
@@ -774,6 +774,43 @@ linkedPostIds: ["post-a", "post-b", "post-c"]`;
|
|||||||
expect(postMediaInserts[1].sortOrder).toBe(1);
|
expect(postMediaInserts[1].sortOrder).toBe(1);
|
||||||
expect(postMediaInserts[2].sortOrder).toBe(2);
|
expect(postMediaInserts[2].sortOrder).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should restore title from sidecar metadata during rebuild', async () => {
|
||||||
|
const fs = await import('fs/promises');
|
||||||
|
|
||||||
|
vi.mocked(fs.readdir).mockImplementation(async (dir: string, options?: any) => {
|
||||||
|
if (typeof dir === 'string' && dir.includes('media')) {
|
||||||
|
return [
|
||||||
|
{ name: 'media-1.jpg', isFile: () => true, isDirectory: () => false },
|
||||||
|
{ name: 'media-1.jpg.meta', isFile: () => true, isDirectory: () => false },
|
||||||
|
] as any;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sidecar with title field
|
||||||
|
const sidecarContent = `id: media-1
|
||||||
|
originalName: test-image.jpg
|
||||||
|
mimeType: image/jpeg
|
||||||
|
size: 1024
|
||||||
|
title: My Beautiful Sunset Photo
|
||||||
|
alt: A sunset over the ocean
|
||||||
|
caption: Taken during vacation
|
||||||
|
createdAt: 2024-01-15T10:00:00.000Z
|
||||||
|
updatedAt: 2024-01-15T10:00:00.000Z
|
||||||
|
tags: ["nature", "sunset"]`;
|
||||||
|
mockFiles.set('/mock/userData/projects/test-project/media/media-1.jpg.meta', sidecarContent);
|
||||||
|
mockFiles.set('/mock/userData/projects/test-project/media/media-1.jpg', Buffer.from('image-data'));
|
||||||
|
|
||||||
|
await mediaEngine.rebuildDatabaseFromFiles();
|
||||||
|
|
||||||
|
// Verify title is stored in database
|
||||||
|
const insertedMedia = mockMedia.get('media-1');
|
||||||
|
expect(insertedMedia).toBeDefined();
|
||||||
|
expect(insertedMedia.title).toBe('My Beautiful Sunset Photo');
|
||||||
|
expect(insertedMedia.alt).toBe('A sunset over the ocean');
|
||||||
|
expect(insertedMedia.caption).toBe('Taken during vacation');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Full-Text Search', () => {
|
describe('Full-Text Search', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user