Merge branch 'pr-177' into feature/clipboard

This is a trial merge
This commit is contained in:
Hariharan Mudaliar 2025-12-24 23:05:41 +05:30
commit 722363cdca
No known key found for this signature in database
GPG key ID: 025583EB0BE5640E
11 changed files with 1229 additions and 11 deletions

View file

@ -105,11 +105,13 @@ enum CalendarSelectionState: Codable, Defaults.Serializable {
enum ClipboardDisplayMode: String, CaseIterable, Codable, Defaults.Serializable {
case popover = "popover" // Traditional popover attached to button
case panel = "panel" // Floating panel near notch
case separateTab = "separateTab" // Separate tab in Dynamic Island
var displayName: String {
switch self {
case .popover: return "Popover"
case .panel: return "Panel"
case .separateTab: return "Separate Tab"
}
}
@ -117,6 +119,7 @@ enum ClipboardDisplayMode: String, CaseIterable, Codable, Defaults.Serializable
switch self {
case .popover: return "Shows clipboard as a dropdown attached to the clipboard button"
case .panel: return "Shows clipboard in a floating panel near the notch"
case .separateTab: return "Shows copied items in a separate tab within the Dynamic Island (merges with Notes if enabled)"
}
}
}
@ -377,6 +380,86 @@ struct AIModel: Codable, Identifiable, Defaults.Serializable {
}
}
struct NoteItem: Codable, Identifiable, Defaults.Serializable, Hashable {
var id: UUID = UUID()
var title: String
var content: String
var creationDate: Date
var colorIndex: Int // 0: Yellow, 1: Blue, 2: Red, 3: Green
var isPinned: Bool = false
var imageFileName: String? = nil // Store filename instead of raw data
// Internal property for migration
private enum CodingKeys: String, CodingKey {
case id, title, content, creationDate, colorIndex, isPinned, imageFileName, imageData
}
init(id: UUID = UUID(), title: String, content: String, creationDate: Date, colorIndex: Int, isPinned: Bool = false, imageFileName: String? = nil) {
self.id = id
self.title = title
self.content = content
self.creationDate = creationDate
self.colorIndex = colorIndex
self.isPinned = isPinned
self.imageFileName = imageFileName
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(UUID.self, forKey: .id)
title = try container.decode(String.self, forKey: .title)
content = try container.decode(String.self, forKey: .content)
creationDate = try container.decode(Date.self, forKey: .creationDate)
colorIndex = try container.decode(Int.self, forKey: .colorIndex)
isPinned = try container.decode(Bool.self, forKey: .isPinned)
// Migration logic: if imageData exists but imageFileName doesn't, save it to disk
if let data = try container.decodeIfPresent(Data.self, forKey: .imageData) {
let fileName = "note_image_\(id.uuidString).png"
let fileURL = NoteItem.noteImageDataDirectory.appendingPathComponent(fileName)
try? data.write(to: fileURL)
imageFileName = fileName
} else {
imageFileName = try container.decodeIfPresent(String.self, forKey: .imageFileName)
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(title, forKey: .title)
try container.encode(content, forKey: .content)
try container.encode(creationDate, forKey: .creationDate)
try container.encode(colorIndex, forKey: .colorIndex)
try container.encode(isPinned, forKey: .isPinned)
try container.encode(imageFileName, forKey: .imageFileName)
}
static let colors: [Color] = [.yellow, .blue, .red, .green, .purple, .orange]
// Directory for storing note image files
static let noteImageDataDirectory: URL = {
let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let notesDir = documentsPath.appendingPathComponent("NoteImages")
try? FileManager.default.createDirectory(at: notesDir, withIntermediateDirectories: true)
return notesDir
}()
var color: Color {
if colorIndex >= 0 && colorIndex < NoteItem.colors.count {
return NoteItem.colors[colorIndex]
}
return .yellow
}
// Helper to get image data from file
func getImageData() -> Data? {
guard let fileName = imageFileName else { return nil }
let fileURL = NoteItem.noteImageDataDirectory.appendingPathComponent(fileName)
return try? Data(contentsOf: fileURL)
}
}
extension Defaults.Keys {
// MARK: General
static let menubarIcon = Key<Bool>("menubarIcon", default: true)
@ -662,6 +745,15 @@ extension Defaults.Keys {
// MARK: Lyrics Feature
static let enableLyrics = Key<Bool>("enableLyrics", default: true)
// MARK: Notes Feature
static let enableNotes = Key<Bool>("enableNotes", default: false)
static let enableNotePinning = Key<Bool>("enableNotePinning", default: true)
static let enableNoteSearch = Key<Bool>("enableNoteSearch", default: false)
static let enableNoteColorFiltering = Key<Bool>("enableNoteColorFiltering", default: false)
static let enableCreateFromClipboard = Key<Bool>("enableCreateFromClipboard", default: true)
static let enableNoteCharCount = Key<Bool>("enableNoteCharCount", default: true)
static let savedNotes = Key<[NoteItem]>("savedNotes", default: [])
// Helper to determine the default media controller based on macOS version
static var defaultMediaController: MediaControllerType {
if #available(macOS 15.4, *) {

View file

@ -269,6 +269,10 @@ class DynamicIslandViewModel: NSObject, ObservableObject {
private func calculateDynamicNotchSize() -> CGSize {
// Use minimalistic size if minimalistic UI is enabled
let baseSize = Defaults[.enableMinimalisticUI] ? minimalisticOpenNotchSize : openNotchSize
if DynamicIslandViewCoordinator.shared.currentView == .notes {
return CGSize(width: baseSize.width, height: 360)
}
return statsAdjustedNotchSize(
from: baseSize,
isStatsTabActive: DynamicIslandViewCoordinator.shared.currentView == .stats,