From aa2018b9ab930e7e9098c75549b2ea9c56d1aa23 Mon Sep 17 00:00:00 2001 From: 142 Date: Wed, 8 Apr 2026 11:02:12 +0100 Subject: [PATCH] feat: add auto-copy response to clipboard toggle Add a "Copy responses" toggle in the menu bar panel that automatically copies Clicky's response text to the macOS clipboard after each response. Uses the clean spoken text with [POINT] tags stripped. Defaults to OFF. Persists preference via UserDefaults. Co-Authored-By: Claude Opus 4.6 (1M context) --- leanring-buddy/CompanionManager.swift | 15 ++++++++++++ leanring-buddy/CompanionPanelView.swift | 32 +++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/leanring-buddy/CompanionManager.swift b/leanring-buddy/CompanionManager.swift index 0234cf19..fc5f3fd8 100644 --- a/leanring-buddy/CompanionManager.swift +++ b/leanring-buddy/CompanionManager.swift @@ -139,6 +139,15 @@ final class CompanionManager: ObservableObject { } } + /// Whether Clicky's responses are automatically copied to the clipboard. + /// Defaults to OFF. Persisted to UserDefaults. + @Published var isAutoCopyResponseEnabled: Bool = UserDefaults.standard.bool(forKey: "isAutoCopyResponseEnabled") + + func setAutoCopyResponseEnabled(_ enabled: Bool) { + isAutoCopyResponseEnabled = enabled + UserDefaults.standard.set(enabled, forKey: "isAutoCopyResponseEnabled") + } + /// Whether the user has completed onboarding at least once. Persisted /// to UserDefaults so the Start button only appears on first launch. var hasCompletedOnboarding: Bool { @@ -681,6 +690,12 @@ final class CompanionManager: ObservableObject { print("🎯 Element pointing: \(parseResult.elementLabel ?? "no element")") } + // Copy the clean response to the clipboard if the user has enabled auto-copy + if isAutoCopyResponseEnabled { + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(spokenText, forType: .string) + } + // Save this exchange to conversation history (with the point tag // stripped so it doesn't confuse future context) conversationHistory.append(( diff --git a/leanring-buddy/CompanionPanelView.swift b/leanring-buddy/CompanionPanelView.swift index 76789b4c..35796937 100644 --- a/leanring-buddy/CompanionPanelView.swift +++ b/leanring-buddy/CompanionPanelView.swift @@ -62,6 +62,9 @@ struct CompanionPanelView: View { Spacer() .frame(height: 16) + autoCopyResponseToggleRow + .padding(.horizontal, 16) + dmFarzaButton .padding(.horizontal, 16) } @@ -545,6 +548,35 @@ struct CompanionPanelView: View { + // MARK: - Auto-Copy Response Toggle + + private var autoCopyResponseToggleRow: some View { + HStack { + HStack(spacing: 8) { + Image(systemName: "doc.on.clipboard") + .font(.system(size: 12, weight: .medium)) + .foregroundColor(DS.Colors.textTertiary) + .frame(width: 16) + + Text("Copy responses") + .font(.system(size: 13, weight: .medium)) + .foregroundColor(DS.Colors.textSecondary) + } + + Spacer() + + Toggle("", isOn: Binding( + get: { companionManager.isAutoCopyResponseEnabled }, + set: { companionManager.setAutoCopyResponseEnabled($0) } + )) + .toggleStyle(.switch) + .labelsHidden() + .tint(DS.Colors.accent) + .scaleEffect(0.8) + } + .padding(.vertical, 4) + } + // MARK: - Show Clicky Cursor Toggle private var showClickyCursorToggleRow: some View {