     1→import SwiftUI
     2→
     3→struct DeepAnalysisView: View {
     4→    let chat: ChatSession
     5→    @ObservedObject var database: WhatsAppDatabase
     6→    @StateObject private var deepService = DeepAnalysisService.shared
     7→    @StateObject private var cache = AnalysisCacheManager.shared
     8→    @StateObject private var audioService = AudioTranscriptionService.shared
     9→
    10→    @State private var deepAnalysis: DeepAnalysis?
    11→    @State private var showingReplySheet = false
    12→    @State private var selectedReply = ""
    13→    @State private var showingAudioTest = false
    14→    @State private var audioTestResult: String = ""
    15→
    16→    var cachedAnalysis: DeepAnalysis? {
    17→        cache.getCachedAnalysis(for: chat.id)
    18→    }
    19→
    20→    var body: some View {
    21→        ScrollView {
    22→            VStack(alignment: .leading, spacing: 20) {
    23→                // Header
    24→                DeepAnalysisHeader(chat: chat, cachedAnalysis: cachedAnalysis)
    25→
    26→                // Progress or Start button
    27→                if deepService.progress.isProcessing {
    28→                    ProcessingProgressView(progress: deepService.progress)
    29→                } else if let analysis = deepAnalysis ?? cachedAnalysis {
    30→                    // Results
    31→                    DeepAnalysisResultsView(
    32→                        analysis: analysis,
    33→                        onReply: { reply in
    34→                            selectedReply = reply
    35→                            showingReplySheet = true
    36→                        }
    37→                    )
    38→                } else {
    39→                    // Start analysis button
    40→                    StartAnalysisView(
    41→                        chat: chat,
    42→                        onStart: startDeepAnalysis
    43→                    )
    44→                }
    45→
    46→                // Action buttons (if has results)
    47→                if let analysis = deepAnalysis ?? cachedAnalysis, !deepService.progress.isProcessing {
    48→                    HStack(spacing: 12) {
    49→                        Button(action: startDeepAnalysis) {
    50→                            Label("Analisar Novamente", systemImage: "arrow.clockwise")
    51→                        }
    52→                        .buttonStyle(.bordered)
    53→
    54→                        Button(action: testAudioTranscription) {
    55→                            Label("Testar Áudio", systemImage: "waveform")
    56→                        }
    57→                        .buttonStyle(.bordered)
    58→                        .tint(.orange)
    59→
    60→                        Button(action: { exportToPDF(analysis) }) {
    61→                            Label("Exportar PDF", systemImage: "arrow.down.doc")
    62→                        }
    63→                        .buttonStyle(.borderedProminent)
    64→                        .tint(.purple)
    65→                    }
    66→                }
    67→            }
    68→            .padding()
    69→        }
    70→        .sheet(isPresented: $showingReplySheet) {
    71→            ReplySheetView(
    72→                chat: chat,
    73→                replyText: $selectedReply,
    74→                onSend: sendReply
    75→            )
    76→        }
    77→        .sheet(isPresented: $showingAudioTest) {
    78→            AudioTestSheet(result: audioTestResult)
    79→        }
    80→        .onAppear {
    81→            if let cached = cachedAnalysis {
    82→                deepAnalysis = cached
    83→            }
    84→        }
    85→    }
    86→
    87→    private func startDeepAnalysis() {
    88→        let allMessages = database.getMessages(for: chat.id, limit: 5000)
    89→
    90→        Task {
    91→            do {
    92→                let result = try await deepService.analyzeFullChat(
    93→                    chatId: chat.id,
    94→                    partnerName: chat.displayName,
    95→                    messages: allMessages
    96→                )
    97→                await MainActor.run {
    98→                    deepAnalysis = result
    99→                }
   100→            } catch {
   101→                await MainActor.run {
   102→                    deepService.progress.fail(with: error.localizedDescription)
   103→                }
   104→            }
   105→        }
   106→    }
   107→
   108→    private func sendReply(_ text: String) {
   109→        // Open WhatsApp with the message
   110→        // Format: whatsapp://send?phone=NUMBER&text=MESSAGE
   111→        let phone = chat.contactJID.replacingOccurrences(of: "@lid", with: "")
   112→        if let encoded = text.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
   113→           let url = URL(string: "whatsapp://send?phone=\(phone)&text=\(encoded)") {
   114→            NSWorkspace.shared.open(url)
   115→        }
   116→        showingReplySheet = false
   117→    }
   118→
   119→    private func exportToPDF(_ analysis: DeepAnalysis) {
   120→        PDFExportService.shared.exportAnalysisToPDF(analysis)
   121→    }
   122→
   123→    private func testAudioTranscription() {
   124→        Task {
   125→            await MainActor.run {
   126→                audioTestResult = "🔄 Buscando áudios..."
   127→                showingAudioTest = true
   128→            }
   129→
   130→            let audioFiles = audioService.findAudioFiles()
   131→
   132→            if audioFiles.isEmpty {
   133→                await MainActor.run {
   134→                    audioTestResult = "❌ Nenhum arquivo de áudio encontrado"
   135→                }
   136→                return
   137→            }
   138→
   139→            await MainActor.run {
   140→                audioTestResult = "🎙 Encontrados \(audioFiles.count) áudios\n\n🔄 Transcrevendo primeiro áudio..."
   141→            }
   142→
   143→            do {
   144→                let transcription = try await audioService.transcribeOpusFile(audioFiles[0])
   145→                await MainActor.run {
   146→                    audioTestResult = """
   147→                    ✅ TRANSCRIÇÃO SUCESSO!
   148→
   149→                    📁 Arquivo: \(audioFiles[0].lastPathComponent)
   150→
   151→                    📝 Texto:
   152→                    "\(transcription)"
   153→
   154→                    Total de áudios disponíveis: \(audioFiles.count)
   155→                    """
   156→                }
   157→            } catch {
   158→                await MainActor.run {
   159→                    audioTestResult = "❌ Erro: \(error.localizedDescription)"
   160→                }
   161→            }
   162→        }
   163→    }
   164→}
   165→
   166→// MARK: - Audio Test Sheet
   167→
   168→struct AudioTestSheet: View {
   169→    let result: String
   170→    @Environment(\.dismiss) private var dismiss
   171→
   172→    var body: some View {
   173→        VStack(spacing: 20) {
   174→            HStack {
   175→                Image(systemName: "waveform.circle.fill")
   176→                    .font(.title)
   177→                    .foregroundColor(.orange)
   178→                Text("Teste de Transcrição")
   179→                    .font(.headline)
   180→                Spacer()
   181→                Button(action: { dismiss() }) {
   182→                    Image(systemName: "xmark.circle.fill")
   183→                        .foregroundColor(.secondary)
   184→                }
   185→                .buttonStyle(.plain)
   186→            }
   187→
   188→            ScrollView {
   189→                Text(result)
   190→                    .font(.system(.body, design: .monospaced))
   191→                    .textSelection(.enabled)
   192→                    .frame(maxWidth: .infinity, alignment: .leading)
   193→            }
   194→            .frame(maxHeight: 300)
   195→            .padding()
   196→            .background(Color(nsColor: .controlBackgroundColor))
   197→            .cornerRadius(8)
   198→
   199→            Button("Fechar") {
   200→                dismiss()
   201→            }
   202→            .buttonStyle(.borderedProminent)
   203→        }
   204→        .padding()
   205→        .frame(width: 500, height: 400)
   206→    }
   207→}
   208→
   209→// MARK: - Subviews
   210→
   211→struct DeepAnalysisHeader: View {
   212→    let chat: ChatSession
   213→    let cachedAnalysis: DeepAnalysis?
   214→
   215→    var body: some View {
   216→        VStack(alignment: .leading, spacing: 8) {
   217→            HStack {
   218→                Image(systemName: "chart.bar.doc.horizontal")
   219→                    .font(.title2)
   220→                Text("Análise Profunda")
   221→                    .font(.title2.bold())
   222→                Spacer()
   223→
   224→                if let analysis = cachedAnalysis {
   225→                    VStack(alignment: .trailing) {
   226→                        Text("Cache: \(analysis.formattedDate)")
   227→                            .font(.caption2)
   228→                            .foregroundColor(.secondary)
   229→                        Text("\(analysis.messagesAnalyzed) msgs analisadas")
   230→                            .font(.caption2)
   231→                            .foregroundColor(.secondary)
   232→                    }
   233→                }
   234→            }
   235→
   236→            Text("Analisa todas as \(chat.messageCount) mensagens do chat com \(chat.displayName)")
   237→                .font(.caption)
   238→                .foregroundColor(.secondary)
   239→        }
   240→        .padding()
   241→        .background(Color.purple.opacity(0.1))
   242→        .cornerRadius(12)
   243→    }
   244→}
   245→
   246→struct ProcessingProgressView: View {
   247→    @ObservedObject var progress: DeepAnalysisProgress
   248→
   249→    var body: some View {
   250→        VStack(spacing: 16) {
   251→            ProgressView(value: progress.progress) {
   252→                Text(progress.currentPhase)
   253→                    .font(.caption)
   254→            }
   255→            .progressViewStyle(.linear)
   256→
   257→            HStack {
   258→                Text("Lote \(progress.completedChunks)/\(progress.totalChunks)")
   259→                    .font(.caption)
   260→                    .foregroundColor(.secondary)
   261→                Spacer()
   262→                Text("\(Int(progress.progress * 100))%")
   263→                    .font(.caption.bold())
   264→            }
   265→
   266→            if let error = progress.error {
   267→                Text(error)
   268→                    .font(.caption)
   269→                    .foregroundColor(.red)
   270→            }
   271→        }
   272→        .padding()
   273→        .background(Color(nsColor: .controlBackgroundColor))
   274→        .cornerRadius(12)
   275→    }
   276→}
   277→
   278→struct StartAnalysisView: View {
   279→    let chat: ChatSession
   280→    let onStart: () -> Void
   281→
   282→    var body: some View {
   283→        VStack(spacing: 16) {
   284→            Image(systemName: "sparkles.rectangle.stack")
   285→                .font(.system(size: 48))
   286→                .foregroundColor(.purple)
   287→
   288→            Text("Análise Completa do Chat")
   289→                .font(.headline)
   290→
   291→            Text("Processa todas as \(chat.messageCount) mensagens usando IA para gerar insights detalhados sobre sua conversa com \(chat.displayName).")
   292→                .font(.caption)
   293→                .foregroundColor(.secondary)
   294→                .multilineTextAlignment(.center)
   295→
   296→            VStack(alignment: .leading, spacing: 4) {
   297→                Label("Resumo geral do relacionamento", systemImage: "doc.text")
   298→                Label("Tópicos mais discutidos", systemImage: "tag")
   299→                Label("Padrões de comunicação", systemImage: "chart.line.uptrend.xyaxis")
   300→                Label("Momentos importantes", systemImage: "star")
   301→                Label("Sugestões de resposta", systemImage: "text.bubble")
   302→                Label("Transcrição de áudios", systemImage: "waveform")
   303→                    .foregroundColor(.orange)
   304→            }
   305→            .font(.caption)
   306→            .foregroundColor(.secondary)
   307→
   308→            Button(action: onStart) {
   309→                HStack {
   310→                    Image(systemName: "play.fill")
   311→                    Text("Iniciar Análise Profunda")
   312→                }
   313→                .frame(maxWidth: .infinity)
   314→                .padding(.vertical, 8)
   315→            }
   316→            .buttonStyle(.borderedProminent)
   317→            .tint(.purple)
   318→        }
   319→        .padding()
   320→        .background(Color(nsColor: .controlBackgroundColor))
   321→        .cornerRadius(12)
   322→    }
   323→}
   324→
   325→struct DeepAnalysisResultsView: View {
   326→    let analysis: DeepAnalysis
   327→    let onReply: (String) -> Void
   328→
   329→    var body: some View {
   330→        VStack(alignment: .leading, spacing: 20) {
   331→            // Overview
   332→            GroupBox {
   333→                VStack(alignment: .leading, spacing: 8) {
   334→                    Label("Resumo Geral", systemImage: "doc.text")
   335→                        .font(.headline)
   336→                    Text(analysis.overallSummary)
   337→                        .textSelection(.enabled)
   338→
   339→                    Divider()
   340→
   341→                    HStack {
   342→                        Label(analysis.relationshipType, systemImage: "person.2")
   343→                            .font(.caption)
   344→                            .padding(.horizontal, 8)
   345→                            .padding(.vertical, 4)
   346→                            .background(Color.blue.opacity(0.2))
   347→                            .cornerRadius(8)
   348→
   349→                        if analysis.hasAudioData {
   350→                            Label("\(analysis.audioCount ?? 0) áudios", systemImage: "waveform")
   351→                                .font(.caption)
   352→                                .padding(.horizontal, 8)
   353→                                .padding(.vertical, 4)
   354→                                .background(Color.orange.opacity(0.2))
   355→                                .cornerRadius(8)
   356→                        }
   357→
   358→                        Spacer()
   359→
   360→                        Text("via \(analysis.provider)")
   361→                            .font(.caption2)
   362→                            .foregroundColor(.secondary)
   363→                    }
   364→                }
   365→            }
   366→
   367→            // Audio Insights (if available)
   368→            if let audioInsights = analysis.audioInsights, !audioInsights.isEmpty {
   369→                GroupBox {
   370→                    VStack(alignment: .leading, spacing: 8) {
   371→                        Label("Insights dos Áudios", systemImage: "waveform.circle")
   372→                            .font(.headline)
   373→                            .foregroundColor(.orange)
   374→                        Text(audioInsights)
   375→                            .textSelection(.enabled)
   376→                    }
   377→                }
   378→            }
   379→
   380→            // Audio Transcriptions (if available)
   381→            if let transcriptions = analysis.audioTranscriptions, !transcriptions.isEmpty {
   382→                GroupBox {
   383→                    VStack(alignment: .leading, spacing: 8) {
   384→                        Label("Transcrições de Áudio (\(transcriptions.count))", systemImage: "text.bubble")
   385→                            .font(.headline)
   386→                            .foregroundColor(.orange)
   387→
   388→                        ForEach(transcriptions) { trans in
   389→                            VStack(alignment: .leading, spacing: 4) {
   390→                                HStack {
   391→                                    Image(systemName: trans.isFromMe ? "arrow.up.circle.fill" : "arrow.down.circle.fill")
   392→                                        .foregroundColor(trans.isFromMe ? .green : .blue)
   393→                                    Text(trans.isFromMe ? "Você" : "Contato")
   394→                                        .font(.caption.bold())
   395→                                    Spacer()
   396→                                    Text("\(trans.duration)s")
   397→                                        .font(.caption)
   398→                                        .foregroundColor(.secondary)
   399→                                }
   400→                                Text(trans.text)
   401→                                    .font(.caption)
   402→                                    .foregroundColor(.secondary)
   403→                                    .textSelection(.enabled)
   404→                            }
   405→                            .padding(8)
   406→                            .background(Color.orange.opacity(0.1))
   407→                            .cornerRadius(8)
   408→                        }
   409→                    }
   410→                }
   411→            }
   412→
   413→            // Topics
   414→            if !analysis.mainTopics.isEmpty {
   415→                GroupBox {
   416→                    VStack(alignment: .leading, spacing: 8) {
   417→                        Label("Tópicos Principais", systemImage: "tag")
   418→                            .font(.headline)
   419→
   420→                        ForEach(analysis.mainTopics) { topic in
   421→                            HStack {
   422→                                Text(topic.topic)
   423→                                    .fontWeight(.medium)
   424→                                Spacer()
   425→                                if topic.frequency > 0 {
   426→                                    Text("\(topic.frequency)x")
   427→                                        .font(.caption)
   428→                                        .foregroundColor(.secondary)
   429→                                }
   430→                            }
   431→                            if !topic.description.isEmpty {
   432→                                Text(topic.description)
   433→                                    .font(.caption)
   434→                                    .foregroundColor(.secondary)
   435→                            }
   436→                            Divider()
   437→                        }
   438→                    }
   439→                }
   440→            }
   441→
   442→            // Communication Patterns
   443→            GroupBox {
   444→                VStack(alignment: .leading, spacing: 8) {
   445→                    Label("Padrões de Comunicação", systemImage: "chart.line.uptrend.xyaxis")
   446→                        .font(.headline)
   447→
   448→                    LabeledContent("Estilo", value: analysis.communicationPatterns.conversationStyle)
   449→                    LabeledContent("Frequência", value: analysis.communicationPatterns.averageResponseTime)
   450→                    LabeledContent("Balanço", value: analysis.communicationPatterns.messageBalance)
   451→
   452→                    if !analysis.communicationPatterns.mostActiveHours.isEmpty {
   453→                        Text("Horários ativos: \(analysis.communicationPatterns.mostActiveHours.joined(separator: ", "))")
   454→                            .font(.caption)
   455→                            .foregroundColor(.secondary)
   456→                    }
   457→                }
   458→            }
   459→
   460→            // Key Moments
   461→            if !analysis.keyMoments.isEmpty {
   462→                GroupBox {
   463→                    VStack(alignment: .leading, spacing: 8) {
   464→                        Label("Momentos Importantes", systemImage: "star")
   465→                            .font(.headline)
   466→
   467→                        ForEach(analysis.keyMoments) { moment in
   468→                            VStack(alignment: .leading, spacing: 4) {
   469→                                HStack {
   470→                                    if !moment.date.isEmpty {
   471→                                        Text(moment.date)
   472→                                            .font(.caption)
   473→                                            .foregroundColor(.secondary)
   474→                                    }
   475→                                    Spacer()
   476→                                }
   477→                                Text(moment.description)
   478→                                    .font(.subheadline)
   479→                                if !moment.significance.isEmpty {
   480→                                    Text(moment.significance)
   481→                                        .font(.caption)
   482→                                        .foregroundColor(.secondary)
   483→                                        .italic()
   484→                                }
   485→                            }
   486→                            .padding(.vertical, 4)
   487→                            Divider()
   488→                        }
   489→                    }
   490→                }
   491→            }
   492→
   493→            // Suggested Responses
   494→            if !analysis.suggestedResponses.isEmpty {
   495→                GroupBox {
   496→                    VStack(alignment: .leading, spacing: 8) {
   497→                        Label("Sugestões de Resposta", systemImage: "text.bubble")
   498→                            .font(.headline)
   499→
   500→                        ForEach(analysis.suggestedResponses.indices, id: \.self) { index in
   501→                            Button(action: { onReply(analysis.suggestedResponses[index]) }) {
   502→                                HStack {
   503→                                    Text(analysis.suggestedResponses[index])
   504→                                        .multilineTextAlignment(.leading)
   505→                                    Spacer()
   506→                                    Image(systemName: "paperplane")
   507→                                }
   508→                                .padding(8)
   509→                                .background(Color.green.opacity(0.1))
   510→                                .cornerRadius(8)
   511→                            }
   512→                            .buttonStyle(.plain)
   513→                        }
   514→                    }
   515→                }
   516→            }
   517→
   518→            // Stats
   519→            HStack {
   520→                Spacer()
   521→                VStack(alignment: .trailing) {
   522→                    Text("\(analysis.messagesAnalyzed) de \(analysis.totalMessages) mensagens analisadas")
   523→                    Text("Processado em \(String(format: "%.1f", analysis.processingTimeSeconds))s")
   524→                }
   525→                .font(.caption2)
   526→                .foregroundColor(.secondary)
   527→            }
   528→        }
   529→    }
   530→}
   531→
   532→struct ReplySheetView: View {
   533→    let chat: ChatSession
   534→    @Binding var replyText: String
   535→    let onSend: (String) -> Void
   536→
   537→    @Environment(\.dismiss) private var dismiss
   538→
   539→    var body: some View {
   540→        VStack(spacing: 20) {
   541→            Text("Responder para \(chat.displayName)")
   542→                .font(.headline)
   543→
   544→            TextEditor(text: $replyText)
   545→                .frame(height: 100)
   546→                .padding(8)
   547→                .background(Color(nsColor: .controlBackgroundColor))
   548→                .cornerRadius(8)
   549→
   550→            HStack {
   551→                Button("Cancelar") {
   552→                    dismiss()
   553→                }
   554→                .keyboardShortcut(.escape)
   555→
   556→                Spacer()
   557→
   558→                Button("Abrir no WhatsApp") {
   559→                    onSend(replyText)
   560→                }
   561→                .buttonStyle(.borderedProminent)
   562→                .keyboardShortcut(.return)
   563→            }
   564→        }
   565→        .padding()
   566→        .frame(width: 400)
   567→    }
   568→}
   569→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

