feat(mobile): long-press file references for path and open actions - #9258

Merged
juliusmarminge merged 4 commits into
mainfrom
t3code/ios-file-chip-context-menu
Sep 2, 2026
Merged

feat(mobile): long-press file references for path and open actions#9258
juliusmarminge merged 4 commits into
mainfrom
t3code/ios-file-chip-context-menu

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 2, 2026

Copy link
Copy Markdown
Member

What changed

  • File chips in iOS messages get a native long-press context menu with Copy full path, Copy relative path, and Open in file viewer, the same actions the web chip offers on right-click.
  • T3MarkdownTextRun accepts a JSON contextMenuConfig; T3MarkdownText maps runs that carry one to UITextItem links so iOS 17's text-item menu owns the gesture. Tap and text selection keep working; text drag is disabled on chat text.
  • SelectableMarkdownText gains fileContextMenu and onFileContextMenuAction. The feed builds the menu description and runs actions from one handler at the root, so rows subscribe to nothing extra per chip.

Why

Tapping a file reference opened it and that was the only thing you could do with it. Users copying a path had to open the file first. Web has had a chip context menu for a while; this brings the same set to iOS through UIKit rather than a JS action sheet.

Split out of #9253 so the media refactor there stays a refactor.

UI changes

Before: tap onlyAfter: native context menu
No long-press affordance on file chips.

Testing

  • fileChipMenu.test.ts (5 tests)
  • Mobile typecheck, lint, and format on changed files

Created with Claude Fable 5 using the Claude Code harness in T3 Code.

🤖 Generated with Claude Code


Note

Medium Risk
Touches native gesture handling and iOS 17 text-item menus on chat text; disabling text drag and suppressing recognizers on menu runs could affect edge-case interactions outside file chips.

Overview
Adds native long-press context menus on iOS for inline file references in chat markdown, matching web chip actions: copy full path, copy relative path, and open in the file viewer.

The t3-markdown-text native layer now accepts a JSON contextMenuConfig on text runs, builds a UIMenu, and wires T3MarkdownText (iOS 17 text-item delegates) so menu runs are exposed as links while custom tap/long-press is skipped on those runs. Chat UITextView also disables text drag. SelectableMarkdownText gains fileContextMenu / onFileContextMenuAction plus a React context so file chips in the tree share one handler.

ThreadFeed centralizes tap and menu behavior in markdownLinkHandlers via new fileChipMenu / resolveFileChipTarget. resolveWorkspaceRelativeFilePath now rejects paths that escape the workspace with .. (e.g. /repo/../outside.txt). User docs note iOS touch-and-hold on file references.

Reviewed by Cursor Bugbot for commit 67b0275. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add long-press context menu for file references in mobile markdown text

  • Native iOS T3MarkdownText and T3MarkdownTextRun build context menus from a serialized JSON config and emit a native event when an action is selected. Custom tap/long-press recognizers are suppressed on runs with a menu, while other runs keep existing gesture handling.
  • React Native wiring in SelectableMarkdownText.ios.tsx and ThreadFeed.tsx resolves file-chip hrefs to menus and dispatches copy-path and open-file actions.
  • fileChipMenu.ts classifies file links into full and relative paths and builds a menu with only the supported copy actions plus the file-viewer open action.
  • filePath.ts now rejects absolute targets that escape the workspace via a .. segment instead of normalizing them.
  • Risk: disabling drag interaction on T3MarkdownText means text drag-and-drop no longer works on iOS; handleLongPressIfNecessary now fires once at the began state rather than across all long-press transitions.

Macroscope summarized 67b0275.

