diff --git a/src/main/engine/MenuEngine.ts b/src/main/engine/MenuEngine.ts index c151633..e2fabae 100644 --- a/src/main/engine/MenuEngine.ts +++ b/src/main/engine/MenuEngine.ts @@ -5,14 +5,14 @@ import { randomUUID } from 'crypto'; import { app } from 'electron'; import { XMLBuilder, XMLParser } from 'fast-xml-parser'; -export type MenuItemKind = 'page' | 'submenu' | 'category-archive'; +export type MenuItemKind = 'page' | 'submenu' | 'category-archive' | 'home'; const HOME_MENU_ID = 'menu-home'; const DEFAULT_HOME_ITEM: MenuItemData = { id: HOME_MENU_ID, title: 'Home', - kind: 'page', + kind: 'home', pageId: undefined, pageSlug: 'home', categoryName: undefined, @@ -79,6 +79,8 @@ function sanitizeMenuItem(input: unknown): MenuItemData { ? 'submenu' : candidate.kind === 'category-archive' ? 'category-archive' + : candidate.kind === 'home' + ? 'home' : 'page'; const childrenSource = Array.isArray(candidate.children) ? candidate.children : []; const title = normalizeNonEmptyString(candidate.title) || 'Untitled'; @@ -88,7 +90,7 @@ function sanitizeMenuItem(input: unknown): MenuItemData { title, kind, pageId: kind === 'page' ? normalizeNonEmptyString(candidate.pageId) : undefined, - pageSlug: kind === 'page' ? normalizeNonEmptyString(candidate.pageSlug) : undefined, + pageSlug: kind === 'page' || kind === 'home' ? normalizeNonEmptyString(candidate.pageSlug) : undefined, categoryName: kind === 'category-archive' ? normalizeNonEmptyString(candidate.categoryName) : undefined, children: childrenSource.map((child) => sanitizeMenuItem(child)), }; @@ -99,7 +101,7 @@ function normalizeHomeItem(item: MenuItemData): MenuItemData { ...item, id: HOME_MENU_ID, title: 'Home', - kind: 'page', + kind: 'home', pageId: undefined, pageSlug: 'home', categoryName: undefined, @@ -167,6 +169,8 @@ function parseOutlineNode(node: OpmlOutlineNode): MenuItemData { ? 'submenu' : rawType === 'category-archive' ? 'category-archive' + : rawType === 'home' + ? 'home' : 'page'; const title = normalizeNonEmptyString(node['@_text']) || normalizeNonEmptyString(node['@_title']) || 'Untitled'; @@ -175,7 +179,7 @@ function parseOutlineNode(node: OpmlOutlineNode): MenuItemData { title, kind, pageId: kind === 'page' ? normalizeNonEmptyString(node['@_pageId']) : undefined, - pageSlug: kind === 'page' ? normalizeNonEmptyString(node['@_pageSlug']) : undefined, + pageSlug: kind === 'page' || kind === 'home' ? normalizeNonEmptyString(node['@_pageSlug']) : undefined, categoryName: kind === 'category-archive' ? normalizeNonEmptyString(node['@_categoryName']) : undefined, children: normalizeOutlineNodes(node.outline).map((child) => parseOutlineNode(child)), }; @@ -192,7 +196,7 @@ function toOpmlOutlineNode(item: MenuItemData): OpmlOutlineNode { outlineNode['@_pageId'] = item.pageId; } - if (item.kind === 'page' && item.pageSlug) { + if ((item.kind === 'page' || item.kind === 'home') && item.pageSlug) { outlineNode['@_pageSlug'] = item.pageSlug; } diff --git a/src/main/shared/electronApi.ts b/src/main/shared/electronApi.ts index 980bd4a..8a02eaa 100644 --- a/src/main/shared/electronApi.ts +++ b/src/main/shared/electronApi.ts @@ -422,7 +422,7 @@ export interface SiteValidationApplyResult { removedEmptyDirCount: number; } -export type MenuItemKind = 'page' | 'submenu' | 'category-archive'; +export type MenuItemKind = 'page' | 'submenu' | 'category-archive' | 'home'; export interface MenuItemData { id: string; diff --git a/src/renderer/components/MenuEditorView/MenuEditorView.css b/src/renderer/components/MenuEditorView/MenuEditorView.css index 36f7005..633c1e9 100644 --- a/src/renderer/components/MenuEditorView/MenuEditorView.css +++ b/src/renderer/components/MenuEditorView/MenuEditorView.css @@ -124,8 +124,19 @@ } .menu-editor-row-kind { - font-size: 0.75rem; - opacity: 0.85; + display: inline-flex; + align-items: center; + justify-content: center; + width: 1rem; + min-width: 1rem; + opacity: 0.9; +} + +.menu-editor-row-kind-icon { + display: inline-flex; + align-items: center; + justify-content: center; + color: inherit; } .menu-editor-row-title { diff --git a/src/renderer/components/MenuEditorView/MenuEditorView.tsx b/src/renderer/components/MenuEditorView/MenuEditorView.tsx index 14c0149..dcbb714 100644 --- a/src/renderer/components/MenuEditorView/MenuEditorView.tsx +++ b/src/renderer/components/MenuEditorView/MenuEditorView.tsx @@ -167,6 +167,22 @@ function createDraftEntry(kind: MenuItemData['kind'] = 'submenu'): MenuItemData }; } +function renderMenuKindIcon(kind: MenuItemData['kind']): React.ReactNode { + if (kind === 'home') { + return ; + } + + if (kind === 'page') { + return ; + } + + if (kind === 'category-archive') { + return ; + } + + return ; +} + export const MenuEditorView: React.FC = () => { const { t: tr } = useI18n(); const [items, setItems] = useState([]); @@ -718,11 +734,30 @@ export const MenuEditorView: React.FC = () => { > <> - {node.data.kind === 'page' - ? tr('menuEditor.type.page') - : node.data.kind === 'category-archive' - ? tr('menuEditor.type.categoryArchive') - : tr('menuEditor.type.submenu')} + + {renderMenuKindIcon(node.data.kind)} + {editingEntryId === node.data.id ? ( diff --git a/src/renderer/i18n/locales/de.json b/src/renderer/i18n/locales/de.json index b231484..dcbe98b 100644 --- a/src/renderer/i18n/locales/de.json +++ b/src/renderer/i18n/locales/de.json @@ -76,6 +76,7 @@ "menuEditor.field.pageSlug": "Seiten-Slug", "menuEditor.field.pageId": "Seiten-ID", "menuEditor.type.page": "Seite", + "menuEditor.type.home": "Startseite", "menuEditor.type.submenu": "Untermenü", "menuEditor.type.categoryArchive": "Kategorie-Archiv", "menuEditor.empty": "Noch keine Menüeinträge. Füge eine Seite oder ein Untermenü hinzu.", diff --git a/src/renderer/i18n/locales/en.json b/src/renderer/i18n/locales/en.json index bc56e3a..854f522 100644 --- a/src/renderer/i18n/locales/en.json +++ b/src/renderer/i18n/locales/en.json @@ -76,6 +76,7 @@ "menuEditor.field.pageSlug": "Page Slug", "menuEditor.field.pageId": "Page ID", "menuEditor.type.page": "Page", + "menuEditor.type.home": "Home", "menuEditor.type.submenu": "Submenu", "menuEditor.type.categoryArchive": "Category Archive", "menuEditor.empty": "No menu entries yet. Add a page or submenu to start.", diff --git a/src/renderer/i18n/locales/es.json b/src/renderer/i18n/locales/es.json index f3e408b..d854ae7 100644 --- a/src/renderer/i18n/locales/es.json +++ b/src/renderer/i18n/locales/es.json @@ -76,6 +76,7 @@ "menuEditor.field.pageSlug": "Slug de página", "menuEditor.field.pageId": "ID de página", "menuEditor.type.page": "Página", + "menuEditor.type.home": "Inicio", "menuEditor.type.submenu": "Submenú", "menuEditor.type.categoryArchive": "Archivo de categoría", "menuEditor.empty": "Aún no hay entradas de menú. Añade una página o un submenú para empezar.", diff --git a/src/renderer/i18n/locales/fr.json b/src/renderer/i18n/locales/fr.json index 47501d6..6f284a0 100644 --- a/src/renderer/i18n/locales/fr.json +++ b/src/renderer/i18n/locales/fr.json @@ -76,6 +76,7 @@ "menuEditor.field.pageSlug": "Slug de page", "menuEditor.field.pageId": "ID de page", "menuEditor.type.page": "Page", + "menuEditor.type.home": "Accueil", "menuEditor.type.submenu": "Sous-menu", "menuEditor.type.categoryArchive": "Archive de catégorie", "menuEditor.empty": "Aucune entrée de menu. Ajoutez une page ou un sous-menu pour commencer.", diff --git a/src/renderer/i18n/locales/it.json b/src/renderer/i18n/locales/it.json index e27788f..aad747c 100644 --- a/src/renderer/i18n/locales/it.json +++ b/src/renderer/i18n/locales/it.json @@ -76,6 +76,7 @@ "menuEditor.field.pageSlug": "Slug pagina", "menuEditor.field.pageId": "ID pagina", "menuEditor.type.page": "Pagina", + "menuEditor.type.home": "Home", "menuEditor.type.submenu": "Sottomenu", "menuEditor.type.categoryArchive": "Archivio categoria", "menuEditor.empty": "Nessuna voce menu. Aggiungi una pagina o un sottomenu per iniziare.", diff --git a/tests/engine/MenuEngine.test.ts b/tests/engine/MenuEngine.test.ts index a4e880e..a34de7a 100644 --- a/tests/engine/MenuEngine.test.ts +++ b/tests/engine/MenuEngine.test.ts @@ -54,7 +54,7 @@ describe('MenuEngine', () => { expect(result.items[0]).toMatchObject({ id: 'menu-home', title: 'Home', - kind: 'page', + kind: 'home', pageSlug: 'home', }); }); @@ -72,7 +72,7 @@ describe('MenuEngine', () => { expect(result.items[0]).toMatchObject({ id: 'menu-home', title: 'Home', - kind: 'page', + kind: 'home', pageSlug: 'home', }); expect(result.items[1]).toMatchObject({ diff --git a/tests/renderer/components/MenuEditorView.test.tsx b/tests/renderer/components/MenuEditorView.test.tsx index 70208e2..59004c6 100644 --- a/tests/renderer/components/MenuEditorView.test.tsx +++ b/tests/renderer/components/MenuEditorView.test.tsx @@ -16,7 +16,7 @@ describe('MenuEditorView entry editor', () => { { id: 'menu-home', title: 'Home', - kind: 'page', + kind: 'home', pageSlug: 'home', children: [], }, @@ -217,4 +217,15 @@ describe('MenuEditorView entry editor', () => { expect(deleteButton).toBeDisabled(); }); + it('shows type as icon only (no visible type text label)', async () => { + const { container } = render(); + + await screen.findByText('Home'); + + const icon = container.querySelector('.menu-editor-row-kind-icon[data-kind="home"]'); + expect(icon).not.toBeNull(); + expect(screen.queryByText(/^page$/i)).not.toBeInTheDocument(); + expect(screen.queryByText(/^submenu$/i)).not.toBeInTheDocument(); + }); + });