xterm.js terminal background was Catppuccin Base (#eff1f5 / #1e1e2e),
but the project's code-block background CSS variable uses Catppuccin
Surface0 (#dce0e8 light / #313244 dark). The mismatch left the output
area visibly lighter (light mode) / darker (dark mode) than the
surrounding container.
WASM path: start_exec_stream → EventSource SSE → xterm.js terminal.
stdout/stderr events write to the terminal in real time; done event
sets exit status and closes the connection. Falls back to polling
get_exec_result (writeAll) if EventSource creation fails.
Server path (placeholder, component runs client-side): retains the
original polling logic so both targets compile.
- xterm.js terminal mounted via xterm_bridge (mirrors CodeMirror mount
pattern: use_effect + TerminalHandle + use_drop)
- run_code split into #[cfg(wasm32)] (SSE) and #[cfg(not)] (polling)
- output area: <pre> replaced with xterm container div
- poll_result / start_sse helpers in WASM-only sse_consumer module
- term_handle declared at component scope (cfg-gated) so the WASM
run_code closure can capture it
Note: dx check reports use_resolved_theme-in-closure for the new xterm
effects, same as the pre-existing CodeMirror effects (lines 145, 177).
This is an existing project-wide pattern; cargo build on both targets
and cargo clippy pass clean.
run_in_container_stream: same container lifecycle + ContainerGuard
cleanup as run_in_container, but pushes output chunks to an mpsc
Sender as the log stream is read, instead of buffering until the
container exits. Also retains a full buffer for the caller to write
back to EXEC_TASKS (polling fallback path).
- OutputChunk enum: Stdout/Stderr/Done{exit_code,oom_killed,timed_out}
- client disconnect detection: tx.send fails → stops pushing but
continues draining the log stream so the container exits cleanly
- Done chunk carries terminal status for the SSE done event
- timeout/inspect/OOM logic identical to run_in_container
- run_in_container and its 4 tests left untouched
Mirrors codemirror_bridge.rs: Reflect::get + unchecked_into for the
IIFE object literal window.XtermTerminal, XtermOptions as wasm-bindgen
extern type (TS class survives erasure), TerminalHandle with Drop →
destroy(). WASM-only (pub mod wasm gated on target_arch = wasm32).
Dioxus.toml: register /xterm/terminal.{css,js} in both prod and dev
resource arrays. CSS is emitted as a separate file by vite lib mode
(cssCodeSplit:false still extracts one CSS file for IIFE).
- tokio-stream 0.1 (sync feature) wraps mpsc::Receiver into a Stream
for the upcoming axum SSE handler; axum::response::sse ships with
axum 0.8 already so no extra dep needed there
- web-sys features: add EventSource + MessageEvent so the WASM frontend
can consume SSE via the native EventSource API
Cross-compiling x86_64-unknown-linux-musl from an arm64 buildx leg breaks
ring: cc-rs emits -m64 for the x86_64 target but hands it to Debian's
arm64 musl-gcc (shipped for host arch only), whose cc1 has no -m64 →
'cc1: error: unrecognized command-line option "-m64"'.
Each buildx platform leg now builds its own native musl target
(amd64→x86_64, arm64→aarch64) so musl-gcc and the target always match —
no cross-compiler, no QEMU for the compile step. The built binary is
staged at an arch-independent path so the scratch runtime stage can COPY
it without knowing which target was used.
Makefile gains a docker-multiarch target (buildx --platform
linux/amd64,linux/arm64 --push); 'make docker' now uses buildx --load
for local single-arch testing.
A transparent HTTP proxy on the build host (http_proxy=127.0.0.1:10808)
truncates apt's plain-HTTP downloads to a 2578-byte HTML error page, which
apt rejects with 'Clearsigned file isn't valid, got NOSPLIT'. curl/wget
inside the same container fetch the full 151074-byte InRelease fine, and
HTTPS apt sources work — so switch the builder's Debian sources to HTTPS.
Proven by: apt-get update over HTTPS fetched 9264 kB successfully (4m27s
through the throttled proxy); plain HTTP failed with NOSPLIT every time.
CodeRunner 根 div 没有任何垂直外边距,而普通代码块经 .md-content pre
规则带 margin-bottom: var(--content-gap-paper)(20px)。可运行块根元素是
div 而非 pre,该规则不生效,导致可运行块紧贴其后正文——上方靠前段落
margin 折叠勉强可见,下方完全无间距。
给根 div 加 mb-[var(--content-gap-paper)](沿用组件既有的 var() 任意值
写法),与普通代码块、段落保持一致的 20px 间距。
Without a key, Dioxus 0.7 VDOM diffing may reuse hook slots from
the previous tab component on tab switch. This caused DelayedSkeleton's
visible signal to retain the old true value, bypassing the 200ms delay.
Adding key based on active_tab forces full unmount/remount on tab switch.
The sticky footer with -mt-12 negative margin caused a jump when
scrolling to bottom: sticky bottom-0 positions against the scroll
container's padding box, but the negative margin pulls the element up
by 48px, so at scroll-end the footer shifts up and clips the editor.
Replace sticky+neg-margin with a proper flex split layout:
- AdminLayout: write route now gets overflow-hidden on the card and a
padding-free, non-scrolling flex main; other admin pages unchanged.
- write.rs: root becomes flex-col; content area is flex-1 overflow-y-auto
with the px-10 py-12 padding moved here; footer is a flex-shrink-0
sibling that sits at the card bottom permanently, never scrolls, never
jumps.
min-h-0 added on flex children so overflow can shrink below content
height (required for flex + overflow to work).
After the shell unification, main already provides py-12 (48px) top
padding via the shared admin shell. The write page's own pt-8 (32px)
was a leftover from when it had no outer card, so it stacked to 80px —
much larger than other admin pages. Remove it to match.