{"id":28386244,"url":"https://github.com/nuplay/richtext","last_synced_at":"2025-10-06T01:32:01.025Z","repository":{"id":38311457,"uuid":"389261402","full_name":"NuPlay/RichText","owner":"NuPlay","description":"Easily show RichText(html) in SwiftUI","archived":false,"fork":false,"pushed_at":"2025-08-24T10:39:12.000Z","size":153,"stargazers_count":257,"open_issues_count":19,"forks_count":46,"subscribers_count":9,"default_branch":"main","last_synced_at":"2025-08-24T12:04:53.320Z","etag":null,"topics":["attributedstring","html","ios","richtext","sfsafariviewcontroller","swift","swiftpackagemanager","swiftui","swiftui-components","text","uicomponents","wkwebview"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NuPlay.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-07-25T04:30:02.000Z","updated_at":"2025-08-16T21:40:47.000Z","dependencies_parsed_at":"2025-07-20T08:17:07.822Z","dependency_job_id":"6b767299-c829-465e-8af8-71d82c0853e5","html_url":"https://github.com/NuPlay/RichText","commit_stats":null,"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"purl":"pkg:github/NuPlay/RichText","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NuPlay%2FRichText","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NuPlay%2FRichText/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NuPlay%2FRichText/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NuPlay%2FRichText/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NuPlay","download_url":"https://codeload.github.com/NuPlay/RichText/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NuPlay%2FRichText/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278547774,"owners_count":26004772,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-05T02:00:06.059Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["attributedstring","html","ios","richtext","sfsafariviewcontroller","swift","swiftpackagemanager","swiftui","swiftui-components","text","uicomponents","wkwebview"],"created_at":"2025-05-30T13:40:56.459Z","updated_at":"2025-10-06T01:32:01.018Z","avatar_url":"https://github.com/NuPlay.png","language":"Swift","readme":"# RichText\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://swift.org/\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/Swift-5.9+-F05138?labelColor=303840\" alt=\"Swift: 5.9+\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://www.apple.com/ios/\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/iOS-15.0+-007AFF?labelColor=303840\" alt=\"iOS: 15.0+\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://www.apple.com/macos/\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/macOS-12.0+-007AFF?labelColor=303840\" alt=\"macOS-12.0+\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://developer.apple.com/xcode/\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/Xcode-16+-blue?labelColor=303840\" alt=\"Xcode: 16+\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/NuPlay/RichText/blob/main/LICENSE\"\u003e\n        \u003cimg src=\"https://img.shields.io/github/license/NuPlay/RichText\" alt=\"License\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/NuPlay/RichText/releases\"\u003e\n        \u003cimg src=\"https://img.shields.io/github/v/release/NuPlay/RichText\" alt=\"Release\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\nA modern, powerful, and type-safe SwiftUI component for rendering HTML content with extensive styling options, async/await support, media interaction, and comprehensive error handling. Built for Swift 5.9+ and optimized for iOS 15.0+ and macOS 12.0+.\n\n![github](https://user-images.githubusercontent.com/73557895/128497417-52d47524-05bf-48af-ae0a-e0cdffdbedf5.png)\n\n| \u003cimg width=\"1440\" alt=\"Light Mode Screenshot\" src=\"https://user-images.githubusercontent.com/73557895/131149958-bbc28435-02e2-4a02-8ad5-43627cd333e0.png\"\u003e \t| \u003cimg width=\"1440\" alt=\"Dark Mode Screenshot\" src=\"https://user-images.githubusercontent.com/73557895/131149926-211e2111-6d6e-4aac-94b8-44c7230b6244.png\"\u003e \t|\n|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------:\t|:------------------------------------------------------------------------------------------------------------------------------:\t|\n| Light Mode                                                                                                                                                                 \t| Dark Mode                                                                                                                        \t|\n\n---\n\n## Table of Contents\n\n- [✨ Features](#-features)\n- [🚀 Quick Start](#-quick-start)\n- [📦 Installation](#-installation)\n- [📚 Complete API Reference](#-complete-api-reference)\n- [🆕 What's New in v3.0.0](#-whats-new-in-v300)\n- [🔧 Advanced Usage](#-advanced-usage)\n- [💡 Examples](#-examples)\n- [🐛 Troubleshooting](#-troubleshooting)\n- [📖 Migration Guide](#-migration-guide)\n- [🤝 Contributing](#-contributing)\n\n---\n\n## ✨ Features\n\n### 🚀 **v3.0.0 - Modern Architecture**\n- ⚡ **Async/Await Support**: Modern Swift concurrency for better performance\n- 🛡️ **Type Safety**: Comprehensive Swift type safety with robust error handling\n- 🧪 **Swift Testing**: Modern testing framework with extensive test coverage\n- 🔧 **Backward Compatible**: 100% compatibility with v2.x while providing modern APIs\n\n### 📱 **Platform Support** \n- 📱 **Cross-platform**: iOS 15.0+ and macOS 12.0+ with Swift 5.9+\n- 🎨 **Theme Support**: Automatic light/dark mode with custom color schemes\n- 🔤 **Typography**: System fonts, custom fonts, monospace, italic, and Dynamic Type support\n\n### 🎛️ **Rich Features**\n- 🖼️ **Interactive Media**: Click events for images/videos with custom handling\n- 🔗 **Smart Link Management**: Safari, SFSafariView, and custom link handlers\n- 🎨 **Advanced Styling**: Type-safe background colors, CSS customization\n- 📐 **Responsive Layout**: Dynamic height calculation with smooth transitions\n- 🔄 **Loading States**: Configurable placeholders with animation support\n- 🌐 **HTML5 Complete**: Full support for modern semantic elements\n- 🚨 **Error Handling**: Comprehensive error types with custom callbacks\n\n---\n\n## 🚀 Quick Start\n\n### Basic Usage\n\nThe simplest way to get started with RichText:\n\n```swift\nimport SwiftUI\nimport RichText\n\nstruct ContentView: View {\n    let htmlContent = \"\"\"\n        \u003ch1\u003eWelcome to RichText\u003c/h1\u003e\n        \u003cp\u003eA powerful HTML renderer for SwiftUI.\u003c/p\u003e\n    \"\"\"\n    \n    var body: some View {\n        ScrollView {\n            RichText(html: htmlContent)\n        }\n    }\n}\n```\n\n### Enhanced Example\n\nAdd styling, media handling, and error handling:\n\n```swift\nstruct ContentView: View {\n    let htmlContent = \"\"\"\n        \u003ch1\u003eWelcome to RichText\u003c/h1\u003e\n        \u003cp\u003eA powerful HTML renderer with \u003cstrong\u003eextensive customization\u003c/strong\u003e.\u003c/p\u003e\n        \u003cimg src=\"https://via.placeholder.com/300x200\" alt=\"Sample Image\"\u003e\n        \u003cp\u003e\u003ca href=\"https://github.com/NuPlay/RichText\"\u003eVisit our GitHub\u003c/a\u003e\u003c/p\u003e\n    \"\"\"\n    \n    var body: some View {\n        ScrollView {\n            RichText(html: htmlContent)\n                .colorScheme(.auto)                    // Auto light/dark mode\n                .lineHeight(170)                       // Line height percentage\n                .imageRadius(12)                       // Rounded image corners\n                .transparentBackground()               // Transparent background\n                .placeholder {                         // Loading Placeholder\n                    Text(\"Loading email...\")\n                }\n                .onMediaClick { media in               // Handle media clicks\n                    switch media {\n                    case .image(let src):\n                        print(\"Image clicked: \\(src)\")\n                    case .video(let src):\n                        print(\"Video clicked: \\(src)\")\n                    }\n                }\n                .onError { error in                    // Handle errors\n                    print(\"RichText error: \\(error)\")\n                }\n        }\n    }\n}\n```\n\n---\n\n## 📦 Installation\n\n### Swift Package Manager (Recommended)\n\n1. In Xcode, select **File → Add Package Dependencies...**\n2. Enter the repository URL:\n   ```\n   https://github.com/NuPlay/RichText.git\n   ```\n3. Select version rule: **\"Up to Next Major Version\"** from **\"3.0.0\"**\n4. Click **Add Package**\n\n### Manual Package.swift\n\nAdd RichText to your `Package.swift`:\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/NuPlay/RichText.git\", .upToNextMajor(from: \"3.0.0\"))\n],\ntargets: [\n    .target(\n        name: \"YourTarget\",\n        dependencies: [\"RichText\"]\n    )\n]\n```\n\n---\n\n## 📚 Complete API Reference\n\n### Core Components\n\n#### RichText Initializers\n\n```swift\n// Basic initializer\nRichText(html: String)\n\n// With configuration\nRichText(html: String, configuration: Configuration)\n\n// With placeholder\nRichText(html: String, placeholder: AnyView?)\n\n// Full initializer\nRichText(html: String, configuration: Configuration, placeholder: AnyView?)\n```\n\n### Styling Modifiers\n\n#### Background Colors\n\n```swift\n// Recommended approaches (v3.0.0+)\n.transparentBackground()                    // Transparent (default)\n.backgroundColor(.system)                   // System default (white/black)\n.backgroundColorHex(\"FF0000\")              // Hex color\n.backgroundColorSwiftUI(.blue)             // SwiftUI Color\n.backgroundColor(.color(.green))           // Using BackgroundColor enum\n\n// Legacy approach (still works, but deprecated)\n.backgroundColor(\"transparent\")             // Deprecated but backward compatible\n```\n\n#### Typography \u0026 Colors\n\n```swift\n// Font configuration\n.fontType(.system)                         // System font (default)\n.fontType(.monospaced)                     // Monospaced font\n.fontType(.italic)                         // Italic font\n.fontType(.customName(\"Helvetica\"))        // Custom font by name\n.fontType(.custom(UIFont.systemFont(ofSize: 16))) // Custom UIFont (iOS only)\n\n// Text colors - Modern API (v3.0.0+)\n.textColor(light: .primary, dark: .primary)         // Modern semantic naming\n\n// Legacy text colors (deprecated but supported)\n.foregroundColor(light: .primary, dark: .primary)   // SwiftUI Colors (deprecated)\n.foregroundColor(light: UIColor.black, dark: UIColor.white) // UIColors (deprecated)\n.foregroundColor(light: NSColor.black, dark: NSColor.white) // NSColors (deprecated)\n\n// Link colors\n.linkColor(light: .blue, dark: .cyan)      // SwiftUI Colors\n.linkColor(light: UIColor.blue, dark: UIColor.cyan) // UIColors\n\n// Color enforcement\n.colorPreference(forceColor: .onlyLinks)   // Force only link colors (default)\n.colorPreference(forceColor: .all)         // Force all colors\n.colorPreference(forceColor: .none)        // Don't force any colors\n```\n\n#### Layout \u0026 Spacing\n\n```swift\n.lineHeight(170)                           // Line height percentage (default: 170)\n.imageRadius(12)                           // Image border radius in points (default: 0)\n.colorScheme(.auto)                        // .auto (default), .light, .dark\n.forceColorSchemeBackground(true)          // Force background color override\n```\n\n#### Link Behavior\n\n```swift\n.linkOpenType(.Safari)                     // Open in Safari (default)\n.linkOpenType(.SFSafariView())            // Open in SFSafariViewController (iOS)\n.linkOpenType(.SFSafariView(               // Advanced SFSafariView config\n    configuration: config,\n    isReaderActivated: true,\n    isAnimated: true\n))\n.linkOpenType(.custom { url in             // Custom link handler\n    // Handle URL yourself\n})\n.linkOpenType(.none)                       // Don't handle link taps\n```\n\n### Advanced Features\n\n#### Loading States\n\n```swift\n// Loading placeholders (Modern approach - recommended)\n.placeholder {                             // Custom placeholder view\n    HStack(spacing: 8) {\n        ProgressView()\n            .scaleEffect(0.8)\n        Text(\"Loading content...\")\n            .foregroundColor(.secondary)\n    }\n    .frame(minHeight: 60)\n}\n\n// Deprecated methods (still supported for backward compatibility)\n.loadingPlaceholder(\"Loading...\")          // Deprecated - use placeholder {}\n.loadingText(\"Please wait...\")             // Deprecated - use placeholder {}\n\n// Loading transitions\n.loadingTransition(.fade)                  // Fade transition\n.loadingTransition(.slide)                 // Slide transition\n.loadingTransition(.scale)                 // Scale transition\n.loadingTransition(.custom(.easeInOut))    // Custom animation\n.transition(.easeOut)                      // Legacy transition method\n```\n\n#### Event Handling\n\n```swift\n// Media click events (v3.0.0+)\n.onMediaClick { media in\n    switch media {\n    case .image(let src):\n        // Handle image clicks\n        presentImageViewer(src)\n    case .video(let src):\n        // Handle video clicks\n        presentVideoPlayer(src)\n    }\n}\n\n// Error handling (v3.0.0+)\n.onError { error in\n    switch error {\n    case .htmlLoadingFailed(let html):\n        print(\"Failed to load HTML: \\(html)\")\n    case .webViewConfigurationFailed:\n        print(\"WebView configuration failed\")\n    case .cssGenerationFailed:\n        print(\"CSS generation failed\")\n    case .mediaHandlingFailed(let media):\n        print(\"Media handling failed: \\(media)\")\n    }\n}\n```\n\n#### Custom Styling\n\n```swift\n// Custom CSS\n.customCSS(\"\"\"\n    p { margin: 10px 0; }\n    h1 { color: #ff6b6b; }\n    img { box-shadow: 0 2px 8px rgba(0,0,0,0.1); }\n\"\"\")\n\n// Base URL for relative resources\n.baseURL(Bundle.main.bundleURL)\n```\n\n### Configuration-Based Initialization\n\nFor complex configurations, create a `Configuration` object:\n\n```swift\nlet config = Configuration(\n    customCSS: \"body { padding: 20px; }\",\n    supportsDynamicType: true,              // Enable Dynamic Type\n    fontType: .system,\n    fontColor: ColorSet(light: \"333333\", dark: \"CCCCCC\"),\n    lineHeight: 180,\n    colorScheme: .auto,\n    forceColorSchemeBackground: false,\n    backgroundColor: .transparent,\n    imageRadius: 8,\n    linkOpenType: .Safari,\n    linkColor: ColorSet(light: \"007AFF\", dark: \"0A84FF\", isImportant: true),\n    baseURL: Bundle.main.bundleURL,\n    mediaClickHandler: { media in /* handle clicks */ },\n    errorHandler: { error in /* handle errors */ },\n    isColorsImportant: .onlyLinks,\n    transition: .easeInOut(duration: 0.3)\n)\n\nRichText(html: htmlContent, configuration: config)\n```\n\n### Utility Methods\n\n```swift\n// Generate CSS programmatically (v3.0.0+)\nlet richText = RichText(html: html)\nlet css = richText.generateCSS(colorScheme: .light, alignment: .center)\n\n// Generate CSS from configuration\nlet config = Configuration(lineHeight: 150)\nlet css = config.generateCompleteCSS(colorScheme: .dark)\n```\n\n---\n\n## 🆕 What's New in v3.0.0\n\n### 🚀 **Core Modernization**\n\n- **⚡ Async/Await Architecture**: Complete rewrite using modern Swift concurrency for better performance and reliability\n- **🛡️ Enhanced Type Safety**: Robust ColorSet equality comparison and validation with RGBA-based color handling\n- **⚙️ Performance Optimizations**: Frame update debouncing, improved WebView management, and reduced main thread blocking\n- **📊 Comprehensive Logging**: Built-in performance monitoring with os.log integration\n\n### 🎨 **Enhanced User Experience**\n\n- **🎨 Type-Safe Background Colors**: Complete background color system with `.transparent`, `.system`, `.hex()`, and `.color()` support\n- **📱 Interactive Media Handling**: Full media click event system for images and videos with custom action support\n- **🔧 Improved Font System**: Better monospace and italic rendering with enhanced CSS generation\n- **🔄 Modern Loading States**: Type-safe loading transitions with `.fade`, `.scale`, `.slide`, and custom animations\n\n### 🛠️ **Developer Experience**\n\n- **🧪 Swift Testing Migration**: Complete migration from XCTest to modern Swift Testing framework\n- **📖 Semantic API Naming**: Modern APIs like `.textColor()` replacing `.foregroundColor()` for better clarity\n- **🚨 Comprehensive Error Handling**: Detailed error types with custom callbacks and debugging support\n- **🛠️ Public CSS Access**: Programmatic CSS generation and access for advanced customization scenarios\n- **🌐 Enhanced HTML5 Support**: Complete support for `\u003cfigure\u003e`, `\u003cdetails\u003e`, `\u003csummary\u003e`, `\u003cfigcaption\u003e`, and semantic elements\n\n### 🔄 **Migration \u0026 Compatibility**\n\n- **✅ 100% Backward Compatible**: All v2.x code works without changes\n- **⚠️ Thoughtful Deprecations**: Deprecated methods include clear migration guidance\n- **📚 Migration Tooling**: Built-in TestApp with Modern API demo and migration examples\n\n### 🔄 **Backward Compatibility Promise**\n\nVersion 3.0.0 maintains **100% backward compatibility** for v2.x users while providing a clear path to modern APIs:\n\n- ✅ **Zero Breaking Changes**: All existing v2.x code works unchanged\n- ✅ **Automatic Performance**: Better async/await performance and font rendering without code changes  \n- ✅ **Guided Migration**: Helpful deprecation warnings with clear modern API alternatives\n- ✅ **Additive Enhancement**: New features are optional and don't affect existing functionality\n- ✅ **Future-Proof**: Modern architecture ready for Swift 6+ and future iOS/macOS versions\n\n### 🎯 **Recommended Migration Path**\n\n1. **Update to v3.0.0**: Immediate performance and reliability improvements\n2. **Add Error Handling**: Use `.onError()` for better debugging and user experience\n3. **Modernize APIs**: Replace deprecated methods with type-safe alternatives\n4. **Enhance Interactivity**: Add `.onMediaClick()` for rich media experiences\n5. **Improve Loading UX**: Implement `.placeholder {}` and modern transitions\n\n---\n\n## 🔧 Advanced Usage\n\n### Custom Fonts\n\n#### Using System-Installed Fonts\n\n```swift\nRichText(html: html)\n    .fontType(.customName(\"SF Mono\"))      // System monospace font\n    .fontType(.customName(\"Helvetica\"))    // System Helvetica\n```\n\n#### Using Bundled Fonts\n\n```swift\nRichText(html: html)\n    .fontType(.customName(\"CustomFont-Regular\"))\n    .customCSS(\"\"\"\n        @font-face {\n            font-family: 'CustomFont-Regular';\n            src: url(\"CustomFont-Regular.ttf\") format('truetype');\n        }\n    \"\"\")\n```\n\n#### Dynamic Type Support\n\n```swift\nlet config = Configuration(\n    supportsDynamicType: true               // Automatically use iOS Dynamic Type\n)\n\nRichText(html: html, configuration: config)\n```\n\n### Complex Color Schemes\n\n#### Gradient Backgrounds\n\n```swift\nRichText(html: html)\n    .backgroundColor(.transparent)\n    .customCSS(\"\"\"\n        body {\n            background: linear-gradient(45deg, #ff6b6b, #4ecdc4);\n            padding: 20px;\n            border-radius: 12px;\n        }\n    \"\"\")\n```\n\n#### Theme-Aware Colors\n\n```swift\nRichText(html: html)\n    .foregroundColor(light: .primary, dark: .primary)\n    .linkColor(light: .blue, dark: .cyan)\n    .backgroundColor(.system)\n    .colorPreference(forceColor: .all)      // Override HTML colors\n```\n\n### Interactive Media Handling\n\n```swift\nstruct ContentView: View {\n    @State private var selectedImage: String?\n    \n    var body: some View {\n        RichText(html: htmlWithImages)\n            .onMediaClick { media in\n                switch media {\n                case .image(let src):\n                    selectedImage = src\n                case .video(let src):\n                    openVideoPlayer(url: src)\n                }\n            }\n            .fullScreenCover(item: Binding\u003cString?\u003e(\n                get: { selectedImage },\n                set: { selectedImage = $0 }\n            )) { imageURL in\n                ImageViewer(url: imageURL)\n            }\n    }\n}\n```\n\n### Error Handling and Debugging\n\n```swift\nstruct ContentView: View {\n    @State private var lastError: RichTextError?\n    \n    var body: some View {\n        VStack {\n            if let error = lastError {\n                ErrorBanner(error: error)\n            }\n            \n            RichText(html: html)\n                .onError { error in\n                    lastError = error\n                    // Log to analytics\n                    Analytics.log(\"RichText Error\", parameters: [\n                        \"error_type\": String(describing: error),\n                        \"html_length\": html.count\n                    ])\n                }\n        }\n    }\n}\n```\n\n### Performance Optimization\n\n#### For Large Content\n\n```swift\nRichText(html: largeHtmlContent)\n    .imageRadius(0)                         // Disable image styling for performance\n    .customCSS(\"\"\"\n        img {\n            max-width: 100%;\n            height: auto;\n            loading: lazy;                  /* Native lazy loading */\n        }\n    \"\"\")\n    .loadingTransition(.none)              // Disable transitions for faster rendering\n```\n\n#### Memory Management\n\n```swift\nstruct ContentView: View {\n    @StateObject private var htmlManager = HTMLContentManager()\n    \n    var body: some View {\n        RichText(html: htmlManager.currentHTML)\n            .onError { error in\n                htmlManager.handleError(error)\n            }\n            .onDisappear {\n                htmlManager.cleanup()       // Custom cleanup logic\n            }\n    }\n}\n```\n\n---\n\n## 💡 Examples\n\n### Blog Post Renderer\n\n```swift\nstruct BlogPostView: View {\n    let post: BlogPost\n    \n    var body: some View {\n        ScrollView {\n            VStack(alignment: .leading, spacing: 16) {\n                Text(post.title)\n                    .font(.largeTitle)\n                    .fontWeight(.bold)\n                \n                RichText(html: post.content)\n                    .lineHeight(175)\n                    .imageRadius(8)\n                    .backgroundColor(.system)\n                    .linkOpenType(.SFSafariView())\n                    .onMediaClick { media in\n                        handleMediaClick(media)\n                    }\n                    .customCSS(\"\"\"\n                        blockquote {\n                            border-left: 4px solid #007AFF;\n                            padding-left: 16px;\n                            margin: 16px 0;\n                            font-style: italic;\n                        }\n                        code {\n                            background-color: #f5f5f5;\n                            padding: 2px 4px;\n                            border-radius: 3px;\n                        }\n                    \"\"\")\n            }\n            .padding()\n        }\n    }\n    \n    private func handleMediaClick(_ media: MediaClickType) {\n        // Custom media handling\n    }\n}\n```\n\n### Email Content Viewer\n\n```swift\nstruct EmailView: View {\n    let emailHTML: String\n    @State private var isLoading = true\n    \n    var body: some View {\n        RichText(html: emailHTML)\n            .backgroundColor(.system)\n            .lineHeight(160)\n            .fontType(.system)\n            .linkOpenType(.custom { url in\n                // Custom link handling for email safety\n                if url.host?.contains(\"trusted-domain.com\") == true {\n                    UIApplication.shared.open(url)\n                } else {\n                    showLinkConfirmation(url)\n                }\n            })\n            .placeholder {\n                Text(\"Loading email...\")\n            }\n            .loadingTransition(.fade)\n            .onError { error in\n                print(\"Email loading error: \\(error)\")\n            }\n    }\n    \n    private func showLinkConfirmation(_ url: URL) {\n        // Show confirmation dialog\n    }\n}\n```\n\n### Documentation Viewer\n\n```swift\nstruct DocumentationView: View {\n    let markdownHTML: String\n    \n    var body: some View {\n        NavigationView {\n            RichText(html: markdownHTML)\n                .fontType(.system)\n                .lineHeight(170)\n                .backgroundColor(.transparent)\n                .customCSS(\"\"\"\n                    h1, h2, h3 { \n                        color: #1d4ed8; \n                        margin-top: 24px;\n                        margin-bottom: 12px;\n                    }\n                    pre {\n                        background-color: #f8f9fa;\n                        padding: 12px;\n                        border-radius: 6px;\n                        overflow-x: auto;\n                    }\n                    code {\n                        font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;\n                    }\n                \"\"\")\n                .navigationTitle(\"Documentation\")\n                .navigationBarTitleDisplayMode(.large)\n        }\n    }\n}\n```\n\n---\n\n## 🐛 Troubleshooting\n\n### Common Issues\n\n#### Content Not Displaying\n\n**Problem**: RichText shows blank or doesn't render content\n\n**Solutions**:\n- Ensure HTML is valid and well-formed\n- Check that images have proper URLs\n- Verify network permissions for external resources\n- Add error handling to debug loading issues\n\n```swift\nRichText(html: html)\n    .onError { error in\n        print(\"Debug error: \\(error)\")\n    }\n```\n\n#### Images Not Loading\n\n**Problem**: Images don't appear in the rendered content\n\n**Solutions**:\n- Verify image URLs are accessible\n- For macOS: Enable \"Outgoing Connections (Client)\" in App Sandbox\n- Use base URL for relative image paths\n\n```swift\nRichText(html: html)\n    .baseURL(Bundle.main.bundleURL)  // For bundled resources\n```\n\n#### Performance Issues\n\n**Problem**: Slow rendering with large HTML content\n\n**Solutions**:\n- Simplify CSS and reduce inline styles\n- Use image compression for better loading\n- Consider pagination for very large content\n- Disable animations for better performance\n\n```swift\nRichText(html: largeContent)\n    .loadingTransition(.none)\n    .imageRadius(0)\n```\n\n#### Dark Mode Issues\n\n**Problem**: Colors don't adapt properly to dark mode\n\n**Solutions**:\n- Use `.colorScheme(.auto)` for automatic adaptation\n- Set proper light/dark colors for text and links\n- Force color scheme background if needed\n\n```swift\nRichText(html: html)\n    .colorScheme(.auto)\n    .forceColorSchemeBackground(true)\n    .foregroundColor(light: .black, dark: .white)\n```\n\n### Platform-Specific Issues\n\n#### macOS Specific\n\n**Issue**: External resources don't load\n- **Solution**: Enable \"Outgoing Connections (Client)\" in App Sandbox settings\n- **Alternative**: Use bundled resources or file URLs\n\n**Issue**: Scrolling behavior differs from iOS\n- **Solution**: This is expected due to platform differences\n- **Workaround**: Embed in a ScrollView for consistent behavior\n\n#### iOS Specific\n\n**Issue**: SFSafariViewController not presenting\n- **Solution**: Ensure you have a presented view controller\n- **Alternative**: Use `.linkOpenType(.Safari)` as fallback\n\n### Memory Management\n\nIf you experience memory issues with large content:\n\n```swift\n// Implement proper cleanup\nstruct ContentView: View {\n    @State private var html = \"\"\n    \n    var body: some View {\n        RichText(html: html)\n            .onDisappear {\n                html = \"\"  // Clear content when not visible\n            }\n    }\n}\n```\n\n### Getting Help\n\n1. **Check the Issues**: Search [GitHub Issues](https://github.com/NuPlay/RichText/issues) for similar problems\n2. **Provide Details**: When reporting issues, include:\n   - iOS/macOS version\n   - RichText version\n   - Sample HTML content\n   - Error messages or console output\n3. **Create Minimal Example**: Provide a minimal reproducible example\n\n---\n\n## 📖 Migration Guide\n\n### From v2.x to v3.0.0\n\n#### Background Colors\n\n```swift\n// ✅ v2.7.0 - Still works, but deprecated\nRichText(html: html)\n    .backgroundColor(\"transparent\")     // Deprecated but functional\n\n// 🚀 v3.0.0 - Recommended approaches\nRichText(html: html)\n    .transparentBackground()           // Easiest for transparent\n    .backgroundColorHex(\"#FF0000\")     // For hex colors  \n    .backgroundColorSwiftUI(.blue)     // For SwiftUI colors\n    .backgroundColor(.system)          // For system colors\n```\n\n#### Enhanced Features (Optional Upgrades)\n\n```swift\n// 🚀 Add error handling\nRichText(html: html)\n    .onError { error in\n        print(\"Error: \\(error)\")\n    }\n\n// 🚀 Add interactive media handling\nRichText(html: html)\n    .onMediaClick { media in\n        switch media {\n        case .image(let src):\n            presentImageViewer(src)\n        case .video(let src):\n            presentVideoPlayer(src)\n        }\n    }\n\n// 🚀 Better loading experience with custom view\nRichText(html: html)\n    .placeholder {\n        HStack(spacing: 8) {\n            ProgressView()\n                .scaleEffect(0.8)\n            Text(\"Loading...\")\n                .foregroundColor(.secondary)\n        }\n        .frame(minHeight: 60)\n    }\n    .loadingTransition(.fade)\n```\n\n#### Font \u0026 Color API Modernization\n\n```swift\n// ✅ v2.x - Still works, but deprecated\nRichText(html: html)\n    .foregroundColor(light: .black, dark: .white)  // Deprecated\n\n// 🚀 v3.0.0 - Modern semantic naming\nRichText(html: html)\n    .textColor(light: .black, dark: .white)        // Modern \u0026 clear\n```\n\n#### Enhanced Font Rendering\n\nNo changes needed - font rendering is automatically improved:\n\n```swift\n// ✅ Automatically better in v3.0.0 with async/await\nRichText(html: html)\n    .fontType(.monospaced)    // Enhanced rendering\n    .fontType(.italic)        // Improved CSS generation\n```\n\n### Recommended Migration Steps\n\n1. **Update to v3.0.0**: Your existing code continues to work\n2. **Add Error Handling**: Use `.onError()` for better debugging\n3. **Update Background Colors**: Replace string-based with type-safe methods\n4. **Add Media Handling**: Use `.onMediaClick()` for interactive content\n5. **Improve Loading UX**: Add `.placeholder {}` with custom views and transitions\n\n---\n\n## 🤝 Contributing\n\nWe welcome contributions! Here's how you can help:\n\n### Reporting Issues\n\n- Use [GitHub Issues](https://github.com/NuPlay/RichText/issues) for bug reports\n- Include reproduction steps and sample code\n- Specify iOS/macOS version and RichText version\n\n### Suggesting Features\n\n- Create a [Discussion](https://github.com/NuPlay/RichText/discussions) for feature requests\n- Explain the use case and expected behavior\n- Consider backward compatibility implications\n\n### Code Contributions\n\n1. **Fork** the repository\n2. **Create** a feature branch (`git checkout -b feature/amazing-feature`)\n3. **Write** tests for your changes\n4. **Commit** your changes (`git commit -m 'Add amazing feature'`)\n5. **Push** to the branch (`git push origin feature/amazing-feature`)\n6. **Open** a Pull Request\n\n### Development Guidelines\n\n- Follow Swift naming conventions and modern async/await patterns\n- Add comprehensive documentation for public APIs with usage examples\n- Ensure backward compatibility and provide clear migration paths\n- Use Swift Testing for all new test coverage\n- Update README.md and TestApp for new features\n- Consider performance implications and use os.log for debugging\n\n---\n\n## 📄 License\n\nRichText is available under the MIT license. See the [LICENSE](LICENSE) file for more info.\n\n---\n\n## 🙏 Acknowledgments\n\n- Built with [WebKit](https://webkit.org/) for reliable HTML rendering\n- Inspired by the SwiftUI community's need for rich text solutions\n- Thanks to all [contributors](https://github.com/NuPlay/RichText/contributors) and users\n\n---\n\n## 📞 Support\n\n- **Issues**: [GitHub Issues](https://github.com/NuPlay/RichText/issues)\n- **Discussions**: [GitHub Discussions](https://github.com/NuPlay/RichText/discussions)\n\n---\n\n*Made with ❤️ for the SwiftUI community*\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnuplay%2Frichtext","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnuplay%2Frichtext","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnuplay%2Frichtext/lists"}