import SwiftUI

struct DeepAnalysisView: View {
    let chat: ChatSession
    @ObservedObject var database: WhatsAppDatabase
    @StateObject private var deepService = DeepAnalysisService.shared
    @StateObject private var cache = AnalysisCacheManager.shared
    @StateObject private var audioService = AudioTranscriptionService.shared

    @State private var deepAnalysis: DeepAnalysis?
    @State private var showingReplySheet = false
    @State private var selectedReply = ""
    @State private var showingAudioTest = false
    @State private var audioTestResult: String = ""

    var cachedAnalysis: DeepAnalysis? {
        cache.getCachedAnalysis(for: chat.id)
    }

    var body: some View {
        ScrollView {
            VStack(alignment: .leading, spacing: 20) {
                // Header
                DeepAnalysisHeader(chat: chat, cachedAnalysis: cachedAnalysis)

                // Progress or Start button
                if deepService.progress.isProcessing {
                    ProcessingProgressView(progress: deepService.progress)
                } else if let analysis = deepAnalysis ?? cachedAnalysis {
                    // Results
                    DeepAnalysisResultsView(
                        analysis: analysis,
                        onReply: { reply in
                            selectedReply = reply
                            showingReplySheet = true
                        }
                    )
                } else {
                    // Start analysis button
                    StartAnalysisView(
                        chat: chat,
                        onStart: startDeepAnalysis
                    )
                }

                // Action buttons (if has results)
                if let analysis = deepAnalysis ?? cachedAnalysis, !deepService.progress.isProcessing {
                    HStack(spacing: 12) {
                        Button(action: startDeepAnalysis) {
                            Label("Analisar Novamente", systemImage: "arrow.clockwise")
                        }
                        .buttonStyle(.bordered)

                        Button(action: testAudioTranscription) {
                            Label("Testar Áudio", systemImage: "waveform")
                        }
                        .buttonStyle(.bordered)
                        .tint(.orange)

                        Button(action: { exportToPDF(analysis) }) {
                            Label("Exportar PDF", systemImage: "arrow.down.doc")
                        }
                        .buttonStyle(.borderedProminent)
                        .tint(.purple)
                    }
                }
            }
            .padding()
        }
        .sheet(isPresented: $showingReplySheet) {
            ReplySheetView(
                chat: chat,
                replyText: $selectedReply,
                onSend: sendReply
            )
        }
        .sheet(isPresented: $showingAudioTest) {
            AudioTestSheet(result: audioTestResult)
        }
        .onAppear {
            if let cached = cachedAnalysis {
                deepAnalysis = cached
            }
        }
    }

