Skip to content

feat(swift-ios): show workspace images inline - #7378

Closed
saphid wants to merge 8 commits into
pingdotgg:t3code/rebuild-mobile-app-swiftfrom
saphid:feat/issue113-inline-workspace-images
Closed

feat(swift-ios): show workspace images inline#7378
saphid wants to merge 8 commits into
pingdotgg:t3code/rebuild-mobile-app-swiftfrom
saphid:feat/issue113-inline-workspace-images

Conversation

@saphid

@saphid saphid commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Observed problem and reproduction

In the SwiftUI chat transcript, Markdown image references to workspace files were rendered as alternative text instead of as images. A representative reproduction is ![chart](out/render.png) in a message whose thread workspace contains out/render.png.

Cause

At the PR's original base, the native Markdown model had no image block and the transcript had no workspace-aware image resolver/rendering seam.

Change and integration boundary

The original contribution added standalone-image parsing, safe workspace-relative resolution through the existing signed asset route, bounded thumbnail loading, hosted-cell context, failure UI, and focused regressions.

For this conflict pass I selected a rebase onto the live target 22b22f1463b83963d851bd0574a36a20f224a28d. The first commit conflicted in MarkdownDocument.swift and ThreadDetailView.swift. Inspection showed that Theo's newer target commit cd2b2f8fef42e3762d03425e9d43f5a3255d8625 independently added the same capability end to end, including broader inline/direct-image behavior and focused source tests. The rebase was aborted. Upstream remains byte-for-byte unchanged and the Saphid head remains 60c3e1c56a7d0acb05d6d4fbd40d57fd8e70eadc; this PR is superseded rather than rebased into a duplicate.

Non-goals and deferred work

This pass does not alter Theo's newer parser, image loader, workspace resolver, or transcript composition. It does not port the old PR's review-fix series into another contributor's implementation. No web, desktop, React Native mobile, server, contract, provider adapter, or connection behavior is changed.

Affected areas

  • Client and entry point: SwiftUI mobile chat transcript Markdown rendering.
  • Platform: iOS and iPadOS native client.
  • Providers: none; image rendering is provider-independent.
  • Contracts/runtime: the existing signed workspace-asset route is reused by the superseding target implementation; no schema or server change is introduced here.
  • Connections: the implementation is transport-agnostic, but this conflict pass did not exercise local, remote, relay, or tunnel runtime behavior.
  • Reverse/failure states: blocked sources remain hidden or textual according to the target implementation; load failures retain its placeholder behavior.

Validation

  • Ownership freeze: gh pr view 7378 --repo pingdotgg/t3code ... confirmed author/head owner saphid, head branch feat/issue113-inline-workspace-images, maintainer edits enabled, old head 60c3e1c56a7d0acb05d6d4fbd40d57fd8e70eadc.
  • Live target fetch: git fetch origin refs/heads/t3code/rebuild-mobile-app-swift:refs/remotes/origin/t3code/rebuild-mobile-app-swift resolved 22b22f1463b83963d851bd0574a36a20f224a28d.
  • Rebase: git rebase refs/remotes/origin/t3code/rebuild-mobile-app-swift stopped at the original feature commit with only the two overlapping conflicts above; git rebase --abort exited 0.
  • Supersession inspection: target source contains MarkdownImage, MarkdownImageSource, workspace/direct image loading, transcript image context, and focused MarkdownDocumentTests introduced by cd2b2f8fe.
  • Focused target test attempt 1: xcodebuild ... test -only-testing:T3CodeTests/MarkdownDocumentTests exited 65 before tests because the simulator code-signing subsystem reported an internal error.
  • Focused target test attempt 2: build and signing completed, but Xcode stalled at Launch T3CodeTests while disk headroom fell to 366 MiB. The exact owned process group was terminated (exit 143), the corrupt incomplete result bundle was identified by xcresulttool exit 64, and zero tests are claimed. The private DerivedData and corrupt bundle were removed after preserving logs and receipts.
  • No source or branch bytes changed, so there is no new contribution head to validate or push.

Risks, limitations, and known gaps

