Skip to content

4.0.0

Choose a tag to compare

@github-actions github-actions released this 20 Jul 11:28
· 22 commits to main since this release

3.0.0 以降に溜まっていた実バグの修正と、公開 API の全面的な見直しをまとめたメジャー。

要点: ①表示されているリンクと実際に開く先が違う等の実バグを 13 件修正 ②ライブ
プレビューの O(n²) を線形化(1 万行で 16.3 秒 → 0.12 秒)③配線されていない公開 API を
全廃 ④外部デザインシステムへの必須依存を解消し、既定でダークモードに追従 ⑤約束を
黙って別の挙動にすり替える経路を塞ぐ ⑥エディタの拡張点を公開。

移行の要点は下の「破壊的変更」に項目ごとの 移行 として書いてある。

修正

  • MarkdownEditorController.canUndo / canRedo が SwiftUI を更新しなかったのを修正。
    UndoManager を読む計算プロパティなのに objectWillChange を流していなかったため、
    利用者が自作した「元に戻す」ボタンが永久に無効のままだった。undo 通知を購読して
    publish する。購読先の object を絞らないのは、NSTextView.undoManager が
    レスポンダチェーン経由で解決され bind(_:) の時点ではまだ nil だから
  • コードブロックの中身が数式に置換される不具合を修正。~~~ フェンス・4 スペースの
    インデントコード・$ が先行する場合のインラインコードスパンが、いずれも数式スキャナの
    保護対象から漏れていた(~~~python\ncost = $5$\n~~~ の中身が cost = 0 になっていた)
  • CRLF 改行のソースで、行末まで伸びるトークン(見出し本文・コードブロック)の範囲に
    \r が 1 コードユニット含まれ、ハイライトが行末まで伸びていたのを修正
  • インデントされた閉じフェンス(リスト項目内など)を認識できず、以降のドキュメント全体が
    コードとして着色され続けていたのを修正

