Add multi-language syntax highlighting to source editor, improve copy button to copy raw file content, and add permission error handling to file API endpoint
This commit is contained in:
parent
6f694148db
commit
1129d1bca5
@ -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":
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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 };
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user