fix(mobile): show an error instead of an endless preview spinner - #9123

Merged
t3dotgg merged 2 commits into
mainfrom
fix/mobile-asset-preview-error-state
Sep 3, 2026
Merged

fix(mobile): show an error instead of an endless preview spinner#9123
t3dotgg merged 2 commits into
mainfrom
fix/mobile-asset-preview-error-state

Conversation

@t3dotgg

@t3dotggt3dotgg commented Sep 1, 2026

Copy link
Copy Markdown
Member

On mobile, an image, PDF, or other asset preview kept spinning on "Preparing ... preview" when the environment was disconnected or the asset URL request failed. useAssetUrlState reported a pending query and a dead connection as the same Loading value, so previews had no terminal state and no way to retry.

The hook now folds the environment connection phase into its result. A query that is still pending while the environment is offline, retrying, or in error resolves to Failure with a disconnected reason. A failed query resolves to Failure with a failed reason. The derivation moved into apps/mobile/src/state/asset-url-state.ts so it can be tested on its own.

On the file screen, image, SVG, browser, and video previews now render an error instead of a spinner when the asset URL will not arrive. A disconnected environment gets the existing EnvironmentConnectionNotice, the same offline surface the terminal and review sheets use, with its "Retry now" button wired to a connection retry plus a fresh URL mint. A failed URL gets a short "Preview unavailable" line with "Try again". The video preview lost its own unavailable branch because the screen now owns that state for every preview type.

The other consumers, the full screen file preview modal, both video preview modals, and the chat feed image and video, already branched on Failure, so they pick up the disconnected case without changes. The web app has its own copy of the hook in apps/web/src/assets/assetUrls.ts and is left alone to keep this to one concern.

Verified with vp test run apps/mobile/src/state/asset-url-state.test.ts, tsc --noEmit in apps/mobile, and lint on the touched files. Not verified on a device or simulator.

Closes#7630

Built by Claude Opus 5 in the Claude Code harness.


Note

Low Risk
Scoped to mobile asset URL presentation and thread file previews; logic is covered by unit tests and does not change auth or shared web hooks.

Overview
Mobile workspace file previews (image, browser/PDF, video) no longer spin forever when a signed asset URL cannot be obtained. useAssetUrlState now combines the shared URL query result with the environment connection phase via deriveAssetUrlState, yielding Failure with disconnected vs failed so UI can distinguish outage from a bad/missing asset.

On ThreadFileScreen, FileContent shows the new WorkspaceFilePreviewError for previews that depend on an asset URL: EnvironmentConnectionNotice (with connection retry + URL refresh) when disconnected, or a “Preview unavailable” empty state with Try again otherwise. Retry refreshes the asset URL and bumps previewRevision to remount the preview. WorkspaceFileVideoPreview drops its local unavailable UI because the parent owns that terminal state.

Unit tests cover deriveAssetUrlState; other mobile useAssetUrlState consumers that already treat Failure as an error surface should pick up disconnected cases without further changes.

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

Note

Fix endless mobile preview spinner by showing reason-specific asset URL errors

  • Adds WorkspaceFilePreviewError in WorkspaceFilePreviewError.tsx to render an environment connection notice for disconnected failures and a generic "Preview unavailable" retry state for other asset URL failures.
  • Adds deriveAssetUrlState in asset-url-state.ts to distinguish disconnected environment failures from connected query failures, and suppresses resolved URLs while the environment is offline, reconnecting, or in error.
  • Updates useAssetUrlState and useConnectionPhase in assets.ts to fold the shared asset result with the current connection phase, and treats a waiting post-outage query as Loading so stale failures are not shown.
  • Wires the typed failure reason and a retry handler (which remints the asset URL and remounts the preview) through FileContent and ThreadFileScreen in ThreadFilesRouteScreen.tsx, removing the old boolean previewUnavailable prop.
  • Risk: WorkspaceFileVideoPreview no longer renders its own "Video unavailable" state; video asset failures must now be handled by the parent before the media player renders. Existing consumers relying on the unavailable prop or the boolean previewUnavailable prop need updating.

Macroscope summarized 9cd4887.

@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). labels Sep 1, 2026
Comment threadapps/mobile/src/features/files/ThreadFilesRouteScreen.tsx Outdated
@github-actions

github-actionsBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.8 KiB13.6 KiB−189 B (−1.3%)15.1 KiB
CodexThread snapshot wire7.0 KiB7.0 KiB−14 B (−0.2%)7.3 KiB
CodexLive turn WebSocket wire6.7 KiB6.5 KiB−175 B (−2.5%)7.8 KiB
CodexLive turn WebSocket decoded58.5 KiB57.1 KiB−1.4 KiB (−2.4%)66.4 KiB
CodexLive turn messages10100 (0.0%)21
ClaudeTotal thread wire13.6 KiB13.5 KiB−28 B (−0.2%)15.1 KiB
ClaudeThread snapshot wire7.0 KiB7.0 KiB−7 B (−0.1%)7.3 KiB
ClaudeLive turn WebSocket wire6.5 KiB6.5 KiB−21 B (−0.3%)7.8 KiB
ClaudeLive turn WebSocket decoded57.8 KiB57.8 KiB−44 B (−0.1%)66.4 KiB
ClaudeLive turn messages98−1 (−11.1%)21

Baseline: 18573d6 · PR result: 9cd4887 · 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: 113.8 KiB
  • Claude decoded thread snapshot: 114.5 KiB

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

Comment threadapps/mobile/src/state/asset-url-state.ts
@macroscopeapp

macroscopeappBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 9cd4887

Macroscope's review found this PR approvable — This is a focused mobile bug fix that replaces terminal preview spinners with actionable connection or asset-unavailable states, including retry support. The shared hook’s broader impact is limited to mobile asset previews and is covered by explicit state-derivation tests, with no schema, deployment, security, billing, or static-analysis changes.

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

@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 6a78e63 to dc35525CompareSeptember 2, 2026 00:04
Comment threadapps/mobile/src/state/asset-url-state.ts
Comment threadapps/mobile/src/state/asset-url-state.ts Outdated
macroscopeapp[bot]
macroscopeappBot previously approved these changes Sep 2, 2026
@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 4b38589 to 13efe5bCompareSeptember 3, 2026 09:26
@macroscopeapp
macroscopeappBot dismissed their stale reviewSeptember 3, 2026 09:26

Dismissing prior approval to re-evaluate 13efe5b

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit 13efe5b. Configure here.

Comment threadapps/mobile/src/state/asset-url-state.ts
t3dotggand others added 2 commits September 3, 2026 02:33
When the connection returns, the URL query re-runs while its last
result is still the outage failure. That in-flight failure read as a
missing file. It now reads as loading until the re-query settles.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 13efe5b to 9cd4887CompareSeptember 3, 2026 09:33
@t3dotgg
t3dotgg merged commit 2aa907b into mainSep 3, 2026
27 checks passed
@t3dotgg
t3dotgg deleted the fix/mobile-asset-preview-error-state branch September 3, 2026 09:41
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 3, 2026
## What's Changed
* fix(mobile): show an error instead of an endless preview spinner by @t3dotgg in pingdotgg/t3code#9123
* fix(usage): price new models without waiting a day for the rate table by @t3dotgg in pingdotgg/t3code#9202
* fix(web): unlock the composer when preview capture fails by @t3dotgg in pingdotgg/t3code#9127
* fix(antigravity): refresh the model manifest so older Gemini models fold as legacy by @t3dotgg in pingdotgg/t3code#9397
* perf(ci): reuse dependency checks in release builds by @t3dotgg in pingdotgg/t3code#9399
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260903.1268...v0.0.39-nightly.20260903.1270
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260903.1270
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Mobile asset previews spin forever when asset URL creation or environment connection fails

1 participant

@t3dotgg
, '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

