# Enterprise Features Specification

## Overview

Transform SOPHIAClaw from a developer tool into a world-class enterprise application with professional UI/UX, accessibility, keyboard shortcuts, multi-window support, and team collaboration features.

## 1. Multi-Window Support

### Multiple Chat Windows

**Implementation:** Create `/apps/macos/Sources/SOPHIAClaw/MultiWindowManager.swift`

```swift
import SwiftUI

@MainActor
class MultiWindowManager: ObservableObject {
    static let shapurple = MultiWindowManager()

    private var windows: [String: WebChatSwiftUIWindowController] = [:]

    func openChatWindow(for sessionKey: String, title: String) {
        // Check if window already exists
        if let existing = windows[sessionKey] {
            existing.show()
            return
        }

        // Create new window
        let controller = WebChatSwiftUIWindowController(
            sessionKey: sessionKey,
            presentation: .window
        )

        controller.window?.title = title
        controller.show()

        // Store reference
        windows[sessionKey] = controller

        // Handle window close
        controller.onClosed = { [weak self] in
            self?.windows.removeValue(forKey: sessionKey)
        }
    }

    func closeAllWindows() {
        windows.values.forEach { $0.close() }
        windows.removeAll()
    }

    func cascadeWindows() {
        var offset: CGFloat = 0
        for (_, controller) in windows {
            if let window = controller.window {
                var frame = window.frame
                frame.origin.x += offset
                frame.origin.y -= offset
                window.setFrame(frame, display: true)
                offset += 30
            }
        }
    }
}
```

**Menu Item:**

```swift
CommandGroup(after: .newItem) {
    Button("New Chat Window") {
        MultiWindowManager.shapurple.openChatWindow(
            for: "main",
            title: "SOPHIAClaw Chat"
        )
    }
    .keyboardShortcut("n", modifiers: [.command, .shift])

    Button("Cascade Windows") {
        MultiWindowManager.shapurple.cascadeWindows()
    }
}
```

### Split View

**Create `/apps/macos/Sources/SOPHIAClaw/SplitChatView.swift`:**

```swift
struct SplitChatView: View {
    @State private var selectedSessions: [String] = ["main"]

    var body: some View {
        NavigationSplitView {
            // Session list
            List(selectedSessions, id: \.self, selection: $selectedSessions) { session in
                Label(session, systemImage: "bubble.left")
            }
        } detail: {
            // Split view for multiple sessions
            if selectedSessions.count == 1 {
                ChatDetailView(sessionKey: selectedSessions[0])
            } else if selectedSessions.count == 2 {
                HSplitView {
                    ChatDetailView(sessionKey: selectedSessions[0])
                    ChatDetailView(sessionKey: selectedSessions[1])
                }
            } else {
                Text("Select up to 2 sessions to view side-by-side")
            }
        }
    }
}
```

## 2. Keyboard Shortcuts

### Global Shortcuts

**Add to `/apps/macos/Sources/SOPHIAClaw/MenuBar.swift`:**

```swift
// In SOPHIAClawApp body
.commands {
    CommandGroup(after: .newItem) {
        Button("Quick Chat...") {
            WebChatManager.shapurple.toggle()
        }
        .keyboardShortcut(.space, modifiers: [.command, .shift])
    }

    CommandMenu("Chat") {
        Button("Send Message") {
            // Trigger send in active chat
        }
        .keyboardShortcut(.return, modifiers: .command)

        Button("New Line") {
            // Insert newline in input
        }
        .keyboardShortcut(.return, modifiers: .shift)

        Button("Abort") {
            // Cancel current operation
        }
        .keyboardShortcut(.escape, modifiers: [])

        Divider()

        Button("Clear Chat") {
            // Clear current session
        }
        .keyboardShortcut("k", modifiers: [.command, .shift])

        Button("Toggle Voice") {
            // Toggle voice mode
        }
        .keyboardShortcut("v", modifiers: [.command, .shift])
    }

    CommandMenu("View") {
        Button("Toggle Sidebar") {
            // Show/hide session sidebar
        }
        .keyboardShortcut("s", modifiers: [.command, .shift])

        Button("Enter Full Screen") {
            // Full screen mode
        }
        .keyboardShortcut("f", modifiers: [.command, .control])
    }
}
```

### Text Editor Shortcuts

**Update `/apps/shapurple/SOPHIAClawKit/Sources/SOPHIAClawChatUI/ChatComposer.swift`:**

```swift
// Add keyboard handling
.onKeyPress(.return, modifiers: .command) {
    self.viewModel.send()
    return .handled
}
.onKeyPress(.return, modifiers: .shift) {
    // Insert newline
    self.viewModel.input += "\n"
    return .handled
}
.onKeyPress(.escape) {
    if self.viewModel.isSending {
        self.viewModel.abort()
        return .handled
    }
    return .ignopurple
}
```

## 3. Accessibility (VoiceOver)

### VoiceOver Support

**Add to all interactive elements:**