    private func startDeepAnalysis() {
        let allMessages = database.getMessages(for: chat.id, limit: 5000)

        Task {
            do {
                let result = try await deepService.analyzeFullChat(
                    chatId: chat.id,
                    partnerName: chat.displayName,
                    messages: allMessages
                )
                await MainActor.run {
                    deepAnalysis = result
                }
            } catch {
                await MainActor.run {
                    deepService.progress.fail(with: error.localizedDescription)
                }
            }
        }
    }

    private func sendReply(_ text: String) {
        // Open WhatsApp with the message
        // Format: whatsapp://send?phone=NUMBER&text=MESSAGE
        let phone = chat.contactJID.replacingOccurrences(of: "@lid", with: "")
        if let encoded = text.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
           let url = URL(string: "whatsapp://send?phone=\(phone)&text=\(encoded)") {
            NSWorkspace.shared.open(url)
        }
        showingReplySheet = false
    }

    private func exportToPDF(_ analysis: DeepAnalysis) {
        PDFExportService.shared.exportAnalysisToPDF(analysis)
    }

    private func testAudioTranscription() {
        Task {
            await MainActor.run {
                audioTestResult = "🔄 Buscando áudios..."
                showingAudioTest = true
            }

            let audioFiles = audioService.findAudioFiles()

            if audioFiles.isEmpty {
                await MainActor.run {
                    audioTestResult = "❌ Nenhum arquivo de áudio encontrado"
                }
                return
            }

            await MainActor.run {
                audioTestResult = "🎙 Encontrados \(audioFiles.count) áudios\n\n🔄 Transcrevendo primeiro áudio..."
            }

            do {
                let transcription = try await audioService.transcribeOpusFile(audioFiles[0])
                await MainActor.run {
                    audioTestResult = """
                    ✅ TRANSCRIÇÃO SUCESSO!

                    📁 Arquivo: \(audioFiles[0].lastPathComponent)

                    📝 Texto:
                    "\(transcription)"

                    Total de áudios disponíveis: \(audioFiles.count)
                    """
                }
            } catch {
                await MainActor.run {
                    audioTestResult = "❌ Erro: \(error.localizedDescription)"
                }
            }
        }
    }
}

// MARK: - Audio Test Sheet

struct AudioTestSheet: View {
    let result: String
    @Environment(\.dismiss) private var dismiss

    var body: some View {
        VStack(spacing: 20) {
            HStack {
                Image(systemName: "waveform.circle.fill")
                    .font(.title)
                    .foregroundColor(.orange)
                Text("Teste de Transcrição")
                    .font(.headline)
                Spacer()
                Button(action: { dismiss() }) {
                    Image(systemName: "xmark.circle.fill")
                        .foregroundColor(.secondary)
                }
                .buttonStyle(.plain)
            }

            ScrollView {
                Text(result)
                    .font(.system(.body, design: .monospaced))
                    .textSelection(.enabled)
                    .frame(maxWidth: .infinity, alignment: .leading)
            }
            .frame(maxHeight: 300)
            .padding()
            .background(Color(nsColor: .controlBackgroundColor))
            .cornerRadius(8)

            Button("Fechar") {
                dismiss()
            }
            .buttonStyle(.borderedProminent)
        }
        .padding()
        .frame(width: 500, height: 400)
    }
}

// MARK: - Subviews

struct DeepAnalysisHeader: View {
    let chat: ChatSession
    let cachedAnalysis: DeepAnalysis?

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            HStack {
                Image(systemName: "chart.bar.doc.horizontal")
                    .font(.title2)
                Text("Análise Profunda")
                    .font(.title2.bold())
                Spacer()

                if let analysis = cachedAnalysis {
                    VStack(alignment: .trailing) {
                        Text("Cache: \(analysis.formattedDate)")
                            .font(.caption2)
                            .foregroundColor(.secondary)
                        Text("\(analysis.messagesAnalyzed) msgs analisadas")
                            .font(.caption2)
                            .foregroundColor(.secondary)
                    }
                }
            }

            Text("Analisa todas as \(chat.messageCount) mensagens do chat com \(chat.displayName)")
                .font(.caption)
                .foregroundColor(.secondary)
        }
        .padding()
        .background(Color.purple.opacity(0.1))
        .cornerRadius(12)
    }
}

struct ProcessingProgressView: View {
    @ObservedObject var progress: DeepAnalysisProgress

    var body: some View {
        VStack(spacing: 16) {
            ProgressView(value: progress.progress) {
                Text(progress.currentPhase)
                    .font(.caption)
            }
            .progressViewStyle(.linear)

            HStack {
                Text("Lote \(progress.completedChunks)/\(progress.totalChunks)")
                    .font(.caption)
                    .foregroundColor(.secondary)
                Spacer()
                Text("\(Int(progress.progress * 100))%")
                    .font(.caption.bold())
            }

            if let error = progress.error {
                Text(error)
                    .font(.caption)
                    .foregroundColor(.red)
            }
        }
        .padding()
        .background(Color(nsColor: .controlBackgroundColor))
        .cornerRadius(12)
    }
}

