feat: added field "title" and switched to it to free up caption for its normal use
This commit is contained in:
@@ -54,6 +54,7 @@ export const media = sqliteTable('media', {
|
||||
size: integer('size').notNull(),
|
||||
width: integer('width'),
|
||||
height: integer('height'),
|
||||
title: text('title'),
|
||||
alt: text('alt'),
|
||||
caption: text('caption'),
|
||||
filePath: text('file_path').notNull(),
|
||||
|
||||
@@ -313,7 +313,7 @@ Available Tools:
|
||||
- list_media: List media files with optional MIME type filtering.
|
||||
- view_image: View an image to analyze its visual content. Use this when you need to describe or analyze what an image looks like.
|
||||
- update_post_metadata: Update a post's title, excerpt, tags, or categories.
|
||||
- update_media_metadata: Update a media file's alt text, caption, or tags.
|
||||
- update_media_metadata: Update a media file's title, alt text, caption, or tags.
|
||||
- list_tags: List all tags with post counts.
|
||||
- list_categories: List all categories with post counts.
|
||||
- get_post_backlinks: Get posts that link TO a given post (backlinks). Use to discover what references a post.
|
||||
|
||||
@@ -573,7 +573,7 @@ export class ImportExecutionEngine extends EventEmitter {
|
||||
// Import the media file
|
||||
const mediaEngine = getMediaEngine();
|
||||
await mediaEngine.importMedia(sourcePath, {
|
||||
caption: wxrMedia.title || undefined,
|
||||
title: wxrMedia.title || undefined,
|
||||
alt: wxrMedia.description || undefined,
|
||||
mimeType: wxrMedia.mimeType,
|
||||
tags: [],
|
||||
|
||||
@@ -27,6 +27,7 @@ export interface MediaData {
|
||||
size: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
title?: string;
|
||||
alt?: string;
|
||||
caption?: string;
|
||||
createdAt: Date;
|
||||
@@ -42,6 +43,7 @@ export interface MediaMetadata {
|
||||
size: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
title?: string;
|
||||
alt?: string;
|
||||
caption?: string;
|
||||
createdAt: string;
|
||||
@@ -61,7 +63,7 @@ export interface MediaFilter {
|
||||
export interface MediaSearchResult {
|
||||
id: string;
|
||||
originalName: string;
|
||||
caption?: string;
|
||||
title?: string;
|
||||
mimeType: string;
|
||||
createdAt: Date;
|
||||
}
|
||||
@@ -92,12 +94,13 @@ export class MediaEngine extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Update the FTS index for a media item.
|
||||
* Stores stemmed content from original_name, alt, caption, and tags.
|
||||
* Stores stemmed content from original_name, title, alt, caption, and tags.
|
||||
*/
|
||||
private async updateFTSIndex(item: {
|
||||
id: string;
|
||||
projectId: string;
|
||||
originalName: string;
|
||||
title?: string;
|
||||
alt?: string;
|
||||
caption?: string;
|
||||
tags: string[];
|
||||
@@ -111,6 +114,7 @@ export class MediaEngine extends EventEmitter {
|
||||
// Combine all searchable fields and stem them
|
||||
const allText = [
|
||||
item.originalName,
|
||||
item.title || '',
|
||||
item.alt || '',
|
||||
item.caption || '',
|
||||
item.tags.join(' '),
|
||||
@@ -300,6 +304,7 @@ export class MediaEngine extends EventEmitter {
|
||||
size: mediaData.size,
|
||||
width: mediaData.width,
|
||||
height: mediaData.height,
|
||||
title: mediaData.title,
|
||||
alt: mediaData.alt,
|
||||
caption: mediaData.caption,
|
||||
createdAt: mediaData.createdAt.toISOString(),
|
||||
@@ -319,6 +324,7 @@ export class MediaEngine extends EventEmitter {
|
||||
|
||||
if (metadata.width) lines.push(`width: ${metadata.width}`);
|
||||
if (metadata.height) lines.push(`height: ${metadata.height}`);
|
||||
if (metadata.title) lines.push(`title: "${metadata.title}"`);
|
||||
if (metadata.alt) lines.push(`alt: "${metadata.alt}"`);
|
||||
if (metadata.caption) lines.push(`caption: "${metadata.caption}"`);
|
||||
|
||||
@@ -385,6 +391,9 @@ export class MediaEngine extends EventEmitter {
|
||||
case 'height':
|
||||
metadata.height = parseInt(value, 10);
|
||||
break;
|
||||
case 'title':
|
||||
metadata.title = value;
|
||||
break;
|
||||
case 'alt':
|
||||
metadata.alt = value;
|
||||
break;
|
||||
@@ -492,6 +501,7 @@ export class MediaEngine extends EventEmitter {
|
||||
size: sourceBuffer.length,
|
||||
width,
|
||||
height,
|
||||
title: metadata?.title,
|
||||
alt: metadata?.alt,
|
||||
caption: metadata?.caption,
|
||||
createdAt,
|
||||
@@ -518,6 +528,7 @@ export class MediaEngine extends EventEmitter {
|
||||
size: mediaData.size,
|
||||
width: mediaData.width,
|
||||
height: mediaData.height,
|
||||
title: mediaData.title,
|
||||
alt: mediaData.alt,
|
||||
caption: mediaData.caption,
|
||||
filePath: destPath,
|
||||
@@ -535,6 +546,7 @@ export class MediaEngine extends EventEmitter {
|
||||
id: mediaData.id,
|
||||
projectId: this.currentProjectId,
|
||||
originalName: mediaData.originalName,
|
||||
title: mediaData.title,
|
||||
alt: mediaData.alt,
|
||||
caption: mediaData.caption,
|
||||
tags: mediaData.tags,
|
||||
@@ -566,6 +578,7 @@ export class MediaEngine extends EventEmitter {
|
||||
|
||||
await db.update(media)
|
||||
.set({
|
||||
title: updated.title,
|
||||
alt: updated.alt,
|
||||
caption: updated.caption,
|
||||
updatedAt: updated.updatedAt,
|
||||
@@ -578,6 +591,7 @@ export class MediaEngine extends EventEmitter {
|
||||
id: updated.id,
|
||||
projectId: this.currentProjectId,
|
||||
originalName: updated.originalName,
|
||||
title: updated.title,
|
||||
alt: updated.alt,
|
||||
caption: updated.caption,
|
||||
tags: updated.tags,
|
||||
@@ -641,6 +655,7 @@ export class MediaEngine extends EventEmitter {
|
||||
size: dbMedia.size,
|
||||
width: dbMedia.width || undefined,
|
||||
height: dbMedia.height || undefined,
|
||||
title: dbMedia.title || undefined,
|
||||
alt: dbMedia.alt || undefined,
|
||||
caption: dbMedia.caption || undefined,
|
||||
createdAt: dbMedia.createdAt,
|
||||
@@ -666,6 +681,7 @@ export class MediaEngine extends EventEmitter {
|
||||
size: dbMedia.size,
|
||||
width: dbMedia.width || undefined,
|
||||
height: dbMedia.height || undefined,
|
||||
title: dbMedia.title || undefined,
|
||||
alt: dbMedia.alt || undefined,
|
||||
caption: dbMedia.caption || undefined,
|
||||
createdAt: dbMedia.createdAt,
|
||||
@@ -726,6 +742,7 @@ export class MediaEngine extends EventEmitter {
|
||||
size: dbMedia.size,
|
||||
width: dbMedia.width || undefined,
|
||||
height: dbMedia.height || undefined,
|
||||
title: dbMedia.title || undefined,
|
||||
alt: dbMedia.alt || undefined,
|
||||
caption: dbMedia.caption || undefined,
|
||||
createdAt: dbMedia.createdAt,
|
||||
@@ -770,7 +787,7 @@ export class MediaEngine extends EventEmitter {
|
||||
searchResults.push({
|
||||
id: item.id,
|
||||
originalName: item.originalName,
|
||||
caption: item.caption || undefined,
|
||||
title: item.title || undefined,
|
||||
mimeType: item.mimeType,
|
||||
createdAt: item.createdAt,
|
||||
});
|
||||
|
||||
@@ -739,11 +739,12 @@ export class OpenCodeManager {
|
||||
},
|
||||
{
|
||||
name: 'update_media_metadata',
|
||||
description: 'Update metadata for a media file (alt text, caption, tags).',
|
||||
description: 'Update metadata for a media file (title, alt text, caption, tags).',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
mediaId: { type: 'string', description: 'The unique ID of the media to update' },
|
||||
title: { type: 'string', description: 'New title for display in lists and search results' },
|
||||
alt: { type: 'string', description: 'New alt text for the image' },
|
||||
caption: { type: 'string', description: 'New caption for the image' },
|
||||
tags: { type: 'array', items: { type: 'string' }, description: 'New tags for the media' },
|
||||
@@ -926,7 +927,7 @@ export class OpenCodeManager {
|
||||
id: media.id, filename: media.filename,
|
||||
originalName: media.originalName, mimeType: media.mimeType,
|
||||
size: media.size, width: media.width, height: media.height,
|
||||
alt: media.alt, caption: media.caption, tags: media.tags,
|
||||
title: media.title, alt: media.alt, caption: media.caption, tags: media.tags,
|
||||
createdAt: media.createdAt, updatedAt: media.updatedAt,
|
||||
},
|
||||
};
|
||||
@@ -945,7 +946,7 @@ export class OpenCodeManager {
|
||||
media: mediaList.map(m => ({
|
||||
id: m.id, filename: m.filename,
|
||||
originalName: m.originalName, mimeType: m.mimeType,
|
||||
alt: m.alt, tags: m.tags,
|
||||
title: m.title, alt: m.alt, tags: m.tags,
|
||||
})),
|
||||
};
|
||||
}
|
||||
@@ -967,6 +968,7 @@ export class OpenCodeManager {
|
||||
|
||||
case 'update_media_metadata': {
|
||||
const updates: Record<string, unknown> = {};
|
||||
if (args.title !== undefined) updates.title = args.title;
|
||||
if (args.alt !== undefined) updates.alt = args.alt;
|
||||
if (args.caption !== undefined) updates.caption = args.caption;
|
||||
if (args.tags !== undefined) updates.tags = args.tags;
|
||||
@@ -1033,6 +1035,7 @@ export class OpenCodeManager {
|
||||
originalName: mediaItem.originalName,
|
||||
width: mediaItem.width,
|
||||
height: mediaItem.height,
|
||||
title: mediaItem.title,
|
||||
alt: mediaItem.alt,
|
||||
caption: mediaItem.caption,
|
||||
size: size,
|
||||
@@ -1080,6 +1083,7 @@ export class OpenCodeManager {
|
||||
filename: link.media.filename,
|
||||
originalName: link.media.originalName,
|
||||
mimeType: link.media.mimeType,
|
||||
title: link.media.title,
|
||||
alt: link.media.alt,
|
||||
caption: link.media.caption,
|
||||
width: link.media.width,
|
||||
@@ -1451,11 +1455,12 @@ Remember: Only suggest mappings from NEW items to EXISTING items. Consider langu
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze a media image and generate alt text and caption using AI
|
||||
* Analyze a media image and generate title, alt text, and caption using AI
|
||||
* This is a one-shot request that looks at the image and suggests metadata
|
||||
*/
|
||||
async analyzeMediaImage(mediaId: string, language: string = 'en'): Promise<{
|
||||
success: boolean;
|
||||
title?: string;
|
||||
alt?: string;
|
||||
caption?: string;
|
||||
error?: string;
|
||||
@@ -1496,12 +1501,13 @@ Remember: Only suggest mappings from NEW items to EXISTING items. Consider langu
|
||||
};
|
||||
const languageName = languageNames[language] || language;
|
||||
|
||||
const systemPrompt = `Generate alt text and caption for this image in ${languageName}.
|
||||
const systemPrompt = `Generate title, alt text, and caption for this image in ${languageName}.
|
||||
|
||||
TITLE: A short, descriptive title for display in lists and search results (3-8 words). Should identify the main subject.
|
||||
ALT: Describe ONLY what is visually present in the image. Be factual, neutral, and concise (5-12 words max). No interpretations, emotions, or "Image of" prefix. Example: "Red bicycle leaning against white brick wall"
|
||||
CAPTION: Short, engaging blog caption (5-20 words).
|
||||
|
||||
Respond with JSON only: {"alt": "...", "caption": "..."}`;
|
||||
Respond with JSON only: {"title": "...", "alt": "...", "caption": "..."}`;
|
||||
|
||||
try {
|
||||
// Using Claude Sonnet 4.5 for best image analysis
|
||||
@@ -1570,6 +1576,7 @@ Respond with JSON only: {"alt": "...", "caption": "..."}`;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
title: result.title || undefined,
|
||||
alt: result.alt || undefined,
|
||||
caption: result.caption || undefined,
|
||||
};
|
||||
|
||||
@@ -333,7 +333,7 @@ export function registerChatHandlers(): void {
|
||||
|
||||
// ============ Media Analysis ============
|
||||
|
||||
// Analyze a media image and generate alt text and caption
|
||||
// Analyze a media image and generate title, alt text, and caption
|
||||
ipcMain.handle('chat:analyzeMediaImage', async (_, mediaId: string, language?: string) => {
|
||||
try {
|
||||
const manager = getOpenCodeManager();
|
||||
|
||||
Reference in New Issue
Block a user