agent-device 0.21.14 → 0.21.16

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.
Files changed (310) hide show
  1. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.14.apk → agent-device-android-ime-helper-0.21.16.apk} +0 -0
  2. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.16.apk.sha256 +1 -0
  3. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.14.manifest.json → agent-device-android-ime-helper-0.21.16.manifest.json} +4 -4
  4. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.16.apk +0 -0
  5. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.16.apk.sha256 +1 -0
  6. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.21.14.manifest.json → agent-device-android-snapshot-helper-0.21.16.manifest.json} +6 -6
  7. package/apple/snapshot-bridge/SnapshotBridgeRuntime.m +11 -1
  8. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +84 -2
  9. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.m +4 -2
  10. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerMainOwnedState.swift +27 -0
  11. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedTextEntry.h +10 -4
  12. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedTextEntry.m +21 -5
  13. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSystemSurfaceHostPolicy.swift +0 -9
  14. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +40 -5
  15. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandDispatch.swift +179 -91
  16. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +5 -4
  17. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +1 -1
  18. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+GestureExecution.swift +1 -0
  19. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +36 -49
  20. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+MainThreadWork.swift +11 -15
  21. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +136 -29
  22. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +4 -2
  23. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+PrivateAXPresentation.swift +3 -0
  24. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScreenRecorder.swift +6 -5
  25. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollDragExecution.swift +4 -0
  26. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +2 -0
  27. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +8 -4
  28. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotAcquisition.swift +56 -14
  29. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +18 -8
  30. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCaptureTarget.swift +9 -7
  31. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotExecution.swift +5 -4
  32. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotTiming.swift +2 -2
  33. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +4 -1
  34. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedInteraction.swift +5 -1
  35. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedTextEntry.swift +245 -41
  36. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +47 -2
  37. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryFocus.swift +7 -4
  38. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextTyping.swift +115 -111
  39. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +17 -10
  40. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TvRemote.swift +8 -9
  41. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TypeExecution.swift +2 -1
  42. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +12 -11
  43. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotGeometry.swift +7 -4
  44. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotModels.swift +15 -8
  45. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentationProjection.swift +1 -0
  46. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFold.swift +26 -9
  47. package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFoldProjection.swift +1 -0
  48. package/dist/src/adapter.js +1 -1
  49. package/dist/src/adb-executor.js +1 -1
  50. package/dist/src/adb-failure.js +1 -1
  51. package/dist/src/adb-transport.js +1 -1
  52. package/dist/src/agent-browser-provider.js +1 -1
  53. package/dist/src/agent-browser-tool.js +1 -1
  54. package/dist/src/agent-device-client.js +2 -2
  55. package/dist/src/ai-sdk.js +1 -1
  56. package/dist/src/alert-runtime.js +1 -1
  57. package/dist/src/android-adb.d.ts +1 -8
  58. package/dist/src/android-input-ownership.js +1 -1
  59. package/dist/src/android-snapshot-quality.js +1 -0
  60. package/dist/src/android-system-chrome.js +1 -1
  61. package/dist/src/android.js +1 -1
  62. package/dist/src/app-catalog.js +1 -1
  63. package/dist/src/app-deployment-resolution.js +1 -1
  64. package/dist/src/app-inventory.js +1 -1
  65. package/dist/src/app-launch.js +1 -1
  66. package/dist/src/app-lifecycle.js +1 -1
  67. package/dist/src/app-lifecycle2.js +1 -1
  68. package/dist/src/app-log-files.js +1 -1
  69. package/dist/src/app-log-runtime2.js +1 -1
  70. package/dist/src/app-resolution.js +2 -2
  71. package/dist/src/apple-runner-platform.js +1 -1
  72. package/dist/src/application-lifecycle-interaction.js +1 -1
  73. package/dist/src/archive.js +1 -1
  74. package/dist/src/artifact-download.js +1 -1
  75. package/dist/src/auth-session.js +1 -1
  76. package/dist/src/auth.js +1 -1
  77. package/dist/src/backend-snapshot-options.js +1 -1
  78. package/dist/src/boot-diagnostics.js +1 -1
  79. package/dist/src/capture-disclosure.js +1 -1
  80. package/dist/src/capture.js +1 -1
  81. package/dist/src/cli-config.js +1 -1
  82. package/dist/src/cli-help.js +3 -2
  83. package/dist/src/cli.js +4 -4
  84. package/dist/src/click-button.js +1 -1
  85. package/dist/src/client-metro.js +1 -1
  86. package/dist/src/client-types.d.ts +103 -96
  87. package/dist/src/clipboard-shell-response.js +1 -1
  88. package/dist/src/command-schema.js +1 -1
  89. package/dist/src/command-tools.js +3 -3
  90. package/dist/src/connection-runtime.js +1 -1
  91. package/dist/src/connection.js +5 -5
  92. package/dist/src/contracts.d.ts +1 -1
  93. package/dist/src/contracts.js +1 -1
  94. package/dist/src/daemon-client-lease-beat.js +1 -0
  95. package/dist/src/daemon-client-lifecycle.js +1 -1
  96. package/dist/src/daemon-client-transport.js +1 -0
  97. package/dist/src/daemon-shutdown-report.js +1 -1
  98. package/dist/src/daemon.js +1 -1
  99. package/dist/src/debug-symbols-facade.js +1 -1
  100. package/dist/src/device-claim-inspection.js +1 -1
  101. package/dist/src/device-claim-rule.js +2 -2
  102. package/dist/src/device-input-state.d.ts +15 -2
  103. package/dist/src/device-inventory-context.js +1 -1
  104. package/dist/src/device-rotation.js +1 -1
  105. package/dist/src/device-selection-resolver.js +1 -1
  106. package/dist/src/device-session.js +1 -1
  107. package/dist/src/device-shell.js +1 -1
  108. package/dist/src/device.js +1 -1
  109. package/dist/src/device2.js +1 -1
  110. package/dist/src/devicectl.js +1 -1
  111. package/dist/src/diagnostics.js +1 -1
  112. package/dist/src/dispatch-resolve.js +1 -1
  113. package/dist/src/doctor-facade.js +1 -1
  114. package/dist/src/doctor2.js +1 -1
  115. package/dist/src/durable-resource.js +1 -1
  116. package/dist/src/engine-eval-script.js +1 -1
  117. package/dist/src/error.js +1 -1
  118. package/dist/src/exec.js +1 -1
  119. package/dist/src/file.js +1 -1
  120. package/dist/src/find.js +1 -1
  121. package/dist/src/format.js +1 -1
  122. package/dist/src/gesture-admission.js +1 -1
  123. package/dist/src/gesture-plan-types.d.ts +1 -1
  124. package/dist/src/gesture-plan.js +1 -1
  125. package/dist/src/gesture-runtime.js +1 -1
  126. package/dist/src/harmonyos.js +1 -1
  127. package/dist/src/helper.js +2 -2
  128. package/dist/src/host.js +1 -1
  129. package/dist/src/human-control-contract.js +1 -1
  130. package/dist/src/human-control.js +1 -1
  131. package/dist/src/index.d.ts +7 -3
  132. package/dist/src/index.js +1 -1
  133. package/dist/src/input-actions.js +1 -1
  134. package/dist/src/input-actions2.js +1 -1
  135. package/dist/src/install-artifact-facade.js +1 -1
  136. package/dist/src/install-source-network-transport.js +1 -1
  137. package/dist/src/install-source2.js +1 -1
  138. package/dist/src/interaction-positionals.js +1 -1
  139. package/dist/src/interaction-ref-policy.js +1 -1
  140. package/dist/src/interaction2.js +1 -1
  141. package/dist/src/interactor-operation-binding.js +1 -1
  142. package/dist/src/interactor.js +5 -4
  143. package/dist/src/interactor2.js +1 -1
  144. package/dist/src/interactors.js +1 -1
  145. package/dist/src/internal/bin.js +1 -1
  146. package/dist/src/internal/daemon.js +2 -2
  147. package/dist/src/internal/png-worker.js +1 -1
  148. package/dist/src/internal/run-script-http-child.js +1 -1
  149. package/dist/src/inventory.js +1 -1
  150. package/dist/src/inventory2.js +1 -1
  151. package/dist/src/inventory3.js +1 -1
  152. package/dist/src/inventory5.js +1 -1
  153. package/dist/src/ios-snapshot-acquisition.js +1 -1
  154. package/dist/src/ios-snapshot-engine.js +1 -0
  155. package/dist/src/ios-snapshot-runtime.js +1 -1
  156. package/dist/src/lease-context.js +1 -1
  157. package/dist/src/lease-scope.js +1 -1
  158. package/dist/src/lease.js +1 -1
  159. package/dist/src/limrun-download-file.js +1 -1
  160. package/dist/src/limrun-runtime-dependencies.js +1 -1
  161. package/dist/src/limrun.d.ts +16 -3
  162. package/dist/src/linux.js +1 -1
  163. package/dist/src/location-coordinates.js +1 -1
  164. package/dist/src/managed-device-scope.js +1 -1
  165. package/dist/src/mechanics.js +17 -17
  166. package/dist/src/mobile-snapshot-semantics.js +1 -1
  167. package/dist/src/multitouch-support.js +1 -1
  168. package/dist/src/native-build-cache.js +1 -1
  169. package/dist/src/observation.js +1 -1
  170. package/dist/src/overlay.js +3 -0
  171. package/dist/src/parameterized-recorded-fill.js +1 -1
  172. package/dist/src/perf-facade.js +1 -1
  173. package/dist/src/perf-runtime-operation-builder.js +1 -1
  174. package/dist/src/perf-runtime-plan.js +1 -1
  175. package/dist/src/perf-target.js +1 -1
  176. package/dist/src/perf.js +1 -1
  177. package/dist/src/physical-device-control.js +1 -1
  178. package/dist/src/platform-request-scope.js +1 -1
  179. package/dist/src/platform-runtime-managed-owner.js +1 -1
  180. package/dist/src/platform-runtime-open-target.js +1 -1
  181. package/dist/src/platform-runtime-operation-host.js +4 -4
  182. package/dist/src/platform-runtime-runtime-hints.js +1 -1
  183. package/dist/src/platform-runtime-screen-recording-apple-simulator-host.js +1 -1
  184. package/dist/src/platform-runtime-screen-recording-finalizer-host.js +1 -1
  185. package/dist/src/platform-runtime-screen-recording-process-host.js +1 -1
  186. package/dist/src/platform-runtime-unavailable.js +1 -1
  187. package/dist/src/platform-runtime.js +1 -1
  188. package/dist/src/platform-runtime2.js +1 -1
  189. package/dist/src/png-crop-bytes.js +1 -1
  190. package/dist/src/png-size.js +1 -1
  191. package/dist/src/png-worker-client.js +1 -1
  192. package/dist/src/png.js +1 -1
  193. package/dist/src/prepare-kind.js +1 -1
  194. package/dist/src/provider-session-ownership.js +1 -1
  195. package/dist/src/provider-webdriver.js +1 -1
  196. package/dist/src/proxy.js +1 -1
  197. package/dist/src/quality-warnings.js +1 -1
  198. package/dist/src/react-native.js +1 -1
  199. package/dist/src/record-runtime.js +1 -1
  200. package/dist/src/recording.js +1 -1
  201. package/dist/src/rect-center.js +1 -1
  202. package/dist/src/register-builtins.js +1 -1
  203. package/dist/src/registry.js +36 -36
  204. package/dist/src/remote-config2.js +1 -1
  205. package/dist/src/replay-divergence.js +1 -1
  206. package/dist/src/replay.js +1 -1
  207. package/dist/src/reporting.js +1 -1
  208. package/dist/src/request-admission.js +1 -1
  209. package/dist/src/request-cancel.js +1 -1
  210. package/dist/src/request-runtime-binding.js +1 -1
  211. package/dist/src/result-serialization.js +1 -1
  212. package/dist/src/retry.js +1 -1
  213. package/dist/src/runner-artifact-manifest.js +1 -0
  214. package/dist/src/runner-cache-metadata.js +3 -2
  215. package/dist/src/runner-client.js +1 -1
  216. package/dist/src/runner-dev-tools-security.js +1 -1
  217. package/dist/src/runner-device-readiness.js +1 -1
  218. package/dist/src/runner-disposal.js +1 -1
  219. package/dist/src/runner-error-classification.js +3 -3
  220. package/dist/src/runner-operations-facade.js +1 -1
  221. package/dist/src/runner.js +1 -1
  222. package/dist/src/runtime-transport-hints.js +1 -1
  223. package/dist/src/runtime.js +1 -3
  224. package/dist/src/runtime10.js +2 -2
  225. package/dist/src/runtime2.js +4 -4
  226. package/dist/src/runtime3.js +1 -1
  227. package/dist/src/runtime4.js +5 -5
  228. package/dist/src/runtime5.js +1 -1
  229. package/dist/src/runtime6.js +1 -1
  230. package/dist/src/runtime7.js +1 -1
  231. package/dist/src/runtime8.js +1 -1
  232. package/dist/src/runtime9.js +1 -1
  233. package/dist/src/screen-recording-admission-ledger.js +1 -1
  234. package/dist/src/screen-recording-completion.js +1 -1
  235. package/dist/src/screen-recording-options.js +1 -1
  236. package/dist/src/screen-recording-resource-recovery.js +1 -1
  237. package/dist/src/screen-recording-transport.js +1 -1
  238. package/dist/src/screenshot-density.js +1 -1
  239. package/dist/src/screenshot-image.js +1 -1
  240. package/dist/src/screenshot-overlay-draw.js +1 -1
  241. package/dist/src/screenshot-runtime.js +1 -1
  242. package/dist/src/screenshot.js +1 -1
  243. package/dist/src/screenshot3.js +1 -1
  244. package/dist/src/script.js +1 -1
  245. package/dist/src/scroll-command.d.ts +13 -6
  246. package/dist/src/scroll-command.js +1 -1
  247. package/dist/src/scroll-gesture.js +1 -1
  248. package/dist/src/sdk-batch-runner.js +2 -2
  249. package/dist/src/sdk-batch.js +2 -2
  250. package/dist/src/sdk-contracts.d.ts +38 -10
  251. package/dist/src/sdk-contracts.js +2 -2
  252. package/dist/src/sdk-device.js +1 -1
  253. package/dist/src/sdk-finders.d.ts +1 -1
  254. package/dist/src/sdk-io.js +1 -1
  255. package/dist/src/sdk-remote-config.js +1 -1
  256. package/dist/src/sdk-selectors.d.ts +1 -1
  257. package/dist/src/selection.js +0 -0
  258. package/dist/src/selector-runtime.js +1 -1
  259. package/dist/src/server.js +1 -1
  260. package/dist/src/session-allocation.js +1 -1
  261. package/dist/src/session-routing.js +1 -1
  262. package/dist/src/session-runtime-admission.js +1 -1
  263. package/dist/src/session-snapshot.js +1 -1
  264. package/dist/src/session-store.js +3 -3
  265. package/dist/src/session.js +1 -1
  266. package/dist/src/session2.js +4 -4
  267. package/dist/src/settings.js +1 -1
  268. package/dist/src/settings2.js +1 -0
  269. package/dist/src/simctl.js +1 -1
  270. package/dist/src/simulator-boot.js +1 -1
  271. package/dist/src/simulator.js +1 -1
  272. package/dist/src/snapshot-capture-annotations.js +1 -1
  273. package/dist/src/snapshot-desktop-projection.js +1 -1
  274. package/dist/src/snapshot-runtime-binding.js +1 -1
  275. package/dist/src/snapshot-runtime.js +1 -1
  276. package/dist/src/snapshot-state.js +1 -1
  277. package/dist/src/snapshot2.js +1 -1
  278. package/dist/src/snapshot3.js +1 -1
  279. package/dist/src/snapshot4.js +1 -1
  280. package/dist/src/src.js +1 -1
  281. package/dist/src/src4.js +1 -1
  282. package/dist/src/src7.js +1 -1
  283. package/dist/src/src8.js +2 -2
  284. package/dist/src/stop-sequence.js +1 -1
  285. package/dist/src/string-enum.js +1 -1
  286. package/dist/src/surface-snapshot.js +1 -1
  287. package/dist/src/takeover.js +1 -1
  288. package/dist/src/text-entry-focus.js +1 -1
  289. package/dist/src/tool-provider.js +1 -1
  290. package/dist/src/tool-provider2.js +1 -1
  291. package/dist/src/touch-runtime.js +1 -1
  292. package/dist/src/transport.js +1 -1
  293. package/dist/src/tree.js +1 -1
  294. package/dist/src/tv-remote.js +1 -1
  295. package/dist/src/type-text-runtime.js +1 -1
  296. package/dist/src/verified-file.js +1 -1
  297. package/dist/src/video.js +1 -1
  298. package/dist/src/viewport.js +1 -1
  299. package/dist/src/web.js +1 -1
  300. package/dist/src/web2.js +1 -1
  301. package/dist/src/webdriver-source.js +1 -1
  302. package/package.json +2 -2
  303. package/server.json +2 -2
  304. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.14.apk.sha256 +0 -1
  305. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.14.apk +0 -0
  306. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.14.apk.sha256 +0 -1
  307. package/dist/src/absence-observation-errors.js +0 -1
  308. package/dist/src/runner-presentation.js +0 -1
  309. package/dist/src/semantic-index.js +0 -1
  310. package/dist/src/snapshot-scope.js +0 -1
