Skip to content

fix(launch): open files passed at cold launch instead of the welcome screen (#1443)#1449

Merged
datlechin merged 3 commits into
mainfrom
fix/1443-csv-cold-launch
May 28, 2026
Merged

fix(launch): open files passed at cold launch instead of the welcome screen (#1443)#1449
datlechin merged 3 commits into
mainfrom
fix/1443-csv-cold-launch

Conversation

@datlechin

Copy link
Copy Markdown
Member

Fixes #1443.

Problem

When TablePro is closed and the user double-clicks a .csv file in Finder (with TablePro as the default app for CSV), TablePro launches and shows its welcome screen. The CSV that triggered the launch is never opened.

Root cause

On cold launch, macOS calls application(_:open:) between applicationWillFinishLaunching and applicationDidFinishLaunching. URLClassifier.classifyFile queries PluginManager.allInspectorFileExtensions, but PluginManager.shared.loadPlugins() ran inside applicationDidFinishLaunching (42ms too late, verified by the existing OSLog instrumentation). At the moment the open event arrived, lazyInspectorFileExtensions was empty, the classifier returned nil, the URL was silently dropped with an Unrecognized URL warning, and deliver([]) early-returned. The 150ms intent-collection timer in AppLaunchCoordinator then fired with no intents, finalizeWindowsIfNoVisibleMain showed the welcome window, and the CSV never opened.

Captured cold-launch log:

09:41:54.076  AppDelegate.application(_:open:) urls=customers-100.csv
09:41:54.076  Unrecognized URL: file:///.../customers-100.csv
09:41:54.077  ThemeEngine activated                              (didFinishLaunching starts)
09:41:54.118  Discovered 13 plugin(s)                            (loadPlugins, 42ms too late)

Same class of issue would have applied to any inspector file type the plugin system registers (TSV, future inspectors), not just CSV.

Fix

  • AppDelegatePluginManager.shared.loadPlugins() moves from applicationDidFinishLaunching to applicationWillFinishLaunching, right after the existing InspectorDocumentController(). InspectorDocumentController.typeForContents(of:) already reads allInspectorFileExtensions, so putting plugin load next to it matches the existing precedent for "must be ready before any file-open event." The synchronous lazy-manifest registration inside loadPlugins populates lazyInspectorFileExtensions before returning; the async eager-loading Task continues in the background, unchanged.
  • AppLaunchCoordinatorWindowOpener.shared.orderOutWelcome() is now called after intents are routed in both code paths: deliver's warm-launch branch and transitionToRouting's cold-launch branch. The pre-existing orderOut loop in deliver's accepting-phase branch only ran during application(_:open:), when SwiftUI had not yet auto-shown the welcome Window scene, so NSApp.windows was empty and the loop was a no-op. The post-routing dismissal is deterministic and runs after SwiftUI has had a chance to show the welcome window.

Verification

Cold-launch reproduction with logging on the patched build: application(_:open:) receives the URL, classifier returns .openInspectorFile, the intent is captured in pendingIntents, transitionToRouting routes it through NSDocumentController.openDocument, the CSV inspector window opens, and the welcome window is dismissed.

One caveat: the welcome window may briefly flash (≤150ms, the intent-collection window) before being dismissed, because SwiftUI auto-shows the first Window scene during didFinishLaunching and macOS 14 has no API to suppress that (defaultLaunchBehavior(.suppressed) is macOS 15+). Eliminating the flash entirely would require replacing the SwiftUI welcome Window scene with an AppKit NSWindowController managed by WindowOpener. Out of scope for this fix.

Tests

Not added. The bug is a singleton-lifecycle ordering race across AppDelegate, AppLaunchCoordinator, WindowOpener, LaunchIntentRouter, and PluginManager. A meaningful regression test would need invasive dependency injection across all of them. The existing OSLog instrumentation (AppDelegate.application(_:open:), AppLaunchCoordinator.handleOpenURLs, LaunchIntentRouter.openInspectorDocument) makes the cold-launch reproduction the practical verification path.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e45320e26e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

await LaunchIntentRouter.shared.route(intent)
}
if !intents.isEmpty {
WindowOpener.shared.orderOutWelcome()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep welcome visible when routing fails

When a collected launch intent is recognized but cannot actually be opened (for example a stale tablepro deep link whose connection is missing, or an inspector document that NSDocumentController rejects), LaunchIntentRouter.route handles the error internally and returns Void, so this branch cannot tell success from failure. Because finalizeWindowsIfNoVisibleMain is skipped whenever intents is non-empty, hiding the welcome window here can leave a cold-launched app with no visible window and may also remove the window that the error sheet was attached to; only dismiss the welcome window after routing has produced a visible target window or after route success is observable.

Useful? React with 👍 / 👎.

@datlechin datlechin merged commit 2db1a23 into main May 28, 2026
1 check passed
@datlechin datlechin deleted the fix/1443-csv-cold-launch branch May 28, 2026 05:18
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.

Double-clicking a CSV opens the startup screen instead of the file

1 participant