The superseding target implementation was verified by source and focused target coverage inspection, but this pass did not obtain a successful new local test execution because of the two recorded Xcode/disk infrastructure failures. Historical old-head tests and media do not prove the live target or a hypothetical rebased head.

Known gaps: full-size current-head light/dark before-and-after proof is queued in saphid/t3code-personal#150.

No review is requested because this PR is being closed as superseded.

Evidence

The following images are historical evidence for old PR head 60c3e1c56; they are explicitly not current-target or post-rebase proof.

Historical after, light appearance

Historical inline workspace image in light appearance

Historical after, dark appearance

Historical inline workspace image in dark appearance

There is no current-head before/after or loading video in this handoff.

Owning issue and stack

Owning lane: saphid/t3code-personal#113. The PR targeted t3code/rebuild-mobile-app-swift, which remains related to #5178. The contribution is superseded by Theo's target commit cd2b2f8fef42e3762d03425e9d43f5a3255d8625 and is not stacked on #7345.

Implemented and reconciled by GPT-5.6 Sol high in the Codex harness.

@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 87138f54-671d-463d-b58c-7e096e98662c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 18, 2026
Comment thread apps/swift-ios/Features/Chat/MarkdownDocument.swift
Comment thread apps/swift-ios/Features/Chat/MarkdownWorkspaceImageView.swift
@macroscopeapp

macroscopeapp Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a new feature - inline rendering of workspace images in Markdown messages. Despite being self-contained with good test coverage and proper path validation, the new user-facing capability warrants human review.

You can add or adjust custom eligibility rules. Learn more.

@github-actions github-actions Bot added size:XL 500-999 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Aug 18, 2026
@saphid

saphid commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

Both High-severity findings against 90b3ac41f were verified and fixed in e45d52bcc:

  • MacroscopeMarkdownImageReference.parse preserved backslash escapes, so ![plot](out/foo\(1\).png) requested the literal name and failed to load. Escaped ASCII punctuation is now unescaped in both the destination and the alternative text. New focused test covers escaped parentheses, escaped brackets, and a literal backslash in a path.
  • Cursor BugbotMarkdownWorkspaceImageView rendered its @State image without checking it belonged to the current request, so a recycled UIHostingConfiguration cell could show another message's image. Loaded and failed state are now gated on request identity, exactly as the sibling FeatureRemoteAttachmentThumbnail does it, and the loader keys its cache from the same request. Cancellation is now handled with Task.checkCancellation() plus a CancellationError branch, matching that sibling too. This is a SwiftUI view-state repair, so it is verified by rebuild and review rather than by a unit test.

Re-run at the current head e45d52bcc, isolated temporary DerivedData, real exit statuses:

  • Focused suites: exit 0, ** TEST SUCCEEDED **, 29 passed, 0 failed, 0 skipped.
  • Full native unit bundle: exit 0, ** TEST SUCCEEDED **, 367 passed, 0 failed, 1 skipped (368 total).

The accepted on-device behaviour (Test 89, built from 90b3ac41f) was not changed to make these repairs. The GPT-5.6 Sol cross-provider review will run against e45d52bcc after the 2026-08-20T03:29Z quota reset; no Sol findings or approval are claimed yet.

Comment thread apps/swift-ios/Features/Chat/MarkdownDocument.swift
Comment thread apps/swift-ios/Features/Chat/MarkdownWorkspaceImageView.swift Outdated
@saphid

saphid commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

Second round, against e45d52bcc.

Fixed in 9803983 — Macroscope Medium, quoted titles: a title holding an unbalanced parenthesis, as in ![plot](out/plot.png "generated (final"), made delimiter balancing miss the real closing ), so the line fell back to alternative text. Balancing now ignores quoted text while still accepting balanced parentheses inside a file name (out/foo(1).png still parses). Focused test covers both cases.

Acknowledged, deliberately not changed — Macroscope Medium, cache freshness: overwriting a workspace file at the same thread and path keeps the previously decoded bitmap until the cache evicts it. The reviewer is right about the mechanism. Invalidating it correctly needs a server-provided content version for a workspace asset, which is a wider change than this PR should make; dropping the cache instead reintroduces a signed-URL round trip and a re-download on every transcript cell recycle, which is the problem this cache was added to solve. A transcript is also a historical record of what a message referenced. Glad to follow up separately if a content version is wanted here.

