@anionex/dsh-computer-use 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +307 -0
- package/README.zh.md +307 -0
- package/assets/computer-use-fixture.png +0 -0
- package/assets/cursor.png +0 -0
- package/cordis.patch.yml +13 -0
- package/docs/interaction-policy.i18n.yaml +4 -0
- package/docs/interaction-policy.md +140 -0
- package/docs/interaction-policy.zh.md +140 -0
- package/lib/approval-policy.js +12 -0
- package/lib/approval-policy.js.map +1 -0
- package/lib/artifacts.js +79 -0
- package/lib/artifacts.js.map +1 -0
- package/lib/backend.js +3 -0
- package/lib/backend.js.map +1 -0
- package/lib/client.js +349 -0
- package/lib/client.js.map +1 -0
- package/lib/config.js +120 -0
- package/lib/config.js.map +1 -0
- package/lib/confirmations.js +99 -0
- package/lib/confirmations.js.map +1 -0
- package/lib/diff.js +58 -0
- package/lib/diff.js.map +1 -0
- package/lib/errors.js +22 -0
- package/lib/errors.js.map +1 -0
- package/lib/exposure.js +231 -0
- package/lib/exposure.js.map +1 -0
- package/lib/index.js +56 -0
- package/lib/index.js.map +1 -0
- package/lib/leases.js +259 -0
- package/lib/leases.js.map +1 -0
- package/lib/providers/macos.js +148 -0
- package/lib/providers/macos.js.map +1 -0
- package/lib/providers/native-helper.js +356 -0
- package/lib/providers/native-helper.js.map +1 -0
- package/lib/service.js +480 -0
- package/lib/service.js.map +1 -0
- package/lib/skill.js +119 -0
- package/lib/skill.js.map +1 -0
- package/lib/target-resolver.js +180 -0
- package/lib/target-resolver.js.map +1 -0
- package/lib/tools.js +493 -0
- package/lib/tools.js.map +1 -0
- package/lib/types/approval-policy.d.ts +13 -0
- package/lib/types/approval-policy.d.ts.map +1 -0
- package/lib/types/artifacts.d.ts +10 -0
- package/lib/types/artifacts.d.ts.map +1 -0
- package/lib/types/backend.d.ts +94 -0
- package/lib/types/backend.d.ts.map +1 -0
- package/lib/types/client/index.d.ts +78 -0
- package/lib/types/client/index.d.ts.map +1 -0
- package/lib/types/config.d.ts +75 -0
- package/lib/types/config.d.ts.map +1 -0
- package/lib/types/confirmations.d.ts +23 -0
- package/lib/types/confirmations.d.ts.map +1 -0
- package/lib/types/diff.d.ts +5 -0
- package/lib/types/diff.d.ts.map +1 -0
- package/lib/types/errors.d.ts +15 -0
- package/lib/types/errors.d.ts.map +1 -0
- package/lib/types/exposure.d.ts +30 -0
- package/lib/types/exposure.d.ts.map +1 -0
- package/lib/types/index.d.ts +21 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/leases.d.ts +80 -0
- package/lib/types/leases.d.ts.map +1 -0
- package/lib/types/providers/macos.d.ts +15 -0
- package/lib/types/providers/macos.d.ts.map +1 -0
- package/lib/types/providers/native-helper.d.ts +36 -0
- package/lib/types/providers/native-helper.d.ts.map +1 -0
- package/lib/types/service.d.ts +52 -0
- package/lib/types/service.d.ts.map +1 -0
- package/lib/types/skill.d.ts +9 -0
- package/lib/types/skill.d.ts.map +1 -0
- package/lib/types/target-resolver.d.ts +38 -0
- package/lib/types/target-resolver.d.ts.map +1 -0
- package/lib/types/tools.d.ts +6 -0
- package/lib/types/tools.d.ts.map +1 -0
- package/lib/types/types.d.ts +246 -0
- package/lib/types/types.d.ts.map +1 -0
- package/lib/types/web.d.ts +32 -0
- package/lib/types/web.d.ts.map +1 -0
- package/lib/types.js +9 -0
- package/lib/types.js.map +1 -0
- package/lib/web.js +163 -0
- package/lib/web.js.map +1 -0
- package/native/macos/Sources/Fixture/main.swift +366 -0
- package/native/macos/Sources/Helper/CursorImage.swift +11 -0
- package/native/macos/Sources/Helper/CursorOverlay.swift +488 -0
- package/native/macos/Sources/Helper/TargetedPointer.swift +314 -0
- package/native/macos/Sources/Helper/main.swift +1140 -0
- package/native/macos/Sources/Monitor/main.swift +139 -0
- package/native/macos/bin/dsh-computer-use-helper +0 -0
- package/native/macos/manifest.json +14 -0
- package/package.json +128 -0
- package/scripts/build-client.mjs +24 -0
- package/scripts/build-native.mjs +158 -0
- package/scripts/check-native.mjs +92 -0
- package/scripts/model-e2e.mjs +291 -0
- package/scripts/session-transcript.mjs +117 -0
- package/scripts/validate.mjs +256 -0
- package/src/approval-policy.ts +16 -0
- package/src/artifacts.ts +93 -0
- package/src/backend.ts +103 -0
- package/src/client/index.tsx +539 -0
- package/src/config.ts +196 -0
- package/src/confirmations.ts +128 -0
- package/src/diff.ts +52 -0
- package/src/errors.ts +39 -0
- package/src/exposure.ts +236 -0
- package/src/index.ts +66 -0
- package/src/leases.ts +337 -0
- package/src/providers/macos.ts +185 -0
- package/src/providers/native-helper.ts +393 -0
- package/src/service.ts +583 -0
- package/src/skill.ts +123 -0
- package/src/target-resolver.ts +237 -0
- package/src/tools.ts +528 -0
- package/src/types.ts +299 -0
- package/src/web.ts +180 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import Foundation
|
|
3
|
+
|
|
4
|
+
private final class InputProbeView: NSView {
|
|
5
|
+
var onEvent: ((String) -> Void)?
|
|
6
|
+
private var dragging = false
|
|
7
|
+
|
|
8
|
+
override var isFlipped: Bool { true }
|
|
9
|
+
|
|
10
|
+
override init(frame frameRect: NSRect) {
|
|
11
|
+
super.init(frame: frameRect)
|
|
12
|
+
wantsLayer = true
|
|
13
|
+
layer?.backgroundColor = NSColor.controlBackgroundColor.cgColor
|
|
14
|
+
layer?.borderColor = NSColor.separatorColor.cgColor
|
|
15
|
+
layer?.borderWidth = 1
|
|
16
|
+
layer?.cornerRadius = 8
|
|
17
|
+
setAccessibilityElement(true)
|
|
18
|
+
setAccessibilityRole(.group)
|
|
19
|
+
setAccessibilityLabel("Targeted pointer probe")
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
required init?(coder: NSCoder) {
|
|
23
|
+
fatalError("init(coder:) has not been implemented")
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
override func acceptsFirstMouse(for event: NSEvent?) -> Bool { true }
|
|
27
|
+
|
|
28
|
+
override func mouseDown(with event: NSEvent) {
|
|
29
|
+
dragging = true
|
|
30
|
+
onEvent?("pointer down")
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
override func mouseDragged(with event: NSEvent) {
|
|
34
|
+
guard dragging else { return }
|
|
35
|
+
onEvent?("pointer drag")
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
override func mouseUp(with event: NSEvent) {
|
|
39
|
+
let wasDragging = dragging
|
|
40
|
+
dragging = false
|
|
41
|
+
onEvent?(wasDragging ? "pointer up" : "pointer up without down")
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
override func scrollWheel(with event: NSEvent) {
|
|
45
|
+
onEvent?("pointer scroll")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
override func draw(_ dirtyRect: NSRect) {
|
|
49
|
+
super.draw(dirtyRect)
|
|
50
|
+
let text = "Targeted pointer probe"
|
|
51
|
+
let attributes: [NSAttributedString.Key: Any] = [
|
|
52
|
+
.font: NSFont.systemFont(ofSize: 13, weight: .medium),
|
|
53
|
+
.foregroundColor: NSColor.secondaryLabelColor,
|
|
54
|
+
]
|
|
55
|
+
let size = text.size(withAttributes: attributes)
|
|
56
|
+
text.draw(at: NSPoint(x: (bounds.width - size.width) / 2, y: (bounds.height - size.height) / 2), withAttributes: attributes)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private final class FixtureDelegate: NSObject, NSApplicationDelegate {
|
|
61
|
+
private var window: NSWindow!
|
|
62
|
+
private var textField: NSTextField!
|
|
63
|
+
private var secureField: NSSecureTextField!
|
|
64
|
+
private var checkbox: NSButton!
|
|
65
|
+
private var popup: NSPopUpButton!
|
|
66
|
+
private var slider: NSSlider!
|
|
67
|
+
private var statusLabel: NSTextField!
|
|
68
|
+
private var inputProbe: InputProbeView!
|
|
69
|
+
private var stack: NSStackView!
|
|
70
|
+
private var insertedHarmlessSibling = false
|
|
71
|
+
private var keyMonitor: Any?
|
|
72
|
+
private var reorderTimer: Timer?
|
|
73
|
+
private var pointerClickCount = 0
|
|
74
|
+
private var pointerScrollCount = 0
|
|
75
|
+
private var pointerDragCount = 0
|
|
76
|
+
private var pointerMouseDownCount = 0
|
|
77
|
+
private var pointerMouseUpCount = 0
|
|
78
|
+
private var pointerDragGestureCount = 0
|
|
79
|
+
private var activeDragGesture = false
|
|
80
|
+
private var activationCount = 0
|
|
81
|
+
private let transcriptPath: String?
|
|
82
|
+
private let reorderTriggerPath: String?
|
|
83
|
+
private let launchInBackground: Bool
|
|
84
|
+
|
|
85
|
+
override init() {
|
|
86
|
+
let arguments = ProcessInfo.processInfo.arguments
|
|
87
|
+
if let index = arguments.firstIndex(of: "--transcript"), arguments.indices.contains(index + 1) {
|
|
88
|
+
transcriptPath = arguments[index + 1]
|
|
89
|
+
} else {
|
|
90
|
+
transcriptPath = nil
|
|
91
|
+
}
|
|
92
|
+
if let index = arguments.firstIndex(of: "--reorder-trigger"), arguments.indices.contains(index + 1) {
|
|
93
|
+
reorderTriggerPath = arguments[index + 1]
|
|
94
|
+
} else {
|
|
95
|
+
reorderTriggerPath = nil
|
|
96
|
+
}
|
|
97
|
+
launchInBackground = arguments.contains("--background")
|
|
98
|
+
super.init()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
func applicationDidFinishLaunching(_ notification: Notification) {
|
|
102
|
+
buildWindow()
|
|
103
|
+
keyMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event in
|
|
104
|
+
guard event.keyCode == 36 else { return event }
|
|
105
|
+
self?.applyValues()
|
|
106
|
+
return nil
|
|
107
|
+
}
|
|
108
|
+
if launchInBackground {
|
|
109
|
+
window.orderFrontRegardless()
|
|
110
|
+
window.orderBack(nil)
|
|
111
|
+
} else {
|
|
112
|
+
NSApplication.shared.activate(ignoringOtherApps: true)
|
|
113
|
+
window.makeKeyAndOrderFront(nil)
|
|
114
|
+
}
|
|
115
|
+
writeTranscript(event: "ready")
|
|
116
|
+
if let reorderTriggerPath {
|
|
117
|
+
reorderTimer = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { [weak self] timer in
|
|
118
|
+
guard FileManager.default.fileExists(atPath: reorderTriggerPath) else { return }
|
|
119
|
+
timer.invalidate()
|
|
120
|
+
self?.insertHarmlessSibling()
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
func applicationDidBecomeActive(_ notification: Notification) {
|
|
126
|
+
activationCount += 1
|
|
127
|
+
writeTranscript(event: "activated")
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
func applicationWillTerminate(_ notification: Notification) {
|
|
131
|
+
if let keyMonitor { NSEvent.removeMonitor(keyMonitor) }
|
|
132
|
+
reorderTimer?.invalidate()
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
|
136
|
+
true
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private func buildWindow() {
|
|
140
|
+
window = NSWindow(
|
|
141
|
+
contentRect: NSRect(x: 0, y: 0, width: 760, height: 560),
|
|
142
|
+
styleMask: [.titled, .closable, .miniaturizable, .resizable],
|
|
143
|
+
backing: .buffered,
|
|
144
|
+
defer: false
|
|
145
|
+
)
|
|
146
|
+
window.title = "DSH Computer Use Fixture"
|
|
147
|
+
window.center()
|
|
148
|
+
window.setFrameAutosaveName("dsh-computer-use-fixture")
|
|
149
|
+
|
|
150
|
+
let content = NSView()
|
|
151
|
+
content.translatesAutoresizingMaskIntoConstraints = false
|
|
152
|
+
window.contentView = content
|
|
153
|
+
|
|
154
|
+
let title = NSTextField(labelWithString: "Computer Use deterministic fixture")
|
|
155
|
+
title.font = .systemFont(ofSize: 22, weight: .semibold)
|
|
156
|
+
title.setAccessibilityLabel("Fixture title")
|
|
157
|
+
|
|
158
|
+
textField = NSTextField(string: "initial text")
|
|
159
|
+
textField.placeholderString = "Editable text"
|
|
160
|
+
textField.setAccessibilityLabel("Editable text")
|
|
161
|
+
textField.identifier = NSUserInterfaceItemIdentifier("fixture.text")
|
|
162
|
+
textField.target = self
|
|
163
|
+
textField.action = #selector(applyValues)
|
|
164
|
+
|
|
165
|
+
secureField = NSSecureTextField(string: "fixture-secret")
|
|
166
|
+
secureField.placeholderString = "Secure text"
|
|
167
|
+
secureField.setAccessibilityLabel("Secure text")
|
|
168
|
+
secureField.identifier = NSUserInterfaceItemIdentifier("fixture.secure")
|
|
169
|
+
|
|
170
|
+
checkbox = makeCheckbox(state: .off)
|
|
171
|
+
|
|
172
|
+
popup = NSPopUpButton(frame: .zero, pullsDown: false)
|
|
173
|
+
popup.addItems(withTitles: ["Alpha", "Beta", "Gamma"])
|
|
174
|
+
popup.selectItem(at: 0)
|
|
175
|
+
popup.target = self
|
|
176
|
+
popup.action = #selector(selectPopup)
|
|
177
|
+
popup.setAccessibilityLabel("Fixture selection")
|
|
178
|
+
|
|
179
|
+
slider = NSSlider(value: 25, minValue: 0, maxValue: 100, target: self, action: #selector(changeSlider))
|
|
180
|
+
slider.setAccessibilityLabel("Fixture slider")
|
|
181
|
+
|
|
182
|
+
let apply = NSButton(title: "Apply", target: self, action: #selector(applyValues))
|
|
183
|
+
apply.bezelStyle = .rounded
|
|
184
|
+
apply.keyEquivalent = "\r"
|
|
185
|
+
apply.setAccessibilityLabel("Apply fixture values")
|
|
186
|
+
|
|
187
|
+
let delayed = NSButton(title: "Delayed update", target: self, action: #selector(delayedUpdate))
|
|
188
|
+
delayed.bezelStyle = .rounded
|
|
189
|
+
delayed.setAccessibilityLabel("Start delayed update")
|
|
190
|
+
|
|
191
|
+
let modal = NSButton(title: "Show modal", target: self, action: #selector(showModal))
|
|
192
|
+
modal.bezelStyle = .rounded
|
|
193
|
+
modal.setAccessibilityLabel("Show fixture modal")
|
|
194
|
+
|
|
195
|
+
statusLabel = NSTextField(labelWithString: "Status: ready")
|
|
196
|
+
statusLabel.font = .monospacedSystemFont(ofSize: 13, weight: .regular)
|
|
197
|
+
statusLabel.setAccessibilityLabel("Fixture status")
|
|
198
|
+
statusLabel.identifier = NSUserInterfaceItemIdentifier("fixture.status")
|
|
199
|
+
|
|
200
|
+
inputProbe = InputProbeView(frame: .zero)
|
|
201
|
+
inputProbe.translatesAutoresizingMaskIntoConstraints = false
|
|
202
|
+
inputProbe.heightAnchor.constraint(equalToConstant: 54).isActive = true
|
|
203
|
+
inputProbe.onEvent = { [weak self] event in
|
|
204
|
+
guard let self else { return }
|
|
205
|
+
switch event {
|
|
206
|
+
case "pointer down":
|
|
207
|
+
self.pointerMouseDownCount += 1
|
|
208
|
+
self.activeDragGesture = false
|
|
209
|
+
return
|
|
210
|
+
case "pointer drag":
|
|
211
|
+
self.pointerDragCount += 1
|
|
212
|
+
self.activeDragGesture = true
|
|
213
|
+
self.statusLabel.stringValue = "Status: pointer drag"
|
|
214
|
+
case "pointer up":
|
|
215
|
+
self.pointerMouseUpCount += 1
|
|
216
|
+
if self.activeDragGesture {
|
|
217
|
+
self.pointerDragGestureCount += 1
|
|
218
|
+
self.statusLabel.stringValue = "Status: pointer drag"
|
|
219
|
+
} else {
|
|
220
|
+
self.pointerClickCount += 1
|
|
221
|
+
self.statusLabel.stringValue = "Status: pointer click"
|
|
222
|
+
}
|
|
223
|
+
self.activeDragGesture = false
|
|
224
|
+
case "pointer scroll":
|
|
225
|
+
self.pointerScrollCount += 1
|
|
226
|
+
self.statusLabel.stringValue = "Status: pointer scroll"
|
|
227
|
+
default: break
|
|
228
|
+
}
|
|
229
|
+
self.writeTranscript(event: event)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
let longText = (1...80).map { "Scrollable row \($0)" }.joined(separator: "\n")
|
|
233
|
+
let textView = NSTextView()
|
|
234
|
+
textView.string = longText
|
|
235
|
+
textView.isEditable = false
|
|
236
|
+
textView.isSelectable = true
|
|
237
|
+
textView.setAccessibilityLabel("Scrollable fixture rows")
|
|
238
|
+
let scroll = NSScrollView()
|
|
239
|
+
scroll.hasVerticalScroller = true
|
|
240
|
+
scroll.documentView = textView
|
|
241
|
+
scroll.heightAnchor.constraint(equalToConstant: 180).isActive = true
|
|
242
|
+
|
|
243
|
+
let fields = NSGridView(views: [
|
|
244
|
+
[NSTextField(labelWithString: "Text"), textField],
|
|
245
|
+
[NSTextField(labelWithString: "Secret"), secureField],
|
|
246
|
+
[NSTextField(labelWithString: "Selection"), popup],
|
|
247
|
+
[NSTextField(labelWithString: "Level"), slider],
|
|
248
|
+
])
|
|
249
|
+
fields.rowSpacing = 10
|
|
250
|
+
fields.columnSpacing = 14
|
|
251
|
+
fields.column(at: 0).xPlacement = .trailing
|
|
252
|
+
fields.column(at: 1).width = 460
|
|
253
|
+
|
|
254
|
+
let buttons = NSStackView(views: [apply, delayed, modal])
|
|
255
|
+
buttons.orientation = .horizontal
|
|
256
|
+
buttons.spacing = 10
|
|
257
|
+
|
|
258
|
+
stack = NSStackView(views: [title, fields, checkbox, buttons, statusLabel, inputProbe, scroll])
|
|
259
|
+
stack.translatesAutoresizingMaskIntoConstraints = false
|
|
260
|
+
stack.orientation = .vertical
|
|
261
|
+
stack.alignment = .leading
|
|
262
|
+
stack.spacing = 16
|
|
263
|
+
content.addSubview(stack)
|
|
264
|
+
NSLayoutConstraint.activate([
|
|
265
|
+
stack.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 28),
|
|
266
|
+
stack.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -28),
|
|
267
|
+
stack.topAnchor.constraint(equalTo: content.topAnchor, constant: 24),
|
|
268
|
+
stack.bottomAnchor.constraint(lessThanOrEqualTo: content.bottomAnchor, constant: -24),
|
|
269
|
+
scroll.widthAnchor.constraint(equalTo: stack.widthAnchor),
|
|
270
|
+
inputProbe.widthAnchor.constraint(equalTo: stack.widthAnchor),
|
|
271
|
+
])
|
|
272
|
+
window.makeFirstResponder(textField)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
@objc private func applyValues() {
|
|
276
|
+
statusLabel.stringValue = "Status: applied \(textField.stringValue)"
|
|
277
|
+
writeTranscript(event: "apply")
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
@objc private func toggleCheckbox() {
|
|
281
|
+
statusLabel.stringValue = checkbox.state == .on ? "Status: option enabled" : "Status: option disabled"
|
|
282
|
+
writeTranscript(event: "checkbox")
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
@objc private func selectPopup() {
|
|
286
|
+
statusLabel.stringValue = "Status: selected \(popup.titleOfSelectedItem ?? "")"
|
|
287
|
+
writeTranscript(event: "selection")
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
@objc private func changeSlider() {
|
|
291
|
+
statusLabel.stringValue = "Status: slider \(Int(slider.doubleValue))"
|
|
292
|
+
writeTranscript(event: "slider")
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
@objc private func delayedUpdate() {
|
|
296
|
+
statusLabel.stringValue = "Status: waiting"
|
|
297
|
+
writeTranscript(event: "delay-start")
|
|
298
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
|
|
299
|
+
self?.statusLabel.stringValue = "Status: delayed complete"
|
|
300
|
+
self?.writeTranscript(event: "delay-complete")
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
private func insertHarmlessSibling() {
|
|
305
|
+
guard !insertedHarmlessSibling else { return }
|
|
306
|
+
insertedHarmlessSibling = true
|
|
307
|
+
let checkboxState = checkbox.state
|
|
308
|
+
stack.removeArrangedSubview(checkbox)
|
|
309
|
+
checkbox.removeFromSuperview()
|
|
310
|
+
let label = NSTextField(labelWithString: "Harmless dynamic sibling")
|
|
311
|
+
label.setAccessibilityLabel("Harmless dynamic sibling")
|
|
312
|
+
label.identifier = NSUserInterfaceItemIdentifier("fixture.harmless-sibling")
|
|
313
|
+
stack.insertArrangedSubview(label, at: 2)
|
|
314
|
+
checkbox = makeCheckbox(state: checkboxState)
|
|
315
|
+
stack.insertArrangedSubview(checkbox, at: 3)
|
|
316
|
+
statusLabel.stringValue = "Status: harmless sibling inserted"
|
|
317
|
+
writeTranscript(event: "reorder")
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
private func makeCheckbox(state: NSControl.StateValue) -> NSButton {
|
|
321
|
+
let value = NSButton(checkboxWithTitle: "Enable deterministic option", target: self, action: #selector(toggleCheckbox))
|
|
322
|
+
value.state = state
|
|
323
|
+
value.setAccessibilityLabel("Enable deterministic option")
|
|
324
|
+
value.identifier = NSUserInterfaceItemIdentifier("fixture.checkbox")
|
|
325
|
+
return value
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
@objc private func showModal() {
|
|
329
|
+
let alert = NSAlert()
|
|
330
|
+
alert.messageText = "Fixture modal"
|
|
331
|
+
alert.informativeText = "This modal exists for deterministic Accessibility observation."
|
|
332
|
+
alert.addButton(withTitle: "Confirm")
|
|
333
|
+
alert.beginSheetModal(for: window) { [weak self] _ in
|
|
334
|
+
self?.statusLabel.stringValue = "Status: modal confirmed"
|
|
335
|
+
self?.writeTranscript(event: "modal")
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
private func writeTranscript(event: String) {
|
|
340
|
+
guard let transcriptPath else { return }
|
|
341
|
+
let payload: [String: Any] = [
|
|
342
|
+
"event": event,
|
|
343
|
+
"text": textField?.stringValue ?? "",
|
|
344
|
+
"secureLength": secureField?.stringValue.count ?? 0,
|
|
345
|
+
"checked": checkbox?.state == .on,
|
|
346
|
+
"selection": popup?.titleOfSelectedItem ?? "",
|
|
347
|
+
"slider": Int(slider?.doubleValue ?? 0),
|
|
348
|
+
"status": statusLabel?.stringValue ?? "",
|
|
349
|
+
"pointerClickCount": pointerClickCount,
|
|
350
|
+
"pointerScrollCount": pointerScrollCount,
|
|
351
|
+
"pointerDragCount": pointerDragCount,
|
|
352
|
+
"pointerMouseDownCount": pointerMouseDownCount,
|
|
353
|
+
"pointerMouseUpCount": pointerMouseUpCount,
|
|
354
|
+
"pointerDragGestureCount": pointerDragGestureCount,
|
|
355
|
+
"activationCount": activationCount,
|
|
356
|
+
]
|
|
357
|
+
guard let data = try? JSONSerialization.data(withJSONObject: payload, options: [.sortedKeys]) else { return }
|
|
358
|
+
try? data.write(to: URL(fileURLWithPath: transcriptPath), options: .atomic)
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
let app = NSApplication.shared
|
|
363
|
+
private let delegate = FixtureDelegate()
|
|
364
|
+
app.delegate = delegate
|
|
365
|
+
app.setActivationPolicy(.regular)
|
|
366
|
+
app.run()
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
|
|
3
|
+
/// Source asset: assets/cursor.png (whole image, 64x64, hotspot at top-left).
|
|
4
|
+
/// The overlay draws it at 28x28 points with the top-left corner at the target.
|
|
5
|
+
/// Embedded transparent cursor image for the Agent cursor overlay.
|
|
6
|
+
enum EmbeddedCursorImage {
|
|
7
|
+
static let image: NSImage? = {
|
|
8
|
+
guard let data = Data(base64Encoded: "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") else { return nil }
|
|
9
|
+
return NSImage(data: data)
|
|
10
|
+
}()
|
|
11
|
+
}
|