feat: added field "title" and switched to it to free up caption for its normal use
This commit is contained in:
@@ -1426,6 +1426,7 @@ const MediaEditor: React.FC<{ mediaId: string }> = ({ mediaId }) => {
|
||||
const { media, updateMedia, showErrorModal, showConfirmDeleteModal, openTab } = useAppStore();
|
||||
const item = media.find(m => m.id === mediaId);
|
||||
|
||||
const [title, setTitle] = useState(item?.title || '');
|
||||
const [alt, setAlt] = useState(item?.alt || '');
|
||||
const [caption, setCaption] = useState(item?.caption || '');
|
||||
const [tags, setTags] = useState(item?.tags.join(', ') || '');
|
||||
@@ -1474,6 +1475,7 @@ const MediaEditor: React.FC<{ mediaId: string }> = ({ mediaId }) => {
|
||||
const result = await window.electronAPI?.chat.analyzeMediaImage(item.id, projectLanguage);
|
||||
|
||||
if (result?.success) {
|
||||
if (result.title) setTitle(result.title);
|
||||
if (result.alt) setAlt(result.alt);
|
||||
if (result.caption) setCaption(result.caption);
|
||||
showToast.success('AI analysis complete');
|
||||
@@ -1581,6 +1583,7 @@ const MediaEditor: React.FC<{ mediaId: string }> = ({ mediaId }) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (item) {
|
||||
setTitle(item.title || '');
|
||||
setAlt(item.alt || '');
|
||||
setCaption(item.caption || '');
|
||||
setTags(item.tags.join(', '));
|
||||
@@ -1594,6 +1597,7 @@ const MediaEditor: React.FC<{ mediaId: string }> = ({ mediaId }) => {
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
const updated = await window.electronAPI?.media.update(item.id, {
|
||||
title,
|
||||
alt,
|
||||
caption,
|
||||
tags: tags.split(',').map(t => t.trim()).filter(t => t.length > 0),
|
||||
@@ -1696,8 +1700,8 @@ const MediaEditor: React.FC<{ mediaId: string }> = ({ mediaId }) => {
|
||||
>
|
||||
<span className="quick-action-icon">🤖</span>
|
||||
<span className="quick-action-text">
|
||||
<strong>AI: Generate Alt & Caption</strong>
|
||||
<small>Uses Claude Sonnet 4.5 to analyze the image</small>
|
||||
<strong>AI: Generate Title, Alt & Caption</strong>
|
||||
<small>Analyzes the image to suggest metadata</small>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -1755,6 +1759,15 @@ const MediaEditor: React.FC<{ mediaId: string }> = ({ mediaId }) => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="editor-field">
|
||||
<label>Title</label>
|
||||
<input
|
||||
type="text"
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
placeholder="Title for lists and search results"
|
||||
/>
|
||||
</div>
|
||||
<div className="editor-field">
|
||||
<label>Alt Text</label>
|
||||
<input
|
||||
|
||||
@@ -11,17 +11,17 @@ interface PostSearchResult {
|
||||
interface MediaSearchResult {
|
||||
id: string;
|
||||
originalName: string;
|
||||
caption?: string;
|
||||
title?: string;
|
||||
mimeType: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
/** Get display name for media: caption (truncated to 60 chars) or fallback to filename */
|
||||
/** Get display name for media: title (truncated to 60 chars) or fallback to filename */
|
||||
function getMediaDisplayName(media: MediaSearchResult): string {
|
||||
if (media.caption) {
|
||||
return media.caption.length > 60
|
||||
? media.caption.substring(0, 60) + '...'
|
||||
: media.caption;
|
||||
if (media.title) {
|
||||
return media.title.length > 60
|
||||
? media.title.substring(0, 60) + '...'
|
||||
: media.title;
|
||||
}
|
||||
return media.originalName;
|
||||
}
|
||||
@@ -187,7 +187,7 @@ export const InsertModal: React.FC<InsertModalProps> = ({
|
||||
const externalLabel = mode === 'link' ? 'External URL' : 'External Image';
|
||||
const searchPlaceholder = mode === 'link'
|
||||
? 'Search posts by title or content...'
|
||||
: 'Search media by name, caption, or alt text...';
|
||||
: 'Search media by name, title, or alt text...';
|
||||
|
||||
return (
|
||||
<div className="insert-modal-backdrop" onClick={handleBackdropClick}>
|
||||
|
||||
@@ -14,12 +14,12 @@ import { useAppStore, MediaData } from '../../store';
|
||||
import { showToast } from '../Toast';
|
||||
import './LinkedMediaPanel.css';
|
||||
|
||||
/** Get display name for media: caption (truncated to 60 chars) or fallback to filename */
|
||||
/** Get display name for media: title (truncated to 60 chars) or fallback to filename */
|
||||
function getMediaDisplayName(media: MediaData): string {
|
||||
if (media.caption) {
|
||||
return media.caption.length > 60
|
||||
? media.caption.substring(0, 60) + '...'
|
||||
: media.caption;
|
||||
if (media.title) {
|
||||
return media.title.length > 60
|
||||
? media.title.substring(0, 60) + '...'
|
||||
: media.title;
|
||||
}
|
||||
return media.originalName;
|
||||
}
|
||||
|
||||
@@ -329,7 +329,7 @@ export const SettingsView: React.FC = () => {
|
||||
<SettingRow
|
||||
id="project-language"
|
||||
label="Main Language"
|
||||
description="The primary language for your blog content. AI-generated alt text and captions will use this language."
|
||||
description="The primary language for your blog content. AI-generated titles, alt text, and captions will use this language."
|
||||
>
|
||||
<select
|
||||
id="project-language"
|
||||
|
||||
@@ -5,12 +5,12 @@ import { groupPostsByStatus } from '../../utils';
|
||||
import type { ChatConversation, ImportDefinitionData } from '../../types/electron';
|
||||
import './Sidebar.css';
|
||||
|
||||
/** Get display name for media: caption (truncated to 60 chars) or fallback to filename */
|
||||
/** Get display name for media: title (truncated to 60 chars) or fallback to filename */
|
||||
function getMediaDisplayName(media: MediaData): string {
|
||||
if (media.caption) {
|
||||
return media.caption.length > 60
|
||||
? media.caption.substring(0, 60) + '...'
|
||||
: media.caption;
|
||||
if (media.title) {
|
||||
return media.title.length > 60
|
||||
? media.title.substring(0, 60) + '...'
|
||||
: media.title;
|
||||
}
|
||||
return media.originalName;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user