feat(swift-ios): attach images pasted into the composer - #7601

Closed
saphid wants to merge 1 commit into
pingdotgg:t3code/rebuild-mobile-app-swiftfrom
saphid:contrib/issue126-composer-image-paste-upstream
Closed

feat(swift-ios): attach images pasted into the composer#7601
saphid wants to merge 1 commit into
pingdotgg:t3code/rebuild-mobile-app-swiftfrom
saphid:contrib/issue126-composer-image-paste-upstream

Conversation

@saphid

@saphidsaphid commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Observed problem and reproduction

Paste an image into the native SwiftUI composer with the long-press menu or Cmd-V. Before this work, image-only pasteboards did nothing because SwiftUI cannot observe paste on iOS and UITextView hides its normal Paste entry for image-only content.

The current head also covers related failures found after replacing the editor: marked-text composition could fight external SwiftUI binding changes, rich-text paste could retain attachments or styling, and asynchronous image intake could finish after the composer changed thread or project.

Cause

The composer used a SwiftUI text input with no UIKit paste(_:) hook. Image attachments entered through picker, camera, and Files paths only. Replacing the input exposed ownership gaps around focus, IME reconciliation, paste normalization, task cancellation, and context-scoped result acceptance.

Change and boundary

The composer now uses a UITextView wrapper that offers Paste for image content when the selected model accepts images. Pasted providers use the existing downscale, JPEG re-encode, thumbnail, and FeatureDraftAttachment pipeline. Text-only paste delegates to normal behavior, then restores plain composer styling.

A reconciliation state machine defers external rewrites during marked-text composition. Image intake tasks are keyed to the attachment context, cancelled on context change or disappearance, and rejected if stale. Multiple images preserve order up to the existing eight-image limit and disclose overflow.

Non-goals

No new attachment wire format, server behavior, provider-specific image list, or parallel upload path. Accompanying text from an image-bearing pasteboard is intentionally not inserted because copied images often also carry a URL. React Native mobile, web, and desktop are unchanged.

Affected areas

Affected client: SwiftUI mobile composer in both existing-thread and new-thread flows.

Platforms: iOS simulator and one physical iPhone acceptance record.

Providers: the UI gates paste on the selected model's existing image capability. No adapter changes.

Contracts/connections: existing attachment upload contracts and connection behavior are reused.

Validation

Current head: da648bbd.

  • xcodebuild test -only-testing:T3CodeTests on the recorded iOS 26.5 simulator: 291 tests across 33 suites passed, ** TEST SUCCEEDED **, exit 0.
  • Focused ComposerImagePasteTests, FeatureComposerPowerTests, and AttachmentPreparationTests: 22 tests across 3 suites passed, exit 0.
  • Simulator with live local backend: long-press Paste created an attachment chip; repeated pastes accumulated; removal and send worked; the backend persisted the image as JPEG. Hardware Cmd-V produced the same attachment.
  • Recorded physical-device acceptance: internal Test build 94.

Risks, untested paths, and known gaps

  • An unresolved Cursor Bugbot thread identifies a real ordinal-collision race after failed intake. A concurrent paste or drop can reuse an ordinal still owned by the first operation. This needs code and concurrency coverage.
  • Multi-image single-paste behavior is unit-tested but was not exercised on device.
  • The physical iOS one-time Allow Paste prompt was not exercised.
  • The branch currently conflicts with its target. It was not rebased in this sweep.
  • This contribution is superseded in the personal queue by merged upstream PR feat(swift-ios): paste images from the composer's native menu #7607. No closure or code change was made here.
  • Known gaps: no current-head light or dark before/after screenshots; no interaction video; no device proof for a single multi-image paste; no Allow Paste prompt proof; unresolved ordinal-collision code finding.

Evidence

No current-head screenshots or video are embedded. Earlier light-only captures predated the current editor and growth semantics, so they are intentionally not presented as proof of this head.

Owning issue and stack

Owning issue: saphid/t3code-personal#126. This PR targets the open SwiftUI parent branch and depends on #5178 landing. It is not stacked on #7345. The owning queue marks it superseded by merged #7607. Maintainer edits are enabled.

@github-actionsgithub-actionsBot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Aug 19, 2026
@coderabbitai

coderabbitaiBot commented Aug 19, 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: 15a893d0-8737-445b-a4cc-89ced43c592c

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-actionsgithub-actionsBot added the size:XL 500-999 changed lines (additions + deletions). label Aug 19, 2026
Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
@macroscopeapp

macroscopeappBot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a new feature (image paste support in the composer) with significant new logic including text reconciliation, async task lifecycle management, and paste text normalization. New features with this level of new state management and behavior warrant human review. An unresolved Medium-severity finding about potential ordinal collisions also requires attention.

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

Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
@saphid

Copy link
Copy Markdown
ContributorAuthor

Current-head paste-flow proof — 36621f9

Captured on the canonical proof simulator (B0B16E05-D2DE-4243-B27B-6837D50FDFE6) from the installed Debug app built at PR head 36621f9ac6aa8867689b47533b2bc968fa25e004.

Before every screenshot and the video recording, the simulator reported Booted and the installed T3Code.debug.dylib SHA-256 exactly matched the branch artifact: 30c76d6e73fbdb58ec54129b4295e3ff32e268270f8741de2534f141ad0feda8.

Light mode

Light-mode composer after pasting an image

Dark mode

Dark-mode composer after pasting an image

Paste-flow video (dark mode)

dark-paste-flow.mp4

The video shows an empty composer receiving the paste and rendering the image attachment tray. Duration: 18.47 seconds.

@saphid
saphidforce-pushed the contrib/issue126-composer-image-paste-upstream branch from 36621f9 to da648bbCompareAugust 21, 2026 01:52
@github-actionsgithub-actionsBot added size:L 100-499 changed lines (additions + deletions). and removed size:XL 500-999 changed lines (additions + deletions). labels Aug 21, 2026

@cursorcursorBot 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 da648bb. Configure here.

} else {
pendingItemsByOperation.removeValue(forKey: operation)
}
}

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.

Ordinal collision after failed intake

Medium Severity

completeItem frees a pending slot on failure while the in-flight operation still assigns filenames from its original firstOrdinal plus offset. A concurrent paste or drop can then plan the same ordinal, so two attachments both land as the same Image N.jpg.

Additional Locations (2)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit da648bb. Configure here.

@saphid

Copy link
Copy Markdown
ContributorAuthor

Closing as superseded. The image-paste behavior shipped via merged #7607.

The surviving real defect—attachment ordinal collisions during failed or concurrent intake—continues as the focused lane saphid/t3code-personal#153, to be fixed as a small PR against current main.

@saphidsaphid closed this Aug 27, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@saphid
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(swift-ios): attach images pasted into the composer - #7601

Closed
saphid wants to merge 1 commit into
pingdotgg:t3code/rebuild-mobile-app-swiftfrom
saphid:contrib/issue126-composer-image-paste-upstream
Closed

feat(swift-ios): attach images pasted into the composer#7601
saphid wants to merge 1 commit into
pingdotgg:t3code/rebuild-mobile-app-swiftfrom
saphid:contrib/issue126-composer-image-paste-upstream

Conversation

@saphid

@saphidsaphid commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Observed problem and reproduction

Paste an image into the native SwiftUI composer with the long-press menu or Cmd-V. Before this work, image-only pasteboards did nothing because SwiftUI cannot observe paste on iOS and UITextView hides its normal Paste entry for image-only content.

The current head also covers related failures found after replacing the editor: marked-text composition could fight external SwiftUI binding changes, rich-text paste could retain attachments or styling, and asynchronous image intake could finish after the composer changed thread or project.

Cause

The composer used a SwiftUI text input with no UIKit paste(_:) hook. Image attachments entered through picker, camera, and Files paths only. Replacing the input exposed ownership gaps around focus, IME reconciliation, paste normalization, task cancellation, and context-scoped result acceptance.

Change and boundary

The composer now uses a UITextView wrapper that offers Paste for image content when the selected model accepts images. Pasted providers use the existing downscale, JPEG re-encode, thumbnail, and FeatureDraftAttachment pipeline. Text-only paste delegates to normal behavior, then restores plain composer styling.

A reconciliation state machine defers external rewrites during marked-text composition. Image intake tasks are keyed to the attachment context, cancelled on context change or disappearance, and rejected if stale. Multiple images preserve order up to the existing eight-image limit and disclose overflow.

Non-goals

No new attachment wire format, server behavior, provider-specific image list, or parallel upload path. Accompanying text from an image-bearing pasteboard is intentionally not inserted because copied images often also carry a URL. React Native mobile, web, and desktop are unchanged.

Affected areas

Affected client: SwiftUI mobile composer in both existing-thread and new-thread flows.

Platforms: iOS simulator and one physical iPhone acceptance record.

Providers: the UI gates paste on the selected model's existing image capability. No adapter changes.

Contracts/connections: existing attachment upload contracts and connection behavior are reused.

Validation

Current head: da648bbd.

  • xcodebuild test -only-testing:T3CodeTests on the recorded iOS 26.5 simulator: 291 tests across 33 suites passed, ** TEST SUCCEEDED **, exit 0.
  • Focused ComposerImagePasteTests, FeatureComposerPowerTests, and AttachmentPreparationTests: 22 tests across 3 suites passed, exit 0.
  • Simulator with live local backend: long-press Paste created an attachment chip; repeated pastes accumulated; removal and send worked; the backend persisted the image as JPEG. Hardware Cmd-V produced the same attachment.
  • Recorded physical-device acceptance: internal Test build 94.

Risks, untested paths, and known gaps

  • An unresolved Cursor Bugbot thread identifies a real ordinal-collision race after failed intake. A concurrent paste or drop can reuse an ordinal still owned by the first operation. This needs code and concurrency coverage.
  • Multi-image single-paste behavior is unit-tested but was not exercised on device.
  • The physical iOS one-time Allow Paste prompt was not exercised.
  • The branch currently conflicts with its target. It was not rebased in this sweep.
  • This contribution is superseded in the personal queue by merged upstream PR feat(swift-ios): paste images from the composer's native menu #7607. No closure or code change was made here.
  • Known gaps: no current-head light or dark before/after screenshots; no interaction video; no device proof for a single multi-image paste; no Allow Paste prompt proof; unresolved ordinal-collision code finding.

Evidence

No current-head screenshots or video are embedded. Earlier light-only captures predated the current editor and growth semantics, so they are intentionally not presented as proof of this head.

Owning issue and stack

Owning issue: saphid/t3code-personal#126. This PR targets the open SwiftUI parent branch and depends on #5178 landing. It is not stacked on #7345. The owning queue marks it superseded by merged #7607. Maintainer edits are enabled.

@github-actionsgithub-actionsBot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Aug 19, 2026
@coderabbitai

coderabbitaiBot commented Aug 19, 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: 15a893d0-8737-445b-a4cc-89ced43c592c

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-actionsgithub-actionsBot added the size:XL 500-999 changed lines (additions + deletions). label Aug 19, 2026
Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
@macroscopeapp

macroscopeappBot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a new feature (image paste support in the composer) with significant new logic including text reconciliation, async task lifecycle management, and paste text normalization. New features with this level of new state management and behavior warrant human review. An unresolved Medium-severity finding about potential ordinal collisions also requires attention.

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

Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
@saphid

Copy link
Copy Markdown
ContributorAuthor

Current-head paste-flow proof — 36621f9

Captured on the canonical proof simulator (B0B16E05-D2DE-4243-B27B-6837D50FDFE6) from the installed Debug app built at PR head 36621f9ac6aa8867689b47533b2bc968fa25e004.

Before every screenshot and the video recording, the simulator reported Booted and the installed T3Code.debug.dylib SHA-256 exactly matched the branch artifact: 30c76d6e73fbdb58ec54129b4295e3ff32e268270f8741de2534f141ad0feda8.

Light mode

Light-mode composer after pasting an image

Dark mode

Dark-mode composer after pasting an image

Paste-flow video (dark mode)

dark-paste-flow.mp4

The video shows an empty composer receiving the paste and rendering the image attachment tray. Duration: 18.47 seconds.

@saphid
saphidforce-pushed the contrib/issue126-composer-image-paste-upstream branch from 36621f9 to da648bbCompareAugust 21, 2026 01:52
@github-actionsgithub-actionsBot added size:L 100-499 changed lines (additions + deletions). and removed size:XL 500-999 changed lines (additions + deletions). labels Aug 21, 2026

@cursorcursorBot 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 da648bb. Configure here.

} else {
pendingItemsByOperation.removeValue(forKey: operation)
}
}

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.

Ordinal collision after failed intake

Medium Severity

completeItem frees a pending slot on failure while the in-flight operation still assigns filenames from its original firstOrdinal plus offset. A concurrent paste or drop can then plan the same ordinal, so two attachments both land as the same Image N.jpg.

Additional Locations (2)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit da648bb. Configure here.

@saphid

Copy link
Copy Markdown
ContributorAuthor

Closing as superseded. The image-paste behavior shipped via merged #7607.

The surviving real defect—attachment ordinal collisions during failed or concurrent intake—continues as the focused lane saphid/t3code-personal#153, to be fixed as a small PR against current main.

@saphidsaphid closed this Aug 27, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@saphid
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(swift-ios): attach images pasted into the composer - #7601

