Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions Sources/DevClip/Support/AppDependencyFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,16 @@ enum AppDependencyFactory {
let pasteboardClient = SystemPasteboardClient()
let writeGuard = ClipboardWriteGuard()
let contentClassifier = DefaultContentClassifier()
let sensitiveDetector = DefaultSensitiveContentDetector()
let ephemeralSensitiveStore = SensitiveEphemeralStore()
let transformEngine = TransformEngine()
let snapshotBuilder = ClipboardSnapshotBuilder(
contentClassifier: contentClassifier,
sensitiveDetector: sensitiveDetector,
blobStore: blobStore
)
let monitor = ClipboardMonitor(
pasteboardClient: pasteboardClient,
repository: repository,
writeGuard: writeGuard,
snapshotBuilder: snapshotBuilder,
ephemeralSensitiveStore: ephemeralSensitiveStore
snapshotBuilder: snapshotBuilder
)
let pasteEngine = PasteEngine(
repository: repository,
Expand All @@ -62,9 +58,7 @@ enum AppDependencyFactory {
repository: repository,
writeGuard: writeGuard,
clipboardMonitor: monitor,
ephemeralSensitiveStore: ephemeralSensitiveStore,
contentClassifier: contentClassifier,
sensitiveDetector: sensitiveDetector,
transformEngine: transformEngine,
searchService: searchService,
blobStore: blobStore,
Expand Down
10 changes: 1 addition & 9 deletions Sources/DevClip/Support/HistoryViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ final class HistoryViewModel: ObservableObject {
enum Scope: String, CaseIterable, Identifiable {
case all
case pinned
case sensitive

var id: String { rawValue }

Expand All @@ -17,8 +16,6 @@ final class HistoryViewModel: ObservableObject {
"历史"
case .pinned:
"固定"
case .sensitive:
"敏感"
}
}

Expand All @@ -28,8 +25,6 @@ final class HistoryViewModel: ObservableObject {
"clock"
case .pinned:
"pin"
case .sensitive:
"lock.shield"
}
}
}
Expand Down Expand Up @@ -71,8 +66,7 @@ final class HistoryViewModel: ObservableObject {
var counts: [Scope: Int] {
[
.all: allEntries.count,
.pinned: allEntries.filter(\.isPinned).count,
.sensitive: allEntries.filter(\.isSensitive).count
.pinned: allEntries.filter(\.isPinned).count
]
}

Expand Down Expand Up @@ -227,8 +221,6 @@ final class HistoryViewModel: ObservableObject {
values
case .pinned:
values.filter(\.isPinned)
case .sensitive:
values.filter(\.isSensitive)
}
}
}
2 changes: 1 addition & 1 deletion Sources/DevClip/Support/SettingsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ final class SettingsViewModel: ObservableObject {
do {
let result = try await archiveService.exportEncrypted(passphrase: archivePassphrase)
try await archiveFileClient.write(result.archive, to: url(from: exportPath))
statusMessage = "已导出 \(result.summary.exportedEntryCount) 条,跳过敏感记录 \(result.summary.skippedSensitiveCount) 条"
statusMessage = "已导出 \(result.summary.exportedEntryCount) 条记录"
} catch {
statusMessage = "导出失败:\(error.localizedDescription)"
}
Expand Down
4 changes: 0 additions & 4 deletions Sources/DevClip/Views/HistoryDetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,6 @@ struct HistoryDetailView: View {

@ViewBuilder
private func headerBadges(_ entry: ClipboardEntry) -> some View {
if entry.isSensitive {
Label("敏感", systemImage: "lock.fill")
.devClipStatusBadge(color: .orange)
}
if entry.isPinned {
Label("已固定", systemImage: "pin.fill")
.devClipStatusBadge(color: Color.accentColor)
Expand Down
2 changes: 1 addition & 1 deletion Sources/DevClip/Views/HistoryListPaneView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ private struct HistoryEntryRow: View {
} else {
Image(systemName: entry.isPinned ? "pin.fill" : ClipboardKindPresentation.iconName(entry.detectedKind))
.font(.system(size: 14))
.foregroundStyle(entry.isSensitive ? .orange : .secondary)
.foregroundStyle(.secondary)
.frame(width: 36, height: 36)
.background(
RoundedRectangle(cornerRadius: 4, style: .continuous)
Expand Down
27 changes: 0 additions & 27 deletions Sources/DevClip/Views/SettingsRootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ struct SettingsRootView: View {
@StateObject private var viewModel: SettingsViewModel
@AppStorage(UserDefaultsPasteAutomationPreferences.automaticPasteEnabledKey)
private var isAutomaticPasteEnabled = false
@AppStorage("privacy.maskSensitiveContent")
private var masksSensitiveContent = true

init(dependencies: DependencyContainer) {
_viewModel = StateObject(wrappedValue: SettingsViewModel(dependencies: dependencies))
Expand All @@ -19,11 +17,6 @@ struct SettingsRootView: View {
Label("通用", systemImage: "gear")
}

privacyTab
.tabItem {
Label("隐私", systemImage: "lock.shield")
}

archiveTab
.tabItem {
Label("导入导出", systemImage: "lock.doc")
Expand Down Expand Up @@ -74,26 +67,6 @@ struct SettingsRootView: View {
}
}

// MARK: - Privacy Tab

private var privacyTab: some View {
Form {
Section("敏感内容") {
Toggle("遮罩敏感内容", isOn: $masksSensitiveContent)

Text("启用后,检测到可能包含密码、密钥等敏感信息的剪贴板内容将自动遮罩显示。")
.font(.system(size: 12))
.foregroundStyle(.secondary)
}

Section("导出安全") {
Text("敏感记录默认不导出,导出文件使用 AES-GCM 加密保护。")
.font(.system(size: 12))
.foregroundStyle(.secondary)
}
}
}

// MARK: - Archive Tab

private var archiveTab: some View {
Expand Down
16 changes: 4 additions & 12 deletions Sources/DevClipCore/Export/EncryptedClipboardArchiveService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import Foundation

public struct ClipboardArchiveExportSummary: Equatable, Sendable {
public var exportedEntryCount: Int
public var skippedSensitiveCount: Int
public var skippedEntryCount: Int

public init(exportedEntryCount: Int, skippedSensitiveCount: Int) {
public init(exportedEntryCount: Int, skippedEntryCount: Int) {
self.exportedEntryCount = exportedEntryCount
self.skippedSensitiveCount = skippedSensitiveCount
self.skippedEntryCount = skippedEntryCount
}
}

Expand Down Expand Up @@ -144,7 +144,7 @@ public actor AESGCMClipboardArchiveService: ClipboardArchiveService {
)
let summary = ClipboardArchiveExportSummary(
exportedEntryCount: exportableEntries.count,
skippedSensitiveCount: allEntries.count - exportableEntries.count
skippedEntryCount: allEntries.count - exportableEntries.count
)

return (archive, summary)
Expand Down Expand Up @@ -200,14 +200,6 @@ public actor AESGCMClipboardArchiveService: ClipboardArchiveService {
}

private static func isExportable(_ entry: ClipboardEntry) -> Bool {
if entry.isSensitive {
return false
}

if entry.metadata.values["sensitiveClassification"] == SensitiveClassification.secret.rawValue {
return false
}

if entry.metadata.values["shouldExport"] == "false" {
return false
}
Expand Down
6 changes: 0 additions & 6 deletions Sources/DevClipCore/Models/SensitiveClassification.swift

This file was deleted.

33 changes: 7 additions & 26 deletions Sources/DevClipCore/Pasteboard/ClipboardMonitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ public enum ClipboardMonitorPollResult: Equatable, Sendable {
case noChange(changeCount: Int)
case ignoredInternalWrite(changeCount: Int)
case saved(changeCount: Int, entryCount: Int)
case protectedSecret(changeCount: Int, entryCount: Int)
}

/// Background actor that polls pasteboard changes and saves new snapshots.
Expand All @@ -30,7 +29,6 @@ public actor ClipboardMonitor {
private let repository: any ClipboardRepository
private let writeGuard: ClipboardWriteGuard
private let snapshotBuilder: ClipboardSnapshotBuilder
private let ephemeralSensitiveStore: SensitiveEphemeralStore
private let options: ClipboardMonitorOptions

private var pollingTask: Task<Void, Never>?
Expand All @@ -43,14 +41,12 @@ public actor ClipboardMonitor {
repository: any ClipboardRepository,
writeGuard: ClipboardWriteGuard = ClipboardWriteGuard(),
snapshotBuilder: ClipboardSnapshotBuilder = ClipboardSnapshotBuilder(),
ephemeralSensitiveStore: SensitiveEphemeralStore = SensitiveEphemeralStore(),
options: ClipboardMonitorOptions = ClipboardMonitorOptions()
) {
self.pasteboardClient = pasteboardClient
self.repository = repository
self.writeGuard = writeGuard
self.snapshotBuilder = snapshotBuilder
self.ephemeralSensitiveStore = ephemeralSensitiveStore
self.options = options
}

Expand Down Expand Up @@ -96,39 +92,24 @@ public actor ClipboardMonitor {
@discardableResult
public func processSnapshot(_ snapshot: ClipboardSnapshot) async throws -> ClipboardMonitorPollResult {
_ = try await repository.deleteExpiredEntries(now: Date())
_ = await ephemeralSensitiveStore.purgeExpired()

let buildResult = await snapshotBuilder.build(from: snapshot)

guard !buildResult.entries.isEmpty || !buildResult.protectedEntries.isEmpty else {
guard !buildResult.entries.isEmpty else {
return .noChange(changeCount: snapshot.changeCount)
}

if try await writeGuard.shouldIgnore(snapshot: snapshot, contentHash: buildResult.snapshotHash) {
return .ignoredInternalWrite(changeCount: snapshot.changeCount)
}

if !buildResult.protectedEntries.isEmpty {
await ephemeralSensitiveStore.store(
entries: buildResult.protectedEntries,
representations: buildResult.protectedRepresentations
)
}

if !buildResult.entries.isEmpty {
try await repository.save(
group: buildResult.group,
entries: buildResult.entries,
representations: buildResult.representations
)

return .saved(changeCount: snapshot.changeCount, entryCount: buildResult.entries.count)
}

return .protectedSecret(
changeCount: snapshot.changeCount,
entryCount: buildResult.protectedEntries.count
try await repository.save(
group: buildResult.group,
entries: buildResult.entries,
representations: buildResult.representations
)

return .saved(changeCount: snapshot.changeCount, entryCount: buildResult.entries.count)
}

private func runPollingLoop() async {
Expand Down
Loading