import SwiftUI struct TjLockChip: View { var body: some View { HStack(spacing: 4) { Image(systemName: "lock.fill") .font(.system(size: 9, weight: .semibold)) Text("本地加密") .font(.system(size: 10)) .tracking(0.5) } .foregroundStyle(Tj.Palette.paper) .padding(.horizontal, 7) .padding(.vertical, 3) .background(Capsule().fill(Tj.Palette.ink)) } } enum TjBadgeStyle { case brick, amber, leaf, ink, neutral var bg: Color { switch self { case .brick: return Tj.Palette.brickSoft case .amber: return Color(red: 0.957, green: 0.890, blue: 0.749) case .leaf: return Tj.Palette.leafSoft case .ink: return Tj.Palette.ink case .neutral: return Tj.Palette.sand2 } } var fg: Color { switch self { case .brick: return Tj.Palette.brick case .amber: return Tj.Palette.amber case .leaf: return Tj.Palette.leaf case .ink: return Tj.Palette.paper case .neutral: return Tj.Palette.text2 } } } struct TjBadge: View { let text: String var style: TjBadgeStyle = .neutral var body: some View { Text(text) .font(.system(size: 10, weight: .semibold)) .tracking(0.3) .foregroundStyle(style.fg) .padding(.horizontal, 7) .padding(.vertical, 2) .background(Capsule().fill(style.bg)) .lineLimit(1) } } struct TjPlaceholder: View { let label: String var dark: Bool = false var radius: CGFloat = Tj.Radius.sm var body: some View { ZStack { RoundedRectangle(cornerRadius: radius, style: .continuous) .fill(dark ? Color(red: 0.110, green: 0.122, blue: 0.110) : Tj.Palette.sand2) DiagonalStripes(spacing: 7, color: dark ? Color.white.opacity(0.04) : Color.black.opacity(0.05)) .clipShape(RoundedRectangle(cornerRadius: radius, style: .continuous)) Text(label) .font(.system(size: 11, design: .monospaced)) .tracking(0.5) .foregroundStyle(dark ? Color.white.opacity(0.5) : Tj.Palette.text3) .multilineTextAlignment(.center) .padding(8) } } } private struct DiagonalStripes: View { let spacing: CGFloat let color: Color var body: some View { Canvas { ctx, size in let step = spacing let count = Int((size.width + size.height) / step) + 4 for i in -2.. some View { configuration.label .font(.system(size: fontSize, weight: .semibold)) .tracking(1) .foregroundStyle(Tj.Palette.paper) .padding(.horizontal, horizontalPadding) .frame(height: height) .background(Capsule().fill(Tj.Palette.ink)) .opacity(configuration.isPressed ? 0.85 : 1) } } struct TjGhostButton: ButtonStyle { var height: CGFloat = 48 var fontSize: CGFloat = 15 var horizontalPadding: CGFloat = 22 func makeBody(configuration: Configuration) -> some View { configuration.label .font(.system(size: fontSize, weight: .semibold)) .tracking(1) .foregroundStyle(Tj.Palette.ink) .padding(.horizontal, horizontalPadding) .frame(height: height) .background( Capsule().strokeBorder(Tj.Palette.ink, lineWidth: 1) ) .opacity(configuration.isPressed ? 0.7 : 1) } } struct TjDashedDivider: View { var body: some View { Rectangle() .fill(Tj.Palette.line) .frame(height: 1) .mask( HStack(spacing: 4) { ForEach(0..<200, id: \.self) { _ in Rectangle().frame(width: 4, height: 1) } } ) } }