Skip to content

Resolve TypeScript .ts files as text previews - #4924

Merged
lawrencecchen merged 10 commits into
mainfrom
task-ts-files-open-typescript
May 28, 2026
Merged

lawrencecchen merged 10 commits into
mainfrom
task-ts-files-open-typescript

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented May 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Sniff ambiguous source/media extensions before routing file previews to media.
  • Add regression coverage for textual .ts files and binary MPEG transport streams.

Testing

  • ./scripts/reload.sh --tag tsfiles (pass)

Issues

  • Task: ensure ts files open as TypeScript instead of video files

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag @codesmith with what you need. Autofix is disabled.


Note

Low Risk
Changes are isolated to file preview kind resolution with broad unit tests; wrong sniffing could mis-route rare .ts or other ambiguous extensions but does not touch auth or persistence.

Overview
File preview routing now defers choosing text vs media when an extension is ambiguous—especially .ts, where macOS often classifies the file as MPEG transport stream instead of TypeScript.

FilePreviewKindResolver forces a content sniff up front for those cases (initial mode stays Quick Look, then async resolution). After sniffing, text wins if the bytes look like text; otherwise it checks for real transport streams via 0x47 sync-byte patterns (188/192/204-byte packets) before falling back to media. Text sniffing is stricter: NUL bytes reject text, UTF-16 BOM is handled explicitly, and valid UTF-8 is required for the plain-UTF-8 path.

Regression tests cover TypeScript .ts (including UTF-8 BOM and sync-byte-like ASCII), extensionless ANSI/UTF-16 text, and binary/M2TS .ts staying on media preview.

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


Summary by cubic

Ensures .ts files open as TypeScript text instead of MPEG transport streams. Initial preview stays Quick Look; final resolves to text for source and to media for real transport streams.

  • Bug Fixes
    • Sniff before choosing text vs media when a text-like extension maps to media (including .ts); text sniff rejects NUL bytes, allows ANSI, and accepts UTF‑16/UTF‑8 BOM; initial mode is Quick Look, then resolves to text if content is textual.
    • Detect real transport streams via sync-byte packet patterns (188/192/204); textual .ts with sync-like bytes still resolve to text; tests cover ANSI/extensionless text, UTF‑8 BOM, NUL‑byte .ts, plain textual .ts, and binary transport streams.

Written for commit db19dd1. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • More accurate preview mode for ambiguous file extensions by forcing content sniffing earlier, improving detection of text vs media (including transport-stream-like binaries and null-byte cases).
  • Tests

    • Added tests verifying preview resolution for ambiguous extensions, covering text-encoded files, BOM/null-byte detection, and transport-stream-like binary vs text scenarios.

Review Change Stack

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@vercel

vercel Bot commented May 28, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Canceled Canceled May 28, 2026 6:16am
cmux-staging Building Building Preview, Comment May 28, 2026 6:16am

@coderabbitai

coderabbitai Bot commented May 28, 2026 •

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

FilePreviewKindResolver defers resolution for certain known-text extensions (e.g., .ts) by returning .needsSniff, then sniffs for text (with null-byte/UTF-16/UTF-8 checks) and for MPEG‑TS sync bytes to decide between .text or .media; tests validate .ts cases.

Changes

File Type Ambiguity Handling for Text/Media Extensions

Layer / File(s) Summary
Ambiguity predicate and resolution logic
Sources/Panels/FilePreviewPanel.swift
Adds needsSniffBeforeTextOrMedia(url:) (special-casing .ts), integrates it into initialResolution(for:) to return .needsSniff, updates resolvedResolution(for:) to sniff text first, then check looksLikeMPEGTransportStream(url:), then fall back to contentTypes(for:); adjusts sniffLooksLikeText(url:) ordering (null-byte before UTF‑8 decode, UTF‑16 BOM still first).
Tests for .ts disambiguation
cmuxTests/FilePreviewReviewFeedbackTests.swift
Adds five XCTest cases exercising extensionless and .ts payloads: UTF‑8 text, UTF‑8 with BOM, text shaped like TS sync pattern (expected .text), and binary TS-shaped sync data (expected .media); all assert initial .quickLook before sniffing.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • manaflow-ai/cmux#4883: Related test-suite changes and CI stabilization touching the same FilePreview review tests.

Poem

🐰 I found a .ts upon the trail,
Could be code or packets in a gale.
I sniffed the start, counted sync and null,
Chose text or stream with a curious mull.
Hoppity-hop — the preview now knows which way to sail!


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (2 errors, 1 warning)

Check name Status Explanation Resolution
Cmux Swift @Concurrent ❌ Error Three file I/O-heavy async functions (resolveMode, load, save) lack @concurrent annotations but are called from @MainActor context without explicit hop protection. Add @concurrent to FilePreviewKindResolver.resolveMode, FilePreviewTextLoader.load, and FilePreviewTextSaver.save to properly mark file-I/O work leaving MainActor.
Cmux Swiftui State Layout ❌ Error PR introduces new ObservableObject with @Published state (FilePreviewPanel) where @Observable is the modern shape; FileExternalOpenMenu also adds new state. Violates swiftui-state-layout.md rule. Replace ObservableObject+@published with @Observable+@State pattern in FilePreviewPanel and review all new SwiftUI views for modern state patterns.
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 (15 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and specifically describes the main change: resolving .ts files to open as text previews rather than media, which is the core objective of the PR.
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 Changes limited to private static methods in FilePreviewKindResolver enum. No implicit MainActor types, unsafe Sendable references, or existing debt worsened introduced.
Cmux Swift Blocking Runtime ✅ Passed PR introduces file-sniffing logic for .ts extension resolution without introducing blocking/timing synchronization primitives; uses only synchronous file I/O and test helper with Task.yield().
Cmux No Hacky Sleeps ✅ Passed PR contains only Swift files (FilePreviewPanel.swift and test file); Swift timing is explicitly excluded from this check per the rule scope.
Cmux Algorithmic Complexity ✅ Passed Uses fixed-size collections (9+30 items), per-file operations, bounded 4KB file reads, async background Tasks—no nested collection scans or batch rescans per algorithmic complexity rules.
Cmux Swift Concurrency ✅ Passed PR introduces only synchronous file I/O helper functions for sniffing .ts file content; no new async patterns, DispatchQueues, Combine, or fire-and-forget Tasks are introduced or expanded.
Cmux Swift File And Package Boundaries ✅ Passed PR adds 54 lines to existing oversized FilePreviewPanel.swift, well under 250-line threshold; focused sniffing feature maintains clear responsibility separation.
Cmux Swift Logging ✅ Passed PR adds file-type sniffing logic and tests with zero logging violations: no print, debugPrint, dump, NSLog, or ad-hoc logging in production or test code.
Cmux User-Facing Error Privacy ✅ Passed All production code changes are internal helper functions with no user-facing errors, alerts, vendor names, or sensitive data. Test code is developer-only with no user-visible strings.
Cmux Full Internationalization ✅ Passed Production code contains only internal file-type detection logic with no user-facing strings. Test code is exempt from i18n requirements. No string catalogs modified.
Cmux Architecture Rethink ✅ Passed Changes are small correctness fixes to file preview routing with clear ownership in FilePreviewKindResolver. No timing repairs, new locks, observers, or split lifecycle ownership introduced.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR adds no NSWindow, NSPanel, NSWindowController, SwiftUI Window, or WindowGroup code. Changes are limited to FilePreviewKindResolver utility methods for file sniffing logic and test fixtures.
Description check ✅ Passed The PR description covers the summary (what changed and why) and testing approach, but is missing several template sections including demo video, review trigger commands, and a complete checklist.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch task-ts-files-open-typescript

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.

@cursor cursor 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.

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

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit b4df44d. Configure here.

Comment thread Sources/Panels/FilePreviewPanel.swift
@greptile-apps

greptile-apps Bot commented May 28, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes .ts files being opened as MPEG transport streams instead of TypeScript source by inserting a content-sniff gate before the UTType-based media routing. FilePreviewKindResolver gains needsSniffBeforeTextOrMedia, looksLikeMPEGTransportStream, and an updated sniffLooksLikeText (NUL check now precedes UTF-8 decode) to correctly distinguish text from binary .ts payloads.

  • needsSniffBeforeTextOrMedia short-circuits initialResolution for every .ts URL, returning .needsSniff (→ Quick Look) instead of immediately routing to .media; resolvedResolution runs the text → MPEG-TS → content-type fallback chain for the same files.
  • sniffLooksLikeText reordering places the NUL-byte guard before the UTF-8 decode, fixing the previously reported path where valid-ASCII data (e.g. MPEG-TS sync headers) passed the UTF-8 check and was wrongly classified as text.
  • Regression tests cover ANSI extensionless text, plain TypeScript, UTF-8 BOM, NUL-byte rejection, sync-byte-pattern TypeScript (text wins), and binary 188-byte and M2TS 192-byte transport streams.

Confidence Score: 5/5

Safe to merge — the change is tightly scoped to file-preview kind resolution and the previously broken NUL-byte ordering in sniffLooksLikeText is now correct.

All three issues raised in previous review rounds have been addressed: the NUL-byte guard now precedes the UTF-8 decode, the binary fixture uses NUL-padded MPEG-TS-sized packets that bypass the ASCII-range UTF-8 path, and the binary .ts initial-mode assertion is present. The new helpers are logically correct, the fallback chain is well-ordered (text wins over MPEG-TS detection), and the six new regression tests cover the full decision surface including sync-byte-like TypeScript content and M2TS 192-byte packets.

No files require special attention.

Important Files Changed

Filename Overview
Sources/Panels/FilePreviewPanel.swift Adds needsSniffBeforeTextOrMedia, looksLikeMPEGTransportStream helpers and fixes sniffLooksLikeText NUL-byte ordering; logic is correct and the new sniff gate properly intercepts .ts before UTType media routing.
cmuxTests/FilePreviewReviewFeedbackTests.swift Adds six regression tests covering the text/transport-stream disambiguation; binary fixtures now correctly include NUL bytes so the UTF-8 decode path is bypassed and the null-byte guard fires first.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[URL with .ts extension] --> B{needsSniffBeforeTextOrMedia?}
    B -- Yes --> C[initialResolution returns .needsSniff\ninitialMode returns .quickLook]
    B -- No --> Z[existing UTType / knownTextFile path]
    C --> D[resolvedResolution sniff block]
    D --> E{sniffLooksLikeText?}
    E -- UTF-16 BOM valid UTF-16 true --> F[.resolved .text]
    E -- NUL bytes present false --> G{looksLikeMPEGTransportStream?}
    E -- valid UTF-8 no NULs true --> F
    G -- sync pattern found --> H[.resolved .media]
    G -- no pattern or too small --> I{contentTypes mediaMode?}
    I -- UTType maps to MPEG-TS --> H
    I -- no media type --> J[.needsSniff returns .quickLook]
Loading

Reviews (7): Last reviewed commit: "Handle m2ts preview sniffing" | Re-trigger Greptile

Comment on lines +92 to +95
try Data([0x47, 0x40, 0x00, 0x10, 0x00, 0x47, 0x41, 0x00, 0x10, 0x00])
.write(to: url, options: .atomic)

XCTAssertEqual(FilePreviewKindResolver.mode(for: url), .media)

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 Binary test data is entirely valid UTF-8 — sniff returns true, mode resolves to .text

Every byte in [0x47, 0x40, 0x00, 0x10, 0x00, 0x47, 0x41, 0x00, 0x10, 0x00] is in the 0x00–0x7F single-byte UTF-8 range, including the null bytes (0x00 encodes U+0000, a valid Unicode scalar). sniffLooksLikeText calls String(data: data, encoding: .utf8) first, and this returns a non-nil String for all-ASCII-range data. That causes an immediate return true, bypassing the data.contains(0) guard entirely. As a result resolvedResolution returns .resolved(.text) and the assertion XCTAssertEqual(… .media) fails. Including at least one byte ≥ 0x80 (e.g. 0xFF) would make the UTF-8 decode fail and let the null-byte or content-type path classify the file as media correctly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed. The binary regression fixture now uses two MPEG-TS-sized packets with sync bytes and NULs, which catches the previous valid-UTF-8 false positive.

— Claude Code

Comment on lines +86 to +96
func testBinaryTransportStreamFileKeepsMediaPreview() throws {
let url = FileManager.default.temporaryDirectory
.appendingPathComponent(UUID().uuidString)
.appendingPathExtension("ts")
defer { try? FileManager.default.removeItem(at: url) }

try Data([0x47, 0x40, 0x00, 0x10, 0x00, 0x47, 0x41, 0x00, 0x10, 0x00])
.write(to: url, options: .atomic)

XCTAssertEqual(FilePreviewKindResolver.mode(for: url), .media)
}

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.

P2 initialMode for a binary .ts file is not tested

After this PR, initialResolution routes ALL .ts files through .needsSniff (because knownTextFileNeedsSniffBeforeMedia returns true for any .ts), so a binary MPEG-TS file now shows .quickLook initially rather than .media. A XCTAssertEqual(FilePreviewKindResolver.initialMode(for: url), .quickLook) assertion in the binary test would confirm this is intentional and guard against future regressions.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed. The binary .ts regression now asserts the intentional initial Quick Look mode before resolved media mode.

— Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Covered in the transport-stream tests: both binary .ts fixtures now assert initialMode is quickLook before resolving to media.

— Claude Code

coderabbitai[bot]
coderabbitai Bot previously requested changes May 28, 2026

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmuxTests/FilePreviewReviewFeedbackTests.swift`:
- Around line 86-101: Add a regression test that writes a tiny ".ts" fixture
containing one or two NUL (\0) bytes and asserts that the sniffing rejects it
before the UTF-8 text path; specifically, add a new test (e.g.,
testTypeScriptWithNulBytesRejectsUtf8) in FilePreviewReviewFeedbackTests.swift
that builds a temporary .ts file with embedded \0 bytes, uses
FilePreviewKindResolver.initialMode(for: url) and
FilePreviewKindResolver.mode(for: url) to verify the sample is not treated as
printable text (expect quickLook or non-.text result), and clean up the temp
file in defer—this pins the boundary ensuring NUL-containing samples are
rejected prior to the UTF-8 text branch.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: dc1bb997-9679-425f-a646-e61421190fb6

📥 Commits

Reviewing files that changed from the base of the PR and between a15a580 and 6df0af9.

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

Comment thread cmuxTests/FilePreviewReviewFeedbackTests.swift
@lawrencecchen
lawrencecchen dismissed coderabbitai[bot]’s stale review May 28, 2026 06:34

Stale bot review on 6df0af9. The requested NUL-byte .ts regression test was added in db19dd1 and the current CodeRabbit check is green.

@lawrencecchen
lawrencecchen merged commit 3244763 into main May 28, 2026
21 checks passed
@lawrencecchen
lawrencecchen deleted the task-ts-files-open-typescript branch May 28, 2026 06:34

This branch was successfully deployed

1 active deployment
Preview – cmux — 05f22dc5 Deployed May 28, 2026 by vercel[bot]
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.

1 participant