fix(mobile): show an error instead of an endless preview spinner - #9123

Merged
t3dotgg merged 2 commits into
mainfrom
fix/mobile-asset-preview-error-state
Sep 3, 2026
Merged

fix(mobile): show an error instead of an endless preview spinner#9123
t3dotgg merged 2 commits into
mainfrom
fix/mobile-asset-preview-error-state

Conversation

@t3dotgg

@t3dotggt3dotgg commented Sep 1, 2026

Copy link
Copy Markdown
Member

On mobile, an image, PDF, or other asset preview kept spinning on "Preparing ... preview" when the environment was disconnected or the asset URL request failed. useAssetUrlState reported a pending query and a dead connection as the same Loading value, so previews had no terminal state and no way to retry.

The hook now folds the environment connection phase into its result. A query that is still pending while the environment is offline, retrying, or in error resolves to Failure with a disconnected reason. A failed query resolves to Failure with a failed reason. The derivation moved into apps/mobile/src/state/asset-url-state.ts so it can be tested on its own.

On the file screen, image, SVG, browser, and video previews now render an error instead of a spinner when the asset URL will not arrive. A disconnected environment gets the existing EnvironmentConnectionNotice, the same offline surface the terminal and review sheets use, with its "Retry now" button wired to a connection retry plus a fresh URL mint. A failed URL gets a short "Preview unavailable" line with "Try again". The video preview lost its own unavailable branch because the screen now owns that state for every preview type.

The other consumers, the full screen file preview modal, both video preview modals, and the chat feed image and video, already branched on Failure, so they pick up the disconnected case without changes. The web app has its own copy of the hook in apps/web/src/assets/assetUrls.ts and is left alone to keep this to one concern.

Verified with vp test run apps/mobile/src/state/asset-url-state.test.ts, tsc --noEmit in apps/mobile, and lint on the touched files. Not verified on a device or simulator.

Closes#7630

Built by Claude Opus 5 in the Claude Code harness.


Note

Low Risk
Scoped to mobile asset URL presentation and thread file previews; logic is covered by unit tests and does not change auth or shared web hooks.

Overview
Mobile workspace file previews (image, browser/PDF, video) no longer spin forever when a signed asset URL cannot be obtained. useAssetUrlState now combines the shared URL query result with the environment connection phase via deriveAssetUrlState, yielding Failure with disconnected vs failed so UI can distinguish outage from a bad/missing asset.

On ThreadFileScreen, FileContent shows the new WorkspaceFilePreviewError for previews that depend on an asset URL: EnvironmentConnectionNotice (with connection retry + URL refresh) when disconnected, or a “Preview unavailable” empty state with Try again otherwise. Retry refreshes the asset URL and bumps previewRevision to remount the preview. WorkspaceFileVideoPreview drops its local unavailable UI because the parent owns that terminal state.

Unit tests cover deriveAssetUrlState; other mobile useAssetUrlState consumers that already treat Failure as an error surface should pick up disconnected cases without further changes.

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

Note

Fix endless mobile preview spinner by showing reason-specific asset URL errors

  • Adds WorkspaceFilePreviewError in WorkspaceFilePreviewError.tsx to render an environment connection notice for disconnected failures and a generic "Preview unavailable" retry state for other asset URL failures.
  • Adds deriveAssetUrlState in asset-url-state.ts to distinguish disconnected environment failures from connected query failures, and suppresses resolved URLs while the environment is offline, reconnecting, or in error.
  • Updates useAssetUrlState and useConnectionPhase in assets.ts to fold the shared asset result with the current connection phase, and treats a waiting post-outage query as Loading so stale failures are not shown.
  • Wires the typed failure reason and a retry handler (which remints the asset URL and remounts the preview) through FileContent and ThreadFileScreen in ThreadFilesRouteScreen.tsx, removing the old boolean previewUnavailable prop.
  • Risk: WorkspaceFileVideoPreview no longer renders its own "Video unavailable" state; video asset failures must now be handled by the parent before the media player renders. Existing consumers relying on the unavailable prop or the boolean previewUnavailable prop need updating.

Macroscope summarized 9cd4887.

@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). labels Sep 1, 2026
Comment threadapps/mobile/src/features/files/ThreadFilesRouteScreen.tsx Outdated
@github-actions

github-actionsBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.8 KiB13.6 KiB−189 B (−1.3%)15.1 KiB
CodexThread snapshot wire7.0 KiB7.0 KiB−14 B (−0.2%)7.3 KiB
CodexLive turn WebSocket wire6.7 KiB6.5 KiB−175 B (−2.5%)7.8 KiB
CodexLive turn WebSocket decoded58.5 KiB57.1 KiB−1.4 KiB (−2.4%)66.4 KiB
CodexLive turn messages10100 (0.0%)21
ClaudeTotal thread wire13.6 KiB13.5 KiB−28 B (−0.2%)15.1 KiB
ClaudeThread snapshot wire7.0 KiB7.0 KiB−7 B (−0.1%)7.3 KiB
ClaudeLive turn WebSocket wire6.5 KiB6.5 KiB−21 B (−0.3%)7.8 KiB
ClaudeLive turn WebSocket decoded57.8 KiB57.8 KiB−44 B (−0.1%)66.4 KiB
ClaudeLive turn messages98−1 (−11.1%)21

Baseline: 18573d6 · PR result: 9cd4887 · 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: 113.8 KiB
  • Claude decoded thread snapshot: 114.5 KiB

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

Comment threadapps/mobile/src/state/asset-url-state.ts
@macroscopeapp

macroscopeappBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 9cd4887

Macroscope's review found this PR approvable — This is a focused mobile bug fix that replaces terminal preview spinners with actionable connection or asset-unavailable states, including retry support. The shared hook’s broader impact is limited to mobile asset previews and is covered by explicit state-derivation tests, with no schema, deployment, security, billing, or static-analysis changes.

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

@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 6a78e63 to dc35525CompareSeptember 2, 2026 00:04
Comment threadapps/mobile/src/state/asset-url-state.ts
Comment threadapps/mobile/src/state/asset-url-state.ts Outdated
macroscopeapp[bot]
macroscopeappBot previously approved these changes Sep 2, 2026
@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 4b38589 to 13efe5bCompareSeptember 3, 2026 09:26
@macroscopeapp
macroscopeappBot dismissed their stale reviewSeptember 3, 2026 09:26

Dismissing prior approval to re-evaluate 13efe5b

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit 13efe5b. Configure here.

Comment threadapps/mobile/src/state/asset-url-state.ts
t3dotggand others added 2 commits September 3, 2026 02:33
When the connection returns, the URL query re-runs while its last
result is still the outage failure. That in-flight failure read as a
missing file. It now reads as loading until the re-query settles.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 13efe5b to 9cd4887CompareSeptember 3, 2026 09:33
@t3dotgg
t3dotgg merged commit 2aa907b into mainSep 3, 2026
27 checks passed
@t3dotgg
t3dotgg deleted the fix/mobile-asset-preview-error-state branch September 3, 2026 09:41
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 3, 2026
## What's Changed
* fix(mobile): show an error instead of an endless preview spinner by @t3dotgg in pingdotgg/t3code#9123
* fix(usage): price new models without waiting a day for the rate table by @t3dotgg in pingdotgg/t3code#9202
* fix(web): unlock the composer when preview capture fails by @t3dotgg in pingdotgg/t3code#9127
* fix(antigravity): refresh the model manifest so older Gemini models fold as legacy by @t3dotgg in pingdotgg/t3code#9397
* perf(ci): reuse dependency checks in release builds by @t3dotgg in pingdotgg/t3code#9399
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260903.1268...v0.0.39-nightly.20260903.1270
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260903.1270
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Mobile asset previews spin forever when asset URL creation or environment connection fails

1 participant

@t3dotgg
, '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

