Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,21 @@ public enum CommandPaletteCatalog {
isSignedIn: Bool,
isTrialing: Bool,
eventHits: [CommandPaletteEventHit],
recentIds: [String]
recentIds: [String],
tipsMuted: Bool,
levelHidden: Bool,
theme: CompassThemeName
) -> [CommandPaletteSection] {
var built: [CommandPaletteSection] = []

if let goToDate = goToDateItem(query: query, now: now) {
built.append(CommandPaletteSection(id: "go-to-date", heading: "", items: [goToDate]))
}

let navigation = navigationItems(view: view)
let navigation = navigationItems(
view: view,
tipsMuted: tipsMuted,
levelHidden: levelHidden)
if !navigation.isEmpty {
built.append(CommandPaletteSection(id: "navigation", heading: "Navigation", items: navigation))
}
Expand All @@ -43,7 +49,7 @@ public enum CommandPaletteCatalog {
}

built.append(contentsOf: eventActionSections())
built.append(contentsOf: moreSections(isTrialing: isTrialing))
built.append(contentsOf: moreSections(isTrialing: isTrialing, theme: theme))

var accountItems = [CommandPaletteItem(
id: "export-data",
Expand Down Expand Up @@ -89,45 +95,86 @@ public enum CommandPaletteCatalog {
}

private static func navigationItems(
view: CalendarGridView
view: CalendarGridView,
tipsMuted: Bool,
levelHidden: Bool
) -> [CommandPaletteItem] {
var items: [CommandPaletteItem] = [
CommandPaletteItem(
id: "today",
label: "Go to Today",
shortcut: "t",
keywords: ["now", "current", "jump"]),
keywords: ["now", "current", "jump"],
systemImage: "arrow.uturn.backward"),
]
if view != .day {
items.append(
CommandPaletteItem(
id: "go-to-day",
label: "Go to Day view",
shortcut: "d",
keywords: ["day view", "daily"]))
keywords: ["day view", "daily"],
systemImage: "calendar"))
}
if view != .week {
items.append(
CommandPaletteItem(
id: "go-to-week",
label: "Go to Week view",
shortcut: "w",
keywords: ["week view", "weekly"]))
keywords: ["week view", "weekly"],
systemImage: "calendar.day.timeline.left"))
}
if view != .life {
items.append(
CommandPaletteItem(
id: "go-to-life",
label: "Go to Life view",
shortcut: "l",
keywords: ["life view", "years"]))
keywords: ["life view", "years"],
systemImage: "hourglass"))
}
items.append(
CommandPaletteItem(
id: "show-shortcuts",
label: "Show shortcuts",
shortcut: "?",
keywords: ["keyboard", "legend", "help"]))
keywords: ["keyboard", "legend", "help"],
systemImage: "keyboard"))
items.append(
CommandPaletteItem(
id: "practice-shortcuts",
label: "Play Block Party (practice shortcuts)",
keywords: [
"onboarding", "tour", "intro", "help", "tutorial", "coach", "sandbox",
"practice", "shortcuts", "game", "play", "block party",
],
systemImage: "safari"))
items.append(
CommandPaletteItem(
id: "show-welcome-guide",
label: "Show welcome guide",
keywords: ["onboarding", "tour", "intro", "help", "faq"],
systemImage: "safari"))
items.append(
CommandPaletteItem(
id: "toggle-shortcut-tips",
label: tipsMuted ? "Show shortcut tips" : "Hide shortcut tips",
keywords: ["sidebar", "hint", "tips", "teaching", "shortcuts", "keyboard"],
systemImage: tipsMuted ? "eye" : "eye.slash"))
items.append(
CommandPaletteItem(
id: "toggle-shortcut-level",
label: levelHidden ? "Show shortcut level" : "Hide shortcut level",
keywords: ["level", "progress", "badge", "shortcuts", "keyboard"],
systemImage: levelHidden ? "eye" : "eye.slash"))
items.append(
CommandPaletteItem(
id: "toggle-sidebar",
label: "Toggle sidebar",
shortcut: "]",
keywords: ["panel", "hide sidebar", "show sidebar"],
systemImage: "sidebar.left"))
return items
}

Expand All @@ -142,7 +189,8 @@ public enum CommandPaletteCatalog {
id: "recent-" + id,
label: item.label,
shortcut: item.shortcut,
keywords: item.keywords)
keywords: item.keywords,
systemImage: item.systemImage)
}
}

Expand All @@ -167,8 +215,10 @@ public enum CommandPaletteCatalog {
}