struct StartAnalysisView: View {
    let chat: ChatSession
    let onStart: () -> Void

    var body: some View {
        VStack(spacing: 16) {
            Image(systemName: "sparkles.rectangle.stack")
                .font(.system(size: 48))
                .foregroundColor(.purple)

            Text("Análise Completa do Chat")
                .font(.headline)

            Text("Processa todas as \(chat.messageCount) mensagens usando IA para gerar insights detalhados sobre sua conversa com \(chat.displayName).")
                .font(.caption)
                .foregroundColor(.secondary)
                .multilineTextAlignment(.center)

            VStack(alignment: .leading, spacing: 4) {
                Label("Resumo geral do relacionamento", systemImage: "doc.text")
                Label("Tópicos mais discutidos", systemImage: "tag")
                Label("Padrões de comunicação", systemImage: "chart.line.uptrend.xyaxis")
                Label("Momentos importantes", systemImage: "star")
                Label("Sugestões de resposta", systemImage: "text.bubble")
                Label("Transcrição de áudios", systemImage: "waveform")
                    .foregroundColor(.orange)
            }
            .font(.caption)
            .foregroundColor(.secondary)

            Button(action: onStart) {
                HStack {
                    Image(systemName: "play.fill")
                    Text("Iniciar Análise Profunda")
                }
                .frame(maxWidth: .infinity)
                .padding(.vertical, 8)
            }
            .buttonStyle(.borderedProminent)
            .tint(.purple)
        }
        .padding()
        .background(Color(nsColor: .controlBackgroundColor))
        .cornerRadius(12)
    }
}

struct DeepAnalysisResultsView: View {
    let analysis: DeepAnalysis
    let onReply: (String) -> Void

    var body: some View {
        VStack(alignment: .leading, spacing: 20) {
            // Overview
            GroupBox {
                VStack(alignment: .leading, spacing: 8) {
                    Label("Resumo Geral", systemImage: "doc.text")
                        .font(.headline)
                    Text(analysis.overallSummary)
                        .textSelection(.enabled)

                    Divider()

                    HStack {
                        Label(analysis.relationshipType, systemImage: "person.2")
                            .font(.caption)
                            .padding(.horizontal, 8)
                            .padding(.vertical, 4)
                            .background(Color.blue.opacity(0.2))
                            .cornerRadius(8)

                        if analysis.hasAudioData {
                            Label("\(analysis.audioCount ?? 0) áudios", systemImage: "waveform")
                                .font(.caption)
                                .padding(.horizontal, 8)
                                .padding(.vertical, 4)
                                .background(Color.orange.opacity(0.2))
                                .cornerRadius(8)
                        }

                        Spacer()

                        Text("via \(analysis.provider)")
                            .font(.caption2)
                            .foregroundColor(.secondary)
                    }
                }
            }

            // Audio Insights (if available)
            if let audioInsights = analysis.audioInsights, !audioInsights.isEmpty {
                GroupBox {
                    VStack(alignment: .leading, spacing: 8) {
                        Label("Insights dos Áudios", systemImage: "waveform.circle")
                            .font(.headline)
                            .foregroundColor(.orange)
                        Text(audioInsights)
                            .textSelection(.enabled)
                    }
                }
            }

            // Audio Transcriptions (if available)
            if let transcriptions = analysis.audioTranscriptions, !transcriptions.isEmpty {
                GroupBox {
                    VStack(alignment: .leading, spacing: 8) {
                        Label("Transcrições de Áudio (\(transcriptions.count))", systemImage: "text.bubble")
                            .font(.headline)
                            .foregroundColor(.orange)

                        ForEach(transcriptions) { trans in
                            VStack(alignment: .leading, spacing: 4) {
                                HStack {
                                    Image(systemName: trans.isFromMe ? "arrow.up.circle.fill" : "arrow.down.circle.fill")
                                        .foregroundColor(trans.isFromMe ? .green : .blue)
                                    Text(trans.isFromMe ? "Você" : "Contato")
                                        .font(.caption.bold())
                                    Spacer()
                                    Text("\(trans.duration)s")
                                        .font(.caption)
                                        .foregroundColor(.secondary)
                                }
                                Text(trans.text)
                                    .font(.caption)
                                    .foregroundColor(.secondary)
                                    .textSelection(.enabled)
                            }
                            .padding(8)
                            .background(Color.orange.opacity(0.1))
                            .cornerRadius(8)
                        }
                    }
                }
            }

            // Topics
            if !analysis.mainTopics.isEmpty {
                GroupBox {
                    VStack(alignment: .leading, spacing: 8) {
                        Label("Tópicos Principais", systemImage: "tag")
                            .font(.headline)

                        ForEach(analysis.mainTopics) { topic in
                            HStack {
                                Text(topic.topic)
                                    .fontWeight(.medium)
                                Spacer()
                                if topic.frequency > 0 {
                                    Text("\(topic.frequency)x")
                                        .font(.caption)
                                        .foregroundColor(.secondary)
                                }
                            }
                            if !topic.description.isEmpty {
                                Text(topic.description)
                                    .font(.caption)
                                    .foregroundColor(.secondary)
                            }
                            Divider()
                        }
                    }
                }
            }

            // Communication Patterns
            GroupBox {
                VStack(alignment: .leading, spacing: 8) {
                    Label("Padrões de Comunicação", systemImage: "chart.line.uptrend.xyaxis")
                        .font(.headline)

                    LabeledContent("Estilo", value: analysis.communicationPatterns.conversationStyle)
                    LabeledContent("Frequência", value: analysis.communicationPatterns.averageResponseTime)
                    LabeledContent("Balanço", value: analysis.communicationPatterns.messageBalance)

                    if !analysis.communicationPatterns.mostActiveHours.isEmpty {
                        Text("Horários ativos: \(analysis.communicationPatterns.mostActiveHours.joined(separator: ", "))")
                            .font(.caption)
                            .foregroundColor(.secondary)
                    }
                }
            }

            // Key Moments
            if !analysis.keyMoments.isEmpty {
                GroupBox {
                    VStack(alignment: .leading, spacing: 8) {
                        Label("Momentos Importantes", systemImage: "star")
                            .font(.headline)

                        ForEach(analysis.keyMoments) { moment in
                            VStack(alignment: .leading, spacing: 4) {
                                HStack {
                                    if !moment.date.isEmpty {
                                        Text(moment.date)
                                            .font(.caption)
                                            .foregroundColor(.secondary)
                                    }
                                    Spacer()
                                }
                                Text(moment.description)
                                    .font(.subheadline)
                                if !moment.significance.isEmpty {
                                    Text(moment.significance)
                                        .font(.caption)
                                        .foregroundColor(.secondary)
                                        .italic()
                                }
                            }
                            .padding(.vertical, 4)
                            Divider()
                        }
                    }
                }
            }

            // Suggested Responses
            if !analysis.suggestedResponses.isEmpty {
                GroupBox {
                    VStack(alignment: .leading, spacing: 8) {
                        Label("Sugestões de Resposta", systemImage: "text.bubble")
                            .font(.headline)

                        ForEach(analysis.suggestedResponses.indices, id: \.self) { index in
                            Button(action: { onReply(analysis.suggestedResponses[index]) }) {
                                HStack {
                                    Text(analysis.suggestedResponses[index])
                                        .multilineTextAlignment(.leading)
                                    Spacer()
                                    Image(systemName: "paperplane")
                                }
                                .padding(8)
                                .background(Color.green.opacity(0.1))
                                .cornerRadius(8)
                            }
                            .buttonStyle(.plain)
                        }
                    }
                }
            }

            // Stats
            HStack {
                Spacer()
                VStack(alignment: .trailing) {
                    Text("\(analysis.messagesAnalyzed) de \(analysis.totalMessages) mensagens analisadas")
                    Text("Processado em \(String(format: "%.1f", analysis.processingTimeSeconds))s")
                }
                .font(.caption2)
                .foregroundColor(.secondary)
            }
        }
    }
}

struct ReplySheetView: View {
    let chat: ChatSession
    @Binding var replyText: String
    let onSend: (String) -> Void

    @Environment(\.dismiss) private var dismiss

    var body: some View {
        VStack(spacing: 20) {
            Text("Responder para \(chat.displayName)")
                .font(.headline)

            TextEditor(text: $replyText)
                .frame(height: 100)
                .padding(8)
                .background(Color(nsColor: .controlBackgroundColor))
                .cornerRadius(8)

            HStack {
                Button("Cancelar") {
                    dismiss()
                }
                .keyboardShortcut(.escape)

                Spacer()

                Button("Abrir no WhatsApp") {
                    onSend(replyText)
                }
                .buttonStyle(.borderedProminent)
                .keyboardShortcut(.return)
            }
        }
        .padding()
        .frame(width: 400)
    }
}


Perfect! Now let me also check the model files to understand the data structures and get a complete picture:


Bash

find "/Users/neog/Apps/macOS/WhatsAppAnalyzer-macOS" -name "*.swift" -type f | wc -l

Count total Swift files


     1→import Foundation
     2→
     3→struct ChatSession: Identifiable, Hashable {
     4→    let id: Int64
     5→    let contactJID: String
     6→    let partnerName: String
     7→    let messageCount: Int
     8→    let lastMessageDate: Date?
     9→
    10→    var displayName: String {
    11→        partnerName.isEmpty ? contactJID : partnerName
    12→    }
    13→
    14→    var formattedLastMessage: String {
    15→        guard let date = lastMessageDate else { return "" }
    16→        let formatter = RelativeDateTimeFormatter()
    17→        formatter.locale = Locale(identifier: "pt-BR")
    18→        return formatter.localizedString(for: date, relativeTo: Date())
    19→    }
    20→}
    21→
    22→struct Message: Identifiable, Hashable {
    23→    let id: Int64
    24→    let text: String
    25→    let isFromMe: Bool
    26→    let date: Date
    27→    let senderName: String?
    28→    let messageType: Int?       // 0 = text, 2 = audio, etc.
    29→    let mediaName: String?      // Media file name (for audio messages)
    30→    let mediaDuration: Int?     // Duration in seconds (for audio)
    31→
    32→    var formattedDate: String {
    33→        let formatter = DateFormatter()
    34→        formatter.dateFormat = "dd/MM HH:mm"
    35→        return formatter.string(from: date)
    36→    }
    37→
    38→    var formattedTime: String {
    39→        let formatter = DateFormatter()
    40→        formatter.dateFormat = "HH:mm"
    41→        return formatter.string(from: date)
    42→    }
    43→
    44→    var isAudio: Bool {
    45→        messageType == 2
    46→    }
    47→}
    48→
    49→struct ChatAnalysis: Identifiable {
    50→    let id = UUID()
    51→    let chatId: Int64
    52→    let partnerName: String
    53→    let summary: String
    54→    let topics: [String]
    55→    let sentiment: String
    56→    let suggestedResponses: [String]
    57→    let generatedAt: Date
    58→}
    59→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

import Foundation

struct ChatSession: Identifiable, Hashable {
    let id: Int64
    let contactJID: String
    let partnerName: String
    let messageCount: Int
    let lastMessageDate: Date?

    var displayName: String {
        partnerName.isEmpty ? contactJID : partnerName
    }

    var formattedLastMessage: String {
        guard let date = lastMessageDate else { return "" }
        let formatter = RelativeDateTimeFormatter()
        formatter.locale = Locale(identifier: "pt-BR")
        return formatter.localizedString(for: date, relativeTo: Date())
    }
}

struct Message: Identifiable, Hashable {
    let id: Int64
    let text: String
    let isFromMe: Bool
    let date: Date
    let senderName: String?
    let messageType: Int?       // 0 = text, 2 = audio, etc.
    let mediaName: String?      // Media file name (for audio messages)
    let mediaDuration: Int?     // Duration in seconds (for audio)

    var formattedDate: String {
        let formatter = DateFormatter()
        formatter.dateFormat = "dd/MM HH:mm"
        return formatter.string(from: date)
    }

    var formattedTime: String {
        let formatter = DateFormatter()
        formatter.dateFormat = "HH:mm"
        return formatter.string(from: date)
    }

    var isAudio: Bool {
        messageType == 2
    }
}

struct ChatAnalysis: Identifiable {
    let id = UUID()
    let chatId: Int64
    let partnerName: String
    let summary: String
    let topics: [String]
    let sentiment: String
    let suggestedResponses: [String]
    let generatedAt: Date
}


     1→import Foundation
     2→
     3→// MARK: - Deep Analysis Model
     4→
     5→struct DeepAnalysis: Codable, Identifiable {
     6→    let id: UUID
     7→    let chatId: Int64
     8→    let partnerName: String
     9→    let totalMessages: Int
    10→    let messagesAnalyzed: Int
    11→    let generatedAt: Date
    12→
    13→    // Analysis results
    14→    let overallSummary: String
    15→    let relationshipType: String
    16→    let mainTopics: [TopicInsight]
    17→    let sentimentOverTime: [SentimentPeriod]
    18→    let communicationPatterns: CommunicationPatterns
    19→    let keyMoments: [KeyMoment]
    20→    let suggestedResponses: [String]
    21→
    22→    // Audio transcription
    23→    var audioTranscriptions: [AudioTranscription]?
    24→    var audioInsights: String?
    25→    var audioCount: Int?
    26→
    27→    // Metadata
    28→    let provider: String
    29→    let processingTimeSeconds: Double
    30→
    31→    var formattedDate: String {
    32→        let formatter = DateFormatter()
    33→        formatter.dateFormat = "dd/MM/yyyy HH:mm"
    34→        return formatter.string(from: generatedAt)
    35→    }
    36→
    37→    var hasAudioData: Bool {
    38→        (audioCount ?? 0) > 0 || !(audioTranscriptions?.isEmpty ?? true)
    39→    }
    40→}
    41→
    42→struct AudioTranscription: Codable, Identifiable {
    43→    let id = UUID()
    44→    let fileName: String
    45→    let duration: Int
    46→    let isFromMe: Bool
    47→    let date: String
    48→    let text: String
    49→
    50→    enum CodingKeys: String, CodingKey {
    51→        case fileName, duration, isFromMe, date, text
    52→    }
    53→}
    54→
    55→struct TopicInsight: Codable, Identifiable {
    56→    let id = UUID()
    57→    let topic: String
    58→    let frequency: Int
    59→    let description: String
    60→
    61→    enum CodingKeys: String, CodingKey {
    62→        case topic, frequency, description
    63→    }
    64→}
    65→
    66→struct SentimentPeriod: Codable, Identifiable {
    67→    let id = UUID()
    68→    let period: String
    69→    let sentiment: String
    70→    let score: Double // -1 to 1
    71→
    72→    enum CodingKeys: String, CodingKey {
    73→        case period, sentiment, score
    74→    }
    75→}
    76→
    77→struct CommunicationPatterns: Codable {
    78→    let averageResponseTime: String
    79→    let mostActiveHours: [String]
    80→    let messageBalance: String // e.g., "60% você, 40% Lucas"
    81→    let conversationStyle: String
    82→}
    83→
    84→struct KeyMoment: Codable, Identifiable {
    85→    let id = UUID()
    86→    let date: String
    87→    let description: String
    88→    let significance: String
    89→
    90→    enum CodingKeys: String, CodingKey {
    91→        case date, description, significance
    92→    }
    93→}
    94→
    95→// MARK: - Cache Manager
    96→
    97→class AnalysisCacheManager: ObservableObject {
    98→    static let shared = AnalysisCacheManager()
    99→
   100→    private let cacheDirectory: URL
   101→    private let cacheFileName = "deep_analysis_cache.json"
   102→
   103→    @Published var cachedAnalyses: [Int64: DeepAnalysis] = [:]
   104→
   105→    init() {
   106→        let paths = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
   107→        cacheDirectory = paths[0].appendingPathComponent("WhatsAppAnalyzer", isDirectory: true)
   108→
   109→        // Create directory if needed
   110→        try? FileManager.default.createDirectory(at: cacheDirectory, withIntermediateDirectories: true)
   111→
   112→        loadCache()
   113→    }
   114→
   115→    private var cacheFileURL: URL {
   116→        cacheDirectory.appendingPathComponent(cacheFileName)
   117→    }
   118→
   119→    func loadCache() {
   120→        guard FileManager.default.fileExists(atPath: cacheFileURL.path) else { return }
   121→
   122→        do {
   123→            let data = try Data(contentsOf: cacheFileURL)
   124→            let analyses = try JSONDecoder().decode([DeepAnalysis].self, from: data)
   125→            cachedAnalyses = Dictionary(uniqueKeysWithValues: analyses.map { ($0.chatId, $0) })
   126→            print("✅ Loaded \(cachedAnalyses.count) cached analyses")
   127→        } catch {
   128→            print("❌ Failed to load cache: \(error)")
   129→        }
   130→    }
   131→
   132→    func saveCache() {
   133→        do {
   134→            let analyses = Array(cachedAnalyses.values)
   135→            let data = try JSONEncoder().encode(analyses)
   136→            try data.write(to: cacheFileURL)
   137→            print("✅ Saved \(analyses.count) analyses to cache")
   138→        } catch {
   139→            print("❌ Failed to save cache: \(error)")
   140→        }
   141→    }
   142→
   143→    func getCachedAnalysis(for chatId: Int64) -> DeepAnalysis? {
   144→        return cachedAnalyses[chatId]
   145→    }
   146→
   147→    func cacheAnalysis(_ analysis: DeepAnalysis) {
   148→        cachedAnalyses[analysis.chatId] = analysis
   149→        saveCache()
   150→    }
   151→
   152→    func invalidateCache(for chatId: Int64) {
   153→        cachedAnalyses.removeValue(forKey: chatId)
   154→        saveCache()
   155→    }
   156→
   157→    func clearAllCache() {
   158→        cachedAnalyses.removeAll()
   159→        try? FileManager.default.removeItem(at: cacheFileURL)
   160→    }
   161→}
   162→
   163→// MARK: - Processing Progress
   164→
   165→class DeepAnalysisProgress: ObservableObject {
   166→    @Published var isProcessing = false
   167→    @Published var currentPhase: String = ""
   168→    @Published var progress: Double = 0
   169→    @Published var totalChunks: Int = 0
   170→    @Published var completedChunks: Int = 0
   171→    @Published var error: String?
   172→
   173→    func reset() {
   174→        isProcessing = false
   175→        currentPhase = ""
   176→        progress = 0
   177→        totalChunks = 0
   178→        completedChunks = 0
   179→        error = nil
   180→    }
   181→
   182→    func startProcessing(totalChunks: Int) {
   183→        self.isProcessing = true
   184→        self.totalChunks = totalChunks
   185→        self.completedChunks = 0
   186→        self.progress = 0
   187→        self.error = nil
   188→    }
   189→
   190→    func updateProgress(phase: String, completed: Int) {
   191→        self.currentPhase = phase
   192→        self.completedChunks = completed
   193→        self.progress = totalChunks > 0 ? Double(completed) / Double(totalChunks) : 0
   194→    }
   195→
   196→    func complete() {
   197→        self.isProcessing = false
   198→        self.progress = 1.0
   199→        self.currentPhase = "Concluído"
   200→    }
   201→
   202→    func fail(with error: String) {
   203→        self.isProcessing = false
   204→        self.error = error
   205→    }
   206→}
   207→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