fix(mobile): show an error instead of an endless preview spinner - #9123

Merged
t3dotgg merged 2 commits into
mainfrom
fix/mobile-asset-preview-error-state
Sep 3, 2026
Merged

fix(mobile): show an error instead of an endless preview spinner#9123
t3dotgg merged 2 commits into
mainfrom
fix/mobile-asset-preview-error-state

Conversation

@t3dotgg

@t3dotggt3dotgg commented Sep 1, 2026

Copy link
Copy Markdown
Member

On mobile, an image, PDF, or other asset preview kept spinning on "Preparing ... preview" when the environment was disconnected or the asset URL request failed. useAssetUrlState reported a pending query and a dead connection as the same Loading value, so previews had no terminal state and no way to retry.

The hook now folds the environment connection phase into its result. A query that is still pending while the environment is offline, retrying, or in error resolves to Failure with a disconnected reason. A failed query resolves to Failure with a failed reason. The derivation moved into apps/mobile/src/state/asset-url-state.ts so it can be tested on its own.

On the file screen, image, SVG, browser, and video previews now render an error instead of a spinner when the asset URL will not arrive. A disconnected environment gets the existing EnvironmentConnectionNotice, the same offline surface the terminal and review sheets use, with its "Retry now" button wired to a connection retry plus a fresh URL mint. A failed URL gets a short "Preview unavailable" line with "Try again". The video preview lost its own unavailable branch because the screen now owns that state for every preview type.

The other consumers, the full screen file preview modal, both video preview modals, and the chat feed image and video, already branched on Failure, so they pick up the disconnected case without changes. The web app has its own copy of the hook in apps/web/src/assets/assetUrls.ts and is left alone to keep this to one concern.

Verified with vp test run apps/mobile/src/state/asset-url-state.test.ts, tsc --noEmit in apps/mobile, and lint on the touched files. Not verified on a device or simulator.

Closes#7630

Built by Claude Opus 5 in the Claude Code harness.


Note

Low Risk
Scoped to mobile asset URL presentation and thread file previews; logic is covered by unit tests and does not change auth or shared web hooks.

Overview
Mobile workspace file previews (image, browser/PDF, video) no longer spin forever when a signed asset URL cannot be obtained. useAssetUrlState now combines the shared URL query result with the environment connection phase via deriveAssetUrlState, yielding Failure with disconnected vs failed so UI can distinguish outage from a bad/missing asset.

On ThreadFileScreen, FileContent shows the new WorkspaceFilePreviewError for previews that depend on an asset URL: EnvironmentConnectionNotice (with connection retry + URL refresh) when disconnected, or a “Preview unavailable” empty state with Try again otherwise. Retry refreshes the asset URL and bumps previewRevision to remount the preview. WorkspaceFileVideoPreview drops its local unavailable UI because the parent owns that terminal state.

Unit tests cover deriveAssetUrlState; other mobile useAssetUrlState consumers that already treat Failure as an error surface should pick up disconnected cases without further changes.

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

Note

Fix endless mobile preview spinner by showing reason-specific asset URL errors

  • Adds WorkspaceFilePreviewError in WorkspaceFilePreviewError.tsx to render an environment connection notice for disconnected failures and a generic "Preview unavailable" retry state for other asset URL failures.
  • Adds deriveAssetUrlState in asset-url-state.ts to distinguish disconnected environment failures from connected query failures, and suppresses resolved URLs while the environment is offline, reconnecting, or in error.
  • Updates useAssetUrlState and useConnectionPhase in assets.ts to fold the shared asset result with the current connection phase, and treats a waiting post-outage query as Loading so stale failures are not shown.
  • Wires the typed failure reason and a retry handler (which remints the asset URL and remounts the preview) through FileContent and ThreadFileScreen in ThreadFilesRouteScreen.tsx, removing the old boolean previewUnavailable prop.
  • Risk: WorkspaceFileVideoPreview no longer renders its own "Video unavailable" state; video asset failures must now be handled by the parent before the media player renders. Existing consumers relying on the unavailable prop or the boolean previewUnavailable prop need updating.

Macroscope summarized 9cd4887.

@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). labels Sep 1, 2026
Comment threadapps/mobile/src/features/files/ThreadFilesRouteScreen.tsx Outdated
@github-actions

github-actionsBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.8 KiB13.6 KiB−189 B (−1.3%)15.1 KiB
CodexThread snapshot wire7.0 KiB7.0 KiB−14 B (−0.2%)7.3 KiB
CodexLive turn WebSocket wire6.7 KiB6.5 KiB−175 B (−2.5%)7.8 KiB
CodexLive turn WebSocket decoded58.5 KiB57.1 KiB−1.4 KiB (−2.4%)66.4 KiB
CodexLive turn messages10100 (0.0%)21
ClaudeTotal thread wire13.6 KiB13.5 KiB−28 B (−0.2%)15.1 KiB
ClaudeThread snapshot wire7.0 KiB7.0 KiB−7 B (−0.1%)7.3 KiB
ClaudeLive turn WebSocket wire6.5 KiB6.5 KiB−21 B (−0.3%)7.8 KiB
ClaudeLive turn WebSocket decoded57.8 KiB57.8 KiB−44 B (−0.1%)66.4 KiB
ClaudeLive turn messages98−1 (−11.1%)21

Baseline: 18573d6 · PR result: 9cd4887 · 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: 113.8 KiB
  • Claude decoded thread snapshot: 114.5 KiB

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

Comment threadapps/mobile/src/state/asset-url-state.ts
@macroscopeapp

macroscopeappBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 9cd4887

Macroscope's review found this PR approvable — This is a focused mobile bug fix that replaces terminal preview spinners with actionable connection or asset-unavailable states, including retry support. The shared hook’s broader impact is limited to mobile asset previews and is covered by explicit state-derivation tests, with no schema, deployment, security, billing, or static-analysis changes.

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

@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 6a78e63 to dc35525CompareSeptember 2, 2026 00:04
Comment threadapps/mobile/src/state/asset-url-state.ts
Comment threadapps/mobile/src/state/asset-url-state.ts Outdated
macroscopeapp[bot]
macroscopeappBot previously approved these changes Sep 2, 2026
@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 4b38589 to 13efe5bCompareSeptember 3, 2026 09:26
@macroscopeapp
macroscopeappBot dismissed their stale reviewSeptember 3, 2026 09:26

Dismissing prior approval to re-evaluate 13efe5b

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit 13efe5b. Configure here.

Comment threadapps/mobile/src/state/asset-url-state.ts
t3dotggand others added 2 commits September 3, 2026 02:33
When the connection returns, the URL query re-runs while its last
result is still the outage failure. That in-flight failure read as a
missing file. It now reads as loading until the re-query settles.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 13efe5b to 9cd4887CompareSeptember 3, 2026 09:33
@t3dotgg
t3dotgg merged commit 2aa907b into mainSep 3, 2026
27 checks passed
@t3dotgg
t3dotgg deleted the fix/mobile-asset-preview-error-state branch September 3, 2026 09:41
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 3, 2026
## What's Changed
* fix(mobile): show an error instead of an endless preview spinner by @t3dotgg in pingdotgg/t3code#9123
* fix(usage): price new models without waiting a day for the rate table by @t3dotgg in pingdotgg/t3code#9202
* fix(web): unlock the composer when preview capture fails by @t3dotgg in pingdotgg/t3code#9127
* fix(antigravity): refresh the model manifest so older Gemini models fold as legacy by @t3dotgg in pingdotgg/t3code#9397
* perf(ci): reuse dependency checks in release builds by @t3dotgg in pingdotgg/t3code#9399
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260903.1268...v0.0.39-nightly.20260903.1270
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260903.1270
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Mobile asset previews spin forever when asset URL creation or environment connection fails

1 participant

@t3dotgg
, '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

