Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  NodesMenu.swift   Sprache: unbekannt

 
Spracherkennung für: .swift vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

import AppKit
import SwiftUI

struct NodeMenuEntryFormatter {
    static func isGateway(_ entry: NodeInfo) -> Bool {
        entry.nodeId == "gateway"
    }

    static func isConnected(_ entry: NodeInfo) -> Bool {
        entry.isConnected
    }

    static func primaryName(_ entry: NodeInfo) -> String {
        if self.isGateway(entry) {
            return entry.displayName?.nonEmpty ?? "Gateway"
        }
        return entry.displayName?.nonEmpty ?? entry.nodeId
    }

    static func summaryText(_ entry: NodeInfo) -> String {
        if self.isGateway(entry) {
            let role = self.roleText(entry)
            let name = self.primaryName(entry)
            var parts = ["\(name) · \(role)"]
            if let ip = entry.remoteIp?.nonEmpty { parts.append("host \(ip)") }
            if let platform = self.platformText(entry) { parts.append(platform) }
            return parts.joined(separator: " · ")
        }
        let name = self.primaryName(entry)
        var prefix = "Node: \(name)"
        if let ip = entry.remoteIp?.nonEmpty {
            prefix += " (\(ip))"
        }
        var parts = [prefix]
        if let platform = self.platformText(entry) {
            parts.append("platform \(platform)")
        }
        let versionLabels = self.versionLabels(entry)
        if !versionLabels.isEmpty {
            parts.append(versionLabels.joined(separator: " · "))
        }
        parts.append("status \(self.roleText(entry))")
        return parts.joined(separator: " · ")
    }

    static func roleText(_ entry: NodeInfo) -> String {
        if entry.isConnected { return "connected" }
        if self.isGateway(entry) { return "disconnected" }
        if entry.isPaired { return "paired" }
        return "unpaired"
    }

    static func detailLeft(_ entry: NodeInfo) -> String {
        let role = self.roleText(entry)
        if let ip = entry.remoteIp?.nonEmpty { return "\(ip) · \(role)" }
        return role
    }

    static func headlineRight(_ entry: NodeInfo) -> String? {
        self.platformText(entry)
    }

    static func detailRightVersion(_ entry: NodeInfo) -> String? {
        let labels = self.versionLabels(entry, compact: false)
        if labels.isEmpty { return nil }
        return labels.joined(separator: " · ")
    }

    static func platformText(_ entry: NodeInfo) -> String? {
        if let raw = entry.platform?.nonEmpty {
            return PlatformLabelFormatter.pretty(raw) ?? raw
        }
        if let family = entry.deviceFamily?.lowercased() {
            if family.contains("mac") { return "macOS" }
            if family.contains("iphone") { return "iOS" }
            if family.contains("ipad") { return "iPadOS" }
            if family.contains("android") { return "Android" }
        }
        return nil
    }

    private static func compactVersion(_ raw: String) -> String {
        let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !trimmed.isEmpty else { return trimmed }
        if let range = trimmed.range(
            of: #"\s*\([^)]*\d[^)]*\)$"#,
            options: .regularExpression)
        {
            return String(trimmed[..<range.lowerBound])
        }
        return trimmed
    }

    private static func shortVersionLabel(_ raw: String) -> String {
        let compact = self.compactVersion(raw)
        if compact.isEmpty { return compact }
        if compact.lowercased().hasPrefix("v") { return compact }
        if let first = compact.unicodeScalars.first, CharacterSet.decimalDigits.contains(first) {
            return "v\(compact)"
        }
        return compact
    }

    private static func versionLabels(_ entry: NodeInfo, compact: Bool = true) -> [String] {
        let (core, ui) = self.resolveVersions(entry)
        var labels: [String] = []
        if let core {
            let label = compact ? self.compactVersion(core) : self.shortVersionLabel(core)
            labels.append("core \(label)")
        }
        if let ui {
            let label = compact ? self.compactVersion(ui) : self.shortVersionLabel(ui)
            labels.append("ui \(label)")
        }
        return labels
    }

    private static func resolveVersions(_ entry: NodeInfo) -> (core: String?, ui: String?) {
        let core = entry.coreVersion?.nonEmpty
        let ui = entry.uiVersion?.nonEmpty
        if core != nil || ui != nil {
            return (core, ui)
        }
        guard let legacy = entry.version?.nonEmpty else { return (nil, nil) }
        if self.isHeadlessPlatform(entry) {
            return (legacy, nil)
        }
        return (nil, legacy)
    }

    private static func isHeadlessPlatform(_ entry: NodeInfo) -> Bool {
        let raw = entry.platform?.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() ?? ""
        if raw == "darwin" || raw == "linux" || raw == "win32" || raw == "windows" { return true }
        return false
    }

    static func leadingSymbol(_ entry: NodeInfo) -> String {
        if self.isGateway(entry) {
            return self.safeSystemSymbol(
                "antenna.radiowaves.left.and.right",
                fallback: "dot.radiowaves.left.and.right")
        }
        if let family = entry.deviceFamily?.lowercased() {
            if family.contains("mac") {
                return self.safeSystemSymbol("laptopcomputer", fallback: "laptopcomputer")
            }
            if family.contains("iphone") { return self.safeSystemSymbol("iphone", fallback: "iphone") }
            if family.contains("ipad") { return self.safeSystemSymbol("ipad", fallback: "ipad") }
        }
        if let platform = entry.platform?.lowercased() {
            if platform.contains("mac") { return self.safeSystemSymbol("laptopcomputer", fallback: "laptopcomputer") }
            if platform.contains("ios") { return self.safeSystemSymbol("iphone", fallback: "iphone") }
            if platform.contains("android") { return self.safeSystemSymbol("cpu", fallback: "cpu") }
        }
        return "cpu"
    }

