diff --git a/backend/main.py b/backend/main.py index dab5cff..0739cbb 100644 --- a/backend/main.py +++ b/backend/main.py @@ -527,7 +527,14 @@ async def api_file(vault_name: str, path: str = Query(..., description="Relative if not file_path.exists() or not file_path.is_file(): raise HTTPException(status_code=404, detail=f"File not found: {path}") - raw = file_path.read_text(encoding="utf-8", errors="replace") + try: + raw = file_path.read_text(encoding="utf-8", errors="replace") + except PermissionError: + raise HTTPException(status_code=403, detail=f"Permission denied: cannot read file {path}") + except Exception as e: + logger.error(f"Error reading file {path}: {e}") + raise HTTPException(status_code=500, detail=f"Error reading file: {str(e)}") + ext = file_path.suffix.lower() if ext == ".md": diff --git a/frontend/app.js b/frontend/app.js index fb531f0..50efa53 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -1203,16 +1203,25 @@ }); // Action buttons - const copyBtn = el("button", { class: "btn-action", title: "Copier le chemin" }, [ + const copyBtn = el("button", { class: "btn-action", title: "Copier la source" }, [ icon("copy", 14), document.createTextNode("Copier"), ]); - copyBtn.addEventListener("click", () => { - navigator.clipboard.writeText(`${data.vault}/${data.path}`).then(() => { - copyBtn.querySelector("span") || (copyBtn.lastChild.textContent = "CopiƩ !"); + copyBtn.addEventListener("click", async () => { + try { + // Fetch raw content if not already cached + if (!cachedRawSource) { + const rawUrl = `/api/file/${encodeURIComponent(data.vault)}/raw?path=${encodeURIComponent(data.path)}`; + const rawData = await api(rawUrl); + cachedRawSource = rawData.raw; + } + await navigator.clipboard.writeText(cachedRawSource); copyBtn.lastChild.textContent = "CopiƩ !"; setTimeout(() => (copyBtn.lastChild.textContent = "Copier"), 1500); - }); + } catch (err) { + console.error("Copy error:", err); + showToast("Erreur lors de la copie"); + } }); const sourceBtn = el("button", { class: "btn-action", title: "Voir la source" }, [ @@ -1903,12 +1912,13 @@ try { await waitForCodeMirror(); - const { EditorView, EditorState, basicSetup, markdown, oneDark, keymap } = window.CodeMirror; + const { EditorView, EditorState, basicSetup, markdown, python, javascript, html, css, json, xml, sql, php, cpp, java, rust, oneDark, keymap } = window.CodeMirror; const currentTheme = document.documentElement.getAttribute("data-theme"); + const fileExt = filePath.split(".").pop().toLowerCase(); + const extensions = [ basicSetup, - markdown(), keymap.of([{ key: "Mod-s", run: () => { @@ -1919,6 +1929,45 @@ EditorView.lineWrapping, ]; + // Add language support based on file extension + const langMap = { + "md": markdown, + "markdown": markdown, + "py": python, + "js": javascript, + "jsx": javascript, + "ts": javascript, + "tsx": javascript, + "mjs": javascript, + "cjs": javascript, + "html": html, + "htm": html, + "css": css, + "scss": css, + "less": css, + "json": json, + "xml": xml, + "svg": xml, + "sql": sql, + "php": php, + "cpp": cpp, + "cc": cpp, + "cxx": cpp, + "c": cpp, + "h": cpp, + "hpp": cpp, + "java": java, + "rs": rust, + "sh": javascript, // Using javascript for shell scripts as fallback + "bash": javascript, + "zsh": javascript, + }; + + const langMode = langMap[fileExt]; + if (langMode) { + extensions.push(langMode()); + } + if (currentTheme === "dark") { extensions.push(oneDark); } diff --git a/frontend/index.html b/frontend/index.html index c3d1a61..5521d28 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -26,6 +26,17 @@ import { searchKeymap, highlightSelectionMatches } from "https://esm.sh/@codemirror/search@6.3.0?external=@codemirror/state"; import { autocompletion, completionKeymap, closeBrackets, closeBracketsKeymap } from "https://esm.sh/@codemirror/autocomplete@6.5.0?external=@codemirror/state"; import { markdown } from "https://esm.sh/@codemirror/lang-markdown@6.1.0?external=@codemirror/state"; + import { python } from "https://esm.sh/@codemirror/lang-python@6.1.3?external=@codemirror/state"; + import { javascript } from "https://esm.sh/@codemirror/lang-javascript@6.1.7?external=@codemirror/state"; + import { html } from "https://esm.sh/@codemirror/lang-html@6.4.3?external=@codemirror/state"; + import { css } from "https://esm.sh/@codemirror/lang-css@6.2.0?external=@codemirror/state"; + import { json } from "https://esm.sh/@codemirror/lang-json@6.0.1?external=@codemirror/state"; + import { xml } from "https://esm.sh/@codemirror/lang-xml@6.0.2?external=@codemirror/state"; + import { sql } from "https://esm.sh/@codemirror/lang-sql@6.5.0?external=@codemirror/state"; + import { php } from "https://esm.sh/@codemirror/lang-php@6.0.1?external=@codemirror/state"; + import { cpp } from "https://esm.sh/@codemirror/lang-cpp@6.0.2?external=@codemirror/state"; + import { java } from "https://esm.sh/@codemirror/lang-java@6.0.1?external=@codemirror/state"; + import { rust } from "https://esm.sh/@codemirror/lang-rust@6.0.1?external=@codemirror/state"; import { oneDark } from "https://esm.sh/@codemirror/theme-one-dark@6.1.0?external=@codemirror/state"; const basicSetup = [ @@ -56,7 +67,7 @@ ]) ]; - window.CodeMirror = { EditorView, EditorState, basicSetup, markdown, oneDark, keymap }; + window.CodeMirror = { EditorView, EditorState, basicSetup, markdown, python, javascript, html, css, json, xml, sql, php, cpp, java, rust, oneDark, keymap };