import { Bold, Check, CheckSquare, Code, Eye, FileCode, Heading1, Heading2, Heading3, Heading4, Heading, Image as ImageIcon, Italic, Link as LinkIcon, List, ListOrdered, Minus, PencilLine, Quote, Redo2, Search, Sigma, SquareCode, Strikethrough, Table as TableIcon, Type, Underline, Undo2, } from "lucide-react"; import React, { useMemo, useState } from "react"; import { type MarkdownActionType } from "../../domain/notes"; import { useAvailableFonts } from "../../application/state/fontStore"; import { useI18n } from "../../application/i18n/I18nProvider"; import type { ActiveTextFormats, NoteEditorMode } from "./noteEditorTypes"; import { EMPTY_ACTIVE_FORMATS } from "./noteEditorTypes"; import { Dropdown, DropdownContent, DropdownTrigger } from "../ui/dropdown"; import { Select, SelectContent, SelectItem, SelectTrigger } from "../ui/select"; import { cn } from "../../lib/utils"; export interface NoteToolbarProps { editorMode: NoteEditorMode; onAction?: (action: MarkdownActionType) => void; onOpenHostPicker?: () => void; className?: string; noteFontFamily?: string; onChangeNoteFontFamily?: (font: string) => void; noteFontSize?: number; onChangeNoteFontSize?: (size: number) => void; noteCodeFontSize?: number; onChangeNoteCodeFontSize?: (size: number) => void; /** Active text-format toggles at the current selection (button highlight). */ activeFormats?: ActiveTextFormats; } export interface NoteModeDropdownProps { editorMode: NoteEditorMode; onChangeMode: (mode: NoteEditorMode) => void; className?: string; } const FONT_SIZES = [12, 13, 14, 15, 16, 18, 20]; const CODE_FONT_SIZES = [11, 12, 13, 14, 15, 16, 18]; export const NoteModeDropdown: React.FC = ({ editorMode, onChangeMode, className = "", }) => { const { t } = useI18n(); const normalizedMode: "edit" | "source" | "preview" = editorMode === "live" ? "edit" : editorMode; const options = [ { mode: "edit" as const, label: t("notes.toolbar.modeLive"), title: t("notes.toolbar.modeLiveTitle"), icon: PencilLine, }, { mode: "source" as const, label: t("notes.toolbar.modeSource"), title: t("notes.toolbar.modeSourceTitle"), icon: SquareCode, }, { mode: "preview" as const, label: t("notes.toolbar.modePreview"), title: t("notes.toolbar.modePreviewTitle"), icon: Eye, }, ]; const activeOption = options.find((option) => option.mode === normalizedMode) ?? options[0]; const ActiveIcon = activeOption.icon; return ( ); }; export const NoteToolbar: React.FC = ({ editorMode, onAction, className = "", noteFontFamily = "", onChangeNoteFontFamily, noteFontSize = 14, onChangeNoteFontSize, noteCodeFontSize = 13, onChangeNoteCodeFontSize, activeFormats = EMPTY_ACTIVE_FORMATS, }) => { const { t } = useI18n(); const [fontSearch, setFontSearch] = useState(""); // The font tool only controls code block / inline code fonts, so it lists // system monospace fonts (fontStore) rather than the UI font set. const availableSystemFonts = useAvailableFonts(); const systemFontList = useMemo(() => { const defaultOption = { label: t("notes.toolbar.defaultFont"), value: "" }; const list = availableSystemFonts.map((f) => ({ label: f.name, value: f.family, })); return [defaultOption, ...list]; }, [availableSystemFonts, t]); const filteredFonts = useMemo(() => { if (!fontSearch.trim()) return systemFontList; const query = fontSearch.trim().toLowerCase(); return systemFontList.filter( (f) => f.label.toLowerCase().includes(query) || f.value.toLowerCase().includes(query), ); }, [fontSearch, systemFontList]); const isEditing = editorMode === "edit" || editorMode === "live" || editorMode === "source"; if (!isEditing) return null; // Highlight style for toggles that are active at the current selection. const formatButtonClass = (active: boolean) => cn( "p-1.5 rounded-md transition-colors", active ? "bg-primary/15 text-primary hover:bg-primary/20 hover:text-primary" : "hover:bg-muted text-muted-foreground hover:text-foreground", ); return (
{/* Formatting Tools (Available in Live Preview & Source Mode) */}
{/* Undo / Redo */}
{/* Code Font & Typography Settings Dropdown */}
{t("notes.toolbar.customCodeFont")} {t("notes.toolbar.fontsAvailable", { count: systemFontList.length })}
{/* Search Bar */}
setFontSearch(e.target.value)} className="w-full pl-6 pr-2 py-1 rounded border border-border bg-background text-[11px] text-foreground outline-none focus:border-primary placeholder:text-muted-foreground/60" />
{/* Scrollable Font List */}
{filteredFonts.length === 0 ? (
{t("notes.toolbar.noFontsFound")}
) : ( filteredFonts.map((f) => ( )) )}
{t("notes.toolbar.bodyFontSize")}
{FONT_SIZES.map((sz) => ( ))}
{t("notes.toolbar.codeFontSize")}
{CODE_FONT_SIZES.map((sz) => ( ))}
{/* Heading Dropdown (Using Portal Dropdown to avoid clipping) */} {/* Inline Styles */}
{/* Lists */}
{/* Blocks */}
); };