Skip to content

Extension registry foundation#1545

Merged
cyanzhong merged 17 commits into
mainfrom
extensions
Jul 13, 2026
Merged

Extension registry foundation#1545
cyanzhong merged 17 commits into
mainfrom
extensions

Conversation

@cyanzhong

Copy link
Copy Markdown
Contributor

Extension registry foundation

Adds the foundation for installing and updating editor extensions from a registry. Extensions are now tracked in extensions.json, can be installed via a markedit:// deep link, and are kept up to date on a configurable cadence. This is a standalone, self-contained slice — a management UI and migration of pre-registry scripts will come in follow-ups.

What's new

  • extensions.json — app-side state for editor extensions (which are installed, in what order, and update preferences). The filesystem remains the source of truth: scripts added or removed under scripts/ are reconciled into the list on launch.
  • Install via deep linkmarkedit://install-extension?id=<registry-id> (reviewed) or ?url=<https-url> (unreviewed, extra warning). Both always confirm, verify/pin the sha256, and offer a relaunch. Never a silent install.
  • Registry index — fetched and cached with ETag revalidation; the last good index is kept for offline use.
  • Updates — checked on a cadence (onLaunch / daily / weekly) and acted on per registry.updateStrategy (manual / prompt / automatic). Only registry-installed (version-tracked) extensions are eligible.
  • Ordered, enabled-only injection — the editor now injects scripts from extensions.json in declared order, skipping disabled ones, instead of scanning the whole scripts/ directory.

Configuration

extensions.json (defaults shown):

{
  "$schema": "https://raw.githubusercontent.com/MarkEdit-app/schemas/main/extensions.json",
  "registry.url": "https://raw.githubusercontent.com/MarkEdit-app/extensions/main/index.json",
  "registry.updateCheck": "weekly",
  "registry.updateStrategy": "prompt",
  "installed": []
}
  • registry.url — overridable to a mirror.
  • registry.updateChecknever / onLaunch / daily / weekly.
  • registry.updateStrategymanual (surface later in the manager UI) / prompt / automatic. Defaults to prompt since injected code is arbitrary JavaScript.

Architecture

New ExtensionCore Swift package (pure logic, unit-tested, no AppKit UI):

  • ExtensionConfig — local extensions.json state: installed[], prefs, reconcileInstalled, upsertInstalled.
  • ExtensionRegistry — remote index: refresh (conditional GET), cachedIndex, cadence (isCheckDue), availableUpdates.
  • ExtensionDownloader — download over HTTPS + 200, verify/pin sha256, atomic write to scripts/<id>.js.
  • ExtensionEnvironment — injectable filesystem paths and app version (test seam).

App layer (NSAlert flows):

  • ExtensionInstaller — the markedit://install-extension deep link.
  • ExtensionUpdater — cadence check -> availableUpdates -> updateStrategy, with a failure alert that names each failed extension and its reason.

Integration points: reconcile at launch (Application), ordered/enabled injection (EditorViewController), deep link + update checks (AppDelegate), default file seed (AppCustomization).

Out of scope (follow-ups)

  • Management UI for browsing/enabling/disabling extensions.
  • Migrating existing pre-registry scripts into version tracking (will live in the manager UI, driven by user selection).

Testing

  • ExtensionCoreTests covers availableUpdates, compatibility checks, schema-version support, reconcile/upsert, and Installed adoption/merging.
  • Builds clean; a #if DEBUG debug/mock-index.json hook lets you exercise the update flow locally without hitting the network (compiled out of release).

Copilot AI left a comment

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.

Pull request overview

This PR lays the groundwork for a registry-backed extension system: extensions are tracked in extensions.json, can be installed via markedit://install-extension, are injected into the editor in an explicit order, and can be updated on a configured cadence/strategy. It introduces a new ExtensionCore package to encapsulate registry/config/downloading logic and wires it into the macOS app lifecycle and deep-link handling.