Closed
saphid wants to merge 1 commit into
pingdotgg:t3code/rebuild-mobile-app-swiftfrom
saphid:contrib/issue126-composer-image-paste-upstream
Closed

feat(swift-ios): attach images pasted into the composer#7601
saphid wants to merge 1 commit into
pingdotgg:t3code/rebuild-mobile-app-swiftfrom
saphid:contrib/issue126-composer-image-paste-upstream

Conversation

@saphid

@saphidsaphid commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Observed problem and reproduction

Paste an image into the native SwiftUI composer with the long-press menu or Cmd-V. Before this work, image-only pasteboards did nothing because SwiftUI cannot observe paste on iOS and UITextView hides its normal Paste entry for image-only content.

The current head also covers related failures found after replacing the editor: marked-text composition could fight external SwiftUI binding changes, rich-text paste could retain attachments or styling, and asynchronous image intake could finish after the composer changed thread or project.

Cause

The composer used a SwiftUI text input with no UIKit paste(_:) hook. Image attachments entered through picker, camera, and Files paths only. Replacing the input exposed ownership gaps around focus, IME reconciliation, paste normalization, task cancellation, and context-scoped result acceptance.

Change and boundary

The composer now uses a UITextView wrapper that offers Paste for image content when the selected model accepts images. Pasted providers use the existing downscale, JPEG re-encode, thumbnail, and FeatureDraftAttachment pipeline. Text-only paste delegates to normal behavior, then restores plain composer styling.

A reconciliation state machine defers external rewrites during marked-text composition. Image intake tasks are keyed to the attachment context, cancelled on context change or disappearance, and rejected if stale. Multiple images preserve order up to the existing eight-image limit and disclose overflow.

Non-goals

No new attachment wire format, server behavior, provider-specific image list, or parallel upload path. Accompanying text from an image-bearing pasteboard is intentionally not inserted because copied images often also carry a URL. React Native mobile, web, and desktop are unchanged.

Affected areas

Affected client: SwiftUI mobile composer in both existing-thread and new-thread flows.

Platforms: iOS simulator and one physical iPhone acceptance record.

Providers: the UI gates paste on the selected model's existing image capability. No adapter changes.

Contracts/connections: existing attachment upload contracts and connection behavior are reused.

Validation

Current head: da648bbd.

  • xcodebuild test -only-testing:T3CodeTests on the recorded iOS 26.5 simulator: 291 tests across 33 suites passed, ** TEST SUCCEEDED **, exit 0.
  • Focused ComposerImagePasteTests, FeatureComposerPowerTests, and AttachmentPreparationTests: 22 tests across 3 suites passed, exit 0.
  • Simulator with live local backend: long-press Paste created an attachment chip; repeated pastes accumulated; removal and send worked; the backend persisted the image as JPEG. Hardware Cmd-V produced the same attachment.
  • Recorded physical-device acceptance: internal Test build 94.

Risks, untested paths, and known gaps

  • An unresolved Cursor Bugbot thread identifies a real ordinal-collision race after failed intake. A concurrent paste or drop can reuse an ordinal still owned by the first operation. This needs code and concurrency coverage.
  • Multi-image single-paste behavior is unit-tested but was not exercised on device.
  • The physical iOS one-time Allow Paste prompt was not exercised.
  • The branch currently conflicts with its target. It was not rebased in this sweep.
  • This contribution is superseded in the personal queue by merged upstream PR feat(swift-ios): paste images from the composer's native menu #7607. No closure or code change was made here.
  • Known gaps: no current-head light or dark before/after screenshots; no interaction video; no device proof for a single multi-image paste; no Allow Paste prompt proof; unresolved ordinal-collision code finding.

Evidence

No current-head screenshots or video are embedded. Earlier light-only captures predated the current editor and growth semantics, so they are intentionally not presented as proof of this head.

Owning issue and stack

Owning issue: saphid/t3code-personal#126. This PR targets the open SwiftUI parent branch and depends on #5178 landing. It is not stacked on #7345. The owning queue marks it superseded by merged #7607. Maintainer edits are enabled.

@github-actionsgithub-actionsBot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Aug 19, 2026
@coderabbitai

coderabbitaiBot commented Aug 19, 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: 15a893d0-8737-445b-a4cc-89ced43c592c

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-actionsgithub-actionsBot added the size:XL 500-999 changed lines (additions + deletions). label Aug 19, 2026
Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
@macroscopeapp

macroscopeappBot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a new feature (image paste support in the composer) with significant new logic including text reconciliation, async task lifecycle management, and paste text normalization. New features with this level of new state management and behavior warrant human review. An unresolved Medium-severity finding about potential ordinal collisions also requires attention.

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

Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
@saphid

Copy link
Copy Markdown
ContributorAuthor

Current-head paste-flow proof — 36621f9

Captured on the canonical proof simulator (B0B16E05-D2DE-4243-B27B-6837D50FDFE6) from the installed Debug app built at PR head 36621f9ac6aa8867689b47533b2bc968fa25e004.

Before every screenshot and the video recording, the simulator reported Booted and the installed T3Code.debug.dylib SHA-256 exactly matched the branch artifact: 30c76d6e73fbdb58ec54129b4295e3ff32e268270f8741de2534f141ad0feda8.

Light mode

Light-mode composer after pasting an image

Dark mode

Dark-mode composer after pasting an image

Paste-flow video (dark mode)

dark-paste-flow.mp4

The video shows an empty composer receiving the paste and rendering the image attachment tray. Duration: 18.47 seconds.

@saphid
saphidforce-pushed the contrib/issue126-composer-image-paste-upstream branch from 36621f9 to da648bbCompareAugust 21, 2026 01:52
@github-actionsgithub-actionsBot added size:L 100-499 changed lines (additions + deletions). and removed size:XL 500-999 changed lines (additions + deletions). labels Aug 21, 2026

@cursorcursorBot 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 da648bb. Configure here.

} else {
pendingItemsByOperation.removeValue(forKey: operation)
}
}

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.

Ordinal collision after failed intake

Medium Severity

completeItem frees a pending slot on failure while the in-flight operation still assigns filenames from its original firstOrdinal plus offset. A concurrent paste or drop can then plan the same ordinal, so two attachments both land as the same Image N.jpg.

Additional Locations (2)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit da648bb. Configure here.

@saphid

Copy link
Copy Markdown
ContributorAuthor

Closing as superseded. The image-paste behavior shipped via merged #7607.

The surviving real defect—attachment ordinal collisions during failed or concurrent intake—continues as the focused lane saphid/t3code-personal#153, to be fixed as a small PR against current main.

@saphidsaphid closed this Aug 27, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@saphid
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(swift-ios): attach images pasted into the composer - #7601

