feat(web): preview document attachments in the file viewer - #9292

Merged
juliusmarminge merged 2 commits into
mainfrom
t3code/attachment-document-preview
Sep 3, 2026
Merged

feat(web): preview document attachments in the file viewer#9292
juliusmarminge merged 2 commits into
mainfrom
t3code/attachment-document-preview

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 3, 2026

Copy link
Copy Markdown
Member

What changed

  • Clicking a sent PDF or HTML attachment opens it in the right-panel file viewer, with a separate download button next to it. Other attachments download as before.
  • The attachment asset resource gains an optional disposition. A document viewer sends inline; generic attachments still download by default and the server only honors inline for application/pdf and text/html, with the same CSP HTML previews already get.
  • FilePreviewPanel takes an optional attachment and hides the workspace tree, breadcrumbs, editor, and "open in browser" affordances for it. shouldShowFileExplorer is the one place that decides whether the tree shows.
  • Attachment surfaces live under attachment:<id> in the right panel store so they never collide with a workspace file at a matching path, and they survive the workspace going away.

Why

Agents and users share PDFs and HTML reports as attachments all the time, and until now the only thing you could do with one was download it. Workspace files already preview in place; attachments should too.

Split out of #9253, which stays a refactor.

Testing

  • rightPanelStore.test.ts, MessagesTimeline.test.tsx, FilePreviewPanel.test.ts (web)
  • AssetAccess.test.ts, http.test.ts (server)
  • Web, server, and contracts 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
Changes attachment serving and inline HTML/PDF headers (security-sensitive), but extension checks and existing HTML sandboxing limit exposure; mostly UI and signed-URL claim logic.

Overview
PDF and HTML chat attachments can be opened in the right-panel file viewer on web/desktop, with a separate download control. Other file types still download when clicked.

The attachment asset contract adds optional disposition (inline / attachment). The preview panel requests inline; the server only honors it when the attachment id’s stored extension is pdf, html, or htm—not the client-supplied MIME type—so a zip cannot be forced inline. Inline PDF/HTML responses get the same Content-Type and HTML CSP behavior as workspace document previews; non-document inline requests stay download-forced.

Right panel gains openAttachment and attachment:<id> surfaces (disjoint from workspace file: tabs). Attachment previews work without an active workspace and survive workspace reconciliation. FilePreviewPanel accepts an optional attachment, loads a signed URL in a shared document iframe, and shouldShowFileExplorer hides the tree for attachments and host paths.

Messages timeline shows preview + download for browser-preview attachments (isBrowserPreviewAttachment) instead of a single download link.

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

@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 3, 2026
@github-actions

