f527c63232
- FU-C 멀티파트 업로드(DSClient.uploadDocument + LiveDSClient 401 재시도 공유 + 툴바/상태바) - FU-D 네이티브 다운로드(NSSavePanel + URLSession, ?token= 미노출, 임시파일 정리) - 로그아웃(AppModel.logout 세션 전체 초기화 + 계정 메뉴) - 셸 2-column 재구성: 질문/이드 제거, 홈 코크핏 + 문서 3-pane 컬럼 브라우저 (인스펙터 TL;DR/핵심점/심층/불일치) + 도메인 필터 전체 load-all - 적대 리뷰 반영(stale 401 데모션·다운로드 임시파일 정리·메모 저장 saveMemo 경유·도메인 필터 선택 정합) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
76 lines
2.9 KiB
Swift
76 lines
2.9 KiB
Swift
import SwiftUI
|
|
import DSKit
|
|
|
|
/// Capture-first treatment: quick-capture box + list distinguishing note vs audio.
|
|
struct MemoListView: View {
|
|
@Environment(AppModel.self) private var model
|
|
@State private var draft: String = ""
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 0) {
|
|
HStack(spacing: 8) {
|
|
TextField("빠른 메모 캡처", text: $draft)
|
|
.textFieldStyle(.roundedBorder)
|
|
Button("저장") {
|
|
let content = draft
|
|
Task { if await model.saveMemo(content) { draft = "" } }
|
|
}
|
|
.buttonStyle(.bordered)
|
|
.disabled(draft.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
|
|
}
|
|
.padding(12)
|
|
|
|
let selection = Binding<Int?>(
|
|
get: { model.selectedMemoID },
|
|
set: { if let id = $0 { Task { await model.openMemo(id) } } }
|
|
)
|
|
List(model.memoList, selection: selection) { memo in
|
|
VStack(alignment: .leading, spacing: 3) {
|
|
HStack(spacing: 6) {
|
|
if memo.isAudio { Chip("음성", Sage.formatColor("audio")) }
|
|
if memo.aiEventKind == "task" { Chip("할 일", Sage.amber) }
|
|
Text(memo.title ?? "메모 \(memo.id)")
|
|
.font(.callout.weight(.medium)).foregroundStyle(Sage.ink).lineLimit(1)
|
|
Spacer()
|
|
if memo.isPinned { Text("고정").font(.caption2).foregroundStyle(Sage.amber) }
|
|
}
|
|
Text(memo.content ?? "").font(.caption).foregroundStyle(Sage.muted).lineLimit(2)
|
|
}
|
|
.padding(.vertical, 3)
|
|
}
|
|
.listStyle(.inset)
|
|
}
|
|
.background(Sage.surface)
|
|
}
|
|
}
|
|
|
|
struct MemoDetailView: View {
|
|
let memo: MemoResponse
|
|
|
|
var body: some View {
|
|
ScrollView {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
Text(memo.title ?? "메모").font(.title2.weight(.bold)).foregroundStyle(Sage.ink)
|
|
|
|
if memo.isAudio {
|
|
HStack(spacing: 8) {
|
|
Image(systemName: "waveform")
|
|
Text("원본 재생 (스캐폴드에서는 비활성)").foregroundStyle(Sage.muted)
|
|
}
|
|
.padding(10)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.background(Sage.surface, in: RoundedRectangle(cornerRadius: 8))
|
|
}
|
|
|
|
if let tags = memo.aiTags, !tags.isEmpty {
|
|
HStack(spacing: 6) { ForEach(tags, id: \.self) { Chip($0, Sage.brand) } }
|
|
}
|
|
|
|
MarkdownView(memo.content ?? "")
|
|
}
|
|
.padding(20)
|
|
}
|
|
.background(Sage.surface)
|
|
}
|
|
}
|