cdea8d544807d0041243b5bbc9f420e06cb968dd
Replies rendered their newlines away and their tables into mush. The parser was never the problem. `AttributedString(markdown:)` is cmark-gfm underneath and records everything — probed against a three-column table, it returns the four characters `ab12` with every cell carrying `[tableCell 0, tableHeaderRow, table [.left, .center, .right]]`. Column count, alignments, header row and cell indices are all there. SwiftUI's `Text` reads inline attributes only and ignores `presentationIntent` entirely, so the structure was parsed and then discarded at draw time. `a\n\nb` has the same shape: two paragraph intents, and a character buffer that literally reads `ab`, because emitting a newline is the renderer's job and there was no renderer. So no new dependency. `apple/swift-markdown` would have bought a ready tree and task-list checkboxes, at the price of a second Markdown parser and a C target in a build that already carries three vendored ones — and the actual labour, the SwiftUI block views, is identical either way. What it costs instead is one pass that rebuilds nesting from the flat run list: walk the runs, keep a stack of open containers, compare the `identity` ints Foundation already hands out. `components` is ordered innermost-first, which the grouper relies on and a test now pins, because that is an observed fact about the framework rather than a contract. Soft breaks are the one place we leave the spec. CommonMark renders a lone newline as a space and Foundation obeys, emitting an untagged `" "` run that is indistinguishable from a typed space — so the choice cannot be made after parsing. It is made on the source, by appending a backslash hard break the way GitHub's comment renderer does, because a model that wrote a newline meant one. Every guard in `MarkdownSoftBreaks` marks somewhere that edit would corrupt rather than break: fenced and indented code, table rows, headings, thematic breaks, setext underlines, list markers. One of them is not cosmetic. `composed(_:)` folds attachments into the message as `<output label="…">…</output>` and promises the bubble shows exactly what was uploaded; Foundation returns that untouched as one HTML block, down to the `*` that emphasis would otherwise eat, so editing a byte inside one would break that promise. Two hazards found while wiring the shell fence card. `SSHSessionModel .insert` writes raw bytes with no bracketed-paste wrapper, so an embedded newline reaches the PTY as Return — "Insert" on a three-line script would execute the first two while presenting itself as a preview. Multi-line fences therefore get the risk badge and a copy button and nothing that touches a session. And `selectedTab` is nil whenever the pinned Assistant tab is showing, which is exactly where the full-width transcript lives: `CommandCardView`'s Insert and Run have been dead there all along. The new card disables them instead of looking live and doing nothing. Rendering model output is a trust surface, and tool results are fed back into it. Only `http`, `https` and `mailto` become clickable; a refused scheme keeps its label as prose and names its destination on hover, which is what stops `[click here](file:///…)` from reading as ordinary text. Raw HTML is never interpreted. Images needed nothing — Foundation drops the URL and keeps the alt text, closing the channel where a model-chosen image address discloses the reader's IP and reading time the moment a message renders. The transcript is rebuilt around it. User turns are trailing bubbles that size to their contents, assistant replies have no container at all, and the rules between turns are gone because alignment and fill already say who is speaking. Tool calls collapse to one line that opens itself while the call is in flight or after it failed. Attachments fold to a labelled row, unaltered and one click from open, rather than printing two hundred lines of log into a right-aligned bubble. `ConversationView` and its cards move out of `ContextPanelView`, where they had nothing to do with the context panel. Two fixes fall out of measuring the result. `TextEditor` has no content-based ideal height — given `minHeight: 46, maxHeight: 120` it takes 120, so an empty composer stood 185pt tall with the placeholder marooned at the top; its height now comes from a hidden twin of the draft and starts at 40. And the table rendered blank: a horizontal `ScrollView` proposes unbounded width, the header `Divider` is greedy and took it, and every centred or trailing column was aligned to an edge no one can reach. Cells wrap instead, which also reads better at 260pt than a scrolling region nested inside a scrolling one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Description
AI-driven remote workspace for Mac, iPhone, and iPad.
https://termind.akinokaede.com
23 MiB
0 Stars
1 Watchers
0 Forks
Languages
Swift
98.9%
Python
0.8%
Ruby
0.3%