Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Idea: Include means for Markdown styles to be defined flexibly, strongly-typed #153

Open
akingdom opened this issue Jun 27, 2024 · 0 comments

Comments

@akingdom
Copy link

akingdom commented Jun 27, 2024

I needed to patch SwiftyMarkdown into existing code. Styles needed to be compatible, strongly typed, consistent and readable. Here's my example of usage. Hope it helps someone.

Detail:

init() takes a list of markdown styles such as body and applies StyleProperty to them.
func applyStyles copies the custom properties to the styles.
enum StyleProperty provides the custom properties structure.

Code:

import AppKit
import SwiftUI
import SwiftyMarkdown

class MarkdownHelper {
    static var shared: MarkdownHelper = MarkdownHelper()
    static let customFontName = "Bitstream Vera Sans Mono"
    var md : SwiftyMarkdown
    
    private init() {
        let md : SwiftyMarkdown = SwiftyMarkdown(string: "")
        self.md = md
        let size = 14
        applyStyles(stylesMapping: [

            md.body: [  // the default style
                .fontName(customFontName),
                .fontStyle(.normal),
                .fontSize(size),
                .color(PhosphorColors.white)
            ],
            md.code: [  // the code style
                .fontName(customFontName),
                .fontStyle(.normal),
                .fontSize(size),
                .color(PhosphorColors.white),
            ],
            md.h1: [
                .fontName(customFontName),
                .fontStyle(.bold),
                .fontSize(size),
                .color(PhosphorColors.brightWhite)
            ],
            md.h2: [
                .fontName(customFontName),
                .fontStyle(.bold),
                .fontSize(size),
                .color(PhosphorColors.brightAmber)
            ],
            md.h3: [
                .fontName(customFontName),
                .fontStyle(.bold),
                .fontSize(size),
                .color(PhosphorColors.brightCyan)
            ],
            md.italic: [
                .fontName(customFontName),
                .fontStyle(.italic),
                .fontSize(size),
                .color(PhosphorColors.green)
            ],
            md.bold: [
                .fontName(customFontName),
                .fontStyle(.bold),
                .fontSize(size),
                .color(PhosphorColors.overBrightGreen)
            ]
        ])
    }

    enum StyleProperty {
        case fontName(String)
        case color(NSColor)
        case fontSize(CGFloat)
        case fontStyle(FontStyle)
        case alignment(NSTextAlignment)
        case lineSpacing(CGFloat)
        case paragraphSpacing(CGFloat)
        case underlineStyle(NSUnderlineStyle)
        case underlineColor(NSColor)
    }

    func applyStyles(stylesMapping: [NSObject: [StyleProperty]]) {
        // Applying styles
        for (instance, properties) in stylesMapping {
            for property in properties {
                switch property {
                case .fontName(let value):
                    (instance as? BasicStyles)?.fontName = value
                    (instance as? LineStyles)?.fontName = value
                case .color(let value):
                    (instance as? BasicStyles)?.color = value
                    (instance as? LineStyles)?.color = value
                case .fontSize(let value):
                    (instance as? BasicStyles)?.fontSize = value
                    (instance as? LineStyles)?.fontSize = value
                case .fontStyle(let value):
                    (instance as? BasicStyles)?.fontStyle = value
                    (instance as? LineStyles)?.fontStyle = value
                case .alignment(let value):
                    (instance as? LineStyles)?.alignment = value
                case .lineSpacing(let value):
                    (instance as? LineStyles)?.lineSpacing = value
                case .paragraphSpacing(let value):
                    (instance as? LineStyles)?.paragraphSpacing = value
                case .underlineStyle(let value):
                    (instance as? LinkStyles)?.underlineStyle = value
                case .underlineColor(let value):
                    (instance as? LinkStyles)?.underlineColor = value
                }
            }
        }
    }
    
    func parse(markdown: String) -> NSAttributedString {
        md.string = markdown.replacingOccurrences(of: "\r\n", with: "\n")  // the presumption is that styles do not change, otherwise we'd reinitialise all styles each time.
        return md.attributedString()
    }

    
    struct PhosphorColors {
        static let white : NSColor = NSColor(Color(#colorLiteral(red: 0.9159484512, green: 0.9159484512, blue: 0.9463420244, alpha: 1)))
        static let amber : NSColor = NSColor(Color(#colorLiteral(red: 0.990, green: 0.857, blue: 0.205, alpha: 1)))
        static let darkAmber : NSColor = NSColor(Color(#colorLiteral(red: 0.937, green: 0.622, blue: 0.227, alpha: 1)))
        static let mediumBrightBlue : NSColor = NSColor(Color(#colorLiteral(red: 0.699, green: 0.834, blue: 0.929, alpha: 1)))
        static let blue : NSColor = NSColor(Color(#colorLiteral(red: 0.623, green: 0.826, blue: 0.973, alpha: 1)))
        static let darkBlue : NSColor = NSColor(Color(#colorLiteral(red: 0.343, green: 0.559, blue: 0.731, alpha: 1)))
        static let overBrightGreen : NSColor = NSColor(Color(#colorLiteral(red: 0.596, green: 0.991, blue: 0.988, alpha: 1)))
        static let darkGreen : NSColor = NSColor(Color(#colorLiteral(red: 0.400, green: 0.704, blue: 0.386, alpha: 1)))
        static let green : NSColor = NSColor(Color(#colorLiteral(red: 0.231, green: 0.925, blue: 0.349, alpha: 1)))
        static let noSignal : NSColor = NSColor(Color(#colorLiteral(red: 0.017, green: 0.198, blue: 1.000, alpha: 1)))
        static let red : NSColor = NSColor(Color(#colorLiteral(red: 0.940, green: 0.300, blue: 0.280, alpha: 1)))
        static let magenta : NSColor = NSColor(Color(#colorLiteral(red: 0.780, green: 0.360, blue: 0.780, alpha: 1)))
        static let cyan : NSColor = NSColor(Color(#colorLiteral(red: 0.420, green: 0.840, blue: 0.780, alpha: 1)))
        static let yellow : NSColor = NSColor(Color(#colorLiteral(red: 0.980, green: 0.880, blue: 0.240, alpha: 1)))
        // Bright colors based on your existing colors
        static let brightWhite : NSColor = NSColor(Color(#colorLiteral(red: 1.000, green: 1.000, blue: 1.000, alpha: 1)))
        static let brightAmber : NSColor = NSColor(Color(#colorLiteral(red: 1.000, green: 0.922, blue: 0.531, alpha: 1)))
        static let brightBrightBlue : NSColor = NSColor(Color(#colorLiteral(red: 0.792, green: 0.937, blue: 1.000, alpha: 1)))
        static let brightBlue : NSColor = NSColor(Color(#colorLiteral(red: 0.792, green: 0.923, blue: 1.000, alpha: 1)))
        static let brightDarkBlue : NSColor = NSColor(Color(#colorLiteral(red: 0.517, green: 0.735, blue: 1.000, alpha: 1)))
        static let brightOverBrightGreen : NSColor = NSColor(Color(#colorLiteral(red: 0.800, green: 1.000, blue: 1.000, alpha: 1)))
        static let brightDarkGreen : NSColor = NSColor(Color(#colorLiteral(red: 0.543, green: 0.893, blue: 0.756, alpha: 1)))
        static let brightGreen : NSColor = NSColor(Color(#colorLiteral(red: 0.447, green: 1.000, blue: 0.752, alpha: 1)))
        static let brightNoSignal : NSColor = NSColor(Color(#colorLiteral(red: 0.042, green: 0.556, blue: 1.000, alpha: 1)))
        static let brightRed : NSColor = NSColor(Color(#colorLiteral(red: 1.000, green: 0.440, blue: 0.420, alpha: 1)))
        static let brightMagenta : NSColor = NSColor(Color(#colorLiteral(red: 0.896, green: 0.501, blue: 0.896, alpha: 1)))
        static let brightCyan : NSColor = NSColor(Color(#colorLiteral(red: 0.504, green: 0.954, blue: 0.934, alpha: 1)))
        static let brightYellow : NSColor = NSColor(Color(#colorLiteral(red: 1.000, green: 0.930, blue: 0.300, alpha: 1)))
    }

}

extension Color {
    init(_ nsColor: NSColor) {
        self.init(
            red: Double(nsColor.redComponent),
            green: Double(nsColor.greenComponent),
            blue: Double(nsColor.blueComponent),
            opacity: Double(nsColor.alphaComponent)
        )
    }
}

extension NSColor {
    var asColor: Color {
        let r = Double(redComponent)
        let g = Double(greenComponent)
        let b = Double(blueComponent)
        let a = Double(alphaComponent)
        return Color(.sRGB, red: r, green: g, blue: b, opacity: a)
    }
}

Caveat: original code works fine but I've removed various code unneeded for the example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant