diff --git a/src/renderer/components/TabBar/TabBar.tsx b/src/renderer/components/TabBar/TabBar.tsx index e11b43f..75c57dc 100644 --- a/src/renderer/components/TabBar/TabBar.tsx +++ b/src/renderer/components/TabBar/TabBar.tsx @@ -10,6 +10,7 @@ const getTabTitle = ( tab: Tab, postTitles: Map, media: { id: string; originalName: string }[], + scriptTitles: Map, chatTitles: Map, importDefTitles: Map, commitTitles: Map, @@ -81,7 +82,7 @@ const getTabTitle = ( } if (tab.type === 'scripts') { - return tr('tabBar.scripts'); + return scriptTitles.get(tab.id) || tr('tabBar.scripts'); } return tr('tabBar.unknown'); @@ -214,6 +215,7 @@ export const TabBar: React.FC = () => { const [showLeftArrow, setShowLeftArrow] = useState(false); const [showRightArrow, setShowRightArrow] = useState(false); const [postTitles, setPostTitles] = useState>(new Map()); + const [scriptTitles, setScriptTitles] = useState>(new Map()); const [chatTitles, setChatTitles] = useState>(new Map()); const [importDefTitles, setImportDefTitles] = useState>(new Map()); const [commitTitles, setCommitTitles] = useState>(new Map()); @@ -297,6 +299,102 @@ export const TabBar: React.FC = () => { }; }, [tr]); + // Fetch script titles for script tabs + useEffect(() => { + const scriptTabs = tabs.filter((t) => t.type === 'scripts'); + const scriptTabIds = new Set(scriptTabs.map((t) => t.id)); + + setScriptTitles((previous) => { + const next = new Map(previous); + let changed = false; + + for (const id of Array.from(next.keys())) { + if (!scriptTabIds.has(id)) { + next.delete(id); + changed = true; + } + } + + return changed ? next : previous; + }); + + if (scriptTabs.length === 0) { + return; + } + + const fetchScriptTitles = async () => { + const newTitles = new Map(scriptTitles); + let changed = false; + + for (const tab of scriptTabs) { + if (scriptTitles.has(tab.id)) { + continue; + } + + try { + const script = await window.electronAPI?.scripts.get(tab.id); + if (script) { + const title = script.title || tr('editor.untitled'); + if (newTitles.get(tab.id) !== title) { + newTitles.set(tab.id, title); + changed = true; + } + } + } catch (error) { + console.error(tr('tabBar.error.fetchScriptTitle'), error); + } + } + + if (changed) { + setScriptTitles(newTitles); + } + }; + + void fetchScriptTitles(); + }, [tabs, tr]); // Note: intentionally not including scriptTitles to avoid infinite loops + + // Listen for script updates to refresh titles + useEffect(() => { + const handleScriptsChanged = async () => { + const scriptTabs = tabs.filter((t) => t.type === 'scripts'); + if (scriptTabs.length === 0) { + return; + } + + const updated = new Map(scriptTitles); + let changed = false; + + for (const tab of scriptTabs) { + try { + const script = await window.electronAPI?.scripts.get(tab.id); + if (script) { + const title = script.title || tr('editor.untitled'); + if (updated.get(tab.id) !== title) { + updated.set(tab.id, title); + changed = true; + } + } + } catch (error) { + console.error(tr('tabBar.error.fetchScriptTitle'), error); + } + } + + if (changed) { + setScriptTitles(updated); + } + }; + + if (typeof window.addEventListener === 'function') { + window.addEventListener('bds:scripts-changed', handleScriptsChanged); + } + + return () => { + if (typeof window.removeEventListener === 'function') { + window.removeEventListener('bds:scripts-changed', handleScriptsChanged); + } + }; + }, [tabs, scriptTitles, tr]); + // Fetch chat titles for chat tabs useEffect(() => { const chatTabs = tabs.filter(t => t.type === 'chat'); @@ -565,7 +663,7 @@ export const TabBar: React.FC = () => { {tabs.map((tab) => { const isActive = tab.id === activeTabId; const isDirty = tab.type === 'post' && dirtyPosts.has(tab.id); - const title = getTabTitle(tab, postTitles, media, chatTitles, importDefTitles, commitTitles, tr); + const title = getTabTitle(tab, postTitles, media, scriptTitles, chatTitles, importDefTitles, commitTitles, tr); const icon = getTabIcon(tab); return ( diff --git a/src/renderer/i18n/locales/de.json b/src/renderer/i18n/locales/de.json index 1b0cb9a..b8874eb 100644 --- a/src/renderer/i18n/locales/de.json +++ b/src/renderer/i18n/locales/de.json @@ -352,6 +352,7 @@ "tabBar.error.fetchPostTitle": "Beitragstitel konnte nicht geladen werden:", "tabBar.error.fetchChatTitle": "Chat-Titel konnte nicht geladen werden:", "tabBar.error.fetchImportTitle": "Titel der Importdefinition konnte nicht geladen werden:", + "tabBar.error.fetchScriptTitle": "Skript-Titel konnte nicht geladen werden:", "tabBar.error.fetchCommitTitle": "Commit-Titel konnten nicht geladen werden:", "metadataDiff.title": "Metadaten-Diff-Werkzeug", "metadataDiff.description": "Vergleicht Beitragsmetadaten zwischen Datenbank und Markdown-Dateien. Behebt Abweichungen durch Bugs oder manuelle Änderungen.", diff --git a/src/renderer/i18n/locales/en.json b/src/renderer/i18n/locales/en.json index 7ed1db4..a2f3ec4 100644 --- a/src/renderer/i18n/locales/en.json +++ b/src/renderer/i18n/locales/en.json @@ -352,6 +352,7 @@ "tabBar.error.fetchPostTitle": "Failed to fetch post title:", "tabBar.error.fetchChatTitle": "Failed to fetch chat title:", "tabBar.error.fetchImportTitle": "Failed to fetch import definition title:", + "tabBar.error.fetchScriptTitle": "Failed to fetch script title:", "tabBar.error.fetchCommitTitle": "Failed to fetch commit titles:", "metadataDiff.title": "Metadata Diff Tool", "metadataDiff.description": "Compare post metadata between database and markdown files. Fix inconsistencies caused by bugs or manual edits.", diff --git a/src/renderer/i18n/locales/es.json b/src/renderer/i18n/locales/es.json index 2fcb095..e080043 100644 --- a/src/renderer/i18n/locales/es.json +++ b/src/renderer/i18n/locales/es.json @@ -352,6 +352,7 @@ "tabBar.error.fetchPostTitle": "No se pudo cargar el título de la entrada:", "tabBar.error.fetchChatTitle": "No se pudo cargar el título del chat:", "tabBar.error.fetchImportTitle": "No se pudo cargar el título de la definición de importación:", + "tabBar.error.fetchScriptTitle": "No se pudo cargar el título del script:", "tabBar.error.fetchCommitTitle": "No se pudieron cargar los títulos de los commits:", "metadataDiff.title": "Herramienta diff de metadatos", "metadataDiff.description": "Compara los metadatos de las entradas entre la base de datos y los archivos Markdown. Corrige inconsistencias causadas por errores o ediciones manuales.", diff --git a/src/renderer/i18n/locales/fr.json b/src/renderer/i18n/locales/fr.json index 30c007b..31f16d9 100644 --- a/src/renderer/i18n/locales/fr.json +++ b/src/renderer/i18n/locales/fr.json @@ -352,6 +352,7 @@ "tabBar.error.fetchPostTitle": "Impossible de charger le titre de l’article :", "tabBar.error.fetchChatTitle": "Impossible de charger le titre du chat :", "tabBar.error.fetchImportTitle": "Impossible de charger le titre de la définition d’import :", + "tabBar.error.fetchScriptTitle": "Impossible de charger le titre du script :", "tabBar.error.fetchCommitTitle": "Impossible de charger les titres des commits :", "metadataDiff.title": "Outil de diff des métadonnées", "metadataDiff.description": "Compare les métadonnées des articles entre la base de données et les fichiers Markdown. Corrige les incohérences causées par des bugs ou des modifications manuelles.", diff --git a/src/renderer/i18n/locales/it.json b/src/renderer/i18n/locales/it.json index 895c451..7df1883 100644 --- a/src/renderer/i18n/locales/it.json +++ b/src/renderer/i18n/locales/it.json @@ -352,6 +352,7 @@ "tabBar.error.fetchPostTitle": "Impossibile caricare il titolo del post:", "tabBar.error.fetchChatTitle": "Impossibile caricare il titolo della chat:", "tabBar.error.fetchImportTitle": "Impossibile caricare il titolo della definizione di importazione:", + "tabBar.error.fetchScriptTitle": "Impossibile caricare il titolo dello script:", "tabBar.error.fetchCommitTitle": "Impossibile caricare i titoli dei commit:", "metadataDiff.title": "Strumento diff metadati", "metadataDiff.description": "Confronta i metadati dei post tra database e file markdown. Correggi incongruenze causate da bug o modifiche manuali.", diff --git a/tests/renderer/components/TabBar.test.tsx b/tests/renderer/components/TabBar.test.tsx index 81820c5..c601b43 100644 --- a/tests/renderer/components/TabBar.test.tsx +++ b/tests/renderer/components/TabBar.test.tsx @@ -69,6 +69,10 @@ describe('TabBar', () => { get: vi.fn(), onNameUpdated: vi.fn(() => () => {}), }, + scripts: { + ...(window as any).electronAPI?.scripts, + get: vi.fn(), + }, }; }); @@ -136,4 +140,24 @@ describe('TabBar', () => { expect(await screen.findByText('Updated Title')).toBeInTheDocument(); }); + + it('renders script title for script tab', async () => { + useAppStore.setState({ + tabs: [{ type: 'scripts', id: 'script-1', isTransient: false }], + activeTabId: 'script-1', + posts: [], + media: [], + dirtyPosts: new Set(), + }); + + (window as any).electronAPI.scripts.get = vi.fn().mockResolvedValue({ + id: 'script-1', + title: 'Publish Macro', + }); + + render(); + + expect(await screen.findByText('Publish Macro')).toBeInTheDocument(); + expect((window as any).electronAPI.scripts.get).toHaveBeenCalledWith('script-1'); + }); });