Files
hyungi_document_server/clients/ds-app/Sources/AppFeature/Pages/MemosView.swift
T
hyungi f527c63232 feat(ds-app): macOS 앱 마무리 — 업로드·다운로드·로그아웃 + 4섹션 페이지
- 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>
2026-06-15 14:52:29 +09:00

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)
}
}