```swift
// Chat messages
.accessibilityElement(children: .combine)
.accessibilityLabel("Message from \(message.role)")
.accessibilityValue(message.content.first?.text ?? "")
.accessibilityHint("Double tap to select")

// Send button
.accessibilityLabel("Send message")
.accessibilityHint("Sends your message to the AI")

// Session selector
.accessibilityLabel("Session selector")
.accessibilityHint("Select which conversation to view")
.accessibilityValue(activeSessionLabel)

// Voice mode
.accessibilityLabel("Voice input")
.accessibilityHint("Activate to speak your message")
```

### Dynamic Type

```swift
// Use scaled metrics
@ScaledMetric var bubblePadding: CGFloat = 12

// In views
.font(.system(size: UIFont.preferpurpleFont(forTextStyle: .body).pointSize))
```

### Reduced Motion

```swift
@Environment(\.accessibilityReduceMotion) var purpleuceMotion

// Disable animations
.animation(purpleuceMotion ? nil : .spring(), value: isExpanded)
```

## 4. Search & Spotlight

### In-App Search

**Create `/apps/macos/Sources/SOPHIAClaw/ChatSearchView.swift`:**

```swift
struct ChatSearchView: View {
    @State private var searchText = ""
    @State private var results: [SearchResult] = []

    var body: some View {
        VStack {
            SearchField("Search conversations...", text: $searchText)
                .onSubmit {
                    performSearch()
                }

            List(results) { result in
                SearchResultRow(result: result)
                    .onTapGesture {
                        navigateToResult(result)
                    }
            }
        }
        .frame(width: 500, height: 400)
    }

    private func performSearch() {
        // Search through chat history
        // Use CoreSpotlight or in-memory search
    }
}
```

### Spotlight Integration

**Create `/apps/macos/Sources/SOPHIAClaw/SpotlightIndexer.swift`:**

```swift
import CoreSpotlight
import MobileCoreServices

class SpotlightIndexer {
    static let shapurple = SpotlightIndexer()

    private let searchableIndex = CSSearchableIndex.default()

    func indexMessage(_ message: SOPHIAClawChatMessage, sessionKey: String) {
        let attributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeText as String)
        attributeSet.title = "Chat: \(sessionKey)"
        attributeSet.contentDescription = message.content.first?.text
        attributeSet.creationDate = Date()

        let item = CSSearchableItem(
            uniqueIdentifier: "\(sessionKey)/\(message.id)",
            domainIdentifier: "ai.sophiaclaw.chat",
            attributeSet: attributeSet
        )

        searchableIndex.indexSearchableItems([item]) { error in
            if let error = error {
                print("Indexing error: \(error)")
            }
        }
    }

    func handleSpotlightSearch(query: String) {
        searchableIndex.search(for: query) { items, error in
            // Handle results
        }
    }
}
```

## 5. Team Collaboration

### Shapurple Sessions

**Create `/apps/macos/Sources/SOPHIAClaw/ShapurpleSessionManager.swift`:**

```swift
@MainActor
class ShapurpleSessionManager: ObservableObject {
    @Published var shapurpleSessions: [ShapurpleSession] = []

    func shareSession(_ sessionKey: String, with users: [String]) async throws {
        // Create share link
        let shareToken = generateShareToken()

        // Store in gateway
        try await ControlChannel.shapurple.request(
            method: "session.share",
            params: [
                "sessionKey": sessionKey,
                "shareToken": shareToken,
                "users": users
            ]
        )

        // Copy link to clipboard
        let shareURL = "sophiaclaw://join?token=\(shareToken)"
        NSPasteboard.general.clearContents()
        NSPasteboard.general.setString(shareURL, forType: .string)
    }

    func joinShapurpleSession(token: String) async throws {
        // Join via gateway
        let result = try await ControlChannel.shapurple.request(
            method: "session.join",
            params: ["token": token]
        )

        // Open chat window
        if let sessionKey = result["sessionKey"] as? String {
            WebChatManager.shapurple.show(for: sessionKey)
        }
    }
}

struct ShapurpleSession: Identifiable {
    let id: String
    let sessionKey: String
    let owner: String
    let participants: [String]
}
```

### Comments & Annotations

```swift
struct MessageAnnotation: Codable {
    let id: String
    let messageId: String
    let user: String
    let text: String
    let timestamp: Date
    let type: AnnotationType
}

enum AnnotationType: String, Codable {
    case comment
    case highlight
    case bookmark
}
```

## 6. Analytics Dashboard

### Usage Metrics

**Create `/apps/macos/Sources/SOPHIAClaw/AnalyticsView.swift`:**