Closed
saphid wants to merge 1 commit into
pingdotgg:t3code/rebuild-mobile-app-swiftfrom
saphid:contrib/issue126-composer-image-paste-upstream
Closed

feat(swift-ios): attach images pasted into the composer#7601
saphid wants to merge 1 commit into
pingdotgg:t3code/rebuild-mobile-app-swiftfrom
saphid:contrib/issue126-composer-image-paste-upstream

Conversation

@saphid

@saphidsaphid commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Observed problem and reproduction

Paste an image into the native SwiftUI composer with the long-press menu or Cmd-V. Before this work, image-only pasteboards did nothing because SwiftUI cannot observe paste on iOS and UITextView hides its normal Paste entry for image-only content.

The current head also covers related failures found after replacing the editor: marked-text composition could fight external SwiftUI binding changes, rich-text paste could retain attachments or styling, and asynchronous image intake could finish after the composer changed thread or project.

Cause

The composer used a SwiftUI text input with no UIKit paste(_:) hook. Image attachments entered through picker, camera, and Files paths only. Replacing the input exposed ownership gaps around focus, IME reconciliation, paste normalization, task cancellation, and context-scoped result acceptance.

Change and boundary

The composer now uses a UITextView wrapper that offers Paste for image content when the selected model accepts images. Pasted providers use the existing downscale, JPEG re-encode, thumbnail, and FeatureDraftAttachment pipeline. Text-only paste delegates to normal behavior, then restores plain composer styling.

A reconciliation state machine defers external rewrites during marked-text composition. Image intake tasks are keyed to the attachment context, cancelled on context change or disappearance, and rejected if stale. Multiple images preserve order up to the existing eight-image limit and disclose overflow.

Non-goals

No new attachment wire format, server behavior, provider-specific image list, or parallel upload path. Accompanying text from an image-bearing pasteboard is intentionally not inserted because copied images often also carry a URL. React Native mobile, web, and desktop are unchanged.

Affected areas

Affected client: SwiftUI mobile composer in both existing-thread and new-thread flows.

Platforms: iOS simulator and one physical iPhone acceptance record.

Providers: the UI gates paste on the selected model's existing image capability. No adapter changes.

Contracts/connections: existing attachment upload contracts and connection behavior are reused.

Validation

Current head: da648bbd.

  • xcodebuild test -only-testing:T3CodeTests on the recorded iOS 26.5 simulator: 291 tests across 33 suites passed, ** TEST SUCCEEDED **, exit 0.
  • Focused ComposerImagePasteTests, FeatureComposerPowerTests, and AttachmentPreparationTests: 22 tests across 3 suites passed, exit 0.
  • Simulator with live local backend: long-press Paste created an attachment chip; repeated pastes accumulated; removal and send worked; the backend persisted the image as JPEG. Hardware Cmd-V produced the same attachment.
  • Recorded physical-device acceptance: internal Test build 94.

Risks, untested paths, and known gaps

  • An unresolved Cursor Bugbot thread identifies a real ordinal-collision race after failed intake. A concurrent paste or drop can reuse an ordinal still owned by the first operation. This needs code and concurrency coverage.
  • Multi-image single-paste behavior is unit-tested but was not exercised on device.
  • The physical iOS one-time Allow Paste prompt was not exercised.
  • The branch currently conflicts with its target. It was not rebased in this sweep.
  • This contribution is superseded in the personal queue by merged upstream PR feat(swift-ios): paste images from the composer's native menu #7607. No closure or code change was made here.
  • Known gaps: no current-head light or dark before/after screenshots; no interaction video; no device proof for a single multi-image paste; no Allow Paste prompt proof; unresolved ordinal-collision code finding.

Evidence

No current-head screenshots or video are embedded. Earlier light-only captures predated the current editor and growth semantics, so they are intentionally not presented as proof of this head.

Owning issue and stack

Owning issue: saphid/t3code-personal#126. This PR targets the open SwiftUI parent branch and depends on #5178 landing. It is not stacked on #7345. The owning queue marks it superseded by merged #7607. Maintainer edits are enabled.

@github-actionsgithub-actionsBot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Aug 19, 2026
@coderabbitai

coderabbitaiBot commented Aug 19, 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: 15a893d0-8737-445b-a4cc-89ced43c592c

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-actionsgithub-actionsBot added the size:XL 500-999 changed lines (additions + deletions). label Aug 19, 2026
Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
@macroscopeapp

macroscopeappBot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a new feature (image paste support in the composer) with significant new logic including text reconciliation, async task lifecycle management, and paste text normalization. New features with this level of new state management and behavior warrant human review. An unresolved Medium-severity finding about potential ordinal collisions also requires attention.

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

Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
@saphid

Copy link
Copy Markdown
ContributorAuthor

Current-head paste-flow proof — 36621f9

Captured on the canonical proof simulator (B0B16E05-D2DE-4243-B27B-6837D50FDFE6) from the installed Debug app built at PR head 36621f9ac6aa8867689b47533b2bc968fa25e004.

Before every screenshot and the video recording, the simulator reported Booted and the installed T3Code.debug.dylib SHA-256 exactly matched the branch artifact: 30c76d6e73fbdb58ec54129b4295e3ff32e268270f8741de2534f141ad0feda8.

Light mode

Light-mode composer after pasting an image

Dark mode

Dark-mode composer after pasting an image

Paste-flow video (dark mode)

dark-paste-flow.mp4

The video shows an empty composer receiving the paste and rendering the image attachment tray. Duration: 18.47 seconds.

@saphid
saphidforce-pushed the contrib/issue126-composer-image-paste-upstream branch from 36621f9 to da648bbCompareAugust 21, 2026 01:52
@github-actionsgithub-actionsBot added size:L 100-499 changed lines (additions + deletions). and removed size:XL 500-999 changed lines (additions + deletions). labels Aug 21, 2026

@cursorcursorBot 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 da648bb. Configure here.

} else {
pendingItemsByOperation.removeValue(forKey: operation)
}
}

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.

Ordinal collision after failed intake

Medium Severity

completeItem frees a pending slot on failure while the in-flight operation still assigns filenames from its original firstOrdinal plus offset. A concurrent paste or drop can then plan the same ordinal, so two attachments both land as the same Image N.jpg.

Additional Locations (2)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit da648bb. Configure here.

@saphid

Copy link
Copy Markdown
ContributorAuthor

Closing as superseded. The image-paste behavior shipped via merged #7607.

The surviving real defect—attachment ordinal collisions during failed or concurrent intake—continues as the focused lane saphid/t3code-personal#153, to be fixed as a small PR against current main.

@saphidsaphid closed this Aug 27, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@saphid
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(swift-ios): attach images pasted into the composer - #7601

