소스 검색

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 1 개월 전
부모
커밋
9359ce8401
1개의 변경된 파일120개의 추가작업 그리고 21개의 파일을 삭제
  1. 120 21
      meetings_app/ViewController.swift

+ 120 - 21
meetings_app/ViewController.swift

@@ -334,8 +334,10 @@ final class ViewController: NSViewController {
334
     private var aiCompanionNotesMeetingIdByView = [ObjectIdentifier: String]()
334
     private var aiCompanionNotesMeetingIdByView = [ObjectIdentifier: String]()
335
     private var aiCompanionNotesStatusLabelByView = [ObjectIdentifier: NSTextField]()
335
     private var aiCompanionNotesStatusLabelByView = [ObjectIdentifier: NSTextField]()
336
     private var aiCompanionNotesCurrentRequestId: UUID?
336
     private var aiCompanionNotesCurrentRequestId: UUID?
337
-    private var aiCompanionNotesWindow: NSWindow?
337
+    private var aiCompanionNotesPanel: NSPanel?
338
     private weak var aiCompanionNotesTextView: NSTextView?
338
     private weak var aiCompanionNotesTextView: NSTextView?
339
+    private weak var aiCompanionNotesCopyButton: NSButton?
340
+    private weak var aiCompanionNotesCloseButton: NSButton?
339
     private var aiCompanionNotesTaskByMeetingId = [String: Task<Void, Never>]()
341
     private var aiCompanionNotesTaskByMeetingId = [String: Task<Void, Never>]()
340
     private var aiCompanionAudioPlayer: AVPlayer?
342
     private var aiCompanionAudioPlayer: AVPlayer?
341
     private var aiCompanionLocalAudioPlayer: AVAudioPlayer?
343
     private var aiCompanionLocalAudioPlayer: AVAudioPlayer?
@@ -3522,58 +3524,148 @@ private extension ViewController {
3522
 
3524
 
3523
     @MainActor
3525
     @MainActor
3524
     private func aiCompanionPresentNotesWindow(meetingTitle: String, initialText: String) {
3526
     private func aiCompanionPresentNotesWindow(meetingTitle: String, initialText: String) {
3525
-        if let window = aiCompanionNotesWindow, let textView = aiCompanionNotesTextView {
3526
-            window.title = "Notes - \(meetingTitle)"
3527
+        guard let hostWindow = view.window else { return }
3528
+        let hostIsDarkMode = hostWindow.effectiveAppearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua
3529
+        let panelAppearanceName: NSAppearance.Name = hostIsDarkMode ? .darkAqua : .aqua
3530
+        let outerBackgroundColor: NSColor = hostIsDarkMode ? .windowBackgroundColor : NSColor(calibratedWhite: 0.96, alpha: 1.0)
3531
+        let headerBackgroundColor: NSColor = hostIsDarkMode ? .controlBackgroundColor : NSColor(calibratedWhite: 0.93, alpha: 1.0)
3532
+
3533
+        if let panel = aiCompanionNotesPanel, let textView = aiCompanionNotesTextView {
3534
+            panel.title = "Notes - \(meetingTitle)"
3535
+            panel.appearance = NSAppearance(named: panelAppearanceName)
3536
+            panel.backgroundColor = outerBackgroundColor
3527
             textView.string = initialText
3537
             textView.string = initialText
3528
-            window.makeKeyAndOrderFront(nil)
3538
+            if hostWindow.attachedSheet !== panel {
3539
+                hostWindow.beginSheet(panel, completionHandler: nil)
3540
+            }
3541
+            panel.makeKeyAndOrderFront(nil)
3529
             NSApp.activate(ignoringOtherApps: true)
3542
             NSApp.activate(ignoringOtherApps: true)
3530
             return
3543
             return
3531
         }
3544
         }
3532
 
3545
 
3533
-        let windowWidth: CGFloat = 640
3534
-        let windowHeight: CGFloat = 560
3535
-        let window = NSWindow(
3536
-            contentRect: NSRect(x: 0, y: 0, width: windowWidth, height: windowHeight),
3537
-            styleMask: [.titled, .closable, .resizable],
3546
+        let panelWidth: CGFloat = 720
3547
+        let panelHeight: CGFloat = 540
3548
+        let panel = NSPanel(
3549
+            contentRect: NSRect(x: 0, y: 0, width: panelWidth, height: panelHeight),
3550
+            styleMask: [.titled, .closable],
3538
             backing: .buffered,
3551
             backing: .buffered,
3539
             defer: false
3552
             defer: false
3540
         )
3553
         )
3541
-        window.isReleasedWhenClosed = false
3542
-        window.title = "Notes - \(meetingTitle)"
3543
-        window.center()
3554
+        panel.isReleasedWhenClosed = false
3555
+        panel.title = "Notes - \(meetingTitle)"
3556
+        panel.isFloatingPanel = false
3557
+        panel.hidesOnDeactivate = false
3558
+        panel.delegate = self
3559
+        panel.appearance = NSAppearance(named: panelAppearanceName)
3560
+        panel.backgroundColor = outerBackgroundColor
3561
+        panel.standardWindowButton(.zoomButton)?.isHidden = true
3562
+        panel.standardWindowButton(.miniaturizeButton)?.isHidden = true
3544
 
3563
 
3545
         let root = NSView()
3564
         let root = NSView()
3546
         root.translatesAutoresizingMaskIntoConstraints = false
3565
         root.translatesAutoresizingMaskIntoConstraints = false
3566
+        root.wantsLayer = true
3567
+        root.layer?.backgroundColor = outerBackgroundColor.cgColor
3568
+
3569
+        let header = NSView()
3570
+        header.translatesAutoresizingMaskIntoConstraints = false
3571
+        header.wantsLayer = true
3572
+        header.layer?.backgroundColor = headerBackgroundColor.cgColor
3573
+        header.layer?.cornerRadius = 10
3574
+
3575
+        let titleLabel = textLabel("Meeting Notes", font: NSFont.systemFont(ofSize: 15, weight: .semibold), color: .labelColor)
3576
+        titleLabel.translatesAutoresizingMaskIntoConstraints = false
3577
+        let subtitleLabel = textLabel(meetingTitle, font: NSFont.systemFont(ofSize: 12, weight: .regular), color: .secondaryLabelColor)
3578
+        subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
3579
+        subtitleLabel.lineBreakMode = .byTruncatingTail
3547
 
3580
 
3548
         let scroll = NSScrollView()
3581
         let scroll = NSScrollView()
3549
         scroll.translatesAutoresizingMaskIntoConstraints = false
3582
         scroll.translatesAutoresizingMaskIntoConstraints = false
3550
-        scroll.drawsBackground = false
3583
+        scroll.drawsBackground = true
3584
+        scroll.backgroundColor = NSColor.textBackgroundColor
3551
         scroll.hasVerticalScroller = true
3585
         scroll.hasVerticalScroller = true
3586
+        scroll.hasHorizontalScroller = false
3587
+        scroll.borderType = .bezelBorder
3552
 
3588
 
3553
         let textView = NSTextView()
3589
         let textView = NSTextView()
3554
         textView.isEditable = false
3590
         textView.isEditable = false
3555
         textView.isSelectable = true
3591
         textView.isSelectable = true
3556
-        textView.backgroundColor = .clear
3592
+        textView.backgroundColor = NSColor.textBackgroundColor
3593
+        textView.textColor = NSColor.labelColor
3557
         textView.font = NSFont.systemFont(ofSize: 13, weight: .regular)
3594
         textView.font = NSFont.systemFont(ofSize: 13, weight: .regular)
3558
         textView.string = initialText
3595
         textView.string = initialText
3559
         textView.textContainer?.widthTracksTextView = true
3596
         textView.textContainer?.widthTracksTextView = true
3597
+        textView.textContainerInset = NSSize(width: 10, height: 10)
3598
+
3599
+        let copyButton = NSButton(title: "Copy", target: self, action: #selector(aiCompanionCopyNotesTapped(_:)))
3600
+        copyButton.translatesAutoresizingMaskIntoConstraints = false
3601
+        copyButton.bezelStyle = .rounded
3602
+
3603
+        let closeButton = NSButton(title: "Close", target: self, action: #selector(aiCompanionCloseNotesTapped(_:)))
3604
+        closeButton.translatesAutoresizingMaskIntoConstraints = false
3605
+        closeButton.bezelStyle = .rounded
3560
 
3606
 
3561
         scroll.documentView = textView
3607
         scroll.documentView = textView
3608
+        header.addSubview(titleLabel)
3609
+        header.addSubview(subtitleLabel)
3610
+        header.addSubview(copyButton)
3611
+        header.addSubview(closeButton)
3612
+        root.addSubview(header)
3562
         root.addSubview(scroll)
3613
         root.addSubview(scroll)
3563
 
3614
 
3564
         NSLayoutConstraint.activate([
3615
         NSLayoutConstraint.activate([
3565
-            scroll.leadingAnchor.constraint(equalTo: root.leadingAnchor),
3566
-            scroll.trailingAnchor.constraint(equalTo: root.trailingAnchor),
3567
-            scroll.topAnchor.constraint(equalTo: root.topAnchor),
3568
-            scroll.bottomAnchor.constraint(equalTo: root.bottomAnchor)
3616
+            header.topAnchor.constraint(equalTo: root.topAnchor, constant: 12),
3617
+            header.leadingAnchor.constraint(equalTo: root.leadingAnchor, constant: 16),
3618
+            header.trailingAnchor.constraint(equalTo: root.trailingAnchor, constant: -16),
3619
+
3620
+            titleLabel.topAnchor.constraint(equalTo: header.topAnchor, constant: 12),
3621
+            titleLabel.leadingAnchor.constraint(equalTo: header.leadingAnchor, constant: 12),
3622
+            titleLabel.trailingAnchor.constraint(lessThanOrEqualTo: copyButton.leadingAnchor, constant: -12),
3623
+
3624
+            subtitleLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 4),
3625
+            subtitleLabel.leadingAnchor.constraint(equalTo: header.leadingAnchor, constant: 12),
3626
+            subtitleLabel.trailingAnchor.constraint(lessThanOrEqualTo: copyButton.leadingAnchor, constant: -12),
3627
+            subtitleLabel.bottomAnchor.constraint(equalTo: header.bottomAnchor, constant: -12),
3628
+
3629
+            copyButton.centerYAnchor.constraint(equalTo: header.centerYAnchor),
3630
+            copyButton.trailingAnchor.constraint(equalTo: closeButton.leadingAnchor, constant: -8),
3631
+
3632
+            closeButton.centerYAnchor.constraint(equalTo: header.centerYAnchor),
3633
+            closeButton.trailingAnchor.constraint(equalTo: header.trailingAnchor, constant: -12),
3634
+
3635
+            scroll.topAnchor.constraint(equalTo: header.bottomAnchor, constant: 12),
3636
+            scroll.leadingAnchor.constraint(equalTo: root.leadingAnchor, constant: 16),
3637
+            scroll.trailingAnchor.constraint(equalTo: root.trailingAnchor, constant: -16),
3638
+            scroll.bottomAnchor.constraint(equalTo: root.bottomAnchor, constant: -16)
3569
         ])
3639
         ])
3570
 
3640
 
3571
-        window.contentView = root
3572
-        window.makeKeyAndOrderFront(nil)
3641
+        panel.contentView = root
3642
+        hostWindow.beginSheet(panel, completionHandler: nil)
3643
+        panel.makeKeyAndOrderFront(nil)
3573
         NSApp.activate(ignoringOtherApps: true)
3644
         NSApp.activate(ignoringOtherApps: true)
3574
 
3645
 
3575
-        aiCompanionNotesWindow = window
3646
+        aiCompanionNotesPanel = panel
3576
         aiCompanionNotesTextView = textView
3647
         aiCompanionNotesTextView = textView
3648
+        aiCompanionNotesCopyButton = copyButton
3649
+        aiCompanionNotesCloseButton = closeButton
3650
+    }
3651
+
3652
+    @objc private func aiCompanionCopyNotesTapped(_ sender: NSButton) {
3653
+        guard let notes = aiCompanionNotesTextView?.string, notes.isEmpty == false else { return }
3654
+        NSPasteboard.general.clearContents()
3655
+        NSPasteboard.general.setString(notes, forType: .string)
3656
+        sender.title = "Copied"
3657
+        DispatchQueue.main.asyncAfter(deadline: .now() + 1.2) { [weak self] in
3658
+            guard self?.aiCompanionNotesCopyButton === sender else { return }
3659
+            sender.title = "Copy"
3660
+        }
3661
+    }
3662
+
3663
+    @objc private func aiCompanionCloseNotesTapped(_ sender: NSButton) {
3664
+        guard let panel = aiCompanionNotesPanel else { return }
3665
+        if let hostWindow = view.window, hostWindow.attachedSheet === panel {
3666
+            hostWindow.endSheet(panel)
3667
+        }
3668
+        panel.orderOut(nil)
3577
     }
3669
     }
3578
 
3670
 
3579
     private func aiCompanionMeetMeetingCode(from meetURL: URL) -> String? {
3671
     private func aiCompanionMeetMeetingCode(from meetURL: URL) -> String? {
@@ -6513,6 +6605,13 @@ extension ViewController: AVAudioPlayerDelegate {
6513
 extension ViewController: NSWindowDelegate {
6605
 extension ViewController: NSWindowDelegate {
6514
     func windowWillClose(_ notification: Notification) {
6606
     func windowWillClose(_ notification: Notification) {
6515
         guard let closingWindow = notification.object as? NSWindow else { return }
6607
         guard let closingWindow = notification.object as? NSWindow else { return }
6608
+        if closingWindow === aiCompanionNotesPanel {
6609
+            aiCompanionNotesPanel = nil
6610
+            aiCompanionNotesTextView = nil
6611
+            aiCompanionNotesCopyButton = nil
6612
+            aiCompanionNotesCloseButton = nil
6613
+            return
6614
+        }
6516
         if closingWindow === paywallWindow {
6615
         if closingWindow === paywallWindow {
6517
             paywallWindow = nil
6616
             paywallWindow = nil
6518
             paywallUpgradeFlowEnabled = false
6617
             paywallUpgradeFlowEnabled = false