fix(mobile): show an error instead of an endless preview spinner - #9123

Merged
t3dotgg merged 2 commits into
mainfrom
fix/mobile-asset-preview-error-state
Sep 3, 2026
Merged

fix(mobile): show an error instead of an endless preview spinner#9123
t3dotgg merged 2 commits into
mainfrom
fix/mobile-asset-preview-error-state

Conversation

@t3dotgg

@t3dotggt3dotgg commented Sep 1, 2026

Copy link
Copy Markdown
Member

On mobile, an image, PDF, or other asset preview kept spinning on "Preparing ... preview" when the environment was disconnected or the asset URL request failed. useAssetUrlState reported a pending query and a dead connection as the same Loading value, so previews had no terminal state and no way to retry.

The hook now folds the environment connection phase into its result. A query that is still pending while the environment is offline, retrying, or in error resolves to Failure with a disconnected reason. A failed query resolves to Failure with a failed reason. The derivation moved into apps/mobile/src/state/asset-url-state.ts so it can be tested on its own.

On the file screen, image, SVG, browser, and video previews now render an error instead of a spinner when the asset URL will not arrive. A disconnected environment gets the existing EnvironmentConnectionNotice, the same offline surface the terminal and review sheets use, with its "Retry now" button wired to a connection retry plus a fresh URL mint. A failed URL gets a short "Preview unavailable" line with "Try again". The video preview lost its own unavailable branch because the screen now owns that state for every preview type.

The other consumers, the full screen file preview modal, both video preview modals, and the chat feed image and video, already branched on Failure, so they pick up the disconnected case without changes. The web app has its own copy of the hook in apps/web/src/assets/assetUrls.ts and is left alone to keep this to one concern.

Verified with vp test run apps/mobile/src/state/asset-url-state.test.ts, tsc --noEmit in apps/mobile, and lint on the touched files. Not verified on a device or simulator.

Closes#7630

Built by Claude Opus 5 in the Claude Code harness.


Note

Low Risk
Scoped to mobile asset URL presentation and thread file previews; logic is covered by unit tests and does not change auth or shared web hooks.

Overview
Mobile workspace file previews (image, browser/PDF, video) no longer spin forever when a signed asset URL cannot be obtained. useAssetUrlState now combines the shared URL query result with the environment connection phase via deriveAssetUrlState, yielding Failure with disconnected vs failed so UI can distinguish outage from a bad/missing asset.

On ThreadFileScreen, FileContent shows the new WorkspaceFilePreviewError for previews that depend on an asset URL: EnvironmentConnectionNotice (with connection retry + URL refresh) when disconnected, or a “Preview unavailable” empty state with Try again otherwise. Retry refreshes the asset URL and bumps previewRevision to remount the preview. WorkspaceFileVideoPreview drops its local unavailable UI because the parent owns that terminal state.

Unit tests cover deriveAssetUrlState; other mobile useAssetUrlState consumers that already treat Failure as an error surface should pick up disconnected cases without further changes.

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

Note

Fix endless mobile preview spinner by showing reason-specific asset URL errors

  • Adds WorkspaceFilePreviewError in WorkspaceFilePreviewError.tsx to render an environment connection notice for disconnected failures and a generic "Preview unavailable" retry state for other asset URL failures.
  • Adds deriveAssetUrlState in asset-url-state.ts to distinguish disconnected environment failures from connected query failures, and suppresses resolved URLs while the environment is offline, reconnecting, or in error.
  • Updates useAssetUrlState and useConnectionPhase in assets.ts to fold the shared asset result with the current connection phase, and treats a waiting post-outage query as Loading so stale failures are not shown.
  • Wires the typed failure reason and a retry handler (which remints the asset URL and remounts the preview) through FileContent and ThreadFileScreen in ThreadFilesRouteScreen.tsx, removing the old boolean previewUnavailable prop.
  • Risk: WorkspaceFileVideoPreview no longer renders its own "Video unavailable" state; video asset failures must now be handled by the parent before the media player renders. Existing consumers relying on the unavailable prop or the boolean previewUnavailable prop need updating.

Macroscope summarized 9cd4887.

@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). labels Sep 1, 2026
Comment threadapps/mobile/src/features/files/ThreadFilesRouteScreen.tsx Outdated
@github-actions

github-actionsBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.8 KiB13.6 KiB−189 B (−1.3%)15.1 KiB
CodexThread snapshot wire7.0 KiB7.0 KiB−14 B (−0.2%)7.3 KiB
CodexLive turn WebSocket wire6.7 KiB6.5 KiB−175 B (−2.5%)7.8 KiB
CodexLive turn WebSocket decoded58.5 KiB57.1 KiB−1.4 KiB (−2.4%)66.4 KiB
CodexLive turn messages10100 (0.0%)21
ClaudeTotal thread wire13.6 KiB13.5 KiB−28 B (−0.2%)15.1 KiB
ClaudeThread snapshot wire7.0 KiB7.0 KiB−7 B (−0.1%)7.3 KiB
ClaudeLive turn WebSocket wire6.5 KiB6.5 KiB−21 B (−0.3%)7.8 KiB
ClaudeLive turn WebSocket decoded57.8 KiB57.8 KiB−44 B (−0.1%)66.4 KiB
ClaudeLive turn messages98−1 (−11.1%)21

Baseline: 18573d6 · PR result: 9cd4887 · 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: 113.8 KiB
  • Claude decoded thread snapshot: 114.5 KiB

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

Comment threadapps/mobile/src/state/asset-url-state.ts
@macroscopeapp

macroscopeappBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 9cd4887

Macroscope's review found this PR approvable — This is a focused mobile bug fix that replaces terminal preview spinners with actionable connection or asset-unavailable states, including retry support. The shared hook’s broader impact is limited to mobile asset previews and is covered by explicit state-derivation tests, with no schema, deployment, security, billing, or static-analysis changes.

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

@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 6a78e63 to dc35525CompareSeptember 2, 2026 00:04
Comment threadapps/mobile/src/state/asset-url-state.ts
Comment threadapps/mobile/src/state/asset-url-state.ts Outdated
macroscopeapp[bot]
macroscopeappBot previously approved these changes Sep 2, 2026
@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 4b38589 to 13efe5bCompareSeptember 3, 2026 09:26
@macroscopeapp
macroscopeappBot dismissed their stale reviewSeptember 3, 2026 09:26

Dismissing prior approval to re-evaluate 13efe5b

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit 13efe5b. Configure here.

Comment threadapps/mobile/src/state/asset-url-state.ts
t3dotggand others added 2 commits September 3, 2026 02:33
When the connection returns, the URL query re-runs while its last
result is still the outage failure. That in-flight failure read as a
missing file. It now reads as loading until the re-query settles.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 13efe5b to 9cd4887CompareSeptember 3, 2026 09:33
@t3dotgg
t3dotgg merged commit 2aa907b into mainSep 3, 2026
27 checks passed
@t3dotgg
t3dotgg deleted the fix/mobile-asset-preview-error-state branch September 3, 2026 09:41
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 3, 2026
## What's Changed
* fix(mobile): show an error instead of an endless preview spinner by @t3dotgg in pingdotgg/t3code#9123
* fix(usage): price new models without waiting a day for the rate table by @t3dotgg in pingdotgg/t3code#9202
* fix(web): unlock the composer when preview capture fails by @t3dotgg in pingdotgg/t3code#9127
* fix(antigravity): refresh the model manifest so older Gemini models fold as legacy by @t3dotgg in pingdotgg/t3code#9397
* perf(ci): reuse dependency checks in release builds by @t3dotgg in pingdotgg/t3code#9399
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260903.1268...v0.0.39-nightly.20260903.1270
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260903.1270
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Mobile asset previews spin forever when asset URL creation or environment connection fails

1 participant

@t3dotgg
, '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

fix(mobile): show an error instead of an endless preview spinner - #9123

