Skip to content
Closed
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: 8 additions & 0 deletions Clipbara/AppState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ final class AppState {
}
}

/// 패널/핀보드 카드에서 공통으로 쓰는 붙여넣기 경로.
/// - Parameter asPlainText: nil이면 설정 + Shift 조합으로 자동 판단
func paste(_ item: ClipboardItem, asPlainText: Bool? = nil) {
clipboardMonitor.skipNextChange()
pasteService.paste(item: item, asPlainText: asPlainText)
hidePanel()
}

func hidePanel() {
previewItem = nil
panelToast = nil
Expand Down
29 changes: 28 additions & 1 deletion Clipbara/Services/PasteService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,34 @@ struct PasteService {

private static let tempDir = NSTemporaryDirectory() + "Clipbara/"

func paste(item: ClipboardItem) {
/// 설정 > General의 "항상 일반 텍스트로 붙여넣기" 값
nonisolated static let alwaysPlainTextDefaultsKey = "alwaysPastePlainText"

/// 서식 제거가 의미 있는 항목인지 (RTF/HTML만 해당)
static func supportsPlainText(_ item: ClipboardItem) -> Bool {
switch item.contentType {
case .richText, .html:
return !(item.textContent?.isEmpty ?? true)
default:
return false
}
}

/// 기본값(설정)과 Shift 수정자를 XOR로 합친다.
/// 설정이 꺼져 있으면 Shift = 서식 제거, 켜져 있으면 Shift = 서식 유지.
static func resolvePlainText() -> Bool {
let always = UserDefaults.standard.bool(forKey: alwaysPlainTextDefaultsKey)
let shiftHeld = NSEvent.modifierFlags.contains(.shift)
return always != shiftHeld
}

/// - Parameter asPlainText: nil이면 설정 + Shift 조합으로 자동 판단
func paste(item: ClipboardItem, asPlainText: Bool? = nil) {
if asPlainText ?? Self.resolvePlainText(), Self.supportsPlainText(item) {
pastePlainText(item: item)
return
}

let pasteboard = NSPasteboard.general
pasteboard.clearContents()

Expand Down
7 changes: 6 additions & 1 deletion Clipbara/Views/ClipboardCardView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,12 @@ struct ClipboardCardView: View {

@ViewBuilder
private var menuItems: some View {
Button("Paste") { onPaste(item) }
if PasteService.supportsPlainText(item) {
Button("Paste with Formatting") { appState.paste(item, asPlainText: false) }
Button("Paste as Plain Text") { appState.paste(item, asPlainText: true) }
} else {
Button("Paste") { onPaste(item) }
}
if showsManagementMenu {
Divider()
Button("Rename") {
Expand Down
18 changes: 18 additions & 0 deletions Clipbara/Views/Settings/GeneralSettingsTab.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ struct GeneralSettingsTab: View {
@Environment(\.modelContext) private var modelContext
@Environment(AppState.self) private var appState
@AppStorage("historyLimit") private var historyLimit: Int = 500
@AppStorage(PasteService.alwaysPlainTextDefaultsKey) private var alwaysPastePlainText: Bool = false
@State private var launchAtLogin: Bool = SMAppService.mainApp.status == .enabled
@State private var transferMessage: String?
@State private var showTransferAlert = false
Expand Down Expand Up @@ -35,6 +36,23 @@ struct GeneralSettingsTab: View {
}
}

Section("Pasting") {
HStack {
VStack(alignment: .leading, spacing: 2) {
HStack(spacing: 5) {
Text("Always Paste as Plain Text")
InfoHoverButton(text: "Removes fonts, colors, and links from rich text and HTML clips, pasting only the text.")
}
Text("Hold \u{21e7} while pasting to switch for a single paste.")
.font(.system(size: 11))
.foregroundStyle(.secondary)
}
Spacer()
Toggle("", isOn: $alwaysPastePlainText)
.labelsHidden()
}
}

Section("Backup") {
LabeledContent("Export history, pinboards, and settings to a JSON file.") {
Button("Export\u{2026}") { exportHistory() }
Expand Down
Loading