Closed
saphid wants to merge 1 commit into
pingdotgg:t3code/rebuild-mobile-app-swiftfrom
saphid:contrib/issue126-composer-image-paste-upstream
Closed

feat(swift-ios): attach images pasted into the composer#7601
saphid wants to merge 1 commit into
pingdotgg:t3code/rebuild-mobile-app-swiftfrom
saphid:contrib/issue126-composer-image-paste-upstream

Conversation

@saphid

@saphidsaphid commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Observed problem and reproduction

Paste an image into the native SwiftUI composer with the long-press menu or Cmd-V. Before this work, image-only pasteboards did nothing because SwiftUI cannot observe paste on iOS and UITextView hides its normal Paste entry for image-only content.

The current head also covers related failures found after replacing the editor: marked-text composition could fight external SwiftUI binding changes, rich-text paste could retain attachments or styling, and asynchronous image intake could finish after the composer changed thread or project.

Cause

The composer used a SwiftUI text input with no UIKit paste(_:) hook. Image attachments entered through picker, camera, and Files paths only. Replacing the input exposed ownership gaps around focus, IME reconciliation, paste normalization, task cancellation, and context-scoped result acceptance.

Change and boundary

The composer now uses a UITextView wrapper that offers Paste for image content when the selected model accepts images. Pasted providers use the existing downscale, JPEG re-encode, thumbnail, and FeatureDraftAttachment pipeline. Text-only paste delegates to normal behavior, then restores plain composer styling.

A reconciliation state machine defers external rewrites during marked-text composition. Image intake tasks are keyed to the attachment context, cancelled on context change or disappearance, and rejected if stale. Multiple images preserve order up to the existing eight-image limit and disclose overflow.

Non-goals

No new attachment wire format, server behavior, provider-specific image list, or parallel upload path. Accompanying text from an image-bearing pasteboard is intentionally not inserted because copied images often also carry a URL. React Native mobile, web, and desktop are unchanged.

Affected areas

Affected client: SwiftUI mobile composer in both existing-thread and new-thread flows.

Platforms: iOS simulator and one physical iPhone acceptance record.

Providers: the UI gates paste on the selected model's existing image capability. No adapter changes.

Contracts/connections: existing attachment upload contracts and connection behavior are reused.

Validation

Current head: da648bbd.

  • xcodebuild test -only-testing:T3CodeTests on the recorded iOS 26.5 simulator: 291 tests across 33 suites passed, ** TEST SUCCEEDED **, exit 0.
  • Focused ComposerImagePasteTests, FeatureComposerPowerTests, and AttachmentPreparationTests: 22 tests across 3 suites passed, exit 0.
  • Simulator with live local backend: long-press Paste created an attachment chip; repeated pastes accumulated; removal and send worked; the backend persisted the image as JPEG. Hardware Cmd-V produced the same attachment.
  • Recorded physical-device acceptance: internal Test build 94.

Risks, untested paths, and known gaps

  • An unresolved Cursor Bugbot thread identifies a real ordinal-collision race after failed intake. A concurrent paste or drop can reuse an ordinal still owned by the first operation. This needs code and concurrency coverage.
  • Multi-image single-paste behavior is unit-tested but was not exercised on device.
  • The physical iOS one-time Allow Paste prompt was not exercised.
  • The branch currently conflicts with its target. It was not rebased in this sweep.
  • This contribution is superseded in the personal queue by merged upstream PR feat(swift-ios): paste images from the composer's native menu #7607. No closure or code change was made here.
  • Known gaps: no current-head light or dark before/after screenshots; no interaction video; no device proof for a single multi-image paste; no Allow Paste prompt proof; unresolved ordinal-collision code finding.

Evidence

No current-head screenshots or video are embedded. Earlier light-only captures predated the current editor and growth semantics, so they are intentionally not presented as proof of this head.

Owning issue and stack

Owning issue: saphid/t3code-personal#126. This PR targets the open SwiftUI parent branch and depends on #5178 landing. It is not stacked on #7345. The owning queue marks it superseded by merged #7607. Maintainer edits are enabled.

@github-actionsgithub-actionsBot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Aug 19, 2026
@coderabbitai

coderabbitaiBot commented Aug 19, 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: 15a893d0-8737-445b-a4cc-89ced43c592c

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-actionsgithub-actionsBot added the size:XL 500-999 changed lines (additions + deletions). label Aug 19, 2026
Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
@macroscopeapp

macroscopeappBot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a new feature (image paste support in the composer) with significant new logic including text reconciliation, async task lifecycle management, and paste text normalization. New features with this level of new state management and behavior warrant human review. An unresolved Medium-severity finding about potential ordinal collisions also requires attention.

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

Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
@saphid

Copy link
Copy Markdown
ContributorAuthor

Current-head paste-flow proof — 36621f9

Captured on the canonical proof simulator (B0B16E05-D2DE-4243-B27B-6837D50FDFE6) from the installed Debug app built at PR head 36621f9ac6aa8867689b47533b2bc968fa25e004.

Before every screenshot and the video recording, the simulator reported Booted and the installed T3Code.debug.dylib SHA-256 exactly matched the branch artifact: 30c76d6e73fbdb58ec54129b4295e3ff32e268270f8741de2534f141ad0feda8.

Light mode

Light-mode composer after pasting an image

Dark mode

Dark-mode composer after pasting an image

Paste-flow video (dark mode)

dark-paste-flow.mp4

The video shows an empty composer receiving the paste and rendering the image attachment tray. Duration: 18.47 seconds.

@saphid
saphidforce-pushed the contrib/issue126-composer-image-paste-upstream branch from 36621f9 to da648bbCompareAugust 21, 2026 01:52
@github-actionsgithub-actionsBot added size:L 100-499 changed lines (additions + deletions). and removed size:XL 500-999 changed lines (additions + deletions). labels Aug 21, 2026

@cursorcursorBot 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 da648bb. Configure here.

} else {
pendingItemsByOperation.removeValue(forKey: operation)
}
}

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.

Ordinal collision after failed intake

Medium Severity

completeItem frees a pending slot on failure while the in-flight operation still assigns filenames from its original firstOrdinal plus offset. A concurrent paste or drop can then plan the same ordinal, so two attachments both land as the same Image N.jpg.

Additional Locations (2)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit da648bb. Configure here.

@saphid

Copy link
Copy Markdown
ContributorAuthor

Closing as superseded. The image-paste behavior shipped via merged #7607.

The surviving real defect—attachment ordinal collisions during failed or concurrent intake—continues as the focused lane saphid/t3code-personal#153, to be fixed as a small PR against current main.

@saphidsaphid closed this Aug 27, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@saphid
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(swift-ios): attach images pasted into the composer - #7601