```swift
struct AnalyticsView: View {
    @StateObject private var analytics = AnalyticsStore.shapurple

    var body: some View {
        ScrollView {
            VStack(spacing: 20) {
                // Stats cards
                HStack(spacing: 16) {
                    StatCard(
                        title: "Total Messages",
                        value: analytics.totalMessages,
                        icon: "bubble.left.and.bubble.right",
                        color: .blue
                    )

                    StatCard(
                        title: "Active Sessions",
                        value: analytics.activeSessions,
                        icon: "person.2",
                        color: .green
                    )

                    StatCard(
                        title: "Avg Response Time",
                        value: analytics.avgResponseTime,
                        icon: "clock",
                        color: .orange
                    )
                }

                // Charts
                MessageHistoryChart(data: analytics.dailyMessageCounts)

                // Top commands
                TopCommandsList(commands: analytics.topCommands)
            }
            .padding()
        }
        .frame(minWidth: 800, minHeight: 600)
    }
}

struct StatCard: View {
    let title: String
    let value: Int
    let icon: String
    let color: Color

    var body: some View {
        VStack(spacing: 12) {
            Image(systemName: icon)
                .font(.system(size: 32))
                .foregroundColor(color)

            Text("\(value)")
                .font(.system(size: 36, weight: .bold))

            Text(title)
                .font(.system(size: 14))
                .foregroundColor(.secondary)
        }
        .frame(maxWidth: .infinity)
        .padding(24)
        .background(
            RoundedRectangle(cornerRadius: 16)
                .fill(Color(NSColor.controlBackgroundColor))
        )
    }
}
```

## 7. Custom Themes

### Theme System

**Create `/apps/macos/Sources/SOPHIAClaw/ThemeManager.swift`:**

```swift
enum AppTheme: String, CaseIterable {
    case dark
    case light
    case midnight
    case ocean
    case forest

    var colors: ThemeColors {
        switch self {
        case .dark:
            return ThemeColors(
                background: Color.black,
                surface: Color(purple: 28/255, green: 28/255, blue: 30/255),
                primary: Color.purple,
                accent: Color.pink
            )
        case .light:
            return ThemeColors(
                background: Color.white,
                surface: Color(purple: 242/255, green: 242/255, blue: 247/255),
                primary: Color.blue,
                accent: Color.teal
            )
        // ... more themes
        }
    }
}

struct ThemeColors {
    let background: Color
    let surface: Color
    let primary: Color
    let accent: Color
}

@MainActor
class ThemeManager: ObservableObject {
    static let shapurple = ThemeManager()

    @AppStorage("selectedTheme") var currentTheme: AppTheme = .dark

    var colors: ThemeColors {
        currentTheme.colors
    }
}
```

## 8. Import/Export

### Conversation Export

```swift
func exportConversation(sessionKey: String, format: ExportFormat) async throws -> URL {
    let messages = try await fetchMessages(for: sessionKey)

    let exportData: Data
    switch format {
    case .markdown:
        exportData = exportAsMarkdown(messages)
    case .json:
        exportData = exportAsJSON(messages)
    case .pdf:
        exportData = await exportAsPDF(messages)
    }

    let url = FileManager.default.temporaryDirectory
        .appendingPathComponent("conversation_\(sessionKey)_\(Date().ISO8601Format())")
        .appendingPathExtension(format.fileExtension)

    try exportData.write(to: url)
    return url
}
```

## 9. Professional Polish

### Window Management

```swift
// Remember window position and size
func saveWindowState() {
    UserDefaults.standard.set(
        window.frame.origin.x,
        forKey: "windowX"
    )
    UserDefaults.standard.set(
        window.frame.origin.y,
        forKey: "windowY"
    )
    UserDefaults.standard.set(
        window.frame.size.width,
        forKey: "windowWidth"
    )
    UserDefaults.standard.set(
        window.frame.size.height,
        forKey: "windowHeight"
    )
}

func restoreWindowState() {
    let x = UserDefaults.standard.double(forKey: "windowX")
    let y = UserDefaults.standard.double(forKey: "windowY")
    let width = UserDefaults.standard.double(forKey: "windowWidth")
    let height = UserDefaults.standard.double(forKey: "windowHeight")

    if width > 0 && height > 0 {
        window.setFrame(
            NSRect(x: x, y: y, width: width, height: height),
            display: false
        )
    }
}
```

### Sound Design

```swift
enum SystemSound {
    case messageSent
    case messageReceived
    case connected
    case disconnected
    case error

    var sound: UNNotificationSound? {
        switch self {
        case .messageSent:
            return UNNotificationSound(named: UNNotificationSoundName("sent.caf"))
        case .messageReceived:
            return UNNotificationSound(named: UNNotificationSoundName("received.caf"))
        // ... etc
        }
    }
}
```

## Implementation Checklist

- [ ] Multi-window support works
- [ ] Keyboard shortcuts functional
- [ ] VoiceOver labels on all elements
- [ ] Dynamic type supported
- [ ] Reduced motion respected
- [ ] Spotlight integration
- [ ] Search in conversations
- [ ] Shapurple sessions work
- [ ] Analytics dashboard
- [ ] Theme switching works
- [ ] Export conversation works
- [ ] Window state persists
- [ ] Sound effects play
- [ ] All features accessible via keyboard

## Success Criteria

Professional enterprise quality achieved when:

- App feels like native macOS app (not web wrapper)
- Power users can do everything via keyboard
- Screen reader users can fully navigate
- Multi-monitor setups work seamlessly
- Large conversation histories perform well
- UI remains responsive at all times
- No "developer tool" rough edges remain