Re-run at 9803983, isolated temporary DerivedData, real exit statuses:

  • Focused suites: exit 0, ** TEST SUCCEEDED **, 30 passed, 0 failed, 0 skipped.
  • Full native unit bundle: exit 0, ** TEST SUCCEEDED **, 368 passed, 0 failed, 1 skipped (369 total).

The two earlier High findings remain fixed in e45d52bcc; the on-device accepted behaviour (Test 89, built from 90b3ac41f) is unchanged by any of these repairs.

Comment thread apps/swift-ios/Features/Chat/MarkdownDocument.swift Outdated
Comment thread apps/swift-ios/Features/Chat/MarkdownDocument.swift
@saphid

saphid commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

Third round, against 9803983. Both bots flagged the same defect and they were right — it was a regression from my previous fix.

Fixed in c8e303d77 — ignoring quoted text while balancing delimiters made an apostrophe in a file name, as in images/team's-logo.png, open a title that never closed, so the reference fell back to alternative text. A title is separated from its destination by whitespace, so only a quote in that position opens one; quotes and apostrophes anywhere else are ordinary path characters. Focused tests now cover an apostrophe in a path, a double quote in a path, and a real quoted title holding an unbalanced parenthesis, alongside the balanced-parentheses and backslash-escape cases.

Re-run at c8e303d77, isolated temporary DerivedData, real exit statuses:

  • Focused suites: exit 0, ** TEST SUCCEEDED **, 31 passed, 0 failed, 0 skipped.
  • Full native unit bundle: exit 0, ** TEST SUCCEEDED **, 369 passed, 0 failed, 1 skipped (370 total).

Standing state of the earlier findings: the two High findings are fixed (e45d52bcc), the quoted-title Medium is fixed (9803983 plus this repair), and the cache-freshness Medium remains a documented, deliberate trade-off explained above. The on-device accepted behaviour (Test 89, built from 90b3ac41f) is unchanged by any of these repairs.

Comment thread apps/swift-ios/Features/Chat/MarkdownDocument.swift
@saphid

saphid commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

Fourth round, against c8e303d77.

Fixed in a91412b12 — Macroscope Medium, angle-bracketed destinations: CommonMark treats <...> contents as literal, so ![plot](<out/plot).png>) is a valid reference, but the parentheses inside were being counted and the destination ended early, falling back to alternative text. Angle-bracket contents are now skipped when they open the destination. Focused tests cover an unmatched parenthesis inside angle brackets and an angle-bracketed path containing spaces followed by a title.

Re-run at a91412b12, isolated temporary DerivedData, real exit statuses:

  • Focused suites: exit 0, ** TEST SUCCEEDED **, 32 passed, 0 failed, 0 skipped.
  • Full native unit bundle: exit 0, ** TEST SUCCEEDED **, 370 passed, 0 failed, 1 skipped (371 total).

Standing state: all High findings fixed (e45d52bcc); quoted-title, apostrophe-in-path, and angle-bracket parsing Mediums fixed (9803983, c8e303d77, a91412b12); the cache-freshness Medium remains the documented, deliberate trade-off described in the PR body, since invalidating it correctly needs a server-provided content version for workspace assets. The on-device accepted behaviour (Test 89, built from 90b3ac41f) is unchanged by any of these repairs.

Every parsing repair in this series degrades to prior behaviour rather than to a wrong image: a reference the parser cannot read stays alternative text.

Comment thread apps/swift-ios/Features/Chat/MarkdownDocument.swift Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a91412b. Configure here.

Comment thread apps/swift-ios/Features/Chat/MarkdownDocument.swift Outdated
@saphid

saphid commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

Fifth round, against a91412b12. Both bots found the two halves of the same gap in my angle-bracket handling, and both were right.

Fixed in f61d3a359:

  • Cursor Bugbot, Medium — literal mode only ran when < sat immediately after (. CommonMark allows whitespace there and the destination reader already trims it, so ![p]( <out/pad).png> ) still ended the destination early. Literal mode now starts wherever the brackets actually open the destination.
  • Macroscope, Medium — the destination reader used a plain search for > while the delimiter scan used an escape-aware one, so ![p](<out/a\>b.png>) truncated the path to out/a\. Both sides now share the one unescaped scan, which is what made the two halves disagree in the first place.