Closed
saphid wants to merge 1 commit into
pingdotgg:t3code/rebuild-mobile-app-swiftfrom
saphid:contrib/issue126-composer-image-paste-upstream
Closed

feat(swift-ios): attach images pasted into the composer#7601
saphid wants to merge 1 commit into
pingdotgg:t3code/rebuild-mobile-app-swiftfrom
saphid:contrib/issue126-composer-image-paste-upstream

Conversation

@saphid

@saphidsaphid commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Observed problem and reproduction

Paste an image into the native SwiftUI composer with the long-press menu or Cmd-V. Before this work, image-only pasteboards did nothing because SwiftUI cannot observe paste on iOS and UITextView hides its normal Paste entry for image-only content.

The current head also covers related failures found after replacing the editor: marked-text composition could fight external SwiftUI binding changes, rich-text paste could retain attachments or styling, and asynchronous image intake could finish after the composer changed thread or project.

Cause

The composer used a SwiftUI text input with no UIKit paste(_:) hook. Image attachments entered through picker, camera, and Files paths only. Replacing the input exposed ownership gaps around focus, IME reconciliation, paste normalization, task cancellation, and context-scoped result acceptance.

Change and boundary

The composer now uses a UITextView wrapper that offers Paste for image content when the selected model accepts images. Pasted providers use the existing downscale, JPEG re-encode, thumbnail, and FeatureDraftAttachment pipeline. Text-only paste delegates to normal behavior, then restores plain composer styling.

A reconciliation state machine defers external rewrites during marked-text composition. Image intake tasks are keyed to the attachment context, cancelled on context change or disappearance, and rejected if stale. Multiple images preserve order up to the existing eight-image limit and disclose overflow.

Non-goals

No new attachment wire format, server behavior, provider-specific image list, or parallel upload path. Accompanying text from an image-bearing pasteboard is intentionally not inserted because copied images often also carry a URL. React Native mobile, web, and desktop are unchanged.

Affected areas

Affected client: SwiftUI mobile composer in both existing-thread and new-thread flows.

Platforms: iOS simulator and one physical iPhone acceptance record.

Providers: the UI gates paste on the selected model's existing image capability. No adapter changes.

Contracts/connections: existing attachment upload contracts and connection behavior are reused.

Validation

Current head: da648bbd.

  • xcodebuild test -only-testing:T3CodeTests on the recorded iOS 26.5 simulator: 291 tests across 33 suites passed, ** TEST SUCCEEDED **, exit 0.
  • Focused ComposerImagePasteTests, FeatureComposerPowerTests, and AttachmentPreparationTests: 22 tests across 3 suites passed, exit 0.
  • Simulator with live local backend: long-press Paste created an attachment chip; repeated pastes accumulated; removal and send worked; the backend persisted the image as JPEG. Hardware Cmd-V produced the same attachment.
  • Recorded physical-device acceptance: internal Test build 94.

Risks, untested paths, and known gaps

  • An unresolved Cursor Bugbot thread identifies a real ordinal-collision race after failed intake. A concurrent paste or drop can reuse an ordinal still owned by the first operation. This needs code and concurrency coverage.
  • Multi-image single-paste behavior is unit-tested but was not exercised on device.
  • The physical iOS one-time Allow Paste prompt was not exercised.
  • The branch currently conflicts with its target. It was not rebased in this sweep.
  • This contribution is superseded in the personal queue by merged upstream PR feat(swift-ios): paste images from the composer's native menu #7607. No closure or code change was made here.
  • Known gaps: no current-head light or dark before/after screenshots; no interaction video; no device proof for a single multi-image paste; no Allow Paste prompt proof; unresolved ordinal-collision code finding.

Evidence

No current-head screenshots or video are embedded. Earlier light-only captures predated the current editor and growth semantics, so they are intentionally not presented as proof of this head.

Owning issue and stack

Owning issue: saphid/t3code-personal#126. This PR targets the open SwiftUI parent branch and depends on #5178 landing. It is not stacked on #7345. The owning queue marks it superseded by merged #7607. Maintainer edits are enabled.

@github-actionsgithub-actionsBot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Aug 19, 2026
@coderabbitai

coderabbitaiBot commented Aug 19, 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: 15a893d0-8737-445b-a4cc-89ced43c592c

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-actionsgithub-actionsBot added the size:XL 500-999 changed lines (additions + deletions). label Aug 19, 2026
Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
@macroscopeapp

macroscopeappBot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a new feature (image paste support in the composer) with significant new logic including text reconciliation, async task lifecycle management, and paste text normalization. New features with this level of new state management and behavior warrant human review. An unresolved Medium-severity finding about potential ordinal collisions also requires attention.

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

Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
@saphid

Copy link
Copy Markdown
ContributorAuthor

Current-head paste-flow proof — 36621f9

Captured on the canonical proof simulator (B0B16E05-D2DE-4243-B27B-6837D50FDFE6) from the installed Debug app built at PR head 36621f9ac6aa8867689b47533b2bc968fa25e004.

Before every screenshot and the video recording, the simulator reported Booted and the installed T3Code.debug.dylib SHA-256 exactly matched the branch artifact: 30c76d6e73fbdb58ec54129b4295e3ff32e268270f8741de2534f141ad0feda8.

Light mode

Light-mode composer after pasting an image

Dark mode

Dark-mode composer after pasting an image

Paste-flow video (dark mode)

dark-paste-flow.mp4

The video shows an empty composer receiving the paste and rendering the image attachment tray. Duration: 18.47 seconds.

@saphid
saphidforce-pushed the contrib/issue126-composer-image-paste-upstream branch from 36621f9 to da648bbCompareAugust 21, 2026 01:52
@github-actionsgithub-actionsBot added size:L 100-499 changed lines (additions + deletions). and removed size:XL 500-999 changed lines (additions + deletions). labels Aug 21, 2026

@cursorcursorBot 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 da648bb. Configure here.

} else {
pendingItemsByOperation.removeValue(forKey: operation)
}
}

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.

Ordinal collision after failed intake

Medium Severity

completeItem frees a pending slot on failure while the in-flight operation still assigns filenames from its original firstOrdinal plus offset. A concurrent paste or drop can then plan the same ordinal, so two attachments both land as the same Image N.jpg.

Additional Locations (2)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit da648bb. Configure here.

@saphid

Copy link
Copy Markdown
ContributorAuthor

Closing as superseded. The image-paste behavior shipped via merged #7607.

The surviving real defect—attachment ordinal collisions during failed or concurrent intake—continues as the focused lane saphid/t3code-personal#153, to be fixed as a small PR against current main.

@saphidsaphid closed this Aug 27, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@saphid
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(swift-ios): attach images pasted into the composer - #7601

