Ariver
2026-06-13 859c14d783dc31fe68550b114fc8adeea09dab2a
privatevoice.src/internal/input/paste_darwin.go
@@ -24,6 +24,11 @@
    [pb setString:str forType:NSPasteboardTypeString];
}
static void clearClipboardText(void) {
    NSPasteboard* pb = [NSPasteboard generalPasteboard];
    [pb clearContents];
}
static char* getClipboardText(void) {
    NSPasteboard* pb = [NSPasteboard generalPasteboard];
    NSString* str = [pb stringForType:NSPasteboardTypeString];
@@ -91,11 +96,33 @@
import (
   "fmt"
   "sync"
   "time"
   "unsafe"
)
const (
   darwinClipboardVerifyTimeout = 500 * time.Millisecond
   darwinClipboardPollInterval  = 15 * time.Millisecond
   darwinClipboardRestoreDelay  = 900 * time.Millisecond
)
type darwinPaster struct{}
type darwinClipboardSnapshot struct {
   text    string
   hasText bool
}
type darwinPasteState struct {
   mu             sync.Mutex
   generation     uint64
   pendingRestore bool
   pendingText    string
   restoreTo      darwinClipboardSnapshot
}
var globalDarwinPasteState darwinPasteState
func newPlatformPaster() Paster {
   return &darwinPaster{}
@@ -106,31 +133,44 @@
      return fmt.Errorf("accessibility permission is required to paste")
   }
   var oldClip *string
   state := &globalDarwinPasteState
   state.mu.Lock()
   state.generation++
   generation := state.generation
   originalClipboard := currentDarwinClipboardSnapshot()
   if !keepClipboard {
      cOld := C.getClipboardText()
      if cOld != nil {
         old := C.GoString(cOld)
         oldClip = &old
         C.free(unsafe.Pointer(cOld))
      }
      originalClipboard = resolveDarwinRestoreTarget(
         originalClipboard,
         state.pendingRestore,
         state.pendingText,
         state.restoreTo,
      )
   }
   cstr := C.CString(text)
   defer C.free(unsafe.Pointer(cstr))
   C.setClipboardText(cstr)
   time.Sleep(60 * time.Millisecond)
   C.simulateCmdV()
   if !keepClipboard && oldClip != nil {
      go func(old string) {
         time.Sleep(600 * time.Millisecond)
         cOld := C.CString(old)
         defer C.free(unsafe.Pointer(cOld))
         C.setClipboardText(cOld)
      }(*oldClip)
   if !waitForDarwinClipboardText(text, darwinClipboardVerifyTimeout) {
      state.pendingRestore = false
      state.mu.Unlock()
      return fmt.Errorf("clipboard verification failed before paste")
   }
   C.simulateCmdV()
   if keepClipboard {
      state.pendingRestore = false
      state.mu.Unlock()
      return nil
   }
   state.pendingRestore = true
   state.pendingText = text
   state.restoreTo = originalClipboard
   state.mu.Unlock()
   go restoreDarwinClipboardAfterPaste(generation, text, originalClipboard)
   return nil
}
@@ -143,3 +183,82 @@
   }
   return nil
}
func currentDarwinClipboardSnapshot() darwinClipboardSnapshot {
   cText := C.getClipboardText()
   if cText == nil {
      return darwinClipboardSnapshot{}
   }
   defer C.free(unsafe.Pointer(cText))
   return darwinClipboardSnapshot{
      text:    C.GoString(cText),
      hasText: true,
   }
}
func resolveDarwinRestoreTarget(current darwinClipboardSnapshot, pending bool, pendingText string, restoreTo darwinClipboardSnapshot) darwinClipboardSnapshot {
   if pending && current.hasText && current.text == pendingText {
      return restoreTo
   }
   return current
}
func waitForDarwinClipboardText(expected string, timeout time.Duration) bool {
   deadline := time.Now().Add(timeout)
   for {
      current := currentDarwinClipboardSnapshot()
      if current.hasText && current.text == expected {
         return true
      }
      if time.Now().After(deadline) {
         return false
      }
      time.Sleep(darwinClipboardPollInterval)
   }
}
func restoreDarwinClipboardAfterPaste(generation uint64, pastedText string, restoreTo darwinClipboardSnapshot) {
   time.Sleep(darwinClipboardRestoreDelay)
   state := &globalDarwinPasteState
   state.mu.Lock()
   defer state.mu.Unlock()
   if state.generation != generation || !state.pendingRestore || state.pendingText != pastedText {
      return
   }
   current := currentDarwinClipboardSnapshot()
   if !shouldRestoreDarwinClipboard(
      state.generation,
      generation,
      state.pendingRestore,
      state.pendingText,
      pastedText,
      current,
   ) {
      state.pendingRestore = false
      return
   }
   restoreDarwinClipboardSnapshot(restoreTo)
   state.pendingRestore = false
}
func shouldRestoreDarwinClipboard(currentGeneration, restoreGeneration uint64, pending bool, pendingText, pastedText string, current darwinClipboardSnapshot) bool {
   if currentGeneration != restoreGeneration || !pending || pendingText != pastedText {
      return false
   }
   return current.hasText && current.text == pastedText
}
func restoreDarwinClipboardSnapshot(snapshot darwinClipboardSnapshot) {
   if !snapshot.hasText {
      C.clearClipboardText()
      return
   }
   cText := C.CString(snapshot.text)
   defer C.free(unsafe.Pointer(cText))
   C.setClipboardText(cText)
}