The SSE streaming path hardcoded '耗时: -' because duration_ms was
never threaded through:
- docker.rs: OutputChunk::Done now carries duration_ms, computed from
start_container to wait completion via Instant::elapsed
- sse.rs: DonePayload gains duration_ms, serialized into the done event
- runner.rs: WASM done callback formats the real duration instead of '-'
The polling fallback path already had correct duration via ExecResult;
only the SSE path was missing it.
The output area was always visible (empty terminal container shown on
page load). Now it only appears after the user clicks Run, with three
states:
- Not run: output area hidden entirely (show_output = false)
- Running, no output yet: skeleton screen placeholder
(running && !has_output)
- Output received: skeleton gone, xterm renders streamed content
Two new signals drive this:
- show_output: set true on Run click, controls output area visibility
- has_output: set true on first stdout/stderr chunk (SSE) or writeAll
(polling fallback), dismisses the skeleton
xterm mount effect now reads show_output, so it retries after the
container div enters the DOM (when show_output flips true).
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.
CodeRunner 根 div 没有任何垂直外边距,而普通代码块经 .md-content pre
规则带 margin-bottom: var(--content-gap-paper)(20px)。可运行块根元素是
div 而非 pre,该规则不生效,导致可运行块紧贴其后正文——上方靠前段落
margin 折叠勉强可见,下方完全无间距。
给根 div 加 mb-[var(--content-gap-paper)](沿用组件既有的 var() 任意值
写法),与普通代码块、段落保持一致的 20px 间距。