Re-run at f61d3a359, isolated temporary DerivedData, real exit statuses:

  • Focused suites: exit 0, ** TEST SUCCEEDED **, 33 passed, 0 failed, 0 skipped.
  • Full native unit bundle: exit 0, ** TEST SUCCEEDED **, 371 passed, 0 failed, 1 skipped (372 total).

Standing state: all High findings fixed in e45d52bcc; every parsing Medium fixed across 9803983, c8e303d77, a91412b12, and f61d3a359; the cache-freshness Medium remains the documented, deliberate trade-off explained in the PR body. The on-device accepted behaviour (Test 89, built from 90b3ac41f) is unchanged by any of these repairs, and every parsing repair degrades to prior behaviour — a reference the parser cannot read stays alternative text rather than becoming a wrong image.

@t3dotgg
t3dotgg force-pushed the t3code/rebuild-mobile-app-swift branch from 1f39d72 to fce7740 Compare August 18, 2026 21:35
@saphid
saphid force-pushed the feat/issue113-inline-workspace-images branch from f61d3a3 to 581e1b1 Compare August 18, 2026 22:23
saphid and others added 7 commits August 21, 2026 11:35
Markdown image references that stand alone on their line become their own
block instead of collapsing to alternative text, and a workspace-relative
image path renders inline from the existing signed workspace asset route.

Resolution reuses the transcript's attachment thumbnail loader and its
bounded cache, keyed by workspace file so a recycled cell never re-resolves
a signed URL it has already decoded. Remote, absolute, escaping, and
non-image references keep rendering as their alternative text.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Unescape backslash-escaped ASCII punctuation in an image reference, so a
file named out/foo(1).png resolves instead of being requested literally.

Gate the loaded and failed image state on request identity, matching the
sibling remote attachment thumbnail: hosted transcript cells reuse view
state across recycling and path changes, so ungated state could briefly
show another message's workspace image.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A quoted title may hold an unbalanced parenthesis, as in
![plot](out/plot.png "generated (final"), and counting those hid the real
closing delimiter, so the line fell back to alternative text. Delimiter
balancing now ignores quoted text while still allowing balanced
parentheses inside a file name.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ignoring quoted text while balancing delimiters made an apostrophe in a
file name, as in images/team's-logo.png, open a title that never closed,
so the reference fell back to alternative text. A title is separated from
the destination by whitespace, so only a quote in that position opens one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CommonMark wraps an awkward destination in angle brackets, as in
(<out/plot).png>), and everything inside is literal. Counting those
parentheses ended the destination early, so the reference fell back to
alternative text.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The destination reader trims whitespace before the brackets, so the
delimiter scan now enters literal mode wherever the brackets actually
open the destination, not only immediately after the parenthesis.

Both sides also share one unescaped scan for the closing bracket, so an
escaped `>` inside the brackets stays part of the file name instead of
truncating it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@saphid
saphid force-pushed the feat/issue113-inline-workspace-images branch from 581e1b1 to f050829 Compare August 21, 2026 02:16
Comment thread apps/swift-ios/Features/Chat/MarkdownDocument.swift
@t3dotgg

t3dotgg commented Aug 21, 2026

Copy link
Copy Markdown
Member

This definitely needs before/after screenshots, ideally also a video of the loading in behavior (i hate layout shifts)

@saphid

saphid commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

This definitely needs before/after screenshots, ideally also a video of the loading in behavior (i hate layout shifts)

added

@saphid

saphid commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

Closing as superseded by Theo’s target commit cd2b2f8. The conflict pass selected a rebase, then aborted it when the live target proved to own the same Markdown image parsing, workspace/direct resolution, bounded loading, transcript context, and focused coverage. No branch bytes changed, no force-push occurred, and the newer upstream implementation was preserved exactly. The attempted focused local target run did not execute tests because of recorded Xcode code-signing and disk-headroom failures, so no validation pass or review request is claimed; the updated description contains the full evidence and limitations.

@saphid saphid closed this Aug 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL 500-999 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants