Ariver
2026-07-12 24f551a39eae849d891da26cc91f90d354e3a2db
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package textoutput
 
import (
    "strings"
    "sync"
    "time"
    "unicode/utf8"
    "voicesnap/internal/axinput"
    "voicesnap/internal/logger"
)
 
type Paster interface {
    Paste(text string, keepClipboard bool) error
    TypeText(text string) error
}
 
type Result struct {
    OK                bool      `json:"ok"`
    Text              string    `json:"text,omitempty"`
    Method            string    `json:"method"`
    Message           string    `json:"message"`
    Target            string    `json:"target,omitempty"`
    NeedsPermission   bool      `json:"needsPermission"`
    DirectInserted    bool      `json:"directInserted"`
    FallbackAvailable bool      `json:"fallbackAvailable"`
    CreatedAt         time.Time `json:"createdAt"`
}
 
type AutoPasteGuard struct {
    OK       bool
    Identity string
    Message  string
}
 
type OutputOptions struct {
    KeepClipboard       bool
    AutoPasteExperiment bool
    AutoPasteGuard      AutoPasteGuard
}
 
type Router struct {
    mu     sync.Mutex
    paster Paster
    driver axinput.Driver
    last   Result
}
 
func NewRouter(paster Paster) *Router {
    return newPlatformRouter(paster)
}
 
func (r *Router) LastResult() Result {
    r.mu.Lock()
    defer r.mu.Unlock()
    return r.last
}
 
func (r *Router) CaptureAutoPasteGuard() AutoPasteGuard {
    if r.driver == nil {
        return AutoPasteGuard{OK: false, Message: "AX driver is unavailable"}
    }
    identity := r.driver.FrontmostAppIdentity()
    return AutoPasteGuard{OK: identity.OK, Identity: identity.Identity, Message: identity.Message}
}
 
func (r *Router) remember(result Result) Result {
    if result.CreatedAt.IsZero() {
        result.CreatedAt = time.Now()
    }
    r.mu.Lock()
    r.last = result
    r.mu.Unlock()
    return result
}
 
func (r *Router) PrepareLastClipboardFallback() axinput.ClipboardResult {
    r.mu.Lock()
    last := r.last
    r.mu.Unlock()
    if !last.FallbackAvailable || last.Text == "" {
        logger.Info("Text output fallback prepare route status=no_pending fallback_available=%t has_text=%t", last.FallbackAvailable, last.Text != "")
        return axinput.ClipboardResult{OK: false, Message: "no pending text output fallback is available"}
    }
    if !clipboardFallbackSafeForTarget(last) {
        logger.Info("Text output fallback prepare route status=blocked_non_writable text_runes=%d", utf8.RuneCountInString(last.Text))
        return axinput.ClipboardResult{OK: false, Message: "clipboard fallback is disabled for this non-writable target"}
    }
    if r.driver == nil {
        logger.Info("Text output fallback prepare route status=no_driver text_runes=%d", utf8.RuneCountInString(last.Text))
        return axinput.ClipboardResult{OK: false, Message: "clipboard fallback is unavailable for this output route"}
    }
    result := r.driver.PrepareClipboardFallback(last.Text)
    logger.Info(
        "Text output fallback prepare route status=driver_result ok=%t prepared=%t token_present=%t text_runes=%d",
        result.OK,
        result.Prepared,
        result.Token != "",
        utf8.RuneCountInString(last.Text),
    )
    return result
}
 
func (r *Router) RestoreClipboardFallback(token string) axinput.ClipboardResult {
    if r.driver == nil {
        logger.Info("Text output fallback restore route status=no_driver token_present=%t", token != "")
        return axinput.ClipboardResult{OK: false, Message: "clipboard fallback is unavailable for this output route"}
    }
    result := r.driver.RestoreClipboardFallback(token)
    logger.Info(
        "Text output fallback restore route status=driver_result ok=%t restored=%t token_present=%t",
        result.OK,
        result.Restored,
        token != "",
    )
    return result
}
 
func clipboardFallbackSafeForTarget(result Result) bool {
    target := strings.TrimSpace(strings.ToLower(result.Target))
    if target == "" || target == "target=unknown" {
        return true
    }
    for _, marker := range []string{
        "app=terminal",
        "app=iterm",
        "app=iterm2",
        "app=warp",
        "role=axterminal",
        "subrole=axterminal",
        "console",
        "shell",
    } {
        if strings.Contains(target, marker) {
            return false
        }
    }
    return true
}
 
func autoPasteSafeForIdentity(identity string) bool {
    normalized := strings.ToLower(strings.TrimSpace(identity))
    if normalized == "" {
        return false
    }
    for _, marker := range []string{
        "com.apple.terminal",
        "com.googlecode.iterm2",
        "iterm",
        "terminal",
        "warp",
        "shell",
        "console",
    } {
        if strings.Contains(normalized, marker) {
            return false
        }
    }
    return true
}