Merged
t3dotgg merged 2 commits into
mainfrom
fix/mobile-asset-preview-error-state
Sep 3, 2026
Merged

fix(mobile): show an error instead of an endless preview spinner#9123
t3dotgg merged 2 commits into
mainfrom
fix/mobile-asset-preview-error-state

Conversation

@t3dotgg

@t3dotggt3dotgg commented Sep 1, 2026

Copy link
Copy Markdown
Member

On mobile, an image, PDF, or other asset preview kept spinning on "Preparing ... preview" when the environment was disconnected or the asset URL request failed. useAssetUrlState reported a pending query and a dead connection as the same Loading value, so previews had no terminal state and no way to retry.

The hook now folds the environment connection phase into its result. A query that is still pending while the environment is offline, retrying, or in error resolves to Failure with a disconnected reason. A failed query resolves to Failure with a failed reason. The derivation moved into apps/mobile/src/state/asset-url-state.ts so it can be tested on its own.

On the file screen, image, SVG, browser, and video previews now render an error instead of a spinner when the asset URL will not arrive. A disconnected environment gets the existing EnvironmentConnectionNotice, the same offline surface the terminal and review sheets use, with its "Retry now" button wired to a connection retry plus a fresh URL mint. A failed URL gets a short "Preview unavailable" line with "Try again". The video preview lost its own unavailable branch because the screen now owns that state for every preview type.

The other consumers, the full screen file preview modal, both video preview modals, and the chat feed image and video, already branched on Failure, so they pick up the disconnected case without changes. The web app has its own copy of the hook in apps/web/src/assets/assetUrls.ts and is left alone to keep this to one concern.

Verified with vp test run apps/mobile/src/state/asset-url-state.test.ts, tsc --noEmit in apps/mobile, and lint on the touched files. Not verified on a device or simulator.

Closes#7630

Built by Claude Opus 5 in the Claude Code harness.


Note

Low Risk
Scoped to mobile asset URL presentation and thread file previews; logic is covered by unit tests and does not change auth or shared web hooks.

Overview
Mobile workspace file previews (image, browser/PDF, video) no longer spin forever when a signed asset URL cannot be obtained. useAssetUrlState now combines the shared URL query result with the environment connection phase via deriveAssetUrlState, yielding Failure with disconnected vs failed so UI can distinguish outage from a bad/missing asset.

On ThreadFileScreen, FileContent shows the new WorkspaceFilePreviewError for previews that depend on an asset URL: EnvironmentConnectionNotice (with connection retry + URL refresh) when disconnected, or a “Preview unavailable” empty state with Try again otherwise. Retry refreshes the asset URL and bumps previewRevision to remount the preview. WorkspaceFileVideoPreview drops its local unavailable UI because the parent owns that terminal state.

Unit tests cover deriveAssetUrlState; other mobile useAssetUrlState consumers that already treat Failure as an error surface should pick up disconnected cases without further changes.

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

Note

Fix endless mobile preview spinner by showing reason-specific asset URL errors

  • Adds WorkspaceFilePreviewError in WorkspaceFilePreviewError.tsx to render an environment connection notice for disconnected failures and a generic "Preview unavailable" retry state for other asset URL failures.
  • Adds deriveAssetUrlState in asset-url-state.ts to distinguish disconnected environment failures from connected query failures, and suppresses resolved URLs while the environment is offline, reconnecting, or in error.
  • Updates useAssetUrlState and useConnectionPhase in assets.ts to fold the shared asset result with the current connection phase, and treats a waiting post-outage query as Loading so stale failures are not shown.
  • Wires the typed failure reason and a retry handler (which remints the asset URL and remounts the preview) through FileContent and ThreadFileScreen in ThreadFilesRouteScreen.tsx, removing the old boolean previewUnavailable prop.
  • Risk: WorkspaceFileVideoPreview no longer renders its own "Video unavailable" state; video asset failures must now be handled by the parent before the media player renders. Existing consumers relying on the unavailable prop or the boolean previewUnavailable prop need updating.

Macroscope summarized 9cd4887.

@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). labels Sep 1, 2026
Comment threadapps/mobile/src/features/files/ThreadFilesRouteScreen.tsx Outdated
@github-actions

github-actionsBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.8 KiB13.6 KiB−189 B (−1.3%)15.1 KiB
CodexThread snapshot wire7.0 KiB7.0 KiB−14 B (−0.2%)7.3 KiB
CodexLive turn WebSocket wire6.7 KiB6.5 KiB−175 B (−2.5%)7.8 KiB
CodexLive turn WebSocket decoded58.5 KiB57.1 KiB−1.4 KiB (−2.4%)66.4 KiB
CodexLive turn messages10100 (0.0%)21
ClaudeTotal thread wire13.6 KiB13.5 KiB−28 B (−0.2%)15.1 KiB
ClaudeThread snapshot wire7.0 KiB7.0 KiB−7 B (−0.1%)7.3 KiB
ClaudeLive turn WebSocket wire6.5 KiB6.5 KiB−21 B (−0.3%)7.8 KiB
ClaudeLive turn WebSocket decoded57.8 KiB57.8 KiB−44 B (−0.1%)66.4 KiB
ClaudeLive turn messages98−1 (−11.1%)21

Baseline: 18573d6 · PR result: 9cd4887 · 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: 113.8 KiB
  • Claude decoded thread snapshot: 114.5 KiB

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

Comment threadapps/mobile/src/state/asset-url-state.ts
@macroscopeapp

macroscopeappBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 9cd4887

Macroscope's review found this PR approvable — This is a focused mobile bug fix that replaces terminal preview spinners with actionable connection or asset-unavailable states, including retry support. The shared hook’s broader impact is limited to mobile asset previews and is covered by explicit state-derivation tests, with no schema, deployment, security, billing, or static-analysis changes.

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

@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 6a78e63 to dc35525CompareSeptember 2, 2026 00:04
Comment threadapps/mobile/src/state/asset-url-state.ts
Comment threadapps/mobile/src/state/asset-url-state.ts Outdated
macroscopeapp[bot]
macroscopeappBot previously approved these changes Sep 2, 2026
@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 4b38589 to 13efe5bCompareSeptember 3, 2026 09:26
@macroscopeapp
macroscopeappBot dismissed their stale reviewSeptember 3, 2026 09:26

Dismissing prior approval to re-evaluate 13efe5b

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit 13efe5b. Configure here.

Comment threadapps/mobile/src/state/asset-url-state.ts
t3dotggand others added 2 commits September 3, 2026 02:33
When the connection returns, the URL query re-runs while its last
result is still the outage failure. That in-flight failure read as a
missing file. It now reads as loading until the re-query settles.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 13efe5b to 9cd4887CompareSeptember 3, 2026 09:33
@t3dotgg
t3dotgg merged commit 2aa907b into mainSep 3, 2026
27 checks passed
@t3dotgg
t3dotgg deleted the fix/mobile-asset-preview-error-state branch September 3, 2026 09:41
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 3, 2026
## What's Changed
* fix(mobile): show an error instead of an endless preview spinner by @t3dotgg in pingdotgg/t3code#9123
* fix(usage): price new models without waiting a day for the rate table by @t3dotgg in pingdotgg/t3code#9202
* fix(web): unlock the composer when preview capture fails by @t3dotgg in pingdotgg/t3code#9127
* fix(antigravity): refresh the model manifest so older Gemini models fold as legacy by @t3dotgg in pingdotgg/t3code#9397
* perf(ci): reuse dependency checks in release builds by @t3dotgg in pingdotgg/t3code#9399
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260903.1268...v0.0.39-nightly.20260903.1270
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260903.1270
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Mobile asset previews spin forever when asset URL creation or environment connection fails

1 participant

@t3dotgg
, '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

fix(mobile): show an error instead of an endless preview spinner - #9123