github-actionsBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.4 KiB13.3 KiB−148 B (−1.1%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB+3 B (+0.0%)7.3 KiB
CodexLive turn WebSocket wire6.5 KiB6.4 KiB−151 B (−2.3%)7.8 KiB
CodexLive turn WebSocket decoded57.0 KiB55.6 KiB−1.4 KiB (−2.5%)66.4 KiB
CodexLive turn messages10100 (0.0%)21
ClaudeTotal thread wire13.3 KiB13.5 KiB+204 B (+1.5%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−1 B (−0.0%)7.3 KiB
ClaudeLive turn WebSocket wire6.4 KiB6.6 KiB+205 B (+3.1%)7.8 KiB
ClaudeLive turn WebSocket decoded56.3 KiB57.8 KiB+1.5 KiB (+2.7%)66.4 KiB
ClaudeLive turn messages810+2 (+25.0%)21

Baseline: dbc7bfa · PR result: 807c0fe · 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.

Sent PDF and HTML attachments could only be downloaded. Clicking one now
opens it in the right-panel file viewer, with a download button beside
it. Other attachments download as before.
The attachment resource gains a disposition field so a document viewer
can ask for an inline response; generic attachments still download by
default and the server honors inline only for PDF and HTML. The file
viewer takes an optional attachment and hides the workspace tree,
breadcrumbs, and editor affordances for it. Attachment surfaces use
their own id namespace so they never collide with a workspace file at a
matching path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/attachment-document-preview branch from f346740 to d3f0821CompareSeptember 3, 2026 00:29
@juliusmarminge
juliusmarminge changed the base branch from t3code/mobile-media-viewers to mainSeptember 3, 2026 00:29

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Fix All in Cursor

Bugbot Autofix is ON, but a cloud agent failed to start.

Reviewed by Cursor Bugbot for commit d3f0821. Configure here.

Comment threadapps/server/src/assets/AssetAccess.ts
@macroscopeapp

macroscopeappBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This cross-layer feature changes the default handling of PDF/HTML attachments and adds inline document serving with browser iframe execution and new right-panel state. It also broadens an existing lint suppression while moving the PDF/HTML security boundary, so the runtime and review-policy implications require human review.

Not approved because:

  • Monthly spending limit reached (workspace setting). Approvability relies on correctness review in order to determine eligibility

Review your spending limits in Billing settings. You can add or adjust custom eligibility rules. Learn more.

…he client mime type
A viewer could pair disposition: inline with a text/html mime type and
have any attachment served as sandboxed HTML. The server now decides
inline eligibility from the extension in the attachment id it assigned,
and sets the mime type itself for those.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarminge merged commit 7751299 into mainSep 3, 2026
28 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/attachment-document-preview branch September 3, 2026 00:49
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 3, 2026
## What's Changed
* feat(desktop): browser profiles for the preview browser by @juliusmarminge in pingdotgg/t3code#7254
* refactor(shared): move the node:sqlite Effect SQL client into shared by @juliusmarminge in pingdotgg/t3code#7272
* feat(web): add opt-in panel animations by @maria-rcks in pingdotgg/t3code#8830
* feat(projects): automatically pull clean default branches by @maria-rcks in pingdotgg/t3code#9277
* fix(web): show pull request state icons in tabs by @flamboh in pingdotgg/t3code#9112
* feat(providers): add context compaction across harnesses by @maria-rcks in pingdotgg/t3code#8808
* feat(web): add proactive panels by @maria-rcks in pingdotgg/t3code#9276
* fix(web): unify control sizing across settings pages by @juliusmarminge in pingdotgg/t3code#9281
* fix(web): offer browser profiles from the empty-panel launcher by @juliusmarminge in pingdotgg/t3code#9279
* Revert "feat(providers): add context compaction across harnesses" by @maria-rcks in pingdotgg/t3code#9284
* fix(web): show scroll-to-end as soon as the last message slips under the composer by @juliusmarminge in pingdotgg/t3code#9280
* fix(cursor): honor auto and full access modes by @maria-rcks in pingdotgg/t3code#9283
* fix(desktop): detect installed Spectre libs for Windows builds by @juliusmarminge in pingdotgg/t3code#9285
* fix(pull-requests): missing features & better behaviour by @maria-rcks in pingdotgg/t3code#9188
* fix(providers): discover workspace skills everywhere by @maria-rcks in pingdotgg/t3code#9180
* fix(server): preserve automatic settlement timestamps by @eimexdev in pingdotgg/t3code#9254
* fix(opencode): show Reasoning selector for OpenCode models by @maria-rcks in pingdotgg/t3code#9287
* feat(web): preview document attachments in the file viewer by @juliusmarminge in pingdotgg/t3code#9292
* chore(ci): narrow the UI consistency check-run agent by @juliusmarminge in pingdotgg/t3code#9297
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1261...v0.0.39-nightly.20260903.1262
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260903.1262
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@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(web): preview document attachments in the file viewer - #9292

Merged
juliusmarminge merged 2 commits into
mainfrom
t3code/attachment-document-preview
Sep 3, 2026
Merged

feat(web): preview document attachments in the file viewer#9292
juliusmarminge merged 2 commits into
mainfrom
t3code/attachment-document-preview

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 3, 2026

Copy link
Copy Markdown
Member

What changed

  • Clicking a sent PDF or HTML attachment opens it in the right-panel file viewer, with a separate download button next to it. Other attachments download as before.
  • The attachment asset resource gains an optional disposition. A document viewer sends inline; generic attachments still download by default and the server only honors inline for application/pdf and text/html, with the same CSP HTML previews already get.
  • FilePreviewPanel takes an optional attachment and hides the workspace tree, breadcrumbs, editor, and "open in browser" affordances for it. shouldShowFileExplorer is the one place that decides whether the tree shows.
  • Attachment surfaces live under attachment:<id> in the right panel store so they never collide with a workspace file at a matching path, and they survive the workspace going away.

Why

Agents and users share PDFs and HTML reports as attachments all the time, and until now the only thing you could do with one was download it. Workspace files already preview in place; attachments should too.

Split out of #9253, which stays a refactor.

Testing

  • rightPanelStore.test.ts, MessagesTimeline.test.tsx, FilePreviewPanel.test.ts (web)
  • AssetAccess.test.ts, http.test.ts (server)
  • Web, server, and contracts 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
Changes attachment serving and inline HTML/PDF headers (security-sensitive), but extension checks and existing HTML sandboxing limit exposure; mostly UI and signed-URL claim logic.

Overview
PDF and HTML chat attachments can be opened in the right-panel file viewer on web/desktop, with a separate download control. Other file types still download when clicked.

The attachment asset contract adds optional disposition (inline / attachment). The preview panel requests inline; the server only honors it when the attachment id’s stored extension is pdf, html, or htm—not the client-supplied MIME type—so a zip cannot be forced inline. Inline PDF/HTML responses get the same Content-Type and HTML CSP behavior as workspace document previews; non-document inline requests stay download-forced.

Right panel gains openAttachment and attachment:<id> surfaces (disjoint from workspace file: tabs). Attachment previews work without an active workspace and survive workspace reconciliation. FilePreviewPanel accepts an optional attachment, loads a signed URL in a shared document iframe, and shouldShowFileExplorer hides the tree for attachments and host paths.

Messages timeline shows preview + download for browser-preview attachments (isBrowserPreviewAttachment) instead of a single download link.

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

@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 3, 2026
@github-actions

github-actionsBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.4 KiB13.3 KiB−148 B (−1.1%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB+3 B (+0.0%)7.3 KiB
CodexLive turn WebSocket wire6.5 KiB6.4 KiB−151 B (−2.3%)7.8 KiB
CodexLive turn WebSocket decoded57.0 KiB55.6 KiB−1.4 KiB (−2.5%)66.4 KiB
CodexLive turn messages10100 (0.0%)21
ClaudeTotal thread wire13.3 KiB13.5 KiB+204 B (+1.5%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−1 B (−0.0%)7.3 KiB
ClaudeLive turn WebSocket wire6.4 KiB6.6 KiB+205 B (+3.1%)7.8 KiB
ClaudeLive turn WebSocket decoded56.3 KiB57.8 KiB+1.5 KiB (+2.7%)66.4 KiB
ClaudeLive turn messages810+2 (+25.0%)21

Baseline: dbc7bfa · PR result: 807c0fe · 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.

Sent PDF and HTML attachments could only be downloaded. Clicking one now
opens it in the right-panel file viewer, with a download button beside
it. Other attachments download as before.
The attachment resource gains a disposition field so a document viewer
can ask for an inline response; generic attachments still download by
default and the server honors inline only for PDF and HTML. The file
viewer takes an optional attachment and hides the workspace tree,
breadcrumbs, and editor affordances for it. Attachment surfaces use
their own id namespace so they never collide with a workspace file at a
matching path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/attachment-document-preview branch from f346740 to d3f0821CompareSeptember 3, 2026 00:29
@juliusmarminge
juliusmarminge changed the base branch from t3code/mobile-media-viewers to mainSeptember 3, 2026 00:29

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Fix All in Cursor

Bugbot Autofix is ON, but a cloud agent failed to start.

Reviewed by Cursor Bugbot for commit d3f0821. Configure here.

Comment threadapps/server/src/assets/AssetAccess.ts
@macroscopeapp

macroscopeappBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This cross-layer feature changes the default handling of PDF/HTML attachments and adds inline document serving with browser iframe execution and new right-panel state. It also broadens an existing lint suppression while moving the PDF/HTML security boundary, so the runtime and review-policy implications require human review.

Not approved because:

  • Monthly spending limit reached (workspace setting). Approvability relies on correctness review in order to determine eligibility

Review your spending limits in Billing settings. You can add or adjust custom eligibility rules. Learn more.

…he client mime type
A viewer could pair disposition: inline with a text/html mime type and
have any attachment served as sandboxed HTML. The server now decides
inline eligibility from the extension in the attachment id it assigned,
and sets the mime type itself for those.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarminge merged commit 7751299 into mainSep 3, 2026
28 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/attachment-document-preview branch September 3, 2026 00:49
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 3, 2026
## What's Changed
* feat(desktop): browser profiles for the preview browser by @juliusmarminge in pingdotgg/t3code#7254
* refactor(shared): move the node:sqlite Effect SQL client into shared by @juliusmarminge in pingdotgg/t3code#7272
* feat(web): add opt-in panel animations by @maria-rcks in pingdotgg/t3code#8830
* feat(projects): automatically pull clean default branches by @maria-rcks in pingdotgg/t3code#9277
* fix(web): show pull request state icons in tabs by @flamboh in pingdotgg/t3code#9112
* feat(providers): add context compaction across harnesses by @maria-rcks in pingdotgg/t3code#8808
* feat(web): add proactive panels by @maria-rcks in pingdotgg/t3code#9276
* fix(web): unify control sizing across settings pages by @juliusmarminge in pingdotgg/t3code#9281
* fix(web): offer browser profiles from the empty-panel launcher by @juliusmarminge in pingdotgg/t3code#9279
* Revert "feat(providers): add context compaction across harnesses" by @maria-rcks in pingdotgg/t3code#9284
* fix(web): show scroll-to-end as soon as the last message slips under the composer by @juliusmarminge in pingdotgg/t3code#9280
* fix(cursor): honor auto and full access modes by @maria-rcks in pingdotgg/t3code#9283
* fix(desktop): detect installed Spectre libs for Windows builds by @juliusmarminge in pingdotgg/t3code#9285
* fix(pull-requests): missing features & better behaviour by @maria-rcks in pingdotgg/t3code#9188
* fix(providers): discover workspace skills everywhere by @maria-rcks in pingdotgg/t3code#9180
* fix(server): preserve automatic settlement timestamps by @eimexdev in pingdotgg/t3code#9254
* fix(opencode): show Reasoning selector for OpenCode models by @maria-rcks in pingdotgg/t3code#9287
* feat(web): preview document attachments in the file viewer by @juliusmarminge in pingdotgg/t3code#9292
* chore(ci): narrow the UI consistency check-run agent by @juliusmarminge in pingdotgg/t3code#9297
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1261...v0.0.39-nightly.20260903.1262
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260903.1262
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@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(web): preview document attachments in the file viewer - #9292

Merged
juliusmarminge merged 2 commits into
mainfrom
t3code/attachment-document-preview
Sep 3, 2026
Merged

feat(web): preview document attachments in the file viewer#9292
juliusmarminge merged 2 commits into
mainfrom
t3code/attachment-document-preview

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 3, 2026

Copy link
Copy Markdown
Member

What changed

  • Clicking a sent PDF or HTML attachment opens it in the right-panel file viewer, with a separate download button next to it. Other attachments download as before.
  • The attachment asset resource gains an optional disposition. A document viewer sends inline; generic attachments still download by default and the server only honors inline for application/pdf and text/html, with the same CSP HTML previews already get.
  • FilePreviewPanel takes an optional attachment and hides the workspace tree, breadcrumbs, editor, and "open in browser" affordances for it. shouldShowFileExplorer is the one place that decides whether the tree shows.
  • Attachment surfaces live under attachment:<id> in the right panel store so they never collide with a workspace file at a matching path, and they survive the workspace going away.

Why

Agents and users share PDFs and HTML reports as attachments all the time, and until now the only thing you could do with one was download it. Workspace files already preview in place; attachments should too.

Split out of #9253, which stays a refactor.

Testing

  • rightPanelStore.test.ts, MessagesTimeline.test.tsx, FilePreviewPanel.test.ts (web)
  • AssetAccess.test.ts, http.test.ts (server)
  • Web, server, and contracts 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
Changes attachment serving and inline HTML/PDF headers (security-sensitive), but extension checks and existing HTML sandboxing limit exposure; mostly UI and signed-URL claim logic.

Overview
PDF and HTML chat attachments can be opened in the right-panel file viewer on web/desktop, with a separate download control. Other file types still download when clicked.

The attachment asset contract adds optional disposition (inline / attachment). The preview panel requests inline; the server only honors it when the attachment id’s stored extension is pdf, html, or htm—not the client-supplied MIME type—so a zip cannot be forced inline. Inline PDF/HTML responses get the same Content-Type and HTML CSP behavior as workspace document previews; non-document inline requests stay download-forced.

Right panel gains openAttachment and attachment:<id> surfaces (disjoint from workspace file: tabs). Attachment previews work without an active workspace and survive workspace reconciliation. FilePreviewPanel accepts an optional attachment, loads a signed URL in a shared document iframe, and shouldShowFileExplorer hides the tree for attachments and host paths.

Messages timeline shows preview + download for browser-preview attachments (isBrowserPreviewAttachment) instead of a single download link.

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

@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 3, 2026
@github-actions

github-actionsBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.4 KiB13.3 KiB−148 B (−1.1%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB+3 B (+0.0%)7.3 KiB
CodexLive turn WebSocket wire6.5 KiB6.4 KiB−151 B (−2.3%)7.8 KiB
CodexLive turn WebSocket decoded57.0 KiB55.6 KiB−1.4 KiB (−2.5%)66.4 KiB
CodexLive turn messages10100 (0.0%)21
ClaudeTotal thread wire13.3 KiB13.5 KiB+204 B (+1.5%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−1 B (−0.0%)7.3 KiB
ClaudeLive turn WebSocket wire6.4 KiB6.6 KiB+205 B (+3.1%)7.8 KiB
ClaudeLive turn WebSocket decoded56.3 KiB57.8 KiB+1.5 KiB (+2.7%)66.4 KiB
ClaudeLive turn messages810+2 (+25.0%)21

Baseline: dbc7bfa · PR result: 807c0fe · 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.

Sent PDF and HTML attachments could only be downloaded. Clicking one now
opens it in the right-panel file viewer, with a download button beside
it. Other attachments download as before.
The attachment resource gains a disposition field so a document viewer
can ask for an inline response; generic attachments still download by
default and the server honors inline only for PDF and HTML. The file
viewer takes an optional attachment and hides the workspace tree,
breadcrumbs, and editor affordances for it. Attachment surfaces use
their own id namespace so they never collide with a workspace file at a
matching path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/attachment-document-preview branch from f346740 to d3f0821CompareSeptember 3, 2026 00:29
@juliusmarminge
juliusmarminge changed the base branch from t3code/mobile-media-viewers to mainSeptember 3, 2026 00:29

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Fix All in Cursor

Bugbot Autofix is ON, but a cloud agent failed to start.

Reviewed by Cursor Bugbot for commit d3f0821. Configure here.

Comment threadapps/server/src/assets/AssetAccess.ts
@macroscopeapp

macroscopeappBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This cross-layer feature changes the default handling of PDF/HTML attachments and adds inline document serving with browser iframe execution and new right-panel state. It also broadens an existing lint suppression while moving the PDF/HTML security boundary, so the runtime and review-policy implications require human review.

Not approved because:

  • Monthly spending limit reached (workspace setting). Approvability relies on correctness review in order to determine eligibility

Review your spending limits in Billing settings. You can add or adjust custom eligibility rules. Learn more.

…he client mime type
A viewer could pair disposition: inline with a text/html mime type and
have any attachment served as sandboxed HTML. The server now decides
inline eligibility from the extension in the attachment id it assigned,
and sets the mime type itself for those.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarminge merged commit 7751299 into mainSep 3, 2026
28 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/attachment-document-preview branch September 3, 2026 00:49
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 3, 2026
## What's Changed
* feat(desktop): browser profiles for the preview browser by @juliusmarminge in pingdotgg/t3code#7254
* refactor(shared): move the node:sqlite Effect SQL client into shared by @juliusmarminge in pingdotgg/t3code#7272
* feat(web): add opt-in panel animations by @maria-rcks in pingdotgg/t3code#8830
* feat(projects): automatically pull clean default branches by @maria-rcks in pingdotgg/t3code#9277
* fix(web): show pull request state icons in tabs by @flamboh in pingdotgg/t3code#9112
* feat(providers): add context compaction across harnesses by @maria-rcks in pingdotgg/t3code#8808
* feat(web): add proactive panels by @maria-rcks in pingdotgg/t3code#9276
* fix(web): unify control sizing across settings pages by @juliusmarminge in pingdotgg/t3code#9281
* fix(web): offer browser profiles from the empty-panel launcher by @juliusmarminge in pingdotgg/t3code#9279
* Revert "feat(providers): add context compaction across harnesses" by @maria-rcks in pingdotgg/t3code#9284
* fix(web): show scroll-to-end as soon as the last message slips under the composer by @juliusmarminge in pingdotgg/t3code#9280
* fix(cursor): honor auto and full access modes by @maria-rcks in pingdotgg/t3code#9283
* fix(desktop): detect installed Spectre libs for Windows builds by @juliusmarminge in pingdotgg/t3code#9285
* fix(pull-requests): missing features & better behaviour by @maria-rcks in pingdotgg/t3code#9188
* fix(providers): discover workspace skills everywhere by @maria-rcks in pingdotgg/t3code#9180
* fix(server): preserve automatic settlement timestamps by @eimexdev in pingdotgg/t3code#9254
* fix(opencode): show Reasoning selector for OpenCode models by @maria-rcks in pingdotgg/t3code#9287
* feat(web): preview document attachments in the file viewer by @juliusmarminge in pingdotgg/t3code#9292
* chore(ci): narrow the UI consistency check-run agent by @juliusmarminge in pingdotgg/t3code#9297
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1261...v0.0.39-nightly.20260903.1262
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260903.1262
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@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(web): preview document attachments in the file viewer - #9292

Merged
juliusmarminge merged 2 commits into
mainfrom
t3code/attachment-document-preview
Sep 3, 2026
Merged

feat(web): preview document attachments in the file viewer#9292
juliusmarminge merged 2 commits into
mainfrom
t3code/attachment-document-preview

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 3, 2026

Copy link
Copy Markdown
Member

What changed

  • Clicking a sent PDF or HTML attachment opens it in the right-panel file viewer, with a separate download button next to it. Other attachments download as before.
  • The attachment asset resource gains an optional disposition. A document viewer sends inline; generic attachments still download by default and the server only honors inline for application/pdf and text/html, with the same CSP HTML previews already get.
  • FilePreviewPanel takes an optional attachment and hides the workspace tree, breadcrumbs, editor, and "open in browser" affordances for it. shouldShowFileExplorer is the one place that decides whether the tree shows.
  • Attachment surfaces live under attachment:<id> in the right panel store so they never collide with a workspace file at a matching path, and they survive the workspace going away.

Why

Agents and users share PDFs and HTML reports as attachments all the time, and until now the only thing you could do with one was download it. Workspace files already preview in place; attachments should too.

Split out of #9253, which stays a refactor.

Testing

  • rightPanelStore.test.ts, MessagesTimeline.test.tsx, FilePreviewPanel.test.ts (web)
  • AssetAccess.test.ts, http.test.ts (server)
  • Web, server, and contracts 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
Changes attachment serving and inline HTML/PDF headers (security-sensitive), but extension checks and existing HTML sandboxing limit exposure; mostly UI and signed-URL claim logic.

Overview
PDF and HTML chat attachments can be opened in the right-panel file viewer on web/desktop, with a separate download control. Other file types still download when clicked.

The attachment asset contract adds optional disposition (inline / attachment). The preview panel requests inline; the server only honors it when the attachment id’s stored extension is pdf, html, or htm—not the client-supplied MIME type—so a zip cannot be forced inline. Inline PDF/HTML responses get the same Content-Type and HTML CSP behavior as workspace document previews; non-document inline requests stay download-forced.

Right panel gains openAttachment and attachment:<id> surfaces (disjoint from workspace file: tabs). Attachment previews work without an active workspace and survive workspace reconciliation. FilePreviewPanel accepts an optional attachment, loads a signed URL in a shared document iframe, and shouldShowFileExplorer hides the tree for attachments and host paths.

Messages timeline shows preview + download for browser-preview attachments (isBrowserPreviewAttachment) instead of a single download link.

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

@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 3, 2026
@github-actions

github-actionsBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.4 KiB13.3 KiB−148 B (−1.1%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB+3 B (+0.0%)7.3 KiB
CodexLive turn WebSocket wire6.5 KiB6.4 KiB−151 B (−2.3%)7.8 KiB
CodexLive turn WebSocket decoded57.0 KiB55.6 KiB−1.4 KiB (−2.5%)66.4 KiB
CodexLive turn messages10100 (0.0%)21
ClaudeTotal thread wire13.3 KiB13.5 KiB+204 B (+1.5%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−1 B (−0.0%)7.3 KiB
ClaudeLive turn WebSocket wire6.4 KiB6.6 KiB+205 B (+3.1%)7.8 KiB
ClaudeLive turn WebSocket decoded56.3 KiB57.8 KiB+1.5 KiB (+2.7%)66.4 KiB
ClaudeLive turn messages810+2 (+25.0%)21

Baseline: dbc7bfa · PR result: 807c0fe · 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.

Sent PDF and HTML attachments could only be downloaded. Clicking one now
opens it in the right-panel file viewer, with a download button beside
it. Other attachments download as before.
The attachment resource gains a disposition field so a document viewer
can ask for an inline response; generic attachments still download by
default and the server honors inline only for PDF and HTML. The file
viewer takes an optional attachment and hides the workspace tree,
breadcrumbs, and editor affordances for it. Attachment surfaces use
their own id namespace so they never collide with a workspace file at a
matching path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/attachment-document-preview branch from f346740 to d3f0821CompareSeptember 3, 2026 00:29
@juliusmarminge
juliusmarminge changed the base branch from t3code/mobile-media-viewers to mainSeptember 3, 2026 00:29

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Fix All in Cursor

Bugbot Autofix is ON, but a cloud agent failed to start.

Reviewed by Cursor Bugbot for commit d3f0821. Configure here.

Comment threadapps/server/src/assets/AssetAccess.ts
@macroscopeapp

macroscopeappBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This cross-layer feature changes the default handling of PDF/HTML attachments and adds inline document serving with browser iframe execution and new right-panel state. It also broadens an existing lint suppression while moving the PDF/HTML security boundary, so the runtime and review-policy implications require human review.

Not approved because:

  • Monthly spending limit reached (workspace setting). Approvability relies on correctness review in order to determine eligibility

Review your spending limits in Billing settings. You can add or adjust custom eligibility rules. Learn more.

…he client mime type
A viewer could pair disposition: inline with a text/html mime type and
have any attachment served as sandboxed HTML. The server now decides
inline eligibility from the extension in the attachment id it assigned,
and sets the mime type itself for those.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarminge merged commit 7751299 into mainSep 3, 2026
28 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/attachment-document-preview branch September 3, 2026 00:49
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 3, 2026
## What's Changed
* feat(desktop): browser profiles for the preview browser by @juliusmarminge in pingdotgg/t3code#7254
* refactor(shared): move the node:sqlite Effect SQL client into shared by @juliusmarminge in pingdotgg/t3code#7272
* feat(web): add opt-in panel animations by @maria-rcks in pingdotgg/t3code#8830
* feat(projects): automatically pull clean default branches by @maria-rcks in pingdotgg/t3code#9277
* fix(web): show pull request state icons in tabs by @flamboh in pingdotgg/t3code#9112
* feat(providers): add context compaction across harnesses by @maria-rcks in pingdotgg/t3code#8808
* feat(web): add proactive panels by @maria-rcks in pingdotgg/t3code#9276
* fix(web): unify control sizing across settings pages by @juliusmarminge in pingdotgg/t3code#9281
* fix(web): offer browser profiles from the empty-panel launcher by @juliusmarminge in pingdotgg/t3code#9279
* Revert "feat(providers): add context compaction across harnesses" by @maria-rcks in pingdotgg/t3code#9284
* fix(web): show scroll-to-end as soon as the last message slips under the composer by @juliusmarminge in pingdotgg/t3code#9280
* fix(cursor): honor auto and full access modes by @maria-rcks in pingdotgg/t3code#9283
* fix(desktop): detect installed Spectre libs for Windows builds by @juliusmarminge in pingdotgg/t3code#9285
* fix(pull-requests): missing features & better behaviour by @maria-rcks in pingdotgg/t3code#9188
* fix(providers): discover workspace skills everywhere by @maria-rcks in pingdotgg/t3code#9180
* fix(server): preserve automatic settlement timestamps by @eimexdev in pingdotgg/t3code#9254
* fix(opencode): show Reasoning selector for OpenCode models by @maria-rcks in pingdotgg/t3code#9287
* feat(web): preview document attachments in the file viewer by @juliusmarminge in pingdotgg/t3code#9292
* chore(ci): narrow the UI consistency check-run agent by @juliusmarminge in pingdotgg/t3code#9297
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1261...v0.0.39-nightly.20260903.1262
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260903.1262
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@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(web): preview document attachments in the file viewer - #9292

Merged
juliusmarminge merged 2 commits into
mainfrom
t3code/attachment-document-preview
Sep 3, 2026
Merged

feat(web): preview document attachments in the file viewer#9292
juliusmarminge merged 2 commits into
mainfrom
t3code/attachment-document-preview

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 3, 2026

Copy link
Copy Markdown
Member

What changed

  • Clicking a sent PDF or HTML attachment opens it in the right-panel file viewer, with a separate download button next to it. Other attachments download as before.
  • The attachment asset resource gains an optional disposition. A document viewer sends inline; generic attachments still download by default and the server only honors inline for application/pdf and text/html, with the same CSP HTML previews already get.
  • FilePreviewPanel takes an optional attachment and hides the workspace tree, breadcrumbs, editor, and "open in browser" affordances for it. shouldShowFileExplorer is the one place that decides whether the tree shows.
  • Attachment surfaces live under attachment:<id> in the right panel store so they never collide with a workspace file at a matching path, and they survive the workspace going away.

Why

Agents and users share PDFs and HTML reports as attachments all the time, and until now the only thing you could do with one was download it. Workspace files already preview in place; attachments should too.

Split out of #9253, which stays a refactor.

Testing

  • rightPanelStore.test.ts, MessagesTimeline.test.tsx, FilePreviewPanel.test.ts (web)
  • AssetAccess.test.ts, http.test.ts (server)
  • Web, server, and contracts 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
Changes attachment serving and inline HTML/PDF headers (security-sensitive), but extension checks and existing HTML sandboxing limit exposure; mostly UI and signed-URL claim logic.

Overview
PDF and HTML chat attachments can be opened in the right-panel file viewer on web/desktop, with a separate download control. Other file types still download when clicked.

The attachment asset contract adds optional disposition (inline / attachment). The preview panel requests inline; the server only honors it when the attachment id’s stored extension is pdf, html, or htm—not the client-supplied MIME type—so a zip cannot be forced inline. Inline PDF/HTML responses get the same Content-Type and HTML CSP behavior as workspace document previews; non-document inline requests stay download-forced.

Right panel gains openAttachment and attachment:<id> surfaces (disjoint from workspace file: tabs). Attachment previews work without an active workspace and survive workspace reconciliation. FilePreviewPanel accepts an optional attachment, loads a signed URL in a shared document iframe, and shouldShowFileExplorer hides the tree for attachments and host paths.

Messages timeline shows preview + download for browser-preview attachments (isBrowserPreviewAttachment) instead of a single download link.

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

@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 3, 2026
@github-actions

github-actionsBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.4 KiB13.3 KiB−148 B (−1.1%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB+3 B (+0.0%)7.3 KiB
CodexLive turn WebSocket wire6.5 KiB6.4 KiB−151 B (−2.3%)7.8 KiB
CodexLive turn WebSocket decoded57.0 KiB55.6 KiB−1.4 KiB (−2.5%)66.4 KiB
CodexLive turn messages10100 (0.0%)21
ClaudeTotal thread wire13.3 KiB13.5 KiB+204 B (+1.5%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−1 B (−0.0%)7.3 KiB
ClaudeLive turn WebSocket wire6.4 KiB6.6 KiB+205 B (+3.1%)7.8 KiB
ClaudeLive turn WebSocket decoded56.3 KiB57.8 KiB+1.5 KiB (+2.7%)66.4 KiB
ClaudeLive turn messages810+2 (+25.0%)21

Baseline: dbc7bfa · PR result: 807c0fe · 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.

Sent PDF and HTML attachments could only be downloaded. Clicking one now
opens it in the right-panel file viewer, with a download button beside
it. Other attachments download as before.
The attachment resource gains a disposition field so a document viewer
can ask for an inline response; generic attachments still download by
default and the server honors inline only for PDF and HTML. The file
viewer takes an optional attachment and hides the workspace tree,
breadcrumbs, and editor affordances for it. Attachment surfaces use
their own id namespace so they never collide with a workspace file at a
matching path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/attachment-document-preview branch from f346740 to d3f0821CompareSeptember 3, 2026 00:29
@juliusmarminge
juliusmarminge changed the base branch from t3code/mobile-media-viewers to mainSeptember 3, 2026 00:29

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Fix All in Cursor

Bugbot Autofix is ON, but a cloud agent failed to start.

Reviewed by Cursor Bugbot for commit d3f0821. Configure here.

Comment threadapps/server/src/assets/AssetAccess.ts
@macroscopeapp

macroscopeappBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This cross-layer feature changes the default handling of PDF/HTML attachments and adds inline document serving with browser iframe execution and new right-panel state. It also broadens an existing lint suppression while moving the PDF/HTML security boundary, so the runtime and review-policy implications require human review.

Not approved because:

  • Monthly spending limit reached (workspace setting). Approvability relies on correctness review in order to determine eligibility

Review your spending limits in Billing settings. You can add or adjust custom eligibility rules. Learn more.

…he client mime type
A viewer could pair disposition: inline with a text/html mime type and
have any attachment served as sandboxed HTML. The server now decides
inline eligibility from the extension in the attachment id it assigned,
and sets the mime type itself for those.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarminge merged commit 7751299 into mainSep 3, 2026
28 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/attachment-document-preview branch September 3, 2026 00:49
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 3, 2026
## What's Changed
* feat(desktop): browser profiles for the preview browser by @juliusmarminge in pingdotgg/t3code#7254
* refactor(shared): move the node:sqlite Effect SQL client into shared by @juliusmarminge in pingdotgg/t3code#7272
* feat(web): add opt-in panel animations by @maria-rcks in pingdotgg/t3code#8830
* feat(projects): automatically pull clean default branches by @maria-rcks in pingdotgg/t3code#9277
* fix(web): show pull request state icons in tabs by @flamboh in pingdotgg/t3code#9112
* feat(providers): add context compaction across harnesses by @maria-rcks in pingdotgg/t3code#8808
* feat(web): add proactive panels by @maria-rcks in pingdotgg/t3code#9276
* fix(web): unify control sizing across settings pages by @juliusmarminge in pingdotgg/t3code#9281
* fix(web): offer browser profiles from the empty-panel launcher by @juliusmarminge in pingdotgg/t3code#9279
* Revert "feat(providers): add context compaction across harnesses" by @maria-rcks in pingdotgg/t3code#9284
* fix(web): show scroll-to-end as soon as the last message slips under the composer by @juliusmarminge in pingdotgg/t3code#9280
* fix(cursor): honor auto and full access modes by @maria-rcks in pingdotgg/t3code#9283
* fix(desktop): detect installed Spectre libs for Windows builds by @juliusmarminge in pingdotgg/t3code#9285
* fix(pull-requests): missing features & better behaviour by @maria-rcks in pingdotgg/t3code#9188
* fix(providers): discover workspace skills everywhere by @maria-rcks in pingdotgg/t3code#9180
* fix(server): preserve automatic settlement timestamps by @eimexdev in pingdotgg/t3code#9254
* fix(opencode): show Reasoning selector for OpenCode models by @maria-rcks in pingdotgg/t3code#9287
* feat(web): preview document attachments in the file viewer by @juliusmarminge in pingdotgg/t3code#9292
* chore(ci): narrow the UI consistency check-run agent by @juliusmarminge in pingdotgg/t3code#9297
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1261...v0.0.39-nightly.20260903.1262
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260903.1262
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@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(web): preview document attachments in the file viewer - #9292

Merged
juliusmarminge merged 2 commits into
mainfrom
t3code/attachment-document-preview
Sep 3, 2026
Merged

feat(web): preview document attachments in the file viewer#9292
juliusmarminge merged 2 commits into
mainfrom
t3code/attachment-document-preview

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 3, 2026

Copy link
Copy Markdown
Member

What changed

  • Clicking a sent PDF or HTML attachment opens it in the right-panel file viewer, with a separate download button next to it. Other attachments download as before.
  • The attachment asset resource gains an optional disposition. A document viewer sends inline; generic attachments still download by default and the server only honors inline for application/pdf and text/html, with the same CSP HTML previews already get.
  • FilePreviewPanel takes an optional attachment and hides the workspace tree, breadcrumbs, editor, and "open in browser" affordances for it. shouldShowFileExplorer is the one place that decides whether the tree shows.
  • Attachment surfaces live under attachment:<id> in the right panel store so they never collide with a workspace file at a matching path, and they survive the workspace going away.

Why

Agents and users share PDFs and HTML reports as attachments all the time, and until now the only thing you could do with one was download it. Workspace files already preview in place; attachments should too.

Split out of #9253, which stays a refactor.

Testing

  • rightPanelStore.test.ts, MessagesTimeline.test.tsx, FilePreviewPanel.test.ts (web)
  • AssetAccess.test.ts, http.test.ts (server)
  • Web, server, and contracts 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
Changes attachment serving and inline HTML/PDF headers (security-sensitive), but extension checks and existing HTML sandboxing limit exposure; mostly UI and signed-URL claim logic.

Overview
PDF and HTML chat attachments can be opened in the right-panel file viewer on web/desktop, with a separate download control. Other file types still download when clicked.

The attachment asset contract adds optional disposition (inline / attachment). The preview panel requests inline; the server only honors it when the attachment id’s stored extension is pdf, html, or htm—not the client-supplied MIME type—so a zip cannot be forced inline. Inline PDF/HTML responses get the same Content-Type and HTML CSP behavior as workspace document previews; non-document inline requests stay download-forced.

Right panel gains openAttachment and attachment:<id> surfaces (disjoint from workspace file: tabs). Attachment previews work without an active workspace and survive workspace reconciliation. FilePreviewPanel accepts an optional attachment, loads a signed URL in a shared document iframe, and shouldShowFileExplorer hides the tree for attachments and host paths.

Messages timeline shows preview + download for browser-preview attachments (isBrowserPreviewAttachment) instead of a single download link.

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

@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 3, 2026
@github-actions

github-actionsBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.4 KiB13.3 KiB−148 B (−1.1%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB+3 B (+0.0%)7.3 KiB
CodexLive turn WebSocket wire6.5 KiB6.4 KiB−151 B (−2.3%)7.8 KiB
CodexLive turn WebSocket decoded57.0 KiB55.6 KiB−1.4 KiB (−2.5%)66.4 KiB
CodexLive turn messages10100 (0.0%)21
ClaudeTotal thread wire13.3 KiB13.5 KiB+204 B (+1.5%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−1 B (−0.0%)7.3 KiB
ClaudeLive turn WebSocket wire6.4 KiB6.6 KiB+205 B (+3.1%)7.8 KiB
ClaudeLive turn WebSocket decoded56.3 KiB57.8 KiB+1.5 KiB (+2.7%)66.4 KiB
ClaudeLive turn messages810+2 (+25.0%)21

Baseline: dbc7bfa · PR result: 807c0fe · 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.

Sent PDF and HTML attachments could only be downloaded. Clicking one now
opens it in the right-panel file viewer, with a download button beside
it. Other attachments download as before.
The attachment resource gains a disposition field so a document viewer
can ask for an inline response; generic attachments still download by
default and the server honors inline only for PDF and HTML. The file
viewer takes an optional attachment and hides the workspace tree,
breadcrumbs, and editor affordances for it. Attachment surfaces use
their own id namespace so they never collide with a workspace file at a
matching path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/attachment-document-preview branch from f346740 to d3f0821CompareSeptember 3, 2026 00:29
@juliusmarminge
juliusmarminge changed the base branch from t3code/mobile-media-viewers to mainSeptember 3, 2026 00:29

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Fix All in Cursor

Bugbot Autofix is ON, but a cloud agent failed to start.

Reviewed by Cursor Bugbot for commit d3f0821. Configure here.

Comment threadapps/server/src/assets/AssetAccess.ts
@macroscopeapp

macroscopeappBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This cross-layer feature changes the default handling of PDF/HTML attachments and adds inline document serving with browser iframe execution and new right-panel state. It also broadens an existing lint suppression while moving the PDF/HTML security boundary, so the runtime and review-policy implications require human review.

Not approved because:

  • Monthly spending limit reached (workspace setting). Approvability relies on correctness review in order to determine eligibility

Review your spending limits in Billing settings. You can add or adjust custom eligibility rules. Learn more.

…he client mime type
A viewer could pair disposition: inline with a text/html mime type and
have any attachment served as sandboxed HTML. The server now decides
inline eligibility from the extension in the attachment id it assigned,
and sets the mime type itself for those.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarminge merged commit 7751299 into mainSep 3, 2026
28 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/attachment-document-preview branch September 3, 2026 00:49
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 3, 2026
## What's Changed
* feat(desktop): browser profiles for the preview browser by @juliusmarminge in pingdotgg/t3code#7254
* refactor(shared): move the node:sqlite Effect SQL client into shared by @juliusmarminge in pingdotgg/t3code#7272
* feat(web): add opt-in panel animations by @maria-rcks in pingdotgg/t3code#8830
* feat(projects): automatically pull clean default branches by @maria-rcks in pingdotgg/t3code#9277
* fix(web): show pull request state icons in tabs by @flamboh in pingdotgg/t3code#9112
* feat(providers): add context compaction across harnesses by @maria-rcks in pingdotgg/t3code#8808
* feat(web): add proactive panels by @maria-rcks in pingdotgg/t3code#9276
* fix(web): unify control sizing across settings pages by @juliusmarminge in pingdotgg/t3code#9281
* fix(web): offer browser profiles from the empty-panel launcher by @juliusmarminge in pingdotgg/t3code#9279
* Revert "feat(providers): add context compaction across harnesses" by @maria-rcks in pingdotgg/t3code#9284
* fix(web): show scroll-to-end as soon as the last message slips under the composer by @juliusmarminge in pingdotgg/t3code#9280
* fix(cursor): honor auto and full access modes by @maria-rcks in pingdotgg/t3code#9283
* fix(desktop): detect installed Spectre libs for Windows builds by @juliusmarminge in pingdotgg/t3code#9285
* fix(pull-requests): missing features & better behaviour by @maria-rcks in pingdotgg/t3code#9188
* fix(providers): discover workspace skills everywhere by @maria-rcks in pingdotgg/t3code#9180
* fix(server): preserve automatic settlement timestamps by @eimexdev in pingdotgg/t3code#9254
* fix(opencode): show Reasoning selector for OpenCode models by @maria-rcks in pingdotgg/t3code#9287
* feat(web): preview document attachments in the file viewer by @juliusmarminge in pingdotgg/t3code#9292
* chore(ci): narrow the UI consistency check-run agent by @juliusmarminge in pingdotgg/t3code#9297
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1261...v0.0.39-nightly.20260903.1262
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260903.1262
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@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(web): preview document attachments in the file viewer - #9292

Merged
juliusmarminge merged 2 commits into
mainfrom
t3code/attachment-document-preview
Sep 3, 2026
Merged

feat(web): preview document attachments in the file viewer#9292
juliusmarminge merged 2 commits into
mainfrom
t3code/attachment-document-preview

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 3, 2026

Copy link
Copy Markdown
Member

What changed

  • Clicking a sent PDF or HTML attachment opens it in the right-panel file viewer, with a separate download button next to it. Other attachments download as before.
  • The attachment asset resource gains an optional disposition. A document viewer sends inline; generic attachments still download by default and the server only honors inline for application/pdf and text/html, with the same CSP HTML previews already get.
  • FilePreviewPanel takes an optional attachment and hides the workspace tree, breadcrumbs, editor, and "open in browser" affordances for it. shouldShowFileExplorer is the one place that decides whether the tree shows.
  • Attachment surfaces live under attachment:<id> in the right panel store so they never collide with a workspace file at a matching path, and they survive the workspace going away.

Why

Agents and users share PDFs and HTML reports as attachments all the time, and until now the only thing you could do with one was download it. Workspace files already preview in place; attachments should too.

Split out of #9253, which stays a refactor.

Testing

  • rightPanelStore.test.ts, MessagesTimeline.test.tsx, FilePreviewPanel.test.ts (web)
  • AssetAccess.test.ts, http.test.ts (server)
  • Web, server, and contracts 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
Changes attachment serving and inline HTML/PDF headers (security-sensitive), but extension checks and existing HTML sandboxing limit exposure; mostly UI and signed-URL claim logic.

Overview
PDF and HTML chat attachments can be opened in the right-panel file viewer on web/desktop, with a separate download control. Other file types still download when clicked.

The attachment asset contract adds optional disposition (inline / attachment). The preview panel requests inline; the server only honors it when the attachment id’s stored extension is pdf, html, or htm—not the client-supplied MIME type—so a zip cannot be forced inline. Inline PDF/HTML responses get the same Content-Type and HTML CSP behavior as workspace document previews; non-document inline requests stay download-forced.

Right panel gains openAttachment and attachment:<id> surfaces (disjoint from workspace file: tabs). Attachment previews work without an active workspace and survive workspace reconciliation. FilePreviewPanel accepts an optional attachment, loads a signed URL in a shared document iframe, and shouldShowFileExplorer hides the tree for attachments and host paths.

Messages timeline shows preview + download for browser-preview attachments (isBrowserPreviewAttachment) instead of a single download link.

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

@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 3, 2026
@github-actions

github-actionsBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.4 KiB13.3 KiB−148 B (−1.1%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB+3 B (+0.0%)7.3 KiB
CodexLive turn WebSocket wire6.5 KiB6.4 KiB−151 B (−2.3%)7.8 KiB
CodexLive turn WebSocket decoded57.0 KiB55.6 KiB−1.4 KiB (−2.5%)66.4 KiB
CodexLive turn messages10100 (0.0%)21
ClaudeTotal thread wire13.3 KiB13.5 KiB+204 B (+1.5%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−1 B (−0.0%)7.3 KiB
ClaudeLive turn WebSocket wire6.4 KiB6.6 KiB+205 B (+3.1%)7.8 KiB
ClaudeLive turn WebSocket decoded56.3 KiB57.8 KiB+1.5 KiB (+2.7%)66.4 KiB
ClaudeLive turn messages810+2 (+25.0%)21

Baseline: dbc7bfa · PR result: 807c0fe · 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.

Sent PDF and HTML attachments could only be downloaded. Clicking one now
opens it in the right-panel file viewer, with a download button beside
it. Other attachments download as before.
The attachment resource gains a disposition field so a document viewer
can ask for an inline response; generic attachments still download by
default and the server honors inline only for PDF and HTML. The file
viewer takes an optional attachment and hides the workspace tree,
breadcrumbs, and editor affordances for it. Attachment surfaces use
their own id namespace so they never collide with a workspace file at a
matching path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/attachment-document-preview branch from f346740 to d3f0821CompareSeptember 3, 2026 00:29
@juliusmarminge
juliusmarminge changed the base branch from t3code/mobile-media-viewers to mainSeptember 3, 2026 00:29

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Fix All in Cursor

Bugbot Autofix is ON, but a cloud agent failed to start.

Reviewed by Cursor Bugbot for commit d3f0821. Configure here.

Comment threadapps/server/src/assets/AssetAccess.ts
@macroscopeapp

macroscopeappBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This cross-layer feature changes the default handling of PDF/HTML attachments and adds inline document serving with browser iframe execution and new right-panel state. It also broadens an existing lint suppression while moving the PDF/HTML security boundary, so the runtime and review-policy implications require human review.

Not approved because:

  • Monthly spending limit reached (workspace setting). Approvability relies on correctness review in order to determine eligibility

Review your spending limits in Billing settings. You can add or adjust custom eligibility rules. Learn more.

…he client mime type
A viewer could pair disposition: inline with a text/html mime type and
have any attachment served as sandboxed HTML. The server now decides
inline eligibility from the extension in the attachment id it assigned,
and sets the mime type itself for those.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarminge merged commit 7751299 into mainSep 3, 2026
28 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/attachment-document-preview branch September 3, 2026 00:49
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 3, 2026
## What's Changed
* feat(desktop): browser profiles for the preview browser by @juliusmarminge in pingdotgg/t3code#7254
* refactor(shared): move the node:sqlite Effect SQL client into shared by @juliusmarminge in pingdotgg/t3code#7272
* feat(web): add opt-in panel animations by @maria-rcks in pingdotgg/t3code#8830
* feat(projects): automatically pull clean default branches by @maria-rcks in pingdotgg/t3code#9277
* fix(web): show pull request state icons in tabs by @flamboh in pingdotgg/t3code#9112
* feat(providers): add context compaction across harnesses by @maria-rcks in pingdotgg/t3code#8808
* feat(web): add proactive panels by @maria-rcks in pingdotgg/t3code#9276
* fix(web): unify control sizing across settings pages by @juliusmarminge in pingdotgg/t3code#9281
* fix(web): offer browser profiles from the empty-panel launcher by @juliusmarminge in pingdotgg/t3code#9279
* Revert "feat(providers): add context compaction across harnesses" by @maria-rcks in pingdotgg/t3code#9284
* fix(web): show scroll-to-end as soon as the last message slips under the composer by @juliusmarminge in pingdotgg/t3code#9280
* fix(cursor): honor auto and full access modes by @maria-rcks in pingdotgg/t3code#9283
* fix(desktop): detect installed Spectre libs for Windows builds by @juliusmarminge in pingdotgg/t3code#9285
* fix(pull-requests): missing features & better behaviour by @maria-rcks in pingdotgg/t3code#9188
* fix(providers): discover workspace skills everywhere by @maria-rcks in pingdotgg/t3code#9180
* fix(server): preserve automatic settlement timestamps by @eimexdev in pingdotgg/t3code#9254
* fix(opencode): show Reasoning selector for OpenCode models by @maria-rcks in pingdotgg/t3code#9287
* feat(web): preview document attachments in the file viewer by @juliusmarminge in pingdotgg/t3code#9292
* chore(ci): narrow the UI consistency check-run agent by @juliusmarminge in pingdotgg/t3code#9297
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1261...v0.0.39-nightly.20260903.1262
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260903.1262
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@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(web): preview document attachments in the file viewer - #9292

Merged
juliusmarminge merged 2 commits into
mainfrom
t3code/attachment-document-preview
Sep 3, 2026
Merged

feat(web): preview document attachments in the file viewer#9292
juliusmarminge merged 2 commits into
mainfrom
t3code/attachment-document-preview

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 3, 2026

Copy link
Copy Markdown
Member

What changed

  • Clicking a sent PDF or HTML attachment opens it in the right-panel file viewer, with a separate download button next to it. Other attachments download as before.
  • The attachment asset resource gains an optional disposition. A document viewer sends inline; generic attachments still download by default and the server only honors inline for application/pdf and text/html, with the same CSP HTML previews already get.
  • FilePreviewPanel takes an optional attachment and hides the workspace tree, breadcrumbs, editor, and "open in browser" affordances for it. shouldShowFileExplorer is the one place that decides whether the tree shows.
  • Attachment surfaces live under attachment:<id> in the right panel store so they never collide with a workspace file at a matching path, and they survive the workspace going away.

Why

Agents and users share PDFs and HTML reports as attachments all the time, and until now the only thing you could do with one was download it. Workspace files already preview in place; attachments should too.

Split out of #9253, which stays a refactor.

Testing

  • rightPanelStore.test.ts, MessagesTimeline.test.tsx, FilePreviewPanel.test.ts (web)
  • AssetAccess.test.ts, http.test.ts (server)
  • Web, server, and contracts 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
Changes attachment serving and inline HTML/PDF headers (security-sensitive), but extension checks and existing HTML sandboxing limit exposure; mostly UI and signed-URL claim logic.

Overview
PDF and HTML chat attachments can be opened in the right-panel file viewer on web/desktop, with a separate download control. Other file types still download when clicked.

The attachment asset contract adds optional disposition (inline / attachment). The preview panel requests inline; the server only honors it when the attachment id’s stored extension is pdf, html, or htm—not the client-supplied MIME type—so a zip cannot be forced inline. Inline PDF/HTML responses get the same Content-Type and HTML CSP behavior as workspace document previews; non-document inline requests stay download-forced.

Right panel gains openAttachment and attachment:<id> surfaces (disjoint from workspace file: tabs). Attachment previews work without an active workspace and survive workspace reconciliation. FilePreviewPanel accepts an optional attachment, loads a signed URL in a shared document iframe, and shouldShowFileExplorer hides the tree for attachments and host paths.

Messages timeline shows preview + download for browser-preview attachments (isBrowserPreviewAttachment) instead of a single download link.

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

@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 3, 2026
@github-actions

github-actionsBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.4 KiB13.3 KiB−148 B (−1.1%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB+3 B (+0.0%)7.3 KiB
CodexLive turn WebSocket wire6.5 KiB6.4 KiB−151 B (−2.3%)7.8 KiB
CodexLive turn WebSocket decoded57.0 KiB55.6 KiB−1.4 KiB (−2.5%)66.4 KiB
CodexLive turn messages10100 (0.0%)21
ClaudeTotal thread wire13.3 KiB13.5 KiB+204 B (+1.5%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−1 B (−0.0%)7.3 KiB
ClaudeLive turn WebSocket wire6.4 KiB6.6 KiB+205 B (+3.1%)7.8 KiB
ClaudeLive turn WebSocket decoded56.3 KiB57.8 KiB+1.5 KiB (+2.7%)66.4 KiB
ClaudeLive turn messages810+2 (+25.0%)21

Baseline: dbc7bfa · PR result: 807c0fe · 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.

Sent PDF and HTML attachments could only be downloaded. Clicking one now
opens it in the right-panel file viewer, with a download button beside
it. Other attachments download as before.
The attachment resource gains a disposition field so a document viewer
can ask for an inline response; generic attachments still download by
default and the server honors inline only for PDF and HTML. The file
viewer takes an optional attachment and hides the workspace tree,
breadcrumbs, and editor affordances for it. Attachment surfaces use
their own id namespace so they never collide with a workspace file at a
matching path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/attachment-document-preview branch from f346740 to d3f0821CompareSeptember 3, 2026 00:29
@juliusmarminge
juliusmarminge changed the base branch from t3code/mobile-media-viewers to mainSeptember 3, 2026 00:29

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Fix All in Cursor

Bugbot Autofix is ON, but a cloud agent failed to start.

Reviewed by Cursor Bugbot for commit d3f0821. Configure here.

Comment threadapps/server/src/assets/AssetAccess.ts
@macroscopeapp

macroscopeappBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This cross-layer feature changes the default handling of PDF/HTML attachments and adds inline document serving with browser iframe execution and new right-panel state. It also broadens an existing lint suppression while moving the PDF/HTML security boundary, so the runtime and review-policy implications require human review.

Not approved because:

  • Monthly spending limit reached (workspace setting). Approvability relies on correctness review in order to determine eligibility

Review your spending limits in Billing settings. You can add or adjust custom eligibility rules. Learn more.

…he client mime type
A viewer could pair disposition: inline with a text/html mime type and
have any attachment served as sandboxed HTML. The server now decides
inline eligibility from the extension in the attachment id it assigned,
and sets the mime type itself for those.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarminge merged commit 7751299 into mainSep 3, 2026
28 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/attachment-document-preview branch September 3, 2026 00:49
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 3, 2026
## What's Changed
* feat(desktop): browser profiles for the preview browser by @juliusmarminge in pingdotgg/t3code#7254
* refactor(shared): move the node:sqlite Effect SQL client into shared by @juliusmarminge in pingdotgg/t3code#7272
* feat(web): add opt-in panel animations by @maria-rcks in pingdotgg/t3code#8830
* feat(projects): automatically pull clean default branches by @maria-rcks in pingdotgg/t3code#9277
* fix(web): show pull request state icons in tabs by @flamboh in pingdotgg/t3code#9112
* feat(providers): add context compaction across harnesses by @maria-rcks in pingdotgg/t3code#8808
* feat(web): add proactive panels by @maria-rcks in pingdotgg/t3code#9276
* fix(web): unify control sizing across settings pages by @juliusmarminge in pingdotgg/t3code#9281
* fix(web): offer browser profiles from the empty-panel launcher by @juliusmarminge in pingdotgg/t3code#9279
* Revert "feat(providers): add context compaction across harnesses" by @maria-rcks in pingdotgg/t3code#9284
* fix(web): show scroll-to-end as soon as the last message slips under the composer by @juliusmarminge in pingdotgg/t3code#9280
* fix(cursor): honor auto and full access modes by @maria-rcks in pingdotgg/t3code#9283
* fix(desktop): detect installed Spectre libs for Windows builds by @juliusmarminge in pingdotgg/t3code#9285
* fix(pull-requests): missing features & better behaviour by @maria-rcks in pingdotgg/t3code#9188
* fix(providers): discover workspace skills everywhere by @maria-rcks in pingdotgg/t3code#9180
* fix(server): preserve automatic settlement timestamps by @eimexdev in pingdotgg/t3code#9254
* fix(opencode): show Reasoning selector for OpenCode models by @maria-rcks in pingdotgg/t3code#9287
* feat(web): preview document attachments in the file viewer by @juliusmarminge in pingdotgg/t3code#9292
* chore(ci): narrow the UI consistency check-run agent by @juliusmarminge in pingdotgg/t3code#9297
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1261...v0.0.39-nightly.20260903.1262
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260903.1262
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@juliusmarminge