Diagram rendering degraded to the mermaid source correctly for engine
refusals, but three paths could not recover and none of them left a trace
in the log.
Log every failure. An engine crash was converted to a Fault.Internal
carrying only err.message, discarding the throwable, so a parser or layout
bug reached the user as an unactionable red label and left nothing to
report. It now logs with the stack trace, as does a failing completion
callback and a failed image allocation.
Close the fallback holes. The completion callback ran unguarded inside the
render coroutine, so a failure in it was reported as a plugin error and
left the block pending forever. Painters.of used first {}, turning a future
art type without a painter into an exception in both paint and sizing.
Painting itself was unguarded, and by the time it runs the source pane is
already hidden, so a failure left a blank surface with no way back; paint
failures now hand a fallback to the owner, which restores the source.
diagramImage allocated 2x the scene with no ceiling, and Limits caps the
model rather than the geometry, so a legal diagram could ask for a
multi-gigabyte raster on the EDT the moment someone pressed copy.
Bound the work that limits did not cover. Cancellation is only checked
between lines, so one line needed its own cap, and per-index open/quote
scanning is replaced by a single pass because it made a long line
quadratic. FlowMarks was the only phase with no cancellation point at all
and resolved frame members by recursing per cluster, rescanning every node
each time; it now resolves bounds and depth in one reverse pass. The
remaining FlowLayout phases gained per-iteration checks, and a wall clock
ceiling ends a render that outlasts its cooperative checks as a limit
fault instead of an endless "rendering" state.
Unsupported diagram types now read as a muted note rather than an error.
classDiagram, stateDiagram and friends are valid mermaid this engine does
not draw, and marking each one red reported working markdown as broken.
Addresses PR review on the mermaid diagram engine:
- Share AwtMeasure safely. The engine service holds one instance and draws
off the EDT on the default dispatcher, so the font cache is now a
ConcurrentHashMap. The Graphics2D that had no dispose() is already gone.
- Refuse pathological sources earlier. A character cap is checked before any
preprocessing, node and link caps are enforced while the model is built so
`A & B & ... --> ...` cannot expand first, and both parsers check
cancellation per line. The `%%{...}%%` mask is a linear scan instead of a
lazy regex that rescanned to end-of-text per unterminated opener, and the
message regex can no longer backtrack over a colon-free line.
- Assign subgraph membership on re-mention. `Client --> Gateway` followed by
`subgraph core` / `Gateway --> Auth` now puts Gateway inside the frame,
which is how mermaid reads it.
- Report sequence scene size from glyph extents, not anchors, so a
left-anchored self-message label is inside the bounds a renderer clips to.
- Close activations left open at the end of a script, so `A->>+B: hi` with no
matching deactivate still draws its bar.
- Parse participant ids through unquote and match ` as ` case-insensitively
outside quotes, so `participant "Alice"` and `PARTICIPANT C AS Client`
resolve to one column.
assertInBounds now checks text extents rather than anchors, so this class of
overflow fails in tests instead of clipping at paint time.
activitySnapshot() merged statuses and activity, two StateFlows that each
prune the removed-session set through their own collector. After a delete,
one flow can still carry the session while the other has already dropped
it, so the snapshot briefly reported a deleted session as RUNNING and any
consumer reading it at that moment rendered a stale badge.
Subtract the removed set in the snapshot itself so the merged view is
consistent regardless of collector ordering, and wait on both flows in the
test that asserts every activity kind, which had the same latent race.
This fixes the flaky KiloSessionServiceTest failure on main.