Commit dece66c32a
dece66c32a50c8dc3f1134f9d32696f5c45192c7
parent: f9aa552633
Unsigned
cmc <hello@cleberg.net> · 2026-08-28 19:28 UTC
Render Org READMEs with the shared OrgSwift package
Depend on OrgSwift (ssh://git@gitbay.org/krz/org-swift.git) and render the .org
README path through OrgRenderer instead of the in-file org renderer. SyntaxHighlighter
conforms to OrgSwift.CodeHighlighter so code blocks keep their Highlightr highlighting;
the sr.ht relative-link resolution is passed through OrgRenderOptions.
Layout: unified · split
Hutch.xcodeproj/project.pbxproj
+17
| @@ -23,6 +23,7 @@ |
| 23 | 23 | 8BE082012F80000100000001 /* HutchWidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 8BE081F62F80000100000001 /* HutchWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; |
| 24 | 24 | 8BE083012F81000100000001 /* HutchSafariExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 8BE083032F81000100000001 /* HutchSafariExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; |
| 25 | 25 | 8BF100032F9A000100000001 /* Markdown in Frameworks */ = {isa = PBXBuildFile; productRef = 8BF100022F9A000100000001 /* Markdown */; }; |
| 26 | 0F0F0F0F0F0F0F0F000000A3 /* OrgSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0F0F0F0F0F0F0F0F000000A2 /* OrgSwift */; }; |
| 26 | 27 | /* End PBXBuildFile section */ |
| 27 | 28 | |
| 28 | 29 | /* Begin PBXContainerItemProxy section */ |
| @@ -174,6 +175,7 @@ |
| 174 | 175 | files = ( |
| 175 | 176 | 8BF100032F9A000100000001 /* Markdown in Frameworks */, |
| 176 | 177 | 8BAA000000000000000000B0 /* Highlightr in Frameworks */, |
| 178 | 0F0F0F0F0F0F0F0F000000A3 /* OrgSwift in Frameworks */, |
| 177 | 179 | ); |
| 178 | 180 | runOnlyForDeploymentPostprocessing = 0; |
| 179 | 181 | }; |
| @@ -325,6 +327,7 @@ |
| 325 | 327 | packageProductDependencies = ( |
| 326 | 328 | 8BF100022F9A000100000001 /* Markdown */, |
| 327 | 329 | 8BAA000000000000000000B2 /* Highlightr */, |
| 330 | 0F0F0F0F0F0F0F0F000000A2 /* OrgSwift */, |
| 328 | 331 | ); |
| 329 | 332 | productName = Hutch; |
| 330 | 333 | productReference = 8B4B28D12F6704280045FA19 /* Hutch.app */; |
| @@ -426,6 +429,7 @@ |
| 426 | 429 | packageReferences = ( |
| 427 | 430 | 8BF100012F9A000100000001 /* XCRemoteSwiftPackageReference "swift-markdown" */, |
| 428 | 431 | 8BAA000000000000000000B3 /* XCRemoteSwiftPackageReference "Highlightr" */, |
| 432 | 0F0F0F0F0F0F0F0F000000A1 /* XCRemoteSwiftPackageReference "org-swift" */, |
| 429 | 433 | ); |
| 430 | 434 | preferredProjectObjectVersion = 77; |
| 431 | 435 | productRefGroup = 8B4B28D22F6704280045FA19 /* Products */; |
| @@ -1019,6 +1023,14 @@ |
| 1019 | 1023 | minimumVersion = 0.8.0; |
| 1020 | 1024 | }; |
| 1021 | 1025 | }; |
| 1026 | 0F0F0F0F0F0F0F0F000000A1 /* XCRemoteSwiftPackageReference "org-swift" */ = { |
| 1027 | isa = XCRemoteSwiftPackageReference; |
| 1028 | repositoryURL = "ssh://git@gitbay.org/krz/org-swift.git"; |
| 1029 | requirement = { |
| 1030 | branch = main; |
| 1031 | kind = branch; |
| 1032 | }; |
| 1033 | }; |
| 1022 | 1034 | /* End XCRemoteSwiftPackageReference section */ |
| 1023 | 1035 | |
| 1024 | 1036 | /* Begin XCSwiftPackageProductDependency section */ |
| @@ -1032,6 +1044,11 @@ |
| 1032 | 1044 | package = 8BF100012F9A000100000001 /* XCRemoteSwiftPackageReference "swift-markdown" */; |
| 1033 | 1045 | productName = Markdown; |
| 1034 | 1046 | }; |
| 1047 | 0F0F0F0F0F0F0F0F000000A2 /* OrgSwift */ = { |
| 1048 | isa = XCSwiftPackageProductDependency; |
| 1049 | package = 0F0F0F0F0F0F0F0F000000A1 /* XCRemoteSwiftPackageReference "org-swift" */; |
| 1050 | productName = OrgSwift; |
| 1051 | }; |
| 1035 | 1052 | /* End XCSwiftPackageProductDependency section */ |
| 1036 | 1053 | }; |
| 1037 | 1054 | rootObject = 8B4B28C92F6704280045FA19 /* Project object */; |
Hutch.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
+10 −1
| @@ -1,5 +1,5 @@ |
| 1 | 1 | { |
| 2 | | "originHash" : "4554417ca2693020116bce7cbda415a5e51ada6b4cc4a8b39f7f43a9644bb5e5", |
| 2 | "originHash" : "161ab13c39311c33c8ab987c4e00f5238e1b905e4b929b88f0ff7103a6dc20e4", |
| 3 | 3 | "pins" : [ |
| 4 | 4 | { |
| 5 | 5 | "identity" : "highlightr", |
| @@ -10,6 +10,15 @@ |
| 10 | 10 | "version" : "2.3.0" |
| 11 | 11 | } |
| 12 | 12 | }, |
| 13 | { |
| 14 | "identity" : "org-swift", |
| 15 | "kind" : "remoteSourceControl", |
| 16 | "location" : "ssh://git@gitbay.org/krz/org-swift.git", |
| 17 | "state" : { |
| 18 | "branch" : "main", |
| 19 | "revision" : "47ac630c479b79dbfa8944c91b22b7bb6974cadc" |
| 20 | } |
| 21 | }, |
| 13 | 22 | { |
| 14 | 23 | "identity" : "swift-cmark", |
| 15 | 24 | "kind" : "remoteSourceControl", |
Hutch/Views/Repositories/ReadmeView.swift
+12 −21
| @@ -1,3 +1,4 @@ |
| 1 | import OrgSwift |
| 1 | 2 | import SwiftUI |
| 2 | 3 | import WebKit |
| 3 | 4 | |
| @@ -314,29 +315,19 @@ struct RenderedMarkupContentView: View { |
| 314 | 315 | return |
| 315 | 316 | } |
| 316 | 317 | let theme = SyntaxHighlightTheme(colorScheme: colorScheme) |
| 318 | let host = repositoryHost |
| 319 | let owner = ownerCanonicalName |
| 320 | let repo = repositoryName |
| 321 | let path = readmePath |
| 317 | 322 | let html = await Task.detached(priority: .userInitiated) { |
| 318 | | orgToHTML( |
| 319 | | text, |
| 320 | | codeTheme: theme, |
| 321 | | imageURLResolver: { source in |
| 322 | | resolveRepositoryAssetURL( |
| 323 | | source, |
| 324 | | owner: ownerCanonicalName, |
| 325 | | repositoryName: repositoryName, |
| 326 | | readmePath: readmePath |
| 327 | | )? |
| 328 | | .replacingOccurrences(of: "git.sr.ht", with: repositoryHost) |
| 329 | | }, |
| 330 | | linkURLResolver: { source in |
| 331 | | resolveRepositoryLinkURL( |
| 332 | | source, |
| 333 | | owner: ownerCanonicalName, |
| 334 | | repositoryName: repositoryName, |
| 335 | | readmePath: readmePath |
| 336 | | )? |
| 337 | | .replacingOccurrences(of: "git.sr.ht", with: repositoryHost) |
| 338 | | } |
| 323 | let options = OrgRenderOptions( |
| 324 | host: host, |
| 325 | owner: owner, |
| 326 | repositoryName: repo, |
| 327 | ref: "HEAD", |
| 328 | readmePath: path |
| 339 | 329 | ) |
| 330 | return OrgRenderer.renderToHTML(text, options: options, highlighter: SyntaxHighlighter(theme: theme)) |
| 340 | 331 | }.value |
| 341 | 332 | RenderedReadmeHTMLCache.shared.setHTML(html, forKey: cacheKey) |
| 342 | 333 | guard !Task.isCancelled else { return } |
Hutch/Views/Repositories/SyntaxHighlighter.swift
+7 −1
| @@ -1,5 +1,6 @@ |
| 1 | 1 | import Foundation |
| 2 | 2 | import Highlightr |
| 3 | import OrgSwift |
| 3 | 4 | import SwiftUI |
| 4 | 5 | import UIKit |
| 5 | 6 | |
| @@ -31,7 +32,7 @@ nonisolated enum SyntaxHighlightTheme { |
| 31 | 32 | /// instance must stay on the thread/task that created it. Callers that fail to |
| 32 | 33 | /// resolve a language — or hit an unavailable engine — get `nil` and should |
| 33 | 34 | /// fall back to plain, escaped text. |
| 34 | | nonisolated final class SyntaxHighlighter { |
| 35 | nonisolated final class SyntaxHighlighter: CodeHighlighter { |
| 35 | 36 | private let highlightr: Highlightr? |
| 36 | 37 | private let supportedLanguages: Set<String> |
| 37 | 38 | |
| @@ -60,6 +61,11 @@ nonisolated final class SyntaxHighlighter { |
| 60 | 61 | return Self.html(from: attributed) |
| 61 | 62 | } |
| 62 | 63 | |
| 64 | /// `OrgSwift.CodeHighlighter` conformance — delegates to the existing method. |
| 65 | func highlightedHTML(code: String, language: String?) -> String? { |
| 66 | highlightedHTML(for: code, language: language) |
| 67 | } |
| 68 | |
| 63 | 69 | private static let htmlMeasurementFont = UIFont.monospacedSystemFont(ofSize: 12, weight: .regular) |
| 64 | 70 | |
| 65 | 71 | private func resolvedLanguage(_ raw: String?) -> String? { |