114 lines
4.3 KiB
Swift
114 lines
4.3 KiB
Swift
import AppKit
|
||
import CoreText
|
||
import OpenAPIRuntime
|
||
import SwiftUI
|
||
|
||
func runFonts(_ font: CTFont, _ text: String) -> String {
|
||
let line = CTLineCreateWithAttributedString(NSAttributedString(string: text, attributes: [.font: font as NSFont]))
|
||
return (CTLineGetGlyphRuns(line) as! [CTRun]).map { run in
|
||
let f = (CTRunGetAttributes(run) as NSDictionary)[kCTFontAttributeName] as! CTFont
|
||
let file = (CTFontCopyAttribute(f, kCTFontURLAttribute) as? URL)?.lastPathComponent ?? "system"
|
||
let r = CTRunGetStringRange(run)
|
||
let part = (text as NSString).substring(with: NSRange(location: r.location, length: r.length))
|
||
return "\(part) → \(CTFontCopyPostScriptName(f)) (\(file))"
|
||
}.joined(separator: "\n")
|
||
}
|
||
|
||
struct FontSpike: View {
|
||
private let sample = "The quick brown fox jumps — 0O 1lI {}[] őŁżę → │"
|
||
|
||
var body: some View {
|
||
ScrollView {
|
||
VStack(alignment: .leading, spacing: 12) {
|
||
ForEach(Array([(400.0, false), (700.0, false), (400.0, true), (700.0, true)].enumerated()), id: \.offset) { _, style in
|
||
let (w, italic) = style
|
||
VStack(alignment: .leading) {
|
||
Text("weight \(Int(w))\(italic ? " italic" : "")")
|
||
.foregroundStyle(.secondary)
|
||
Text(sample)
|
||
.font(Fonts.font(16, weight: w, italic: italic))
|
||
Text(runFonts(Fonts.ct(16, weight: w, italic: italic), sample))
|
||
.foregroundStyle(.secondary)
|
||
}
|
||
}
|
||
VStack(alignment: .leading) {
|
||
Text("Fonts.font(16) with SwiftUI .bold() and .italic()")
|
||
.foregroundStyle(.secondary)
|
||
Text(sample)
|
||
.font(Fonts.font(16))
|
||
.bold()
|
||
.italic()
|
||
}
|
||
VStack(alignment: .leading) {
|
||
Text("System font, for comparison")
|
||
.foregroundStyle(.secondary)
|
||
Text(sample)
|
||
.font(.system(size: 16))
|
||
}
|
||
}
|
||
.frame(maxWidth: .infinity, alignment: .leading)
|
||
.padding(8)
|
||
}
|
||
}
|
||
}
|
||
|
||
struct SVGSpike: View {
|
||
@Environment(Store.self) private var store
|
||
@ViewState private var path = "web/favicon.svg"
|
||
@ViewState private var image: NSImage?
|
||
@ViewState private var failure = ""
|
||
|
||
var body: some View {
|
||
VStack(alignment: .leading) {
|
||
HStack {
|
||
TextField("Worktree path", text: $path)
|
||
.frame(width: 320)
|
||
Button("Load") {
|
||
Task {
|
||
await load()
|
||
}
|
||
}
|
||
}
|
||
if let image {
|
||
Text(verbatim: "NSImage size \(Int(image.size.width))×\(Int(image.size.height)), reps \(image.representations.map { String(describing: type(of: $0)) })")
|
||
.foregroundStyle(.secondary)
|
||
HStack(alignment: .top, spacing: 16) {
|
||
Image(nsImage: image)
|
||
Image(nsImage: image)
|
||
.resizable()
|
||
.scaledToFit()
|
||
.frame(width: 256, height: 256)
|
||
}
|
||
}
|
||
if !failure.isEmpty {
|
||
Text(failure)
|
||
}
|
||
Spacer()
|
||
}
|
||
.task {
|
||
await load()
|
||
}
|
||
}
|
||
|
||
private func load() async {
|
||
image = nil
|
||
failure = ""
|
||
guard let taskId = store.selectedTaskId else {
|
||
return
|
||
}
|
||
do {
|
||
let body = switch try await store.api.client.getTaskImage(path: .init(taskId: taskId), query: .init(path: path)).ok.body {
|
||
case .png(let b), .jpeg(let b), .imageGif(let b), .imageWebp(let b), .imageSvgXml(let b): b
|
||
}
|
||
let data = try await Data(collecting: body, upTo: 32 << 20)
|
||
guard let img = NSImage(data: data) else {
|
||
failure = "image not available: \(path)"
|
||
return
|
||
}
|
||
image = img
|
||
} catch {
|
||
failure = "image not available: \(path) (\(errorText(error)))"
|
||
}
|
||
}
|
||
}
|