Closed
saphid wants to merge 1 commit into
pingdotgg:t3code/rebuild-mobile-app-swiftfrom
saphid:contrib/issue126-composer-image-paste-upstream
Closed

feat(swift-ios): attach images pasted into the composer#7601
saphid wants to merge 1 commit into
pingdotgg:t3code/rebuild-mobile-app-swiftfrom
saphid:contrib/issue126-composer-image-paste-upstream

Conversation

@saphid

@saphidsaphid commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Observed problem and reproduction

Paste an image into the native SwiftUI composer with the long-press menu or Cmd-V. Before this work, image-only pasteboards did nothing because SwiftUI cannot observe paste on iOS and UITextView hides its normal Paste entry for image-only content.

The current head also covers related failures found after replacing the editor: marked-text composition could fight external SwiftUI binding changes, rich-text paste could retain attachments or styling, and asynchronous image intake could finish after the composer changed thread or project.

Cause

The composer used a SwiftUI text input with no UIKit paste(_:) hook. Image attachments entered through picker, camera, and Files paths only. Replacing the input exposed ownership gaps around focus, IME reconciliation, paste normalization, task cancellation, and context-scoped result acceptance.

Change and boundary

The composer now uses a UITextView wrapper that offers Paste for image content when the selected model accepts images. Pasted providers use the existing downscale, JPEG re-encode, thumbnail, and FeatureDraftAttachment pipeline. Text-only paste delegates to normal behavior, then restores plain composer styling.

A reconciliation state machine defers external rewrites during marked-text composition. Image intake tasks are keyed to the attachment context, cancelled on context change or disappearance, and rejected if stale. Multiple images preserve order up to the existing eight-image limit and disclose overflow.

Non-goals

No new attachment wire format, server behavior, provider-specific image list, or parallel upload path. Accompanying text from an image-bearing pasteboard is intentionally not inserted because copied images often also carry a URL. React Native mobile, web, and desktop are unchanged.

Affected areas

Affected client: SwiftUI mobile composer in both existing-thread and new-thread flows.

Platforms: iOS simulator and one physical iPhone acceptance record.

Providers: the UI gates paste on the selected model's existing image capability. No adapter changes.

Contracts/connections: existing attachment upload contracts and connection behavior are reused.

Validation

Current head: da648bbd.

  • xcodebuild test -only-testing:T3CodeTests on the recorded iOS 26.5 simulator: 291 tests across 33 suites passed, ** TEST SUCCEEDED **, exit 0.
  • Focused ComposerImagePasteTests, FeatureComposerPowerTests, and AttachmentPreparationTests: 22 tests across 3 suites passed, exit 0.
  • Simulator with live local backend: long-press Paste created an attachment chip; repeated pastes accumulated; removal and send worked; the backend persisted the image as JPEG. Hardware Cmd-V produced the same attachment.
  • Recorded physical-device acceptance: internal Test build 94.

Risks, untested paths, and known gaps

  • An unresolved Cursor Bugbot thread identifies a real ordinal-collision race after failed intake. A concurrent paste or drop can reuse an ordinal still owned by the first operation. This needs code and concurrency coverage.
  • Multi-image single-paste behavior is unit-tested but was not exercised on device.
  • The physical iOS one-time Allow Paste prompt was not exercised.
  • The branch currently conflicts with its target. It was not rebased in this sweep.
  • This contribution is superseded in the personal queue by merged upstream PR feat(swift-ios): paste images from the composer's native menu #7607. No closure or code change was made here.
  • Known gaps: no current-head light or dark before/after screenshots; no interaction video; no device proof for a single multi-image paste; no Allow Paste prompt proof; unresolved ordinal-collision code finding.

Evidence

No current-head screenshots or video are embedded. Earlier light-only captures predated the current editor and growth semantics, so they are intentionally not presented as proof of this head.

Owning issue and stack

Owning issue: saphid/t3code-personal#126. This PR targets the open SwiftUI parent branch and depends on #5178 landing. It is not stacked on #7345. The owning queue marks it superseded by merged #7607. Maintainer edits are enabled.

@github-actionsgithub-actionsBot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Aug 19, 2026
@coderabbitai

coderabbitaiBot commented Aug 19, 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: 15a893d0-8737-445b-a4cc-89ced43c592c

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-actionsgithub-actionsBot added the size:XL 500-999 changed lines (additions + deletions). label Aug 19, 2026
Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
@macroscopeapp

macroscopeappBot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a new feature (image paste support in the composer) with significant new logic including text reconciliation, async task lifecycle management, and paste text normalization. New features with this level of new state management and behavior warrant human review. An unresolved Medium-severity finding about potential ordinal collisions also requires attention.

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

Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
@saphid

Copy link
Copy Markdown
ContributorAuthor

Current-head paste-flow proof — 36621f9

Captured on the canonical proof simulator (B0B16E05-D2DE-4243-B27B-6837D50FDFE6) from the installed Debug app built at PR head 36621f9ac6aa8867689b47533b2bc968fa25e004.

Before every screenshot and the video recording, the simulator reported Booted and the installed T3Code.debug.dylib SHA-256 exactly matched the branch artifact: 30c76d6e73fbdb58ec54129b4295e3ff32e268270f8741de2534f141ad0feda8.

Light mode

Light-mode composer after pasting an image

Dark mode

Dark-mode composer after pasting an image

Paste-flow video (dark mode)

dark-paste-flow.mp4

The video shows an empty composer receiving the paste and rendering the image attachment tray. Duration: 18.47 seconds.

@saphid
saphidforce-pushed the contrib/issue126-composer-image-paste-upstream branch from 36621f9 to da648bbCompareAugust 21, 2026 01:52
@github-actionsgithub-actionsBot added size:L 100-499 changed lines (additions + deletions). and removed size:XL 500-999 changed lines (additions + deletions). labels Aug 21, 2026

@cursorcursorBot 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 da648bb. Configure here.

} else {
pendingItemsByOperation.removeValue(forKey: operation)
}
}

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.

Ordinal collision after failed intake

Medium Severity

completeItem frees a pending slot on failure while the in-flight operation still assigns filenames from its original firstOrdinal plus offset. A concurrent paste or drop can then plan the same ordinal, so two attachments both land as the same Image N.jpg.

Additional Locations (2)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit da648bb. Configure here.

@saphid

Copy link
Copy Markdown
ContributorAuthor

Closing as superseded. The image-paste behavior shipped via merged #7607.

The surviving real defect—attachment ordinal collisions during failed or concurrent intake—continues as the focused lane saphid/t3code-personal#153, to be fixed as a small PR against current main.

@saphidsaphid closed this Aug 27, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@saphid
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(swift-ios): attach images pasted into the composer - #7601