Changes:

  • Add ExtensionCore (config model + registry cache/refresh + downloader + environment seam) with unit tests.
  • Add app-layer install/update flows (ExtensionInstaller, ExtensionUpdater) and integrate them into launch + URL scheme handling.
  • Switch script injection to use extensions.json order/enabled state instead of scanning the entire scripts/ folder.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
MarkEditMac/Sources/Main/Application/Application.swift Reconcile extensions.json against on-disk scripts at startup.
MarkEditMac/Sources/Main/Application/AppDelegate.swift Trigger extension update checks and handle markedit://install-extension deep links.
MarkEditMac/Sources/Main/AppCustomization.swift Seed extensions.json and support loading scripts by explicit filenames.
MarkEditMac/Sources/Extension/ExtensionUpdater.swift New: update-check + prompt/automatic application flow (UI layer).
MarkEditMac/Sources/Extension/ExtensionInstaller.swift New: deep-link install flow with confirmation UI and relaunch prompts.
MarkEditMac/Sources/Editor/Controllers/EditorViewController.swift Inject enabled scripts in configured order from extensions.json.
MarkEditMac/Resources/Localizable.xcstrings Add localized strings for extension install/update alerts.
MarkEditMac/Modules/Tests/ExtensionCoreTests.swift New: unit tests for core extension config/registry behaviors.
MarkEditMac/Modules/Sources/ExtensionCore/Internal/ExtensionEnvironment.swift New: injectable filesystem/app-version environment seam for ExtensionCore.
MarkEditMac/Modules/Sources/ExtensionCore/ExtensionRegistry.swift New: registry index types + refresh/caching + update detection logic.
MarkEditMac/Modules/Sources/ExtensionCore/ExtensionDownloader.swift New: HTTPS download + sha256 verification/pinning + atomic install into scripts/.
MarkEditMac/Modules/Sources/ExtensionCore/ExtensionConfig.swift New: extensions.json encoding/decoding + reconcile/upsert + enabled order.
MarkEditMac/Modules/Package.swift Add ExtensionCore as a new SwiftPM library target and dependency.
MarkEdit.xcodeproj/project.pbxproj Wire new app source files and link ExtensionCore product.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread MarkEditMac/Sources/Main/Application/AppDelegate.swift
Comment thread MarkEditMac/Modules/Sources/ExtensionCore/ExtensionDownloader.swift
Comment thread MarkEditMac/Modules/Sources/ExtensionCore/ExtensionConfig.swift Outdated

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 6 comments.

Comment thread MarkEditMac/Sources/Main/AppCustomization.swift
Comment thread MarkEditMac/Modules/Sources/ExtensionCore/ExtensionRegistry.swift
Comment thread MarkEditMac/Modules/Sources/ExtensionCore/ExtensionRegistry.swift
Comment thread MarkEditMac/Modules/Sources/ExtensionCore/ExtensionRegistry.swift Outdated
Comment thread MarkEditMac/Modules/Sources/ExtensionCore/ExtensionConfig.swift
Comment thread MarkEditMac/Sources/Extension/ExtensionInstaller.swift Outdated

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.

Comment thread MarkEditMac/Sources/Main/AppCustomization.swift
Comment thread MarkEditMac/Sources/Extension/ExtensionUpdater.swift
Comment thread MarkEditMac/Sources/Extension/ExtensionInstaller.swift
Comment thread MarkEditMac/Modules/Sources/ExtensionCore/ExtensionRegistry.swift Outdated

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Comment thread MarkEditMac/Modules/Sources/ExtensionCore/ExtensionRegistry.swift
Comment thread MarkEditMac/Sources/Extension/ExtensionInstaller.swift Outdated

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Comment thread MarkEditMac/Sources/Extension/ExtensionInstaller.swift
Comment thread MarkEditMac/Sources/Main/AppCustomization.swift
@cyanzhong cyanzhong merged commit 3de32ae into main Jul 13, 2026
2 checks passed
@cyanzhong cyanzhong deleted the extensions branch July 13, 2026 12: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.

2 participants