@@ -3,6 +3,7 @@ import XCTest
3
3
 
4
4
 
5
5
  extension RunnerTests {
6
+ @MainActor
6
7
  func typeTextReliably(
7
8
  app: XCUIApplication,
8
9
  target: TextEntryTarget,
@@ -135,6 +136,39 @@ extension RunnerTests {
135
136
  }
136
137
  }
137
138
 
139
+
140
+
141
+
142
+
143
+
144
+ let synthesizedTypePlan = Self.synthesizedTextPlan(
145
+ characterCount: text.count,
146
+ delaySeconds: delaySeconds,
147
+ selectsExistingText: false,
148
+ peelsWarmupCharacter: repairMode != .none
149
+ )
150
+
151
+
152
+
153
+
154
+ let synthesizedTypeCarriesTheCommand = !SynthesizedDeliveryBudget.exceeds(synthesizedTypePlan)
155
+
156
+
157
+
158
+
159
+
160
+ func typeApplicationWide(_ value: String, reason: String) -> (element: XCUIElement?, failure: TextEntryFailure?) {
161
+ NSLog(
162
+ "AGENT_DEVICE_RUNNER_TEXT_ENTRY_ROUTE route=xctest-application-fallback reason=%@ chars=%d commandChars=%d",
163
+ reason,
164
+ value.count,
165
+ text.count
166
+ )
167
+ textEntryRoute = "xctest-application-fallback"
168
+ app.typeText(value)
169
+ return (resolveTextEntryElement(app: app, target: activeTarget), nil)
170
+ }
171
+
138
172
  func typeIntoCurrentTarget(_ value: String) -> (element: XCUIElement?, failure: TextEntryFailure?) {
139
173
  #if os(iOS)
140
174
  if shouldUseSynthesizedFirstResponderType {
@@ -162,17 +196,18 @@ extension RunnerTests {
162
196
  return (currentTarget, nil)
163
197
  } else if activeTarget.prefersFocusedElement && isKeyboardVisible(app: app) {
164
198
  #if os(iOS)
199
+
200
+
201
+
202
+
203
+ guard synthesizedTypeCarriesTheCommand else {
204
+ return typeApplicationWide(value, reason: "delivery-budget")
205
+ }
165
206
  textEntryRoute = "synthesized-first-responder"
166
207
  NSLog("AGENT_DEVICE_RUNNER_TEXT_ENTRY_ROUTE route=synthesized-first-responder")
167
- let action = synthesizer.enterText(
168
- app: app,
169
- text: value,
170
- replacingExistingText: false
171
- )
172
- switch action {
208
+ switch synthesizer.enterText(app: app, text: value, replacingExistingText: false) {
173
209
  case .fallback:
174
- textEntryRoute = "xctest-application-fallback"
175
- app.typeText(value)
210
+ return typeApplicationWide(value, reason: "synthesis-unavailable")
176
211
  case .raise(let message):
177
212
  NSException(
178
213
  name: NSExceptionName.internalInconsistencyException,
@@ -200,131 +235,100 @@ extension RunnerTests {
200
235
  )
201
236
  }
202
237
 
203
- func waitForWarmupValue(_ expectedValue: String?, target: TextEntryTarget) {
204
- guard let expectedValue else {
238
+ func readBackWarmupCharacter(_ peeledCharacter: String) {
239
+
240
+
241
+
242
+ let warmupExpectedText = expectedTextEntryValue(
243
+ typedText: peeledCharacter,
244
+ mode: repairMode,
245
+ initialText: initialText
246
+ )
247
+ guard let warmupExpectedText else {
205
248
  sleepFor(TextEntryTiming.pollInterval)
206
249
  return
207
250
  }
208
251
  let deadline = Date().addingTimeInterval(TextEntryTiming.warmupValueTimeout)
209
252
  while Date() < deadline {
210
- if editableTextValue(for: resolveTextEntryElement(app: app, target: target)) == expectedValue {
253
+ if editableTextValue(for: resolveTextEntryElement(app: app, target: activeTarget)) == warmupExpectedText {
211
254
  return
212
255
  }
213
256
  sleepFor(TextEntryTiming.pollInterval)
214
257
  }
215
258
  }
216
259
 
217
- let characters = Array(text)
218
- if delaySeconds > 0 && characters.count > 1 {
219
- var typedTarget: XCUIElement?
220
- let delayedTypeStartedAt = Date()
221
- for (index, character) in characters.enumerated() {
222
- let dispatch = typeIntoCurrentTarget(String(character))
260
+
261
+
262
+ var typedTarget: XCUIElement?
263
+ var sawWarmupPost = false
264
+ var planFailure: TextEntryResult?
265
+ var stepStartedAt = Date()
266
+ let pacedStartedAt = Date()
267
+ runSynthesizedTextPlan(
268
+ synthesizedTypePlan,
269
+ text: text,
270
+ post: { slice, step in
271
+ stepStartedAt = Date()
272
+ let dispatch = typeIntoCurrentTarget(slice)
223
273
  if let failure = dispatch.failure {
224
- return dispatchFailureResult(failure)
274
+ planFailure = dispatchFailureResult(failure)
275
+ return .stop
225
276
  }
226
277
  typedTarget = dispatch.element ?? typedTarget
227
- if index + 1 < characters.count {
228
- sleepFor(delaySeconds)
278
+ if step.warmsUpField {
279
+
280
+ activeTarget = activeTarget.withElement(typedTarget)
229
281
  }
230
- }
231
- logTextEntryPhase(
232
- commandId: commandId,
233
- phase: "type-delayed",
234
- startedAt: delayedTypeStartedAt,
235
- chars: characters.count,
236
- mode: repairMode
237
- )
238
- if repairMode == .none {
239
- logTextEntryPhase(commandId: commandId, phase: "total", startedAt: totalStartedAt, chars: text.count, mode: repairMode)
240
- return TextEntryResult(
241
- verified: nil,
242
- repaired: false,
243
- expectedText: nil,
244
- observedText: nil,
245
- textEntryRoute: textEntryRoute
282
+ return .posted
283
+ },
284
+ waitAfterWarmupCharacter: { peeledCharacter in
285
+ let warmupStartedAt = Date()
286
+ readBackWarmupCharacter(peeledCharacter)
287
+ logTextEntryPhase(
288
+ commandId: commandId,
289
+ phase: "warmup",
290
+ startedAt: warmupStartedAt,
291
+ chars: 1,
292
+ mode: repairMode
293
+ )
294
+ },
295
+ didPostStep: { step in
296
+ guard !synthesizedTypePlan.pacesEveryCharacter else {
297
+ return
298
+ }
299
+ let phase: String
300
+ if step.warmsUpField {
301
+ phase = "type-first"
302
+ sawWarmupPost = true
303
+ } else {
304
+ phase = sawWarmupPost ? "type-remaining" : "type-all"
305
+ }
306
+ logTextEntryPhase(
307
+ commandId: commandId,
308
+ phase: phase,
309
+ startedAt: stepStartedAt,
310
+ chars: step.characterCount,
311
+ mode: repairMode
246
312
  )
247
313
  }
248
- let verifyStartedAt = Date()
249
- var result = verifyTextEntryWithRepairIfNeeded(
250
- app: app,
251
- target: activeTarget.withElement(typedTarget),
252
- expectedText: expectedText,
253
- repairMode: repairMode
254
- )
314
+ )
315
+ if synthesizedTypePlan.pacesEveryCharacter {
316
+
317
+
255
318
  logTextEntryPhase(
256
319
  commandId: commandId,
257
- phase: "verify",
258
- startedAt: verifyStartedAt,
259
- chars: characters.count,
320
+ phase: "type-delayed",
321
+ startedAt: pacedStartedAt,
322
+ chars: text.count,
260
323
  mode: repairMode
261
324
  )
262
- logTextEntryPhase(commandId: commandId, phase: "total", startedAt: totalStartedAt, chars: text.count, mode: repairMode)
263
- result.textEntryRoute = textEntryRoute
264
- return result
265
325
  }
266
326
 
267
- let typedTarget: XCUIElement?
268
- if repairMode != .none && characters.count > 1 {
269
- let firstCharacter = String(characters[0])
270
- let firstStartedAt = Date()
271
- let firstDispatch = typeIntoCurrentTarget(firstCharacter)
272
- if let failure = firstDispatch.failure {
273
- return dispatchFailureResult(failure)
274
- }
275
- var firstTypedTarget = firstDispatch.element
276
- logTextEntryPhase(
277
- commandId: commandId,
278
- phase: "type-first",
279
- startedAt: firstStartedAt,
280
- chars: 1,
281
- mode: repairMode
282
- )
283
- activeTarget = activeTarget.withElement(firstTypedTarget)
284
- let warmupExpectedText = expectedTextEntryValue(
285
- typedText: firstCharacter,
286
- mode: repairMode,
287
- initialText: initialText
288
- )
289
- let warmupStartedAt = Date()
290
- waitForWarmupValue(warmupExpectedText, target: activeTarget)
291
- logTextEntryPhase(
292
- commandId: commandId,
293
- phase: "warmup",
294
- startedAt: warmupStartedAt,
295
- chars: 1,
296
- mode: repairMode
297
- )
298
- let remainingText = String(characters.dropFirst())
299
- let remainingStartedAt = Date()
300
- let remainingDispatch = typeIntoCurrentTarget(remainingText)
301
- if let failure = remainingDispatch.failure {
302
- return dispatchFailureResult(failure)
303
- }
304
- firstTypedTarget = remainingDispatch.element ?? firstTypedTarget
305
- logTextEntryPhase(
306
- commandId: commandId,
307
- phase: "type-remaining",
308
- startedAt: remainingStartedAt,
309
- chars: characters.count - 1,
310
- mode: repairMode
311
- )
312
- typedTarget = firstTypedTarget
313
- } else {
314
- let typeStartedAt = Date()
315
- let dispatch = typeIntoCurrentTarget(text)
316
- if let failure = dispatch.failure {
317
- return dispatchFailureResult(failure)
318
- }
319
- typedTarget = dispatch.element
320
- logTextEntryPhase(
321
- commandId: commandId,
322
- phase: "type-all",
323
- startedAt: typeStartedAt,
324
- chars: characters.count,
325
- mode: repairMode
326
- )
327
+
328
+ if let failure = planFailure {
329
+ return failure
327
330
  }
331
+ activeTarget = activeTarget.withElement(typedTarget)
328
332
  if repairMode == .none {
329
333
  logTextEntryPhase(commandId: commandId, phase: "total", startedAt: totalStartedAt, chars: text.count, mode: repairMode)
330
334
  return TextEntryResult(
@@ -338,7 +342,7 @@ extension RunnerTests {
338
342
  let verifyStartedAt = Date()
339
343
  var result = verifyTextEntryWithRepairIfNeeded(
340
344
  app: app,
341
- target: activeTarget.withElement(typedTarget),
345
+ target: activeTarget,
342
346
  expectedText: expectedText,
343
347
  repairMode: repairMode
344
348
  )
@@ -346,7 +350,7 @@ extension RunnerTests {
346
350
  commandId: commandId,
347
351
  phase: "verify",
348
352
  startedAt: verifyStartedAt,
349
- chars: characters.count,
353
+ chars: text.count,
350
354
  mode: repairMode
351
355
  )
352
356
  logTextEntryPhase(commandId: commandId, phase: "total", startedAt: totalStartedAt, chars: text.count, mode: repairMode)
@@ -33,11 +33,7 @@ extension RunnerTests {
33
33
  let combined = buffer + data
34
34
  if let body = self.parseRequest(data: combined) {
35
35
  self.handleRequestBody(body) { [weak self] result in
36
- self?.sendResponse(result.data, over: connection) { [weak self] in
37
- if result.shouldFinish {
38
- self?.finish()
39
- }
40
- }
36
+ self?.sendResult(result, over: connection)
41
37
  }
42
38
  } else {
43
39
  self.receiveRequest(connection: connection, buffer: combined)
@@ -45,10 +41,21 @@ extension RunnerTests {
45
41
  }
46
42
  }
47
43
 
44
+ private func sendResult(
45
+ _ result: (data: Data, shouldFinish: Bool),
46
+ over connection: NWConnection
47
+ ) {
48
+ sendResponse(result.data, over: connection) { [weak self] in
49
+ if result.shouldFinish {
50
+ self?.finish()
51
+ }
52
+ }
53
+ }
54
+
48
55
  private func sendResponse(
49
56
  _ response: Data,
50
57
  over connection: NWConnection,
51
- afterSend: @escaping () -> Void = {}
58
+ afterSend: @escaping @Sendable () -> Void = {}
52
59
  ) {
53
60
  connection.send(content: response, isComplete: true, completion: .contentProcessed { error in
54
61
  if let error {
@@ -89,7 +96,7 @@ extension RunnerTests {
89
96
 
90
97
  private func handleRequestBody(
91
98
  _ body: Data,
92
- completion: @escaping ((data: Data, shouldFinish: Bool)) -> Void
99
+ completion: @escaping @Sendable ((data: Data, shouldFinish: Bool)) -> Void
93
100
  ) {
94
101
  guard String(data: body, encoding: .utf8) != nil else {
95
102
  completion((
@@ -187,7 +194,7 @@ extension RunnerTests {
187
194
 
188
195
  func enqueueAccepted(
189
196
  command: Command,
190
- completion: @escaping (Result<Response, Error>) -> Void
197
+ completion: @escaping @Sendable (Result<Response, Error>) -> Void
191
198
  ) {
192
199
  commandJournal.accept(command: command)
193
200
  commandExecutionQueue.async {
@@ -202,7 +209,7 @@ extension RunnerTests {
202
209
 
203
210
  func attachToInFlightCommandIfNeeded(
204
211
  command: Command,
205
- completion: @escaping ((data: Data, shouldFinish: Bool)) -> Void
212
+ completion: @escaping @Sendable ((data: Data, shouldFinish: Bool)) -> Void
206
213
  ) -> Bool {
207
214
  guard let commandId = command.commandId?.trimmedNonEmpty else { return false }
208
215
  inFlightCommandLock.lock()
@@ -226,7 +233,7 @@ extension RunnerTests {
226
233
  result: (data: Data, shouldFinish: Bool),
227
234
  completion: ((data: Data, shouldFinish: Bool)) -> Void
228
235
  ) {
229
- var waiters: [((data: Data, shouldFinish: Bool)) -> Void] = []
236
+ var waiters: [@Sendable ((data: Data, shouldFinish: Bool)) -> Void] = []
230
237
  if let commandId = command.commandId?.trimmedNonEmpty {
231
238
  inFlightCommandLock.lock()
232
239
  inFlightCommandIds.remove(commandId)
@@ -52,23 +52,22 @@ extension RunnerTests {
52
52
  }
53
53
 
54
54
  func elementHasFocus(_ element: XCUIElement) -> Bool {
55
- var focused = false
56
- _ = RunnerObjCExceptionCatcher.catchException({
57
- if let value = (element as NSObject).value(forKey: "hasFocus") as? Bool {
58
- focused = value
59
- }
60
- })
61
- return focused
55
+ return focusBool(element as NSObject)
62
56
  }
63
57
 
64
- func activateElement(app: XCUIApplication, element: XCUIElement, action: String) -> RunnerInteractionOutcome {
58
+ func activateElement(
59
+ app: XCUIApplication,
60
+ element: XCUIElement,
61
+ action: String,
62
+ resolvedFrame: CGRect? = nil
63
+ ) -> RunnerInteractionOutcome {
65
64
  if let outcome = selectFocusedTvElement(app: app, element: element, action: action) {
66
65
  return outcome
67
66
  }
68
67
  #if os(tvOS)
69
68
  return performElementTap(element)
70
69
  #else
71
- let frame = element.frame
70
+ let frame = resolvedFrame ?? element.frame
72
71
  if !frame.isEmpty {
73
72
 
74
73
 
@@ -2,6 +2,7 @@ import XCTest
2
2
  import AgentDeviceSnapshotPresentation
3
3
 
4
4
  extension RunnerTests {
5
+ @MainActor
5
6
  func executeTypeCommand(activeApp: XCUIApplication, command: Command) -> Response {
6
7
  guard let text = command.text else {
7
8
  return Response(ok: false, error: ErrorPayload(message: "type requires text"))
@@ -18,7 +19,7 @@ extension RunnerTests {
18
19
  : nil
19
20
  let focusStartedAt = Date()
20
21
  #if os(iOS)
21
- let xCTestChannelPenalized = isSnapshotXCTestChannelPenalized(bundleId: currentBundleId)
22
+ let xCTestChannelPenalized = isSnapshotXCTestChannelPenalized(bundleId: mainOwned.bundleId)
22
23
  var resolvedCoordinateTarget: TextEntryTarget?
23
24
  if Self.shouldUseResolvedCoordinateTextEntryRoute(
24
25
  repairMode: textEntryMode,
@@ -54,11 +54,7 @@ final class RunnerTests: XCTestCase {
54
54
  let commandExecutionQueue = DispatchQueue(label: "agent-device.runner.commands")
55
55
  let app = XCUIApplication()
56
56
  lazy var springboard = XCUIApplication(bundleIdentifier: Self.springboardBundleId)
57
-
58
-
59
- var currentApp: XCUIApplication?
60
- var currentBundleId: String?
61
- var currentAppProcessIdentifier: Int?
57
+ let mainOwned = RunnerMainOwnedState()
62
58
 
63
59
 
64
60
  var pendingTargetActivation: TargetActivationFactPayload?
@@ -68,7 +64,7 @@ final class RunnerTests: XCTestCase {
68
64
 
69
65
  var textEntryTapWitness: TextEntryTapWitness?
70
66
  let maxRequestBytes = 2 * 1024 * 1024
71
- let mainThreadExecutionTimeout: TimeInterval = 30
67
+ static let mainThreadExecutionTimeout: TimeInterval = 30
72
68
  let appExistenceTimeout: TimeInterval = 30
73
69
  let retryCooldown: TimeInterval = 0.2
74
70
  let postSnapshotInteractionDelay: TimeInterval = 0.2
@@ -83,27 +79,25 @@ final class RunnerTests: XCTestCase {
83
79
 
84
80
 
85
81
  let recordingFrameCaptureTimeout: TimeInterval = 1
86
- var needsPostSnapshotInteractionDelay = false
87
82
 
88
83
 
89
84
 
90
85
 
91
86
  var lastLoggedFastAppGuardLine: String?
92
87
  var lastLoggedGesturePolicyLines: [SynthesizedGesturePolicyKind: String] = [:]
93
- var runnerMarkerWriter: (String) -> Void = { NSLog("%@", $0) }
88
+ var runnerMarkerWriter: @MainActor (String) -> Void = { NSLog("%@", $0) }
94
89
 
95
90
 
96
91
 
97
92
 
98
93
  var firstInteractionReadyUptime: TimeInterval?
99
- var runnerAccessibilityHealth: RunnerAccessibilityHealth = .unknown
100
94
  var activeRecording: ScreenRecorder?
101
95
  let commandJournal = RunnerCommandJournal()
102
96
 
103
97
 
104
98
  let inFlightCommandLock = NSLock()
105
99
  var inFlightCommandIds: Set<String> = []
106
- var inFlightCommandWaiters: [String: [((data: Data, shouldFinish: Bool)) -> Void]] = [:]
100
+ var inFlightCommandWaiters: [String: [@Sendable ((data: Data, shouldFinish: Bool)) -> Void]] = [:]
107
101
 
108
102
 
109
103
 
@@ -206,6 +200,13 @@ final class RunnerTests: XCTestCase {
206
200
 
207
201
 
208
202
 
203
+
204
+
205
+
206
+
207
+
208
+
209
+
209
210
 
210
211
 
211
212
 
@@ -316,7 +317,7 @@ final class RunnerTests: XCTestCase {
316
317
  deadline: .now() + xctestIdleKeepaliveInterval,
317
318
  repeating: xctestIdleKeepaliveInterval
318
319
  )
319
- idleKeepaliveTimer.setEventHandler {
320
+ idleKeepaliveTimer.setEventHandler { @Sendable in
320
321
  NSLog("AGENT_DEVICE_RUNNER_IDLE_KEEPALIVE")
321
322
  }
322
323
  idleKeepaliveTimer.resume()
@@ -2,12 +2,15 @@ import Foundation
2
2
  import CoreGraphics
3
3
 
4
4
  public enum SnapshotGeometry {
5
- /// Twin of `isPositiveFiniteRect` in `packages/kernel/src/rect.ts`. `CGRect.infinite` is built
6
- /// from finite components, so it is refused by identity.
5
+ /// Twin of `isPositiveFiniteRect` in `packages/kernel/src/rect.ts`, and the one place the
6
+ /// `hittable` rule asks whether a box may be plotted or measured (#2891). Three refusals, each
7
+ /// reachable by a different input: a non-finite component, a finite box wide enough to overflow
8
+ /// its own right or bottom edge, and `CGRect.infinite`, which is built of finite components and
9
+ /// finite extents and so is refused by identity alone.
7
10
  public static func isPositiveFinite(_ rect: CGRect) -> Bool {
8
11
  !rect.isInfinite
9
- && rect.origin.x.isFinite && rect.origin.y.isFinite
10
- && rect.size.width.isFinite && rect.size.height.isFinite
12
+ && rect.minX.isFinite && rect.minY.isFinite
13
+ && rect.maxX.isFinite && rect.maxY.isFinite
11
14
  && rect.size.width > 0 && rect.size.height > 0
12
15
  }
13
16
 
@@ -34,6 +34,8 @@ public struct RawAXNode: Equatable {
34
34
  public let label: String?
35
35
  public let identifier: String?
36
36
  public let value: String?
37
+ /// The text a text field shows while empty (`placeholderValue`); `nil` for every other node.
38
+ public let placeholder: String?
37
39
  public var rect: SnapshotRect
38
40
  public let enabled: Bool
39
41
  public let focused: Bool?
@@ -52,6 +54,7 @@ public struct RawAXNode: Equatable {
52
54
  label: String?,
53
55
  identifier: String?,
54
56
  value: String?,
57
+ placeholder: String? = nil,
55
58
  rect: SnapshotRect,
56
59
  enabled: Bool,
57
60
  focused: Bool?,
@@ -68,6 +71,7 @@ public struct RawAXNode: Equatable {
68
71
  self.label = label
69
72
  self.identifier = identifier
70
73
  self.value = value
74
+ self.placeholder = placeholder
71
75
  self.rect = rect
72
76
  self.enabled = enabled
73
77
  self.focused = focused
@@ -157,11 +161,14 @@ public struct PresentationOptions: Equatable {
157
161
  /// What a capture knows about the viewport hosting its tree: the three cases of the host's
158
162
  /// `IosViewportEvidence` (#2891). No rectangle stands for "unknown".
159
163
  public enum SnapshotViewport: Equatable {
160
- /// A box `SnapshotGeometry.isPositiveFinite` accepted. Only the factories below construct one.
164
+ /// A box that was checked on the way in. The initializer is the only gate: no caller, inside this
165
+ /// package or outside it, holds a `Box` whose rectangle `SnapshotGeometry.isPositiveFinite`
166
+ /// refuses, so a `.reported` case never needs re-checking what it was handed.
161
167
  public struct Box: Equatable {
162
168
  public let rect: CGRect
163
169
 
164
- init(positiveFinite rect: CGRect) {
170
+ init?(checked rect: CGRect) {
171
+ guard SnapshotGeometry.isPositiveFinite(rect) else { return nil }
165
172
  self.rect = rect
166
173
  }
167
174
  }
@@ -194,15 +201,13 @@ public enum SnapshotViewport: Equatable {
194
201
  box: CGRect,
195
202
  interfaceOrientation: Int = RunnerInterfaceOrientation.unknown
196
203
  ) -> SnapshotViewport {
197
- SnapshotGeometry.isPositiveFinite(box)
198
- ? .reported(Box(positiveFinite: box), interfaceOrientation: interfaceOrientation)
199
- : .missing(reason: .invalid)
204
+ guard let checked = Box(checked: box) else { return .missing(reason: .invalid) }
205
+ return .reported(checked, interfaceOrientation: interfaceOrientation)
200
206
  }
201
207
 
202
208
  public static func derived(box: CGRect) -> SnapshotViewport {
203
- SnapshotGeometry.isPositiveFinite(box)
204
- ? .derived(Box(positiveFinite: box))
205
- : .missing(reason: .invalid)
209
+ guard let checked = Box(checked: box) else { return .missing(reason: .invalid) }
210
+ return .derived(checked)
206
211
  }
207
212
  }
208
213
 
@@ -271,6 +276,7 @@ public struct PresentedNode: Codable, Equatable {
271
276
  public let label: String?
272
277
  public let identifier: String?
273
278
  public let value: String?
279
+ public let placeholder: String?
274
280
  public let rect: SnapshotRect
275
281
  public let enabled: Bool
276
282
  public let focused: Bool?
@@ -294,6 +300,7 @@ public struct PresentedNode: Codable, Equatable {
294
300
  self.label = raw.label
295
301
  self.identifier = raw.identifier
296
302
  self.value = raw.value
303
+ self.placeholder = raw.placeholder
297
304
  self.rect = rect ?? raw.rect
298
305
  self.enabled = raw.enabled
299
306
  self.focused = raw.focused
@@ -140,6 +140,7 @@ extension SnapshotPresentation {
140
140
  label: raw.label,
141
141
  identifier: raw.identifier,
142
142
  value: raw.value,
143
+ placeholder: raw.placeholder,
143
144
  rect: raw.rect,
144
145
  enabled: raw.enabled,
145
146
  focused: raw.focused,
@@ -199,21 +199,19 @@ public enum SnapshotVisibilityFold {
199
199
  label: node.label,
200
200
  identifier: node.identifier,
201
201
  value: node.value,
202
+ placeholder: node.placeholder,
202
203
  rect: node.rect,
203
204
  enabled: node.enabled,
204
205
  focused: node.focused,
205
206
  selected: node.selected,
206
207
  hittable: node.parentIndex == nil
207
208
  ? false
208
- : node.hittable.flatMap { sourceHittable in
209
- sourceHittable
210
- ? SnapshotGeometry.isGeometricallyActionable(
211
- enabled: node.enabled,
212
- frame: decision.effectiveFrame,
213
- viewport: viewport
214
- )
215
- : false
216
- },
209
+ : clippedHittability(
210
+ source: node.hittable,
211
+ enabled: node.enabled,
212
+ clippedFrame: decision.effectiveFrame,
213
+ viewport: viewport
214
+ ),
217
215
  depth: outDepth,
218
216
  parentIndex: keptIndex,
219
217
  hiddenContentAbove: node.hiddenContentAbove,
@@ -244,4 +242,23 @@ public enum SnapshotVisibilityFold {
244
242
  return applyHiddenContentHints(hints, to: kept)
245
243
  }
246
244
 
245
+ /// The fold's share of the `hittable` policy (#2891). A declared `false` is kept; anything the
246
+ /// source left undecided is re-decided on the clipped frame: a disabled or degenerate frame —
247
+ /// including a `visibilityExempt` carrier a scroll anchor clipped to nothing — answers `false`,
248
+ /// and `nil` stays reserved for the one question the clipped frame cannot answer, containment
249
+ /// while the capture has no viewport box.
250
+ private static func clippedHittability(
251
+ source: Bool?,
252
+ enabled: Bool,
253
+ clippedFrame: CGRect,
254
+ viewport: SnapshotViewport
255
+ ) -> Bool? {
256
+ if source == false { return false }
257
+ return SnapshotGeometry.isGeometricallyActionable(
258
+ enabled: enabled,
259
+ frame: clippedFrame,
260
+ viewport: viewport
261
+ )
262
+ }
263
+
247
264
  }
@@ -79,6 +79,7 @@ extension SnapshotVisibilityFold {
79
79
  label: node.label,
80
80
  identifier: node.identifier,
81
81
  value: node.value,
82
+ placeholder: node.placeholder,
82
83
  rect: node.rect,
83
84
  enabled: node.enabled,
84
85
  focused: node.focused,