import { ArrowLeft, Download, Edit2, Expand, FileText, Folder, FolderPlus, Hash, ListTree, MoreHorizontal, Minimize2, Plus, Search, Trash2, Upload, X, } from "lucide-react"; import React, { lazy, Suspense, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import { useI18n } from "../../application/i18n/I18nProvider"; import { useApplicationBackend } from "../../application/state/useApplicationBackend"; import { useStoredNumber } from "../../application/state/useStoredNumber"; import { useStoredString } from "../../application/state/useStoredString"; import { useAvailableFonts } from "../../application/state/fontStore"; import { resolveNoteFontFamily } from "../../domain/noteFonts"; import { NoteOutline } from "./NoteOutline"; import { NoteExportMenu } from "./NoteExportMenu"; import { NoteModeDropdown, NoteToolbar } from "./NoteToolbar"; import type { NoteSourceEditorHandle } from "./NoteSourceEditor"; import { ancestorNoteGroupPaths, buildVaultNoteMarkdownExportFiles, cleanNoteGroupPath, getNoteGroupParentPath, isNoteGroupInside, joinNoteGroupPath, matchesVaultNoteSearch, importMarkdownPayloadsToVaultNotes, normalizeNoteGroups, normalizeVaultNotes, remapExpandedNoteGroupPaths, replaceNoteGroupPrefix, resolveMovedNoteGroupPath, sanitizeNoteExportFileNamePart, type MarkdownActionType, type NoteHeadingItem, type VaultNotesExportScope, } from "../../domain/notes"; import { getNextVaultOrder, reorderVaultItems, reorderVaultStrings, sortByVaultOrder } from "../../domain/vaultOrder"; import { STORAGE_KEY_VAULT_NOTES_EDITOR_MODE, STORAGE_KEY_VAULT_NOTES_FONT_FAMILY, STORAGE_KEY_VAULT_NOTES_FONT_SIZE, STORAGE_KEY_VAULT_NOTES_CODE_FONT_SIZE, STORAGE_KEY_VAULT_NOTES_TREE_WIDTH, } from "../../infrastructure/config/storageKeys"; import { logger } from "../../lib/logger"; import { cn } from "../../lib/utils"; import { TERMINAL_SIDE_PANEL_INNER_HEADER_CLASS } from "../terminalLayer/terminalSidePanelChrome"; import { readTextFile } from "../../lib/readTextFile"; import { buildTextFilesZipBlob } from "../../lib/textZip"; import type { Host, VaultNote } from "../../types"; import { Button } from "../ui/button"; import { LazyLoadBoundary } from "../ui/lazy-load-boundary"; import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger, } from "../ui/context-menu"; import { Dropdown, DropdownContent, DropdownTrigger } from "../ui/dropdown"; import { Input } from "../ui/input"; import { ScrollArea } from "../ui/scroll-area"; import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"; import { toast } from "../ui/toast"; import { VaultTreeGroupRow, VaultTreeInlineRenameInput, VaultTreeItemRow, } from "../vault/VaultTreeRow"; import { VaultDeleteConfirmDialog } from "../vault/VaultDeleteConfirmDialog"; import { clearVaultDropIndicator, getVaultDropIntent, getVaultDropPosition, type VaultDropPosition, hasVaultDragType, markVaultDropIndicator, markVaultInsideDropIndicator, } from "../vault/vaultReorderDrag"; import type { ActiveTextFormats, InlineMarkdownEditorHandle, NoteEditorMode, } from "./noteEditorTypes"; import { EMPTY_ACTIVE_FORMATS } from "./noteEditorTypes"; import { NoteTitleInput } from "./NoteTitleInput"; const InlineMarkdownEditor = lazy(() => import("./InlineMarkdownEditor.lazy").then((module) => ({ default: module.InlineMarkdownEditor })), ); /** Warm the MDXEditor chunk before Suspense. */ export function prefetchInlineMarkdownEditor(): void { void import("./InlineMarkdownEditor.lazy"); } interface NoteFolderNode { name: string; path: string; children: NoteFolderNode[]; notes: VaultNote[]; } type NotesToolbarPanel = "search" | null; const toolbarIconButtonClass = "netcatty-tab h-6 w-6 shrink-0 rounded-md p-0 hover:bg-transparent"; const menuItemClass = "flex h-8 w-full items-center rounded-md px-3 text-left text-sm hover:bg-secondary"; const noteMetadataPillClass = "inline-flex h-5 items-center gap-1 rounded-md bg-muted/70 px-2 text-[11px] font-medium leading-none"; const noteMetadataLabelClass = "translate-y-px"; const NOTES_TREE_DEFAULT_WIDTH = 300; /** Narrow enough for nested folders + ellipsis; toolbar scrolls if needed. */ const NOTES_TREE_MIN_WIDTH = 160; const NOTES_TREE_MAX_WIDTH = 520; const NOTE_DRAG_TYPE = "application/x-netcatty-note-id"; const NOTE_GROUP_DRAG_TYPE = "application/x-netcatty-note-group-path"; export function clampNotesTreeWidth(value: number): number { return Math.max(NOTES_TREE_MIN_WIDTH, Math.min(NOTES_TREE_MAX_WIDTH, value)); } export const normalizeNoteEditorMode = (value: string | null): NoteEditorMode | null => value === "live" ? "edit" : value === "edit" || value === "preview" || value === "source" ? value : null; export const isNoteEditorMode = (value: string | null): value is NoteEditorMode => value === "edit" || value === "preview" || value === "source"; const InlineMarkdownEditorFallback = () => (