破壊的変更

  • 配線されていない公開 API を全廃した。 いずれも本体レンダリング経路からの参照が
    0 件で、Catalog のデモとテストだけが参照していた。

    • Style プロトコル 5 系統(HeadingStyle / CodeBlockStyle / TableStyle /
      LinkStyle / AsideStyle)と既定実装 17 型を削除。
      移行: 見た目の調整は MarkdownPalette / MarkdownMetrics / MarkdownTypeScale
      で行う(下記「テーマ」参照)
    • MarkdownRenderingOptions を renderMath 1 フィールドに縮小。他 7 つと
      .compact / .plainText を削除。移行: 該当する代替は無い。画像の抑止は
      .markdownImagePolicy(.bundleOnly) が近い
    • EditorHistory / MarkdownEditorEngine を削除。undo/redo はシステムの
      UndoManager が担っており二重実装だった。移行: 不要(既に UndoManager 経由)
    • MathBlockView / CodeBlockView / MermaidDiagramView / AdaptiveMermaidView /
      MermaidFallbackView を削除。移行: MarkdownView を使う
    • MathRenderer.displayMath(_:) 要件を削除。移行: ディスプレイ数式は
      MarkdownAttachmentRendering.renderedImage(for:theme:) で描く
  • 外部デザインシステムへの必須依存を外した。

    • SwiftMarkdownView / SwiftMarkdownEditor は swift-design-system を
      リンクしなくなった。色・寸法・文字サイズは MarkdownPalette /
      MarkdownMetrics / MarkdownTypeScale として自前で抽象化してある
    • 既定パレットがシステムの意味色になり、ライト/ダークに自動追従する。
      従来の既定は LightColorPalette() 固定で colorScheme を見ておらず、
      素で使うとダークモードで文字が読めなかった。README の回避手順は不要になった
    • 移行: .markdownTheme(themeProvider) を使っていた場合は
      SwiftMarkdownViewDesignSystem product を依存に追加して import する。
      呼び出し自体は変わらない。エディタは SwiftMarkdownEditorDesignSystem の
      .markdownEditorDesignSystemTheme()
    • 移行: MarkdownEditorTheme.fromDesignSystem(palette:) は
      SwiftMarkdownEditorDesignSystem へ移動した
  • MathRenderer の要件を 1 本にまとめた。
    inlineMath(_:palette:) と inlineMath(_:fontSize:palette:) の 2 本立てをやめ、
    inlineMath(_:fontSize:textColor:) だけにした。旧デフォルト実装は fontSize を
    破棄しており、同梱の LaTeXMathRenderer は両方実装していたため、
    サードパーティ実装でのみ数式のサイズが周囲に追従しないという出方をしていた。
    移行: palette の代わりに解決済みの textColor を受け取る。fontSize が
    nil のときは実装側の既定サイズを使う

  • MathRenderer が MarkdownAttachmentRendering を継承するようになった。
    従来は無関係な 2 プロトコルを MarkdownView 内の as? で繋いでおり、
    自作 MathRenderer を注入しても無言で無視され、数式が $latex$ のまま
    表示された
    。移行: renderedImage(for:theme:) を実装する。組版しないなら
    nil を返せば読み取り可能なテキストに落ちる

  • MermaidScriptSource.inline が実際に機能するようになった。
    従来は .url と .localFile しか解釈せず、それ以外は無条件で CDN に落ちていた。
    オフライン動作のつもりで .inline を選んだ利用者のアプリから、気づかないまま
    外部通信が飛んでいた。
    あわせて .localFile も、file URL を <script src> に
    渡す実装(WebView の origin からは読めない)から、中身を読み込んで埋め込む方式に
    改めた。読み込めない場合はログを出して描画しない — CDN には落ちない

  • MarkdownAttachmentRendering が Sendable を要求するようになった。
    戻り値の MarkdownRenderedImage が @unchecked Sendable を名乗る一方、
    生成側に並行性の契約が無かった

  • SyntaxHighlighter の失敗を握り潰さなくなった。
    try? で捨てていたため、doc がスローを約束しているのに利用者は自作ハイライターの
    失敗を「なぜか色が付かない」としてしか観測できなかった。今は理由をログに出す
    (描画自体は素のコードで続行する)

  • 実装詳細の再輸出をやめた。 import SwiftMarkdownView から見えていた
    MarkdownAttributedBuilder / MarkdownCodeHighlighting / MarkdownCodeRegion /
    MarkdownSyntaxHighlighting / MarkdownImageRequest / MarkdownImageAttachments /
    MarkdownBlockDecoration と NSAttributedString.Key 拡張を package に降格した。
    引き続き見えるのは MarkdownTextTheme / MarkdownAttachment /
    MarkdownRenderedImage / MarkdownAttachmentRendering の 4 型で、いずれも
    自作アタッチメントレンダラを書くのに必要

  • BundledMermaidScriptProvider を failable にした。 リソースが見つからない場合は
    nil を返す。従来はデバッグビルドで assertionFailure を出しつつ、リリースでは
    無言で CDN にフォールバックしていた。移行: if let か ?? で受ける。
    bundle / filename プロパティは解決済みの url に置き換わった

    // 見つからなければ Mermaid を描画しない
    if let provider = BundledMermaidScriptProvider() {
        view.markdownMermaidScriptProvider(provider)
    }
    // 明示的に CDN へ倒す判断も、利用者が書く
    let provider = BundledMermaidScriptProvider() ?? CDNMermaidScriptProvider()
  • グローバル定数 defaultMermaidScriptProvider を削除した。
    移行: .markdownMermaidScriptProvider(.cdn) を使う。あわせて同 modifier の
    引数を any MermaidScriptProvider から some MermaidScriptProvider に変えた
    (実存パラメータだと先頭ドット構文の推論が効かないため)

  • 旧 modifier 名の deprecated エイリアスを削除した。
    syntaxHighlighter / mermaidScriptProvider / mathRenderer の 3 件。
    移行: markdown prefix 付きの名前に置き換える
    (残り 3 件の headingStyle / codeBlockStyle / asideStyle は本体ごと廃止済み)

  • MarkdownEditorController のコマンド名を実挙動に合わせた。 いずれも
    適用済みなら解除する toggle なのに、toggleHeading / toggleQuote だけが
    toggle を名乗っていた。移行:

    • bold() → toggleBold()
    • italic() → toggleItalic()
    • code() → toggleInlineCode()
    • strikethrough() → toggleStrikethrough()
    • bulletList() → toggleBulletList()
    • wrap(_:) → toggleWrap(_:)
    • linePrefix(_:) → toggleLinePrefix(_:)
  • エディタ 4 ターゲットの公開面を確定した。 SwiftMarkdownEditorCore /
    Rules / TextKit は .library product として宣言されていなかったが、
    SwiftPM は依存グラフ内の全ターゲットをモジュール検索パスに載せるため、
    外部から import できてしまっていた(実測で確認)。つまり利用者は未文書な
    挙動に依存し、こちらは何が契約かを決めていない状態だった。

    • import SwiftMarkdownEditor 1 つで、init の引数型(InputRuleProcessor)も
      テーマ型(MarkdownEditorTheme)も独自コマンドの部品(EditTransform /
      MarkdownFormatting)も届くよう再輸出した
    • 契約でないものは package に降格: LineIndex / InlineSpan /
      InlineSpanParser / StyleRun / LivePreviewStyler
    • 移行: 3 ターゲットを個別に import していた場合は
      import SwiftMarkdownEditor だけで足りる
  • MarkdownTokenizer と MathScanner の公開面を狭めた。

    • MarkdownTokenizer.isInsideFencedCode(_:offset:) は public のまま。独自 InputRule が
      コードブロック内でオートフォーマットを抑えるのに要る(同梱の ListContinuationRule が実例)
    • MarkdownTokenizer.tokenize(_:) / fencedCodeRanges(_:) と MathScanner 全体は
      package へ。いずれも呼び出し元がパッケージ内のハイライタ・前処理だけだった
    • 移行: 該当する外部ユースケースは想定していない。トークン列を直接扱う必要が
      生じた場合は issue で相談してほしい
  • PlatformColor / PlatformFont の二重定義を解消した。
    MarkdownAttributedKit と SwiftMarkdownEditorTextKit が同名の public typealias を
    別々に宣言しており、両方を import した利用者のスコープで曖昧になっていた。
    定義は MarkdownPlatform ターゲット 1 か所に集約した。移行: 型は同一
    (UIColor/NSColor)なので通常は変更不要

  • 画像の読み込みに MarkdownImagePolicy を導入し、既定でローカルファイルシステムへの
    アクセスを行わなくなった
    。![alt](source) の source はドキュメント由来の文字列で、
    LLM 出力やユーザー投稿の場合がある。従来は file: URL と裸のファイルパスを無条件に
    開いていたため、描画するだけでアプリのファイルを読みにいく指定が成立していた。

    • 既定(.default): リモート(http/https)とバンドルリソース名のみ
    • .bundleOnly: ネットワークも使わない
    • .trustedDocument: 従来どおりローカルファイルを許可
    • 設定は .markdownImagePolicy(_:) で行う
  • あわせてリモート取得にサイズ上限(既定 10 MB)とタイムアウト(既定 15 秒)を追加し、
    失敗を try? で握りつぶすのをやめてログに理由を出すようにした

  • BundledMermaidScriptProvider はバンドルにスクリプトが無い場合、デバッグビルドで
    assertionFailure を発生させる。従来は無言で CDN にフォールバックしており、
    オフライン動作を期待した利用者のアプリから気づかないまま外部通信が発生していた

