Repository navigation
UI Consistency: 1 issue found
apps/web/src/components/chat/ChatComposer.tsx: the composer video tile still renders aPlay {name}control, but with the video branch removed fromChatView'sdownloadFileAttachment, theonFileOpenfallback used for rehydrated drafts (file.file === null+uploadedAttachmentId) now downloads the file instead of opening the player. The removedaria-busy/"Loading…" state on the same lines assumed the click still ended in the expanded dialog.
Previously reported findings in ExpandedImageDialog.tsx (loading/failure surface geometry and contrast) and MessagesTimeline.tsx (optimistic video tile, shared state panel height, stale previewUrl) are resolved at this head.
Details
Note
Your check run agent prompt is: .macroscope/check-run-agents/ui-consistency.md
More information about how Check Run Agents work can be found in our Help Center.
Files examined at head 452f18a: assets/assetUrls.ts, components/ChatMarkdown.tsx, components/ChatView.tsx, components/ChatView.logic.ts, components/chat/ChatComposer.tsx, components/chat/ExpandedImageDialog.tsx, components/chat/ExpandedImagePreview.tsx, components/chat/MessagesTimeline.tsx, components/media/MediaActions.tsx, components/media/MediaVideoPlayer.tsx, plus markdown-links.ts, filePathDisplay.ts, terminal-links.ts (pure logic extraction into @t3tools/client-runtime/markdown-links, no UI surface).
Checked and found acceptable:
MediaVideoPlayer's newstateClassNameis applied to both non-playing panels; the expanded dialog and timeline call sites now both pass surface-appropriate values (bg-black text-white,min-h-fullin the timeline so a wrapped failure panel can grow).MediaActionsdropping thehasActionsgate is safe because the item list now always containsSave {noun}, so the intercepted context menu is never empty;copy-imageis additionally gated on clipboard support.- Removing
onExpandfromMediaVideoPlayerleaves inline markdown and file-panel videos with the browser's own fullscreen control, matching the updateddocs/user/composer.md. UserVideoAttachmentresolves its asset fromactiveThreadEnvironmentIdrather than a global active-environment fallback.
🔍 Agent activity — 14 tool calls
💭 I'm checking the current state of changed web files at head 452f18a, noting that some earlier findings were moved elsewhere while MessagesTimeline changes still show in the diff. I'm going through the list of modified files under apps/web/src to see what's actually in scope.
1. browse_code — apps/web/src/components/media/MediaVideoPlayer.tsx
2. browse_code — apps/web/src/components/chat/ExpandedImageDialog.tsx
💭 The ExpandedImageDialog issues look resolved. For MessagesTimeline, the diff still shows UserVideoAttachment even though the author says that code moved to #9292 which is merged — likely because the merge base predates that merge, so main already has the changes but the diff against the older base still surfaces them as differences. I want to verify this against MessagesTimeline directly.
3. browse_code — apps/web/src/components/chat/MessagesTimeline.tsx
4. browse_code — apps/web/src/components/media/MediaActions.tsx
💭 I'm noticing that removing
hasActionsmeans the context menu now always opens on right-click for MediaActions-wrapped elements, since "save" is always available as an option, so the menu is never empty. For MediaVideoPlayer specifically, wrapping the whole player intercepts the browser's native video context menu (which normally offers download, playback speed, picture-in-picture) in favor of the custom media actions menu — this seems intentional given the docs describe hovering/right-clicking previews to copy references or save media. Now I'm checking other scope items: ChatMarkdown.tsx dropped the onExpand prop for videos since MediaVideoPlayer removed its expand button entirely, relying instead on native fullscreen controls per the docs update — I want to grep for any remaining onExpand call sites that might break. Also ChatComposer.tsx removed the aria-busy/loading state for the composer video tile, so clicking now opens the dialog immediately with a null src, letting the dialog's own loading placeholder handle that state instead.
5. git_tools — MediaVideoPlayer|onExpand|onImageExpand
💭 I want to check what clicking the ChatComposer video tile at the head does now, and whether removing the loading state leaves a gap—since the expanded dialog mints and shows its own loading placeholder, it's probably fine. Let me look at the composer code near line 4020.
6. browse_code — apps/web/src/components/chat/ChatComposer.tsx
💭 I'm tracing how the composer handles video clicks—local files open a blob preview dialog directly, while uploaded ones go through onFileOpen. I want to verify whether ChatView's onFileOpen still has special video handling or if that branch was removed so it just downloads now, so I need to check the current code around line 2600.
7. browse_code — apps/web/src/components/ChatView.tsx
💭 I'm tracing whether uploaded composer videos now trigger a download instead of opening the expanded video dialog like before, checking if this is a behavioral regression tied to how remote versus local file objects are distinguished. Actually, this looks like a real inconsistency: the play button's aria-label still says "Play" but the actual click action just downloads the file, likely because the video-preview branch got removed from openFileAttachment in ChatView when timeline videos moved to inline playback. Composer drafts restored after reload without a local file blob fall into this gap, so I'm wondering whether a proper expanded preview should be reconstructed for that case instead.
8. browse_code — apps/web/src/components/chat/ExpandedImagePreview.tsx
💭 I'm tracing through why composer local videos still open in the dialog rather than a different view — checking whether the asset URL state's initial/failure tag logic incorrectly falls through to treat the blob source as failed when no asset or action source exists. For null environmentId, the empty asset URL atom stays in "Initial" state rather than "Failure," so sourceFailed evaluates to false, which seems correct. But then I notice a separate issue: for a markdown video that already has a minted URL and an existing asset, ExpandedVideo ignores that already-available src and re-mints the asset URL anyway, which could cause an unnecessary loading flash on open even though the video was ready to display immediately. Actually I realize this path may not even be reachable anymore since ChatMarkdown no longer wires up onExpand for videos, so I need to trace which components actually open the expanded video dialog with an asset-backed item — checking whether timeline attachments, media failure panels, or markdown media previews still trigger that flow before concluding this is a real regression worth flagging.
9. git_tools — apps/web/src/** 7751299..452f18a
💭 I'm noticing that in ChatComposer, the video tile's "Play" aria-label now triggers a download instead of opening the video dialog for drafts without a local file, since that branch was removed from ChatView's openFileAttachment—a behavior regression with a mislabeled control that's within scope since ChatComposer.tsx falls under apps/web/src/**/*.tsx. I need to verify whether the composer's file.file can actually become null—likely after a persisted draft is restored across a reload, since ComposerFileAttachment stores file as File | null. I also want to check whether the timeline's video attachments behave differently, since they no longer call onFileOpen while the composer still does.
10. git_tools — file: File | null|uploadedAttachmentId
💭 I've confirmed that a persisted draft rehydrates with
file: nullbut a saveduploadedAttachmentId, so clicking "Play" on a video tile now routes throughopenFileAttachmentand falls into the non-preview branch, which just downloads the file instead of playing it. This is a regression—previouslydownloadFileAttachmenthad a video branch that opened the expanded video dialog instead. I want to check whether video counts as a browser-preview attachment by looking atisBrowserPreviewAttachment.
11. git_tools — export function isBrowserPreviewAttachment
💭 I've confirmed the composer's "Play" button on a rehydrated uploaded video is downloading instead of playing—a real regression rooted in ChatComposer.tsx and ChatView.tsx. The fix is to route through onExpandImage with a proper attachment video asset (null src plus actionsSource built via buildAttachmentVideoAsset) so it matches the intended preview behavior. Writing the click handler logic... I'm also noting a wrinkle: which environmentId to use (file's own upload environment vs. the composer-level one), and I need to check whether buildAttachmentVideoAsset is already imported in ChatComposer before finalizing the diff.
12. git_tools — ExpandedImagePreview|composerVideos =|const environmentId
💭 I'm checking the logic for
isPreviewableComposerVideoto see whether it requires either a local file or an uploaded attachment tied to this environment.
13. browse_code — In apps/web, what does isPreviewableComposerVideo do (definition and conditions)? And in the current head of branch t3code/mobile-media-viewers, for a composer video draft attachment whose local file is null but which has an uploadedAttachmentId, what happens when the user clicks the composer video tile's "Play" button — does anything play the video, or does it download?
💭 This confirms a real regression, since that Play-labelled path used to actually play, and the tile also loses its thumbnail. I'm setting aside the MessagesTimeline image tile change since the author said that one was intentional, and moving to look at MediaActions now always intercepting the contextmenu, even for videos without a reference. For timeline attachment videos, right-clicking now suppresses the native browser video menu (Download, Picture-in-Picture, playback speed) in favor of the app's own menu, which only offers "Save video" — this seems consistent with the documented intent, so not a finding. I'm also checking canCopyImage guard logic, which looks fine, and now looking at the stateClassName prop shared between loading and failure panels in ExpandedImageDialog. The failure panel uses a fixed h-48 height with min-h-0 overriding the earlier min-h-28, so if the file name label is long and the viewport is narrow (down to ~300px at 92vw), the wrapped label text plus wrapped Retry/Open link buttons could add up to roughly the full 192px height with padding — a borderline overflow risk similar to what I flagged in MessagesTimeline, since there's no overflow-hidden to contain any spillover past the caption. Doing the width math for the failure text at 92vw, it looks like it wraps to about two lines and fits within the available height, so I'll skip flagging it. Next I want to check the sixth case: ExpandedVideo's
sourceFailedcheck when there's no asset URL yet, verifying whether the atom resolves to "Loading" initially rather than a failure state. Confirming the tag maps correctly, sosourceFailedstays false during that initial phase, which is correct. Now looking at the direct/blob video path, tracing how MediaVideoPlayer's local failure state interacts with the retry button and confirming retry correctly resets playback state and triggers a reload attempt. I'm now checking whether ExpandedVideo can actually receive a video item that already has both an asset and a minted src, since that would briefly force a redundant re-mint on open — tracing back through resolveMarkdownMediaPreview's callers to see if that scenario is reachable in practice, and whether the underlying atom cache would mask it anyway. The removal of onImageExpand from ChatMarkdownVideo means inline markdown videos now rely solely on the native video element's fullscreen control, and that matches the doc update — ExpandedImagePreview is still used elsewhere so its import stays valid, and MediaVideoPlayer's failure panel dropping the expand button is likewise fine. The main new finding worth flagging is about the composer. I need to find a valid diff line for the review comment — line 4037, where the code calls onFileOpen with the uploaded attachment id, sits as context within the hunk range (roughly 4018-4050), so that should work since GitHub allows commenting on context lines too. The aria-label line with the "Play" text is another candidate worth considering. Timeline rows correctly use the bound thread's environment viaactiveThreadEnvironmentId, so that's fine. But I noticeuseAssetUrlStatenow mints an attachment URL eagerly for every video in the timeline, whereas before URLs were only created on click — that's a real behavior change worth flagging. I'm drafting a comment about a labeling issue inChatComposer.tsx: a tile labeledPlay {name}for a rehydrated draft no longer actually plays, since the video-open logic that used to route to the expanded player was removed fromdownloadFileAttachment, so clicking now just downloads the file instead. The smallest fix would be to build the attachment's video asset and pass it into the item's action source soExpandedVideocan mint and play it, or otherwise just relabel the button as a download. I'm deciding against a diff here since the fix needs an added import alongside the inline change, so I'll leave it as a written suggestion rather than a formal patch. For the review outcome, this is a real functional regression on a labeled control, so I'll mark the check as failure rather than success.
14. modify_pr — Submitted review with 1 inline comment
Agent Credits: 468 credits
Token usage: 84 uncached input · 3,616,882 cache-read input · 315,736 cache-write input · 26,956 output
Agent Credits may also include non-token charges from external tools such as web research.