File chips in iOS messages had no way to copy a path; tapping opened the
file and that was it. The native text view now maps chips with a menu to
UITextItem links, so iOS 17 shows a context menu on long-press while
tapping and text selection keep working. The menu offers copy full path,
copy relative path, and open in file viewer, matching the web chip.
The menu is described per chip as JSON and actions run from one handler
at the feed root, so rows subscribe to nothing extra per chip.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). 📱 Native Change Changes the native fingerprint; merging blocks production OTAs until a new store build ships. labels Sep 2, 2026
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx
Julius Marmingeand others added 2 commits September 2, 2026 12:44
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…links
Chips inside blockquotes, lists, and tables never received the menu
handlers because NativeMarkdownBlock only forwarded onLinkPress. The
handlers now travel through a context the same way the image renderer
does. Links the feed cannot open, such as ~/x or ../x, get no menu
instead of one whose only action does nothing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment threadapps/mobile/src/features/threads/fileChipMenu.ts
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.2 KiB13.1 KiB−88 B (−0.6%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB+4 B (+0.1%)7.3 KiB
CodexLive turn WebSocket wire6.3 KiB6.2 KiB−92 B (−1.4%)7.8 KiB
CodexLive turn WebSocket decoded55.5 KiB54.7 KiB−822 B (−1.4%)66.4 KiB
CodexLive turn messages880 (0.0%)21
ClaudeTotal thread wire13.3 KiB13.3 KiB+12 B (+0.1%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−3 B (−0.0%)7.3 KiB
ClaudeLive turn WebSocket wire6.4 KiB6.4 KiB+15 B (+0.2%)7.8 KiB
ClaudeLive turn WebSocket decoded56.4 KiB56.4 KiB0 B (0.0%)66.4 KiB
ClaudeLive turn messages10100 (0.0%)21

Baseline: aab4049 · PR result: 67b0275 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 109.4 KiB
  • Claude decoded thread snapshot: 110.1 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

…t is not relative
resolveWorkspaceRelativeFilePath matched /repo/../outside.txt as inside
/repo because the prefix check ran before traversal was normalized.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
juliusmarminge pushed a commit that referenced this pull request Sep 2, 2026
Split the iOS file-chip context menu into its own PR (#9258) and cut the
parts of this one that were not unification:
- restore the work-log presentation tests that b1daa51 deleted; they
cover tool naming and browser summaries this PR never touched
- drop availableMediaActions; both clients kept a full label and runner
table anyway, so only the MediaActionId union is shared
- one behavior for link parsing: conventional extensionless names are
files everywhere, and file: URLs are decoded by the caller like every
other destination, so no more option flags
- inline resolveAssetUrlResult into its one caller
- remove comments that explained the change instead of the code
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This adds a new iOS long-press workflow through native UIKit text-item menus and bridges actions into production message rendering. It also disables text drag-and-drop and changes gesture handling for the shared markdown text component, giving the change a broader interaction surface than a simple UI adjustment.

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

@juliusmarminge
juliusmarminge merged commit 9159b80 into mainSep 2, 2026
27 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/ios-file-chip-context-menu branch September 2, 2026 20:03
sheehanmunim added a commit to munimtechnologies/mtcode that referenced this pull request Sep 2, 2026
…continuation, provider editor redesign, context meter opt-in, linked-PR summaries)
Brings the fork up to origin/main e0da6c6..b57726c. Highlights: continue
active threads across server self-updates (pingdotgg#9167), provider editor and models
list redesign (pingdotgg#8508), opt-in context window meter (pingdotgg#9190), linked-PR summary
RPC with last-good caching (pingdotgg#9176), relay credential refresh before expiry
(pingdotgg#9178), composer draft preserved through worktree setup (pingdotgg#9197), project
skill discovery for Claude (pingdotgg#9210), copy-path on diff headers (pingdotgg#2403), mobile
long-press file references (pingdotgg#9258).
Conflict resolutions (14 files), keep-both unless noted:
- RpcAuthorization / contracts rpc / ws.ts: fork's PR rank, cherry-pick and
upstream-release RPCs sit beside upstream's new pullRequestsSummary.
- ws.ts route layer: fork's computer task/view brokers plus upstream's
serverSelfUpdate wrapped in withRunningThreadContinuation.
- PullRequestService: fork's upstream-project reads and scoped diff epochs
kept; upstream's summary read, refCacheKey and viewer-flight invalidation
added. invalidate() now scopes diff-only invalidation AND clears viewer
flights on a whole-workspace refresh. Test file keeps both new suites.
- client-runtime pullRequests: fork's PR stack atoms kept, TTL constant
takes upstream's new name.
- ChatMarkdown: upstream's one-character gutter rule replaces the fork's
equivalent digitWidth fix.
- ChatComposer: fork's voice-session button kept; context meter now honours
settings.contextWindowMeterEnabled.
- ConnectionsSettings: fork's EnvironmentLabelControl kept with upstream's
min-w-0 truncate.
- mobile ThreadFeed: upstream's markdownLinkHandlers plus the fork's
onCancelQueuedMessage.
- ClaudeDriver imports: fork's superset. build-desktop-artifact: distro and
arch both threaded through.
Integration: upstream's restart continuation and the fork's resume-on-restart
both ran at boot on the same orphans. The continuation-marker helpers move to
provider/serverUpdateContinuation.ts and SessionStartupReconciler now defers
any binding carrying the marker to upstream's pass (logged as deferred), so a
thread is never continued twice. Test added.
Dependencies reinstalled for the expo-sharing pin; the stale pnpm copy of
@t3tools/mobile-markdown-text was what broke the mobile typecheck.
Typecheck clean: contracts, shared, client-runtime, server, web, mobile
(desktop exits 1 on a pre-existing warning-level diagnostic). Startup and
self-update suites 36 passing; web sidebar/composer/settings suites 419
passing; fork guard script OK.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 2, 2026
## What's Changed
* fix(web): hide deleted providers with prototype keys by @Lucenx9 in pingdotgg/t3code#8337
* feat(mobile): long-press file references for path and open actions by @juliusmarminge in pingdotgg/t3code#9258
* fix(desktop): exclude opposite macOS pty prebuilds by @extoci in pingdotgg/t3code#9240
* feat(web): add copy path button to diff headers by @ipanasenko in pingdotgg/t3code#2403
* fix(server): subscribe before provider settings watcher by @t3-code[bot] in pingdotgg/t3code#9271
* fix(mobile): show filled filter icon on Android when filters are active by @none23 in pingdotgg/t3code#9217
* fix(chat): show single tool calls without summaries by @maria-rcks in pingdotgg/t3code#9267
* fix(web): warn when shared settings have no target environment by @imabdulazeez in pingdotgg/t3code#9207
* fix(web): confirm closing agent-controlled browsers by @maria-rcks in pingdotgg/t3code#9272
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1260...v0.0.39-nightly.20260902.1261
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260902.1261
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📱 Native ChangeChanges the native fingerprint; merging blocks production OTAs until a new store build ships.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

@juliusmarminge
, '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(mobile): long-press file references for path and open actions - #9258

Merged
juliusmarminge merged 4 commits into
mainfrom
t3code/ios-file-chip-context-menu
Sep 2, 2026
Merged

feat(mobile): long-press file references for path and open actions#9258
juliusmarminge merged 4 commits into
mainfrom
t3code/ios-file-chip-context-menu

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 2, 2026

Copy link
Copy Markdown
Member

What changed

  • File chips in iOS messages get a native long-press context menu with Copy full path, Copy relative path, and Open in file viewer, the same actions the web chip offers on right-click.
  • T3MarkdownTextRun accepts a JSON contextMenuConfig; T3MarkdownText maps runs that carry one to UITextItem links so iOS 17's text-item menu owns the gesture. Tap and text selection keep working; text drag is disabled on chat text.
  • SelectableMarkdownText gains fileContextMenu and onFileContextMenuAction. The feed builds the menu description and runs actions from one handler at the root, so rows subscribe to nothing extra per chip.

Why

Tapping a file reference opened it and that was the only thing you could do with it. Users copying a path had to open the file first. Web has had a chip context menu for a while; this brings the same set to iOS through UIKit rather than a JS action sheet.

Split out of #9253 so the media refactor there stays a refactor.

UI changes

Before: tap onlyAfter: native context menu
No long-press affordance on file chips.

Testing

  • fileChipMenu.test.ts (5 tests)
  • Mobile typecheck, lint, and format on changed files

Created with Claude Fable 5 using the Claude Code harness in T3 Code.

🤖 Generated with Claude Code


Note

Medium Risk
Touches native gesture handling and iOS 17 text-item menus on chat text; disabling text drag and suppressing recognizers on menu runs could affect edge-case interactions outside file chips.

Overview
Adds native long-press context menus on iOS for inline file references in chat markdown, matching web chip actions: copy full path, copy relative path, and open in the file viewer.

The t3-markdown-text native layer now accepts a JSON contextMenuConfig on text runs, builds a UIMenu, and wires T3MarkdownText (iOS 17 text-item delegates) so menu runs are exposed as links while custom tap/long-press is skipped on those runs. Chat UITextView also disables text drag. SelectableMarkdownText gains fileContextMenu / onFileContextMenuAction plus a React context so file chips in the tree share one handler.

ThreadFeed centralizes tap and menu behavior in markdownLinkHandlers via new fileChipMenu / resolveFileChipTarget. resolveWorkspaceRelativeFilePath now rejects paths that escape the workspace with .. (e.g. /repo/../outside.txt). User docs note iOS touch-and-hold on file references.

Reviewed by Cursor Bugbot for commit 67b0275. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add long-press context menu for file references in mobile markdown text

  • Native iOS T3MarkdownText and T3MarkdownTextRun build context menus from a serialized JSON config and emit a native event when an action is selected. Custom tap/long-press recognizers are suppressed on runs with a menu, while other runs keep existing gesture handling.
  • React Native wiring in SelectableMarkdownText.ios.tsx and ThreadFeed.tsx resolves file-chip hrefs to menus and dispatches copy-path and open-file actions.
  • fileChipMenu.ts classifies file links into full and relative paths and builds a menu with only the supported copy actions plus the file-viewer open action.
  • filePath.ts now rejects absolute targets that escape the workspace via a .. segment instead of normalizing them.
  • Risk: disabling drag interaction on T3MarkdownText means text drag-and-drop no longer works on iOS; handleLongPressIfNecessary now fires once at the began state rather than across all long-press transitions.

Macroscope summarized 67b0275.

File chips in iOS messages had no way to copy a path; tapping opened the
file and that was it. The native text view now maps chips with a menu to
UITextItem links, so iOS 17 shows a context menu on long-press while
tapping and text selection keep working. The menu offers copy full path,
copy relative path, and open in file viewer, matching the web chip.
The menu is described per chip as JSON and actions run from one handler
at the feed root, so rows subscribe to nothing extra per chip.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). 📱 Native Change Changes the native fingerprint; merging blocks production OTAs until a new store build ships. labels Sep 2, 2026
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx
Julius Marmingeand others added 2 commits September 2, 2026 12:44
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…links
Chips inside blockquotes, lists, and tables never received the menu
handlers because NativeMarkdownBlock only forwarded onLinkPress. The
handlers now travel through a context the same way the image renderer
does. Links the feed cannot open, such as ~/x or ../x, get no menu
instead of one whose only action does nothing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment threadapps/mobile/src/features/threads/fileChipMenu.ts
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.2 KiB13.1 KiB−88 B (−0.6%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB+4 B (+0.1%)7.3 KiB
CodexLive turn WebSocket wire6.3 KiB6.2 KiB−92 B (−1.4%)7.8 KiB
CodexLive turn WebSocket decoded55.5 KiB54.7 KiB−822 B (−1.4%)66.4 KiB
CodexLive turn messages880 (0.0%)21
ClaudeTotal thread wire13.3 KiB13.3 KiB+12 B (+0.1%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−3 B (−0.0%)7.3 KiB
ClaudeLive turn WebSocket wire6.4 KiB6.4 KiB+15 B (+0.2%)7.8 KiB
ClaudeLive turn WebSocket decoded56.4 KiB56.4 KiB0 B (0.0%)66.4 KiB
ClaudeLive turn messages10100 (0.0%)21

Baseline: aab4049 · PR result: 67b0275 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 109.4 KiB
  • Claude decoded thread snapshot: 110.1 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

…t is not relative
resolveWorkspaceRelativeFilePath matched /repo/../outside.txt as inside
/repo because the prefix check ran before traversal was normalized.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
juliusmarminge pushed a commit that referenced this pull request Sep 2, 2026
Split the iOS file-chip context menu into its own PR (#9258) and cut the
parts of this one that were not unification:
- restore the work-log presentation tests that b1daa51 deleted; they
cover tool naming and browser summaries this PR never touched
- drop availableMediaActions; both clients kept a full label and runner
table anyway, so only the MediaActionId union is shared
- one behavior for link parsing: conventional extensionless names are
files everywhere, and file: URLs are decoded by the caller like every
other destination, so no more option flags
- inline resolveAssetUrlResult into its one caller
- remove comments that explained the change instead of the code
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This adds a new iOS long-press workflow through native UIKit text-item menus and bridges actions into production message rendering. It also disables text drag-and-drop and changes gesture handling for the shared markdown text component, giving the change a broader interaction surface than a simple UI adjustment.

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

@juliusmarminge
juliusmarminge merged commit 9159b80 into mainSep 2, 2026
27 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/ios-file-chip-context-menu branch September 2, 2026 20:03
sheehanmunim added a commit to munimtechnologies/mtcode that referenced this pull request Sep 2, 2026
…continuation, provider editor redesign, context meter opt-in, linked-PR summaries)
Brings the fork up to origin/main e0da6c6..b57726c. Highlights: continue
active threads across server self-updates (pingdotgg#9167), provider editor and models
list redesign (pingdotgg#8508), opt-in context window meter (pingdotgg#9190), linked-PR summary
RPC with last-good caching (pingdotgg#9176), relay credential refresh before expiry
(pingdotgg#9178), composer draft preserved through worktree setup (pingdotgg#9197), project
skill discovery for Claude (pingdotgg#9210), copy-path on diff headers (pingdotgg#2403), mobile
long-press file references (pingdotgg#9258).
Conflict resolutions (14 files), keep-both unless noted:
- RpcAuthorization / contracts rpc / ws.ts: fork's PR rank, cherry-pick and
upstream-release RPCs sit beside upstream's new pullRequestsSummary.
- ws.ts route layer: fork's computer task/view brokers plus upstream's
serverSelfUpdate wrapped in withRunningThreadContinuation.
- PullRequestService: fork's upstream-project reads and scoped diff epochs
kept; upstream's summary read, refCacheKey and viewer-flight invalidation
added. invalidate() now scopes diff-only invalidation AND clears viewer
flights on a whole-workspace refresh. Test file keeps both new suites.
- client-runtime pullRequests: fork's PR stack atoms kept, TTL constant
takes upstream's new name.
- ChatMarkdown: upstream's one-character gutter rule replaces the fork's
equivalent digitWidth fix.
- ChatComposer: fork's voice-session button kept; context meter now honours
settings.contextWindowMeterEnabled.
- ConnectionsSettings: fork's EnvironmentLabelControl kept with upstream's
min-w-0 truncate.
- mobile ThreadFeed: upstream's markdownLinkHandlers plus the fork's
onCancelQueuedMessage.
- ClaudeDriver imports: fork's superset. build-desktop-artifact: distro and
arch both threaded through.
Integration: upstream's restart continuation and the fork's resume-on-restart
both ran at boot on the same orphans. The continuation-marker helpers move to
provider/serverUpdateContinuation.ts and SessionStartupReconciler now defers
any binding carrying the marker to upstream's pass (logged as deferred), so a
thread is never continued twice. Test added.
Dependencies reinstalled for the expo-sharing pin; the stale pnpm copy of
@t3tools/mobile-markdown-text was what broke the mobile typecheck.
Typecheck clean: contracts, shared, client-runtime, server, web, mobile
(desktop exits 1 on a pre-existing warning-level diagnostic). Startup and
self-update suites 36 passing; web sidebar/composer/settings suites 419
passing; fork guard script OK.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 2, 2026
## What's Changed
* fix(web): hide deleted providers with prototype keys by @Lucenx9 in pingdotgg/t3code#8337
* feat(mobile): long-press file references for path and open actions by @juliusmarminge in pingdotgg/t3code#9258
* fix(desktop): exclude opposite macOS pty prebuilds by @extoci in pingdotgg/t3code#9240
* feat(web): add copy path button to diff headers by @ipanasenko in pingdotgg/t3code#2403
* fix(server): subscribe before provider settings watcher by @t3-code[bot] in pingdotgg/t3code#9271
* fix(mobile): show filled filter icon on Android when filters are active by @none23 in pingdotgg/t3code#9217
* fix(chat): show single tool calls without summaries by @maria-rcks in pingdotgg/t3code#9267
* fix(web): warn when shared settings have no target environment by @imabdulazeez in pingdotgg/t3code#9207
* fix(web): confirm closing agent-controlled browsers by @maria-rcks in pingdotgg/t3code#9272
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1260...v0.0.39-nightly.20260902.1261
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260902.1261
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📱 Native ChangeChanges the native fingerprint; merging blocks production OTAs until a new store build ships.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

@juliusmarminge
, '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(mobile): long-press file references for path and open actions - #9258

Merged
juliusmarminge merged 4 commits into
mainfrom
t3code/ios-file-chip-context-menu
Sep 2, 2026
Merged

feat(mobile): long-press file references for path and open actions#9258
juliusmarminge merged 4 commits into
mainfrom
t3code/ios-file-chip-context-menu

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 2, 2026

Copy link
Copy Markdown
Member

What changed

  • File chips in iOS messages get a native long-press context menu with Copy full path, Copy relative path, and Open in file viewer, the same actions the web chip offers on right-click.
  • T3MarkdownTextRun accepts a JSON contextMenuConfig; T3MarkdownText maps runs that carry one to UITextItem links so iOS 17's text-item menu owns the gesture. Tap and text selection keep working; text drag is disabled on chat text.
  • SelectableMarkdownText gains fileContextMenu and onFileContextMenuAction. The feed builds the menu description and runs actions from one handler at the root, so rows subscribe to nothing extra per chip.

Why

Tapping a file reference opened it and that was the only thing you could do with it. Users copying a path had to open the file first. Web has had a chip context menu for a while; this brings the same set to iOS through UIKit rather than a JS action sheet.

Split out of #9253 so the media refactor there stays a refactor.

UI changes

Before: tap onlyAfter: native context menu
No long-press affordance on file chips.

Testing

  • fileChipMenu.test.ts (5 tests)
  • Mobile typecheck, lint, and format on changed files

Created with Claude Fable 5 using the Claude Code harness in T3 Code.

🤖 Generated with Claude Code


Note

Medium Risk
Touches native gesture handling and iOS 17 text-item menus on chat text; disabling text drag and suppressing recognizers on menu runs could affect edge-case interactions outside file chips.

Overview
Adds native long-press context menus on iOS for inline file references in chat markdown, matching web chip actions: copy full path, copy relative path, and open in the file viewer.

The t3-markdown-text native layer now accepts a JSON contextMenuConfig on text runs, builds a UIMenu, and wires T3MarkdownText (iOS 17 text-item delegates) so menu runs are exposed as links while custom tap/long-press is skipped on those runs. Chat UITextView also disables text drag. SelectableMarkdownText gains fileContextMenu / onFileContextMenuAction plus a React context so file chips in the tree share one handler.

ThreadFeed centralizes tap and menu behavior in markdownLinkHandlers via new fileChipMenu / resolveFileChipTarget. resolveWorkspaceRelativeFilePath now rejects paths that escape the workspace with .. (e.g. /repo/../outside.txt). User docs note iOS touch-and-hold on file references.

Reviewed by Cursor Bugbot for commit 67b0275. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add long-press context menu for file references in mobile markdown text

  • Native iOS T3MarkdownText and T3MarkdownTextRun build context menus from a serialized JSON config and emit a native event when an action is selected. Custom tap/long-press recognizers are suppressed on runs with a menu, while other runs keep existing gesture handling.
  • React Native wiring in SelectableMarkdownText.ios.tsx and ThreadFeed.tsx resolves file-chip hrefs to menus and dispatches copy-path and open-file actions.
  • fileChipMenu.ts classifies file links into full and relative paths and builds a menu with only the supported copy actions plus the file-viewer open action.
  • filePath.ts now rejects absolute targets that escape the workspace via a .. segment instead of normalizing them.
  • Risk: disabling drag interaction on T3MarkdownText means text drag-and-drop no longer works on iOS; handleLongPressIfNecessary now fires once at the began state rather than across all long-press transitions.

Macroscope summarized 67b0275.

File chips in iOS messages had no way to copy a path; tapping opened the
file and that was it. The native text view now maps chips with a menu to
UITextItem links, so iOS 17 shows a context menu on long-press while
tapping and text selection keep working. The menu offers copy full path,
copy relative path, and open in file viewer, matching the web chip.
The menu is described per chip as JSON and actions run from one handler
at the feed root, so rows subscribe to nothing extra per chip.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). 📱 Native Change Changes the native fingerprint; merging blocks production OTAs until a new store build ships. labels Sep 2, 2026
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx
Julius Marmingeand others added 2 commits September 2, 2026 12:44
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…links
Chips inside blockquotes, lists, and tables never received the menu
handlers because NativeMarkdownBlock only forwarded onLinkPress. The
handlers now travel through a context the same way the image renderer
does. Links the feed cannot open, such as ~/x or ../x, get no menu
instead of one whose only action does nothing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment threadapps/mobile/src/features/threads/fileChipMenu.ts
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.2 KiB13.1 KiB−88 B (−0.6%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB+4 B (+0.1%)7.3 KiB
CodexLive turn WebSocket wire6.3 KiB6.2 KiB−92 B (−1.4%)7.8 KiB
CodexLive turn WebSocket decoded55.5 KiB54.7 KiB−822 B (−1.4%)66.4 KiB
CodexLive turn messages880 (0.0%)21
ClaudeTotal thread wire13.3 KiB13.3 KiB+12 B (+0.1%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−3 B (−0.0%)7.3 KiB
ClaudeLive turn WebSocket wire6.4 KiB6.4 KiB+15 B (+0.2%)7.8 KiB
ClaudeLive turn WebSocket decoded56.4 KiB56.4 KiB0 B (0.0%)66.4 KiB
ClaudeLive turn messages10100 (0.0%)21

Baseline: aab4049 · PR result: 67b0275 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 109.4 KiB
  • Claude decoded thread snapshot: 110.1 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

…t is not relative
resolveWorkspaceRelativeFilePath matched /repo/../outside.txt as inside
/repo because the prefix check ran before traversal was normalized.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
juliusmarminge pushed a commit that referenced this pull request Sep 2, 2026
Split the iOS file-chip context menu into its own PR (#9258) and cut the
parts of this one that were not unification:
- restore the work-log presentation tests that b1daa51 deleted; they
cover tool naming and browser summaries this PR never touched
- drop availableMediaActions; both clients kept a full label and runner
table anyway, so only the MediaActionId union is shared
- one behavior for link parsing: conventional extensionless names are
files everywhere, and file: URLs are decoded by the caller like every
other destination, so no more option flags
- inline resolveAssetUrlResult into its one caller
- remove comments that explained the change instead of the code
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This adds a new iOS long-press workflow through native UIKit text-item menus and bridges actions into production message rendering. It also disables text drag-and-drop and changes gesture handling for the shared markdown text component, giving the change a broader interaction surface than a simple UI adjustment.

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

@juliusmarminge
juliusmarminge merged commit 9159b80 into mainSep 2, 2026
27 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/ios-file-chip-context-menu branch September 2, 2026 20:03
sheehanmunim added a commit to munimtechnologies/mtcode that referenced this pull request Sep 2, 2026
…continuation, provider editor redesign, context meter opt-in, linked-PR summaries)
Brings the fork up to origin/main e0da6c6..b57726c. Highlights: continue
active threads across server self-updates (pingdotgg#9167), provider editor and models
list redesign (pingdotgg#8508), opt-in context window meter (pingdotgg#9190), linked-PR summary
RPC with last-good caching (pingdotgg#9176), relay credential refresh before expiry
(pingdotgg#9178), composer draft preserved through worktree setup (pingdotgg#9197), project
skill discovery for Claude (pingdotgg#9210), copy-path on diff headers (pingdotgg#2403), mobile
long-press file references (pingdotgg#9258).
Conflict resolutions (14 files), keep-both unless noted:
- RpcAuthorization / contracts rpc / ws.ts: fork's PR rank, cherry-pick and
upstream-release RPCs sit beside upstream's new pullRequestsSummary.
- ws.ts route layer: fork's computer task/view brokers plus upstream's
serverSelfUpdate wrapped in withRunningThreadContinuation.
- PullRequestService: fork's upstream-project reads and scoped diff epochs
kept; upstream's summary read, refCacheKey and viewer-flight invalidation
added. invalidate() now scopes diff-only invalidation AND clears viewer
flights on a whole-workspace refresh. Test file keeps both new suites.
- client-runtime pullRequests: fork's PR stack atoms kept, TTL constant
takes upstream's new name.
- ChatMarkdown: upstream's one-character gutter rule replaces the fork's
equivalent digitWidth fix.
- ChatComposer: fork's voice-session button kept; context meter now honours
settings.contextWindowMeterEnabled.
- ConnectionsSettings: fork's EnvironmentLabelControl kept with upstream's
min-w-0 truncate.
- mobile ThreadFeed: upstream's markdownLinkHandlers plus the fork's
onCancelQueuedMessage.
- ClaudeDriver imports: fork's superset. build-desktop-artifact: distro and
arch both threaded through.
Integration: upstream's restart continuation and the fork's resume-on-restart
both ran at boot on the same orphans. The continuation-marker helpers move to
provider/serverUpdateContinuation.ts and SessionStartupReconciler now defers
any binding carrying the marker to upstream's pass (logged as deferred), so a
thread is never continued twice. Test added.
Dependencies reinstalled for the expo-sharing pin; the stale pnpm copy of
@t3tools/mobile-markdown-text was what broke the mobile typecheck.
Typecheck clean: contracts, shared, client-runtime, server, web, mobile
(desktop exits 1 on a pre-existing warning-level diagnostic). Startup and
self-update suites 36 passing; web sidebar/composer/settings suites 419
passing; fork guard script OK.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 2, 2026
## What's Changed
* fix(web): hide deleted providers with prototype keys by @Lucenx9 in pingdotgg/t3code#8337
* feat(mobile): long-press file references for path and open actions by @juliusmarminge in pingdotgg/t3code#9258
* fix(desktop): exclude opposite macOS pty prebuilds by @extoci in pingdotgg/t3code#9240
* feat(web): add copy path button to diff headers by @ipanasenko in pingdotgg/t3code#2403
* fix(server): subscribe before provider settings watcher by @t3-code[bot] in pingdotgg/t3code#9271
* fix(mobile): show filled filter icon on Android when filters are active by @none23 in pingdotgg/t3code#9217
* fix(chat): show single tool calls without summaries by @maria-rcks in pingdotgg/t3code#9267
* fix(web): warn when shared settings have no target environment by @imabdulazeez in pingdotgg/t3code#9207
* fix(web): confirm closing agent-controlled browsers by @maria-rcks in pingdotgg/t3code#9272
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1260...v0.0.39-nightly.20260902.1261
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260902.1261
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📱 Native ChangeChanges the native fingerprint; merging blocks production OTAs until a new store build ships.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

@juliusmarminge
, '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(mobile): long-press file references for path and open actions - #9258

Merged
juliusmarminge merged 4 commits into
mainfrom
t3code/ios-file-chip-context-menu
Sep 2, 2026
Merged

feat(mobile): long-press file references for path and open actions#9258
juliusmarminge merged 4 commits into
mainfrom
t3code/ios-file-chip-context-menu

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 2, 2026

Copy link
Copy Markdown
Member

What changed

  • File chips in iOS messages get a native long-press context menu with Copy full path, Copy relative path, and Open in file viewer, the same actions the web chip offers on right-click.
  • T3MarkdownTextRun accepts a JSON contextMenuConfig; T3MarkdownText maps runs that carry one to UITextItem links so iOS 17's text-item menu owns the gesture. Tap and text selection keep working; text drag is disabled on chat text.
  • SelectableMarkdownText gains fileContextMenu and onFileContextMenuAction. The feed builds the menu description and runs actions from one handler at the root, so rows subscribe to nothing extra per chip.

Why

Tapping a file reference opened it and that was the only thing you could do with it. Users copying a path had to open the file first. Web has had a chip context menu for a while; this brings the same set to iOS through UIKit rather than a JS action sheet.

Split out of #9253 so the media refactor there stays a refactor.

UI changes

Before: tap onlyAfter: native context menu
No long-press affordance on file chips.

Testing

  • fileChipMenu.test.ts (5 tests)
  • Mobile typecheck, lint, and format on changed files

Created with Claude Fable 5 using the Claude Code harness in T3 Code.

🤖 Generated with Claude Code


Note

Medium Risk
Touches native gesture handling and iOS 17 text-item menus on chat text; disabling text drag and suppressing recognizers on menu runs could affect edge-case interactions outside file chips.

Overview
Adds native long-press context menus on iOS for inline file references in chat markdown, matching web chip actions: copy full path, copy relative path, and open in the file viewer.

The t3-markdown-text native layer now accepts a JSON contextMenuConfig on text runs, builds a UIMenu, and wires T3MarkdownText (iOS 17 text-item delegates) so menu runs are exposed as links while custom tap/long-press is skipped on those runs. Chat UITextView also disables text drag. SelectableMarkdownText gains fileContextMenu / onFileContextMenuAction plus a React context so file chips in the tree share one handler.

ThreadFeed centralizes tap and menu behavior in markdownLinkHandlers via new fileChipMenu / resolveFileChipTarget. resolveWorkspaceRelativeFilePath now rejects paths that escape the workspace with .. (e.g. /repo/../outside.txt). User docs note iOS touch-and-hold on file references.

Reviewed by Cursor Bugbot for commit 67b0275. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add long-press context menu for file references in mobile markdown text

  • Native iOS T3MarkdownText and T3MarkdownTextRun build context menus from a serialized JSON config and emit a native event when an action is selected. Custom tap/long-press recognizers are suppressed on runs with a menu, while other runs keep existing gesture handling.
  • React Native wiring in SelectableMarkdownText.ios.tsx and ThreadFeed.tsx resolves file-chip hrefs to menus and dispatches copy-path and open-file actions.
  • fileChipMenu.ts classifies file links into full and relative paths and builds a menu with only the supported copy actions plus the file-viewer open action.
  • filePath.ts now rejects absolute targets that escape the workspace via a .. segment instead of normalizing them.
  • Risk: disabling drag interaction on T3MarkdownText means text drag-and-drop no longer works on iOS; handleLongPressIfNecessary now fires once at the began state rather than across all long-press transitions.

Macroscope summarized 67b0275.

File chips in iOS messages had no way to copy a path; tapping opened the
file and that was it. The native text view now maps chips with a menu to
UITextItem links, so iOS 17 shows a context menu on long-press while
tapping and text selection keep working. The menu offers copy full path,
copy relative path, and open in file viewer, matching the web chip.
The menu is described per chip as JSON and actions run from one handler
at the feed root, so rows subscribe to nothing extra per chip.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). 📱 Native Change Changes the native fingerprint; merging blocks production OTAs until a new store build ships. labels Sep 2, 2026
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx
Julius Marmingeand others added 2 commits September 2, 2026 12:44
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…links
Chips inside blockquotes, lists, and tables never received the menu
handlers because NativeMarkdownBlock only forwarded onLinkPress. The
handlers now travel through a context the same way the image renderer
does. Links the feed cannot open, such as ~/x or ../x, get no menu
instead of one whose only action does nothing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment threadapps/mobile/src/features/threads/fileChipMenu.ts
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.2 KiB13.1 KiB−88 B (−0.6%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB+4 B (+0.1%)7.3 KiB
CodexLive turn WebSocket wire6.3 KiB6.2 KiB−92 B (−1.4%)7.8 KiB
CodexLive turn WebSocket decoded55.5 KiB54.7 KiB−822 B (−1.4%)66.4 KiB
CodexLive turn messages880 (0.0%)21
ClaudeTotal thread wire13.3 KiB13.3 KiB+12 B (+0.1%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−3 B (−0.0%)7.3 KiB
ClaudeLive turn WebSocket wire6.4 KiB6.4 KiB+15 B (+0.2%)7.8 KiB
ClaudeLive turn WebSocket decoded56.4 KiB56.4 KiB0 B (0.0%)66.4 KiB
ClaudeLive turn messages10100 (0.0%)21

Baseline: aab4049 · PR result: 67b0275 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 109.4 KiB
  • Claude decoded thread snapshot: 110.1 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

…t is not relative
resolveWorkspaceRelativeFilePath matched /repo/../outside.txt as inside
/repo because the prefix check ran before traversal was normalized.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
juliusmarminge pushed a commit that referenced this pull request Sep 2, 2026
Split the iOS file-chip context menu into its own PR (#9258) and cut the
parts of this one that were not unification:
- restore the work-log presentation tests that b1daa51 deleted; they
cover tool naming and browser summaries this PR never touched
- drop availableMediaActions; both clients kept a full label and runner
table anyway, so only the MediaActionId union is shared
- one behavior for link parsing: conventional extensionless names are
files everywhere, and file: URLs are decoded by the caller like every
other destination, so no more option flags
- inline resolveAssetUrlResult into its one caller
- remove comments that explained the change instead of the code
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This adds a new iOS long-press workflow through native UIKit text-item menus and bridges actions into production message rendering. It also disables text drag-and-drop and changes gesture handling for the shared markdown text component, giving the change a broader interaction surface than a simple UI adjustment.

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

@juliusmarminge
juliusmarminge merged commit 9159b80 into mainSep 2, 2026
27 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/ios-file-chip-context-menu branch September 2, 2026 20:03
sheehanmunim added a commit to munimtechnologies/mtcode that referenced this pull request Sep 2, 2026
…continuation, provider editor redesign, context meter opt-in, linked-PR summaries)
Brings the fork up to origin/main e0da6c6..b57726c. Highlights: continue
active threads across server self-updates (pingdotgg#9167), provider editor and models
list redesign (pingdotgg#8508), opt-in context window meter (pingdotgg#9190), linked-PR summary
RPC with last-good caching (pingdotgg#9176), relay credential refresh before expiry
(pingdotgg#9178), composer draft preserved through worktree setup (pingdotgg#9197), project
skill discovery for Claude (pingdotgg#9210), copy-path on diff headers (pingdotgg#2403), mobile
long-press file references (pingdotgg#9258).
Conflict resolutions (14 files), keep-both unless noted:
- RpcAuthorization / contracts rpc / ws.ts: fork's PR rank, cherry-pick and
upstream-release RPCs sit beside upstream's new pullRequestsSummary.
- ws.ts route layer: fork's computer task/view brokers plus upstream's
serverSelfUpdate wrapped in withRunningThreadContinuation.
- PullRequestService: fork's upstream-project reads and scoped diff epochs
kept; upstream's summary read, refCacheKey and viewer-flight invalidation
added. invalidate() now scopes diff-only invalidation AND clears viewer
flights on a whole-workspace refresh. Test file keeps both new suites.
- client-runtime pullRequests: fork's PR stack atoms kept, TTL constant
takes upstream's new name.
- ChatMarkdown: upstream's one-character gutter rule replaces the fork's
equivalent digitWidth fix.
- ChatComposer: fork's voice-session button kept; context meter now honours
settings.contextWindowMeterEnabled.
- ConnectionsSettings: fork's EnvironmentLabelControl kept with upstream's
min-w-0 truncate.
- mobile ThreadFeed: upstream's markdownLinkHandlers plus the fork's
onCancelQueuedMessage.
- ClaudeDriver imports: fork's superset. build-desktop-artifact: distro and
arch both threaded through.
Integration: upstream's restart continuation and the fork's resume-on-restart
both ran at boot on the same orphans. The continuation-marker helpers move to
provider/serverUpdateContinuation.ts and SessionStartupReconciler now defers
any binding carrying the marker to upstream's pass (logged as deferred), so a
thread is never continued twice. Test added.
Dependencies reinstalled for the expo-sharing pin; the stale pnpm copy of
@t3tools/mobile-markdown-text was what broke the mobile typecheck.
Typecheck clean: contracts, shared, client-runtime, server, web, mobile
(desktop exits 1 on a pre-existing warning-level diagnostic). Startup and
self-update suites 36 passing; web sidebar/composer/settings suites 419
passing; fork guard script OK.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 2, 2026
## What's Changed
* fix(web): hide deleted providers with prototype keys by @Lucenx9 in pingdotgg/t3code#8337
* feat(mobile): long-press file references for path and open actions by @juliusmarminge in pingdotgg/t3code#9258
* fix(desktop): exclude opposite macOS pty prebuilds by @extoci in pingdotgg/t3code#9240
* feat(web): add copy path button to diff headers by @ipanasenko in pingdotgg/t3code#2403
* fix(server): subscribe before provider settings watcher by @t3-code[bot] in pingdotgg/t3code#9271
* fix(mobile): show filled filter icon on Android when filters are active by @none23 in pingdotgg/t3code#9217
* fix(chat): show single tool calls without summaries by @maria-rcks in pingdotgg/t3code#9267
* fix(web): warn when shared settings have no target environment by @imabdulazeez in pingdotgg/t3code#9207
* fix(web): confirm closing agent-controlled browsers by @maria-rcks in pingdotgg/t3code#9272
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1260...v0.0.39-nightly.20260902.1261
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260902.1261
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📱 Native ChangeChanges the native fingerprint; merging blocks production OTAs until a new store build ships.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

@juliusmarminge
, '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(mobile): long-press file references for path and open actions - #9258

Merged
juliusmarminge merged 4 commits into
mainfrom
t3code/ios-file-chip-context-menu
Sep 2, 2026
Merged

feat(mobile): long-press file references for path and open actions#9258
juliusmarminge merged 4 commits into
mainfrom
t3code/ios-file-chip-context-menu

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 2, 2026

Copy link
Copy Markdown
Member

What changed

  • File chips in iOS messages get a native long-press context menu with Copy full path, Copy relative path, and Open in file viewer, the same actions the web chip offers on right-click.
  • T3MarkdownTextRun accepts a JSON contextMenuConfig; T3MarkdownText maps runs that carry one to UITextItem links so iOS 17's text-item menu owns the gesture. Tap and text selection keep working; text drag is disabled on chat text.
  • SelectableMarkdownText gains fileContextMenu and onFileContextMenuAction. The feed builds the menu description and runs actions from one handler at the root, so rows subscribe to nothing extra per chip.

Why

Tapping a file reference opened it and that was the only thing you could do with it. Users copying a path had to open the file first. Web has had a chip context menu for a while; this brings the same set to iOS through UIKit rather than a JS action sheet.

Split out of #9253 so the media refactor there stays a refactor.

UI changes

Before: tap onlyAfter: native context menu
No long-press affordance on file chips.

Testing

  • fileChipMenu.test.ts (5 tests)
  • Mobile typecheck, lint, and format on changed files

Created with Claude Fable 5 using the Claude Code harness in T3 Code.

🤖 Generated with Claude Code


Note

Medium Risk
Touches native gesture handling and iOS 17 text-item menus on chat text; disabling text drag and suppressing recognizers on menu runs could affect edge-case interactions outside file chips.

Overview
Adds native long-press context menus on iOS for inline file references in chat markdown, matching web chip actions: copy full path, copy relative path, and open in the file viewer.

The t3-markdown-text native layer now accepts a JSON contextMenuConfig on text runs, builds a UIMenu, and wires T3MarkdownText (iOS 17 text-item delegates) so menu runs are exposed as links while custom tap/long-press is skipped on those runs. Chat UITextView also disables text drag. SelectableMarkdownText gains fileContextMenu / onFileContextMenuAction plus a React context so file chips in the tree share one handler.

ThreadFeed centralizes tap and menu behavior in markdownLinkHandlers via new fileChipMenu / resolveFileChipTarget. resolveWorkspaceRelativeFilePath now rejects paths that escape the workspace with .. (e.g. /repo/../outside.txt). User docs note iOS touch-and-hold on file references.

Reviewed by Cursor Bugbot for commit 67b0275. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add long-press context menu for file references in mobile markdown text

  • Native iOS T3MarkdownText and T3MarkdownTextRun build context menus from a serialized JSON config and emit a native event when an action is selected. Custom tap/long-press recognizers are suppressed on runs with a menu, while other runs keep existing gesture handling.
  • React Native wiring in SelectableMarkdownText.ios.tsx and ThreadFeed.tsx resolves file-chip hrefs to menus and dispatches copy-path and open-file actions.
  • fileChipMenu.ts classifies file links into full and relative paths and builds a menu with only the supported copy actions plus the file-viewer open action.
  • filePath.ts now rejects absolute targets that escape the workspace via a .. segment instead of normalizing them.
  • Risk: disabling drag interaction on T3MarkdownText means text drag-and-drop no longer works on iOS; handleLongPressIfNecessary now fires once at the began state rather than across all long-press transitions.

Macroscope summarized 67b0275.

File chips in iOS messages had no way to copy a path; tapping opened the
file and that was it. The native text view now maps chips with a menu to
UITextItem links, so iOS 17 shows a context menu on long-press while
tapping and text selection keep working. The menu offers copy full path,
copy relative path, and open in file viewer, matching the web chip.
The menu is described per chip as JSON and actions run from one handler
at the feed root, so rows subscribe to nothing extra per chip.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). 📱 Native Change Changes the native fingerprint; merging blocks production OTAs until a new store build ships. labels Sep 2, 2026
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx
Julius Marmingeand others added 2 commits September 2, 2026 12:44
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…links
Chips inside blockquotes, lists, and tables never received the menu
handlers because NativeMarkdownBlock only forwarded onLinkPress. The
handlers now travel through a context the same way the image renderer
does. Links the feed cannot open, such as ~/x or ../x, get no menu
instead of one whose only action does nothing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment threadapps/mobile/src/features/threads/fileChipMenu.ts
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.2 KiB13.1 KiB−88 B (−0.6%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB+4 B (+0.1%)7.3 KiB
CodexLive turn WebSocket wire6.3 KiB6.2 KiB−92 B (−1.4%)7.8 KiB
CodexLive turn WebSocket decoded55.5 KiB54.7 KiB−822 B (−1.4%)66.4 KiB
CodexLive turn messages880 (0.0%)21
ClaudeTotal thread wire13.3 KiB13.3 KiB+12 B (+0.1%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−3 B (−0.0%)7.3 KiB
ClaudeLive turn WebSocket wire6.4 KiB6.4 KiB+15 B (+0.2%)7.8 KiB
ClaudeLive turn WebSocket decoded56.4 KiB56.4 KiB0 B (0.0%)66.4 KiB
ClaudeLive turn messages10100 (0.0%)21

Baseline: aab4049 · PR result: 67b0275 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 109.4 KiB
  • Claude decoded thread snapshot: 110.1 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

…t is not relative
resolveWorkspaceRelativeFilePath matched /repo/../outside.txt as inside
/repo because the prefix check ran before traversal was normalized.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
juliusmarminge pushed a commit that referenced this pull request Sep 2, 2026
Split the iOS file-chip context menu into its own PR (#9258) and cut the
parts of this one that were not unification:
- restore the work-log presentation tests that b1daa51 deleted; they
cover tool naming and browser summaries this PR never touched
- drop availableMediaActions; both clients kept a full label and runner
table anyway, so only the MediaActionId union is shared
- one behavior for link parsing: conventional extensionless names are
files everywhere, and file: URLs are decoded by the caller like every
other destination, so no more option flags
- inline resolveAssetUrlResult into its one caller
- remove comments that explained the change instead of the code
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This adds a new iOS long-press workflow through native UIKit text-item menus and bridges actions into production message rendering. It also disables text drag-and-drop and changes gesture handling for the shared markdown text component, giving the change a broader interaction surface than a simple UI adjustment.

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

@juliusmarminge
juliusmarminge merged commit 9159b80 into mainSep 2, 2026
27 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/ios-file-chip-context-menu branch September 2, 2026 20:03
sheehanmunim added a commit to munimtechnologies/mtcode that referenced this pull request Sep 2, 2026
…continuation, provider editor redesign, context meter opt-in, linked-PR summaries)
Brings the fork up to origin/main e0da6c6..b57726c. Highlights: continue
active threads across server self-updates (pingdotgg#9167), provider editor and models
list redesign (pingdotgg#8508), opt-in context window meter (pingdotgg#9190), linked-PR summary
RPC with last-good caching (pingdotgg#9176), relay credential refresh before expiry
(pingdotgg#9178), composer draft preserved through worktree setup (pingdotgg#9197), project
skill discovery for Claude (pingdotgg#9210), copy-path on diff headers (pingdotgg#2403), mobile
long-press file references (pingdotgg#9258).
Conflict resolutions (14 files), keep-both unless noted:
- RpcAuthorization / contracts rpc / ws.ts: fork's PR rank, cherry-pick and
upstream-release RPCs sit beside upstream's new pullRequestsSummary.
- ws.ts route layer: fork's computer task/view brokers plus upstream's
serverSelfUpdate wrapped in withRunningThreadContinuation.
- PullRequestService: fork's upstream-project reads and scoped diff epochs
kept; upstream's summary read, refCacheKey and viewer-flight invalidation
added. invalidate() now scopes diff-only invalidation AND clears viewer
flights on a whole-workspace refresh. Test file keeps both new suites.
- client-runtime pullRequests: fork's PR stack atoms kept, TTL constant
takes upstream's new name.
- ChatMarkdown: upstream's one-character gutter rule replaces the fork's
equivalent digitWidth fix.
- ChatComposer: fork's voice-session button kept; context meter now honours
settings.contextWindowMeterEnabled.
- ConnectionsSettings: fork's EnvironmentLabelControl kept with upstream's
min-w-0 truncate.
- mobile ThreadFeed: upstream's markdownLinkHandlers plus the fork's
onCancelQueuedMessage.
- ClaudeDriver imports: fork's superset. build-desktop-artifact: distro and
arch both threaded through.
Integration: upstream's restart continuation and the fork's resume-on-restart
both ran at boot on the same orphans. The continuation-marker helpers move to
provider/serverUpdateContinuation.ts and SessionStartupReconciler now defers
any binding carrying the marker to upstream's pass (logged as deferred), so a
thread is never continued twice. Test added.
Dependencies reinstalled for the expo-sharing pin; the stale pnpm copy of
@t3tools/mobile-markdown-text was what broke the mobile typecheck.
Typecheck clean: contracts, shared, client-runtime, server, web, mobile
(desktop exits 1 on a pre-existing warning-level diagnostic). Startup and
self-update suites 36 passing; web sidebar/composer/settings suites 419
passing; fork guard script OK.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 2, 2026
## What's Changed
* fix(web): hide deleted providers with prototype keys by @Lucenx9 in pingdotgg/t3code#8337
* feat(mobile): long-press file references for path and open actions by @juliusmarminge in pingdotgg/t3code#9258
* fix(desktop): exclude opposite macOS pty prebuilds by @extoci in pingdotgg/t3code#9240
* feat(web): add copy path button to diff headers by @ipanasenko in pingdotgg/t3code#2403
* fix(server): subscribe before provider settings watcher by @t3-code[bot] in pingdotgg/t3code#9271
* fix(mobile): show filled filter icon on Android when filters are active by @none23 in pingdotgg/t3code#9217
* fix(chat): show single tool calls without summaries by @maria-rcks in pingdotgg/t3code#9267
* fix(web): warn when shared settings have no target environment by @imabdulazeez in pingdotgg/t3code#9207
* fix(web): confirm closing agent-controlled browsers by @maria-rcks in pingdotgg/t3code#9272
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1260...v0.0.39-nightly.20260902.1261
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260902.1261
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📱 Native ChangeChanges the native fingerprint; merging blocks production OTAs until a new store build ships.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

@juliusmarminge
, '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(mobile): long-press file references for path and open actions - #9258

Merged
juliusmarminge merged 4 commits into
mainfrom
t3code/ios-file-chip-context-menu
Sep 2, 2026
Merged

feat(mobile): long-press file references for path and open actions#9258
juliusmarminge merged 4 commits into
mainfrom
t3code/ios-file-chip-context-menu

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 2, 2026

Copy link
Copy Markdown
Member

What changed

  • File chips in iOS messages get a native long-press context menu with Copy full path, Copy relative path, and Open in file viewer, the same actions the web chip offers on right-click.
  • T3MarkdownTextRun accepts a JSON contextMenuConfig; T3MarkdownText maps runs that carry one to UITextItem links so iOS 17's text-item menu owns the gesture. Tap and text selection keep working; text drag is disabled on chat text.
  • SelectableMarkdownText gains fileContextMenu and onFileContextMenuAction. The feed builds the menu description and runs actions from one handler at the root, so rows subscribe to nothing extra per chip.

Why

Tapping a file reference opened it and that was the only thing you could do with it. Users copying a path had to open the file first. Web has had a chip context menu for a while; this brings the same set to iOS through UIKit rather than a JS action sheet.

Split out of #9253 so the media refactor there stays a refactor.

UI changes

Before: tap onlyAfter: native context menu
No long-press affordance on file chips.

Testing

  • fileChipMenu.test.ts (5 tests)
  • Mobile typecheck, lint, and format on changed files

Created with Claude Fable 5 using the Claude Code harness in T3 Code.

🤖 Generated with Claude Code


Note

Medium Risk
Touches native gesture handling and iOS 17 text-item menus on chat text; disabling text drag and suppressing recognizers on menu runs could affect edge-case interactions outside file chips.

Overview
Adds native long-press context menus on iOS for inline file references in chat markdown, matching web chip actions: copy full path, copy relative path, and open in the file viewer.

The t3-markdown-text native layer now accepts a JSON contextMenuConfig on text runs, builds a UIMenu, and wires T3MarkdownText (iOS 17 text-item delegates) so menu runs are exposed as links while custom tap/long-press is skipped on those runs. Chat UITextView also disables text drag. SelectableMarkdownText gains fileContextMenu / onFileContextMenuAction plus a React context so file chips in the tree share one handler.

ThreadFeed centralizes tap and menu behavior in markdownLinkHandlers via new fileChipMenu / resolveFileChipTarget. resolveWorkspaceRelativeFilePath now rejects paths that escape the workspace with .. (e.g. /repo/../outside.txt). User docs note iOS touch-and-hold on file references.

Reviewed by Cursor Bugbot for commit 67b0275. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add long-press context menu for file references in mobile markdown text

  • Native iOS T3MarkdownText and T3MarkdownTextRun build context menus from a serialized JSON config and emit a native event when an action is selected. Custom tap/long-press recognizers are suppressed on runs with a menu, while other runs keep existing gesture handling.
  • React Native wiring in SelectableMarkdownText.ios.tsx and ThreadFeed.tsx resolves file-chip hrefs to menus and dispatches copy-path and open-file actions.
  • fileChipMenu.ts classifies file links into full and relative paths and builds a menu with only the supported copy actions plus the file-viewer open action.
  • filePath.ts now rejects absolute targets that escape the workspace via a .. segment instead of normalizing them.
  • Risk: disabling drag interaction on T3MarkdownText means text drag-and-drop no longer works on iOS; handleLongPressIfNecessary now fires once at the began state rather than across all long-press transitions.

Macroscope summarized 67b0275.

File chips in iOS messages had no way to copy a path; tapping opened the
file and that was it. The native text view now maps chips with a menu to
UITextItem links, so iOS 17 shows a context menu on long-press while
tapping and text selection keep working. The menu offers copy full path,
copy relative path, and open in file viewer, matching the web chip.
The menu is described per chip as JSON and actions run from one handler
at the feed root, so rows subscribe to nothing extra per chip.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). 📱 Native Change Changes the native fingerprint; merging blocks production OTAs until a new store build ships. labels Sep 2, 2026
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx
Julius Marmingeand others added 2 commits September 2, 2026 12:44
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…links
Chips inside blockquotes, lists, and tables never received the menu
handlers because NativeMarkdownBlock only forwarded onLinkPress. The
handlers now travel through a context the same way the image renderer
does. Links the feed cannot open, such as ~/x or ../x, get no menu
instead of one whose only action does nothing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment threadapps/mobile/src/features/threads/fileChipMenu.ts
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.2 KiB13.1 KiB−88 B (−0.6%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB+4 B (+0.1%)7.3 KiB
CodexLive turn WebSocket wire6.3 KiB6.2 KiB−92 B (−1.4%)7.8 KiB
CodexLive turn WebSocket decoded55.5 KiB54.7 KiB−822 B (−1.4%)66.4 KiB
CodexLive turn messages880 (0.0%)21
ClaudeTotal thread wire13.3 KiB13.3 KiB+12 B (+0.1%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−3 B (−0.0%)7.3 KiB
ClaudeLive turn WebSocket wire6.4 KiB6.4 KiB+15 B (+0.2%)7.8 KiB
ClaudeLive turn WebSocket decoded56.4 KiB56.4 KiB0 B (0.0%)66.4 KiB
ClaudeLive turn messages10100 (0.0%)21

Baseline: aab4049 · PR result: 67b0275 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 109.4 KiB
  • Claude decoded thread snapshot: 110.1 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

…t is not relative
resolveWorkspaceRelativeFilePath matched /repo/../outside.txt as inside
/repo because the prefix check ran before traversal was normalized.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
juliusmarminge pushed a commit that referenced this pull request Sep 2, 2026
Split the iOS file-chip context menu into its own PR (#9258) and cut the
parts of this one that were not unification:
- restore the work-log presentation tests that b1daa51 deleted; they
cover tool naming and browser summaries this PR never touched
- drop availableMediaActions; both clients kept a full label and runner
table anyway, so only the MediaActionId union is shared
- one behavior for link parsing: conventional extensionless names are
files everywhere, and file: URLs are decoded by the caller like every
other destination, so no more option flags
- inline resolveAssetUrlResult into its one caller
- remove comments that explained the change instead of the code
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This adds a new iOS long-press workflow through native UIKit text-item menus and bridges actions into production message rendering. It also disables text drag-and-drop and changes gesture handling for the shared markdown text component, giving the change a broader interaction surface than a simple UI adjustment.

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

@juliusmarminge
juliusmarminge merged commit 9159b80 into mainSep 2, 2026
27 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/ios-file-chip-context-menu branch September 2, 2026 20:03
sheehanmunim added a commit to munimtechnologies/mtcode that referenced this pull request Sep 2, 2026
…continuation, provider editor redesign, context meter opt-in, linked-PR summaries)
Brings the fork up to origin/main e0da6c6..b57726c. Highlights: continue
active threads across server self-updates (pingdotgg#9167), provider editor and models
list redesign (pingdotgg#8508), opt-in context window meter (pingdotgg#9190), linked-PR summary
RPC with last-good caching (pingdotgg#9176), relay credential refresh before expiry
(pingdotgg#9178), composer draft preserved through worktree setup (pingdotgg#9197), project
skill discovery for Claude (pingdotgg#9210), copy-path on diff headers (pingdotgg#2403), mobile
long-press file references (pingdotgg#9258).
Conflict resolutions (14 files), keep-both unless noted:
- RpcAuthorization / contracts rpc / ws.ts: fork's PR rank, cherry-pick and
upstream-release RPCs sit beside upstream's new pullRequestsSummary.
- ws.ts route layer: fork's computer task/view brokers plus upstream's
serverSelfUpdate wrapped in withRunningThreadContinuation.
- PullRequestService: fork's upstream-project reads and scoped diff epochs
kept; upstream's summary read, refCacheKey and viewer-flight invalidation
added. invalidate() now scopes diff-only invalidation AND clears viewer
flights on a whole-workspace refresh. Test file keeps both new suites.
- client-runtime pullRequests: fork's PR stack atoms kept, TTL constant
takes upstream's new name.
- ChatMarkdown: upstream's one-character gutter rule replaces the fork's
equivalent digitWidth fix.
- ChatComposer: fork's voice-session button kept; context meter now honours
settings.contextWindowMeterEnabled.
- ConnectionsSettings: fork's EnvironmentLabelControl kept with upstream's
min-w-0 truncate.
- mobile ThreadFeed: upstream's markdownLinkHandlers plus the fork's
onCancelQueuedMessage.
- ClaudeDriver imports: fork's superset. build-desktop-artifact: distro and
arch both threaded through.
Integration: upstream's restart continuation and the fork's resume-on-restart
both ran at boot on the same orphans. The continuation-marker helpers move to
provider/serverUpdateContinuation.ts and SessionStartupReconciler now defers
any binding carrying the marker to upstream's pass (logged as deferred), so a
thread is never continued twice. Test added.
Dependencies reinstalled for the expo-sharing pin; the stale pnpm copy of
@t3tools/mobile-markdown-text was what broke the mobile typecheck.
Typecheck clean: contracts, shared, client-runtime, server, web, mobile
(desktop exits 1 on a pre-existing warning-level diagnostic). Startup and
self-update suites 36 passing; web sidebar/composer/settings suites 419
passing; fork guard script OK.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 2, 2026
## What's Changed
* fix(web): hide deleted providers with prototype keys by @Lucenx9 in pingdotgg/t3code#8337
* feat(mobile): long-press file references for path and open actions by @juliusmarminge in pingdotgg/t3code#9258
* fix(desktop): exclude opposite macOS pty prebuilds by @extoci in pingdotgg/t3code#9240
* feat(web): add copy path button to diff headers by @ipanasenko in pingdotgg/t3code#2403
* fix(server): subscribe before provider settings watcher by @t3-code[bot] in pingdotgg/t3code#9271
* fix(mobile): show filled filter icon on Android when filters are active by @none23 in pingdotgg/t3code#9217
* fix(chat): show single tool calls without summaries by @maria-rcks in pingdotgg/t3code#9267
* fix(web): warn when shared settings have no target environment by @imabdulazeez in pingdotgg/t3code#9207
* fix(web): confirm closing agent-controlled browsers by @maria-rcks in pingdotgg/t3code#9272
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1260...v0.0.39-nightly.20260902.1261
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260902.1261
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📱 Native ChangeChanges the native fingerprint; merging blocks production OTAs until a new store build ships.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

@juliusmarminge
, '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(mobile): long-press file references for path and open actions - #9258

Merged
juliusmarminge merged 4 commits into
mainfrom
t3code/ios-file-chip-context-menu
Sep 2, 2026
Merged

feat(mobile): long-press file references for path and open actions#9258
juliusmarminge merged 4 commits into
mainfrom
t3code/ios-file-chip-context-menu

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 2, 2026

Copy link
Copy Markdown
Member

What changed

  • File chips in iOS messages get a native long-press context menu with Copy full path, Copy relative path, and Open in file viewer, the same actions the web chip offers on right-click.
  • T3MarkdownTextRun accepts a JSON contextMenuConfig; T3MarkdownText maps runs that carry one to UITextItem links so iOS 17's text-item menu owns the gesture. Tap and text selection keep working; text drag is disabled on chat text.
  • SelectableMarkdownText gains fileContextMenu and onFileContextMenuAction. The feed builds the menu description and runs actions from one handler at the root, so rows subscribe to nothing extra per chip.

Why

Tapping a file reference opened it and that was the only thing you could do with it. Users copying a path had to open the file first. Web has had a chip context menu for a while; this brings the same set to iOS through UIKit rather than a JS action sheet.

Split out of #9253 so the media refactor there stays a refactor.

UI changes

Before: tap onlyAfter: native context menu
No long-press affordance on file chips.

Testing

  • fileChipMenu.test.ts (5 tests)
  • Mobile typecheck, lint, and format on changed files

Created with Claude Fable 5 using the Claude Code harness in T3 Code.

🤖 Generated with Claude Code


Note

Medium Risk
Touches native gesture handling and iOS 17 text-item menus on chat text; disabling text drag and suppressing recognizers on menu runs could affect edge-case interactions outside file chips.

Overview
Adds native long-press context menus on iOS for inline file references in chat markdown, matching web chip actions: copy full path, copy relative path, and open in the file viewer.

The t3-markdown-text native layer now accepts a JSON contextMenuConfig on text runs, builds a UIMenu, and wires T3MarkdownText (iOS 17 text-item delegates) so menu runs are exposed as links while custom tap/long-press is skipped on those runs. Chat UITextView also disables text drag. SelectableMarkdownText gains fileContextMenu / onFileContextMenuAction plus a React context so file chips in the tree share one handler.

ThreadFeed centralizes tap and menu behavior in markdownLinkHandlers via new fileChipMenu / resolveFileChipTarget. resolveWorkspaceRelativeFilePath now rejects paths that escape the workspace with .. (e.g. /repo/../outside.txt). User docs note iOS touch-and-hold on file references.

Reviewed by Cursor Bugbot for commit 67b0275. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add long-press context menu for file references in mobile markdown text

  • Native iOS T3MarkdownText and T3MarkdownTextRun build context menus from a serialized JSON config and emit a native event when an action is selected. Custom tap/long-press recognizers are suppressed on runs with a menu, while other runs keep existing gesture handling.
  • React Native wiring in SelectableMarkdownText.ios.tsx and ThreadFeed.tsx resolves file-chip hrefs to menus and dispatches copy-path and open-file actions.
  • fileChipMenu.ts classifies file links into full and relative paths and builds a menu with only the supported copy actions plus the file-viewer open action.
  • filePath.ts now rejects absolute targets that escape the workspace via a .. segment instead of normalizing them.
  • Risk: disabling drag interaction on T3MarkdownText means text drag-and-drop no longer works on iOS; handleLongPressIfNecessary now fires once at the began state rather than across all long-press transitions.

Macroscope summarized 67b0275.

File chips in iOS messages had no way to copy a path; tapping opened the
file and that was it. The native text view now maps chips with a menu to
UITextItem links, so iOS 17 shows a context menu on long-press while
tapping and text selection keep working. The menu offers copy full path,
copy relative path, and open in file viewer, matching the web chip.
The menu is described per chip as JSON and actions run from one handler
at the feed root, so rows subscribe to nothing extra per chip.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). 📱 Native Change Changes the native fingerprint; merging blocks production OTAs until a new store build ships. labels Sep 2, 2026
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx
Julius Marmingeand others added 2 commits September 2, 2026 12:44
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…links
Chips inside blockquotes, lists, and tables never received the menu
handlers because NativeMarkdownBlock only forwarded onLinkPress. The
handlers now travel through a context the same way the image renderer
does. Links the feed cannot open, such as ~/x or ../x, get no menu
instead of one whose only action does nothing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment threadapps/mobile/src/features/threads/fileChipMenu.ts
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.2 KiB13.1 KiB−88 B (−0.6%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB+4 B (+0.1%)7.3 KiB
CodexLive turn WebSocket wire6.3 KiB6.2 KiB−92 B (−1.4%)7.8 KiB
CodexLive turn WebSocket decoded55.5 KiB54.7 KiB−822 B (−1.4%)66.4 KiB
CodexLive turn messages880 (0.0%)21
ClaudeTotal thread wire13.3 KiB13.3 KiB+12 B (+0.1%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−3 B (−0.0%)7.3 KiB
ClaudeLive turn WebSocket wire6.4 KiB6.4 KiB+15 B (+0.2%)7.8 KiB
ClaudeLive turn WebSocket decoded56.4 KiB56.4 KiB0 B (0.0%)66.4 KiB
ClaudeLive turn messages10100 (0.0%)21

Baseline: aab4049 · PR result: 67b0275 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 109.4 KiB
  • Claude decoded thread snapshot: 110.1 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

…t is not relative
resolveWorkspaceRelativeFilePath matched /repo/../outside.txt as inside
/repo because the prefix check ran before traversal was normalized.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
juliusmarminge pushed a commit that referenced this pull request Sep 2, 2026
Split the iOS file-chip context menu into its own PR (#9258) and cut the
parts of this one that were not unification:
- restore the work-log presentation tests that b1daa51 deleted; they
cover tool naming and browser summaries this PR never touched
- drop availableMediaActions; both clients kept a full label and runner
table anyway, so only the MediaActionId union is shared
- one behavior for link parsing: conventional extensionless names are
files everywhere, and file: URLs are decoded by the caller like every
other destination, so no more option flags
- inline resolveAssetUrlResult into its one caller
- remove comments that explained the change instead of the code
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This adds a new iOS long-press workflow through native UIKit text-item menus and bridges actions into production message rendering. It also disables text drag-and-drop and changes gesture handling for the shared markdown text component, giving the change a broader interaction surface than a simple UI adjustment.

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

@juliusmarminge
juliusmarminge merged commit 9159b80 into mainSep 2, 2026
27 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/ios-file-chip-context-menu branch September 2, 2026 20:03
sheehanmunim added a commit to munimtechnologies/mtcode that referenced this pull request Sep 2, 2026
…continuation, provider editor redesign, context meter opt-in, linked-PR summaries)
Brings the fork up to origin/main e0da6c6..b57726c. Highlights: continue
active threads across server self-updates (pingdotgg#9167), provider editor and models
list redesign (pingdotgg#8508), opt-in context window meter (pingdotgg#9190), linked-PR summary
RPC with last-good caching (pingdotgg#9176), relay credential refresh before expiry
(pingdotgg#9178), composer draft preserved through worktree setup (pingdotgg#9197), project
skill discovery for Claude (pingdotgg#9210), copy-path on diff headers (pingdotgg#2403), mobile
long-press file references (pingdotgg#9258).
Conflict resolutions (14 files), keep-both unless noted:
- RpcAuthorization / contracts rpc / ws.ts: fork's PR rank, cherry-pick and
upstream-release RPCs sit beside upstream's new pullRequestsSummary.
- ws.ts route layer: fork's computer task/view brokers plus upstream's
serverSelfUpdate wrapped in withRunningThreadContinuation.
- PullRequestService: fork's upstream-project reads and scoped diff epochs
kept; upstream's summary read, refCacheKey and viewer-flight invalidation
added. invalidate() now scopes diff-only invalidation AND clears viewer
flights on a whole-workspace refresh. Test file keeps both new suites.
- client-runtime pullRequests: fork's PR stack atoms kept, TTL constant
takes upstream's new name.
- ChatMarkdown: upstream's one-character gutter rule replaces the fork's
equivalent digitWidth fix.
- ChatComposer: fork's voice-session button kept; context meter now honours
settings.contextWindowMeterEnabled.
- ConnectionsSettings: fork's EnvironmentLabelControl kept with upstream's
min-w-0 truncate.
- mobile ThreadFeed: upstream's markdownLinkHandlers plus the fork's
onCancelQueuedMessage.
- ClaudeDriver imports: fork's superset. build-desktop-artifact: distro and
arch both threaded through.
Integration: upstream's restart continuation and the fork's resume-on-restart
both ran at boot on the same orphans. The continuation-marker helpers move to
provider/serverUpdateContinuation.ts and SessionStartupReconciler now defers
any binding carrying the marker to upstream's pass (logged as deferred), so a
thread is never continued twice. Test added.
Dependencies reinstalled for the expo-sharing pin; the stale pnpm copy of
@t3tools/mobile-markdown-text was what broke the mobile typecheck.
Typecheck clean: contracts, shared, client-runtime, server, web, mobile
(desktop exits 1 on a pre-existing warning-level diagnostic). Startup and
self-update suites 36 passing; web sidebar/composer/settings suites 419
passing; fork guard script OK.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 2, 2026
## What's Changed
* fix(web): hide deleted providers with prototype keys by @Lucenx9 in pingdotgg/t3code#8337
* feat(mobile): long-press file references for path and open actions by @juliusmarminge in pingdotgg/t3code#9258
* fix(desktop): exclude opposite macOS pty prebuilds by @extoci in pingdotgg/t3code#9240
* feat(web): add copy path button to diff headers by @ipanasenko in pingdotgg/t3code#2403
* fix(server): subscribe before provider settings watcher by @t3-code[bot] in pingdotgg/t3code#9271
* fix(mobile): show filled filter icon on Android when filters are active by @none23 in pingdotgg/t3code#9217
* fix(chat): show single tool calls without summaries by @maria-rcks in pingdotgg/t3code#9267
* fix(web): warn when shared settings have no target environment by @imabdulazeez in pingdotgg/t3code#9207
* fix(web): confirm closing agent-controlled browsers by @maria-rcks in pingdotgg/t3code#9272
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1260...v0.0.39-nightly.20260902.1261
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260902.1261
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📱 Native ChangeChanges the native fingerprint; merging blocks production OTAs until a new store build ships.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

@juliusmarminge
, '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(mobile): long-press file references for path and open actions - #9258

Merged
juliusmarminge merged 4 commits into
mainfrom
t3code/ios-file-chip-context-menu
Sep 2, 2026
Merged

feat(mobile): long-press file references for path and open actions#9258
juliusmarminge merged 4 commits into
mainfrom
t3code/ios-file-chip-context-menu

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 2, 2026

Copy link
Copy Markdown
Member

What changed

  • File chips in iOS messages get a native long-press context menu with Copy full path, Copy relative path, and Open in file viewer, the same actions the web chip offers on right-click.
  • T3MarkdownTextRun accepts a JSON contextMenuConfig; T3MarkdownText maps runs that carry one to UITextItem links so iOS 17's text-item menu owns the gesture. Tap and text selection keep working; text drag is disabled on chat text.
  • SelectableMarkdownText gains fileContextMenu and onFileContextMenuAction. The feed builds the menu description and runs actions from one handler at the root, so rows subscribe to nothing extra per chip.

Why

Tapping a file reference opened it and that was the only thing you could do with it. Users copying a path had to open the file first. Web has had a chip context menu for a while; this brings the same set to iOS through UIKit rather than a JS action sheet.

Split out of #9253 so the media refactor there stays a refactor.

UI changes

Before: tap onlyAfter: native context menu
No long-press affordance on file chips.

Testing

  • fileChipMenu.test.ts (5 tests)
  • Mobile typecheck, lint, and format on changed files

Created with Claude Fable 5 using the Claude Code harness in T3 Code.

🤖 Generated with Claude Code


Note

Medium Risk
Touches native gesture handling and iOS 17 text-item menus on chat text; disabling text drag and suppressing recognizers on menu runs could affect edge-case interactions outside file chips.

Overview
Adds native long-press context menus on iOS for inline file references in chat markdown, matching web chip actions: copy full path, copy relative path, and open in the file viewer.

The t3-markdown-text native layer now accepts a JSON contextMenuConfig on text runs, builds a UIMenu, and wires T3MarkdownText (iOS 17 text-item delegates) so menu runs are exposed as links while custom tap/long-press is skipped on those runs. Chat UITextView also disables text drag. SelectableMarkdownText gains fileContextMenu / onFileContextMenuAction plus a React context so file chips in the tree share one handler.

ThreadFeed centralizes tap and menu behavior in markdownLinkHandlers via new fileChipMenu / resolveFileChipTarget. resolveWorkspaceRelativeFilePath now rejects paths that escape the workspace with .. (e.g. /repo/../outside.txt). User docs note iOS touch-and-hold on file references.

Reviewed by Cursor Bugbot for commit 67b0275. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add long-press context menu for file references in mobile markdown text

  • Native iOS T3MarkdownText and T3MarkdownTextRun build context menus from a serialized JSON config and emit a native event when an action is selected. Custom tap/long-press recognizers are suppressed on runs with a menu, while other runs keep existing gesture handling.
  • React Native wiring in SelectableMarkdownText.ios.tsx and ThreadFeed.tsx resolves file-chip hrefs to menus and dispatches copy-path and open-file actions.
  • fileChipMenu.ts classifies file links into full and relative paths and builds a menu with only the supported copy actions plus the file-viewer open action.
  • filePath.ts now rejects absolute targets that escape the workspace via a .. segment instead of normalizing them.
  • Risk: disabling drag interaction on T3MarkdownText means text drag-and-drop no longer works on iOS; handleLongPressIfNecessary now fires once at the began state rather than across all long-press transitions.

Macroscope summarized 67b0275.

File chips in iOS messages had no way to copy a path; tapping opened the
file and that was it. The native text view now maps chips with a menu to
UITextItem links, so iOS 17 shows a context menu on long-press while
tapping and text selection keep working. The menu offers copy full path,
copy relative path, and open in file viewer, matching the web chip.
The menu is described per chip as JSON and actions run from one handler
at the feed root, so rows subscribe to nothing extra per chip.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). 📱 Native Change Changes the native fingerprint; merging blocks production OTAs until a new store build ships. labels Sep 2, 2026
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx
Julius Marmingeand others added 2 commits September 2, 2026 12:44
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…links
Chips inside blockquotes, lists, and tables never received the menu
handlers because NativeMarkdownBlock only forwarded onLinkPress. The
handlers now travel through a context the same way the image renderer
does. Links the feed cannot open, such as ~/x or ../x, get no menu
instead of one whose only action does nothing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment threadapps/mobile/src/features/threads/fileChipMenu.ts
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.2 KiB13.1 KiB−88 B (−0.6%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB+4 B (+0.1%)7.3 KiB
CodexLive turn WebSocket wire6.3 KiB6.2 KiB−92 B (−1.4%)7.8 KiB
CodexLive turn WebSocket decoded55.5 KiB54.7 KiB−822 B (−1.4%)66.4 KiB
CodexLive turn messages880 (0.0%)21
ClaudeTotal thread wire13.3 KiB13.3 KiB+12 B (+0.1%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−3 B (−0.0%)7.3 KiB
ClaudeLive turn WebSocket wire6.4 KiB6.4 KiB+15 B (+0.2%)7.8 KiB
ClaudeLive turn WebSocket decoded56.4 KiB56.4 KiB0 B (0.0%)66.4 KiB
ClaudeLive turn messages10100 (0.0%)21

Baseline: aab4049 · PR result: 67b0275 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 109.4 KiB
  • Claude decoded thread snapshot: 110.1 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

…t is not relative
resolveWorkspaceRelativeFilePath matched /repo/../outside.txt as inside
/repo because the prefix check ran before traversal was normalized.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
juliusmarminge pushed a commit that referenced this pull request Sep 2, 2026
Split the iOS file-chip context menu into its own PR (#9258) and cut the
parts of this one that were not unification:
- restore the work-log presentation tests that b1daa51 deleted; they
cover tool naming and browser summaries this PR never touched
- drop availableMediaActions; both clients kept a full label and runner
table anyway, so only the MediaActionId union is shared
- one behavior for link parsing: conventional extensionless names are
files everywhere, and file: URLs are decoded by the caller like every
other destination, so no more option flags
- inline resolveAssetUrlResult into its one caller
- remove comments that explained the change instead of the code
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This adds a new iOS long-press workflow through native UIKit text-item menus and bridges actions into production message rendering. It also disables text drag-and-drop and changes gesture handling for the shared markdown text component, giving the change a broader interaction surface than a simple UI adjustment.

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

@juliusmarminge
juliusmarminge merged commit 9159b80 into mainSep 2, 2026
27 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/ios-file-chip-context-menu branch September 2, 2026 20:03
sheehanmunim added a commit to munimtechnologies/mtcode that referenced this pull request Sep 2, 2026
…continuation, provider editor redesign, context meter opt-in, linked-PR summaries)
Brings the fork up to origin/main e0da6c6..b57726c. Highlights: continue
active threads across server self-updates (pingdotgg#9167), provider editor and models
list redesign (pingdotgg#8508), opt-in context window meter (pingdotgg#9190), linked-PR summary
RPC with last-good caching (pingdotgg#9176), relay credential refresh before expiry
(pingdotgg#9178), composer draft preserved through worktree setup (pingdotgg#9197), project
skill discovery for Claude (pingdotgg#9210), copy-path on diff headers (pingdotgg#2403), mobile
long-press file references (pingdotgg#9258).
Conflict resolutions (14 files), keep-both unless noted:
- RpcAuthorization / contracts rpc / ws.ts: fork's PR rank, cherry-pick and
upstream-release RPCs sit beside upstream's new pullRequestsSummary.
- ws.ts route layer: fork's computer task/view brokers plus upstream's
serverSelfUpdate wrapped in withRunningThreadContinuation.
- PullRequestService: fork's upstream-project reads and scoped diff epochs
kept; upstream's summary read, refCacheKey and viewer-flight invalidation
added. invalidate() now scopes diff-only invalidation AND clears viewer
flights on a whole-workspace refresh. Test file keeps both new suites.
- client-runtime pullRequests: fork's PR stack atoms kept, TTL constant
takes upstream's new name.
- ChatMarkdown: upstream's one-character gutter rule replaces the fork's
equivalent digitWidth fix.
- ChatComposer: fork's voice-session button kept; context meter now honours
settings.contextWindowMeterEnabled.
- ConnectionsSettings: fork's EnvironmentLabelControl kept with upstream's
min-w-0 truncate.
- mobile ThreadFeed: upstream's markdownLinkHandlers plus the fork's
onCancelQueuedMessage.
- ClaudeDriver imports: fork's superset. build-desktop-artifact: distro and
arch both threaded through.
Integration: upstream's restart continuation and the fork's resume-on-restart
both ran at boot on the same orphans. The continuation-marker helpers move to
provider/serverUpdateContinuation.ts and SessionStartupReconciler now defers
any binding carrying the marker to upstream's pass (logged as deferred), so a
thread is never continued twice. Test added.
Dependencies reinstalled for the expo-sharing pin; the stale pnpm copy of
@t3tools/mobile-markdown-text was what broke the mobile typecheck.
Typecheck clean: contracts, shared, client-runtime, server, web, mobile
(desktop exits 1 on a pre-existing warning-level diagnostic). Startup and
self-update suites 36 passing; web sidebar/composer/settings suites 419
passing; fork guard script OK.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 2, 2026
## What's Changed
* fix(web): hide deleted providers with prototype keys by @Lucenx9 in pingdotgg/t3code#8337
* feat(mobile): long-press file references for path and open actions by @juliusmarminge in pingdotgg/t3code#9258
* fix(desktop): exclude opposite macOS pty prebuilds by @extoci in pingdotgg/t3code#9240
* feat(web): add copy path button to diff headers by @ipanasenko in pingdotgg/t3code#2403
* fix(server): subscribe before provider settings watcher by @t3-code[bot] in pingdotgg/t3code#9271
* fix(mobile): show filled filter icon on Android when filters are active by @none23 in pingdotgg/t3code#9217
* fix(chat): show single tool calls without summaries by @maria-rcks in pingdotgg/t3code#9267
* fix(web): warn when shared settings have no target environment by @imabdulazeez in pingdotgg/t3code#9207
* fix(web): confirm closing agent-controlled browsers by @maria-rcks in pingdotgg/t3code#9272
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1260...v0.0.39-nightly.20260902.1261
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260902.1261
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📱 Native ChangeChanges the native fingerprint; merging blocks production OTAs until a new store build ships.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

@juliusmarminge