追加

  • MarkdownPalette / MarkdownMetrics / MarkdownTypeScale と、それぞれの
    既定実装・環境値・View modifier。外部依存なしで見た目を差し替えられる:

    struct BrandPalette: MarkdownPalette {
        var text: Color { .primary }
        var secondaryText: Color { .secondary }
        var heading: Color { .indigo }
        var link: Color { .blue }
        var codeBackground: Color { Color.gray.opacity(0.12) }
        var rule: Color { Color.gray.opacity(0.4) }
    }
    
    MarkdownView(source).markdownPalette(BrandPalette())
  • SwiftMarkdownViewDesignSystem / SwiftMarkdownEditorDesignSystem product。
    swift-design-system のトークンを上記の抽象へ写すブリッジ

  • MarkdownEditorTheme の環境値と .markdownEditorTheme(_:) modifier

  • macOS の検索バーについて、ホストアプリ側で .commands { TextEditingCommands() } が
    要ることを README とソースコメントに明記した。エディタは usesFindBar を有効にしているが、
    ⌘F はアプリの Edit メニュー経由で届き、SwiftUI の既定メニューには Find が含まれない。
    宣言しないと検索バーは出てこない(実機で確認)

  • エディタの拡張点を公開した。 差別化の中核でありながら、テーマ以外は
    ほぼ差し替え不能だった:

    • MarkdownEditorToolbarItem — ツールバーを項目の配列で構成する。既定は
      .standard。label(読み上げ名)は省略できず、key を与えると
      ショートカットも付く。ショートカットは項目定義から供給されるので、
      ツールバーを差し替えても失われない
      。空配列を渡すとツールバーを表示しない
    • MarkdownFormattingToolbar を public 化。自前のモード UI と組み合わせられる
    • MarkdownEditorController.state / apply(_:) を public 化。これで
      MarkdownFormatting の純関数と組み合わせて任意コマンドが書ける。従来は
      apply も readState も private で、現在のテキストと選択を読むことすら
      できなかった
    • MarkdownEditor(controller:) でコントローラを注入できる。外部から
      プログラム的にコマンドを送れる
    • MarkdownEditor(text:mode:...) でモードを Binding として外に出せる
    MarkdownEditor(
        text: $text,
        mode: $mode,
        toolbar: [
            .bold, .italic,
            .separator,
            .item(icon: "highlighter", label: "マーカー", key: "h") { controller in
                guard let state = controller.state else { return }
                controller.apply(MarkdownFormatting.wrap(
                    text: state.text, selection: state.selection, delimiter: "=="
                ))
            }
        ],
        controller: controller
    )
  • MarkdownContent(blocks:) を public 化。MarkdownView.init(_ content:) の doc は
    「レンダリング前にコンテンツを加工したい場合に使用する」と書いていたが、
    ブロック配列から組み直す入口が internal で実行不可能だった

変更

  • View modifier の命名を markdown prefix に統一した。View の extension は
    グローバル名前空間で、headingStyle や codeBlockStyle のような一般名は利用者の
    アプリや他ライブラリと衝突しうる。旧名は deprecated エイリアスとして残しており、
    Xcode の Fix-it で移行できる(削除は次のメジャー)
    • syntaxHighlighter → markdownSyntaxHighlighter
    • mathRenderer → markdownMathRenderer
    • mermaidScriptProvider → markdownMermaidScriptProvider
  • tvOS / watchOS のプラットフォーム宣言を削除。依存パッケージが iOS / macOS しか
    宣言しておらず、実際には解決・ビルドできなかった
  • デモ画面を SwiftMarkdownViewCatalog として別 product に分離。本体を import しても
    カタログ画面が補完候補に出なくなる