Assistant text parts now render through the NATIVE markdown renderable instead of
plain spans — bold/headings/lists/fences render, raw `**`/backtick markup is
concealed (spec §7; never hand-roll a parser).
- view/markdown.tsx: `<code filetype="markdown" streaming conceal drawUnstyledText>`
(CodeRenderable — opencode's v2 AssistantText path; `<markdown>` +
internalBlockMode="top-level" deferred paint headlessly). SyntaxStyle.fromStyles
is derived from the theme (markup.* → theme.color.*, non-hex colors guarded) and
cached by theme-object identity so all text parts share one instance, rebuilt
only on skin change. drawUnstyledText paints raw text immediately while
Tree-sitter highlighting settles (and makes it headless-capturable).
- view/messageLine.tsx: text-part Match renders <Markdown> instead of <text>.
- test/lib/render.ts: settle async markdown via flush(); captureFrame gains an
`until` option (waitForFrame) for content that paints after the first pass.
Verified: bun run check green (23 tests / 5 files). Live tmux: a markdown reply
(heading + bold word + 2-item list) rendered with `**` concealed (grep -c '**' = 0);
Ctrl+C clean, no orphan. Phase 2 complete (2a shell + 2b-i parts/tools + 2b-ii
markdown) — smoke steps 1–4 run live. Next: Phase 3 blocking prompts.