import Foundation

// MARK: - Deep Analysis Model

struct DeepAnalysis: Codable, Identifiable {
    let id: UUID
    let chatId: Int64
    let partnerName: String
    let totalMessages: Int
    let messagesAnalyzed: Int
    let generatedAt: Date

    // Analysis results
    let overallSummary: String
    let relationshipType: String
    let mainTopics: [TopicInsight]
    let sentimentOverTime: [SentimentPeriod]
    let communicationPatterns: CommunicationPatterns
    let keyMoments: [KeyMoment]
    let suggestedResponses: [String]

    // Audio transcription
    var audioTranscriptions: [AudioTranscription]?
    var audioInsights: String?
    var audioCount: Int?

    // Metadata
    let provider: String
    let processingTimeSeconds: Double

    var formattedDate: String {
        let formatter = DateFormatter()
        formatter.dateFormat = "dd/MM/yyyy HH:mm"
        return formatter.string(from: generatedAt)
    }

    var hasAudioData: Bool {
        (audioCount ?? 0) > 0 || !(audioTranscriptions?.isEmpty ?? true)
    }
}

struct AudioTranscription: Codable, Identifiable {
    let id = UUID()
    let fileName: String
    let duration: Int
    let isFromMe: Bool
    let date: String
    let text: String

    enum CodingKeys: String, CodingKey {
        case fileName, duration, isFromMe, date, text
    }
}

struct TopicInsight: Codable, Identifiable {
    let id = UUID()
    let topic: String
    let frequency: Int
    let description: String

    enum CodingKeys: String, CodingKey {
        case topic, frequency, description
    }
}

struct SentimentPeriod: Codable, Identifiable {
    let id = UUID()
    let period: String
    let sentiment: String
    let score: Double // -1 to 1

    enum CodingKeys: String, CodingKey {
        case period, sentiment, score
    }
}

struct CommunicationPatterns: Codable {
    let averageResponseTime: String
    let mostActiveHours: [String]
    let messageBalance: String // e.g., "60% você, 40% Lucas"
    let conversationStyle: String
}

struct KeyMoment: Codable, Identifiable {
    let id = UUID()
    let date: String
    let description: String
    let significance: String

    enum CodingKeys: String, CodingKey {
        case date, description, significance
    }
}

// MARK: - Cache Manager

class AnalysisCacheManager: ObservableObject {
    static let shared = AnalysisCacheManager()

    private let cacheDirectory: URL
    private let cacheFileName = "deep_analysis_cache.json"

    @Published var cachedAnalyses: [Int64: DeepAnalysis] = [:]

    init() {
        let paths = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
        cacheDirectory = paths[0].appendingPathComponent("WhatsAppAnalyzer", isDirectory: true)

        // Create directory if needed
        try? FileManager.default.createDirectory(at: cacheDirectory, withIntermediateDirectories: true)

        loadCache()
    }

    private var cacheFileURL: URL {
        cacheDirectory.appendingPathComponent(cacheFileName)
    }

    func loadCache() {
        guard FileManager.default.fileExists(atPath: cacheFileURL.path) else { return }

        do {
            let data = try Data(contentsOf: cacheFileURL)
            let analyses = try JSONDecoder().decode([DeepAnalysis].self, from: data)
            cachedAnalyses = Dictionary(uniqueKeysWithValues: analyses.map { ($0.chatId, $0) })
            print("✅ Loaded \(cachedAnalyses.count) cached analyses")
        } catch {
            print("❌ Failed to load cache: \(error)")
        }
    }

    func saveCache() {
        do {
            let analyses = Array(cachedAnalyses.values)
            let data = try JSONEncoder().encode(analyses)
            try data.write(to: cacheFileURL)
            print("✅ Saved \(analyses.count) analyses to cache")
        } catch {
            print("❌ Failed to save cache: \(error)")
        }
    }

    func getCachedAnalysis(for chatId: Int64) -> DeepAnalysis? {
        return cachedAnalyses[chatId]
    }

    func cacheAnalysis(_ analysis: DeepAnalysis) {
        cachedAnalyses[analysis.chatId] = analysis
        saveCache()
    }

    func invalidateCache(for chatId: Int64) {
        cachedAnalyses.removeValue(forKey: chatId)
        saveCache()
    }

    func clearAllCache() {
        cachedAnalyses.removeAll()
        try? FileManager.default.removeItem(at: cacheFileURL)
    }
}

// MARK: - Processing Progress

class DeepAnalysisProgress: ObservableObject {
    @Published var isProcessing = false
    @Published var currentPhase: String = ""
    @Published var progress: Double = 0
    @Published var totalChunks: Int = 0
    @Published var completedChunks: Int = 0
    @Published var error: String?

    func reset() {
        isProcessing = false
        currentPhase = ""
        progress = 0
        totalChunks = 0
        completedChunks = 0
        error = nil
    }

    func startProcessing(totalChunks: Int) {
        self.isProcessing = true
        self.totalChunks = totalChunks
        self.completedChunks = 0
        self.progress = 0
        self.error = nil
    }

    func updateProgress(phase: String, completed: Int) {
        self.currentPhase = phase
        self.completedChunks = completed
        self.progress = totalChunks > 0 ? Double(completed) / Double(totalChunks) : 0
    }

    func complete() {
        self.isProcessing = false
        self.progress = 1.0
        self.currentPhase = "Concluído"
    }

    func fail(with error: String) {
        self.isProcessing = false
        self.error = error
    }
}


Now let me generate a comprehensive visual map by checking the project configuration:


ls -la "/Users/neog/Apps/macOS/WhatsAppAnalyzer-macOS/Sources/Services/" | grep -E "\.swift$"

List service files


     1→name: WhatsAppAnalyzer
     2→options:
     3→  bundleIdPrefix: com.neog
     4→  deploymentTarget:
     5→    macOS: "14.0"
     6→  developmentLanguage: pt-BR
     7→
     8→settings:
     9→  base:
    10→    MARKETING_VERSION: "1.0.0"
    11→    CURRENT_PROJECT_VERSION: "1"
    12→    DEVELOPMENT_TEAM: ""
    13→    CODE_SIGN_STYLE: Automatic
    14→    SWIFT_VERSION: "5.9"
    15→
    16→targets:
    17→  WhatsAppAnalyzer:
    18→    type: application
    19→    platform: macOS
    20→    sources:
    21→      - Sources
    22→    dependencies:
    23→      - framework: Speech.framework
    24→        embed: false
    25→      - sdk: libsqlite3.tbd
    26→    settings:
    27→      base:
    28→        PRODUCT_BUNDLE_IDENTIFIER: com.neog.whatsapp-analyzer
    29→        INFOPLIST_FILE: Sources/Info.plist
    30→        PRODUCT_NAME: WhatsApp Analyzer
    31→        MACOSX_DEPLOYMENT_TARGET: "14.0"
    32→        CODE_SIGN_ENTITLEMENTS: Sources/WhatsAppAnalyzer.entitlements
    33→    info:
    34→      path: Sources/Info.plist
    35→      properties:
    36→        CFBundleName: WhatsApp Analyzer
    37→        CFBundleDisplayName: WhatsApp Analyzer
    38→        CFBundleIdentifier: $(PRODUCT_BUNDLE_IDENTIFIER)
    39→        CFBundleVersion: $(CURRENT_PROJECT_VERSION)
    40→        CFBundleShortVersionString: $(MARKETING_VERSION)
    41→        CFBundlePackageType: APPL
    42→        CFBundleExecutable: $(EXECUTABLE_NAME)
    43→        LSMinimumSystemVersion: $(MACOSX_DEPLOYMENT_TARGET)
    44→        NSHumanReadableCopyright: "© 2025 NEOG"
    45→        LSApplicationCategoryType: public.app-category.productivity
    46→        NSSpeechRecognitionUsageDescription: "WhatsApp Analyzer precisa de acesso ao reconhecimento de fala para transcrever mensagens de áudio."
    47→        NSMicrophoneUsageDescription: "WhatsApp Analyzer precisa de acesso ao microfone para reconhecimento de fala."
    48→    entitlements:
    49→      path: Sources/WhatsAppAnalyzer.entitlements
    50→      properties:
    51→        com.apple.security.app-sandbox: false
    52→        com.apple.security.files.user-selected.read-write: true
    53→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

name: WhatsAppAnalyzer
options:
  bundleIdPrefix: com.neog
  deploymentTarget:
    macOS: "14.0"
  developmentLanguage: pt-BR

