|
@@ -7089,8 +7089,8 @@ class ViewController: NSViewController {
|
|
|
root.addSubview(container)
|
|
root.addSubview(container)
|
|
|
|
|
|
|
|
// Header row: recordings list (AI Companion mode).
|
|
// Header row: recordings list (AI Companion mode).
|
|
|
- let title = makeLabel("Ai Companion", size: 28, color: primaryText, weight: .bold, centered: false)
|
|
|
|
|
- let subtitle = makeLabel("Recorded meetings and Apple transcripts", size: 13, color: secondaryText, weight: .regular, centered: false)
|
|
|
|
|
|
|
+ let title = makeLabel("AI Companion", size: 28, color: primaryText, weight: .bold, centered: false)
|
|
|
|
|
+ let subtitle = makeLabel("Get notes from your previous meetings", size: 13, color: secondaryText, weight: .regular, centered: false)
|
|
|
let count = makeLabel("0 recordings", size: 12, color: secondaryText, weight: .medium, centered: false)
|
|
let count = makeLabel("0 recordings", size: 12, color: secondaryText, weight: .medium, centered: false)
|
|
|
let titleStack = NSStackView(views: [title, subtitle, count])
|
|
let titleStack = NSStackView(views: [title, subtitle, count])
|
|
|
titleStack.orientation = .vertical
|
|
titleStack.orientation = .vertical
|
|
@@ -7165,9 +7165,11 @@ class ViewController: NSViewController {
|
|
|
applyFiltersButton.translatesAutoresizingMaskIntoConstraints = false
|
|
applyFiltersButton.translatesAutoresizingMaskIntoConstraints = false
|
|
|
applyFiltersButton.isBordered = false
|
|
applyFiltersButton.isBordered = false
|
|
|
applyFiltersButton.wantsLayer = true
|
|
applyFiltersButton.wantsLayer = true
|
|
|
- applyFiltersButton.layer?.backgroundColor = accentBlue.cgColor
|
|
|
|
|
|
|
+ applyFiltersButton.layer?.backgroundColor = palette.inputBackground.cgColor
|
|
|
applyFiltersButton.layer?.cornerRadius = 10
|
|
applyFiltersButton.layer?.cornerRadius = 10
|
|
|
- applyFiltersButton.contentTintColor = .white
|
|
|
|
|
|
|
+ applyFiltersButton.layer?.borderWidth = 1
|
|
|
|
|
+ applyFiltersButton.layer?.borderColor = palette.inputBorder.cgColor
|
|
|
|
|
+ applyFiltersButton.contentTintColor = primaryText
|
|
|
applyFiltersButton.font = .systemFont(ofSize: 13, weight: .semibold)
|
|
applyFiltersButton.font = .systemFont(ofSize: 13, weight: .semibold)
|
|
|
|
|
|
|
|
let resetFiltersButton = NSButton(title: "Reset", target: self, action: #selector(aiCompanionFilterResetTapped))
|
|
let resetFiltersButton = NSButton(title: "Reset", target: self, action: #selector(aiCompanionFilterResetTapped))
|
|
@@ -7428,8 +7430,12 @@ class ViewController: NSViewController {
|
|
|
if mgr.isRecording {
|
|
if mgr.isRecording {
|
|
|
aiCompanionStatusLabel?.stringValue =
|
|
aiCompanionStatusLabel?.stringValue =
|
|
|
"Recording… When you leave the meeting, click Stop & save (here or in the menu bar)."
|
|
"Recording… When you leave the meeting, click Stop & save (here or in the menu bar)."
|
|
|
|
|
+ aiCompanionStatusLabel?.font = .systemFont(ofSize: 12, weight: .regular)
|
|
|
|
|
+ aiCompanionStatusLabel?.textColor = secondaryText
|
|
|
} else {
|
|
} else {
|
|
|
aiCompanionStatusLabel?.stringValue = "Previous meetings"
|
|
aiCompanionStatusLabel?.stringValue = "Previous meetings"
|
|
|
|
|
+ aiCompanionStatusLabel?.font = .systemFont(ofSize: 15, weight: .semibold)
|
|
|
|
|
+ aiCompanionStatusLabel?.textColor = primaryText
|
|
|
}
|
|
}
|
|
|
refreshAiCompanionStatusPreferredMaxLayoutWidth()
|
|
refreshAiCompanionStatusPreferredMaxLayoutWidth()
|
|
|
}
|
|
}
|
|
@@ -7516,6 +7522,52 @@ class ViewController: NSViewController {
|
|
|
return .notRequested
|
|
return .notRequested
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private func aiCompanionCardDateLabel(for date: Date) -> String {
|
|
|
|
|
+ let formatter = DateFormatter()
|
|
|
|
|
+ formatter.locale = Locale(identifier: "en_GB")
|
|
|
|
|
+ formatter.dateFormat = "d-MMMM-yyyy"
|
|
|
|
|
+ return "Date: \(formatter.string(from: date))"
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private func aiCompanionCardSavedTimeLabel(for date: Date) -> String {
|
|
|
|
|
+ let formatter = DateFormatter()
|
|
|
|
|
+ formatter.locale = Locale(identifier: "en_US_POSIX")
|
|
|
|
|
+ formatter.dateStyle = .none
|
|
|
|
|
+ formatter.timeStyle = .short
|
|
|
|
|
+ return "Saved time: \(formatter.string(from: date))"
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private func localRecordingNotesStatusLabel(
|
|
|
|
|
+ for recording: MeetingRecordingManager.RecordedMeeting,
|
|
|
|
|
+ notesStatus: LocalRecordingNotesStatus
|
|
|
|
|
+ ) -> (text: String, color: NSColor) {
|
|
|
|
|
+ if aiCompanionTranscriptionActiveRecordingIDs.contains(recording.id) {
|
|
|
|
|
+ return ("Generating transcript…", secondaryText)
|
|
|
|
|
+ }
|
|
|
|
|
+ if let err = recording.transcriptError?.trimmingCharacters(in: .whitespacesAndNewlines), err.isEmpty == false {
|
|
|
|
|
+ return ("Transcript unavailable", NSColor.systemRed)
|
|
|
|
|
+ }
|
|
|
|
|
+ switch notesStatus {
|
|
|
|
|
+ case .notRequested:
|
|
|
|
|
+ let hasTranscript = recording.transcriptText?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false
|
|
|
|
|
+ if hasTranscript {
|
|
|
|
|
+ return ("Notes not requested", secondaryText)
|
|
|
|
|
+ }
|
|
|
|
|
+ return ("Waiting for transcript…", secondaryText)
|
|
|
|
|
+ case .processing:
|
|
|
|
|
+ let msg = localRecordingNotesProgressByRecordingId[recording.id] ?? "Generating notes…"
|
|
|
|
|
+ return (msg, secondaryText)
|
|
|
|
|
+ case .ready:
|
|
|
|
|
+ return ("Notes ready", secondaryText)
|
|
|
|
|
+ case .failed:
|
|
|
|
|
+ let err = recording.notesError?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
|
|
|
+ if err.isEmpty {
|
|
|
|
|
+ return ("Notes could not be generated", NSColor.systemRed)
|
|
|
|
|
+ }
|
|
|
|
|
+ return (err, NSColor.systemRed)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
@MainActor
|
|
@MainActor
|
|
|
private func makeRecordedMeetingCard(recording: MeetingRecordingManager.RecordedMeeting, index: Int) -> NSView {
|
|
private func makeRecordedMeetingCard(recording: MeetingRecordingManager.RecordedMeeting, index: Int) -> NSView {
|
|
|
let card = NSView()
|
|
let card = NSView()
|
|
@@ -7526,152 +7578,83 @@ class ViewController: NSViewController {
|
|
|
card.layer?.borderColor = palette.inputBorder.cgColor
|
|
card.layer?.borderColor = palette.inputBorder.cgColor
|
|
|
card.layer?.backgroundColor = (palette.isDarkMode ? NSColor.white.withAlphaComponent(0.04) : NSColor.black.withAlphaComponent(0.03)).cgColor
|
|
card.layer?.backgroundColor = (palette.isDarkMode ? NSColor.white.withAlphaComponent(0.04) : NSColor.black.withAlphaComponent(0.03)).cgColor
|
|
|
|
|
|
|
|
- let title = makeLabel(recording.resolvedDisplayTitle, size: 14, color: primaryText, weight: .semibold, centered: false)
|
|
|
|
|
|
|
+ let title = makeLabel(recording.resolvedDisplayTitle, size: 15, color: primaryText, weight: .semibold, centered: false)
|
|
|
title.translatesAutoresizingMaskIntoConstraints = false
|
|
title.translatesAutoresizingMaskIntoConstraints = false
|
|
|
- title.maximumNumberOfLines = 1
|
|
|
|
|
|
|
+ title.maximumNumberOfLines = 2
|
|
|
title.lineBreakMode = .byTruncatingTail
|
|
title.lineBreakMode = .byTruncatingTail
|
|
|
|
|
|
|
|
- let dateFormatter = DateFormatter()
|
|
|
|
|
- dateFormatter.dateStyle = .medium
|
|
|
|
|
- dateFormatter.timeStyle = .short
|
|
|
|
|
- let dateText = "\(dateFormatter.string(from: recording.startedAt))"
|
|
|
|
|
- let dateLabel = makeLabel(dateText, size: 11, color: secondaryText, weight: .regular, centered: false)
|
|
|
|
|
|
|
+ let dateLabel = makeLabel(aiCompanionCardDateLabel(for: recording.startedAt), size: 12, color: secondaryText, weight: .regular, centered: false)
|
|
|
dateLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
dateLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
|
|
|
- let transcriptBtn = NSButton(title: "Transcript", target: self, action: #selector(recordingTranscriptTapped(_:)))
|
|
|
|
|
- transcriptBtn.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
- transcriptBtn.isBordered = false
|
|
|
|
|
- transcriptBtn.wantsLayer = true
|
|
|
|
|
- transcriptBtn.layer?.backgroundColor = (palette.isDarkMode ? NSColor.white.withAlphaComponent(0.08) : NSColor.black.withAlphaComponent(0.06)).cgColor
|
|
|
|
|
- transcriptBtn.layer?.cornerRadius = 10
|
|
|
|
|
- transcriptBtn.layer?.borderWidth = 1
|
|
|
|
|
- transcriptBtn.layer?.borderColor = palette.inputBorder.cgColor
|
|
|
|
|
- transcriptBtn.contentTintColor = primaryText
|
|
|
|
|
- transcriptBtn.font = .systemFont(ofSize: 12, weight: .semibold)
|
|
|
|
|
- transcriptBtn.tag = index
|
|
|
|
|
-
|
|
|
|
|
- let getNotesBtn = NSButton(title: "Get notes", target: self, action: #selector(recordingGetNotesTapped(_:)))
|
|
|
|
|
|
|
+ let savedTimeLabel = makeLabel(aiCompanionCardSavedTimeLabel(for: recording.endedAt), size: 12, color: secondaryText, weight: .regular, centered: false)
|
|
|
|
|
+ savedTimeLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+
|
|
|
|
|
+ let notesStatus = localRecordingNotesStatus(for: recording)
|
|
|
|
|
+ let hasTranscript = recording.transcriptText?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false
|
|
|
|
|
+ let transcriptionBusy = aiCompanionTranscriptionActiveRecordingIDs.contains(recording.id)
|
|
|
|
|
+ let notesReady = notesStatus == .ready
|
|
|
|
|
+ let getNotesTitle = notesReady ? "View Notes" : "Get Notes"
|
|
|
|
|
+ let getNotesBtn = NSButton(title: getNotesTitle, target: self, action: #selector(recordingGetNotesTapped(_:)))
|
|
|
getNotesBtn.translatesAutoresizingMaskIntoConstraints = false
|
|
getNotesBtn.translatesAutoresizingMaskIntoConstraints = false
|
|
|
getNotesBtn.isBordered = false
|
|
getNotesBtn.isBordered = false
|
|
|
getNotesBtn.wantsLayer = true
|
|
getNotesBtn.wantsLayer = true
|
|
|
- getNotesBtn.layer?.backgroundColor = (palette.isDarkMode ? NSColor.white.withAlphaComponent(0.08) : NSColor.black.withAlphaComponent(0.06)).cgColor
|
|
|
|
|
getNotesBtn.layer?.cornerRadius = 10
|
|
getNotesBtn.layer?.cornerRadius = 10
|
|
|
- getNotesBtn.layer?.borderWidth = 1
|
|
|
|
|
- getNotesBtn.layer?.borderColor = palette.inputBorder.cgColor
|
|
|
|
|
- getNotesBtn.contentTintColor = primaryText
|
|
|
|
|
- getNotesBtn.font = .systemFont(ofSize: 12, weight: .semibold)
|
|
|
|
|
|
|
+ getNotesBtn.font = .systemFont(ofSize: 13, weight: .semibold)
|
|
|
getNotesBtn.tag = index
|
|
getNotesBtn.tag = index
|
|
|
|
|
+ if let icon = NSImage(systemSymbolName: "note.text", accessibilityDescription: nil) {
|
|
|
|
|
+ let config = NSImage.SymbolConfiguration(pointSize: 12, weight: .semibold)
|
|
|
|
|
+ getNotesBtn.image = icon.withSymbolConfiguration(config)
|
|
|
|
|
+ getNotesBtn.imagePosition = .imageLeading
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- let notesStatus = localRecordingNotesStatus(for: recording)
|
|
|
|
|
- let hasTranscript = recording.transcriptText?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false
|
|
|
|
|
- let transcriptionBusy = aiCompanionTranscriptionActiveRecordingIDs.contains(recording.id)
|
|
|
|
|
let getNotesEnabled = (notesStatus != .processing)
|
|
let getNotesEnabled = (notesStatus != .processing)
|
|
|
- && (notesStatus == .ready || notesStatus == .failed || (hasTranscript && !transcriptionBusy))
|
|
|
|
|
|
|
+ && (notesReady || notesStatus == .failed || (hasTranscript && !transcriptionBusy))
|
|
|
getNotesBtn.isEnabled = getNotesEnabled
|
|
getNotesBtn.isEnabled = getNotesEnabled
|
|
|
|
|
+ if getNotesEnabled {
|
|
|
|
|
+ getNotesBtn.layer?.backgroundColor = accentBlue.cgColor
|
|
|
|
|
+ getNotesBtn.contentTintColor = .white
|
|
|
|
|
+ } else {
|
|
|
|
|
+ getNotesBtn.layer?.backgroundColor = (palette.isDarkMode ? NSColor.white.withAlphaComponent(0.08) : NSColor.black.withAlphaComponent(0.06)).cgColor
|
|
|
|
|
+ getNotesBtn.contentTintColor = secondaryText
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- let play = NSButton(title: recordedMeetingsPlayingID == recording.id ? "Stop" : "Play", target: self, action: #selector(recordingPlayTapped(_:)))
|
|
|
|
|
- play.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
- play.isBordered = false
|
|
|
|
|
- play.wantsLayer = true
|
|
|
|
|
- play.layer?.cornerRadius = 10
|
|
|
|
|
- play.layer?.backgroundColor = accentBlue.cgColor
|
|
|
|
|
- play.contentTintColor = .white
|
|
|
|
|
- play.font = .systemFont(ofSize: 12, weight: .semibold)
|
|
|
|
|
- play.tag = index
|
|
|
|
|
-
|
|
|
|
|
- let buttonsRow = NSStackView(views: [transcriptBtn, getNotesBtn, play])
|
|
|
|
|
- transcriptBtn.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
|
|
- getNotesBtn.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
|
|
- play.setContentHuggingPriority(.required, for: .horizontal)
|
|
|
|
|
- buttonsRow.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
- buttonsRow.orientation = .horizontal
|
|
|
|
|
- buttonsRow.alignment = .centerY
|
|
|
|
|
- buttonsRow.spacing = 8
|
|
|
|
|
|
|
+ let statusInfo = localRecordingNotesStatusLabel(for: recording, notesStatus: notesStatus)
|
|
|
|
|
+ let statusLabel = makeLabel(statusInfo.text, size: 12, color: statusInfo.color, weight: .regular, centered: false)
|
|
|
|
|
+ statusLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ statusLabel.maximumNumberOfLines = 3
|
|
|
|
|
+ statusLabel.lineBreakMode = .byWordWrapping
|
|
|
|
|
+ if let cell = statusLabel.cell as? NSTextFieldCell {
|
|
|
|
|
+ cell.wraps = true
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
card.addSubview(title)
|
|
card.addSubview(title)
|
|
|
card.addSubview(dateLabel)
|
|
card.addSubview(dateLabel)
|
|
|
- card.addSubview(buttonsRow)
|
|
|
|
|
-
|
|
|
|
|
- var lastStackBottom: NSView = dateLabel
|
|
|
|
|
-
|
|
|
|
|
- if aiCompanionTranscriptionActiveRecordingIDs.contains(recording.id) {
|
|
|
|
|
- let busy = makeLabel("Generating transcript…", size: 11, color: secondaryText, weight: .regular, centered: false)
|
|
|
|
|
- busy.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
- busy.maximumNumberOfLines = 1
|
|
|
|
|
- card.addSubview(busy)
|
|
|
|
|
- NSLayoutConstraint.activate([
|
|
|
|
|
- busy.topAnchor.constraint(equalTo: dateLabel.bottomAnchor, constant: 6),
|
|
|
|
|
- busy.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 12),
|
|
|
|
|
- busy.trailingAnchor.constraint(lessThanOrEqualTo: card.trailingAnchor, constant: -12)
|
|
|
|
|
- ])
|
|
|
|
|
- lastStackBottom = busy
|
|
|
|
|
- } else if let err = recording.transcriptError?.trimmingCharacters(in: .whitespacesAndNewlines), err.isEmpty == false {
|
|
|
|
|
- let errLabel = makeLabel("Transcript: \(err)", size: 11, color: NSColor.systemRed, weight: .regular, centered: false)
|
|
|
|
|
- errLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
- errLabel.maximumNumberOfLines = 2
|
|
|
|
|
- errLabel.lineBreakMode = .byWordWrapping
|
|
|
|
|
- if let cell = errLabel.cell as? NSTextFieldCell {
|
|
|
|
|
- cell.wraps = true
|
|
|
|
|
- }
|
|
|
|
|
- card.addSubview(errLabel)
|
|
|
|
|
- NSLayoutConstraint.activate([
|
|
|
|
|
- errLabel.topAnchor.constraint(equalTo: dateLabel.bottomAnchor, constant: 6),
|
|
|
|
|
- errLabel.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 12),
|
|
|
|
|
- errLabel.trailingAnchor.constraint(equalTo: card.trailingAnchor, constant: -12)
|
|
|
|
|
- ])
|
|
|
|
|
- lastStackBottom = errLabel
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if notesStatus == .processing {
|
|
|
|
|
- let msg = localRecordingNotesProgressByRecordingId[recording.id] ?? "Preparing notes…"
|
|
|
|
|
- let busyNotes = makeLabel("Notes: \(msg)", size: 11, color: secondaryText, weight: .regular, centered: false)
|
|
|
|
|
- busyNotes.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
- busyNotes.maximumNumberOfLines = 2
|
|
|
|
|
- card.addSubview(busyNotes)
|
|
|
|
|
- NSLayoutConstraint.activate([
|
|
|
|
|
- busyNotes.topAnchor.constraint(equalTo: lastStackBottom.bottomAnchor, constant: 6),
|
|
|
|
|
- busyNotes.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 12),
|
|
|
|
|
- busyNotes.trailingAnchor.constraint(lessThanOrEqualTo: card.trailingAnchor, constant: -12)
|
|
|
|
|
- ])
|
|
|
|
|
- lastStackBottom = busyNotes
|
|
|
|
|
- } else if notesStatus == .failed,
|
|
|
|
|
- let ne = recording.notesError?.trimmingCharacters(in: .whitespacesAndNewlines), ne.isEmpty == false {
|
|
|
|
|
- let notesErr = makeLabel("Notes: \(ne)", size: 11, color: NSColor.systemRed, weight: .regular, centered: false)
|
|
|
|
|
- notesErr.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
- notesErr.maximumNumberOfLines = 2
|
|
|
|
|
- notesErr.lineBreakMode = .byWordWrapping
|
|
|
|
|
- if let cell = notesErr.cell as? NSTextFieldCell {
|
|
|
|
|
- cell.wraps = true
|
|
|
|
|
- }
|
|
|
|
|
- card.addSubview(notesErr)
|
|
|
|
|
- NSLayoutConstraint.activate([
|
|
|
|
|
- notesErr.topAnchor.constraint(equalTo: lastStackBottom.bottomAnchor, constant: 6),
|
|
|
|
|
- notesErr.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 12),
|
|
|
|
|
- notesErr.trailingAnchor.constraint(equalTo: card.trailingAnchor, constant: -12)
|
|
|
|
|
- ])
|
|
|
|
|
- lastStackBottom = notesErr
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ card.addSubview(savedTimeLabel)
|
|
|
|
|
+ card.addSubview(getNotesBtn)
|
|
|
|
|
+ card.addSubview(statusLabel)
|
|
|
|
|
|
|
|
NSLayoutConstraint.activate([
|
|
NSLayoutConstraint.activate([
|
|
|
- title.topAnchor.constraint(equalTo: card.topAnchor, constant: 10),
|
|
|
|
|
- title.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 12),
|
|
|
|
|
- title.trailingAnchor.constraint(lessThanOrEqualTo: buttonsRow.leadingAnchor, constant: -10),
|
|
|
|
|
|
|
+ title.topAnchor.constraint(equalTo: card.topAnchor, constant: 14),
|
|
|
|
|
+ title.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 16),
|
|
|
|
|
+ title.trailingAnchor.constraint(equalTo: card.trailingAnchor, constant: -16),
|
|
|
|
|
|
|
|
- buttonsRow.centerYAnchor.constraint(equalTo: title.centerYAnchor),
|
|
|
|
|
- buttonsRow.trailingAnchor.constraint(equalTo: card.trailingAnchor, constant: -12),
|
|
|
|
|
|
|
+ dateLabel.topAnchor.constraint(equalTo: title.bottomAnchor, constant: 8),
|
|
|
|
|
+ dateLabel.leadingAnchor.constraint(equalTo: title.leadingAnchor),
|
|
|
|
|
+ dateLabel.trailingAnchor.constraint(equalTo: card.trailingAnchor, constant: -16),
|
|
|
|
|
|
|
|
- transcriptBtn.heightAnchor.constraint(equalToConstant: 30),
|
|
|
|
|
- transcriptBtn.widthAnchor.constraint(greaterThanOrEqualToConstant: 80),
|
|
|
|
|
- getNotesBtn.heightAnchor.constraint(equalToConstant: 30),
|
|
|
|
|
- getNotesBtn.widthAnchor.constraint(greaterThanOrEqualToConstant: 88),
|
|
|
|
|
- play.heightAnchor.constraint(equalToConstant: 30),
|
|
|
|
|
- play.widthAnchor.constraint(greaterThanOrEqualToConstant: 68),
|
|
|
|
|
|
|
+ savedTimeLabel.topAnchor.constraint(equalTo: dateLabel.bottomAnchor, constant: 4),
|
|
|
|
|
+ savedTimeLabel.leadingAnchor.constraint(equalTo: title.leadingAnchor),
|
|
|
|
|
+ savedTimeLabel.trailingAnchor.constraint(equalTo: card.trailingAnchor, constant: -16),
|
|
|
|
|
|
|
|
- dateLabel.topAnchor.constraint(equalTo: title.bottomAnchor, constant: 4),
|
|
|
|
|
- dateLabel.leadingAnchor.constraint(equalTo: title.leadingAnchor),
|
|
|
|
|
- dateLabel.trailingAnchor.constraint(equalTo: card.trailingAnchor, constant: -12),
|
|
|
|
|
|
|
+ getNotesBtn.topAnchor.constraint(equalTo: savedTimeLabel.bottomAnchor, constant: 14),
|
|
|
|
|
+ getNotesBtn.leadingAnchor.constraint(equalTo: title.leadingAnchor),
|
|
|
|
|
+ getNotesBtn.heightAnchor.constraint(equalToConstant: 34),
|
|
|
|
|
+ getNotesBtn.widthAnchor.constraint(greaterThanOrEqualToConstant: 130),
|
|
|
|
|
|
|
|
- lastStackBottom.bottomAnchor.constraint(equalTo: card.bottomAnchor, constant: -10)
|
|
|
|
|
|
|
+ statusLabel.topAnchor.constraint(equalTo: getNotesBtn.bottomAnchor, constant: 8),
|
|
|
|
|
+ statusLabel.leadingAnchor.constraint(equalTo: title.leadingAnchor),
|
|
|
|
|
+ statusLabel.trailingAnchor.constraint(equalTo: card.trailingAnchor, constant: -16),
|
|
|
|
|
+ statusLabel.bottomAnchor.constraint(equalTo: card.bottomAnchor, constant: -14)
|
|
|
])
|
|
])
|
|
|
|
|
|
|
|
return card
|
|
return card
|
|
@@ -7693,7 +7676,7 @@ class ViewController: NSViewController {
|
|
|
private func bodyTextForLocalRecordingNotes(recording: MeetingRecordingManager.RecordedMeeting) -> String {
|
|
private func bodyTextForLocalRecordingNotes(recording: MeetingRecordingManager.RecordedMeeting) -> String {
|
|
|
switch localRecordingNotesStatus(for: recording) {
|
|
switch localRecordingNotesStatus(for: recording) {
|
|
|
case .processing:
|
|
case .processing:
|
|
|
- return localRecordingNotesProgressByRecordingId[recording.id] ?? "Preparing notes…"
|
|
|
|
|
|
|
+ return localRecordingNotesProgressByRecordingId[recording.id] ?? "Generating notes…"
|
|
|
case .failed:
|
|
case .failed:
|
|
|
let err = recording.notesError?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
let err = recording.notesError?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
|
return err.isEmpty ? "Notes could not be generated." : "Notes unavailable.\n\n\(err)"
|
|
return err.isEmpty ? "Notes could not be generated." : "Notes unavailable.\n\n\(err)"
|
|
@@ -7857,7 +7840,7 @@ class ViewController: NSViewController {
|
|
|
private func startLocalRecordingNotesProcessing(recordingId: String, transcript: String, requestId: UUID, autoPresentOnReady: Bool) {
|
|
private func startLocalRecordingNotesProcessing(recordingId: String, transcript: String, requestId: UUID, autoPresentOnReady: Bool) {
|
|
|
localRecordingNotesTaskByRecordingId[recordingId]?.cancel()
|
|
localRecordingNotesTaskByRecordingId[recordingId]?.cancel()
|
|
|
localRecordingNotesRequestIdByRecordingId[recordingId] = requestId
|
|
localRecordingNotesRequestIdByRecordingId[recordingId] = requestId
|
|
|
- localRecordingNotesProgressByRecordingId[recordingId] = "Preparing notes, please wait..."
|
|
|
|
|
|
|
+ localRecordingNotesProgressByRecordingId[recordingId] = "Generating notes…"
|
|
|
MeetingRecordingManager.shared.setMeetingNotes(
|
|
MeetingRecordingManager.shared.setMeetingNotes(
|
|
|
recordingId: recordingId,
|
|
recordingId: recordingId,
|
|
|
notesStatusRaw: LocalRecordingNotesStatus.processing.rawValue,
|
|
notesStatusRaw: LocalRecordingNotesStatus.processing.rawValue,
|