Merged
t3dotgg merged 2 commits into
mainfrom
fix/mobile-asset-preview-error-state
Sep 3, 2026
Merged

fix(mobile): show an error instead of an endless preview spinner#9123
t3dotgg merged 2 commits into
mainfrom
fix/mobile-asset-preview-error-state

Conversation

@t3dotgg

@t3dotggt3dotgg commented Sep 1, 2026

Copy link
Copy Markdown
Member

On mobile, an image, PDF, or other asset preview kept spinning on "Preparing ... preview" when the environment was disconnected or the asset URL request failed. useAssetUrlState reported a pending query and a dead connection as the same Loading value, so previews had no terminal state and no way to retry.

The hook now folds the environment connection phase into its result. A query that is still pending while the environment is offline, retrying, or in error resolves to Failure with a disconnected reason. A failed query resolves to Failure with a failed reason. The derivation moved into apps/mobile/src/state/asset-url-state.ts so it can be tested on its own.

On the file screen, image, SVG, browser, and video previews now render an error instead of a spinner when the asset URL will not arrive. A disconnected environment gets the existing EnvironmentConnectionNotice, the same offline surface the terminal and review sheets use, with its "Retry now" button wired to a connection retry plus a fresh URL mint. A failed URL gets a short "Preview unavailable" line with "Try again". The video preview lost its own unavailable branch because the screen now owns that state for every preview type.

The other consumers, the full screen file preview modal, both video preview modals, and the chat feed image and video, already branched on Failure, so they pick up the disconnected case without changes. The web app has its own copy of the hook in apps/web/src/assets/assetUrls.ts and is left alone to keep this to one concern.

Verified with vp test run apps/mobile/src/state/asset-url-state.test.ts, tsc --noEmit in apps/mobile, and lint on the touched files. Not verified on a device or simulator.

Closes#7630

Built by Claude Opus 5 in the Claude Code harness.


Note

Low Risk
Scoped to mobile asset URL presentation and thread file previews; logic is covered by unit tests and does not change auth or shared web hooks.

Overview
Mobile workspace file previews (image, browser/PDF, video) no longer spin forever when a signed asset URL cannot be obtained. useAssetUrlState now combines the shared URL query result with the environment connection phase via deriveAssetUrlState, yielding Failure with disconnected vs failed so UI can distinguish outage from a bad/missing asset.

On ThreadFileScreen, FileContent shows the new WorkspaceFilePreviewError for previews that depend on an asset URL: EnvironmentConnectionNotice (with connection retry + URL refresh) when disconnected, or a “Preview unavailable” empty state with Try again otherwise. Retry refreshes the asset URL and bumps previewRevision to remount the preview. WorkspaceFileVideoPreview drops its local unavailable UI because the parent owns that terminal state.

Unit tests cover deriveAssetUrlState; other mobile useAssetUrlState consumers that already treat Failure as an error surface should pick up disconnected cases without further changes.

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

Note

Fix endless mobile preview spinner by showing reason-specific asset URL errors

  • Adds WorkspaceFilePreviewError in WorkspaceFilePreviewError.tsx to render an environment connection notice for disconnected failures and a generic "Preview unavailable" retry state for other asset URL failures.
  • Adds deriveAssetUrlState in asset-url-state.ts to distinguish disconnected environment failures from connected query failures, and suppresses resolved URLs while the environment is offline, reconnecting, or in error.
  • Updates useAssetUrlState and useConnectionPhase in assets.ts to fold the shared asset result with the current connection phase, and treats a waiting post-outage query as Loading so stale failures are not shown.
  • Wires the typed failure reason and a retry handler (which remints the asset URL and remounts the preview) through FileContent and ThreadFileScreen in ThreadFilesRouteScreen.tsx, removing the old boolean previewUnavailable prop.
  • Risk: WorkspaceFileVideoPreview no longer renders its own "Video unavailable" state; video asset failures must now be handled by the parent before the media player renders. Existing consumers relying on the unavailable prop or the boolean previewUnavailable prop need updating.

Macroscope summarized 9cd4887.

@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). labels Sep 1, 2026
Comment threadapps/mobile/src/features/files/ThreadFilesRouteScreen.tsx Outdated
@github-actions

github-actionsBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.8 KiB13.6 KiB−189 B (−1.3%)15.1 KiB
CodexThread snapshot wire7.0 KiB7.0 KiB−14 B (−0.2%)7.3 KiB
CodexLive turn WebSocket wire6.7 KiB6.5 KiB−175 B (−2.5%)7.8 KiB
CodexLive turn WebSocket decoded58.5 KiB57.1 KiB−1.4 KiB (−2.4%)66.4 KiB
CodexLive turn messages10100 (0.0%)21
ClaudeTotal thread wire13.6 KiB13.5 KiB−28 B (−0.2%)15.1 KiB
ClaudeThread snapshot wire7.0 KiB7.0 KiB−7 B (−0.1%)7.3 KiB
ClaudeLive turn WebSocket wire6.5 KiB6.5 KiB−21 B (−0.3%)7.8 KiB
ClaudeLive turn WebSocket decoded57.8 KiB57.8 KiB−44 B (−0.1%)66.4 KiB
ClaudeLive turn messages98−1 (−11.1%)21

Baseline: 18573d6 · PR result: 9cd4887 · 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: 113.8 KiB
  • Claude decoded thread snapshot: 114.5 KiB

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

Comment threadapps/mobile/src/state/asset-url-state.ts
@macroscopeapp

macroscopeappBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 9cd4887

Macroscope's review found this PR approvable — This is a focused mobile bug fix that replaces terminal preview spinners with actionable connection or asset-unavailable states, including retry support. The shared hook’s broader impact is limited to mobile asset previews and is covered by explicit state-derivation tests, with no schema, deployment, security, billing, or static-analysis changes.

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

@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 6a78e63 to dc35525CompareSeptember 2, 2026 00:04
Comment threadapps/mobile/src/state/asset-url-state.ts
Comment threadapps/mobile/src/state/asset-url-state.ts Outdated
macroscopeapp[bot]
macroscopeappBot previously approved these changes Sep 2, 2026
@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 4b38589 to 13efe5bCompareSeptember 3, 2026 09:26
@macroscopeapp
macroscopeappBot dismissed their stale reviewSeptember 3, 2026 09:26

Dismissing prior approval to re-evaluate 13efe5b

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit 13efe5b. Configure here.

Comment threadapps/mobile/src/state/asset-url-state.ts
t3dotggand others added 2 commits September 3, 2026 02:33
When the connection returns, the URL query re-runs while its last
result is still the outage failure. That in-flight failure read as a
missing file. It now reads as loading until the re-query settles.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 13efe5b to 9cd4887CompareSeptember 3, 2026 09:33
@t3dotgg
t3dotgg merged commit 2aa907b into mainSep 3, 2026
27 checks passed
@t3dotgg
t3dotgg deleted the fix/mobile-asset-preview-error-state branch September 3, 2026 09:41
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 3, 2026
## What's Changed
* fix(mobile): show an error instead of an endless preview spinner by @t3dotgg in pingdotgg/t3code#9123
* fix(usage): price new models without waiting a day for the rate table by @t3dotgg in pingdotgg/t3code#9202
* fix(web): unlock the composer when preview capture fails by @t3dotgg in pingdotgg/t3code#9127
* fix(antigravity): refresh the model manifest so older Gemini models fold as legacy by @t3dotgg in pingdotgg/t3code#9397
* perf(ci): reuse dependency checks in release builds by @t3dotgg in pingdotgg/t3code#9399
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260903.1268...v0.0.39-nightly.20260903.1270
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260903.1270
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Mobile asset previews spin forever when asset URL creation or environment connection fails

1 participant

@t3dotgg
, '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

fix(mobile): show an error instead of an endless preview spinner - #9123

