mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-28 07:34:32 +00:00
Merge branch 'pr-177' into feature/clipboard
This is a trial merge
This commit is contained in:
commit
722363cdca
11 changed files with 1229 additions and 11 deletions
|
|
@ -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, *) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue