From c8ff24d6a4fc774c9cf6d67b999e96b023077eba Mon Sep 17 00:00:00 2001 From: Mr-Neutr0n <64578610+Mr-Neutr0n@users.noreply.github.com> Date: Sat, 5 Sep 2026 08:31:30 +0000 Subject: [PATCH] Fix #217: Implement Universal File Import Support Signed-off-by: Mr-Neutr0n <64578610+Mr-Neutr0n@users.noreply.github.com> --- frontend/src/app/page.tsx | 34 ++++++++--------- frontend/src/hooks/useEditor.ts | 11 +----- frontend/src/lib/supportedFormats.ts | 56 ++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 28 deletions(-) create mode 100644 frontend/src/lib/supportedFormats.ts diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 6009df8..dbfe170 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -28,18 +28,15 @@ import { isEditableTarget, type ActionId, } from "@/lib/keyboardShortcuts"; - -const OPEN_FILE_EXTENSIONS = ["md", "markdown", "txt", "html", "htm", "pdf", "docx"]; -const CONVERTIBLE_EXTENSIONS = new Set(["html", "htm", "pdf", "docx"]); -const SUPPORTED_FILE_EXTENSIONS = new Set(OPEN_FILE_EXTENSIONS); - -function fileExtension(name: string) { - return name.split(".").pop()?.toLowerCase() ?? ""; -} - -function isSupportedFile(name: string) { - return SUPPORTED_FILE_EXTENSIONS.has(fileExtension(name)); -} +import { + isSupportedFile, + needsConversion, + MARKITDOWN_EXTENSIONS, + NATIVE_CONVERTIBLE_EXTENSIONS, + OPEN_FILE_ACCEPT, + OPEN_FILE_EXTENSIONS, + PLAIN_TEXT_EXTENSIONS, +} from "@/lib/supportedFormats"; function convertedMarkdownLabel(name: string) { const withoutExtension = name.replace(/\.[^/.]+$/, ""); @@ -233,8 +230,9 @@ export default function HomePage() { multiple: false, filters: [ { name: "Supported documents", extensions: OPEN_FILE_EXTENSIONS }, - { name: "Markdown", extensions: ["md", "markdown", "txt"] }, - { name: "Convertible documents", extensions: ["html", "htm", "pdf", "docx"] }, + { name: "Markdown", extensions: [...PLAIN_TEXT_EXTENSIONS] }, + { name: "Convertible documents", extensions: NATIVE_CONVERTIBLE_EXTENSIONS }, + { name: "Universal import (MarkItDown)", extensions: MARKITDOWN_EXTENSIONS }, ], }); if (!selected) return; @@ -261,8 +259,7 @@ export default function HomePage() { const file = e.target.files?.[0]; if (!file) return; try { - const ext = fileExtension(file.name); - if (CONVERTIBLE_EXTENSIONS.has(ext)) { + if (needsConversion(file.name)) { const content_base64 = arrayBufferToBase64(await file.arrayBuffer()); const { markdown } = await Files.convertToMarkdown({ filename: file.name, @@ -804,8 +801,7 @@ export default function HomePage() { for (const file of files) { if (!isSupportedFile(file.name)) continue; - const ext = fileExtension(file.name); - if (CONVERTIBLE_EXTENSIONS.has(ext)) { + if (needsConversion(file.name)) { try { const content_base64 = arrayBufferToBase64(await file.arrayBuffer()); const { markdown } = await Files.convertToMarkdown({ @@ -843,7 +839,7 @@ export default function HomePage() { diff --git a/frontend/src/hooks/useEditor.ts b/frontend/src/hooks/useEditor.ts index 182f312..f666003 100644 --- a/frontend/src/hooks/useEditor.ts +++ b/frontend/src/hooks/useEditor.ts @@ -10,6 +10,7 @@ import { useState, useCallback, useRef } from "react"; import { Files, Markdown, Export, type ExportPayload, type WordCountResult } from "@/lib/api"; +import { needsConversion } from "@/lib/supportedFormats"; export type Tab = { id: string; @@ -20,12 +21,6 @@ export type Tab = { dirty: boolean; }; -const CONVERTIBLE_EXTENSIONS = new Set(["pdf", "html", "htm", "docx"]); - -function fileExtension(name: string) { - return name.split(".").pop()?.toLowerCase() ?? ""; -} - function convertedMarkdownLabel(name: string) { const baseName = name.split(/[/\\]/).pop() ?? name; const withoutExtension = baseName.replace(/\.[^/.]+$/, ""); @@ -104,9 +99,7 @@ export function useEditor() { // ── file ops ─────────────────────────────────────────────────────────────── const openFile = useCallback( async (filePath: string) => { - const ext = fileExtension(filePath); - - if (CONVERTIBLE_EXTENSIONS.has(ext)) { + if (needsConversion(filePath)) { try { const { markdown } = await Files.convertToMarkdown({ path: filePath }); const label = convertedMarkdownLabel(filePath); diff --git a/frontend/src/lib/supportedFormats.ts b/frontend/src/lib/supportedFormats.ts new file mode 100644 index 0000000..4feba2a --- /dev/null +++ b/frontend/src/lib/supportedFormats.ts @@ -0,0 +1,56 @@ +/** + * File extensions accepted for open/import, matching backend/routers/files.py. + */ + +export const PLAIN_TEXT_EXTENSIONS = new Set(["md", "markdown", "txt"]); + +export const NATIVE_CONVERTIBLE_EXTENSIONS = ["html", "htm", "pdf", "docx"]; + +export const MARKITDOWN_EXTENSIONS = [ + "pptx", + "ppt", + "xlsx", + "xls", + "csv", + "epub", + "xml", + "zip", + "wav", + "mp3", + "jpg", + "jpeg", + "png", + "gif", + "bmp", + "tiff", + "tif", + "msg", + "ipynb", +]; + +export const OPEN_FILE_EXTENSIONS = [ + ...PLAIN_TEXT_EXTENSIONS, + ...NATIVE_CONVERTIBLE_EXTENSIONS, + ...MARKITDOWN_EXTENSIONS, +]; + +export const SUPPORTED_FILE_EXTENSIONS = new Set(OPEN_FILE_EXTENSIONS); + +export const OPEN_FILE_ACCEPT = OPEN_FILE_EXTENSIONS.map((ext) => `.${ext}`).join(","); + +export function fileExtension(name: string) { + return name.split(".").pop()?.toLowerCase() ?? ""; +} + +export function isPlainTextFile(name: string) { + return PLAIN_TEXT_EXTENSIONS.has(fileExtension(name)); +} + +export function isSupportedFile(name: string) { + return SUPPORTED_FILE_EXTENSIONS.has(fileExtension(name)); +} + +export function needsConversion(name: string) { + const ext = fileExtension(name); + return SUPPORTED_FILE_EXTENSIONS.has(ext) && !PLAIN_TEXT_EXTENSIONS.has(ext); +}