Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import PackageDescription
let package = Package(
name: "MarkdownView",
platforms: [
.macOS(.v12),
.iOS(.v15),
.tvOS(.v15),
.watchOS(.v8),
.macOS(.v13),
.iOS(.v16),
.tvOS(.v16),
.watchOS(.v9),
.visionOS(.v1),
],
products: [
Expand All @@ -19,6 +19,7 @@ let package = Package(
.package(url: "https://github.com/swiftlang/swift-markdown.git", from: "0.5.0"),
.package(url: "https://github.com/raspu/Highlightr.git", from: "2.2.1"),
.package(url: "https://github.com/colinc86/LaTeXSwiftUI.git", from: "1.4.1"),
.package(url: "https://github.com/LiYanan2004/RichText.git", branch: "main"),
],
targets: [
.target(
Expand All @@ -38,6 +39,11 @@ let package = Package(
package: "LaTeXSwiftUI",
condition: .when(platforms: [.iOS, .macOS])
),
.product(
name: "RichText",
package: "RichText",
condition: .when(platforms: [.iOS, .macOS])
),
],
swiftSettings: [.swiftLanguageMode(.v6)]
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ extension BlockQuoteStyle where Self == DefaultBlockQuoteStyle {
fileprivate struct DefaultBlockQuoteView: View {
var configuration: BlockQuoteStyleConfiguration
@Environment(\.markdownFontGroup.blockQuote) private var font
@Environment(\.markdownRendererConfiguration.blockQuoteTintColor) private var tint
@Environment(\.markdownRendererConfiguration) private var rendererConfiguration
var body: some View {
let tint = rendererConfiguration.preferredTintColors[.blockQuote] ?? .accentColor
configuration.content
.padding(.vertical, 8)
.frame(maxWidth: .infinity, alignment: .leading)
Expand Down
18 changes: 18 additions & 0 deletions Sources/MarkdownView/Customizations/Font/MarkdownComponent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Foundation

@_documentation(visibility: internal)
public enum MarkdownComponent: Hashable, Sendable, CaseIterable {
case h1
case h2
case h3
case h4
case h5
case h6
case body
case codeBlock
case blockQuote
case tableHeader
case tableBody
case inlineMath
case displayMath
}
10 changes: 0 additions & 10 deletions Sources/MarkdownView/Customizations/Font/MarkdownTextType.swift

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@

import Foundation

struct AnyOrderedListMarkerProtocol: OrderedListMarkerProtocol {
public struct AnyOrderedListMarkerProtocol: OrderedListMarkerProtocol {
private var _marker: AnyHashable
var monospaced: Bool {
public var monospaced: Bool {
(_marker as! (any OrderedListMarkerProtocol)).monospaced
}

init<T: OrderedListMarkerProtocol>(_ marker: T) {
public init<T: OrderedListMarkerProtocol>(_ marker: T) {
self._marker = AnyHashable(marker)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@

import Foundation

struct AnyUnorderedListMarkerProtocol: UnorderedListMarkerProtocol {
public struct AnyUnorderedListMarkerProtocol: UnorderedListMarkerProtocol {
private var _marker: AnyHashable
var monospaced: Bool {
public var monospaced: Bool {
(_marker as! (any UnorderedListMarkerProtocol)).monospaced
}

init<T: UnorderedListMarkerProtocol>(_ marker: T) {
public init<T: UnorderedListMarkerProtocol>(_ marker: T) {
self._marker = AnyHashable(marker)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
//
// KeyPathModifiable.swift
// KeyPathModifying.swift
// MarkdownView
//
// Created by Yanan Li on 2025/2/9.
//

import Foundation

protocol KeyPathModifiable { }
protocol KeyPathModifying { }

extension KeyPathModifiable {
extension KeyPathModifying {
public func with<T>(_ keyPath: WritableKeyPath<Self, T>, _ newValue: T) -> Self {
var copy = self
copy[keyPath: keyPath] = newValue
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//
// BlockQuote.swift
// BlockMarkup++.swift
// MarkdownView
//
// Created by LiYanan2004 on 2024/12/11.
// Created by Yanan Li on 2026/1/18.
//

import Markdown
Expand All @@ -25,3 +25,22 @@ extension BlockQuote {
return index
}
}


extension ListItemContainer {
var listDepth: Int {
var index = 0

var currentElement = parent

while currentElement != nil {
if currentElement is ListItemContainer {
index += 1
}

currentElement = currentElement?.parent
}

return index
}
}
27 changes: 0 additions & 27 deletions Sources/MarkdownView/Helpers/Markdown/ListItemContainer.swift

This file was deleted.

16 changes: 0 additions & 16 deletions Sources/MarkdownView/Helpers/Markdown/Markdown+Sendable.swift

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,13 @@ extension Markup {
return false
}
}

// MARK: - Sendable Assumptions

// TODO: Remove these when swift-markdown adapted for swift 6.0
extension Markdown.Table: @retroactive @unchecked Sendable { }
extension Markdown.Table.Row: @retroactive @unchecked Sendable { }
extension Markdown.OrderedList: @retroactive @unchecked Sendable { }
extension Markdown.UnorderedList: @retroactive @unchecked Sendable { }
extension Markdown.ParseOptions: @retroactive @unchecked Sendable { }
extension Markdown.Heading: @retroactive @unchecked Sendable { }
32 changes: 32 additions & 0 deletions Sources/MarkdownView/Helpers/Markdown/SourceLocation++.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// SourceLocation++.swift
// MarkdownView
//
// Created by Yanan Li on 2026/1/18.
//

import Markdown

extension SourceLocation {
@available(*, deprecated, message: "Use `SourceLocation.offset(in:) -> String.Index` instead")
func offset(in text: String) -> Int {
let colIndex = column - 1
let previousLinesTotalChar = text
.split(separator: "\n", maxSplits: line - 1, omittingEmptySubsequences: false)
.dropLast()
.map { String($0) }
.joined(separator: "\n")
.count
return previousLinesTotalChar + colIndex + 1
}

func offset(in text: String) -> String.Index {
let colIndex = column - 1
let previousLinesTotalChar = text
.split(separator: "\n", maxSplits: line - 1, omittingEmptySubsequences: false)
.dropLast()
.joined(separator: "\n")
.count
return text.index(text.startIndex, offsetBy: previousLinesTotalChar + colIndex + 1)
}
}
14 changes: 0 additions & 14 deletions Sources/MarkdownView/Helpers/Markdown/SourceLocation.swift

This file was deleted.

1 change: 0 additions & 1 deletion Sources/MarkdownView/Helpers/Markdown/Table++.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,4 @@ extension Markdown.Table.Cell {
}
}
}

}
17 changes: 17 additions & 0 deletions Sources/MarkdownView/Helpers/Swift/Sequence++.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// Sequence++.swift
// MarkdownView
//
// Created by Yanan Li on 2026/1/19.
//

import Foundation

extension Sequence {
@_spi(Internal)
public func first<T>(
byUnwrapping transform: @escaping (Element) throws -> T?
) rethrows -> T? {
try self.lazy.compactMap(transform).first
}
}
25 changes: 7 additions & 18 deletions Sources/MarkdownView/MarkdownView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import Markdown
public struct MarkdownView: View {
@ObservedObject private var content: MarkdownContent

@Environment(\.markdownFontGroup.body) private var bodyFont
@Environment(\.markdownRendererConfiguration) private var configuration
@Environment(\.markdownViewRenderer) private var renderer

/// Creates a view that renders given markdown string.
/// - Parameter text: The markdown source to render.
Expand All @@ -27,29 +27,18 @@ public struct MarkdownView: View {
}

public var body: some View {
_renderedBody.font(bodyFont)
}

@ViewBuilder
private var _renderedBody: some View {
if configuration.rendersMath {
MathFirstMarkdownViewRenderer().makeBody(
content: content,
configuration: configuration
)
} else {
CmarkFirstMarkdownViewRenderer().makeBody(
content: content,
configuration: configuration
)
}
renderer
.makeBody(content: content, configuration: configuration)
.erasedToAnyView()
.font(configuration.fonts[.body] ?? Font.body)
}
}

@available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *)
@available(iOS 26.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *)
#Preview(traits: .sizeThatFitsLayout) {
VStack {
MarkdownView("Hello **World**")
.markdownTextSelection(.enabled)
}
#if os(macOS) || os(iOS)
.textSelection(.enabled)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,26 @@ struct CmarkFirstMarkdownViewRenderer: MarkdownViewRenderer {
.makeBody(for: content.document(options: parseOptions))
}
}

#if canImport(RichText)
import RichText

@available(iOS 26, macOS 26, *)
struct TextViewViewRenderer: MarkdownViewRenderer {
func makeBody(
content: MarkdownContent,
configuration: MarkdownRendererConfiguration
) -> some View {
var parseOptions = ParseOptions()
if !configuration.allowedBlockDirectiveRenderers.isEmpty {
parseOptions.insert(.parseBlockDirectives)
}

let textContent = CmarkTextContentVisitor(configuration: configuration)
.makeTextContent(for: content.document(options: parseOptions))
return TextView {
textContent
}
}
}
#endif
Loading
Loading