From 2e65745aeda1f04ba75bd9ae2078053e86202f1d Mon Sep 17 00:00:00 2001 From: NguyenND Date: Fri, 24 Jul 2026 11:21:03 -0400 Subject: [PATCH] Jul 24 - Update Rich-text editor to enhance table editor 3 --- .claude/settings.json | 3 ++- CLAUDE.md | 5 +++++ templates/admin/topic_form.html | 14 +++++++++++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.claude/settings.json b/.claude/settings.json index ac1d33d..df26673 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -29,7 +29,8 @@ "Bash(cp /tmp/qtb.css static/vendor/quill-table-better.css)", "Bash(python -m py_compile app.py admin.py)", "Bash(cd /tmp *)", - "Read(//tmp/**)" + "Read(//tmp/**)", + "Bash(curl -sSL https://raw.githubusercontent.com/attoae/quill-table-better/main/src/utils/clipboard-matchers.ts -o cm.ts -w \"cm %{http_code}\\\\n\")" ] } } diff --git a/CLAUDE.md b/CLAUDE.md index 31afcec..12e6bc2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -127,6 +127,11 @@ shows only `is_published` topics (sections with no published topics are hidden). custom wiring. Init is guarded by `typeof QuillTableBetter` so a failed vendor load still leaves a working editor + textarea. Registration and keyboard bindings (`QuillTableBetter.keyboardBindings`) live in `topic_form.html`. + - **Save/load round-trip (critical):** save with `deleteTableTemporary()` then + `quill.getSemanticHTML()`. **Load must NOT use `dangerouslyPasteHTML`/`setContents`** + — quill-table-better renders tables *blank* that way. Instead convert then apply: + `quill.updateContents(quill.clipboard.convert({html}), 'user')`. The non-table + fallback path still uses `dangerouslyPasteHTML`. - **Sanitize on save:** `sanitize_html()` (bleach) runs on every `body_html` write — `ALLOWED_TAGS`/`ALLOWED_ATTRS` cover `img` + full table tags with `colspan/rowspan/data-*/style`; a `CSSSanitizer` (bleach[css] + `tinycss2`) diff --git a/templates/admin/topic_form.html b/templates/admin/topic_form.html index 47f7071..a8a52a9 100644 --- a/templates/admin/topic_form.html +++ b/templates/admin/topic_form.html @@ -168,7 +168,19 @@ } var quill = new Quill(editorEl, { theme: 'snow', modules: modules }); - if (ta.value.trim()) quill.clipboard.dangerouslyPasteHTML(ta.value); + + // Load existing body. quill-table-better warns that setContents / + // dangerouslyPasteHTML make tables render blank, so convert the saved HTML + // to a delta and apply it with updateContents instead. + if (ta.value.trim()) { + if (hasTables) { + var delta = quill.clipboard.convert({ html: ta.value }); + quill.updateContents(delta, Quill.sources.USER); + quill.setSelection(delta.length(), Quill.sources.SILENT); + } else { + quill.clipboard.dangerouslyPasteHTML(ta.value); + } + } var csrf = document.querySelector('meta[name="csrf-token"]').content;