* fix(web): show the localized label for untitled notes everywhere Untitled notes are stored with the zh-CN default title, and several views only replaced an empty title, so they showed 无标题笔记 in every locale. The visible cases were the public share page (heading and browser tab), the public notebook site, AI companion cards and mentions, the note link picker, link text inserted for a note, and the title of a duplicated note. Add getMemoDisplayTitle() next to DEFAULT_MEMO_TITLE and use it in those views, matching what MemoCard already does with isDefaultMemoTitle. * fix(web): localize diagrams and content label on the public share page The public share page passed zh-CN to read-only diagrams for every language other than English, so Japanese and Polish visitors saw Chinese diagram labels. Resolve the locale with resolveSupportedLocale(), which the diagram renderer already supports for all four languages. Also translate the note content aria-label, which was hard-coded in English. --------- Co-authored-by: tianma-if <yingwaizhiying@gmail.com>
82 lines
2.7 KiB
TypeScript
82 lines
2.7 KiB
TypeScript
import { Link2, X } from "lucide-react";
|
|
import { getMemoDisplayTitle } from "@edgeever/shared";
|
|
import { useTranslation } from "react-i18next";
|
|
import type { MemoSummary } from "@edgeever/shared";
|
|
import {
|
|
Command,
|
|
CommandEmpty,
|
|
CommandGroup,
|
|
CommandInput,
|
|
CommandItem,
|
|
CommandList,
|
|
} from "@/components/ui/command";
|
|
|
|
export const EditorNoteLinkPicker = ({
|
|
query,
|
|
isLoading,
|
|
memos,
|
|
currentMemoId,
|
|
onQueryChange,
|
|
onClose,
|
|
onInsert,
|
|
}: {
|
|
query: string;
|
|
isLoading: boolean;
|
|
memos: MemoSummary[];
|
|
currentMemoId: string;
|
|
onQueryChange: (query: string) => void;
|
|
onClose: () => void;
|
|
onInsert: (memo: MemoSummary) => void;
|
|
}) => {
|
|
const { t } = useTranslation();
|
|
const candidates = memos.filter((candidate) => candidate.id !== currentMemoId && !candidate.isDeleted);
|
|
|
|
return (
|
|
<div
|
|
className="absolute left-3 right-3 top-14 z-30 h-[min(22rem,calc(100%-4rem))] max-w-xl rounded-lg border border-slate-200 bg-card shadow-xl sm:left-5 sm:right-auto sm:w-[28rem]"
|
|
role="dialog"
|
|
aria-label={t("noteLinkPicker.title")}
|
|
>
|
|
<Command shouldFilter={false}>
|
|
<div className="flex items-center justify-between border-b border-slate-100 pr-2">
|
|
<CommandInput
|
|
autoFocus
|
|
value={query}
|
|
placeholder={t("noteLinkPicker.searchPlaceholder")}
|
|
onValueChange={onQueryChange}
|
|
onKeyDown={(event) => {
|
|
if (event.key === "Escape") onClose();
|
|
}}
|
|
/>
|
|
<button
|
|
type="button"
|
|
className="rounded-md p-1.5 text-slate-400 hover:bg-slate-100 hover:text-slate-700"
|
|
aria-label={t("noteLinkPicker.close")}
|
|
onClick={onClose}
|
|
>
|
|
<X className="h-4 w-4" />
|
|
</button>
|
|
</div>
|
|
<CommandList>
|
|
{isLoading ? (
|
|
<div className="p-6 text-center text-sm text-slate-500">{t("noteLinkPicker.loading")}</div>
|
|
) : (
|
|
<>
|
|
<CommandEmpty>{t("noteLinkPicker.empty")}</CommandEmpty>
|
|
<CommandGroup>
|
|
{candidates.map((candidate) => (
|
|
<CommandItem key={candidate.id} value={candidate.id} onSelect={() => onInsert(candidate)}>
|
|
<Link2 className="h-4 w-4 shrink-0 text-slate-600" />
|
|
<span className="min-w-0 flex-1 truncate">{getMemoDisplayTitle(candidate.title, t("common.untitledMemo"))}</span>
|
|
<span className="max-w-40 truncate text-xs text-slate-400">{candidate.excerpt}</span>
|
|
</CommandItem>
|
|
))}
|
|
</CommandGroup>
|
|
</>
|
|
)}
|
|
</CommandList>
|
|
</Command>
|
|
</div>
|
|
);
|
|
};
|