Skip to content

Fix .ts files opening as media player in file preview - #4380

Closed
philip-zhan wants to merge 3 commits into
manaflow-ai:mainfrom
philip-zhan:fix-ts-file-opens-as-media
Closed

philip-zhan wants to merge 3 commits into
manaflow-ai:mainfrom
philip-zhan:fix-ts-file-opens-as-media

Conversation

@philip-zhan

@philip-zhan philip-zhan commented May 19, 2026 •

Copy link
Copy Markdown

Summary

  • .ts (TypeScript) files were opening in the AVPlayer-backed media preview instead of the text editor
  • On macOS, UTType(filenameExtension: "ts") resolves to public.mpeg-2-transport-stream, which conforms to .movie. FilePreviewKindResolver was checking media UTType conformance before consulting its own text-extension allowlist (which does contain "ts"), so the media branch always won
  • Reordered both initialResolution and resolvedResolution in Sources/Panels/FilePreviewPanel.swift so the explicit text-file allowlist wins over UTType-based media classification

Two-commit structure per the regression test policy: commit 1 adds the failing test, commit 2 applies the fix.

Test plan

  • CI: testTypeScriptExtensionResolvesToTextNotMediaPlayer red on commit 1, green on commit 2
  • Manually open a .ts file in the cmux file preview and confirm it opens in the text editor with the doc.text icon
  • Open a real .mov/.mp4 and confirm media playback still works
  • Open a binary .plist and confirm it still routes to QuickLook (not text)

🤖 Generated with Claude Code


Summary by cubic

Fixes .ts files opening in the media player by prioritizing the text extension allowlist over UTType-based media detection. TypeScript files now open in the text editor; preserves QuickLook for binary plist files and media behavior is unchanged.

  • Bug Fixes
    • Reordered resolution in FilePreviewPanel.swift: plist check first, then knownTextFile(...), then media UTType in the appropriate paths.
    • Added regression test testTypeScriptExtensionResolvesToTextNotMedia.

Written for commit 7f75b05. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Improved file preview classification so text-based files (e.g., TypeScript) are recognized and opened as text rather than media; adjusted detection precedence to favor text in ambiguous cases.
  • Tests
    • Added a unit test ensuring TypeScript files resolve to text preview and show the correct text icon.

Review Change Stack

philip-zhan and others added 2 commits May 19, 2026 11:59
Reproduces the bug where TypeScript files open in the AVPlayer-backed
media preview because macOS's UTType("ts") resolves to MPEG-2 transport
stream, and the resolver checks media-mode before the text-extension
allowlist.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The resolver was checking media UTType conformance before consulting the
text-extension allowlist. On macOS, UTType("ts") resolves to MPEG-2
transport stream (a movie type), so TypeScript files were routed to the
AVPlayer-backed media preview even though "ts" is explicitly listed in
textExtensions.

Reorder both initialResolution and resolvedResolution so the explicit
text-file allowlist wins over UTType-based media classification.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented May 19, 2026

Copy link
Copy Markdown

@philip-zhan is attempting to deploy a commit to the Manaflow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented May 19, 2026 •

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 47364688-3936-48b4-b913-4051ceec3e7d

📥 Commits

Reviewing files that changed from the base of the PR and between 4d04459 and 7f75b05.

📒 Files selected for processing (2)
  • Sources/Panels/FilePreviewPanel.swift
  • cmuxTests/WindowAndDragTests.swift

📝 Walkthrough

Walkthrough

FilePreviewKindResolver reorders text-file detection to take precedence over media-type inference in two resolver methods. The initialResolution method now checks for known-text files before plist-extension handling, and resolvedResolution checks for known-text before scanning inferred content types. A new test verifies TypeScript files resolve as text under this reordered logic.

Changes

Text-first file preview resolution

