Skip to content

Commit f00687a

Browse files
authored
Merge pull request #16 from mobrava/feat/paste-plain-text
Paste as plain text (#14)
2 parents d0c278a + f01d087 commit f00687a

4 files changed

Lines changed: 60 additions & 2 deletions

File tree

Clipbara/AppState.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@ final class AppState {
6565
}
6666
}
6767

68+
/// Shared paste path for panel and pinboard cards.
69+
/// - Parameter asPlainText: `nil` resolves from the setting combined with the Shift modifier.
70+
func paste(_ item: ClipboardItem, asPlainText: Bool? = nil) {
71+
clipboardMonitor.skipNextChange()
72+
pasteService.paste(item: item, asPlainText: asPlainText)
73+
hidePanel()
74+
}
75+
6876
func hidePanel() {
6977
previewItem = nil
7078
panelToast = nil

Clipbara/Services/PasteService.swift

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,34 @@ struct PasteService {
55

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

8-
func paste(item: ClipboardItem) {
8+
/// Backing key for the "Always Paste as Plain Text" setting (Settings > General).
9+
nonisolated static let alwaysPlainTextDefaultsKey = "alwaysPastePlainText"
10+
11+
/// Whether stripping formatting is meaningful for this item (RTF/HTML only).
12+
static func supportsPlainText(_ item: ClipboardItem) -> Bool {
13+
switch item.contentType {
14+
case .richText, .html:
15+
return !(item.textContent?.isEmpty ?? true)
16+
default:
17+
return false
18+
}
19+
}
20+
21+
/// Combines the setting with the Shift modifier using XOR.
22+
/// Setting off: Shift strips formatting. Setting on: Shift keeps formatting.
23+
static func resolvePlainText() -> Bool {
24+
let always = UserDefaults.standard.bool(forKey: alwaysPlainTextDefaultsKey)
25+
let shiftHeld = NSEvent.modifierFlags.contains(.shift)
26+
return always != shiftHeld
27+
}
28+
29+
/// - Parameter asPlainText: `nil` resolves from the setting combined with the Shift modifier.
30+
func paste(item: ClipboardItem, asPlainText: Bool? = nil) {
31+
if asPlainText ?? Self.resolvePlainText(), Self.supportsPlainText(item) {
32+
pastePlainText(item: item)
33+
return
34+
}
35+
936
let pasteboard = NSPasteboard.general
1037
pasteboard.clearContents()
1138

Clipbara/Views/ClipboardCardView.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,12 @@ struct ClipboardCardView: View {
6262

6363
@ViewBuilder
6464
private var menuItems: some View {
65-
Button("Paste") { onPaste(item) }
65+
if PasteService.supportsPlainText(item) {
66+
Button("Paste with Formatting") { appState.paste(item, asPlainText: false) }
67+
Button("Paste as Plain Text") { appState.paste(item, asPlainText: true) }
68+
} else {
69+
Button("Paste") { onPaste(item) }
70+
}
6671
if showsManagementMenu {
6772
Divider()
6873
Button("Rename") {

Clipbara/Views/Settings/GeneralSettingsTab.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ struct GeneralSettingsTab: View {
77
@Environment(\.modelContext) private var modelContext
88
@Environment(AppState.self) private var appState
99
@AppStorage("historyLimit") private var historyLimit: Int = 500
10+
@AppStorage(PasteService.alwaysPlainTextDefaultsKey) private var alwaysPastePlainText: Bool = false
1011
@State private var launchAtLogin: Bool = SMAppService.mainApp.status == .enabled
1112
@State private var transferMessage: String?
1213
@State private var showTransferAlert = false
@@ -35,6 +36,23 @@ struct GeneralSettingsTab: View {
3536
}
3637
}
3738

39+
Section("Pasting") {
40+
HStack {
41+
VStack(alignment: .leading, spacing: 2) {
42+
HStack(spacing: 5) {
43+
Text("Always Paste as Plain Text")
44+
InfoHoverButton(text: "Removes fonts, colors, and links from rich text and HTML clips, pasting only the text.")
45+
}
46+
Text("Hold \u{21e7} while pasting to switch for a single paste.")
47+
.font(.system(size: 11))
48+
.foregroundStyle(.secondary)
49+
}
50+
Spacer()
51+
Toggle("", isOn: $alwaysPastePlainText)
52+
.labelsHidden()
53+
}
54+
}
55+
3856
Section("Backup") {
3957
LabeledContent("Export history, pinboards, and settings to a JSON file.") {
4058
Button("Export\u{2026}") { exportHistory() }

0 commit comments

Comments
 (0)