settings:
  base:
    MARKETING_VERSION: "1.0.0"
    CURRENT_PROJECT_VERSION: "1"
    DEVELOPMENT_TEAM: ""
    CODE_SIGN_STYLE: Automatic
    SWIFT_VERSION: "5.9"

targets:
  WhatsAppAnalyzer:
    type: application
    platform: macOS
    sources:
      - Sources
    dependencies:
      - framework: Speech.framework
        embed: false
      - sdk: libsqlite3.tbd
    settings:
      base:
        PRODUCT_BUNDLE_IDENTIFIER: com.neog.whatsapp-analyzer
        INFOPLIST_FILE: Sources/Info.plist
        PRODUCT_NAME: WhatsApp Analyzer
        MACOSX_DEPLOYMENT_TARGET: "14.0"
        CODE_SIGN_ENTITLEMENTS: Sources/WhatsAppAnalyzer.entitlements
    info:
      path: Sources/Info.plist
      properties:
        CFBundleName: WhatsApp Analyzer
        CFBundleDisplayName: WhatsApp Analyzer
        CFBundleIdentifier: $(PRODUCT_BUNDLE_IDENTIFIER)
        CFBundleVersion: $(CURRENT_PROJECT_VERSION)
        CFBundleShortVersionString: $(MARKETING_VERSION)
        CFBundlePackageType: APPL
        CFBundleExecutable: $(EXECUTABLE_NAME)
        LSMinimumSystemVersion: $(MACOSX_DEPLOYMENT_TARGET)
        NSHumanReadableCopyright: "© 2025 NEOG"
        LSApplicationCategoryType: public.app-category.productivity
        NSSpeechRecognitionUsageDescription: "WhatsApp Analyzer precisa de acesso ao reconhecimento de fala para transcrever mensagens de áudio."
        NSMicrophoneUsageDescription: "WhatsApp Analyzer precisa de acesso ao microfone para reconhecimento de fala."
    entitlements:
      path: Sources/WhatsAppAnalyzer.entitlements
      properties:
        com.apple.security.app-sandbox: false
        com.apple.security.files.user-selected.read-write: true


-rw-------@ 1 neog  staff  12478 Dec 22 19:13 AIService.swift
-rw-------@ 1 neog  staff   5878 Dec 22 21:42 AudioTranscriptionService.swift
-rw-------@ 1 neog  staff  22800 Dec 22 21:54 DeepAnalysisService.swift
-rw-------@ 1 neog  staff   7239 Dec 22 18:36 LMStudioService.swift
-rw-------@ 1 neog  staff  11395 Dec 22 20:29 PDFExportService.swift
-rw-------@ 1 neog  staff   6305 Dec 22 21:52 WhatsAppDatabase.swift


Perfect! I now have a comprehensive understanding of the WhatsAppAnalyzer macOS app's UI/UX structure. Let me provide you with a thorough exploration report.

---

## WhatsAppAnalyzer macOS - Complete UI/UX Architecture Report

### PROJECT OVERVIEW
- **Language:** SwiftUI
- **Platform:** macOS 14.0+
- **Swift Version:** 5.9
- **Total Swift Files:** 14 files
- **View Files:** 4 main views + multiple reusable components
- **Default Window Size:** 1200x700

---

### 1. VIEW HIERARCHY & STRUCTURE

```
WhatsAppAnalyzerApp
└── ContentView (Main Container)
    ├── NavigationSplitView (3-column layout)
    │   ├── Sidebar (280px min width)
    │   │   └── ChatListView
    │   │       └── ChatRowView (custom component)
    │   │
    │   └── Detail Panel
    │       ├── If chat selected:
    │       │   ├── HSplitView (horizontal split)
    │       │   │   ├── ChatDetailView (400px min width)
    │       │   │   │   ├── ChatHeaderView
    │       │   │   │   └── MessageBubbleView (lazy stack)
    │       │   │   │
    │       │   │   └── AIAnalysisView (380-500px conditional)
    │       │   │       ├── QuickAnalysisContent
    │       │   │       │   ├── AIConnectionStatusView
    │       │   │       │   ├── QuickAnalysisSectionView
    │       │   │       │   ├── QuickAnalysisResultsView
    │       │   │       │   └── CustomPromptWithReplyView
    │       │   │       │
    │       │   │       └── DeepAnalysisView (tab 1)
    │       │   │           ├── DeepAnalysisHeader
    │       │   │           ├── ProcessingProgressView
    │       │   │           ├── StartAnalysisView
    │       │   │           └── DeepAnalysisResultsView
    │       │   │
    │       │   └── Toolbar (AI Status + Toggle buttons)
    │       │
    │       └── Empty State View
```

---

### 2. ALL VIEW FILES LOCATION & STRUCTURE

| File | Lines | Purpose | Components |
|------|-------|---------|-----------|
| **ContentView.swift** | 87 | Main container & layout | NavigationSplitView, HSplitView |
| **ChatListView.swift** | 114 | Sidebar chat list | ChatListView, ChatRowView |
| **ChatDetailView.swift** | 136 | Message display | ChatDetailView, ChatHeaderView, MessageBubbleView |
| **AIAnalysisView.swift** | 549 | Analysis panels (2 tabs) | QuickAnalysisContent, DeepAnalysisView, AIConnectionStatusView, QuickAnalysisResultsView, CustomPromptWithReplyView, QuickReplySheet, FlowLayout |
| **DeepAnalysisView.swift** | 569 | Deep analysis tab | DeepAnalysisHeader, ProcessingProgressView, StartAnalysisView, DeepAnalysisResultsView, ReplySheetView, AudioTestSheet |

**Location:** `/Users/neog/Apps/macOS/WhatsAppAnalyzer-macOS/Sources/Views/`

---

### 3. COLOR SCHEME & STYLING PATTERNS

#### Primary Colors
```swift
// System colors (adaptive to light/dark mode)
Color(nsColor: .controlBackgroundColor)  // Default backgrounds
Color.accentColor                         // Primary interactive elements
Color.secondary                           // Secondary text
Color.green                               // User messages, "from me"
Color.blue                                // Analysis results, Claude provider
Color.purple                              // Deep analysis, relationship insights
Color.orange                              // LM Gateway provider, Audio features
Color.red                                 // Error states, offline status
```

#### Component-Specific Colors
- **Message Bubbles:** Green for user (`.green`), System background for partner
- **AI Status Indicators:** Green (online) / Red (offline) with 8x8 circles
- **Tags & Badges:** Color-specific opacity variants (`.opacity(0.1)` to `.opacity(0.2)`)
- **Sentiment Tags:**
  - Positive: `.green`
  - Negative: `.red`
  - Neutral: `.gray`

#### Avatar System
```swift
// Hash-based color assignment for user avatars
private let avatarColors = [
    Color.blue, Color.green, Color.orange,
    Color.purple, Color.pink, Color.teal
]
```

---

### 4. NAVIGATION PATTERNS

#### Primary Navigation
- **NavigationSplitView:** 3-column (sidebar + detail area)
- **HSplitView:** Horizontal split for dual-panel analysis
- **Picker (Segmented):** "Rápida" (Quick) vs "Profunda" (Deep) analysis tabs
- **Sheet Modals:** Reply composition and audio test results

#### Keyboard Shortcuts
```swift
Cmd+R     → Refresh chats
Cmd+Shift+L → Reconnect AI
Escape    → Close sheets
Return    → Send reply/confirm actions
```

---

### 5. REUSABLE COMPONENTS & PATTERNS

### A. **Connection Status Components**
```swift
// Found in multiple views
Circle()
    .fill(isConnected ? Color.green : Color.red)
    .frame(width: 8, height: 8)  // Standard status dot

// Status label pattern
HStack(spacing: 4) {
    Circle().fill(color)
    Text(status)
        .font(.caption)
}
```

### B. **Card/GroupBox Pattern**
```swift
GroupBox {
    VStack(alignment: .leading, spacing: 8) {
        Label("Title", systemImage: "systemImage")
            .font(.headline)
        // Content
    }
}
```

### C. **Header Patterns**
```swift
// Consistent header styling
HStack {
    VStack(alignment: .leading, spacing: 2) {
        Text(title).font(.headline)
        Text(subtitle).font(.caption).foregroundColor(.secondary)
    }
    Spacer()
    // Info or controls
}
.padding()
.background(Color(nsColor: .controlBackgroundColor))
```

### D. **Message Bubble Pattern**
```swift
VStack(alignment: message.isFromMe ? .trailing : .leading) {
    HStack {
        Text(message.text)
        Text(time).font(.caption)
    }
    .padding(.horizontal, 12)
    .padding(.vertical, 8)
    .background(bubbleColor)
    .clipShape(RoundedRectangle(cornerRadius: 16))
}
```

### E. **Button Style Patterns**
```swift
// Primary action
Button { /* action */ }
    .buttonStyle(.borderedProminent)
    .tint(.purple)  // or .green, .orange

// Secondary action
Button { /* action */ }
    .buttonStyle(.bordered)

// Plain/text button
Button { /* action */ }
    .buttonStyle(.plain)
    .foregroundColor(.accentColor)
```

### F. **Input Field Patterns**
```swift
HStack {
    Image(systemName: "magnifyingglass")
        .foregroundColor(.secondary)
    TextField("Placeholder", text: $text)
        .textFieldStyle(.plain)
}
.padding(10)
.background(Color(nsColor: .controlBackgroundColor))
```

### G. **Text Input with Actions**
```swift
HStack {
    TextField("...", text: $text)
        .textFieldStyle(.roundedBorder)
    Button { /* action */ }
        .buttonStyle(.borderedProminent)
        .disabled(text.isEmpty)
}
```

### H. **Flow Layout (Custom)**
```swift
// For wrapping tags - custom Layout struct
struct FlowLayout: Layout {
    var spacing: CGFloat = 8
    // Handles automatic line wrapping
}
```

### I. **Progress Indication**
```swift
// Inline progress
if isProcessing {
    ProgressView()
        .scaleEffect(0.7)
} else {
    Image(systemName: "sparkles")
}

// Linear progress bar
ProgressView(value: progress) {
    Text(phase).font(.caption)
}
.progressViewStyle(.linear)
```

### J. **Sheet/Modal Patterns**
```swift
.sheet(isPresented: $showingSheet) {
    VStack(spacing: 20) {
        // Header
        HStack {
            Image(systemName: "icon")
                .font(.title)
                .foregroundColor(.accentColor)
            Text("Title").font(.headline)
        }

        // Content

        // Actions
        HStack {
            Button("Cancel").keyboardShortcut(.escape)
            Spacer()
            Button("Confirm").buttonStyle(.borderedProminent).keyboardShortcut(.return)
        }
    }
    .padding()
    .frame(width: 400-500)  // Standard widths
}
```

---

### 6. TYPOGRAPHY & FONT HIERARCHY

```swift
// Headings
.font(.title2)              // Main page titles
.font(.headline)            // Section headers
.font(.subheadline.bold())  // Section labels
.font(.subheadline)         // Secondary headers

// Body text
.font(.body)                // Primary content
.font(.system(size: 13))    // Chat row names
.font(.system(size: 11))    // Chat metadata

// Captions
.font(.caption)             // Secondary labels, timestamps
.font(.caption2)            // Tertiary info
.font(.system(size: 9))     // Message timestamps

// Monospace
.font(.system(.body, design: .monospaced))  // Logs, transcriptions

// Weights
.fontWeight(.medium)        // Emphasis
.fontWeight(.semibold)      // Strong emphasis
```

---

### 7. SPACING & LAYOUT PATTERNS

```swift
// Standard spacing values
VStack(spacing: 0)          // No gap (dividers between)
VStack(spacing: 2)          // Tight (related items)
VStack(spacing: 4)          // Compact
VStack(spacing: 8)          // Standard (most common)
VStack(spacing: 12)         // Comfortable
VStack(spacing: 16)         // Spacious (section breaks)
VStack(spacing: 20)         // Large breaks

// Padding patterns
.padding(10)                // Small padding
.padding()                  // Standard (16pt)
.padding(.horizontal, 12)   // Horizontal only
.padding(.vertical, 8)      // Vertical only

// Frame sizing
.frame(minWidth: 280)       // Sidebar
.frame(minWidth: 400)       // Detail view
.frame(minWidth: 380, maxWidth: 500)  // Analysis panel
.frame(width: 400-500)      // Sheets
```

---

### 8. STATE MANAGEMENT PATTERNS

#### View State
```swift
@State private var selectedChat: ChatSession?     // Selection state
@State private var showAnalysis = false           // Panel visibility
@State private var isAnalyzing = false            // Loading state
@State private var error: String?                 // Error messaging
@State private var customPrompt = ""              // User input
```

#### Shared Services (Singletons)
```swift
@StateObject private var database = WhatsAppDatabase.shared
@ObservedObject var aiService = AIService.shared
@StateObject private var deepService = DeepAnalysisService.shared
@StateObject private var cache = AnalysisCacheManager.shared
```

#### Binding Patterns
```swift
@Binding var selectedChat: ChatSession?      // Parent → Child
$selectedChat                                 // Explicit binding creation
Binding(get: { value }, set: { value = $0 }) // Custom binding
```

---

### 9. DATA DISPLAY PATTERNS

#### Lists
```swift
List(items, selection: $selected) { item in
    ItemRow(item)
        .tag(item)
}
.listStyle(.sidebar)
```

#### Scroll Views
```swift
ScrollViewReader { proxy in
    ScrollView {
        LazyVStack(spacing: 8) {
            ForEach(items) { item in
                ItemView(item).id(item.id)
            }
        }
    }
    .onChange(of: items) { _, new in
        if let last = new.last {
            proxy.scrollTo(last.id, anchor: .bottom)  // Auto-scroll
        }
    }
}
```

#### Conditional Content
```swift
if let optional = optionalValue {
    ContentView(optional)
} else if condition {
    EmptyStateView()
} else {
    LoadingView()
}
```

---

### 10. ACCESSIBILITY & HELP PATTERNS

```swift
.help("Tooltip text")                      // Hover tooltips
Image(systemName: "systemName")            // SF Symbols
.foregroundColor(.secondary)               // Semantic colors
.lineLimit(1)                              // Text truncation
.textSelection(.enabled)                   // User selection
.multilineTextAlignment(.center)           // Text alignment
```

---

### 11. ERROR HANDLING & EMPTY STATES

#### Empty State Pattern
```swift
VStack(spacing: 16) {
    Image(systemName: "message.circle")
        .font(.system(size: 64))
        .foregroundColor(.secondary)
    Text("Selecione um chat")
        .font(.title2)
        .foregroundColor(.secondary)
    Text("Explanation...").font(.caption)
}
```

#### Error Display
```swift
if let error = error {
    Text(error)
        .font(.caption)
        .foregroundColor(.red)
}
```

---

### 12. COMPONENT HIERARCHY BY COMPLEXITY

**Simple Components** (Reusable across app)
- Status Indicators (circles with color)
- Badges/Tags (text with background)
- Headers (icon + title + metadata)
- Button variants

**Medium Components** (Multiple sections)
- ChatRowView (avatar + chat info)
- MessageBubbleView (sender + content + timestamp)
- AIConnectionStatusView (multi-indicator status)
- QuickAnalysisResultsView (summary + topics + sentiment)

**Complex Components** (Full features)
- ChatListView (search + filtering + list)
- ChatDetailView (headers + lazy scroll + load more)
- QuickAnalysisContent (multi-section analysis)
- DeepAnalysisResultsView (8+ distinct sections)

---

### 13. KEY STYLING DETAILS

#### Corners & Shapes
```swift
.cornerRadius(8)                    // Standard (tags, cards)
.cornerRadius(12)                   // Larger (headers, containers)
.cornerRadius(16)                   // Maximum (message bubbles)
.clipShape(RoundedRectangle(...))   // Precise shapes
```

#### Borders & Strokes
```swift
.overlay(
    RoundedRectangle(cornerRadius: 8)
        .stroke(Color.secondary.opacity(0.3), lineWidth: 1)
)
```

#### Dividers
```swift
Divider()                           // Full-width line
.padding()                          // With padding
```

---

### 14. WINDOW & LAYOUT CONFIGURATION

**Main Window**
- Default: 1200x700
- Chat List Sidebar: 280px minimum
- Messages Panel: 400px minimum
- Analysis Panel: 380-500px (conditional)

**Sheets**
- Quick Reply: 450x auto
- Deep Reply: 400x auto
- Audio Test: 500x400
- Settings: 500x400

---

### 15. BEHAVIORAL PATTERNS

#### Auto-show Logic
```swift
.onAppear {
    if aiService.isConnected {
        showAnalysis = true  // Auto-show when AI available
    }
}
```

#### Lazy Loading
```swift
LazyVStack(spacing: 8) {
    ForEach(messages) { message in
        MessageBubbleView(...)
    }
}
// Only renders visible items
```

#### Search/Filter Pattern
```swift
@State private var searchText = ""
var filteredItems: [Item] {
    database.search(query: searchText)
}
```

---

## SUMMARY OF KEY ARCHITECTURAL DECISIONS

1. **Split View Architecture:** Three-column layout allows chat list + messages + analysis simultaneously
2. **Modular Components:** Each section has dedicated View struct for reusability
3. **Conditional Panels:** Analysis panel toggles without navigation changes
4. **Sheet Modals:** Keeps main UI clean for reply composition
5. **Lazy Loading:** Messages load on demand with "load more" pattern
6. **Singleton Services:** Shared instances for database and AI services
7. **Cache Management:** Deep analysis caching for performance
8. **Adaptive Colors:** System colors that respond to OS theme changes
9. **Progress Indication:** Visual feedback for long operations
10. **Tab-based Analysis:** Quick (last 50 msgs) vs Deep (all messages) analysis modes

---

**Complete View File Locations:**
- `/Users/neog/Apps/macOS/WhatsAppAnalyzer-macOS/Sources/Views/ContentView.swift`
- `/Users/neog/Apps/macOS/WhatsAppAnalyzer-macOS/Sources/Views/ChatListView.swift`
- `/Users/neog/Apps/macOS/WhatsAppAnalyzer-macOS/Sources/Views/ChatDetailView.swift`
- `/Users/neog/Apps/macOS/WhatsAppAnalyzer-macOS/Sources/Views/AIAnalysisView.swift`
- `/Users/neog/Apps/macOS/WhatsAppAnalyzer-macOS/Sources/Views/DeepAnalysisView.swift`

This architecture provides a solid foundation for planning UI/UX improvements with clear component isolation and consistent styling patterns throughout the application.