several bugs found via in-browser debugging, all in lightbox.js + the
blur-up CSS:
1. DOMRect attribute mismatch: getBoundingClientRect returns .width/
.height, but code read .w/.h -> NaN transforms. map rectOf() to a
uniform {x,y,w,h}.
2. zoom scale base was the huge original natural size (2942px), so the
centered state collapsed to a tiny scale and the open animation ran
backwards (shrink). base the scale on originRect instead: first
frame is the in-article image at scale 1, centered state scales to
target.w/originRect.w. open is always grow, close always shrink.
3. scroll-to-close fired on a phantom scroll jump: the oversized
original img (layout size = naturalW) expanded the document's
scrollable area. set img layout size explicitly and add
overflow:hidden on the (position:fixed) overlay so the img cannot
expand the body.
4. first open animation jumped from the wrong position: a single rAF
was not enough to commit the no-transition first frame. use a forced
reflow (offsetHeight) + double-rAF before starting the transition.
5. blur-up full layer stuck at opacity 0 until a forced reflow (opening
the lightbox) repainted it. drop the opacity transition on .blur-img-
full and set opacity:1 from JS on load (deterministic, no repaint
race). mark the container .is-loaded and hide the placeholder via
the container class.
6. a 16px grey gap above in-article images: .md-content img { margin:
1rem 0 } applied to the absolute .blur-img-full too, pushing it
down. reset margin:0 / max-width:none on the blur-up img layers.
remove the duplicated .image-viewer-* and .md-image-lightbox-* rules
(~93 lines); add a single .lightbox-* set sized/positioned by JS
transform. entry-cover and post-card-cover now target the blur-img
structure (4:3 ratio on cards, cover-fill on entry cover).
both call sites (PostCover, PostCard) now render raw .blur-img, so the
Dioxus ImageViewer component — with its leaky closure.forget() and dead
js_sys::eval delay — is no longer needed. remove the file and its mod
declaration.
card cover is display-only (click goes to the card link), so it uses
a bare .blur-img with ?w=20 placeholder + ?thumb=400x300 display, no
lightbox-single class. card pages are outside .post-content/.entry-cover
so lightbox.js never scans them.
drop ImageViewer; render .blur-img with a lightbox-single class (the
class, not a data attribute, because Dioxus' typed span elements reject
arbitrary data-* attrs). lightbox.js now keys single-mode off the
lightbox-single class instead of data-single. cover uses ?w=20
placeholder + ?w=1200 display; SSR writes --ar from real dimensions.
eval both post-content.js (copy) and lightbox.js (lightbox + lazy-load),
then call __initLightbox over ['.post-content', '.entry-cover'] so both
in-content images (gallery) and the cover (single) are wired up.
add prev/next arrow buttons (only in gallery mode with >1 image) and
ArrowLeft/ArrowRight keyboard handling. switching uses a 150ms
fade-out -> swap src -> fade-in (no fly, matching Medium). originNode
is updated to the new image so close/scroll-close flies back to the
right place; openScrollY is reset to keep scroll-close baseline valid.
bindInteractions() was only called from start(), which waits for the
image load event. during the load window Esc/scroll/backdrop-click did
nothing. bind right after appending the overlay so the lightbox is
closable throughout the load.
click a .blur-img to open: record its viewport rect, the lightbox img
starts at that position/size and transforms to centered (Medium-style
origin-aware zoom). close flies back to the live originNode rect.
scroll-to-close: while open, any window scroll drives the image back
to its (moving) position via per-frame rect tracking + linear
interpolation; the article keeps its scroll position on close.
Esc + click-on-backdrop close; click-on-image does not close.
prefers-reduced-motion falls back to fade-only / immediate close.
strip syntect-injected background-color from generated CSS so the code
block background comes from a single source (--color-paper-code-block
in input.css), avoiding double-layer color mismatch at the .code span
edge. update the variable values for both light and dark themes.
The tag splitter matched ASCII ',' and ';' twice instead of the
fullwidth ',' (U+FF0C) / ';' (U+FF1B) the comment intended, so tags
separated by fullwidth punctuation (common under Chinese IMEs)
collapsed into a single tag.
Also resolves the compiler warnings surfaced in the same build:
- tiptap_bridge: drop unused Readable/Writable imports and
EditorInstance/TiptapEditorModule re-exports
- tiptap_bridge: migrate deprecated RequestInit::method/body/
credentials builders to set_method/set_body/set_credentials
- write: remove redundant `mut` on Dioxus signals that are only
read/set, never reassigned
- image_viewer: gate the server-only `mut` reassignment so the WASM
build (which strips the cfg block) doesn't warn
Adds src/tiptap_bridge.rs with extern bindings for window.TiptapEditor
module, EditorInstance, EditorOptions (builder), UploadEventJs.
EditorHandle unifies instance + 4 closures lifecycle (Drop calls destroy).
consume_upload_event replaces the old polling body. make_upload_closure
does the /api/upload fetch in Rust (web_sys) instead of eval'd JS.