krz/hutch

an ios client for sourcehut

clone: git clone https://gitbay.org/krz/hutch.git

01ce5ee73feb8bda35749a186c0efcd3d6d7a060

verified · cmc

author: Christian Cleberg <hello@cleberg.net> · 2026-03-19T00:52:01Z

use native markdown rendering for settings bio
 Hutch.xcodeproj/project.pbxproj         |  8 ++++----
 Hutch/Views/Settings/SettingsView.swift | 33 ++++++++++++++++++++++++++-------
 HutchTests/SettingsViewTests.swift      | 22 ++++++++++++++++++++++
 3 files changed, 52 insertions(+), 11 deletions(-)

diff --git a/Hutch.xcodeproj/project.pbxproj b/Hutch.xcodeproj/project.pbxproj
index 3cd0646..6368778 100644
--- a/Hutch.xcodeproj/project.pbxproj
+++ b/Hutch.xcodeproj/project.pbxproj
@@ -363,7 +363,7 @@
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 1;
+				CURRENT_PROJECT_VERSION = 2;
 				DEVELOPMENT_TEAM = ZCNAX3VL9D;
 				ENABLE_PREVIEWS = YES;
 				GENERATE_INFOPLIST_FILE = YES;
@@ -380,7 +380,7 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 1.0;
+				MARKETING_VERSION = 1.2;
 				PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -399,7 +399,7 @@
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 1;
+				CURRENT_PROJECT_VERSION = 2;
 				DEVELOPMENT_TEAM = ZCNAX3VL9D;
 				ENABLE_PREVIEWS = YES;
 				GENERATE_INFOPLIST_FILE = YES;
@@ -416,7 +416,7 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 1.0;
+				MARKETING_VERSION = 1.2;
 				PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.Hutch;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				STRING_CATALOG_GENERATE_SYMBOLS = YES;
diff --git a/Hutch/Views/Settings/SettingsView.swift b/Hutch/Views/Settings/SettingsView.swift
index 802e12d..9b7d41f 100644
--- a/Hutch/Views/Settings/SettingsView.swift
+++ b/Hutch/Views/Settings/SettingsView.swift
@@ -1,6 +1,10 @@
 import PhotosUI
 import SwiftUI
 
+private let settingsBioMarkdownOptions = AttributedString.MarkdownParsingOptions(
+    interpretedSyntax: .inlineOnlyPreservingWhitespace
+)
+
 struct SettingsView: View {
     @Environment(AppState.self) private var appState
     @Environment(\.colorScheme) private var colorScheme
@@ -163,13 +167,7 @@ struct SettingsView: View {
                     Text("Bio")
                         .font(.caption)
                         .foregroundStyle(.secondary)
-                    RenderedMarkupContentView(
-                        content: .markdown(bio),
-                        readmePath: nil,
-                        colorScheme: colorScheme,
-                        ownerCanonicalName: "",
-                        repositoryName: ""
-                    )
+                    SettingsBioView(markdown: bio)
                 }
             }
 
@@ -424,6 +422,27 @@ struct SettingsView: View {
     }
 }
 
+private struct SettingsBioView: View {
+    let markdown: String
+
+    var body: some View {
+        Text(settingsBioAttributedString(markdown))
+            .frame(maxWidth: .infinity, alignment: .leading)
+            .tint(.accentColor)
+            .textSelection(.enabled)
+    }
+}
+
+func settingsBioAttributedString(_ markdown: String) -> AttributedString {
+    guard let attributed = try? AttributedString(
+        markdown: markdown,
+        options: settingsBioMarkdownOptions
+    ) else {
+        return AttributedString(markdown)
+    }
+    return attributed
+}
+
 // MARK: - Edit Profile Sheet
 
 private struct EditProfileSheet: View {
diff --git a/HutchTests/SettingsViewTests.swift b/HutchTests/SettingsViewTests.swift
new file mode 100644
index 0000000..950c3dc
--- /dev/null
+++ b/HutchTests/SettingsViewTests.swift
@@ -0,0 +1,22 @@
+import Foundation
+import Testing
+@testable import Hutch
+
+struct SettingsViewTests {
+
+    @Test
+    @MainActor
+    func settingsBioAttributedStringPreservesInlineMarkdown() {
+        let attributed = settingsBioAttributedString("Hello **world** and [link](https://example.com)")
+
+        #expect(String(attributed.characters).contains("Hello world and link"))
+    }
+
+    @Test
+    @MainActor
+    func settingsBioAttributedStringFallsBackForInvalidMarkdown() {
+        let attributed = settingsBioAttributedString("[broken")
+
+        #expect(String(attributed.characters) == "[broken")
+    }
+}