Skip to content

Catalogue resolver + first-launch bundle installer sheet - #3

Merged
dayglojesus merged 3 commits into
mainfrom
fix-bundle-fileindex
May 23, 2026
Merged

Catalogue resolver + first-launch bundle installer sheet#3
dayglojesus merged 3 commits into
mainfrom
fix-bundle-fileindex

Conversation

@dayglojesus

@dayglojesus dayglojesus commented May 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

Lands the textmate-side half of the bundle-catalogue refactor and adds a first-launch UX for getting the default-tier bundles onto a fresh install. Three commits:

  1. Resolve catalogue + file-type index from Bundle Support.tmbundle — companion to textmatelives/bundle-support.tmbundle#1. Stops shipping DefaultBundles.plist, AvailableBundles.plist, and BundleFileTypeIndex.plist as app resources; replaces them with a runtime resolver that prefers the managed install at ~/Library/Application Support/TextMate/Managed/Bundles/Bundle Support.tmbundle/Support/<file> and falls back to the embedded copy under <App>/Contents/SharedSupport/Bundles/Bundle Support.tmbundle/Support/<file>. Catalogue + index updates ride the existing 3h bundle auto-update path instead of requiring a notarized app release.
  2. Bump pinned Bundle Support SHA to 8ca9ddb — picks up textmatelives/bundle-support.tmbundle#2 (the description backfill: 40/40 default-tier entries now carry descriptions normalized from each bundle's own info.plist). Re-fetches the embedded copy at the new SHA.
  3. Add first-launch sheet for default-tier bundle install — closes the UX gap that the resolver itself exposed (see below).

Why the first-launch sheet

BundlesManager.init schedules default-tier auto-installs via NSBackgroundActivityScheduler with a 3-hour interval. The scheduler is opportunistic — it doesn't fire at t=0; it waits at least the interval, and then only when the system feels idle. So a freshly-installed TextMate didn't show Python, Ruby, Git, etc. for hours, even though they were already registered in Bundles.plist. (Pre-existing behaviour, surfaced by the catalogue refactor when we tested cold-start.)

The sheet:

  • Fires once on first launch, gated by a new DidPromptForDefaultBundles user default.
  • Lists every uninstalled TMBundleOriginShipped bundle (filtered by !installedSHA and !in BundlesToNeverSuggest), pre-checked, with category and description columns.
  • "Install Selected" (⏎ default) calls [BundlesManager installSpecs:] and shows a determinate progress bar driven by NSProgress.completedUnitCount via KVO.
  • "Skip" (⎋) records every candidate's UUID in BundlesToNeverSuggest so the on-demand per-extension prompt (SelectGrammarViewController) won't ambush the user later. Install Selected does the same for any unchecked rows.
  • ␣ on a focused row toggles its checkbox (small NSTableView subclass).
  • Window is modeless + NSFloatingWindowLevel. runModalForWindow: was a no-go — it blocks the main NSOperationQueue that the BundleArchiveTask URLSession delegates run on (BundleFetcher.mm:78), and the install would never complete.

Code surface

New

  • Applications/TextMate/src/FirstLaunchBundleInstaller.{h,mm} — the window controller, candidate filter, table data source, install kickoff.

Modified

  • Frameworks/BundlesManager/src/{BundleRegistry,BundlesManager}.{h,mm} — resolver +[BundleRegistry pathForCatalogueResource:], callsites switched in seedShippedDefaults/seedAvailableBundles/fileTypeIndex/shippedDefaultsByUUID, new kUserDefaultsDidPromptForDefaultBundlesKey.
  • Frameworks/BundlesManager/src/MandatoryBundles.h — Bundle Support pin 7c779d068ca9ddb.
  • Applications/TextMate/src/AppController.mm[FirstLaunchBundleInstaller promptIfNeeded] from applicationDidFinishLaunching:.
  • bin/fetch_embedded_bundles.sh — post-extract scrub of .github/ (CI metadata, no runtime use) and Support/shared/bin/CocoaDialog.app/ (Intel-only, blocks notarization — already removed in 297d39d but the tarball re-extracted it on every SHA bump).
  • Frameworks/BundlesManager/tests/t_filetype_lookup.mm — comment update (test seam unchanged; uses initForTestingWithSpecs:fileTypeIndexPath:).

Deleted

  • Applications/TextMate/resources/DefaultBundles.plist
  • Applications/TextMate/resources/AvailableBundles.plist
  • Applications/TextMate/resources/BundleFileTypeIndex.plist
  • bin/build_filetype_index.rb

Embedded bundle refresh

  • Applications/TextMate/support/Bundles/Bundle Support.tmbundle/ re-fetched at 8ca9ddb. Includes populated Support/DefaultBundles.plist, the regenerated BundleFileTypeIndex.plist, and the new Support/backfill_descriptions.rb.

Test plan

  • ninja BundlesManager/test — 10/10 pass.
  • ninja document/test — 9/9 pass.
  • ninja TextMate — clean full build, signed.
  • <App>/Contents/Resources/ confirmed clean of the 3 catalogue plists.
  • <App>/Contents/SharedSupport/Bundles/Bundle Support.tmbundle/Support/ confirmed carrying the 3 plists + generator + backfill script.
  • Cold-start integration (scrub ~/Library/Application Support/TextMate + caches + prefs, launch built app):
    • Mandatory bundles install from embedded copy (4 of 4).
    • Registry seeds 153 specs (4 mandatory + 40 default + 109 available).
    • First-launch sheet appears, lists 40 default-tier candidates with descriptions, allows toggle + batch install.
    • .yml open triggers on-demand install prompt for YAML when it was unchecked at first launch.