    static func isAndroid(_ entry: NodeInfo) -> Bool {
        let family = entry.deviceFamily?.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
        if family == "android" { return true }
        let platform = entry.platform?.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
        return platform?.contains("android") == true
    }

    private static func safeSystemSymbol(_ preferred: String, fallback: String) -> String {
        if NSImage(systemSymbolName: preferred, accessibilityDescription: nil) != nil { return preferred }
        return fallback
    }
}

struct NodeMenuRowView: View {
    let entry: NodeInfo
    let width: CGFloat
    @Environment(\.menuItemHighlighted) private var isHighlighted

    private var palette: MenuItemHighlightColors.Palette {
        MenuItemHighlightColors.palette(self.isHighlighted)
    }

    var body: some View {
        HStack(alignment: .center, spacing: 10) {
            self.leadingIcon
                .frame(width: 22, height: 22, alignment: .center)

            VStack(alignment: .leading, spacing: 2) {
                HStack(alignment: .firstTextBaseline, spacing: 8) {
                    Text(NodeMenuEntryFormatter.primaryName(self.entry))
                        .font(.callout.weight(NodeMenuEntryFormatter.isConnected(self.entry) ? .semibold : .regular))
                        .foregroundStyle(self.palette.primary)
                        .lineLimit(1)
                        .truncationMode(.middle)
                        .layoutPriority(1)

                    Spacer(minLength: 8)

                    HStack(alignment: .firstTextBaseline, spacing: 6) {
                        if let right = NodeMenuEntryFormatter.headlineRight(self.entry) {
                            Text(right)
                                .font(.caption.monospacedDigit())
                                .foregroundStyle(self.palette.secondary)
                                .lineLimit(1)
                                .truncationMode(.middle)
                                .layoutPriority(2)
                        }

                        Image(systemName: "chevron.right")
                            .font(.caption.weight(.semibold))
                            .foregroundStyle(self.palette.secondary)
                            .padding(.leading, 2)
                    }
                }

                HStack(alignment: .firstTextBaseline, spacing: 8) {
                    Text(NodeMenuEntryFormatter.detailLeft(self.entry))
                        .font(.caption)
                        .foregroundStyle(self.palette.secondary)
                        .lineLimit(1)
                        .truncationMode(.middle)

                    Spacer(minLength: 0)

                    if let version = NodeMenuEntryFormatter.detailRightVersion(self.entry) {
                        Text(version)
                            .font(.caption.monospacedDigit())
                            .foregroundStyle(self.palette.secondary)
                            .lineLimit(1)
                            .truncationMode(.middle)
                    }
                }
                .frame(maxWidth: .infinity, alignment: .leading)
            }
            .frame(maxWidth: .infinity, alignment: .leading)
        }
        .padding(.vertical, 8)
        .padding(.leading, 18)
        .padding(.trailing, 12)
        .frame(width: max(1, self.width), alignment: .leading)
    }

    @ViewBuilder
    private var leadingIcon: some View {
        if NodeMenuEntryFormatter.isAndroid(self.entry) {
            AndroidMark()
                .foregroundStyle(self.palette.secondary)
        } else {
            Image(systemName: NodeMenuEntryFormatter.leadingSymbol(self.entry))
                .font(.system(size: 18, weight: .regular))
                .foregroundStyle(self.palette.secondary)
        }
    }
}

struct AndroidMark: View {
    var body: some View {
        GeometryReader { geo in
            let w = geo.size.width
            let h = geo.size.height
            let headHeight = h * 0.68
            let headWidth = w * 0.92
            let headX = (w - headWidth) * 0.5
            let headY = (h - headHeight) * 0.5
            let corner = min(w, h) * 0.18
            RoundedRectangle(cornerRadius: corner, style: .continuous)
                .frame(width: headWidth, height: headHeight)
                .position(x: headX + headWidth * 0.5, y: headY + headHeight * 0.5)
        }
    }
}

struct NodeMenuMultilineView: View {
    let label: String
    let value: String
    let width: CGFloat
    @Environment(\.menuItemHighlighted) private var isHighlighted

    private var palette: MenuItemHighlightColors.Palette {
        MenuItemHighlightColors.palette(self.isHighlighted)
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 4) {
            Text("\(self.label):")
                .font(.caption.weight(.semibold))
                .foregroundStyle(self.palette.secondary)

            Text(self.value)
                .font(.caption)
                .foregroundStyle(self.palette.primary)
                .multilineTextAlignment(.leading)
                .fixedSize(horizontal: false, vertical: true)
        }
        .padding(.vertical, 6)
        .padding(.leading, 18)
        .padding(.trailing, 12)
        .frame(width: max(1, self.width), alignment: .leading)
    }
}

[Dauer der Verarbeitung: 0.25 Sekunden, vorverarbeitet 2026-04-27]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge