tauri-plugin-system-components 0.1.0

Native system UI components for Tauri 2 — native iOS tab bar over the webview, native controls, and glass window backgrounds on macOS/iOS.
Documentation
//
//  ImageUtil.swift
//  tauri-plugin-system-components
//
//  base64 → UIImage decoding plus the avatar-style transforms used by tab
//  items and image components.
//

import UIKit

enum ColorUtil {
    /// Parses `#RRGGBB` / `#RRGGBBAA` (leading `#` optional).
    static func from(hex: String) -> UIColor? {
        var value = hex.trimmingCharacters(in: .whitespacesAndNewlines)
        if value.hasPrefix("#") { value.removeFirst() }
        guard value.count == 6 || value.count == 8,
            let parsed = UInt64(value, radix: 16)
        else { return nil }
        let hasAlpha = value.count == 8
        let divisor: CGFloat = 255
        let r = CGFloat((parsed >> (hasAlpha ? 24 : 16)) & 0xFF) / divisor
        let g = CGFloat((parsed >> (hasAlpha ? 16 : 8)) & 0xFF) / divisor
        let b = CGFloat((parsed >> (hasAlpha ? 8 : 0)) & 0xFF) / divisor
        let a = hasAlpha ? CGFloat(parsed & 0xFF) / divisor : 1
        return UIColor(red: r, green: g, blue: b, alpha: a)
    }
}

enum ImageUtil {
    /// Decodes a raw base64 string or a `data:` URL into a UIImage.
    static func decode(_ input: String) -> UIImage? {
        var b64 = Substring(input)
        if input.hasPrefix("data:"), let comma = input.firstIndex(of: ",") {
            b64 = input[input.index(after: comma)...]
        }
        guard
            let data = Data(
                base64Encoded: String(b64).trimmingCharacters(in: .whitespacesAndNewlines),
                options: .ignoreUnknownCharacters
            )
        else { return nil }
        return UIImage(data: data)
    }

    /// Aspect-fills `image` into a `side`×`side` square, optionally clipped
    /// to a circle — the avatar treatment.
    static func icon(_ image: UIImage, side: CGFloat, circular: Bool) -> UIImage {
        let size = CGSize(width: side, height: side)
        let renderer = UIGraphicsImageRenderer(size: size)
        let rendered = renderer.image { _ in
            if circular {
                UIBezierPath(ovalIn: CGRect(origin: .zero, size: size)).addClip()
            }
            // Aspect-fill into the square.
            let scale = max(side / image.size.width, side / image.size.height)
            let drawSize = CGSize(width: image.size.width * scale, height: image.size.height * scale)
            let origin = CGPoint(x: (side - drawSize.width) / 2, y: (side - drawSize.height) / 2)
            image.draw(in: CGRect(origin: origin, size: drawSize))
        }
        // Keep original colors (tab bars tint template images).
        return rendered.withRenderingMode(.alwaysOriginal)
    }
}