Files
ProxyIOS/Scripts/generate_split_lock_variants.swift
Trey t 148bc3887c Add iPad support, auto-pinning, and comprehensive logging
- Adaptive iPhone/iPad layout with NavigationSplitView sidebar
- Auto-detect SSL-pinned domains, fall back to passthrough
- Certificate install via local HTTP server (Safari profile flow)
- App Group-backed CA, per-domain leaf cert LRU cache
- DB-backed config repository, Darwin notification throttling
- Rules engine, breakpoint rules, pinned domain tracking
- os.Logger instrumentation across tunnel/proxy/mitm/capture/cert/rules/db/ipc/ui
- Fix dyld framework embed, race conditions, thread safety
2026-04-11 12:52:18 -05:00

539 lines
19 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import AppKit
import CoreGraphics
import Foundation
enum SplitLockVariant: CaseIterable {
case balancedGate
case glassCore
case bracketTrace
case shieldLock
case signalKeyhole
var title: String {
switch self {
case .balancedGate: return "Balanced Gate"
case .glassCore: return "Glass Core"
case .bracketTrace: return "Bracket Trace"
case .shieldLock: return "Shield Lock"
case .signalKeyhole: return "Signal Keyhole"
}
}
var summary: String {
switch self {
case .balancedGate:
return "Closest to the original idea. Balanced lock and readable traces."
case .glassCore:
return "More premium and layered. Feels the most iOS 26-native."
case .bracketTrace:
return "Leans developer-tool. The right half reads closer to code."
case .shieldLock:
return "Feels safer and more consumer-trust oriented than technical."
case .signalKeyhole:
return "Adds a pulse/keyhole center so inspection feels active, not static."
}
}
var accent: NSColor {
switch self {
case .balancedGate: return NSColor(hex: 0x4F86FF)
case .glassCore: return NSColor(hex: 0x67B8FF)
case .bracketTrace: return NSColor(hex: 0x5A9DFF)
case .shieldLock: return NSColor(hex: 0x41A6FF)
case .signalKeyhole: return NSColor(hex: 0x4DE3FF)
}
}
}
enum LockStyle {
case flat
case glossy
}
struct Page {
static let size = CGSize(width: 960, height: 1400)
}
let outputDirectory = URL(fileURLWithPath: FileManager.default.currentDirectoryPath)
.appendingPathComponent("Design/icon_concepts", isDirectory: true)
let pdfURL = outputDirectory.appendingPathComponent("split_lock_variants.pdf")
let pngURL = outputDirectory.appendingPathComponent("split_lock_variants_preview.png")
try FileManager.default.createDirectory(at: outputDirectory, withIntermediateDirectories: true)
renderPDF(to: pdfURL)
renderPreview(to: pngURL)
print("Generated:")
print(pdfURL.path)
print(pngURL.path)
func renderPDF(to url: URL) {
var mediaBox = CGRect(origin: .zero, size: Page.size)
guard let context = CGContext(url as CFURL, mediaBox: &mediaBox, nil) else {
fatalError("Unable to create PDF context")
}
beginPDFPage(context, mediaBox) {
drawOverviewPage(in: mediaBox)
}
beginPDFPage(context, mediaBox) {
drawLargeIconsPage(in: mediaBox)
}
context.closePDF()
}
func renderPreview(to url: URL) {
let previewSize = CGSize(width: 1500, height: 2188)
let image = NSImage(size: previewSize)
image.lockFocus()
NSGraphicsContext.current?.imageInterpolation = .high
let scale = previewSize.width / Page.size.width
let context = NSGraphicsContext.current?.cgContext
context?.saveGState()
context?.scaleBy(x: scale, y: scale)
drawOverviewPage(in: CGRect(origin: .zero, size: Page.size))
context?.restoreGState()
image.unlockFocus()
guard let tiff = image.tiffRepresentation,
let rep = NSBitmapImageRep(data: tiff),
let png = rep.representation(using: .png, properties: [:]) else {
fatalError("Unable to create PNG preview")
}
try? png.write(to: url)
}
func beginPDFPage(_ context: CGContext, _ mediaBox: CGRect, draw: () -> Void) {
context.beginPDFPage(nil)
let graphicsContext = NSGraphicsContext(cgContext: context, flipped: false)
NSGraphicsContext.saveGraphicsState()
NSGraphicsContext.current = graphicsContext
draw()
NSGraphicsContext.restoreGraphicsState()
context.endPDFPage()
}
func drawOverviewPage(in rect: CGRect) {
fill(rect, color: NSColor(hex: 0xF5F7FC))
drawText(
"Split Lock Explorations",
in: CGRect(x: 68, y: rect.height - 116, width: rect.width - 136, height: 40),
font: .systemFont(ofSize: 34, weight: .bold),
color: NSColor(hex: 0x0F172A)
)
drawText(
"Five directions built from the Split Lock concept. Each card shows the glossy primary icon with a small flat simplification inset.",
in: CGRect(x: 68, y: rect.height - 160, width: rect.width - 136, height: 34),
font: .systemFont(ofSize: 16, weight: .medium),
color: NSColor(hex: 0x475467)
)
let cardWidth = (rect.width - 68 * 2 - 26) / 2
let cardHeight: CGFloat = 300
let layouts: [(SplitLockVariant, CGRect)] = [
(.balancedGate, CGRect(x: 68, y: 900, width: cardWidth, height: cardHeight)),
(.glassCore, CGRect(x: 68 + cardWidth + 26, y: 900, width: cardWidth, height: cardHeight)),
(.bracketTrace, CGRect(x: 68, y: 574, width: cardWidth, height: cardHeight)),
(.shieldLock, CGRect(x: 68 + cardWidth + 26, y: 574, width: cardWidth, height: cardHeight)),
(.signalKeyhole, CGRect(x: 68, y: 248, width: cardWidth, height: cardHeight))
]
for (variant, frame) in layouts {
drawVariantCard(variant: variant, in: frame)
}
drawDecisionCard(
in: CGRect(x: 68 + cardWidth + 26, y: 248, width: cardWidth, height: cardHeight)
)
}
func drawLargeIconsPage(in rect: CGRect) {
fill(rect, color: NSColor(hex: 0xF8FAFD))
drawText(
"Large Icon Comparison",
in: CGRect(x: 68, y: rect.height - 114, width: rect.width - 136, height: 38),
font: .systemFont(ofSize: 32, weight: .bold),
color: NSColor(hex: 0x0F172A)
)
drawText(
"This page removes most of the annotation so you can judge silhouette, weight, and app-store readability.",
in: CGRect(x: 68, y: rect.height - 156, width: rect.width - 136, height: 28),
font: .systemFont(ofSize: 15, weight: .medium),
color: NSColor(hex: 0x475467)
)
let rowHeight: CGFloat = 220
for (index, variant) in SplitLockVariant.allCases.enumerated() {
let y = rect.height - 250 - CGFloat(index) * (rowHeight + 16)
let frame = CGRect(x: 68, y: y, width: rect.width - 136, height: rowHeight)
drawSurface(frame, fill: NSColor.white, stroke: NSColor(hex: 0xD9E0EC))
drawIconBackground(in: CGRect(x: frame.minX + 30, y: frame.minY + 26, width: 168, height: 168))
drawSplitLock(variant: variant, style: .glossy, in: CGRect(x: frame.minX + 42, y: frame.minY + 38, width: 144, height: 144))
drawText(
variant.title,
in: CGRect(x: frame.minX + 228, y: frame.maxY - 62, width: frame.width - 252, height: 30),
font: .systemFont(ofSize: 26, weight: .bold),
color: NSColor(hex: 0x111827)
)
drawText(
variant.summary,
in: CGRect(x: frame.minX + 228, y: frame.maxY - 118, width: frame.width - 252, height: 60),
font: .systemFont(ofSize: 16, weight: .medium),
color: NSColor(hex: 0x475467)
)
drawPill(
"glossy primary",
in: CGRect(x: frame.minX + 228, y: frame.minY + 28, width: 112, height: 28),
fill: variant.accent.withAlphaComponent(0.14),
textColor: NSColor(hex: 0x275FD7)
)
}
}
func drawVariantCard(variant: SplitLockVariant, in frame: CGRect) {
drawSurface(frame, fill: NSColor.white, stroke: NSColor(hex: 0xD9E0EC))
let iconFrame = CGRect(x: frame.minX + 24, y: frame.minY + 62, width: 158, height: 158)
drawIconBackground(in: iconFrame)
drawSplitLock(variant: variant, style: .glossy, in: iconFrame.insetBy(dx: 14, dy: 14))
let flatFrame = CGRect(x: frame.minX + 30, y: frame.minY + 196, width: 58, height: 58)
drawMiniBadge(flatFrame, variant: variant)
drawText(
variant.title,
in: CGRect(x: frame.minX + 204, y: frame.maxY - 70, width: frame.width - 226, height: 28),
font: .systemFont(ofSize: 22, weight: .bold),
color: NSColor(hex: 0x111827)
)
drawText(
variant.summary,
in: CGRect(x: frame.minX + 204, y: frame.maxY - 138, width: frame.width - 226, height: 78),
font: .systemFont(ofSize: 14, weight: .medium),
color: NSColor(hex: 0x475467)
)
drawPill(
"flat inset",
in: CGRect(x: frame.minX + 100, y: frame.minY + 208, width: 72, height: 22),
fill: NSColor(hex: 0xEDF2FF),
textColor: NSColor(hex: 0x3A63D1)
)
}
func drawDecisionCard(in frame: CGRect) {
drawSurface(frame, fill: NSColor(hex: 0x17223A), stroke: NSColor(hex: 0x17223A))
drawText(
"What To Pick For",
in: CGRect(x: frame.minX + 26, y: frame.maxY - 68, width: frame.width - 52, height: 28),
font: .systemFont(ofSize: 24, weight: .bold),
color: .white
)
let bullets = [
"Most balanced: Balanced Gate",
"Most premium: Glass Core",
"Most dev-tool: Bracket Trace",
"Most trustworthy: Shield Lock",
"Most active / dynamic: Signal Keyhole"
]
for (index, bullet) in bullets.enumerated() {
let rowY = frame.maxY - 118 - CGFloat(index) * 34
fillEllipse(CGRect(x: frame.minX + 28, y: rowY + 6, width: 10, height: 10), color: NSColor(hex: 0x6DA1FF))
drawText(
bullet,
in: CGRect(x: frame.minX + 48, y: rowY, width: frame.width - 72, height: 22),
font: .systemFont(ofSize: 15, weight: .semibold),
color: NSColor.white.withAlphaComponent(0.94)
)
}
drawText(
"Pick one and Ill iterate on thickness, color, background, or make it more minimal.",
in: CGRect(x: frame.minX + 26, y: frame.minY + 28, width: frame.width - 52, height: 42),
font: .systemFont(ofSize: 15, weight: .medium),
color: NSColor.white.withAlphaComponent(0.82)
)
}
func drawIconBackground(in rect: CGRect) {
let path = NSBezierPath(roundedRect: rect, xRadius: rect.width * 0.23, yRadius: rect.height * 0.23)
let gradient = NSGradient(colors: [
NSColor(hex: 0xF6F9FF),
NSColor(hex: 0xDFE9FF),
NSColor(hex: 0xD1DEFF)
])
gradient?.draw(in: path, angle: 90)
let highlight = NSBezierPath(roundedRect: rect.insetBy(dx: 16, dy: 16).offsetBy(dx: 0, dy: 20), xRadius: 34, yRadius: 34)
NSColor.white.withAlphaComponent(0.24).setFill()
highlight.fill()
NSColor.white.withAlphaComponent(0.74).setStroke()
path.lineWidth = 1.6
path.stroke()
}
func drawMiniBadge(_ rect: CGRect, variant: SplitLockVariant) {
let path = NSBezierPath(roundedRect: rect, xRadius: rect.width * 0.28, yRadius: rect.height * 0.28)
let gradient = NSGradient(colors: [NSColor(hex: 0xFCFDFF), NSColor(hex: 0xEEF3FF)])
gradient?.draw(in: path, angle: 90)
NSColor.white.withAlphaComponent(0.8).setStroke()
path.lineWidth = 1.2
path.stroke()
drawSplitLock(variant: variant, style: .flat, in: rect.insetBy(dx: 8, dy: 8))
}
func drawSplitLock(variant: SplitLockVariant, style: LockStyle, in rect: CGRect) {
switch variant {
case .balancedGate:
drawBalancedGate(style: style, in: rect)
case .glassCore:
drawGlassCore(style: style, in: rect)
case .bracketTrace:
drawBracketTrace(style: style, in: rect)
case .shieldLock:
drawShieldLock(style: style, in: rect)
case .signalKeyhole:
drawSignalKeyhole(style: style, in: rect)
}
}
func drawBalancedGate(style: LockStyle, in rect: CGRect) {
drawBaseLock(style: style, in: rect, bodyInset: 0, shackleLift: 0)
drawTraceLines(
in: rect,
color: NSColor(hex: style == .flat ? 0x5EA4FF : 0x7EB8FF),
spread: 26,
weight: style == .flat ? 6 : 7
)
}
func drawGlassCore(style: LockStyle, in rect: CGRect) {
drawBaseLock(style: style, in: rect, bodyInset: 4, shackleLift: 6)
let glowRect = CGRect(x: rect.midX - 18, y: rect.midY - 14, width: 36, height: 36)
if style == .glossy {
fillEllipse(glowRect.insetBy(dx: -10, dy: -10), color: NSColor(hex: 0x84DAFF).withAlphaComponent(0.16))
}
fillEllipse(glowRect, color: NSColor(hex: style == .flat ? 0x6CCFFF : 0x86E5FF))
drawTraceLines(
in: rect,
color: NSColor(hex: style == .flat ? 0x74B0FF : 0xA5CCFF),
spread: 20,
weight: style == .flat ? 5 : 6
)
}
func drawBracketTrace(style: LockStyle, in rect: CGRect) {
drawBaseLock(style: style, in: rect, bodyInset: 0, shackleLift: 0)
let bracket = NSBezierPath()
bracket.move(to: CGPoint(x: rect.midX + 10, y: rect.midY + 34))
bracket.line(to: CGPoint(x: rect.maxX - 30, y: rect.midY + 34))
bracket.line(to: CGPoint(x: rect.maxX - 30, y: rect.midY + 10))
bracket.move(to: CGPoint(x: rect.midX + 10, y: rect.midY - 34))
bracket.line(to: CGPoint(x: rect.maxX - 30, y: rect.midY - 34))
bracket.line(to: CGPoint(x: rect.maxX - 30, y: rect.midY - 10))
bracket.lineWidth = style == .flat ? 6 : 7
bracket.lineCapStyle = .round
bracket.lineJoinStyle = .round
NSColor(hex: style == .flat ? 0x67A8FF : 0x8FC1FF).setStroke()
bracket.stroke()
drawTraceLines(
in: rect,
color: NSColor(hex: style == .flat ? 0x4F86FF : 0x69A8FF),
spread: 24,
weight: style == .flat ? 5 : 6
)
}
func drawShieldLock(style: LockStyle, in rect: CGRect) {
let shield = NSBezierPath()
shield.move(to: CGPoint(x: rect.midX, y: rect.maxY - 6))
shield.curve(
to: CGPoint(x: rect.minX + 32, y: rect.midY + 18),
controlPoint1: CGPoint(x: rect.midX - 40, y: rect.maxY - 8),
controlPoint2: CGPoint(x: rect.minX + 26, y: rect.maxY - 26)
)
shield.line(to: CGPoint(x: rect.midX, y: rect.minY + 6))
shield.line(to: CGPoint(x: rect.maxX - 32, y: rect.midY + 18))
shield.curve(
to: CGPoint(x: rect.midX, y: rect.maxY - 6),
controlPoint1: CGPoint(x: rect.maxX - 26, y: rect.maxY - 26),
controlPoint2: CGPoint(x: rect.midX + 40, y: rect.maxY - 8)
)
shield.close()
if style == .glossy {
NSColor.white.withAlphaComponent(0.18).setFill()
shield.fill()
} else {
NSColor(hex: 0xEDF4FF).setFill()
shield.fill()
}
drawBaseLock(style: style, in: rect.insetBy(dx: 10, dy: 10), bodyInset: 2, shackleLift: 2)
drawTraceLines(
in: rect.insetBy(dx: 10, dy: 10),
color: NSColor(hex: style == .flat ? 0x5F9FFF : 0x8CC1FF),
spread: 22,
weight: style == .flat ? 5 : 6
)
}
func drawSignalKeyhole(style: LockStyle, in rect: CGRect) {
drawBaseLock(style: style, in: rect, bodyInset: 1, shackleLift: 0)
let keyholeCircle = CGRect(x: rect.midX - 14, y: rect.midY + 2, width: 28, height: 28)
let keyholeStem = NSBezierPath(roundedRect: CGRect(x: rect.midX - 7, y: rect.midY - 24, width: 14, height: 30), xRadius: 7, yRadius: 7)
if style == .glossy {
fillEllipse(keyholeCircle.insetBy(dx: -8, dy: -8), color: NSColor(hex: 0x7FE4FF).withAlphaComponent(0.16))
}
fillEllipse(keyholeCircle, color: NSColor(hex: style == .flat ? 0x143A6F : 0xF8FBFF))
let stemColor = style == .flat ? NSColor(hex: 0x143A6F) : NSColor(hex: 0xF8FBFF)
stemColor.setFill()
keyholeStem.fill()
drawTraceLines(
in: rect,
color: NSColor(hex: style == .flat ? 0x49CFFF : 0x5CE7FF),
spread: 28,
weight: style == .flat ? 5 : 6
)
}
func drawBaseLock(style: LockStyle, in rect: CGRect, bodyInset: CGFloat, shackleLift: CGFloat) {
let bodyRect = CGRect(
x: rect.midX - 42 + bodyInset,
y: rect.midY - 34,
width: 96 - bodyInset * 2,
height: 76
)
let body = NSBezierPath(roundedRect: bodyRect, xRadius: 22, yRadius: 22)
switch style {
case .flat:
NSColor(hex: 0xF7FAFF).setFill()
body.fill()
NSColor(hex: 0x1E3A72).setStroke()
body.lineWidth = 5.5
body.stroke()
case .glossy:
let gradient = NSGradient(colors: [
NSColor.white.withAlphaComponent(0.92),
NSColor(hex: 0xE0EBFF).withAlphaComponent(0.88)
])
gradient?.draw(in: body, angle: 90)
NSColor.white.withAlphaComponent(0.82).setStroke()
body.lineWidth = 5
body.stroke()
}
let shackle = NSBezierPath()
shackle.move(to: CGPoint(x: rect.midX - 32, y: bodyRect.maxY - 1))
shackle.curve(
to: CGPoint(x: rect.midX + 12, y: bodyRect.maxY + 40 + shackleLift),
controlPoint1: CGPoint(x: rect.midX - 34, y: bodyRect.maxY + 42 + shackleLift),
controlPoint2: CGPoint(x: rect.midX + 12, y: bodyRect.maxY + 42 + shackleLift)
)
shackle.lineWidth = style == .flat ? 11 : 12
shackle.lineCapStyle = .round
NSColor(hex: style == .flat ? 0x1E3A72 : 0xF8FBFF).setStroke()
shackle.stroke()
let leftFillRect = CGRect(x: bodyRect.minX, y: bodyRect.minY, width: bodyRect.width * 0.52, height: bodyRect.height)
let leftFill = NSBezierPath(roundedRect: leftFillRect, xRadius: 18, yRadius: 18)
NSColor(hex: style == .flat ? 0x76A8FF : 0xBCD4FF).setFill()
leftFill.fill()
}
func drawTraceLines(in rect: CGRect, color: NSColor, spread: CGFloat, weight: CGFloat) {
let traces: [(CGFloat, CGFloat)] = [(spread, 0.94), (0, 1.0), (-spread, 0.92)]
for (offset, scale) in traces {
let start = CGPoint(x: rect.midX + 6, y: rect.midY + offset * 0.32)
let end = CGPoint(x: rect.maxX - 20, y: rect.midY + offset)
let path = NSBezierPath()
path.move(to: start)
path.curve(
to: end,
controlPoint1: CGPoint(x: start.x + 22, y: start.y),
controlPoint2: CGPoint(x: end.x - 18, y: end.y)
)
path.lineWidth = weight * scale
path.lineCapStyle = .round
color.setStroke()
path.stroke()
fillEllipse(CGRect(x: end.x - 5, y: end.y - 5, width: 10, height: 10), color: color)
}
}
func drawSurface(_ rect: CGRect, fill: NSColor, stroke: NSColor) {
let shadow = NSShadow()
shadow.shadowColor = NSColor.black.withAlphaComponent(0.08)
shadow.shadowOffset = CGSize(width: 0, height: -7)
shadow.shadowBlurRadius = 18
NSGraphicsContext.saveGraphicsState()
shadow.set()
let path = NSBezierPath(roundedRect: rect, xRadius: 26, yRadius: 26)
fill.setFill()
path.fill()
NSGraphicsContext.restoreGraphicsState()
let strokePath = NSBezierPath(roundedRect: rect, xRadius: 26, yRadius: 26)
stroke.setStroke()
strokePath.lineWidth = 1.1
strokePath.stroke()
}
func drawPill(_ title: String, in rect: CGRect, fill: NSColor, textColor: NSColor) {
let path = NSBezierPath(roundedRect: rect, xRadius: rect.height / 2, yRadius: rect.height / 2)
fill.setFill()
path.fill()
drawText(title, in: rect.offsetBy(dx: 0, dy: 2), font: .systemFont(ofSize: 11, weight: .bold), color: textColor, alignment: .center)
}
func drawText(_ text: String, in rect: CGRect, font: NSFont, color: NSColor, alignment: NSTextAlignment = .left) {
let paragraph = NSMutableParagraphStyle()
paragraph.alignment = alignment
paragraph.lineBreakMode = .byWordWrapping
let attributes: [NSAttributedString.Key: Any] = [
.font: font,
.foregroundColor: color,
.paragraphStyle: paragraph
]
NSString(string: text).draw(with: rect, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: attributes)
}
func fill(_ rect: CGRect, color: NSColor) {
color.setFill()
NSBezierPath(rect: rect).fill()
}
func fillEllipse(_ rect: CGRect, color: NSColor) {
color.setFill()
NSBezierPath(ovalIn: rect).fill()
}
extension NSColor {
convenience init(hex: Int, alpha: CGFloat = 1.0) {
let red = CGFloat((hex >> 16) & 0xFF) / 255.0
let green = CGFloat((hex >> 8) & 0xFF) / 255.0
let blue = CGFloat(hex & 0xFF) / 255.0
self.init(calibratedRed: red, green: green, blue: blue, alpha: alpha)
}
}