Out of scope

  • Applications/TextMate/resources/DefaultBundles.tbz.bl + the derived DefaultBundles.tbz — legacy installer payload, no remaining references. Cleanup deferred.
  • bin/generate_available_bundles.rb — not audited as part of this PR.
  • Version bump / release cut — explicit user direction is to defer Phase 4.

@dayglojesus dayglojesus changed the title Resolve catalogue + file-type index from Bundle Support.tmbundle Catalogue resolver + first-launch bundle installer sheet May 23, 2026
Bumps Bundle Support pinned SHA in MandatoryBundles.h to the merge
commit of textmatelives/bundle-support.tmbundle#1 (1663f664), which
adds Support/DefaultBundles.plist, Support/AvailableBundles.plist,
Support/BundleFileTypeIndex.plist, and Support/build_filetype_index.rb
to the bundle. The app no longer carries those three plists or the
generator script.

New + (NSString*)pathForCatalogueResource: on BundleRegistry resolves
a basename (DefaultBundles.plist / AvailableBundles.plist /
BundleFileTypeIndex.plist) to an on-disk path. It prefers the managed
install at ~/Library/Application Support/TextMate/Managed/Bundles/
Bundle Support.tmbundle/Support/<file>.plist, falling back to the
embedded copy under <App>/Contents/SharedSupport/Bundles/
Bundle Support.tmbundle/Support/<file>.plist for first launch and
offline. Returns nil if neither exists.

Callsites switched: BundleRegistry -seedShippedDefaults, -seedAvailableBundles,
-fileTypeIndex; BundlesManager -shippedDefaultsByUUID. The
initForTestingWithSpecs:fileTypeIndexPath: seam still takes an
explicit path, so existing unit tests need no other changes.

Embedded Bundle Support copy refreshed via bin/fetch_embedded_bundles.sh
at the new SHA. The fetch script now also scrubs .github/ (CI metadata,
no runtime use) and Support/shared/bin/CocoaDialog.app/ (Intel-only,
blocks notarization — removed in commit 297d39d but the tarball
re-extracts it on every SHA bump).

Catalogue + extension updates now ship via the existing 3h bundle
auto-update path rather than a notarized app release.
Picks up textmatelives/bundle-support.tmbundle#2 (the description
backfill for default-tier bundles — 40/40 entries now carry
description fields pulled from each bundle's own info.plist and
normalized HTML→plain to match the available-tier format already in
place).

- MandatoryBundles.h: pin 1663f664 → 8ca9ddb.
- Applications/TextMate/support/Bundles/Bundle Support.tmbundle/:
  re-fetched at the new SHA via bin/fetch_embedded_bundles.sh.
  Brings in the populated Support/DefaultBundles.plist and the new
  Support/backfill_descriptions.rb helper.

No code change. With the resolver introduced earlier in this branch,
the app loads catalogue descriptions either from the managed install
or from this embedded copy at cold start.
NSBackgroundActivityScheduler is the only entry point that drives
default-tier bundle installation, and it doesn't fire at t=0 — it
waits at least its 3-hour interval and then only when the system
feels idle. On a fresh install that meant default bundles (Python,
Ruby, Git, Rave, …) wouldn't appear for hours, even though they're
listed in DefaultBundles.plist and registered in Bundles.plist.

This commit adds a modeless floating window that fires once on first
launch (gated by kUserDefaultsDidPromptForDefaultBundlesKey), lists
every uninstalled default-tier bundle (filtered by
TMBundleOriginShipped + !installedSHA + !in BundlesToNeverSuggest),
pre-checks them, and lets the user kick off a batch install via
-[BundlesManager installSpecs:].

Window stays non-modal on purpose: the install path is async and
NSURLSession delegates run on the main NSOperationQueue, which a
runModalForWindow: session would block — exactly the deadlock the
prototype hit. NSFloatingWindowLevel + activateIgnoringOtherApps:
gives it the "prominent" feel without the deadlock.

Keys: ⏎ activates Install Selected; ⎋ activates Skip. Space toggles
the focused row's checkbox via a tiny NSTableView subclass.

Skip records every candidate UUID into
kUserDefaultsBundlesToNeverSuggestKey so the on-demand per-extension
prompt (SelectGrammarViewController) doesn't ambush the user later.
Install Selected records unchecked rows the same way. Either button
flips kUserDefaultsDidPromptForDefaultBundlesKey so we never
re-prompt.

Progress: NSProgress.completedUnitCount via KVO drives a determinate
bar + "Installed N of M…" label.

Tested with full ~/Library/Application Support/TextMate +
~/Library/Caches/com.macromates.TextMate + prefs scrub: sheet
appears, 40 default-tier bundles listed (incl. promoted Rave),
install completes, registry and managed/Bundles directory populate
correctly, on-demand prompt still fires for .yml etc. when their
bundle is unchecked.
@dayglojesus
dayglojesus force-pushed the fix-bundle-fileindex branch from b856e06 to 0932d14 Compare May 23, 2026 01:56
@dayglojesus
dayglojesus merged commit bf48370 into main May 23, 2026
1 check passed
@dayglojesus
dayglojesus deleted the fix-bundle-fileindex branch August 21, 2026 02:52
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