diff --git a/khata/web/static/editor.js b/khata/web/static/editor.js index 4ffb858..862e2b0 100644 --- a/khata/web/static/editor.js +++ b/khata/web/static/editor.js @@ -2,12 +2,15 @@ (function () { "use strict"; - function uploadImage(uploadUrl, file, onSuccess, onError) { + function upload(uploadUrl, file, onSuccess, onError) { const fd = new FormData(); fd.append("file", file); fetch(uploadUrl, { method: "POST", body: fd }) .then(async (r) => { - if (!r.ok) throw new Error(`Upload failed: ${r.status}`); + if (!r.ok) { + const txt = await r.text().catch(() => ""); + throw new Error(`upload ${r.status}: ${txt.slice(0, 120)}`); + } return r.json(); }) .then((j) => onSuccess((j && j.url) || (j && j.data && j.data.filePath))) @@ -19,30 +22,42 @@ textarea.dataset.khataMounted = "1"; const uploadUrl = textarea.dataset.upload; + const hasUpload = !!uploadUrl; const easy = new EasyMDE({ element: textarea, autoDownloadFontAwesome: false, spellChecker: false, status: ["lines", "words"], - minHeight: "160px", - uploadImage: !!uploadUrl, - imageUploadFunction: uploadUrl - ? (file, onSuccess, onError) => uploadImage(uploadUrl, file, onSuccess, onError) - : undefined, + minHeight: "200px", + // Enables paste + drag-drop + the 'upload-image' toolbar button. + uploadImage: hasUpload, + imageAccept: "image/png, image/jpeg, image/webp, image/gif", + imageMaxSize: 10 * 1024 * 1024, imagePathAbsolute: true, + imageUploadFunction: hasUpload + ? (file, onSuccess, onError) => upload(uploadUrl, file, onSuccess, onError) + : undefined, + // Toolbar items must use 'upload-image' (not 'image') to trigger the + // file-picker + imageUploadFunction flow. 'image' only inserts a stub + // `![alt](url)` line, which was the source of "can't upload" confusion. toolbar: [ "bold", "italic", "heading", "|", "quote", "unordered-list", "ordered-list", "|", - "link", "image", "code", "|", - "preview", "side-by-side", "|", + "link", hasUpload ? "upload-image" : "image", "code", "|", + { + // Custom preview-toggle button with an explicit title so users can + // see how to come *back* from preview mode. + name: "preview", + action: EasyMDE.togglePreview, + className: "fa fa-eye no-disable", + title: "Toggle preview (click again to go back)", + noDisable: true, + }, + "side-by-side", "fullscreen", "|", "guide", ], - previewRender: (text) => { - // Use marked if available — otherwise EasyMDE's default. marked ships - // inside EasyMDE. - return easy.markdown(text); - }, + previewRender: (text) => easy.markdown(text), }); // Keep the native textarea's value in sync so HTMX form submission works. diff --git a/khata/web/static/style.css b/khata/web/static/style.css index 089913b..d192d7d 100644 --- a/khata/web/static/style.css +++ b/khata/web/static/style.css @@ -436,9 +436,16 @@ h2 { } .note-block .editor-toolbar button.active, .note-block .editor-toolbar button.fa.active { - background: var(--surface); - color: var(--accent) !important; - border-color: var(--border); + background: var(--accent); + color: var(--accent-ink) !important; + border-color: var(--accent); +} +/* Preview/fullscreen dim other buttons. Keep them readable so the user can + still see the preview button to click it again and return. */ +.note-block .editor-toolbar.disabled-for-preview button:not(.no-disable), +.note-block .editor-toolbar button[disabled] { + opacity: 0.35; + cursor: not-allowed; } .note-block .editor-toolbar i.separator { border-color: var(--border); }