Repository: shaps80/MarkdownText Branch: main Commit: c5f5e3e7d2de Files: 44 Total size: 85.4 KB Directory structure: gitextract_m34wpoa7/ ├── .gitignore ├── LICENSE.md ├── Package.resolved ├── Package.swift ├── README.md ├── Sources/ │ └── MarkdownText/ │ ├── Helpers/ │ │ ├── InlineStyle.swift │ │ ├── ListLabelStyle.swift │ │ └── Platform.swift │ ├── MarkdownBlockElement.swift │ ├── MarkdownInlineElement.swift │ ├── MarkdownListElement.swift │ ├── MarkdownText.swift │ ├── MarkdownTextBuilder.swift │ └── Styles/ │ ├── Block Elements/ │ │ ├── CodeStyle.swift │ │ ├── HeadingStyle.swift │ │ ├── Image Styles/ │ │ │ ├── DefaultImageStyle.swift │ │ │ ├── RemoteImageStyle.swift │ │ │ └── SymbolImageStyle.swift │ │ ├── ImageStyle.swift │ │ ├── ListStyle.swift │ │ ├── ParagraphStyle.swift │ │ ├── QuoteStyle.swift │ │ └── ThematicStyle.swift │ ├── Inline Elements/ │ │ ├── EmphasisStyle.swift │ │ ├── InlineCodeStyle.swift │ │ ├── InlineLinkStyle.swift │ │ ├── StrikethroughStyle.swift │ │ └── StrongStyle.swift │ ├── Lists/ │ │ ├── Bullets/ │ │ │ ├── ChecklistBulletStyle.swift │ │ │ ├── OrderedBulletStyle.swift │ │ │ └── UnorderedBulletStyle.swift │ │ └── Items/ │ │ ├── CheckedListItemStyle.swift │ │ ├── OrderedListItemStyle.swift │ │ └── UnorderedListItemStyle.swift │ └── Visibility/ │ ├── CheckListVisibility.swift │ ├── CodeVisibility.swift │ ├── HeadingVisibility.swift │ ├── ImageVisibility.swift │ ├── ListItemVisibility.swift │ ├── OrderedListVisibility.swift │ ├── QuoteVisibility.swift │ ├── ThematicVisibility.swift │ └── UnorderedListVisibility.swift └── generate-docs.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: LICENSE.md ================================================ MIT License Copyright (c) 2021 Shaps Benkau Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Package.resolved ================================================ { "pins" : [ { "identity" : "swift-cmark", "kind" : "remoteSourceControl", "location" : "https://github.com/shaps80/swift-cmark.git", "state" : { "revision" : "476f7b4fcf12eba381b4aaed8987006fd8fcec9c", "version" : "0.2.0" } }, { "identity" : "swift-markdown", "kind" : "remoteSourceControl", "location" : "https://github.com/shaps80/swift-markdown", "state" : { "revision" : "83188dee2dbccfa70124c56055dd0b759d3e4ec3", "version" : "0.3.0" } }, { "identity" : "swiftuibackports", "kind" : "remoteSourceControl", "location" : "https://github.com/shaps80/SwiftUIBackports", "state" : { "revision" : "252d01385a0730cc604310373ac934a64f1d4b1a", "version" : "1.8.2" } } ], "version" : 2 } ================================================ FILE: Package.swift ================================================ // swift-tools-version: 5.6 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "MarkdownText", platforms: [ .iOS(.v13), .macOS(.v11), ], products: [ .library( name: "MarkdownText", targets: ["MarkdownText"] ), ], dependencies: [ .package(url: "https://github.com/shaps80/swift-markdown", .upToNextMinor(from: "0.3.0")), .package(url: "https://github.com/shaps80/SwiftUIBackports", .upToNextMajor(from: "2.0.0")), ], targets: [ .target( name: "MarkdownText", dependencies: [ .product(name: "Markdown", package: "swift-markdown"), .byName(name: "SwiftUIBackports"), ] ), ] ) ================================================ FILE: README.md ================================================ ![macOS](https://img.shields.io/badge/macOS-EE751F) ![ios](https://img.shields.io/badge/iOS-0C62C7) [![swift](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fshaps80%2FMarkdownText%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/shaps80/MarkdownText) # MarkdownText A native SwiftUI view for rendering Markdown text in an iOS or macOS app. ## Preview ![Markdown Text Screenshot](Resources/screenshot.jpg) ## Sponsor Building useful libraries like these, takes time away from my family. I build these tools in my spare time because I feel its important to give back to the community. Please consider [Sponsoring](https://github.com/sponsors/shaps80) me as it helps keep me working on useful libraries like these 😬 You can also give me a follow and a 'thanks' anytime. [![Twitter](https://img.shields.io/badge/Twitter-@shaps-4AC71B)](http://twitter.com/shaps) ## Supported Markdown - Headings - Paragraphs - Quotes - Inline formatting - Strong/Bold - Emphasis/Italic - Strikethrough - Code - Links (non interactive only) - Lists - Ordered - Unordered - Checklist (GitHub style) - Thematic Breaks - Code Blocks - Images - A full backport of `AsyncImage` is included ## Features **Style APIs** Adopting the familiar SwiftUI style-based APIs, you can customize the appearance of almost all markdown elements either individually or composed. ```swift struct CustomUnorderedBullets: UnorderedListBulletMarkdownStyle { func makeBody(configuration: Configuration) -> some View { // you can also provide a completely new View if preferred 👍 configuration.label .foregroundColor(.blue) } } ``` You can even customize animations since the library is composed of 100% SwiftUI elements only. ```swift struct ScaledImageStyle: ImageMarkdownStyle { // image will scale up as its loaded, moving content out of the way func makeBody(configuration: Configuration) -> some View { configuration.label .transition(.scale) } } ``` Modifiers for styling and visibility can also be placed anywhere in your SwiftUI hierarchy, just as you'd expect: ``` NavigationView { MarkdownText(markdown) } // Styling .markdownQuoteStyle(.inset) .markdownOrderedListBulletStyle(.tinted) .markdownImageStyle(.animated) // Visibility .markdownCode(.visible) .markdownThematicBreak(.hidden) ``` ## Demo App A [MarkdownText Demo](https://github.com/shaps80/MarkdownTextDemo) is also available to better showcase the libraries capabilities. ## Usage Using the view couldn't be easier: ```swift MarkdownText("Some **markdown** text") LazyMarkdownText(someMassiveMarkdownText) ``` There's even a `LazyMarkdownText` view that loads its view's lazily for those cases where you need improved scrolling and loading performance. ## Installation You can install manually (by copying the files in the `Sources` directory) or using Swift Package Manager (**preferred**) To install using Swift Package Manager, add this to the `dependencies` section of your `Package.swift` file: `.package(url: "https://github.com/shaps80/MarkdownText.git", .upToNextMinor(from: "1.0.0"))` ================================================ FILE: Sources/MarkdownText/Helpers/InlineStyle.swift ================================================ import SwiftUI struct InlineMarkdownConfiguration { struct Label: View { @Environment(\.font) private var font @Environment(\.markdownStrongStyle) private var strong @Environment(\.markdownEmphasisStyle) private var emphasis @Environment(\.markdownStrikethroughStyle) private var strikethrough @Environment(\.markdownInlineCodeStyle) private var code @Environment(\.markdownInlineLinkStyle) private var link let elements: [MarkdownInlineElement] var body: some View { elements.reduce(into: Text("")) { result, component in if component.attributes.contains(.code) { return result = result + code.makeBody( configuration: .init(code: component.content, font: font) ) } else { return result = result + Text(component.content).apply( strong: strong, emphasis: emphasis, strikethrough: strikethrough, link: link, attributes: component.attributes ) } } } } public let elements: [MarkdownInlineElement] public var label: some View { Label(elements: elements) .fixedSize(horizontal: false, vertical: true) } } struct InlineMarkdownStyle { func makeBody(configuration: InlineMarkdownConfiguration) -> some View { configuration.label } } ================================================ FILE: Sources/MarkdownText/Helpers/ListLabelStyle.swift ================================================ import SwiftUI import SwiftUIBackports struct ListLabelStyle: LabelStyle { struct Content: View { let configuration: Configuration var body: some View { HStack(alignment: .firstTextBaseline, spacing: 0) { configuration.icon Text(Array(repeating: " ", count: 1).joined()) configuration.title } } } init() { } func makeBody(configuration: Configuration) -> some View { Content(configuration: configuration) } } extension LabelStyle where Self == ListLabelStyle { static var list: Self { .init() } } ================================================ FILE: Sources/MarkdownText/Helpers/Platform.swift ================================================ import SwiftUI import SwiftUIBackports #if os(macOS) typealias ScaledMetric = SwiftUI.ScaledMetric typealias LabelStyle = SwiftUI.LabelStyle typealias Label = SwiftUI.Label typealias ProgressView = SwiftUI.ProgressView #else typealias ScaledMetric = Backport.ScaledMetric typealias LabelStyle = SwiftUIBackports.BackportLabelStyle typealias Label = Backport.Label typealias ProgressView = Backport.ProgressView extension View { func labelStyle(_ style: S) -> some View { backport.labelStyle(style) } } #endif ================================================ FILE: Sources/MarkdownText/MarkdownBlockElement.swift ================================================ import Foundation // Used during parsing to store all discovered block elements enum MarkdownBlockElement { case heading(HeadingMarkdownConfiguration) case paragraph(ParagraphMarkdownConfiguration) case quote(QuoteMarkdownConfiguration) case list(ListStyleMarkdownConfiguration) case code(CodeMarkdownConfiguration) case image(ImageMarkdownConfiguration) case thematicBreak(ThematicMarkdownConfiguration) case inline(InlineMarkdownConfiguration) } ================================================ FILE: Sources/MarkdownText/MarkdownInlineElement.swift ================================================ import SwiftUI /// Represents a single inline element, including any applied attributes (e.g. strong, italic, etc) public struct MarkdownInlineElement { /// The string content for this inline element public var content: String /// The attributes to apply to this content public var attributes: InlineAttributes = [] } /// Represents the supported attributes for an inline element public struct InlineAttributes: OptionSet, CustomStringConvertible { public let rawValue: Int public init(rawValue: Int) { self.rawValue = rawValue } /// A `bold` representation should be applied public static let bold = InlineAttributes(rawValue: 1 << 0) /// An `italic` representation should be applied public static let italic = InlineAttributes(rawValue: 1 << 1) /// A `strikethrough` representation should be applied public static let strikethrough = InlineAttributes(rawValue: 1 << 2) /// A `monospaced` representation should be applied public static let code = InlineAttributes(rawValue: 1 << 3) /// A link representation should be applied public static let link = InlineAttributes(rawValue: 1 << 4) public var description: String { var elements: [String] = [] if contains(.bold) { elements.append("bold") } if contains(.italic) { elements.append("italic") } if contains(.strikethrough) { elements.append("strikethrough") } if contains(.code) { elements.append("code") } return elements.joined(separator: ", ") } } internal extension Text { func apply( strong: StrongMarkdownStyle, emphasis: EmphasisMarkdownStyle, strikethrough: StrikethroughMarkdownStyle, link: InlineLinkMarkdownStyle, attributes: InlineAttributes ) -> Self { var text = self if attributes.contains(.bold) { text = strong.makeBody(configuration: .init(content: text)) } if attributes.contains(.italic) { text = emphasis.makeBody(configuration: .init(content: text)) } if attributes.contains(.strikethrough) { text = strikethrough.makeBody(configuration: .init(content: text)) } if attributes.contains(.link) { text = link.makeBody(configuration: .init(content: text)) } return text } } ================================================ FILE: Sources/MarkdownText/MarkdownListElement.swift ================================================ import Foundation /// Represents a list markdown element public struct MarkdownList { /// Represents the types of lists public enum ListType { /// An unordered list case unordered /// An ordered list case ordered } /// The type of list this represents public let type: ListType /// The elements contained in this list. This could be single elements, or even another nested list public var elements: [MarkdownListElement] = [] internal mutating func append(ordered item: OrderedListItemMarkdownConfiguration) { elements.append(.ordered(item)) } internal mutating func append(unordered item: UnorderedListItemMarkdownConfiguration) { elements.append(.unordered(item)) } internal mutating func append(checklist item: CheckListItemMarkdownConfiguration) { elements.append(.checklist(item)) } internal mutating func append(nested list: Self) { elements.append(.list(list)) } } /// Represents a list, including any nested list elements public enum MarkdownListElement { /// A nested list case list(MarkdownList) /// An ordered list case ordered(OrderedListItemMarkdownConfiguration) /// An unordered list case unordered(UnorderedListItemMarkdownConfiguration) /// A checked list case checklist(CheckListItemMarkdownConfiguration) } ================================================ FILE: Sources/MarkdownText/MarkdownText.swift ================================================ import SwiftUI import Markdown /// A view that renders Markdown text, but only creates elements as they are needed. /// /// The stack is "lazy," in that the stack view doesn't create items until /// it needs to render them onscreen. @available(iOS 14, *) public struct LazyMarkdownText: View, MarkupWalker { private let content: MarkdownContent public var body: some View { content } /// Creates a new Markdown view /// - Parameters: /// - markdown: The markdown text to render /// - source: An explicit source URL from which the input string came for marking source locations. This need not be a file URL. /// - paragraphSpacing: The spacing to apply between all block elements public init(_ markdown: String, source: URL? = nil, paragraphSpacing: CGFloat? = 20) { let elements = MarkdownTextBuilder( document: Document(parsing: markdown, source: source) ).blockElements content = .init(elements: elements, paragraphSpacing: paragraphSpacing, isLazy: true) } } /// A view that rendered Markdown text. public struct MarkdownText: View, MarkupWalker { private let content: MarkdownContent public var body: some View { content } /// Creates a new Markdown view /// - Parameters: /// - markdown: The markdown text to render /// - source: An explicit source URL from which the input string came for marking source locations. This need not be a file URL. /// - paragraphSpacing: The spacing to apply between all block elements public init(_ markdown: String, source: URL? = nil, paragraphSpacing: CGFloat? = 20) { let elements = MarkdownTextBuilder( document: Document(parsing: markdown, source: source) ).blockElements content = .init(elements: elements, paragraphSpacing: paragraphSpacing, isLazy: false) } } private struct MarkdownContent: View { @Environment(\.multilineTextAlignment) private var alignment @Environment(\.markdownHeadingStyle) private var headerStyle @Environment(\.markdownParagraphStyle) private var paragraphStyle @Environment(\.markdownQuoteStyle) private var quoteStyle @Environment(\.markdownCodeStyle) private var codeStyle @Environment(\.markdownThematicBreakStyle) private var thematicBreak @Environment(\.markdownListStyle) private var listStyle @Environment(\.markdownImageStyle) private var imageStyle @Environment(\.markdownCodeVisibility) private var codeVisibility @Environment(\.markdownImageVisibility) private var imageVisibility @Environment(\.markdownHeadingVisibility) private var headingVisibility @Environment(\.markdownQuoteListVisibility) private var quoteVisibility @Environment(\.markdownThematicBreakVisibility) private var thematicBreakVisibility @Environment(\.markdownListVisibility) private var listVisibility private var inlineStyle = InlineMarkdownStyle() private var content: some View { ForEach(elements.indices, id: \.self) { index in switch elements[index] { case let .heading(config): if headingVisibility != .hidden { AnyView(headerStyle.makeBody(configuration: config)) } case let .quote(config): if quoteVisibility != .hidden { AnyView(quoteStyle.makeBody(configuration: config)) } case let .code(config): if codeVisibility != .hidden { AnyView(codeStyle.makeBody(configuration: config)) } case let .thematicBreak(config): if thematicBreakVisibility != .hidden { AnyView(thematicBreak.makeBody(configuration: config)) } case let .image(config): if imageVisibility != .hidden { AnyView(imageStyle.makeBody(configuration: config)) } case let .list(config): if listVisibility != .hidden { AnyView(listStyle.makeBody(configuration: config)) } case let .paragraph(config): AnyView(paragraphStyle.makeBody(configuration: config)) case let .inline(config): AnyView(inlineStyle.makeBody(configuration: config)) } } } let elements: [MarkdownBlockElement] let paragraphSpacing: CGFloat? let isLazy: Bool private var stackAlignment: HorizontalAlignment { alignment == .leading ? .leading : alignment == .trailing ? .trailing : .center } init(elements: [MarkdownBlockElement], paragraphSpacing: CGFloat?, isLazy: Bool) { self.elements = elements self.paragraphSpacing = paragraphSpacing self.isLazy = isLazy } public var body: some View { if isLazy { if #available(iOS 14.0, *) { LazyVStack(alignment: stackAlignment, spacing: paragraphSpacing) { content } } else { VStack(alignment: stackAlignment, spacing: paragraphSpacing) { content } } } else { VStack(alignment: stackAlignment, spacing: paragraphSpacing) { content } } } } ================================================ FILE: Sources/MarkdownText/MarkdownTextBuilder.swift ================================================ import SwiftUI import Markdown struct MarkdownTextBuilder: MarkupWalker { var isNested: Bool = false var nestedBlockElements: [MarkdownBlockElement] = [] var inlineElements: [MarkdownInlineElement] = [] var blockElements: [MarkdownBlockElement] = [] var lists: [MarkdownList] = [] init(document: Document) { visit(document) } mutating func visitHeading(_ markdown: Heading) { descendInto(markdown) blockElements.append(.heading(.init(level: markdown.level, content: .init(elements: inlineElements)))) inlineElements = [] } mutating func visitText(_ markdown: Markdown.Text) { var attributes: InlineAttributes = [] var parent = markdown.parent var text = markdown.string while parent != nil { defer { parent = parent?.parent } if parent is Strong { attributes.insert(.bold) } if parent is Emphasis { attributes.insert(.italic) } if parent is Strikethrough { attributes.insert(.strikethrough) } if parent is InlineCode { attributes.insert(.code) } if let link = parent as? Markdown.Link { /* One idea here could be to collect links like footnotes, reference them in the rendered result as such (at least by default) and then add actual buttons to the bottom of the rendered output? */ attributes.insert(.link) text = link.plainText // + (link.destination.flatMap { " [\($0)]" } ?? "") } } inlineElements.append(.init(content: .init(text), attributes: attributes)) } mutating func visitOrderedList(_ markdown: OrderedList) { lists.append(.init(type: .ordered)) descendInto(markdown) if let list = lists.last { if lists.count == 1 { // if we're at the root element, add the the tree to the block elements blockElements.append(.list(.init(list: list, level: lists.count - 1))) } else { // otherwise, append nested lists to the last list let index = lists.index(before: lists.index(before: lists.endIndex)) lists[index].append(nested: list) } } lists.removeLast() } mutating func visitUnorderedList(_ markdown: UnorderedList) { lists.append(.init(type: .unordered)) descendInto(markdown) if let list = lists.last { if lists.count == 1 { // if we're at the root element, add the the tree to the block elements blockElements.append(.list(.init(list: list, level: lists.count - 1))) } else { // otherwise, append nested lists to the last list let index = lists.index(before: lists.index(before: lists.endIndex)) lists[index].append(nested: list) } } lists.removeLast() } mutating func visitListItem(_ markdown: Markdown.ListItem) { descendInto(markdown) } mutating func visitParagraph(_ markdown: Paragraph) { descendInto(markdown) if let listItem = markdown.parent as? ListItem { let index = lists.index(before: lists.endIndex) switch lists[index].type { case .ordered: lists[index].append(ordered: .init( level: lists.count - 1, bullet: .init(order: listItem.indexInParent + 1), content: .init(content: .init(elements: inlineElements)) ) ) default: if let checkbox = listItem.checkbox { lists[index].append(checklist: .init( level: lists.count - 1, bullet: .init(isChecked: checkbox == .checked), content: .init(content: .init(elements: inlineElements)) ) ) } else { lists[index].append(unordered: .init( level: lists.count - 1, bullet: .init(level: lists.count - 1), content: .init(content: .init(elements: inlineElements)) ) ) } } } else { if isNested { nestedBlockElements.append(.paragraph(.init(content: .init(elements: inlineElements)))) } else { blockElements.append(.paragraph(.init(content: .init(elements: inlineElements)))) } } inlineElements = [] } mutating func visitImage(_ markdown: Markdown.Image) { let title = markdown.title ?? "" blockElements.append(.image(.init(source: markdown.source, title: title.isEmpty ? nil : title))) } mutating func visitLink(_ markdown: Markdown.Link) { descendInto(markdown) } mutating func visitStrong(_ markdown: Strong) { descendInto(markdown) } mutating func visitEmphasis(_ markdown: Emphasis) { descendInto(markdown) } mutating func visitInlineCode(_ markdown: InlineCode) { inlineElements.append(.init(content: .init(markdown.code), attributes: .code)) } mutating func visitStrikethrough(_ markdown: Strikethrough) { descendInto(markdown) } mutating func visitCodeBlock(_ markdown: CodeBlock) { blockElements.append(.code(.init(code: markdown.code, language: markdown.language))) inlineElements = [] } mutating func visitThematicBreak(_ markdown: ThematicBreak) { blockElements.append(.thematicBreak(.init())) descendInto(markdown) } mutating func visitBlockQuote(_ markdown: BlockQuote) { isNested = true descendInto(markdown) for element in nestedBlockElements { if case let .paragraph(config) = element { blockElements.append(.quote(.init(content: config))) } } inlineElements = [] nestedBlockElements = [] isNested = false } mutating func visitSoftBreak(_ markdown: SoftBreak) { visitText(.init(markdown.plainText)) } mutating func visitTable(_: Markdown.Table) { } mutating func visitTableRow(_: Markdown.Table.Row) { } mutating func visitTableBody(_: Markdown.Table.Body) { } mutating func visitTableCell(_: Markdown.Table.Cell) { } mutating func visitTableHead(_: Markdown.Table.Head) { } mutating func visitSymbolLink(_: SymbolLink) { } mutating func visitBlockDirective(_: BlockDirective) { } mutating func visitCustomInline(_: CustomInline) { } mutating func visitHTMLBlock(_: HTMLBlock) { } mutating func visitInlineHTML(_: InlineHTML) { } } ================================================ FILE: Sources/MarkdownText/Styles/Block Elements/CodeStyle.swift ================================================ import SwiftUI /// A type that applies a custom appearance to code block markdown elements public protocol CodeMarkdownStyle { associatedtype Body: View /// The properties of an code block markdown element typealias Configuration = CodeMarkdownConfiguration /// Creates a view that represents the body of a label @ViewBuilder func makeBody(configuration: Configuration) -> Body } /// The properties of a code block markdown element public struct CodeMarkdownConfiguration { /// The raw code for this element public let code: String /// The code language for this element public let language: String? struct Label: View { @Environment(\.font) private var font let code: String let language: String? var body: some View { #if os(macOS) if #available(macOS 12, *) { Text(code.trimmingCharacters(in: .newlines)) .font( font?.monospaced() ?? .system(.body, design: .monospaced) ) } else { Text(code.trimmingCharacters(in: .newlines)) .font(.system(.body, design: .monospaced)) } #elseif os(iOS) if #available(iOS 15, *) { Text(code.trimmingCharacters(in: .newlines)) .font( font?.monospaced() ?? .system(.body, design: .monospaced) ) } else { Text(code.trimmingCharacters(in: .newlines)) .font(.system(.body, design: .monospaced)) } #else Text(code.trimmingCharacters(in: .newlines)) .font(.system(.body, design: .monospaced)) #endif } } /// Returns a default code block markdown representation public var label: some View { Label(code: code, language: language) .font(.callout) .lineSpacing(5) .environment(\.layoutDirection, .leftToRight) } } /// A code block style that applies a monospaced representation to its content and wraps it in a horizontal `ScrollView` public struct DefaultCodeMarkdownStyle: CodeMarkdownStyle { var axes: Axis.Set var showsIndicators: Bool /// Creates a new instance of this style /// - Parameters: /// - axes: The scrollable axes /// - showsIndicators: If `true`, scroll indicators will be visible when required public init(_ axes: Axis.Set = .horizontal, showsIndicators: Bool = false) { self.axes = axes self.showsIndicators = showsIndicators } public func makeBody(configuration: Configuration) -> some View { ScrollView(axes, showsIndicators: showsIndicators) { configuration.label } } } public extension CodeMarkdownStyle where Self == DefaultCodeMarkdownStyle { /// A code block style that applies a monospaced representation to its content and wraps it in a horizontal `ScrollView` static var `default`: Self { .init() } /// A code block style that applies a monospaced representation to its content and wraps it in a horizontal `ScrollView` /// - Parameters: /// - axes: The scrollable axes /// - showsIndicators: If `true`, scroll indicators will be visible when required static func `default`(_ axes: Axis.Set, showsIndicators: Bool = false) -> Self { .init(axes, showsIndicators: showsIndicators) } } private struct CodeMarkdownEnvironmentKey: EnvironmentKey { static let defaultValue: any CodeMarkdownStyle = DefaultCodeMarkdownStyle() } public extension EnvironmentValues { /// The current code block markdown style var markdownCodeStyle: any CodeMarkdownStyle { get { self[CodeMarkdownEnvironmentKey.self] } set { self[CodeMarkdownEnvironmentKey.self] = newValue } } } public extension View { /// Sets the style for code block markdown elements func markdownCodeStyle(_ style: some CodeMarkdownStyle) -> some View { environment(\.markdownCodeStyle, style) } } ================================================ FILE: Sources/MarkdownText/Styles/Block Elements/HeadingStyle.swift ================================================ import SwiftUI /// A type that applies a custom appearance to heading markdown elements public protocol HeadingMarkdownStyle { associatedtype Body: View /// The properties of a heading markdown element typealias Configuration = HeadingMarkdownConfiguration /// Creates a view that represents the body of a label @ViewBuilder func makeBody(configuration: Configuration) -> Body } /// The properties of a heading markdown element public struct HeadingMarkdownConfiguration { /// The header level (e.g. `H2` would have a level of `2`) public let level: Int /// The content for this element /// /// You can use this to maintain the existing heading style: /// /// content.label // maintains its font style /// .foregroundColor(.accentColor) let content: InlineMarkdownConfiguration /// The preferred text tyle for this heading. public var preferredStyle: Font.TextStyle { switch level { case 1: return .title case 2: if #available(iOS 14.0, *) { return .title2 } else { return .title } case 3: if #available(iOS 14.0, *) { return .title3 } else { return .title } case 4: return .headline default: return .subheadline } } private struct Label: View { public let level: Int let content: InlineMarkdownConfiguration var body: some View { content.label } } /// Returns a default heading markdown representation public var label: some View { Label(level: level, content: content) .font(.system(preferredStyle).weight(.bold)) } } /// A heading style that applies a preferred font style based on the heading level public struct DefaultHeadingMarkdownStyle: HeadingMarkdownStyle { public init() { } public func makeBody(configuration: Configuration) -> some View { configuration.label } } public extension HeadingMarkdownStyle where Self == DefaultHeadingMarkdownStyle { /// A heading style that applies a preferred font style based on the heading level static var `default`: Self { .init() } } private struct HeadingMarkdownEnvironmentKey: EnvironmentKey { static let defaultValue: any HeadingMarkdownStyle = DefaultHeadingMarkdownStyle() } public extension EnvironmentValues { /// The current heading markdown style var markdownHeadingStyle: any HeadingMarkdownStyle { get { self[HeadingMarkdownEnvironmentKey.self] } set { self[HeadingMarkdownEnvironmentKey.self] = newValue } } } public extension View { /// Sets the style for heading markdown elements func markdownHeadingStyle(_ style: some HeadingMarkdownStyle) -> some View { environment(\.markdownHeadingStyle, style) } } ================================================ FILE: Sources/MarkdownText/Styles/Block Elements/Image Styles/DefaultImageStyle.swift ================================================ import SwiftUI /// An image style that loads content asynchronously if a valid URL is supplied, otherwise tries to load an SFSymbol public struct DefaultImageMarkdownStyle: ImageMarkdownStyle { public func makeBody(configuration: Configuration) -> some View { if let source = configuration.source, let url = URL(string: source), url.scheme != nil { RemoteImageMarkdownStyle() .makeBody(configuration: configuration) } else { SFSymbolImageMarkdownStyle() .makeBody(configuration: configuration) } } } public extension ImageMarkdownStyle where Self == DefaultImageMarkdownStyle { /// A default image style that loads content asynchronously if a valid URL is supplied, otherwise tries to load an SFSymbol /// /// The following example will load the `star` SF Symbol /// /// ![][star] /// /// To render a remote image: /// /// ![Lorem Image](https://picsum.photos/500) /// static var automatic: Self { .init() } } ================================================ FILE: Sources/MarkdownText/Styles/Block Elements/Image Styles/RemoteImageStyle.swift ================================================ import SwiftUI /// An image style that loads content asynchronously if a valid URL is supplied public struct RemoteImageMarkdownStyle: ImageMarkdownStyle { public func makeBody(configuration: Configuration) -> some View { configuration.label } } public extension ImageMarkdownStyle where Self == RemoteImageMarkdownStyle { /// An image style that loads content asynchronously if a valid URL is supplied /// /// Example: /// /// ![Lorem Image](https://picsum.photos/500) /// static var remote: Self { .init() } } ================================================ FILE: Sources/MarkdownText/Styles/Block Elements/Image Styles/SymbolImageStyle.swift ================================================ import SwiftUI /// An image style that renders an SFSymbol (if possible) public struct SFSymbolImageMarkdownStyle: ImageMarkdownStyle { public func makeBody(configuration: Configuration) -> some View { if let source = configuration.source { Image(systemName: source) } } } public extension ImageMarkdownStyle where Self == SFSymbolImageMarkdownStyle { /// An image style that renders an SFSymbol (if possible) /// /// Example: /// /// ![](star) /// static var symbol: Self { .init() } } ================================================ FILE: Sources/MarkdownText/Styles/Block Elements/ImageStyle.swift ================================================ import SwiftUI import SwiftUIBackports /// A type that applies a custom appearance to image markdown elements public protocol ImageMarkdownStyle { associatedtype Body: View /// The properties of an image markdown element typealias Configuration = ImageMarkdownConfiguration /// Creates a view that represents the body of a label @ViewBuilder func makeBody(configuration: Configuration) -> Body } /// The properties of an image markdown element public struct ImageMarkdownConfiguration { /// The source of the image. Generally either a URL public let source: String? /// The title of the image public let title: String? private struct Label: View { private var inlineStyle = InlineMarkdownStyle() let source: String? let title: String? init(source: String?, title: String?) { self.source = source self.title = title } var body: some View { if let source = source, let url = URL(string: source), url.scheme != nil { if source.localizedCaseInsensitiveContains("img.shields.io") || source.localizedCaseInsensitiveContains(".svg") { inlineStyle.makeBody(configuration: .init(elements: [ .init(content: .init(title ?? source)), ])) } else { Backport.AsyncImage(url: url) { phase in switch phase { case let .success(image): image .resizable() .scaledToFit() case .empty: ProgressView() default: EmptyView() } } .scaledToFit() .animation(.default) } } } } /// Returns a default image markdown representation public var label: some View { Label(source: source, title: title) } } private struct ImageMarkdownEnvironmentKey: EnvironmentKey { static let defaultValue: any ImageMarkdownStyle = DefaultImageMarkdownStyle() } public extension EnvironmentValues { /// The current image markdown style var markdownImageStyle: any ImageMarkdownStyle { get { self[ImageMarkdownEnvironmentKey.self] } set { self[ImageMarkdownEnvironmentKey.self] = newValue } } } public extension View { /// Sets the style for image markdown elements func markdownImageStyle(_ style: some ImageMarkdownStyle) -> some View { environment(\.markdownImageStyle, style) } } ================================================ FILE: Sources/MarkdownText/Styles/Block Elements/ListStyle.swift ================================================ import SwiftUI import SwiftUIBackports /// A type that applies a custom appearance to list markdown elements public protocol ListMarkdownStyle { associatedtype Body: View /// The properties of a list markdown element typealias Configuration = ListStyleMarkdownConfiguration /// Creates a view that represents the body of a label @ViewBuilder func makeBody(configuration: Configuration) -> Body } public struct AnyListMarkdownStyle: ListMarkdownStyle { var label: (Configuration) -> AnyView init(_ style: S) { label = { AnyView(style.makeBody(configuration: $0)) } } public func makeBody(configuration: Configuration) -> some View { label(configuration) } } /// The properties of a list markdown element public struct ListStyleMarkdownConfiguration { private struct Label: View { @Environment(\.markdownListStyle) private var list @Environment(\.markdownUnorderedListItemStyle) private var unordered @Environment(\.markdownOrderedListItemStyle) private var ordered @Environment(\.markdownCheckListItemStyle) private var checklist @Environment(\.markdownCheckListItemVisibility) private var checkListItemVisibility @Environment(\.markdownUnorderedListItemVisibility) private var unorderedListItemVisibility @Environment(\.markdownOrderedListItemVisibility) private var orderedListItemVisibility @ScaledMetric private var spacing: CGFloat = 8 let markdownList: MarkdownList let level: Int var body: some View { VStack(alignment: .leading, spacing: spacing) { ForEach(markdownList.elements.indices, id: \.self) { index in switch markdownList.elements[index] { case let .ordered(config): if orderedListItemVisibility != .hidden { AnyView(ordered.makeBody(configuration: config)) } case let .unordered(config): if unorderedListItemVisibility != .hidden { AnyView(unordered.makeBody(configuration: config)) } case let .checklist(config): if checkListItemVisibility != .hidden { AnyView(checklist.makeBody(configuration: config)) } case let .list(nested): AnyView(list.makeBody(configuration: .init(list: nested, level: level + 1))) } } } } } /// A model representing the elements of this list, including any nested lists public let list: MarkdownList /// The indentation level of the list public let level: Int /// Returns a default list markdown representation public var label: some View { Label(markdownList: list, level: level) } } /// The default list style public struct DefaultListMarkdownStyle: ListMarkdownStyle { public init() { } public func makeBody(configuration: Configuration) -> some View { configuration.label } } public extension ListMarkdownStyle where Self == DefaultListMarkdownStyle { /// The default list style static var `default`: Self { .init() } } private struct ListMarkdownEnvironmentKey: EnvironmentKey { static let defaultValue = AnyListMarkdownStyle(.default) } public extension EnvironmentValues { /// The current list markdown style var markdownListStyle: AnyListMarkdownStyle { get { self[ListMarkdownEnvironmentKey.self] } set { self[ListMarkdownEnvironmentKey.self] = newValue } } } public extension View { /// Sets the style for list markdown elements func markdownListStyle(_ style: S) -> some View where S: ListMarkdownStyle { environment(\.markdownListStyle, .init(style)) } } ================================================ FILE: Sources/MarkdownText/Styles/Block Elements/ParagraphStyle.swift ================================================ import SwiftUI /// A type that applies a custom appearance to paragraph markdown elements public protocol ParagraphMarkdownStyle { associatedtype Body: View /// The properties of a paragraph markdown element typealias Configuration = ParagraphMarkdownConfiguration /// Creates a view that represents the body of a label @ViewBuilder func makeBody(configuration: Configuration) -> Body } public struct AnyParagraphMarkdownStyle: ParagraphMarkdownStyle { var label: (Configuration) -> AnyView init(_ style: S) { label = { AnyView(style.makeBody(configuration: $0)) } } public func makeBody(configuration: Configuration) -> some View { label(configuration) } } /// The properties of a paragraph markdown element public struct ParagraphMarkdownConfiguration { /// The content for this element /// /// You can use this to maintain the existing paragraph style: /// /// content.label // maintains the default style /// .lineSpacing(20) let content: InlineMarkdownConfiguration private struct Label: View { let content: InlineMarkdownConfiguration var body: some View { content.label } } /// Returns a default heading markdown representation public var label: some View { Label(content: content) } } /// The default paragraph style public struct DefaultParagraphMarkdownStyle: ParagraphMarkdownStyle { public init() { } public func makeBody(configuration: Configuration) -> some View { configuration.label } } public extension ParagraphMarkdownStyle where Self == DefaultParagraphMarkdownStyle { /// The default paragraph style static var `default`: Self { .init() } } private struct ParagraphMarkdownEnvironmentKey: EnvironmentKey { static let defaultValue = AnyParagraphMarkdownStyle(.default) } public extension EnvironmentValues { /// The current paragraph markdown style var markdownParagraphStyle: AnyParagraphMarkdownStyle { get { self[ParagraphMarkdownEnvironmentKey.self] } set { self[ParagraphMarkdownEnvironmentKey.self] = newValue } } } public extension View { /// Sets the style for paragraph markdown elements func markdownParagraphStyle(_ style: S) -> some View where S: ParagraphMarkdownStyle { environment(\.markdownParagraphStyle, AnyParagraphMarkdownStyle(style)) } } ================================================ FILE: Sources/MarkdownText/Styles/Block Elements/QuoteStyle.swift ================================================ import SwiftUI /// A type that applies a custom appearance to quote markdown elements public protocol QuoteMarkdownStyle { associatedtype Body: View /// The properties of a quote markdown element typealias Configuration = QuoteMarkdownConfiguration /// Creates a view that represents the body of a label @ViewBuilder func makeBody(configuration: Configuration) -> Body } public struct AnyQuoteMarkdownStyle: QuoteMarkdownStyle { var label: (Configuration) -> AnyView init(_ style: S) { label = { AnyView(style.makeBody(configuration: $0)) } } public func makeBody(configuration: Configuration) -> some View { label(configuration) } } /// The properties of a quote markdown element public struct QuoteMarkdownConfiguration { /// The content for this element /// /// You can use this to maintain the existing heading style: /// /// content.label // maintains its font style /// .padding() /// .background { /// Color.primary /// .opacity(0.05) /// .cornerRadius(13) /// } public let content: ParagraphMarkdownConfiguration private struct Label: View { let paragraph: ParagraphMarkdownConfiguration var body: some View { paragraph.label } } /// Returns a default quote markdown representation public var label: some View { Label(paragraph: content) } } public struct DefaultQuoteMarkdownStyle: QuoteMarkdownStyle { public init() { } public func makeBody(configuration: Configuration) -> some View { configuration.label } } public extension QuoteMarkdownStyle where Self == DefaultQuoteMarkdownStyle { /// The default quote style static var `default`: Self { .init() } } private struct QuoteMarkdownEnvironmentKey: EnvironmentKey { static let defaultValue = AnyQuoteMarkdownStyle(.default) } public extension EnvironmentValues { /// The current quote markdown style var markdownQuoteStyle: AnyQuoteMarkdownStyle { get { self[QuoteMarkdownEnvironmentKey.self] } set { self[QuoteMarkdownEnvironmentKey.self] = newValue } } } public extension View { /// Sets the style for quote markdown elements func markdownQuoteStyle(_ style: S) -> some View where S: QuoteMarkdownStyle { environment(\.markdownQuoteStyle, AnyQuoteMarkdownStyle(style)) } } ================================================ FILE: Sources/MarkdownText/Styles/Block Elements/ThematicStyle.swift ================================================ import SwiftUI /// A type that applies a custom appearance to thematic break markdown elements public protocol ThematicBreakMarkdownStyle { associatedtype Body: View /// The properties of a thematic break markdown element typealias Configuration = ThematicMarkdownConfiguration /// Creates a view that represents the body of a label @ViewBuilder func makeBody(configuration: Configuration) -> Body } /// The properties of a thematic break markdown element public struct AnyThematicMarkdownStyle: ThematicBreakMarkdownStyle { var label: (Configuration) -> AnyView init(_ style: S) { label = { AnyView(style.makeBody(configuration: $0)) } } public func makeBody(configuration: Configuration) -> some View { label(configuration) } } public struct ThematicMarkdownConfiguration { /// Returns a default thematic break markdown representation public let label = Divider() } /// A thematic break style represented by a SwiftUI `Divider` public struct DefaultThematicMarkdownStyle: ThematicBreakMarkdownStyle { public init() { } public func makeBody(configuration: Configuration) -> some View { configuration.label } } public extension ThematicBreakMarkdownStyle where Self == DefaultThematicMarkdownStyle { /// A thematic break style represented by a SwiftUI `Divider` static var `default`: Self { .init() } } private struct MarkdownEnvironmentKey: EnvironmentKey { static let defaultValue = AnyThematicMarkdownStyle(.default) } public extension EnvironmentValues { /// The current thematic break markdown style var markdownThematicBreakStyle: AnyThematicMarkdownStyle { get { self[MarkdownEnvironmentKey.self] } set { self[MarkdownEnvironmentKey.self] = newValue } } } public extension View { /// Sets the style for thematic break markdown elements func markdownThematicBreakStyle(_ style: S) -> some View where S: ThematicBreakMarkdownStyle { environment(\.markdownThematicBreakStyle, AnyThematicMarkdownStyle(style)) } } ================================================ FILE: Sources/MarkdownText/Styles/Inline Elements/EmphasisStyle.swift ================================================ import SwiftUI /// A type that applies a custom appearance to italic (emphasis) markdown elements public protocol EmphasisMarkdownStyle { /// The properties of an emphasis markdown element typealias Configuration = EmphasisMarkdownConfiguration /// Creates a view that represents the body of a label func makeBody(configuration: Configuration) -> Text } /// The properties of an italic (emphasis) markdown element public struct EmphasisMarkdownConfiguration { /// The textual content for this element public let content: Text /// Returns a default italic (emphasis) markdown representation public var label: Text { content.italic() } } /// An italic (emphasis) style that applies the `italic` modifier to its textual content public struct DefaultEmphasisMarkdownStyle: EmphasisMarkdownStyle { public init() { } public func makeBody(configuration: Configuration) -> Text { configuration.label } } public extension EmphasisMarkdownStyle where Self == DefaultEmphasisMarkdownStyle { /// An italic (emphasis) style that applies the `italic` modifier to its textual content static var `default`: Self { .init() } } private struct EmphasisMarkdownEnvironmentKey: EnvironmentKey { static let defaultValue: EmphasisMarkdownStyle = DefaultEmphasisMarkdownStyle() } public extension EnvironmentValues { /// The current italic (amphasis) markdown style var markdownEmphasisStyle: EmphasisMarkdownStyle { get { self[EmphasisMarkdownEnvironmentKey.self] } set { self[EmphasisMarkdownEnvironmentKey.self] = newValue } } } public extension View { /// Sets the style for italic (emphasis) markdown elements func markdownEmphasisStyle(_ style: S) -> some View where S: EmphasisMarkdownStyle { environment(\.markdownEmphasisStyle, style) } } ================================================ FILE: Sources/MarkdownText/Styles/Inline Elements/InlineCodeStyle.swift ================================================ import SwiftUI /// A type that applies a custom appearance to inline code markdown elements public protocol InlineCodeMarkdownStyle { /// The properties of an inline code markdown element typealias Configuration = InlineCodeMarkdownConfiguration /// Creates a view that represents the body of a label func makeBody(configuration: Configuration) -> Text } /// The properties of an inline code markdown element public struct InlineCodeMarkdownConfiguration { /// The code for this element public let code: String internal let font: Font? /// Returns a default inline code markdown representation public var label: Text { #if os(macOS) if #available(macOS 12, *) { return Text(code) .font(font?.monospaced() ?? .system(.body, design: .monospaced)) } else { return Text(code) .font(.system(.body, design: .monospaced)) } #elseif os(iOS) if #available(iOS 15, *) { return Text(code) .font(font?.monospaced() ?? .system(.body, design: .monospaced)) } else { return Text(code) .font(.system(.body, design: .monospaced)) } #else return Text(code) .font(.system(.body, design: .monospaced)) #endif } } /// An inline code style that applies the `monospaced` modifier to its textual content public struct DefaultInlineCodeMarkdownStyle: InlineCodeMarkdownStyle { public init() { } public func makeBody(configuration: Configuration) -> Text { configuration.label } } public extension InlineCodeMarkdownStyle where Self == DefaultInlineCodeMarkdownStyle { /// An inline code style that applies the `monospaced` modifier to its textual content static var `default`: Self { .init() } } private struct InlineCodeMarkdownEnvironmentKey: EnvironmentKey { static let defaultValue: InlineCodeMarkdownStyle = DefaultInlineCodeMarkdownStyle() } public extension EnvironmentValues { /// The current inline code markdown style var markdownInlineCodeStyle: InlineCodeMarkdownStyle { get { self[InlineCodeMarkdownEnvironmentKey.self] } set { self[InlineCodeMarkdownEnvironmentKey.self] = newValue } } } public extension View { /// Sets the style for inline code markdown elements func markdownInlineCodeStyle(_ style: S) -> some View where S: InlineCodeMarkdownStyle { environment(\.markdownInlineCodeStyle, style) } } ================================================ FILE: Sources/MarkdownText/Styles/Inline Elements/InlineLinkStyle.swift ================================================ import SwiftUI /// A type that applies a custom appearance to inline link markdown elements public protocol InlineLinkMarkdownStyle { /// The properties of an inline link markdown element typealias Configuration = InlineLinkMarkdownConfiguration /// Creates a view that represents the body of a label func makeBody(configuration: Configuration) -> Text } /// The properties of an inline link markdown element public struct InlineLinkMarkdownConfiguration { /// The textual content for this element public let content: Text /// Returns a default inline link markdown representation public var label: Text { content } } /// An inline link style that sets the `foregroundColor` to the view's current `accentColor` public struct DefaultInlineLinkMarkdownStyle: InlineLinkMarkdownStyle { public init() { } public func makeBody(configuration: Configuration) -> Text { configuration.label .foregroundColor(.accentColor) } } public extension InlineLinkMarkdownStyle where Self == DefaultInlineLinkMarkdownStyle { /// An inline link style that sets the `foregroundColor` to the view's current `accentColor` /// /// - note: Inline links are always **non-interactive**. static var nonInteractiveInline: Self { .init() } } private struct InlineLinkMarkdownEnvironmentKey: EnvironmentKey { static let defaultValue: InlineLinkMarkdownStyle = DefaultInlineLinkMarkdownStyle.nonInteractiveInline } public extension EnvironmentValues { /// The current inline link markdown style var markdownInlineLinkStyle: InlineLinkMarkdownStyle { get { self[InlineLinkMarkdownEnvironmentKey.self] } set { self[InlineLinkMarkdownEnvironmentKey.self] = newValue } } } public extension View { /// Sets the style for inline link markdown elements func markdownInlineLinkStyle(_ style: S) -> some View where S: InlineLinkMarkdownStyle { environment(\.markdownInlineLinkStyle, style) } } ================================================ FILE: Sources/MarkdownText/Styles/Inline Elements/StrikethroughStyle.swift ================================================ import SwiftUI /// A type that applies a custom appearance to strikethough markdown elements public protocol StrikethroughMarkdownStyle { /// The properties of a strikethough markdown element typealias Configuration = StrikethroughMarkdownConfiguration /// Creates a view that represents the body of a label func makeBody(configuration: Configuration) -> Text } /// The properties of a strikethrough markdown element public struct StrikethroughMarkdownConfiguration { /// The textual content for this element public let content: Text /// Returns a default strikethrough markdown representation public var label: Text { content.strikethrough() } } /// An strikethrough style that applies the `strikethrough` modifier to its textual content public struct DefaultStrikethroughMarkdownStyle: StrikethroughMarkdownStyle { public init() { } public func makeBody(configuration: Configuration) -> Text { configuration.label } } public extension StrikethroughMarkdownStyle where Self == DefaultStrikethroughMarkdownStyle { /// An strikethrough style that applies the `strikethrough` modifier to its textual content static var `default`: Self { .init() } } private struct StrikethroughMarkdownEnvironmentKey: EnvironmentKey { static let defaultValue: StrikethroughMarkdownStyle = DefaultStrikethroughMarkdownStyle() } public extension EnvironmentValues { /// The current strikethrough markdown style var markdownStrikethroughStyle: StrikethroughMarkdownStyle { get { self[StrikethroughMarkdownEnvironmentKey.self] } set { self[StrikethroughMarkdownEnvironmentKey.self] = newValue } } } public extension View { /// Sets the style for strikethrough markdown elements func markdownStrikethroughStyle(_ style: S) -> some View where S: StrikethroughMarkdownStyle { environment(\.markdownStrikethroughStyle, style) } } ================================================ FILE: Sources/MarkdownText/Styles/Inline Elements/StrongStyle.swift ================================================ import SwiftUI /// A type that applies a custom appearance to bold (strong) markdown elements public protocol StrongMarkdownStyle { /// The properties of a bold (strong) markdown element typealias Configuration = StrongMarkdownConfiguration /// Creates a view that represents the body of a label func makeBody(configuration: Configuration) -> Text } /// The properties of a bold (strong) markdown element public struct StrongMarkdownConfiguration { /// The textual content for this element public let content: Text /// Returns a default bold (strong) markdown representation public var label: Text { content.bold() } } /// An bold (strong) style that applies the `bold` modifier to its textual content public struct DefaultStrongMarkdownStyle: StrongMarkdownStyle { public init() { } public func makeBody(configuration: Configuration) -> Text { configuration.label } } public extension StrongMarkdownStyle where Self == DefaultStrongMarkdownStyle { /// An bold (strong) style that applies the `bold` modifier to its textual content static var `default`: Self { .init() } } private struct StrongMarkdownEnvironmentKey: EnvironmentKey { static let defaultValue: StrongMarkdownStyle = DefaultStrongMarkdownStyle() } public extension EnvironmentValues { /// The current bold (strong) markdown style var markdownStrongStyle: StrongMarkdownStyle { get { self[StrongMarkdownEnvironmentKey.self] } set { self[StrongMarkdownEnvironmentKey.self] = newValue } } } public extension View { /// Sets the style for bold (strong) markdown elements func markdownStrongStyle(_ style: S) -> some View where S: StrongMarkdownStyle { environment(\.markdownStrongStyle, style) } } ================================================ FILE: Sources/MarkdownText/Styles/Lists/Bullets/ChecklistBulletStyle.swift ================================================ import SwiftUI import SwiftUIBackports /// A type that applies a custom appearance to checklist bullet markdown elements public protocol CheckListBulletMarkdownStyle { associatedtype Body: View /// The properties of a checklist bullet markdown element typealias Configuration = CheckListBulletMarkdownConfiguration /// Creates a view that represents the body of a label func makeBody(configuration: Configuration) -> Body } public struct AnyCheckListBulletMarkdownStyle: CheckListBulletMarkdownStyle { var label: (Configuration) -> AnyView init(_ style: S) { label = { AnyView(style.makeBody(configuration: $0)) } } public func makeBody(configuration: Configuration) -> some View { label(configuration) } } /// The properties of a checklist bullet markdown element public struct CheckListBulletMarkdownConfiguration { private struct Label: View { @ScaledMetric private var reservedWidth: CGFloat = 25 public let isChecked: Bool var body: some View { Image(systemName: isChecked ? "checkmark.circle.fill" : "circle") .frame(minWidth: reservedWidth) } } /// A boolean that represents whether the checklist item is selected or not public let isChecked: Bool /// Returns a default checklist bullet markdown representation public var label: some View { Label(isChecked: isChecked) } } /// The default checklist bullet style public struct DefaultChecklistBulletMarkdownStyle: CheckListBulletMarkdownStyle { public init() { } public func makeBody(configuration: Configuration) -> some View { configuration.label } } public extension CheckListBulletMarkdownStyle where Self == DefaultChecklistBulletMarkdownStyle { /// The default checklist bullet style static var `default`: Self { .init() } } private struct ChecklistBulletMarkdownEnvironmentKey: EnvironmentKey { static let defaultValue: AnyCheckListBulletMarkdownStyle = .init(DefaultChecklistBulletMarkdownStyle()) } public extension EnvironmentValues { /// The current checklist bullet markdown style var markdownCheckListBulletStyle: AnyCheckListBulletMarkdownStyle { get { self[ChecklistBulletMarkdownEnvironmentKey.self] } set { self[ChecklistBulletMarkdownEnvironmentKey.self] = newValue } } } public extension View { /// Sets the style for checklist bullet markdown elements func markdownCheckListBulletStyle(_ style: S) -> some View where S: CheckListBulletMarkdownStyle { environment(\.markdownCheckListBulletStyle, .init(style)) } } ================================================ FILE: Sources/MarkdownText/Styles/Lists/Bullets/OrderedBulletStyle.swift ================================================ import SwiftUI import SwiftUIBackports /// A type that applies a custom appearance to ordered bullet markdown elements public protocol OrderedListBulletMarkdownStyle { associatedtype Body: View /// The properties of a ordered bullet markdown element typealias Configuration = OrderedListBulletMarkdownConfiguration /// Creates a view that represents the body of a label func makeBody(configuration: Configuration) -> Body } public struct AnyOrderedListBulletMarkdownStyle: OrderedListBulletMarkdownStyle { var label: (Configuration) -> AnyView init(_ style: S) { label = { AnyView(style.makeBody(configuration: $0)) } } public func makeBody(configuration: Configuration) -> some View { label(configuration) } } /// The properties of a ordered bullet markdown element public struct OrderedListBulletMarkdownConfiguration { struct Label: View { @ScaledMetric private var reservedWidth: CGFloat = 25 let order: Int var body: some View { Text("\(order).") .frame(minWidth: reservedWidth) } } /// An integer value representing this element's order in the list public let order: Int /// Returns a default ordered bullet markdown representation public var label: some View { Label(order: order) } } /// An ordered bullet style that presents its bullet as a numerical value (e.g. `1.`, `2.`) public struct NumericallyOrderedListBulletMarkdownStyle: OrderedListBulletMarkdownStyle { public init() { } public func makeBody(configuration: Configuration) -> some View { configuration.label } } public extension OrderedListBulletMarkdownStyle where Self == NumericallyOrderedListBulletMarkdownStyle { /// An ordered bullet style that presents its bullet as a numerical value (e.g. `1.`, `2.`) static var numerical: Self { .init() } } private struct OrderedBulletMarkdownEnvironmentKey: EnvironmentKey { static let defaultValue: AnyOrderedListBulletMarkdownStyle = .init(.numerical) } public extension EnvironmentValues { /// The current ordered bullet markdown style var markdownOrderedListBulletStyle: AnyOrderedListBulletMarkdownStyle { get { self[OrderedBulletMarkdownEnvironmentKey.self] } set { self[OrderedBulletMarkdownEnvironmentKey.self] = newValue } } } public extension View { /// Sets the style for ordered bullet markdown elements func markdownOrderedListBulletStyle(_ style: S) -> some View where S: OrderedListBulletMarkdownStyle { environment(\.markdownOrderedListBulletStyle, .init(style)) } } ================================================ FILE: Sources/MarkdownText/Styles/Lists/Bullets/UnorderedBulletStyle.swift ================================================ import SwiftUI import SwiftUIBackports /// Various styles for representing unordered list item bullet elements /// /// ● Element one /// ● Element two /// ○ Element one /// ○ Element two /// ◼︎ Element one /// ◼︎ Element two public enum UnorderedListBulletStyle: String { /// Represents a filled circle bullet. By default this is used for all level-0 elements /// /// ● Element one /// ● Element two case filledCircle = "●" /// Represents an outlined circle bullet. By default this is used for all level-1 elements /// /// ○ Element one /// ○ Element two case outlineCircle = "○" /// Represents a filled square bullet. By default this is used for all elements greater than level 1 /// /// ◼︎ Element one /// ◼︎ Element two case square = "◼︎" } /// A type that applies a custom appearance to unordered bullet markdown elements public protocol UnorderedListBulletMarkdownStyle { associatedtype Body: View /// The properties of a unordered bullet markdown element typealias Configuration = UnorderedListBulletMarkdownConfiguration /// Creates a view that represents the body of a label func makeBody(configuration: Configuration) -> Body } public struct AnyUnorderedListBulletMarkdownStyle: UnorderedListBulletMarkdownStyle { var label: (Configuration) -> AnyView init(_ style: S) { label = { AnyView(style.makeBody(configuration: $0)) } } public func makeBody(configuration: Configuration) -> some View { label(configuration) } } /// The properties of a unordered bullet markdown element public struct UnorderedListBulletMarkdownConfiguration { struct Label: View { @ScaledMetric private var reservedWidth: CGFloat = 25 let bulletStyle: UnorderedListBulletStyle var body: some View { Text("\(bulletStyle.rawValue)") .frame(minWidth: reservedWidth) } } /// An integer value representing this element's indentation level public let level: Int /// The preferred bullet style, based on the current indentation level /// /// ● Element one /// ● Element two /// ○ Element one /// ○ Element two /// ◼︎ Element one /// ◼︎ Element two public var preferredBulletStyle: UnorderedListBulletStyle { switch level { case 0: return .filledCircle case 1: return .outlineCircle default: return .square } } /// Returns a default unordered bullet markdown representation public var label: some View { Label(bulletStyle: preferredBulletStyle) } } /// An unordered bullet style that presents its bullets as `UnorderedListBulletStyle` elements, based on the elements indendation level public struct DefaultUnorderedListBulletMarkdownStyle: UnorderedListBulletMarkdownStyle { public init() { } public func makeBody(configuration: Configuration) -> some View { configuration.label } } public extension UnorderedListBulletMarkdownStyle where Self == DefaultUnorderedListBulletMarkdownStyle { /// An unordered bullet style that presents its bullets as `UnorderedListBulletStyle` elements, based on the elements indendation level static var automatic: Self { .init() } } private struct UnorderedListBulletMarkdownEnvironmentKey: EnvironmentKey { static let defaultValue: AnyUnorderedListBulletMarkdownStyle = .init(.automatic) } public extension EnvironmentValues { /// The current unordered bullet markdown style var markdownUnorderedListBulletStyle: AnyUnorderedListBulletMarkdownStyle { get { self[UnorderedListBulletMarkdownEnvironmentKey.self] } set { self[UnorderedListBulletMarkdownEnvironmentKey.self] = newValue } } } public extension View { /// Sets the style for unordered bullet markdown elements func markdownUnorderedListBulletStyle(_ style: S) -> some View where S: UnorderedListBulletMarkdownStyle { environment(\.markdownUnorderedListBulletStyle, .init(style)) } } ================================================ FILE: Sources/MarkdownText/Styles/Lists/Items/CheckedListItemStyle.swift ================================================ import SwiftUI import SwiftUIBackports /// A type that applies a custom appearance to checklist item markdown elements public protocol CheckListItemMarkdownStyle { associatedtype Body: View /// The properties of a checklist item markdown element typealias Configuration = CheckListItemMarkdownConfiguration /// Creates a view that represents the body of a label @ViewBuilder func makeBody(configuration: Configuration) -> Body } public struct AnyCheckListItemMarkdownStyle: CheckListItemMarkdownStyle { var label: (Configuration) -> AnyView init(_ style: S) { label = { AnyView(style.makeBody(configuration: $0)) } } public func makeBody(configuration: Configuration) -> some View { label(configuration) } } /// The properties of a checklist item markdown element public struct CheckListItemMarkdownConfiguration { private struct Item: View { @ScaledMetric private var reservedWidth: CGFloat = 25 @Environment(\.markdownParagraphStyle) private var paragraphStyle @Environment(\.markdownCheckListBulletStyle) private var bulletStyle @Environment(\.markdownCheckListItemBulletVisibility) private var bulletVisibility let level: Int let bullet: CheckListBulletMarkdownConfiguration let paragraph: ParagraphMarkdownConfiguration private var space: String { Array(repeating: " ", count: level).joined() } var body: some View { HStack(alignment: .firstTextBaseline, spacing: 0) { Text(space) Label { paragraphStyle.makeBody(configuration: paragraph) } icon: { if bulletVisibility != .hidden { bulletStyle.makeBody(configuration: bullet) .frame(minWidth: reservedWidth) } } .labelStyle(.list) } } } /// An integer value representing this element's indentation level public let level: Int /// The bullet configuration for this element public let bullet: CheckListBulletMarkdownConfiguration /// The content configuration for this element public let content: ParagraphMarkdownConfiguration /// Returns a default checklist item markdown representation public var label: some View { Item(level: level, bullet: bullet, paragraph: content) } } /// The default checklist item style public struct DefaultCheckListItemMarkdownStyle: CheckListItemMarkdownStyle { public init() { } public func makeBody(configuration: Configuration) -> some View { configuration.label } } public extension CheckListItemMarkdownStyle where Self == DefaultCheckListItemMarkdownStyle { /// The default checklist item style static var `default`: Self { .init() } } private struct CheckListItemMarkdownEnvironmentKey: EnvironmentKey { static let defaultValue = AnyCheckListItemMarkdownStyle(.default) } public extension EnvironmentValues { /// The current checklist item markdown style var markdownCheckListItemStyle: AnyCheckListItemMarkdownStyle { get { self[CheckListItemMarkdownEnvironmentKey.self] } set { self[CheckListItemMarkdownEnvironmentKey.self] = newValue } } } public extension View { /// Sets the style for checklist item markdown elements func markdownCheckListItemStyle(_ style: S) -> some View where S: CheckListItemMarkdownStyle { environment(\.markdownCheckListItemStyle, AnyCheckListItemMarkdownStyle(style)) } } ================================================ FILE: Sources/MarkdownText/Styles/Lists/Items/OrderedListItemStyle.swift ================================================ import SwiftUI import SwiftUIBackports /// A type that applies a custom appearance to ordered item markdown elements public protocol OrderedListItemMarkdownStyle { associatedtype Body: View /// The properties of a ordered item markdown element typealias Configuration = OrderedListItemMarkdownConfiguration /// Creates a view that represents the body of a label @ViewBuilder func makeBody(configuration: Configuration) -> Body } public struct AnyOrderedListItemMarkdownStyle: OrderedListItemMarkdownStyle { var label: (Configuration) -> AnyView init(_ style: S) { label = { AnyView(style.makeBody(configuration: $0)) } } public func makeBody(configuration: Configuration) -> some View { label(configuration) } } /// The properties of a ordered item markdown element public struct OrderedListItemMarkdownConfiguration { private struct Item: View { @ScaledMetric private var reservedWidth: CGFloat = 25 @Environment(\.markdownParagraphStyle) private var paragraphStyle @Environment(\.markdownOrderedListBulletStyle) private var bulletStyle @Environment(\.markdownOrderedListItemBulletVisibility) private var bulletVisibility public let level: Int public let bullet: OrderedListBulletMarkdownConfiguration public let paragraph: ParagraphMarkdownConfiguration private var space: String { Array(repeating: " ", count: level).joined() } var body: some View { HStack(alignment: .firstTextBaseline, spacing: 0) { Text(space) Label { paragraphStyle.makeBody(configuration: paragraph) } icon: { if bulletVisibility != .hidden { bulletStyle.makeBody(configuration: bullet) .frame(minWidth: reservedWidth) } } .labelStyle(.list) } } } /// An integer value representing this element's indentation level in the list public let level: Int /// The bullet configuration for this element public let bullet: OrderedListBulletMarkdownConfiguration /// The content configuration for this element public let content: ParagraphMarkdownConfiguration /// Returns a default ordered item markdown representation public var label: some View { Item(level: level, bullet: bullet, paragraph: content) } } /// The default ordered item style public struct DefaultOrderedListItemMarkdownStyle: OrderedListItemMarkdownStyle { public init() { } public func makeBody(configuration: Configuration) -> some View { configuration.label } } public extension OrderedListItemMarkdownStyle where Self == DefaultOrderedListItemMarkdownStyle { /// The default ordered item style static var `default`: Self { .init() } } private struct OrderedListMarkdownEnvironmentKey: EnvironmentKey { static let defaultValue = AnyOrderedListItemMarkdownStyle(.default) } public extension EnvironmentValues { /// The current ordered item markdown style var markdownOrderedListItemStyle: AnyOrderedListItemMarkdownStyle { get { self[OrderedListMarkdownEnvironmentKey.self] } set { self[OrderedListMarkdownEnvironmentKey.self] = newValue } } } public extension View { /// Sets the style for ordered item markdown elements func markdownOrderedListItemStyle(_ style: S) -> some View where S: OrderedListItemMarkdownStyle { environment(\.markdownOrderedListItemStyle, AnyOrderedListItemMarkdownStyle(style)) } } ================================================ FILE: Sources/MarkdownText/Styles/Lists/Items/UnorderedListItemStyle.swift ================================================ import SwiftUI import SwiftUIBackports /// A type that applies a custom appearance to unordered item markdown elements public protocol UnorderedListItemMarkdownStyle { associatedtype Body: View /// The properties of an unordered item markdown element typealias Configuration = UnorderedListItemMarkdownConfiguration /// Creates a view that represents the body of a label @ViewBuilder func makeBody(configuration: Configuration) -> Body } public struct AnyUnorderedListItemMarkdownStyle: UnorderedListItemMarkdownStyle { var label: (Configuration) -> AnyView init(_ style: S) { label = { AnyView(style.makeBody(configuration: $0)) } } public func makeBody(configuration: Configuration) -> some View { label(configuration) } } /// The properties of an unordered item markdown element public struct UnorderedListItemMarkdownConfiguration { private struct Item: View { @ScaledMetric private var reservedWidth: CGFloat = 25 @Environment(\.markdownParagraphStyle) private var paragraphStyle @Environment(\.markdownUnorderedListBulletStyle) private var bulletStyle @Environment(\.markdownUnorderedListItemBulletVisibility) private var bulletVisibility public let level: Int public let bullet: UnorderedListBulletMarkdownConfiguration public let paragraph: ParagraphMarkdownConfiguration private var space: String { Array(repeating: " ", count: level).joined() } var body: some View { HStack(alignment: .firstTextBaseline, spacing: 0) { Text(space) Label { paragraphStyle.makeBody(configuration: paragraph) } icon: { if bulletVisibility != .hidden { bulletStyle.makeBody(configuration: bullet) .frame(minWidth: reservedWidth) } } .labelStyle(.list) } } } /// An integer value representing this element's indentation level in the list public let level: Int /// The bullet configuration for this element public let bullet: UnorderedListBulletMarkdownConfiguration /// The content configuration for this element public let content: ParagraphMarkdownConfiguration /// Returns a default unordered item markdown representation public var label: some View { Item(level: level, bullet: bullet, paragraph: content) } } /// The default unordered item style public struct DefaultUnorderedListItemMarkdownStyle: UnorderedListItemMarkdownStyle { public init() { } public func makeBody(configuration: Configuration) -> some View { configuration.label } } public extension UnorderedListItemMarkdownStyle where Self == DefaultUnorderedListItemMarkdownStyle { /// The default unordered item style static var `default`: Self { .init() } } private struct UnorderedListItemMarkdownEnvironmentKey: EnvironmentKey { static let defaultValue = AnyUnorderedListItemMarkdownStyle(.default) } public extension EnvironmentValues { /// The current unordered item markdown style var markdownUnorderedListItemStyle: AnyUnorderedListItemMarkdownStyle { get { self[UnorderedListItemMarkdownEnvironmentKey.self] } set { self[UnorderedListItemMarkdownEnvironmentKey.self] = newValue } } } public extension View { /// Sets the style for unordered item markdown elements func markdownUnorderedListItemStyle(_ style: S) -> some View where S: UnorderedListItemMarkdownStyle { environment(\.markdownUnorderedListItemStyle, AnyUnorderedListItemMarkdownStyle(style)) } } ================================================ FILE: Sources/MarkdownText/Styles/Visibility/CheckListVisibility.swift ================================================ import SwiftUI import SwiftUIBackports struct CheckListItemMarkdownVisibility: EnvironmentKey { static let defaultValue: Backport.Visibility = .automatic } internal extension EnvironmentValues { var markdownCheckListItemVisibility: CheckListItemMarkdownVisibility.Value { get { self[CheckListItemMarkdownVisibility.self] } set { self[CheckListItemMarkdownVisibility.self] = newValue } } } public extension View { /// Sets the visibility for checklist item markdown elements func markdownCheckListItem(_ visibility: Backport.Visibility) -> some View { environment(\.markdownCheckListItemVisibility, visibility) } } struct CheckListItemBulletMarkdownVisibility: EnvironmentKey { static let defaultValue: Backport.Visibility = .automatic } internal extension EnvironmentValues { var markdownCheckListItemBulletVisibility: CheckListItemBulletMarkdownVisibility.Value { get { self[CheckListItemBulletMarkdownVisibility.self] } set { self[CheckListItemBulletMarkdownVisibility.self] = newValue } } } public extension View { /// Sets the visibility for checklist bullet markdown elements func markdownCheckListItemBullet(_ visibility: Backport.Visibility) -> some View { environment(\.markdownCheckListItemBulletVisibility, visibility) } } ================================================ FILE: Sources/MarkdownText/Styles/Visibility/CodeVisibility.swift ================================================ import SwiftUI import SwiftUIBackports struct CodeMarkdownVisibility: EnvironmentKey { static let defaultValue: Backport.Visibility = .automatic } internal extension EnvironmentValues { var markdownCodeVisibility: CodeMarkdownVisibility.Value { get { self[CodeMarkdownVisibility.self] } set { self[CodeMarkdownVisibility.self] = newValue } } } public extension View { /// Sets the visibility for code block markdown elements func markdownCode(_ visibility: Backport.Visibility) -> some View { environment(\.markdownCodeVisibility, visibility) } } ================================================ FILE: Sources/MarkdownText/Styles/Visibility/HeadingVisibility.swift ================================================ import SwiftUI import SwiftUIBackports #warning("Refactor to allow for range based API as well (inspo: DynamicType API)") struct HeadingMarkdownVisibility: EnvironmentKey { static let defaultValue: Backport.Visibility = .automatic } internal extension EnvironmentValues { var markdownHeadingVisibility: HeadingMarkdownVisibility.Value { get { self[HeadingMarkdownVisibility.self] } set { self[HeadingMarkdownVisibility.self] = newValue } } } public extension View { /// Sets the visibility for heading markdown elements func markdownHeading(_ visibility: Backport.Visibility) -> some View { environment(\.markdownHeadingVisibility, visibility) } } ================================================ FILE: Sources/MarkdownText/Styles/Visibility/ImageVisibility.swift ================================================ import SwiftUI import SwiftUIBackports struct ImageMarkdownVisibility: EnvironmentKey { static let defaultValue: Backport.Visibility = .automatic } internal extension EnvironmentValues { var markdownImageVisibility: ImageMarkdownVisibility.Value { get { self[ImageMarkdownVisibility.self] } set { self[ImageMarkdownVisibility.self] = newValue } } } public extension View { /// Sets the visibility for image markdown elements func markdownImage(_ visibility: Backport.Visibility) -> some View { environment(\.markdownImageVisibility, visibility) } } ================================================ FILE: Sources/MarkdownText/Styles/Visibility/ListItemVisibility.swift ================================================ import SwiftUI import SwiftUIBackports struct ListMarkdownVisibility: EnvironmentKey { static let defaultValue: Backport.Visibility = .automatic } internal extension EnvironmentValues { var markdownListVisibility: ListMarkdownVisibility.Value { get { self[ListMarkdownVisibility.self] } set { self[ListMarkdownVisibility.self] = newValue } } } public extension View { /// Sets the visibility for all list item markdown elements func markdownList(_ visibility: Backport.Visibility) -> some View { environment(\.markdownListVisibility, visibility) } } ================================================ FILE: Sources/MarkdownText/Styles/Visibility/OrderedListVisibility.swift ================================================ import SwiftUI import SwiftUIBackports struct OrderedListItemMarkdownVisibility: EnvironmentKey { static let defaultValue: Backport.Visibility = .automatic } internal extension EnvironmentValues { var markdownOrderedListItemVisibility: OrderedListItemMarkdownVisibility.Value { get { self[OrderedListItemMarkdownVisibility.self] } set { self[OrderedListItemMarkdownVisibility.self] = newValue } } } public extension View { /// Sets the visibility for ordered list markdown elements func markdownOrderedListItem(_ visibility: Backport.Visibility) -> some View { environment(\.markdownOrderedListItemVisibility, visibility) } } struct OrderedListItemBulletMarkdownVisibility: EnvironmentKey { static let defaultValue: Backport.Visibility = .automatic } internal extension EnvironmentValues { var markdownOrderedListItemBulletVisibility: OrderedListItemBulletMarkdownVisibility.Value { get { self[OrderedListItemBulletMarkdownVisibility.self] } set { self[OrderedListItemBulletMarkdownVisibility.self] = newValue } } } public extension View { /// Sets the visibility for ordered bullet markdown elements func markdownOrderedListItemBullet(_ visibility: Backport.Visibility) -> some View { environment(\.markdownOrderedListItemBulletVisibility, visibility) } } ================================================ FILE: Sources/MarkdownText/Styles/Visibility/QuoteVisibility.swift ================================================ import SwiftUI import SwiftUIBackports struct QuoteMarkdownVisibility: EnvironmentKey { static let defaultValue: Backport.Visibility = .automatic } internal extension EnvironmentValues { var markdownQuoteListVisibility: QuoteMarkdownVisibility.Value { get { self[QuoteMarkdownVisibility.self] } set { self[QuoteMarkdownVisibility.self] = newValue } } } public extension View { /// Sets the visibility for quote markdown elements func markdownQuote(_ visibility: Backport.Visibility) -> some View { environment(\.markdownQuoteListVisibility, visibility) } } ================================================ FILE: Sources/MarkdownText/Styles/Visibility/ThematicVisibility.swift ================================================ import SwiftUI import SwiftUIBackports struct ThematicBreakMarkdownVisibility: EnvironmentKey { static let defaultValue: Backport.Visibility = .automatic } internal extension EnvironmentValues { var markdownThematicBreakVisibility: ThematicBreakMarkdownVisibility.Value { get { self[ThematicBreakMarkdownVisibility.self] } set { self[ThematicBreakMarkdownVisibility.self] = newValue } } } public extension View { /// Sets the visibility for thematic break markdown elements func markdownThematicBreak(_ visibility: Backport.Visibility) -> some View { environment(\.markdownThematicBreakVisibility, visibility) } } ================================================ FILE: Sources/MarkdownText/Styles/Visibility/UnorderedListVisibility.swift ================================================ import SwiftUI import SwiftUIBackports struct UnorderedListItemMarkdownVisibility: EnvironmentKey { static let defaultValue: Backport.Visibility = .automatic } internal extension EnvironmentValues { var markdownUnorderedListItemVisibility: UnorderedListItemMarkdownVisibility.Value { get { self[UnorderedListItemMarkdownVisibility.self] } set { self[UnorderedListItemMarkdownVisibility.self] = newValue } } } public extension View { /// Sets the visibility for unordered item markdown elements func markdownUnorderedListItem(_ visibility: Backport.Visibility) -> some View { environment(\.markdownUnorderedListItemVisibility, visibility) } } struct UnorderedListItemBulletMarkdownVisibility: EnvironmentKey { static let defaultValue: Backport.Visibility = .automatic } internal extension EnvironmentValues { var markdownUnorderedListItemBulletVisibility: UnorderedListItemBulletMarkdownVisibility.Value { get { self[UnorderedListItemBulletMarkdownVisibility.self] } set { self[UnorderedListItemBulletMarkdownVisibility.self] = newValue } } } public extension View { /// Sets the visibility for unordered bullet markdown elements func markdownUnorderedListItemBullet(_ visibility: Backport.Visibility) -> some View { environment(\.markdownUnorderedListItemBulletVisibility, visibility) } } ================================================ FILE: generate-docs.sh ================================================ #!/bin/sh swift package \ --allow-writing-to-directory ./docs \ generate-documentation \ --disable-indexing \ --output-path ./docs \ --transform-for-static-hosting \ --hosting-base-path ./docs