Sfoglia il codice sorgente

Present AI Companion notes inside an in-app sheet.

Replace the separate notes window with a styled in-app popup that supports scrolling, copy, and close actions while preserving light/dark adaptive colors.

Co-authored-by: Cursor <cursoragent@cursor.com>
huzaifahayat12 2 mesi fa
parent
commit
9359ce8401
1 ha cambiato i file con 120 aggiunte e 21 eliminazioni
  1. 120 21
      meetings_app/ViewController.swift

+ 120 - 21
meetings_app/ViewController.swift

@@ -334,8 +334,10 @@ final class ViewController: NSViewController {
     private var aiCompanionNotesMeetingIdByView = [ObjectIdentifier: String]()
     private var aiCompanionNotesStatusLabelByView = [ObjectIdentifier: NSTextField]()
     private var aiCompanionNotesCurrentRequestId: UUID?
-    private var aiCompanionNotesWindow: NSWindow?
+    private var aiCompanionNotesPanel: NSPanel?
     private weak var aiCompanionNotesTextView: NSTextView?
+    private weak var aiCompanionNotesCopyButton: NSButton?
+    private weak var aiCompanionNotesCloseButton: NSButton?
     private var aiCompanionNotesTaskByMeetingId = [String: Task<Void, Never>]()
     private var aiCompanionAudioPlayer: AVPlayer?
     private var aiCompanionLocalAudioPlayer: AVAudioPlayer?
@@ -3522,58 +3524,148 @@ private extension ViewController {
 
     @MainActor
     private func aiCompanionPresentNotesWindow(meetingTitle: String, initialText: String) {
-        if let window = aiCompanionNotesWindow, let textView = aiCompanionNotesTextView {
-            window.title = "Notes - \(meetingTitle)"
+        guard let hostWindow = view.window else { return }
+        let hostIsDarkMode = hostWindow.effectiveAppearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua
+        let panelAppearanceName: NSAppearance.Name = hostIsDarkMode ? .darkAqua : .aqua
+        let outerBackgroundColor: NSColor = hostIsDarkMode ? .windowBackgroundColor : NSColor(calibratedWhite: 0.96, alpha: 1.0)
+        let headerBackgroundColor: NSColor = hostIsDarkMode ? .controlBackgroundColor : NSColor(calibratedWhite: 0.93, alpha: 1.0)
+
+        if let panel = aiCompanionNotesPanel, let textView = aiCompanionNotesTextView {
+            panel.title = "Notes - \(meetingTitle)"
+            panel.appearance = NSAppearance(named: panelAppearanceName)
+            panel.backgroundColor = outerBackgroundColor
             textView.string = initialText
-            window.makeKeyAndOrderFront(nil)
+            if hostWindow.attachedSheet !== panel {
+                hostWindow.beginSheet(panel, completionHandler: nil)
+            }
+            panel.makeKeyAndOrderFront(nil)
             NSApp.activate(ignoringOtherApps: true)
             return
         }
 
-        let windowWidth: CGFloat = 640
-        let windowHeight: CGFloat = 560
-        let window = NSWindow(
-            contentRect: NSRect(x: 0, y: 0, width: windowWidth, height: windowHeight),
-            styleMask: [.titled, .closable, .resizable],
+        let panelWidth: CGFloat = 720
+        let panelHeight: CGFloat = 540
+        let panel = NSPanel(
+            contentRect: NSRect(x: 0, y: 0, width: panelWidth, height: panelHeight),
+            styleMask: [.titled, .closable],
             backing: .buffered,
             defer: false
         )
-        window.isReleasedWhenClosed = false
-        window.title = "Notes - \(meetingTitle)"
-        window.center()
+        panel.isReleasedWhenClosed = false
+        panel.title = "Notes - \(meetingTitle)"
+        panel.isFloatingPanel = false
+        panel.hidesOnDeactivate = false
+        panel.delegate = self
+        panel.appearance = NSAppearance(named: panelAppearanceName)
+        panel.backgroundColor = outerBackgroundColor
+        panel.standardWindowButton(.zoomButton)?.isHidden = true
+        panel.standardWindowButton(.miniaturizeButton)?.isHidden = true
 
         let root = NSView()
         root.translatesAutoresizingMaskIntoConstraints = false
+        root.wantsLayer = true
+        root.layer?.backgroundColor = outerBackgroundColor.cgColor
+
+        let header = NSView()
+        header.translatesAutoresizingMaskIntoConstraints = false
+        header.wantsLayer = true
+        header.layer?.backgroundColor = headerBackgroundColor.cgColor
+        header.layer?.cornerRadius = 10
+
+        let titleLabel = textLabel("Meeting Notes", font: NSFont.systemFont(ofSize: 15, weight: .semibold), color: .labelColor)
+        titleLabel.translatesAutoresizingMaskIntoConstraints = false
+        let subtitleLabel = textLabel(meetingTitle, font: NSFont.systemFont(ofSize: 12, weight: .regular), color: .secondaryLabelColor)
+        subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
+        subtitleLabel.lineBreakMode = .byTruncatingTail
 
         let scroll = NSScrollView()
         scroll.translatesAutoresizingMaskIntoConstraints = false
-        scroll.drawsBackground = false
+        scroll.drawsBackground = true
+        scroll.backgroundColor = NSColor.textBackgroundColor
         scroll.hasVerticalScroller = true
+        scroll.hasHorizontalScroller = false
+        scroll.borderType = .bezelBorder
 
         let textView = NSTextView()
         textView.isEditable = false
         textView.isSelectable = true
-        textView.backgroundColor = .clear
+        textView.backgroundColor = NSColor.textBackgroundColor
+        textView.textColor = NSColor.labelColor
         textView.font = NSFont.systemFont(ofSize: 13, weight: .regular)
         textView.string = initialText
         textView.textContainer?.widthTracksTextView = true
+        textView.textContainerInset = NSSize(width: 10, height: 10)
+
+        let copyButton = NSButton(title: "Copy", target: self, action: #selector(aiCompanionCopyNotesTapped(_:)))
+        copyButton.translatesAutoresizingMaskIntoConstraints = false
+        copyButton.bezelStyle = .rounded
+
+        let closeButton = NSButton(title: "Close", target: self, action: #selector(aiCompanionCloseNotesTapped(_:)))
+        closeButton.translatesAutoresizingMaskIntoConstraints = false
+        closeButton.bezelStyle = .rounded
 
         scroll.documentView = textView
+        header.addSubview(titleLabel)
+        header.addSubview(subtitleLabel)
+        header.addSubview(copyButton)
+        header.addSubview(closeButton)
+        root.addSubview(header)
         root.addSubview(scroll)
 
         NSLayoutConstraint.activate([
-            scroll.leadingAnchor.constraint(equalTo: root.leadingAnchor),
-            scroll.trailingAnchor.constraint(equalTo: root.trailingAnchor),
-            scroll.topAnchor.constraint(equalTo: root.topAnchor),
-            scroll.bottomAnchor.constraint(equalTo: root.bottomAnchor)
+            header.topAnchor.constraint(equalTo: root.topAnchor, constant: 12),
+            header.leadingAnchor.constraint(equalTo: root.leadingAnchor, constant: 16),
+            header.trailingAnchor.constraint(equalTo: root.trailingAnchor, constant: -16),
+
+            titleLabel.topAnchor.constraint(equalTo: header.topAnchor, constant: 12),
+            titleLabel.leadingAnchor.constraint(equalTo: header.leadingAnchor, constant: 12),
+            titleLabel.trailingAnchor.constraint(lessThanOrEqualTo: copyButton.leadingAnchor, constant: -12),
+
+            subtitleLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 4),
+            subtitleLabel.leadingAnchor.constraint(equalTo: header.leadingAnchor, constant: 12),
+            subtitleLabel.trailingAnchor.constraint(lessThanOrEqualTo: copyButton.leadingAnchor, constant: -12),
+            subtitleLabel.bottomAnchor.constraint(equalTo: header.bottomAnchor, constant: -12),
+
+            copyButton.centerYAnchor.constraint(equalTo: header.centerYAnchor),
+            copyButton.trailingAnchor.constraint(equalTo: closeButton.leadingAnchor, constant: -8),
+
+            closeButton.centerYAnchor.constraint(equalTo: header.centerYAnchor),
+            closeButton.trailingAnchor.constraint(equalTo: header.trailingAnchor, constant: -12),
+
+            scroll.topAnchor.constraint(equalTo: header.bottomAnchor, constant: 12),
+            scroll.leadingAnchor.constraint(equalTo: root.leadingAnchor, constant: 16),
+            scroll.trailingAnchor.constraint(equalTo: root.trailingAnchor, constant: -16),
+            scroll.bottomAnchor.constraint(equalTo: root.bottomAnchor, constant: -16)
         ])
 
-        window.contentView = root
-        window.makeKeyAndOrderFront(nil)
+        panel.contentView = root
+        hostWindow.beginSheet(panel, completionHandler: nil)
+        panel.makeKeyAndOrderFront(nil)
         NSApp.activate(ignoringOtherApps: true)
 
-        aiCompanionNotesWindow = window
+        aiCompanionNotesPanel = panel
         aiCompanionNotesTextView = textView
+        aiCompanionNotesCopyButton = copyButton
+        aiCompanionNotesCloseButton = closeButton
+    }
+
+    @objc private func aiCompanionCopyNotesTapped(_ sender: NSButton) {
+        guard let notes = aiCompanionNotesTextView?.string, notes.isEmpty == false else { return }
+        NSPasteboard.general.clearContents()
+        NSPasteboard.general.setString(notes, forType: .string)
+        sender.title = "Copied"
+        DispatchQueue.main.asyncAfter(deadline: .now() + 1.2) { [weak self] in
+            guard self?.aiCompanionNotesCopyButton === sender else { return }
+            sender.title = "Copy"
+        }
+    }
+
+    @objc private func aiCompanionCloseNotesTapped(_ sender: NSButton) {
+        guard let panel = aiCompanionNotesPanel else { return }
+        if let hostWindow = view.window, hostWindow.attachedSheet === panel {
+            hostWindow.endSheet(panel)
+        }
+        panel.orderOut(nil)
     }
 
     private func aiCompanionMeetMeetingCode(from meetURL: URL) -> String? {
@@ -6513,6 +6605,13 @@ extension ViewController: AVAudioPlayerDelegate {
 extension ViewController: NSWindowDelegate {
     func windowWillClose(_ notification: Notification) {
         guard let closingWindow = notification.object as? NSWindow else { return }
+        if closingWindow === aiCompanionNotesPanel {
+            aiCompanionNotesPanel = nil
+            aiCompanionNotesTextView = nil
+            aiCompanionNotesCopyButton = nil
+            aiCompanionNotesCloseButton = nil
+            return
+        }
         if closingWindow === paywallWindow {
             paywallWindow = nil
             paywallUpgradeFlowEnabled = false