Commit 2b633be5ae
2b633be5ae65c1db9c073d6e84b1aa636cd18bc5
parent: 3bdfe61a93
Verified · cmc
cmc <hello@cleberg.net> · 2026-08-28T19:01:13Z
Resolve repo-relative README links and images to gitbay URLs
Pass the instance host and owner/repo into OrgSwift's render options so relative
references resolve: images against /{owner}/{repo}/raw/HEAD/ (raw bytes) and links
against /blob/HEAD/ (the file page), matching gitbay's routes. Absolute and
in-page (#anchor) references are unchanged.
gitbay/Views/Repos/OrgReadmeWebView.swift
+2 −1
| @@ -9,6 +9,7 @@ import WebKit |
| 9 | 9 | /// highlighted through Highlightr; links open in the system browser. |
| 10 | 10 | struct OrgReadmeWebView: View { |
| 11 | 11 | let source: String |
| 12 | var options: OrgRenderOptions = .init() |
| 12 | 13 | |
| 13 | 14 | @Environment(\.colorScheme) private var colorScheme |
| 14 | 15 | @State private var html = "" |
| @@ -24,7 +25,7 @@ struct OrgReadmeWebView: View { |
| 24 | 25 | // READMEs are small, and Highlightr (JavaScriptCore) is thread-confined; rendering |
| 25 | 26 | // on the main actor keeps it simple and matches how the app highlights elsewhere. |
| 26 | 27 | let highlighter = OrgCodeHighlighter(colorScheme: scheme) |
| 27 | | let body = OrgRenderer.renderToHTML(source, highlighter: highlighter) |
| 28 | let body = OrgRenderer.renderToHTML(source, options: options, highlighter: highlighter) |
| 28 | 29 | html = OrgReadmeDocument.wrap(body, colorScheme: scheme) |
| 29 | 30 | } |
| 30 | 31 | } |
gitbay/Views/Repos/ReadmeView.swift
+24 −1
| @@ -1,4 +1,5 @@ |
| 1 | 1 | import Foundation |
| 2 | import OrgSwift |
| 2 | 3 | import SwiftUI |
| 3 | 4 | |
| 4 | 5 | /// README rendering follows the file's format. Org is rendered by the shared `OrgSwift` |
| @@ -6,15 +7,37 @@ import SwiftUI |
| 6 | 7 | struct ReadmeView: View { |
| 7 | 8 | let name: String |
| 8 | 9 | let content: String |
| 10 | /// Instance host and `owner/repo`, used to resolve repository-relative links and images. |
| 11 | /// Empty leaves relative references unresolved. |
| 12 | var host: String = "" |
| 13 | var repoPath: String = "" |
| 9 | 14 | |
| 10 | 15 | /// Format follows the file name, never a guess at the content. |
| 11 | 16 | var isOrg: Bool { name.lowercased().hasSuffix(".org") } |
| 12 | 17 | |
| 13 | 18 | var body: some View { |
| 14 | 19 | if isOrg { |
| 15 | | OrgReadmeWebView(source: content) |
| 20 | OrgReadmeWebView(source: content, options: orgOptions) |
| 16 | 21 | } else { |
| 17 | 22 | MarkdownView(markdown: content) |
| 18 | 23 | } |
| 19 | 24 | } |
| 25 | |
| 26 | /// gitbay serves raw bytes at `/{owner}/{repo}/raw/{ref}/…` and the file page at |
| 27 | /// `/blob/…`, so relative images resolve against `raw` and links against `blob`. |
| 28 | private var orgOptions: OrgRenderOptions { |
| 29 | let parts = repoPath.split(separator: "/", maxSplits: 1).map(String.init) |
| 30 | guard parts.count == 2, !host.isEmpty else { |
| 31 | return OrgRenderOptions() |
| 32 | } |
| 33 | return OrgRenderOptions( |
| 34 | host: host, |
| 35 | owner: parts[0], |
| 36 | repositoryName: parts[1], |
| 37 | ref: "HEAD", |
| 38 | readmePath: name, |
| 39 | imagePathSegment: "raw", |
| 40 | linkPathSegment: "blob" |
| 41 | ) |
| 42 | } |
| 20 | 43 | } |
gitbay/Views/Repos/RepoView.swift
+9 −2
| @@ -4,9 +4,11 @@ struct RepoView: View { |
| 4 | 4 | |
| 5 | 5 | @State private var model: RepoDetailViewModel |
| 6 | 6 | private let path: String |
| 7 | private let host: String |
| 7 | 8 | |
| 8 | 9 | init(client: GitbayClient, path: String) { |
| 9 | 10 | self.path = path |
| 11 | self.host = client.instance.baseURL.host() ?? "" |
| 10 | 12 | _model = State(initialValue: RepoDetailViewModel(client: client, path: path)) |
| 11 | 13 | } |
| 12 | 14 | |
| @@ -63,8 +65,13 @@ struct RepoView: View { |
| 63 | 65 | |
| 64 | 66 | if let readme = model.readme { |
| 65 | 67 | Section("README") { |
| 66 | | ReadmeView(name: model.readmeName ?? "README.md", content: readme) |
| 67 | | .padding(.vertical, 4) |
| 68 | ReadmeView( |
| 69 | name: model.readmeName ?? "README.md", |
| 70 | content: readme, |
| 71 | host: host, |
| 72 | repoPath: path |
| 73 | ) |
| 74 | .padding(.vertical, 4) |
| 68 | 75 | } |
| 69 | 76 | } |
| 70 | 77 | } |