diff --git a/src/main/engine/MenuEngine.ts b/src/main/engine/MenuEngine.ts index e2fabae..78f7bd6 100644 --- a/src/main/engine/MenuEngine.ts +++ b/src/main/engine/MenuEngine.ts @@ -172,7 +172,11 @@ function parseOutlineNode(node: OpmlOutlineNode): MenuItemData { : rawType === 'home' ? 'home' : 'page'; - const title = normalizeNonEmptyString(node['@_text']) || normalizeNonEmptyString(node['@_title']) || 'Untitled'; + const textTitle = normalizeNonEmptyString(node['@_text']); + const explicitTitle = normalizeNonEmptyString(node['@_title']); + const title = kind === 'category-archive' + ? explicitTitle || textTitle || 'Untitled' + : textTitle || explicitTitle || 'Untitled'; return { id: normalizeNonEmptyString(node['@_id']) || generateMenuItemId(), diff --git a/src/renderer/components/CategoryInput/CategoryInput.tsx b/src/renderer/components/CategoryInput/CategoryInput.tsx index 9964768..64ef915 100644 --- a/src/renderer/components/CategoryInput/CategoryInput.tsx +++ b/src/renderer/components/CategoryInput/CategoryInput.tsx @@ -2,11 +2,17 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'; import '../TagInput/TagInput.css'; import './CategoryInput.css'; +export interface CategoryOption { + name: string; + title: string; +} + interface CategoryInputProps { - categories: string[]; - onSelectCategory: (categoryName: string) => void; + categories: CategoryOption[]; + onSelectCategory: (category: CategoryOption) => void; placeholder?: string; createCategoryArchiveLabel: string; + allowCreate?: boolean; disabled?: boolean; autoFocus?: boolean; inlinePlain?: boolean; @@ -17,6 +23,7 @@ export const CategoryInput: React.FC = ({ onSelectCategory, placeholder = '', createCategoryArchiveLabel, + allowCreate = true, disabled = false, autoFocus = false, inlinePlain = false, @@ -34,7 +41,7 @@ export const CategoryInput: React.FC = ({ const query = inputValue.toLowerCase().trim(); return categories - .filter((categoryName) => categoryName.toLowerCase().includes(query)) + .filter((category) => category.title.toLowerCase().includes(query) || category.name.toLowerCase().includes(query)) .slice(0, 8); }, [categories, inputValue]); @@ -62,23 +69,33 @@ export const CategoryInput: React.FC = ({ return () => clearTimeout(timer); }, [autoFocus, disabled]); - const createArchive = (label: string): void => { - const trimmed = label.trim(); - if (!trimmed) { + const selectCategory = (category: CategoryOption): void => { + onSelectCategory(category); + setInputValue(''); + setShowSuggestions(false); + setSelectedIndex(-1); + }; + + const createArchiveFromInput = (label: string): void => { + const trimmedName = label.trim(); + if (!trimmedName) { return; } - onSelectCategory(trimmed); + onSelectCategory({ + name: trimmedName, + title: trimmedName, + }); setInputValue(''); setShowSuggestions(false); setSelectedIndex(-1); }; const exactMatchExists = inputValue.trim() - ? suggestions.some((item) => item.toLowerCase() === inputValue.trim().toLowerCase()) + ? suggestions.some((item) => item.title.toLowerCase() === inputValue.trim().toLowerCase() || item.name.toLowerCase() === inputValue.trim().toLowerCase()) : false; - const showCreateOption = inputValue.trim() && !exactMatchExists; + const showCreateOption = allowCreate && Boolean(inputValue.trim()) && !exactMatchExists; const handleKeyDown = (event: React.KeyboardEvent): void => { if (event.key === 'ArrowDown') { @@ -97,15 +114,18 @@ export const CategoryInput: React.FC = ({ if (event.key === 'Enter') { event.preventDefault(); if (selectedIndex >= 0 && selectedIndex < suggestions.length) { - createArchive(suggestions[selectedIndex]); + selectCategory(suggestions[selectedIndex]); } else if (selectedIndex === suggestions.length && showCreateOption) { - createArchive(inputValue); + createArchiveFromInput(inputValue); } else { - const exactMatch = categories.find((categoryName) => categoryName.toLowerCase() === inputValue.trim().toLowerCase()); + const exactMatch = categories.find((category) => { + const query = inputValue.trim().toLowerCase(); + return category.name.toLowerCase() === query || category.title.toLowerCase() === query; + }); if (exactMatch) { - createArchive(exactMatch); - } else if (inputValue.trim()) { - createArchive(inputValue); + selectCategory(exactMatch); + } else if (allowCreate && inputValue.trim()) { + createArchiveFromInput(inputValue); } } return; @@ -144,14 +164,14 @@ export const CategoryInput: React.FC = ({ {showSuggestions && (suggestions.length > 0 || showCreateOption) && (
- {suggestions.map((categoryName, index) => ( + {suggestions.map((category, index) => ( ))} @@ -159,7 +179,7 @@ export const CategoryInput: React.FC = ({