private static func moreSections(
isTrialing: Bool
isTrialing: Bool,
theme: CompassThemeName
) -> [CommandPaletteSection] {
let isDark = theme == .darkAbyss
var items: [CommandPaletteItem] = [
CommandPaletteItem(
id: "undo",
Expand All @@ -181,25 +231,21 @@ public enum CommandPaletteCatalog {
shortcut: "mod+shift+z",
keywords: ["repeat", "forward"]),
CommandPaletteItem(
id: "show-welcome-guide",
label: "Show welcome guide",
keywords: ["onboarding", "tour", "intro", "help", "faq"]),
CommandPaletteItem(
id: "practice-shortcuts",
label: "Play Block Party (practice shortcuts)",
keywords: [
"onboarding", "tour", "intro", "help", "tutorial", "coach", "sandbox",
"practice", "shortcuts", "game", "play", "block party",
]),
id: "toggle-theme",
label: isDark ? "Switch to light theme" : "Switch to dark theme",
keywords: ["theme", "dark mode", "light mode", "appearance"],
systemImage: isDark ? "sun.max" : "moon.stars"),
CommandPaletteItem(
id: "open-settings",
label: "Settings",
shortcut: ",",
keywords: ["preferences", "account"]),
keywords: ["preferences", "account"],
systemImage: "gearshape"),
CommandPaletteItem(
id: "about",
label: "About Compass",
keywords: ["version", "info"]),
keywords: ["version", "info"],
systemImage: "info.circle"),
]
if isTrialing {
items.insert(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,22 @@ public struct CommandPaletteItem: Identifiable, Hashable, Sendable {
public let shortcut: String?
public let keywords: [String]
public let subtitle: String?
public let systemImage: String?

public init(
id: String,
label: String,
shortcut: String? = nil,
keywords: [String] = [],
subtitle: String? = nil
subtitle: String? = nil,
systemImage: String? = nil
) {
self.id = id
self.label = label
self.shortcut = shortcut
self.keywords = keywords
self.subtitle = subtitle
self.systemImage = systemImage
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ extension NativeCalendarRootModel {
isSignedIn: isSignedIn,
isTrialing: billingStore.status?.subscriptionStatus == .trialing,
eventHits: mergedHits,
recentIds: commandPaletteStore.recentCommandIds)
recentIds: commandPaletteStore.recentCommandIds,
tipsMuted: levelsStore.tipsMuted,
levelHidden: levelsStore.badgeHidden,
theme: settingsStore.theme)
}

public func filteredPaletteSections() -> [CommandPaletteSection] {
Expand Down Expand Up @@ -114,12 +117,22 @@ extension NativeCalendarRootModel {
handleShortcut(.navLifeView)
case "show-shortcuts":
shortcutsLegendStore.open()
case "toggle-theme":
let next: CompassThemeName =
settingsStore.theme == .darkAbyss ? .lightBeach : .darkAbyss
settingsStore.setTheme(next)
case "open-settings":
handleShortcut(.otherSettings)
case "practice-shortcuts":
blockPartyStore.replayFromPalette()
case "show-welcome-guide":
onboardingStore.openWelcomeGuide()
case "toggle-shortcut-tips":
levelsStore.setTipsMuted(!levelsStore.tipsMuted)
case "toggle-shortcut-level":
levelsStore.setBadgeHidden(!levelsStore.badgeHidden)
case "toggle-sidebar":
handleShortcut(.otherSidebar)
case "sign-up":
authStore.openModal(.signUp)
case "log-in":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ extension NativeCalendarRootModel {
func draftTargetDay() -> Date {
let calendar = EffectiveTimeZone.calendar
let now = referenceNow
let jumpColumnDay = QuickTime.quickTimeFocusedColumnDay(
activeDayKeys: eventJumpSelection.activeDayKeys)
let focusedDay: Date? = {
if let jumpColumnDay { return jumpColumnDay }
guard let focusedId = focusStore.focusedEventId?.rawValue,
let event = loadedEvents.first(where: { $0.id.rawValue == focusedId })
else { return nil }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public struct LevelUpToastSnapshot: Hashable, Sendable, Identifiable {
public final class LevelsStore {
public private(set) var levelSnapshot: ShortcutLevelSnapshot
public private(set) var badgeHidden: Bool
public private(set) var tipsMuted: Bool
public private(set) var badgePulsing = false
public private(set) var currentTip: ShortcutTipSnapshot?
public private(set) var pendingLevelUpToast: LevelUpToastSnapshot?
Expand All @@ -63,6 +64,7 @@ public final class LevelsStore {
let initialUsedIds = ShortcutUsagePersistence.usedShortcutIds(from: profile)
usedIds = initialUsedIds
badgeHidden = defaults.bool(forKey: ShortcutUsageStorageKeys.levelHidden)
tipsMuted = defaults.bool(forKey: ShortcutUsageStorageKeys.tipsMuted)
tipRotationIndex = defaults.integer(forKey: ShortcutUsageStorageKeys.tipRotationIndex)
levelSnapshot = ShortcutLevelMath.compute(
usedIds: initialUsedIds,
Expand Down Expand Up @@ -104,6 +106,17 @@ public final class LevelsStore {
advanceTipRotation()
}

public func setTipsMuted(_ muted: Bool) {
guard muted != tipsMuted else { return }
tipsMuted = muted
defaults.set(muted, forKey: ShortcutUsageStorageKeys.tipsMuted)
if muted {
currentTip = nil
} else {
refreshTip()
}
}

public func setBadgeHidden(_ hidden: Bool) {
guard hidden != badgeHidden else { return }
badgeHidden = hidden
Expand Down Expand Up @@ -176,7 +189,7 @@ public final class LevelsStore {
}

private func refreshTip() {
guard !badgeHidden else {
guard !tipsMuted, !badgeHidden else {
currentTip = nil
return
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Foundation
enum ShortcutUsageStorageKeys {
static let personalization = "compass.shortcuts.personalization"
static let levelHidden = "compass.shortcuts.level-hidden"
static let tipsMuted = "compass.shortcuts.tips-muted"
static let levelCelebrated = "compass.shortcuts.level-celebrated"
static let tipRotationIndex = "compass.shortcuts.tip-rotation-index"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,14 @@ final class CommandPaletteActionsTests: XCTestCase {
let ids = Set(model.filteredPaletteSections().flatMap(\.items).map(\.id))
XCTAssertTrue(ids.contains("undo"))
XCTAssertTrue(ids.contains("redo"))
XCTAssertTrue(ids.contains("toggle-theme"))
}

func testToggleThemeFlipsStoredTheme() async throws {
let model = try await makeModel()
let initial = model.settingsStore.theme
model.runPaletteCommand(id: "toggle-theme")
XCTAssertNotEqual(model.settingsStore.theme, initial)
}

func testAboutInvokesNativePresentation() async throws {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,21 @@ final class NativeCalendarRootModelEventJumpTests: XCTestCase {
model.handleShiftDayJump(letter: "A")
XCTAssertEqual(model.eventJumpSelection.announcement, "Saturday selected")
}

func testQuickTimeUsesJumpSelectedDay() async throws {
let fixture = try DemoSeedFixture.load()
let environment = try NativeCalendarEnvironment(inMemoryDatabase: true)
let model = NativeCalendarRootModel(environment: environment, demoPresentation: fixture)
await model.start()
model.viewStore.view = .week
model.handleShiftDayJump(letter: "M")
model.handleQuickTimeDigit("1")
model.handleQuickTimeDigit("1")
model.commitQuickTimeIfBuffered()
let draft = try XCTUnwrap(model.draftStore.gridDraft)
let start = try XCTUnwrap(draft.schedule.start)
let calendar = EffectiveTimeZone.calendar
XCTAssertEqual(calendar.component(.hour, from: start), 11)
XCTAssertEqual(CompassDateParsing.formatCalendarDay(start), model.eventJumpSelection.activeDayKeys.first)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ final class QuickTimeTests: XCTestCase {
EffectiveTimeZone.identifier = "UTC"
}

func testFocusedColumnDayFromJumpSelection() {
let day = QuickTime.quickTimeFocusedColumnDay(activeDayKeys: ["2026-05-18"])
XCTAssertNotNil(day)
XCTAssertEqual(CompassDateParsing.formatCalendarDay(day!), "2026-05-18")
XCTAssertNil(QuickTime.quickTimeFocusedColumnDay(activeDayKeys: []))
}

func testResolveNoonFromTwoDigits() {
let day = CompassDateParsing.calendarDateInEffectiveTimeZone("2026-05-20")!
let start = QuickTime.resolveQuickTimeStart(digits: "12", targetDay: day)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ public enum QuickTime {
digits.count < maxDigits
}

/// Jump-selected or parked-click day column (web `quickTimeFocusedColumnDay`).
public static func quickTimeFocusedColumnDay(activeDayKeys: [String]) -> Date? {
guard activeDayKeys.count == 1,
let key = activeDayKeys.first,
let date = CompassDateParsing.calendarDateInEffectiveTimeZone(key)
else { return nil }
return EffectiveTimeZone.calendar.startOfDay(for: date)
}

public static func quickTimeTargetDay(
startOfView: Date,
endOfView: Date,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,13 @@ public struct CommandPaletteOverlay: View {
Button {
model.runPaletteCommand(id: item.id)
} label: {
HStack {
HStack(spacing: 10) {
if let systemImage = item.systemImage {
Image(systemName: systemImage)
.font(.system(size: 14, weight: .medium))
.foregroundStyle(theme.textMutedColor)
.frame(width: 18, alignment: .center)
}
VStack(alignment: .leading, spacing: 2) {
CommandPaletteHighlightedLabel(label: item.label, query: query)
if let subtitle = item.subtitle {
Expand Down
Loading
Loading