Layer / File(s) Summary
Text-first resolution precedence in initialResolution and resolvedResolution
Sources/Panels/FilePreviewPanel.swift
initialResolution(for:) moves the known-text check before plist-extension logic; resolvedResolution(for:) moves the known-text check before content-type-based media-mode scanning.
TypeScript text resolution verification
cmuxTests/WindowAndDragTests.swift
New test testTypeScriptExtensionResolvesToTextNotMediaPlayer confirms .ts files resolve to .text mode and "doc.text" icon under the reordered precedence.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A rabbit hops through filenames bright,
Finds TypeScript clearly marked as light,
Text is checked before the media song,
So previews show what they belong,
Quiet, plain, and typed just right.

🚥 Pre-merge checks | ✅ 16 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (16 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main bug being fixed: .ts files incorrectly opening as media player instead of in the text editor.
Description check ✅ Passed The description includes a comprehensive summary explaining the issue, root cause, and solution; includes testing methodology with a two-commit regression test structure; however, the test plan items lack confirmation status markers.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed No actor isolation issues introduced. FilePreviewKindResolver changes only reorder logic in private static utility methods with no new mutable state, declarations, or isolation violations.
Cmux Swift Blocking Runtime ✅ Passed PR changes to initialResolution and resolvedResolution reorder decision flow without introducing blocking/timing primitives. Added test is test-only code, allowed per rule.
Cmux No Hacky Sleeps ✅ Passed PR contains only Swift code changes. The "no hacky sleeps" check applies only to TypeScript, JavaScript, shell, and build scripts. Swift timing is covered by a separate rule.
Cmux Swift Concurrency ✅ Passed PR reorders decision logic in FilePreviewKindResolver and adds XCTest. No new legacy async patterns (DispatchQueue, Combine, completion handlers, fire-and-forget Tasks) were introduced.
Cmux Swift @Concurrent ✅ Passed PR modifies only private synchronous functions and test code; no async functions, @concurrent annotations, or actor isolation changes.
Cmux Swift File And Package Boundaries ✅ Passed Focused bug fix reordering FilePreviewKindResolver logic (net ±0) and adds 13-line regression test. Allowed case: incidental touch to existing file with small fix.
Cmux Swift Logging ✅ Passed PR adds FilePreviewKindResolver logic reordering and .ts file regression test with zero logging violations. No print/debugPrint/dump/NSLog found in production code.
Cmux User-Facing Error Privacy ✅ Passed No user-facing error messages added or modified. All localized text properly uses String(localized:). No sensitive information exposed in error/alert text.
Cmux Full Internationalization ✅ Passed PR contains no new user-facing strings in production code (FilePreviewPanel.swift reorders logic only) and adds only test code (WindowAndDragTests.swift), which is exempted from i18n requirements.
Cmux Swiftui State Layout ✅ Passed PR modifies enum static functions and adds unit tests; no new SwiftUI state patterns introduced or existing state modified. Check not applicable.
Cmux Architecture Rethink ✅ Passed Small correctness fix reordering checks in FilePreviewKindResolver. Clear owners/invariants, no timing/async/locks/observers to patch symptoms.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed Changes only modify FilePreviewKindResolver helper enum logic and test assertions; no NSWindow, NSPanel, NSWindowController, SwiftUI Window, or WindowGroup created or changed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@philip-zhan philip-zhan reopened this May 19, 2026
@greptile-apps

greptile-apps Bot commented May 19, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes .ts TypeScript files being classified by FilePreviewKindResolver as MPEG-2 transport-stream video because macOS's UTType(filenameExtension: "ts") resolves to public.mpeg-2-transport-stream which conforms to .movie. The fix reorders the two resolution functions so the text-extension allowlist (textExtensions, which already contained "ts") wins over UTType-based media detection.

  • initialResolution: UTType media check moved after knownTextFile; the ext == \"plist\" guard remains first, so binary-plist sniffing is unaffected.
  • resolvedResolution: knownTextFile (with includeResourceContentType: true) moved before the contentTypes media loop, so any file whose extension is in textExtensions routes to the text editor before media classification is consulted.
  • A new testTypeScriptExtensionResolvesToTextNotMediaPlayer regression test creates a real temporary .ts file and asserts all three public entry-points (initialMode, mode, tabIconName) resolve to the text path.

Confidence Score: 5/5

Safe to merge — the change is a minimal, targeted reordering of two guard blocks with no new state or side effects.

Both resolution functions now correctly route TypeScript files to the text editor. The ext == plist early-exit in initialResolution is still ordered before knownTextFile, so binary-plist sniffing is unaffected. The looksLikeBinaryPropertyList guard in resolvedResolution is likewise unchanged. A regression test exercises the fixed path end-to-end with a real temporary file, and the existing testBinaryPlistDoesNotOpenAsEditableText test continues to guard the plist path.

No files require special attention.

Important Files Changed

Filename Overview
Sources/Panels/FilePreviewPanel.swift Reorders two resolution functions so the textExtensions allowlist wins over UTType media detection; plist guard remains before knownTextFile, preserving binary-plist sniffing behaviour.
cmuxTests/WindowAndDragTests.swift Adds a targeted regression test for the .ts → text resolution fix; creates a real temporary file and asserts all three public resolver entry-points return the text path.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[URL with extension] --> B{initialResolution}
    A --> C{resolvedResolution}

    B --> B1{ext == plist?}
    B1 -->|Yes| B2[.needsSniff]
    B1 -->|No| B3{knownTextFile extension in textExtensions?}
    B3 -->|Yes| B4[.resolved .text]
    B3 -->|No| B5{UTType conforms to media?}
    B5 -->|Yes| B6[.resolved .video/.audio]
    B5 -->|No| B7[.needsSniff]

    C --> C1{ext==plist AND binary content?}
    C1 -->|Yes| C2[.resolved .quickLook]
    C1 -->|No| C3{knownTextFile with content type?}
    C3 -->|Yes| C4[.resolved .text]
    C3 -->|No| C5{contentTypes loop media conformance?}
    C5 -->|Match| C6[.resolved .video/.audio]
    C5 -->|No match| C7[.needsSniff]
Loading

Reviews (2): Last reviewed commit: "Preserve binary plist QuickLook initial ..." | Re-trigger Greptile

Comment on lines 710 to 726
@@ -718,10 +722,6 @@ enum FilePreviewKindResolver {
return .needsSniff
}

if knownTextFile(url: url, includeResourceContentType: false) {
return .resolved(.text)
}

return .needsSniff
}

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.

