// A stable, well-distributed color per profile name, shared by the menu // bar dots and the Manage window's profile list so the same name always // reads as the same color everywhere. import AppKit enum ProfileColor { // FNV-1a: a proper avalanching hash, not a preset-palette lookup — the // full hue wheel is available, and single-character name edits (e.g. // "personal" vs "personal2") land on unrelated hues instead of an // adjacent bucket the way a plain sum-of-scalars hash mod'd into a // short color list would. private static func fnv1aHash(_ s: String) -> UInt32 { var hash: UInt32 = 0x811c_9dc5 for byte in s.utf8 { hash ^= UInt32(byte) hash = hash &* 0x0100_0193 } return hash } static func dotColor(for name: String) -> NSColor { let hue = CGFloat(fnv1aHash(name) % 360) / 360.0 return NSColor(calibratedHue: hue, saturation: 0.65, brightness: 0.9, alpha: 1.0) } // `dimmed` marks a profile that isn't actually running right now — the // colored dot still identifies it, but faded, so "open" vs "known but // closed" is visible without reading a tooltip. static func dotImage(for name: String, dimmed: Bool = false) -> NSImage { let size = NSSize(width: 10, height: 10) let image = NSImage(size: size) image.lockFocus() dotColor(for: name).withAlphaComponent(dimmed ? 0.3 : 1.0).setFill() NSBezierPath(ovalIn: NSRect(origin: .zero, size: size)).fill() image.unlockFocus() image.isTemplate = false return image } }