dyn_into runs 'instanceof TiptapEditorModule', but the IIFE TiptapEditor
is a plain object literal — not an instance of wasm-bindgen's registered
constructor — so the check always fails with a panic.
unchecked_into does only the compile-time type annotation; Reflect::get
already guarantees we have the right object.
The extern 'fn get_module() -> TiptapEditorModule' was compiled by
wasm-bindgen to 'window.TiptapEditor()' — a function call. But
TiptapEditor is the IIFE module object, not a function, so this threw
'window.TiptapEditor is not a function'.
Replace with js_sys::Reflect::get(&window, "TiptapEditor") +
dyn_into, which reads the property without invoking it.
This bug predated the Vite 8 upgrade but only surfaced after the
EditorOptions fix let initialization progress further.
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.
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.
待审核评论显示真实相对时间,而非永久"刚刚"
pending_item.rs 改用 format_relative_time_iso 基于创建时间动态计算
相对文本(承接上一个 relative time 重构 commit),并加 title 悬浮提示
显示原始 ISO 时间。修复待审核评论一直显示"刚刚"的问题。
DB migration failure at startup panicked via .expect() (main.rs:223),
dumping a raw backtrace for what is usually just "PostgreSQL isn't running
yet". Worse, the 1.6s runtime retry budget was reused at startup, so it
almost always failed against a cold/slow DB (docker-compose without
healthchecks, local Postgres not started, etc.).
Changes:
- pool: extract build_pg_config() (returns Result, no panic); add
validate_database_url() so URL-format / DB_POOL_SIZE errors surface as
friendly exit(1) instead of hitting the LazyLock's unreachable .expect()
- pool: add get_conn_for_startup() — a startup-only retry loop with a
configurable total-duration window (MIGRATE_STARTUP_TIMEOUT_SECS,
default 30s, 500ms interval), separate from the runtime get_conn()
anti-avalanche fast retry (untouched)
- migrate: split run() into run_on_conn(&mut conn) so callers control the
connection-acquisition strategy; main.rs pairs this with the startup
retry. Advisory-lock release comment updated: exit(1) terminates the
process just like panic, so the session-level lock is still freed
- main: startup fatal errors (bad URL, DB unreachable, migration failed)
now each emit tracing::error! + eprintln! ERROR + targeted HINT, then
exit(1) — no panic, no backtrace nudge
- .env.example / AGENTS.md: document MIGRATE_STARTUP_TIMEOUT_SECS
No runtime behavior change: get_conn() and its ~40 call sites are
unchanged. Advisory-lock safety preserved (documented in migrate.rs).
upload_image declared ConnectInfo<SocketAddr> as a required Axum extractor,
but dioxus::server::serve() owns the listener and cannot call
into_make_service_with_connect_info::<SocketAddr>(), so the request extension
is absent on manually-merged routes. The extractor failed and Axum returned
500. dev (dx serve) passed by luck; release builds failed consistently.
Match serve_image's graceful-degradation pattern: take
Option<Extension<ConnectInfo<SocketAddr>>>, fall back to the 'unknown'
rate-limit bucket when missing. Production should deploy behind a reverse
proxy with TRUSTED_PROXY_COUNT so rate limiting keys on the real client IP.
Also correct the misleading comment in main.rs: axum 0.8 does have
ConnectInfoLayer/into_make_service_with_connect_info; the real blocker is
that Dioxus owns the listener.