P1 .plist initial-mode regression from reorder

Moving knownTextFile to the top of initialResolution causes every .plist file — including binary ones — to immediately resolve to .text, because "plist" is present in textExtensions. The original code had the if ext == "plist" { return .needsSniff } guard specifically to force content sniffing before committing to text mode. That guard is now dead code: knownTextFile short-circuits to .resolved(.text) before the guard is ever reached.

The pre-existing test testBinaryPlistDoesNotOpenAsEditableText asserts XCTAssertEqual(FilePreviewKindResolver.initialMode(for: url), .quickLook), which will now fail. The resolvedResolution path is unaffected (looksLikeBinaryPropertyList still fires first there), but the initial speculative classification shown to the user regresses.

Suggested change
private static func initialResolution(for url: URL) -> Resolution {
let ext = url.pathExtension.lowercased()
if ext == "plist" {
return .needsSniff
}
if knownTextFile(url: url, includeResourceContentType: false) {
return .resolved(.text)
}
if let type = UTType(filenameExtension: ext),
let mediaMode = mediaMode(for: type) {
return .resolved(mediaMode)
}
return .needsSniff
}

Greptile flagged that moving knownTextFile before the plist guard in
initialResolution dead-coded the `ext == "plist"` branch, since "plist"
is in textExtensions. That would cause binary plists to flash as .text
in initialMode (regressing testBinaryPlistDoesNotOpenAsEditableText)
before resolvedResolution sniffed them back to .quickLook.

Check the plist extension first so plists always go through .needsSniff
and let resolvedResolution decide based on the bplist00 header.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 2 files

Re-trigger cubic

@teamleaderleo

Copy link
Copy Markdown
Collaborator

Thanks for this! .ts files now open as text previews landed on main in #4924. You opened this first, so you got there first. Closing since main covers it now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants