|
@@ -37,6 +37,7 @@ class ViewController: NSViewController {
|
|
|
case meetings = "Meetings"
|
|
case meetings = "Meetings"
|
|
|
case scheduler = "Scheduler"
|
|
case scheduler = "Scheduler"
|
|
|
case widgets = "Widgets"
|
|
case widgets = "Widgets"
|
|
|
|
|
+ case aiCompanion = "Ai Companion"
|
|
|
case settings = "Settings"
|
|
case settings = "Settings"
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -307,6 +308,7 @@ class ViewController: NSViewController {
|
|
|
private weak var schedulerTodayButton: NSButton?
|
|
private weak var schedulerTodayButton: NSButton?
|
|
|
private weak var meetingsPageRootView: NSView?
|
|
private weak var meetingsPageRootView: NSView?
|
|
|
private weak var widgetsPageRootView: NSView?
|
|
private weak var widgetsPageRootView: NSView?
|
|
|
|
|
+ private weak var aiCompanionPageRootView: NSView?
|
|
|
private weak var meetingsPageCategoryFilter: NSPopUpButton?
|
|
private weak var meetingsPageCategoryFilter: NSPopUpButton?
|
|
|
private weak var meetingsPageFromDatePicker: NSDatePicker?
|
|
private weak var meetingsPageFromDatePicker: NSDatePicker?
|
|
|
private weak var meetingsPageToDatePicker: NSDatePicker?
|
|
private weak var meetingsPageToDatePicker: NSDatePicker?
|
|
@@ -729,6 +731,7 @@ class ViewController: NSViewController {
|
|
|
meetingSearchEmptyLabel = nil
|
|
meetingSearchEmptyLabel = nil
|
|
|
meetingsPageRootView = nil
|
|
meetingsPageRootView = nil
|
|
|
widgetsPageRootView = nil
|
|
widgetsPageRootView = nil
|
|
|
|
|
+ aiCompanionPageRootView = nil
|
|
|
homeView?.removeFromSuperview()
|
|
homeView?.removeFromSuperview()
|
|
|
if let profileName = profile?.name?.trimmingCharacters(in: .whitespacesAndNewlines), profileName.isEmpty == false {
|
|
if let profileName = profile?.name?.trimmingCharacters(in: .whitespacesAndNewlines), profileName.isEmpty == false {
|
|
|
topBarProfileInitial = String(profileName.prefix(1)).uppercased()
|
|
topBarProfileInitial = String(profileName.prefix(1)).uppercased()
|
|
@@ -5191,7 +5194,7 @@ class ViewController: NSViewController {
|
|
|
homeSidebarRowViews = [:]
|
|
homeSidebarRowViews = [:]
|
|
|
homeSidebarIconViews = [:]
|
|
homeSidebarIconViews = [:]
|
|
|
homeSidebarLabels = [:]
|
|
homeSidebarLabels = [:]
|
|
|
- let sidebar = makeSidebar(items: ["Home", "Meetings", "Scheduler", "Widgets", "Settings"], selected: selectedHomeSidebarItem, style: .home)
|
|
|
|
|
|
|
+ let sidebar = makeSidebar(items: ["Home", "Meetings", "Scheduler", "Widgets", "Ai Companion", "Settings"], selected: selectedHomeSidebarItem, style: .home)
|
|
|
let content = NSView()
|
|
let content = NSView()
|
|
|
content.wantsLayer = true
|
|
content.wantsLayer = true
|
|
|
content.layer?.backgroundColor = NSColor.clear.cgColor
|
|
content.layer?.backgroundColor = NSColor.clear.cgColor
|
|
@@ -5403,6 +5406,8 @@ class ViewController: NSViewController {
|
|
|
meetingsPageView.isHidden = selectedHomeSidebarItem != "Meetings"
|
|
meetingsPageView.isHidden = selectedHomeSidebarItem != "Meetings"
|
|
|
let widgetsPageView = makeWidgetsPageView()
|
|
let widgetsPageView = makeWidgetsPageView()
|
|
|
widgetsPageView.isHidden = selectedHomeSidebarItem != "Widgets"
|
|
widgetsPageView.isHidden = selectedHomeSidebarItem != "Widgets"
|
|
|
|
|
+ let aiCompanionPageView = makeAiCompanionPageView()
|
|
|
|
|
+ aiCompanionPageView.isHidden = selectedHomeSidebarItem != HomeSection.aiCompanion.rawValue
|
|
|
|
|
|
|
|
let searchOverlay = NSView()
|
|
let searchOverlay = NSView()
|
|
|
searchOverlay.wantsLayer = true
|
|
searchOverlay.wantsLayer = true
|
|
@@ -5674,7 +5679,7 @@ class ViewController: NSViewController {
|
|
|
[searchIcon, searchField, searchHintLabel].forEach {
|
|
[searchIcon, searchField, searchHintLabel].forEach {
|
|
|
searchPill.addSubview($0)
|
|
searchPill.addSubview($0)
|
|
|
}
|
|
}
|
|
|
- [timeTitle, dateTitle, actions, panel, panelHeader, meetingsStatus, meetingsDayNav, noMeeting, meetingsScrollView, refreshMeetingsButton, placeholder, settingsView, meetingsPageView, widgetsPageView, schedulerRoot].forEach {
|
|
|
|
|
|
|
+ [timeTitle, dateTitle, actions, panel, panelHeader, meetingsStatus, meetingsDayNav, noMeeting, meetingsScrollView, refreshMeetingsButton, placeholder, settingsView, meetingsPageView, widgetsPageView, aiCompanionPageView, schedulerRoot].forEach {
|
|
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
|
|
mainContentColumn.addSubview($0)
|
|
mainContentColumn.addSubview($0)
|
|
|
}
|
|
}
|
|
@@ -5830,6 +5835,11 @@ class ViewController: NSViewController {
|
|
|
widgetsPageView.trailingAnchor.constraint(equalTo: mainContentColumn.trailingAnchor),
|
|
widgetsPageView.trailingAnchor.constraint(equalTo: mainContentColumn.trailingAnchor),
|
|
|
widgetsPageView.bottomAnchor.constraint(equalTo: mainContentColumn.bottomAnchor),
|
|
widgetsPageView.bottomAnchor.constraint(equalTo: mainContentColumn.bottomAnchor),
|
|
|
|
|
|
|
|
|
|
+ aiCompanionPageView.topAnchor.constraint(equalTo: mainContentColumn.topAnchor),
|
|
|
|
|
+ aiCompanionPageView.leadingAnchor.constraint(equalTo: mainContentColumn.leadingAnchor),
|
|
|
|
|
+ aiCompanionPageView.trailingAnchor.constraint(equalTo: mainContentColumn.trailingAnchor),
|
|
|
|
|
+ aiCompanionPageView.bottomAnchor.constraint(equalTo: mainContentColumn.bottomAnchor),
|
|
|
|
|
+
|
|
|
schedulerRoot.topAnchor.constraint(equalTo: mainContentColumn.topAnchor, constant: 8),
|
|
schedulerRoot.topAnchor.constraint(equalTo: mainContentColumn.topAnchor, constant: 8),
|
|
|
schedulerRoot.leadingAnchor.constraint(equalTo: mainContentColumn.leadingAnchor, constant: 8),
|
|
schedulerRoot.leadingAnchor.constraint(equalTo: mainContentColumn.leadingAnchor, constant: 8),
|
|
|
schedulerRoot.trailingAnchor.constraint(equalTo: mainContentColumn.trailingAnchor, constant: -8),
|
|
schedulerRoot.trailingAnchor.constraint(equalTo: mainContentColumn.trailingAnchor, constant: -8),
|
|
@@ -5940,6 +5950,7 @@ class ViewController: NSViewController {
|
|
|
homeSettingsHostColumn = mainContentColumn
|
|
homeSettingsHostColumn = mainContentColumn
|
|
|
meetingsPageRootView = meetingsPageView
|
|
meetingsPageRootView = meetingsPageView
|
|
|
widgetsPageRootView = widgetsPageView
|
|
widgetsPageRootView = widgetsPageView
|
|
|
|
|
+ aiCompanionPageRootView = aiCompanionPageView
|
|
|
schedulerRootView = schedulerRoot
|
|
schedulerRootView = schedulerRoot
|
|
|
self.schedulerMonthLabel = schedulerMonthLabel
|
|
self.schedulerMonthLabel = schedulerMonthLabel
|
|
|
schedulerCalendarGridStack = schedulerCalendarGrid
|
|
schedulerCalendarGridStack = schedulerCalendarGrid
|
|
@@ -6113,6 +6124,37 @@ class ViewController: NSViewController {
|
|
|
return host
|
|
return host
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private func makeAiCompanionPageView() -> NSView {
|
|
|
|
|
+ let root = NSView()
|
|
|
|
|
+ root.wantsLayer = true
|
|
|
|
|
+ root.layer?.backgroundColor = NSColor.clear.cgColor
|
|
|
|
|
+
|
|
|
|
|
+ let container = NSView()
|
|
|
|
|
+ container.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ container.wantsLayer = true
|
|
|
|
|
+ container.layer?.backgroundColor = secondaryCardBackground.withAlphaComponent(0.94).cgColor
|
|
|
|
|
+ container.layer?.cornerRadius = 16
|
|
|
|
|
+ container.layer?.borderWidth = 1
|
|
|
|
|
+ container.layer?.borderColor = palette.inputBorder.cgColor
|
|
|
|
|
+ root.addSubview(container)
|
|
|
|
|
+
|
|
|
|
|
+ let title = makeLabel("Ai Companion", size: 28, color: primaryText, weight: .bold, centered: false)
|
|
|
|
|
+ title.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ container.addSubview(title)
|
|
|
|
|
+
|
|
|
|
|
+ NSLayoutConstraint.activate([
|
|
|
|
|
+ container.topAnchor.constraint(equalTo: root.topAnchor, constant: 8),
|
|
|
|
|
+ container.leadingAnchor.constraint(equalTo: root.leadingAnchor, constant: 8),
|
|
|
|
|
+ container.trailingAnchor.constraint(equalTo: root.trailingAnchor, constant: -8),
|
|
|
|
|
+ container.bottomAnchor.constraint(equalTo: root.bottomAnchor, constant: -8),
|
|
|
|
|
+
|
|
|
|
|
+ title.topAnchor.constraint(equalTo: container.topAnchor, constant: 20),
|
|
|
|
|
+ title.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 20),
|
|
|
|
|
+ title.trailingAnchor.constraint(lessThanOrEqualTo: container.trailingAnchor, constant: -20)
|
|
|
|
|
+ ])
|
|
|
|
|
+ return root
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
@MainActor
|
|
@MainActor
|
|
|
private func refreshWidgetsPagePremiumState() {
|
|
private func refreshWidgetsPagePremiumState() {
|
|
|
guard let host = widgetsPageRootView as? NSHostingView<WidgetsRootView> else { return }
|
|
guard let host = widgetsPageRootView as? NSHostingView<WidgetsRootView> else { return }
|
|
@@ -6703,6 +6745,7 @@ class ViewController: NSViewController {
|
|
|
let isSettings = selectedHomeSidebarItem == HomeSection.settings.rawValue
|
|
let isSettings = selectedHomeSidebarItem == HomeSection.settings.rawValue
|
|
|
let isScheduler = selectedHomeSidebarItem == HomeSection.scheduler.rawValue
|
|
let isScheduler = selectedHomeSidebarItem == HomeSection.scheduler.rawValue
|
|
|
let isWidgets = selectedHomeSidebarItem == HomeSection.widgets.rawValue
|
|
let isWidgets = selectedHomeSidebarItem == HomeSection.widgets.rawValue
|
|
|
|
|
+ let isAiCompanion = selectedHomeSidebarItem == HomeSection.aiCompanion.rawValue
|
|
|
let premiumLocked = isUserLoggedIn() && (storeKitCoordinator.hasPremiumAccess == false)
|
|
let premiumLocked = isUserLoggedIn() && (storeKitCoordinator.hasPremiumAccess == false)
|
|
|
homeWelcomeLabel?.stringValue = "Home"
|
|
homeWelcomeLabel?.stringValue = "Home"
|
|
|
homeWelcomeLabel?.isHidden = (isHome == false) || isSettings || isMeetings
|
|
homeWelcomeLabel?.isHidden = (isHome == false) || isSettings || isMeetings
|
|
@@ -6720,7 +6763,7 @@ class ViewController: NSViewController {
|
|
|
meetingsScrollView,
|
|
meetingsScrollView,
|
|
|
refreshMeetingsButton
|
|
refreshMeetingsButton
|
|
|
]
|
|
]
|
|
|
- let hideDashboard = isHome == false || isSettings || isMeetings || isScheduler || isWidgets
|
|
|
|
|
|
|
+ let hideDashboard = isHome == false || isSettings || isMeetings || isScheduler || isWidgets || isAiCompanion
|
|
|
dashboardViews.forEach { $0?.isHidden = hideDashboard }
|
|
dashboardViews.forEach { $0?.isHidden = hideDashboard }
|
|
|
// Do not toggle emptyMeetingLabel with other dashboard views — that overrode applyFilteredMeetings()
|
|
// Do not toggle emptyMeetingLabel with other dashboard views — that overrode applyFilteredMeetings()
|
|
|
// and showed "No meetings…" on top of meeting cards when returning to Home.
|
|
// and showed "No meetings…" on top of meeting cards when returning to Home.
|
|
@@ -6734,6 +6777,7 @@ class ViewController: NSViewController {
|
|
|
meetingsPageRootView?.isHidden = isMeetings == false
|
|
meetingsPageRootView?.isHidden = isMeetings == false
|
|
|
schedulerRootView?.isHidden = isScheduler == false
|
|
schedulerRootView?.isHidden = isScheduler == false
|
|
|
widgetsPageRootView?.isHidden = isWidgets == false
|
|
widgetsPageRootView?.isHidden = isWidgets == false
|
|
|
|
|
+ aiCompanionPageRootView?.isHidden = isAiCompanion == false
|
|
|
|
|
|
|
|
if isHome {
|
|
if isHome {
|
|
|
homePlaceholderLabel?.isHidden = true
|
|
homePlaceholderLabel?.isHidden = true
|
|
@@ -6765,6 +6809,8 @@ class ViewController: NSViewController {
|
|
|
return "calendar.badge.clock"
|
|
return "calendar.badge.clock"
|
|
|
case "Widgets":
|
|
case "Widgets":
|
|
|
return filled ? "rectangle.grid.2x2.fill" : "rectangle.grid.2x2"
|
|
return filled ? "rectangle.grid.2x2.fill" : "rectangle.grid.2x2"
|
|
|
|
|
+ case "Ai Companion":
|
|
|
|
|
+ return filled ? "sparkles" : "sparkles"
|
|
|
case "Settings":
|
|
case "Settings":
|
|
|
// `gearshape.fill` may not exist on all macOS versions; handled via safe image assignment.
|
|
// `gearshape.fill` may not exist on all macOS versions; handled via safe image assignment.
|
|
|
return filled ? "gearshape.fill" : "gearshape"
|
|
return filled ? "gearshape.fill" : "gearshape"
|