// // CVResumeAppearance.swift // App for Indeed // // Shared résumé colours for gallery thumbnails and filled CV preview/export. // Résumés always use a print-style light palette regardless of app light/dark mode. // import AppKit @MainActor enum CVResumeAppearance { struct Colors { let paper: NSColor let ink: NSColor let muted: NSColor let rule: NSColor let cardBackground: NSColor let sidebarTint: NSColor let accentRed: NSColor let accentBlue: NSColor } /// Print-style palette — always light, even when the app chrome is in dark mode. static func colors() -> Colors { Colors( paper: NSColor.white, ink: NSColor(srgbRed: 38 / 255, green: 50 / 255, blue: 71 / 255, alpha: 1), muted: NSColor(srgbRed: 110 / 255, green: 118 / 255, blue: 132 / 255, alpha: 1), rule: NSColor(srgbRed: 228 / 255, green: 232 / 255, blue: 240 / 255, alpha: 1), cardBackground: NSColor.white, sidebarTint: NSColor(srgbRed: 244 / 255, green: 246 / 255, blue: 250 / 255, alpha: 1), accentRed: NSColor(srgbRed: 207 / 255, green: 67 / 255, blue: 50 / 255, alpha: 1), accentBlue: AppDashboardTheme.brandBlue ) } /// Slight paper tint by layout variant (gallery + filled CV use the same rule). static func paperBackground(variant: Int, base: NSColor) -> NSColor { switch variant % 5 { case 0: return base case 1: return NSColor(srgbRed: 0.995, green: 0.992, blue: 0.985, alpha: 1) case 2: return NSColor(srgbRed: 0.96, green: 0.99, blue: 1, alpha: 1) case 3: return NSColor(srgbRed: 0.99, green: 0.99, blue: 0.99, alpha: 1) default: return NSColor(srgbRed: 0.99, green: 0.98, blue: 0.995, alpha: 1) } } static func accentColor(for template: CVTemplate) -> NSColor { let palette = colors() switch template.accent { case .redUnderline, .redBar: return palette.accentRed case .blueBar: return template.themeColor case .none: return template.themeColor.blended(withFraction: 0.5, of: palette.ink) ?? template.themeColor } } static func sectionHeadingColor(for template: CVTemplate) -> NSColor { accentColor(for: template) } // MARK: - Document layout (RTL for Arabic / Hebrew) static var isRightToLeft: Bool { AppLayoutDirection.isRightToLeft } static var documentInterfaceLayoutDirection: NSUserInterfaceLayoutDirection { isRightToLeft ? .rightToLeft : .leftToRight } static var documentTextAlignment: NSTextAlignment { isRightToLeft ? .right : .left } static var documentWritingDirection: NSWritingDirection { isRightToLeft ? .rightToLeft : .leftToRight } static func resolvedDocumentAlignment(_ alignment: NSTextAlignment) -> NSTextAlignment { alignment == .left ? documentTextAlignment : alignment } static func applyDocumentLayout(to view: NSView) { view.userInterfaceLayoutDirection = documentInterfaceLayoutDirection for child in view.subviews { applyDocumentLayout(to: child) } } static func documentParagraphStyle( alignment: NSTextAlignment, lineBreakMode: NSLineBreakMode = .byWordWrapping ) -> NSParagraphStyle { let paragraph = NSMutableParagraphStyle() paragraph.alignment = resolvedDocumentAlignment(alignment) paragraph.baseWritingDirection = documentWritingDirection paragraph.lineBreakMode = lineBreakMode return paragraph } static func applyDocumentTextLayout(to field: NSTextField, alignment: NSTextAlignment? = nil) { let sourceAlignment = alignment ?? field.alignment let resolved = resolvedDocumentAlignment(sourceAlignment) field.alignment = resolved field.baseWritingDirection = documentWritingDirection let lineBreak: NSLineBreakMode = field.maximumNumberOfLines == 0 ? .byWordWrapping : .byTruncatingTail field.attributedStringValue = NSAttributedString(string: field.stringValue, attributes: [ .font: field.font ?? .systemFont(ofSize: 12), .foregroundColor: field.textColor ?? .labelColor, .paragraphStyle: documentParagraphStyle(alignment: resolved, lineBreakMode: lineBreak) ]) } }