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():
|
if not file_path.exists() or not file_path.is_file():
|
||||||
raise HTTPException(status_code=404, detail=f"File not found: {path}")
|
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()
|
ext = file_path.suffix.lower()
|
||||||
|
|
||||||
if ext == ".md":
|
if ext == ".md":
|
||||||
|
|||||||
@ -1203,16 +1203,25 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Action buttons
|
// 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),
|
icon("copy", 14),
|
||||||
document.createTextNode("Copier"),
|
document.createTextNode("Copier"),
|
||||||
]);
|
]);
|
||||||
copyBtn.addEventListener("click", () => {
|
copyBtn.addEventListener("click", async () => {
|
||||||
navigator.clipboard.writeText(`${data.vault}/${data.path}`).then(() => {
|
try {
|
||||||
copyBtn.querySelector("span") || (copyBtn.lastChild.textContent = "Copié !");
|
// 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é !";
|
copyBtn.lastChild.textContent = "Copié !";
|
||||||
setTimeout(() => (copyBtn.lastChild.textContent = "Copier"), 1500);
|
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" }, [
|
const sourceBtn = el("button", { class: "btn-action", title: "Voir la source" }, [
|
||||||
@ -1903,12 +1912,13 @@
|
|||||||
try {
|
try {
|
||||||
await waitForCodeMirror();
|
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 currentTheme = document.documentElement.getAttribute("data-theme");
|
||||||
|
const fileExt = filePath.split(".").pop().toLowerCase();
|
||||||
|
|
||||||
const extensions = [
|
const extensions = [
|
||||||
basicSetup,
|
basicSetup,
|
||||||
markdown(),
|
|
||||||
keymap.of([{
|
keymap.of([{
|
||||||
key: "Mod-s",
|
key: "Mod-s",
|
||||||
run: () => {
|
run: () => {
|
||||||
@ -1919,6 +1929,45 @@
|
|||||||
EditorView.lineWrapping,
|
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") {
|
if (currentTheme === "dark") {
|
||||||
extensions.push(oneDark);
|
extensions.push(oneDark);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,6 +26,17 @@
|
|||||||
import { searchKeymap, highlightSelectionMatches } from "https://esm.sh/@codemirror/search@6.3.0?external=@codemirror/state";
|
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 { 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 { 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";
|
import { oneDark } from "https://esm.sh/@codemirror/theme-one-dark@6.1.0?external=@codemirror/state";
|
||||||
|
|
||||||
const basicSetup = [
|
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>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user