a native ios client for gitbay

client ios swift

https://gitbay.org

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
99 /// highlighted through Highlightr; links open in the system browser.
1010 struct OrgReadmeWebView: View {
1111 let source: String
12 var options: OrgRenderOptions = .init()
1213
1314 @Environment(\.colorScheme) private var colorScheme
1415 @State private var html = ""
@@ -24,7 +25,7 @@ struct OrgReadmeWebView: View {
2425 // READMEs are small, and Highlightr (JavaScriptCore) is thread-confined; rendering
2526 // on the main actor keeps it simple and matches how the app highlights elsewhere.
2627 let highlighter = OrgCodeHighlighter(colorScheme: scheme)
27 let body = OrgRenderer.renderToHTML(source, highlighter: highlighter)
28 let body = OrgRenderer.renderToHTML(source, options: options, highlighter: highlighter)
2829 html = OrgReadmeDocument.wrap(body, colorScheme: scheme)
2930 }
3031 }
gitbay/Views/Repos/ReadmeView.swift +24 −1
@@ -1,4 +1,5 @@
11 import Foundation
2import OrgSwift
23 import SwiftUI
34
45 /// README rendering follows the file's format. Org is rendered by the shared `OrgSwift`
@@ -6,15 +7,37 @@ import SwiftUI
67 struct ReadmeView: View {
78 let name: String
89 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 = ""
914
1015 /// Format follows the file name, never a guess at the content.
1116 var isOrg: Bool { name.lowercased().hasSuffix(".org") }
1217
1318 var body: some View {
1419 if isOrg {
15 OrgReadmeWebView(source: content)
20 OrgReadmeWebView(source: content, options: orgOptions)
1621 } else {
1722 MarkdownView(markdown: content)
1823 }
1924 }
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 }
2043 }
gitbay/Views/Repos/RepoView.swift +9 −2
@@ -4,9 +4,11 @@ struct RepoView: View {
44
55 @State private var model: RepoDetailViewModel
66 private let path: String
7 private let host: String
78
89 init(client: GitbayClient, path: String) {
910 self.path = path
11 self.host = client.instance.baseURL.host() ?? ""
1012 _model = State(initialValue: RepoDetailViewModel(client: client, path: path))
1113 }
1214
@@ -63,8 +65,13 @@ struct RepoView: View {
6365
6466 if let readme = model.readme {
6567 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)
6875 }
6976 }
7077 }