Closed
saphid wants to merge 1 commit into
pingdotgg:t3code/rebuild-mobile-app-swiftfrom
saphid:contrib/issue126-composer-image-paste-upstream
Closed

feat(swift-ios): attach images pasted into the composer#7601
saphid wants to merge 1 commit into
pingdotgg:t3code/rebuild-mobile-app-swiftfrom
saphid:contrib/issue126-composer-image-paste-upstream

Conversation

@saphid

@saphidsaphid commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Observed problem and reproduction

Paste an image into the native SwiftUI composer with the long-press menu or Cmd-V. Before this work, image-only pasteboards did nothing because SwiftUI cannot observe paste on iOS and UITextView hides its normal Paste entry for image-only content.

The current head also covers related failures found after replacing the editor: marked-text composition could fight external SwiftUI binding changes, rich-text paste could retain attachments or styling, and asynchronous image intake could finish after the composer changed thread or project.

Cause

The composer used a SwiftUI text input with no UIKit paste(_:) hook. Image attachments entered through picker, camera, and Files paths only. Replacing the input exposed ownership gaps around focus, IME reconciliation, paste normalization, task cancellation, and context-scoped result acceptance.

Change and boundary

The composer now uses a UITextView wrapper that offers Paste for image content when the selected model accepts images. Pasted providers use the existing downscale, JPEG re-encode, thumbnail, and FeatureDraftAttachment pipeline. Text-only paste delegates to normal behavior, then restores plain composer styling.

A reconciliation state machine defers external rewrites during marked-text composition. Image intake tasks are keyed to the attachment context, cancelled on context change or disappearance, and rejected if stale. Multiple images preserve order up to the existing eight-image limit and disclose overflow.

Non-goals

No new attachment wire format, server behavior, provider-specific image list, or parallel upload path. Accompanying text from an image-bearing pasteboard is intentionally not inserted because copied images often also carry a URL. React Native mobile, web, and desktop are unchanged.

Affected areas

Affected client: SwiftUI mobile composer in both existing-thread and new-thread flows.

Platforms: iOS simulator and one physical iPhone acceptance record.

Providers: the UI gates paste on the selected model's existing image capability. No adapter changes.

Contracts/connections: existing attachment upload contracts and connection behavior are reused.

Validation

Current head: da648bbd.

  • xcodebuild test -only-testing:T3CodeTests on the recorded iOS 26.5 simulator: 291 tests across 33 suites passed, ** TEST SUCCEEDED **, exit 0.
  • Focused ComposerImagePasteTests, FeatureComposerPowerTests, and AttachmentPreparationTests: 22 tests across 3 suites passed, exit 0.
  • Simulator with live local backend: long-press Paste created an attachment chip; repeated pastes accumulated; removal and send worked; the backend persisted the image as JPEG. Hardware Cmd-V produced the same attachment.
  • Recorded physical-device acceptance: internal Test build 94.

Risks, untested paths, and known gaps

  • An unresolved Cursor Bugbot thread identifies a real ordinal-collision race after failed intake. A concurrent paste or drop can reuse an ordinal still owned by the first operation. This needs code and concurrency coverage.
  • Multi-image single-paste behavior is unit-tested but was not exercised on device.
  • The physical iOS one-time Allow Paste prompt was not exercised.
  • The branch currently conflicts with its target. It was not rebased in this sweep.
  • This contribution is superseded in the personal queue by merged upstream PR feat(swift-ios): paste images from the composer's native menu #7607. No closure or code change was made here.
  • Known gaps: no current-head light or dark before/after screenshots; no interaction video; no device proof for a single multi-image paste; no Allow Paste prompt proof; unresolved ordinal-collision code finding.

Evidence

No current-head screenshots or video are embedded. Earlier light-only captures predated the current editor and growth semantics, so they are intentionally not presented as proof of this head.

Owning issue and stack

Owning issue: saphid/t3code-personal#126. This PR targets the open SwiftUI parent branch and depends on #5178 landing. It is not stacked on #7345. The owning queue marks it superseded by merged #7607. Maintainer edits are enabled.

@github-actionsgithub-actionsBot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Aug 19, 2026
@coderabbitai

coderabbitaiBot commented Aug 19, 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: 15a893d0-8737-445b-a4cc-89ced43c592c

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-actionsgithub-actionsBot added the size:XL 500-999 changed lines (additions + deletions). label Aug 19, 2026
Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
@macroscopeapp

macroscopeappBot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces a new feature (image paste support in the composer) with significant new logic including text reconciliation, async task lifecycle management, and paste text normalization. New features with this level of new state management and behavior warrant human review. An unresolved Medium-severity finding about potential ordinal collisions also requires attention.

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

Comment threadapps/swift-ios/Features/Chat/FeatureComposerTextInput.swift
@saphid

Copy link
Copy Markdown
ContributorAuthor

Current-head paste-flow proof — 36621f9

Captured on the canonical proof simulator (B0B16E05-D2DE-4243-B27B-6837D50FDFE6) from the installed Debug app built at PR head 36621f9ac6aa8867689b47533b2bc968fa25e004.

Before every screenshot and the video recording, the simulator reported Booted and the installed T3Code.debug.dylib SHA-256 exactly matched the branch artifact: 30c76d6e73fbdb58ec54129b4295e3ff32e268270f8741de2534f141ad0feda8.

Light mode

Light-mode composer after pasting an image

Dark mode

Dark-mode composer after pasting an image

Paste-flow video (dark mode)

dark-paste-flow.mp4

The video shows an empty composer receiving the paste and rendering the image attachment tray. Duration: 18.47 seconds.

@saphid
saphidforce-pushed the contrib/issue126-composer-image-paste-upstream branch from 36621f9 to da648bbCompareAugust 21, 2026 01:52
@github-actionsgithub-actionsBot added size:L 100-499 changed lines (additions + deletions). and removed size:XL 500-999 changed lines (additions + deletions). labels Aug 21, 2026

@cursorcursorBot 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 da648bb. Configure here.

} else {
pendingItemsByOperation.removeValue(forKey: operation)
}
}

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.

Ordinal collision after failed intake

Medium Severity

completeItem frees a pending slot on failure while the in-flight operation still assigns filenames from its original firstOrdinal plus offset. A concurrent paste or drop can then plan the same ordinal, so two attachments both land as the same Image N.jpg.

Additional Locations (2)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit da648bb. Configure here.

@saphid

Copy link
Copy Markdown
ContributorAuthor

Closing as superseded. The image-paste behavior shipped via merged #7607.

The surviving real defect—attachment ordinal collisions during failed or concurrent intake—continues as the focused lane saphid/t3code-personal#153, to be fixed as a small PR against current main.

@saphidsaphid closed this Aug 27, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@saphid