/
KellerDmitry
/
KellerNotch
Обзор
Документация
Войти
/
KellerDmitry
/
KellerNotch
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
Sources/NotesView.swift
116 строк
3 KB
Keller Dmitry
feat: Implement Clipboard Management System with Notch Panel UI
06 авг 2026, 15:57
06 авг 2026, 15:57
bcecb15
Код
Авторство
О чём код?
import SwiftUI struct Note: Identifiable, Codable, Hashable { var id = UUID() var text = "" var date = Date.now var title: String { let first = text.split(separator: "\n", maxSplits: 1).first.map(String.init) ?? "" return first.isEmpty ? "Без названия" : first } } @MainActor @Observable final class NotesStore { var notes: [Note] = JSONStore.load([Note].self, from: "notes.json") ?? [] private var saveTask: Task<Void, Never>? func add() -> Note { let note = Note() notes.insert(note, at: 0) flush() return note } func delete(_ note: Note) { notes.removeAll { $0.id == note.id } flush() } /// Печатаем — не пишем файл на каждую букву. func scheduleSave() { saveTask?.cancel() saveTask = Task { try? await Task.sleep(for: .seconds(1)) guard !Task.isCancelled else { return } flush() } } func flush() { saveTask?.cancel() saveTask = nil JSONStore.save(notes, to: "notes.json") } } struct NotesView: View { @Bindable var store: NotesStore @State private var selection: Note.ID? private var selectedIndex: Int? { store.notes.firstIndex { $0.id == selection } } var body: some View { HSplitView { list .frame(minWidth: 170, idealWidth: 190, maxWidth: 240) editor .frame(maxWidth: .infinity, maxHeight: .infinity) } } private var list: some View { VStack(spacing: 0) { HStack { Text("Заметки").font(.headline) Spacer() Button { selection = store.add().id } label: { Image(systemName: "square.and.pencil") } .buttonStyle(.plain) } .padding(.horizontal, 10) .padding(.vertical, 6) List(store.notes, selection: $selection) { note in VStack(alignment: .leading, spacing: 2) { Text(note.title).lineLimit(1) Text(note.date, format: .dateTime.day().month().hour().minute()) .font(.caption2) .foregroundStyle(.secondary) } .tag(note.id) .contextMenu { Button("Удалить", role: .destructive) { store.delete(note) } } } .scrollContentBackground(.hidden) } } @ViewBuilder private var editor: some View { if let index = selectedIndex { TextEditor(text: Binding( get: { store.notes[index].text }, set: { store.notes[index].text = $0 store.notes[index].date = .now store.scheduleSave() } )) .font(.body) .scrollContentBackground(.hidden) .padding(8) } else { ContentUnavailableView("Выбери заметку", systemImage: "note.text") } } }