Merged
t3dotgg merged 2 commits into
mainfrom
fix/mobile-asset-preview-error-state
Sep 3, 2026
Merged

fix(mobile): show an error instead of an endless preview spinner#9123
t3dotgg merged 2 commits into
mainfrom
fix/mobile-asset-preview-error-state

Conversation

@t3dotgg

@t3dotggt3dotgg commented Sep 1, 2026

Copy link
Copy Markdown
Member

On mobile, an image, PDF, or other asset preview kept spinning on "Preparing ... preview" when the environment was disconnected or the asset URL request failed. useAssetUrlState reported a pending query and a dead connection as the same Loading value, so previews had no terminal state and no way to retry.

The hook now folds the environment connection phase into its result. A query that is still pending while the environment is offline, retrying, or in error resolves to Failure with a disconnected reason. A failed query resolves to Failure with a failed reason. The derivation moved into apps/mobile/src/state/asset-url-state.ts so it can be tested on its own.

On the file screen, image, SVG, browser, and video previews now render an error instead of a spinner when the asset URL will not arrive. A disconnected environment gets the existing EnvironmentConnectionNotice, the same offline surface the terminal and review sheets use, with its "Retry now" button wired to a connection retry plus a fresh URL mint. A failed URL gets a short "Preview unavailable" line with "Try again". The video preview lost its own unavailable branch because the screen now owns that state for every preview type.

The other consumers, the full screen file preview modal, both video preview modals, and the chat feed image and video, already branched on Failure, so they pick up the disconnected case without changes. The web app has its own copy of the hook in apps/web/src/assets/assetUrls.ts and is left alone to keep this to one concern.

Verified with vp test run apps/mobile/src/state/asset-url-state.test.ts, tsc --noEmit in apps/mobile, and lint on the touched files. Not verified on a device or simulator.

Closes#7630

Built by Claude Opus 5 in the Claude Code harness.


Note

Low Risk
Scoped to mobile asset URL presentation and thread file previews; logic is covered by unit tests and does not change auth or shared web hooks.

Overview
Mobile workspace file previews (image, browser/PDF, video) no longer spin forever when a signed asset URL cannot be obtained. useAssetUrlState now combines the shared URL query result with the environment connection phase via deriveAssetUrlState, yielding Failure with disconnected vs failed so UI can distinguish outage from a bad/missing asset.

On ThreadFileScreen, FileContent shows the new WorkspaceFilePreviewError for previews that depend on an asset URL: EnvironmentConnectionNotice (with connection retry + URL refresh) when disconnected, or a “Preview unavailable” empty state with Try again otherwise. Retry refreshes the asset URL and bumps previewRevision to remount the preview. WorkspaceFileVideoPreview drops its local unavailable UI because the parent owns that terminal state.

Unit tests cover deriveAssetUrlState; other mobile useAssetUrlState consumers that already treat Failure as an error surface should pick up disconnected cases without further changes.

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

Note

Fix endless mobile preview spinner by showing reason-specific asset URL errors

  • Adds WorkspaceFilePreviewError in WorkspaceFilePreviewError.tsx to render an environment connection notice for disconnected failures and a generic "Preview unavailable" retry state for other asset URL failures.
  • Adds deriveAssetUrlState in asset-url-state.ts to distinguish disconnected environment failures from connected query failures, and suppresses resolved URLs while the environment is offline, reconnecting, or in error.
  • Updates useAssetUrlState and useConnectionPhase in assets.ts to fold the shared asset result with the current connection phase, and treats a waiting post-outage query as Loading so stale failures are not shown.
  • Wires the typed failure reason and a retry handler (which remints the asset URL and remounts the preview) through FileContent and ThreadFileScreen in ThreadFilesRouteScreen.tsx, removing the old boolean previewUnavailable prop.
  • Risk: WorkspaceFileVideoPreview no longer renders its own "Video unavailable" state; video asset failures must now be handled by the parent before the media player renders. Existing consumers relying on the unavailable prop or the boolean previewUnavailable prop need updating.

Macroscope summarized 9cd4887.

@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). labels Sep 1, 2026
Comment threadapps/mobile/src/features/files/ThreadFilesRouteScreen.tsx Outdated
@github-actions

github-actionsBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.8 KiB13.6 KiB−189 B (−1.3%)15.1 KiB
CodexThread snapshot wire7.0 KiB7.0 KiB−14 B (−0.2%)7.3 KiB
CodexLive turn WebSocket wire6.7 KiB6.5 KiB−175 B (−2.5%)7.8 KiB
CodexLive turn WebSocket decoded58.5 KiB57.1 KiB−1.4 KiB (−2.4%)66.4 KiB
CodexLive turn messages10100 (0.0%)21
ClaudeTotal thread wire13.6 KiB13.5 KiB−28 B (−0.2%)15.1 KiB
ClaudeThread snapshot wire7.0 KiB7.0 KiB−7 B (−0.1%)7.3 KiB
ClaudeLive turn WebSocket wire6.5 KiB6.5 KiB−21 B (−0.3%)7.8 KiB
ClaudeLive turn WebSocket decoded57.8 KiB57.8 KiB−44 B (−0.1%)66.4 KiB
ClaudeLive turn messages98−1 (−11.1%)21

Baseline: 18573d6 · PR result: 9cd4887 · 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: 113.8 KiB
  • Claude decoded thread snapshot: 114.5 KiB

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

Comment threadapps/mobile/src/state/asset-url-state.ts
@macroscopeapp

macroscopeappBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 9cd4887

Macroscope's review found this PR approvable — This is a focused mobile bug fix that replaces terminal preview spinners with actionable connection or asset-unavailable states, including retry support. The shared hook’s broader impact is limited to mobile asset previews and is covered by explicit state-derivation tests, with no schema, deployment, security, billing, or static-analysis changes.

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

@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 6a78e63 to dc35525CompareSeptember 2, 2026 00:04
Comment threadapps/mobile/src/state/asset-url-state.ts
Comment threadapps/mobile/src/state/asset-url-state.ts Outdated
macroscopeapp[bot]
macroscopeappBot previously approved these changes Sep 2, 2026
@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 4b38589 to 13efe5bCompareSeptember 3, 2026 09:26
@macroscopeapp
macroscopeappBot dismissed their stale reviewSeptember 3, 2026 09:26

Dismissing prior approval to re-evaluate 13efe5b

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit 13efe5b. Configure here.

Comment threadapps/mobile/src/state/asset-url-state.ts
t3dotggand others added 2 commits September 3, 2026 02:33
When the connection returns, the URL query re-runs while its last
result is still the outage failure. That in-flight failure read as a
missing file. It now reads as loading until the re-query settles.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 13efe5b to 9cd4887CompareSeptember 3, 2026 09:33
@t3dotgg
t3dotgg merged commit 2aa907b into mainSep 3, 2026
27 checks passed
@t3dotgg
t3dotgg deleted the fix/mobile-asset-preview-error-state branch September 3, 2026 09:41
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 3, 2026
## What's Changed
* fix(mobile): show an error instead of an endless preview spinner by @t3dotgg in pingdotgg/t3code#9123
* fix(usage): price new models without waiting a day for the rate table by @t3dotgg in pingdotgg/t3code#9202
* fix(web): unlock the composer when preview capture fails by @t3dotgg in pingdotgg/t3code#9127
* fix(antigravity): refresh the model manifest so older Gemini models fold as legacy by @t3dotgg in pingdotgg/t3code#9397
* perf(ci): reuse dependency checks in release builds by @t3dotgg in pingdotgg/t3code#9399
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260903.1268...v0.0.39-nightly.20260903.1270
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260903.1270
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Mobile asset previews spin forever when asset URL creation or environment connection fails

1 participant

@t3dotgg
, '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

fix(mobile): show an error instead of an endless preview spinner - #9123

Merged
t3dotgg merged 2 commits into
mainfrom
fix/mobile-asset-preview-error-state
Sep 3, 2026
Merged

fix(mobile): show an error instead of an endless preview spinner#9123
t3dotgg merged 2 commits into
mainfrom
fix/mobile-asset-preview-error-state

Conversation

@t3dotgg

@t3dotggt3dotgg commented Sep 1, 2026

Copy link
Copy Markdown
Member

On mobile, an image, PDF, or other asset preview kept spinning on "Preparing ... preview" when the environment was disconnected or the asset URL request failed. useAssetUrlState reported a pending query and a dead connection as the same Loading value, so previews had no terminal state and no way to retry.

The hook now folds the environment connection phase into its result. A query that is still pending while the environment is offline, retrying, or in error resolves to Failure with a disconnected reason. A failed query resolves to Failure with a failed reason. The derivation moved into apps/mobile/src/state/asset-url-state.ts so it can be tested on its own.

On the file screen, image, SVG, browser, and video previews now render an error instead of a spinner when the asset URL will not arrive. A disconnected environment gets the existing EnvironmentConnectionNotice, the same offline surface the terminal and review sheets use, with its "Retry now" button wired to a connection retry plus a fresh URL mint. A failed URL gets a short "Preview unavailable" line with "Try again". The video preview lost its own unavailable branch because the screen now owns that state for every preview type.

The other consumers, the full screen file preview modal, both video preview modals, and the chat feed image and video, already branched on Failure, so they pick up the disconnected case without changes. The web app has its own copy of the hook in apps/web/src/assets/assetUrls.ts and is left alone to keep this to one concern.

Verified with vp test run apps/mobile/src/state/asset-url-state.test.ts, tsc --noEmit in apps/mobile, and lint on the touched files. Not verified on a device or simulator.

Closes#7630

Built by Claude Opus 5 in the Claude Code harness.


Note

Low Risk
Scoped to mobile asset URL presentation and thread file previews; logic is covered by unit tests and does not change auth or shared web hooks.

Overview
Mobile workspace file previews (image, browser/PDF, video) no longer spin forever when a signed asset URL cannot be obtained. useAssetUrlState now combines the shared URL query result with the environment connection phase via deriveAssetUrlState, yielding Failure with disconnected vs failed so UI can distinguish outage from a bad/missing asset.

On ThreadFileScreen, FileContent shows the new WorkspaceFilePreviewError for previews that depend on an asset URL: EnvironmentConnectionNotice (with connection retry + URL refresh) when disconnected, or a “Preview unavailable” empty state with Try again otherwise. Retry refreshes the asset URL and bumps previewRevision to remount the preview. WorkspaceFileVideoPreview drops its local unavailable UI because the parent owns that terminal state.

Unit tests cover deriveAssetUrlState; other mobile useAssetUrlState consumers that already treat Failure as an error surface should pick up disconnected cases without further changes.

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

Note

Fix endless mobile preview spinner by showing reason-specific asset URL errors

  • Adds WorkspaceFilePreviewError in WorkspaceFilePreviewError.tsx to render an environment connection notice for disconnected failures and a generic "Preview unavailable" retry state for other asset URL failures.
  • Adds deriveAssetUrlState in asset-url-state.ts to distinguish disconnected environment failures from connected query failures, and suppresses resolved URLs while the environment is offline, reconnecting, or in error.
  • Updates useAssetUrlState and useConnectionPhase in assets.ts to fold the shared asset result with the current connection phase, and treats a waiting post-outage query as Loading so stale failures are not shown.
  • Wires the typed failure reason and a retry handler (which remints the asset URL and remounts the preview) through FileContent and ThreadFileScreen in ThreadFilesRouteScreen.tsx, removing the old boolean previewUnavailable prop.
  • Risk: WorkspaceFileVideoPreview no longer renders its own "Video unavailable" state; video asset failures must now be handled by the parent before the media player renders. Existing consumers relying on the unavailable prop or the boolean previewUnavailable prop need updating.

Macroscope summarized 9cd4887.

@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). labels Sep 1, 2026
Comment threadapps/mobile/src/features/files/ThreadFilesRouteScreen.tsx Outdated
@github-actions

github-actionsBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.8 KiB13.6 KiB−189 B (−1.3%)15.1 KiB
CodexThread snapshot wire7.0 KiB7.0 KiB−14 B (−0.2%)7.3 KiB
CodexLive turn WebSocket wire6.7 KiB6.5 KiB−175 B (−2.5%)7.8 KiB
CodexLive turn WebSocket decoded58.5 KiB57.1 KiB−1.4 KiB (−2.4%)66.4 KiB
CodexLive turn messages10100 (0.0%)21
ClaudeTotal thread wire13.6 KiB13.5 KiB−28 B (−0.2%)15.1 KiB
ClaudeThread snapshot wire7.0 KiB7.0 KiB−7 B (−0.1%)7.3 KiB
ClaudeLive turn WebSocket wire6.5 KiB6.5 KiB−21 B (−0.3%)7.8 KiB
ClaudeLive turn WebSocket decoded57.8 KiB57.8 KiB−44 B (−0.1%)66.4 KiB
ClaudeLive turn messages98−1 (−11.1%)21

Baseline: 18573d6 · PR result: 9cd4887 · 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: 113.8 KiB
  • Claude decoded thread snapshot: 114.5 KiB

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

Comment threadapps/mobile/src/state/asset-url-state.ts
@macroscopeapp

macroscopeappBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 9cd4887

Macroscope's review found this PR approvable — This is a focused mobile bug fix that replaces terminal preview spinners with actionable connection or asset-unavailable states, including retry support. The shared hook’s broader impact is limited to mobile asset previews and is covered by explicit state-derivation tests, with no schema, deployment, security, billing, or static-analysis changes.

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

@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 6a78e63 to dc35525CompareSeptember 2, 2026 00:04
Comment threadapps/mobile/src/state/asset-url-state.ts
Comment threadapps/mobile/src/state/asset-url-state.ts Outdated
macroscopeapp[bot]
macroscopeappBot previously approved these changes Sep 2, 2026
@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 4b38589 to 13efe5bCompareSeptember 3, 2026 09:26
@macroscopeapp
macroscopeappBot dismissed their stale reviewSeptember 3, 2026 09:26

Dismissing prior approval to re-evaluate 13efe5b

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit 13efe5b. Configure here.

Comment threadapps/mobile/src/state/asset-url-state.ts
t3dotggand others added 2 commits September 3, 2026 02:33
When the connection returns, the URL query re-runs while its last
result is still the outage failure. That in-flight failure read as a
missing file. It now reads as loading until the re-query settles.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@t3dotgg
t3dotggforce-pushed the fix/mobile-asset-preview-error-state branch from 13efe5b to 9cd4887CompareSeptember 3, 2026 09:33
@t3dotgg
t3dotgg merged commit 2aa907b into mainSep 3, 2026
27 checks passed
@t3dotgg
t3dotgg deleted the fix/mobile-asset-preview-error-state branch September 3, 2026 09:41
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 3, 2026
## What's Changed
* fix(mobile): show an error instead of an endless preview spinner by @t3dotgg in pingdotgg/t3code#9123
* fix(usage): price new models without waiting a day for the rate table by @t3dotgg in pingdotgg/t3code#9202
* fix(web): unlock the composer when preview capture fails by @t3dotgg in pingdotgg/t3code#9127
* fix(antigravity): refresh the model manifest so older Gemini models fold as legacy by @t3dotgg in pingdotgg/t3code#9397
* perf(ci): reuse dependency checks in release builds by @t3dotgg in pingdotgg/t3code#9399
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260903.1268...v0.0.39-nightly.20260903.1270
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260903.1270
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Mobile asset previews spin forever when asset URL creation or environment connection fails

1 participant

@t3dotgg