@itmetsam/samurai-framework 0.10.0 → 0.12.1

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 (326) hide show
  1. package/README.md +3 -4
  2. package/dist/assert/assertion-error.d.ts +1 -1
  3. package/dist/assert/expect.d.ts +5 -50
  4. package/dist/assert/expect.js +5 -182
  5. package/dist/assert/locator-assertions.d.ts +22 -0
  6. package/dist/assert/locator-assertions.js +61 -0
  7. package/dist/assert/pending-assertions.d.ts +7 -0
  8. package/dist/assert/pending-assertions.js +40 -0
  9. package/dist/assert/text-match.d.ts +4 -0
  10. package/dist/assert/text-match.js +11 -0
  11. package/dist/assert/value-assertions.d.ts +23 -0
  12. package/dist/assert/value-assertions.js +82 -0
  13. package/dist/browser/browser-context.d.ts +0 -1
  14. package/dist/browser/browser-context.js +13 -5
  15. package/dist/browser/browser-finder.js +5 -9
  16. package/dist/browser/browser.d.ts +3 -31
  17. package/dist/browser/browser.js +36 -219
  18. package/dist/browser/chrome-launch.d.ts +2 -1
  19. package/dist/browser/dialog-handling.d.ts +32 -0
  20. package/dist/browser/dialog-handling.js +85 -0
  21. package/dist/browser/document-watcher.d.ts +44 -0
  22. package/dist/browser/document-watcher.js +160 -0
  23. package/dist/browser/download-folders.d.ts +0 -1
  24. package/dist/browser/download-folders.js +7 -15
  25. package/dist/browser/download.js +5 -10
  26. package/dist/browser/downloads-dir.js +2 -2
  27. package/dist/browser/emit.d.ts +7 -0
  28. package/dist/browser/emit.js +16 -0
  29. package/dist/browser/emulation.d.ts +1 -1
  30. package/dist/browser/emulation.js +54 -18
  31. package/dist/browser/file-chooser.js +4 -11
  32. package/dist/browser/history.d.ts +6 -0
  33. package/dist/browser/history.js +89 -0
  34. package/dist/browser/index.d.ts +2 -0
  35. package/dist/browser/index.js +2 -0
  36. package/dist/browser/init-script.d.ts +2 -0
  37. package/dist/browser/init-script.js +16 -0
  38. package/dist/browser/launch-firefox.d.ts +20 -0
  39. package/dist/browser/launch-firefox.js +66 -0
  40. package/dist/browser/launch-flags.d.ts +18 -0
  41. package/dist/browser/launch-flags.js +100 -0
  42. package/dist/browser/lazy.d.ts +21 -0
  43. package/dist/browser/lazy.js +44 -0
  44. package/dist/browser/page-events.d.ts +44 -0
  45. package/dist/browser/page-events.js +58 -0
  46. package/dist/browser/page-logs.js +2 -9
  47. package/dist/browser/page-navigation.d.ts +58 -0
  48. package/dist/browser/page-navigation.js +108 -0
  49. package/dist/browser/page-network.d.ts +69 -0
  50. package/dist/browser/page-network.js +163 -0
  51. package/dist/browser/page-settings.d.ts +35 -0
  52. package/dist/browser/page-settings.js +51 -0
  53. package/dist/browser/page-transfers.d.ts +36 -0
  54. package/dist/browser/page-transfers.js +50 -0
  55. package/dist/browser/page.d.ts +39 -154
  56. package/dist/browser/page.js +134 -758
  57. package/dist/browser/pdf.d.ts +1 -1
  58. package/dist/browser/session.d.ts +16 -0
  59. package/dist/browser/session.js +32 -0
  60. package/dist/browser/viewport.d.ts +19 -0
  61. package/dist/browser/viewport.js +72 -0
  62. package/dist/browser/warm-up.d.ts +1 -1
  63. package/dist/browser/warm-up.js +2 -1
  64. package/dist/browsers/index.d.ts +1 -1
  65. package/dist/browsers/index.js +1 -1
  66. package/dist/browsers/pins.d.ts +0 -2
  67. package/dist/browsers/pins.js +0 -2
  68. package/dist/browsers/store.d.ts +0 -2
  69. package/dist/browsers/store.js +1 -2
  70. package/dist/cli/args.d.ts +13 -6
  71. package/dist/cli/args.js +13 -10
  72. package/dist/cli/browsers.d.ts +1 -2
  73. package/dist/cli/browsers.js +2 -2
  74. package/dist/cli/console-reporter.js +3 -2
  75. package/dist/cli/flow-reporter.js +2 -1
  76. package/dist/cli/flow.d.ts +1 -2
  77. package/dist/cli/flow.js +25 -22
  78. package/dist/cli/init.js +2 -7
  79. package/dist/cli/io.d.ts +10 -0
  80. package/dist/cli/io.js +1 -0
  81. package/dist/cli/main.d.ts +3 -10
  82. package/dist/cli/main.js +10 -143
  83. package/dist/cli/paint.d.ts +2 -0
  84. package/dist/cli/paint.js +4 -0
  85. package/dist/cli/record.d.ts +4 -0
  86. package/dist/cli/record.js +93 -0
  87. package/dist/cli/usage.d.ts +1 -0
  88. package/dist/cli/usage.js +58 -0
  89. package/dist/cli/version.d.ts +2 -0
  90. package/dist/cli/version.js +7 -0
  91. package/dist/config/browsers.d.ts +11 -0
  92. package/dist/config/browsers.js +32 -0
  93. package/dist/config/config.js +4 -1
  94. package/dist/config/mask.d.ts +1 -1
  95. package/dist/config/mask.js +7 -2
  96. package/dist/config/run-settings.d.ts +1 -2
  97. package/dist/config/run-settings.js +1 -6
  98. package/dist/config/timeouts.d.ts +2 -0
  99. package/dist/config/timeouts.js +13 -0
  100. package/dist/config/workers.d.ts +14 -0
  101. package/dist/config/workers.js +29 -0
  102. package/dist/flows/run/branch-context.d.ts +14 -0
  103. package/dist/flows/run/branch-context.js +35 -0
  104. package/dist/flows/run/handlers/condition.d.ts +1 -1
  105. package/dist/flows/run/handlers/end.d.ts +1 -1
  106. package/dist/flows/run/handlers/group.d.ts +1 -1
  107. package/dist/flows/run/handlers/index.d.ts +2 -2
  108. package/dist/flows/run/handlers/parallel.d.ts +1 -1
  109. package/dist/flows/run/handlers/start.d.ts +1 -1
  110. package/dist/flows/run/handlers/support.d.ts +3 -27
  111. package/dist/flows/run/handlers/test.d.ts +1 -1
  112. package/dist/flows/run/handlers/types.d.ts +27 -0
  113. package/dist/flows/run/handlers/types.js +1 -0
  114. package/dist/flows/run/index.d.ts +1 -1
  115. package/dist/flows/run/prepare-flow.d.ts +14 -0
  116. package/dist/flows/run/prepare-flow.js +44 -0
  117. package/dist/flows/run/project.d.ts +33 -2
  118. package/dist/flows/run/project.js +139 -57
  119. package/dist/flows/run/run.d.ts +3 -4
  120. package/dist/flows/run/run.js +23 -193
  121. package/dist/flows/run/walker.d.ts +56 -0
  122. package/dist/flows/run/walker.js +225 -0
  123. package/dist/flows/steps/catalog.d.ts +1 -1
  124. package/dist/flows/steps/handler.d.ts +1 -1
  125. package/dist/flows/steps/set.d.ts +1 -1
  126. package/dist/index.js +1 -1
  127. package/dist/locator/actionability.d.ts +33 -0
  128. package/dist/locator/actionability.js +104 -0
  129. package/dist/locator/chain-resolver.d.ts +20 -0
  130. package/dist/locator/chain-resolver.js +102 -0
  131. package/dist/locator/document-swap.d.ts +1 -0
  132. package/dist/locator/document-swap.js +11 -0
  133. package/dist/locator/element-state.d.ts +3 -1
  134. package/dist/locator/element-state.js +11 -3
  135. package/dist/locator/file-actions.d.ts +35 -0
  136. package/dist/locator/file-actions.js +55 -0
  137. package/dist/{script/helpers.js → locator/helper-realm.js} +2 -2
  138. package/dist/locator/input-actions.d.ts +18 -0
  139. package/dist/locator/input-actions.js +80 -0
  140. package/dist/locator/label-locator.d.ts +1 -1
  141. package/dist/locator/label-locator.js +5 -12
  142. package/dist/locator/locator.d.ts +14 -37
  143. package/dist/locator/locator.js +68 -424
  144. package/dist/locator/page-scripts.d.ts +16 -0
  145. package/dist/locator/page-scripts.js +54 -0
  146. package/dist/locator/page-search.d.ts +10 -0
  147. package/dist/locator/page-search.js +19 -0
  148. package/dist/locator/selector-errors.d.ts +0 -4
  149. package/dist/locator/selector-errors.js +0 -7
  150. package/dist/locator/selector.d.ts +14 -13
  151. package/dist/locator/selector.js +10 -16
  152. package/dist/locator/text-locator.d.ts +1 -1
  153. package/dist/locator/text-locator.js +5 -9
  154. package/dist/logger/index.d.ts +6 -0
  155. package/dist/logger/index.js +14 -7
  156. package/dist/network/bidi-wire.d.ts +10 -0
  157. package/dist/network/bidi-wire.js +38 -0
  158. package/dist/network/intercept.d.ts +30 -0
  159. package/dist/network/intercept.js +130 -0
  160. package/dist/network/network-tracker.d.ts +10 -47
  161. package/dist/network/network-tracker.js +34 -68
  162. package/dist/network/network-types.d.ts +33 -0
  163. package/dist/network/network-types.js +1 -0
  164. package/dist/network/network-wait.d.ts +7 -2
  165. package/dist/network/network-wait.js +14 -4
  166. package/dist/network/response.d.ts +1 -1
  167. package/dist/network/response.js +1 -1
  168. package/dist/network/route.d.ts +92 -0
  169. package/dist/network/route.js +142 -0
  170. package/dist/network/router.d.ts +6 -91
  171. package/dist/network/router.js +25 -258
  172. package/dist/recorder/expectation.d.ts +5 -0
  173. package/dist/recorder/expectation.js +20 -0
  174. package/dist/recorder/files-recorder.d.ts +30 -0
  175. package/dist/recorder/files-recorder.js +88 -0
  176. package/dist/recorder/normalise.d.ts +17 -6
  177. package/dist/recorder/normalise.js +96 -91
  178. package/dist/recorder/options.d.ts +56 -0
  179. package/dist/recorder/options.js +1 -0
  180. package/dist/recorder/page-messages.d.ts +35 -0
  181. package/dist/recorder/page-messages.js +26 -0
  182. package/dist/recorder/ranking.js +1 -0
  183. package/dist/recorder/realms.d.ts +6 -0
  184. package/dist/recorder/realms.js +13 -0
  185. package/dist/recorder/recorder.d.ts +10 -74
  186. package/dist/recorder/recorder.js +39 -199
  187. package/dist/recorder/session-browser.d.ts +22 -0
  188. package/dist/recorder/session-browser.js +21 -0
  189. package/dist/recorder/session-secrets.d.ts +8 -0
  190. package/dist/recorder/session-secrets.js +45 -0
  191. package/dist/recorder/session.d.ts +5 -14
  192. package/dist/recorder/session.js +22 -120
  193. package/dist/recorder/spec-source.d.ts +39 -0
  194. package/dist/recorder/spec-source.js +84 -0
  195. package/dist/recorder/toolbar/action-guard.d.ts +21 -0
  196. package/dist/recorder/toolbar/action-guard.js +43 -0
  197. package/dist/recorder/toolbar/actions/assert.d.ts +4 -0
  198. package/dist/recorder/toolbar/actions/assert.js +52 -0
  199. package/dist/recorder/toolbar/actions/insert.d.ts +6 -0
  200. package/dist/recorder/toolbar/actions/insert.js +93 -0
  201. package/dist/recorder/toolbar/actions/pick.d.ts +8 -0
  202. package/dist/recorder/toolbar/actions/pick.js +26 -0
  203. package/dist/recorder/toolbar/actions/reply.d.ts +7 -0
  204. package/dist/recorder/toolbar/actions/reply.js +10 -0
  205. package/dist/recorder/toolbar/controller-fixture.d.ts +47 -0
  206. package/dist/recorder/toolbar/controller-fixture.js +129 -0
  207. package/dist/recorder/toolbar/controller.d.ts +12 -78
  208. package/dist/recorder/toolbar/controller.js +35 -247
  209. package/dist/recorder/toolbar/inspect.d.ts +1 -3
  210. package/dist/recorder/toolbar/inspect.js +2 -2
  211. package/dist/recorder/toolbar/page-scope.d.ts +19 -0
  212. package/dist/recorder/toolbar/page-scope.js +43 -0
  213. package/dist/recorder/toolbar/protocol.d.ts +2 -1
  214. package/dist/recorder/toolbar/source/attach.d.ts +2 -0
  215. package/dist/recorder/toolbar/source/attach.js +41 -0
  216. package/dist/recorder/toolbar/source/dom.d.ts +2 -0
  217. package/dist/recorder/toolbar/source/dom.js +47 -0
  218. package/dist/recorder/toolbar/source/drag.d.ts +2 -0
  219. package/dist/recorder/toolbar/source/drag.js +32 -0
  220. package/dist/recorder/toolbar/source/events-page.d.ts +2 -0
  221. package/dist/recorder/toolbar/source/events-page.js +67 -0
  222. package/dist/recorder/toolbar/source/events-toolbar.d.ts +2 -0
  223. package/dist/recorder/toolbar/source/events-toolbar.js +35 -0
  224. package/dist/recorder/toolbar/source/fields.d.ts +2 -0
  225. package/dist/recorder/toolbar/source/fields.js +44 -0
  226. package/dist/recorder/toolbar/source/handlers.d.ts +2 -0
  227. package/dist/recorder/toolbar/source/handlers.js +134 -0
  228. package/dist/recorder/toolbar/source/names.d.ts +2 -0
  229. package/dist/recorder/toolbar/source/names.js +12 -0
  230. package/dist/recorder/toolbar/source/picking.d.ts +2 -0
  231. package/dist/recorder/toolbar/source/picking.js +72 -0
  232. package/dist/recorder/toolbar/source/placement.d.ts +2 -0
  233. package/dist/recorder/toolbar/source/placement.js +45 -0
  234. package/dist/recorder/toolbar/source/recorder-side.d.ts +2 -0
  235. package/dist/recorder/toolbar/source/recorder-side.js +54 -0
  236. package/dist/recorder/toolbar/source/setup.d.ts +2 -0
  237. package/dist/recorder/toolbar/source/setup.js +50 -0
  238. package/dist/recorder/toolbar/source/views-panels.d.ts +2 -0
  239. package/dist/recorder/toolbar/source/views-panels.js +150 -0
  240. package/dist/recorder/toolbar/source/views-rail.d.ts +2 -0
  241. package/dist/recorder/toolbar/source/views-rail.js +40 -0
  242. package/dist/recorder/toolbar/source/views-render.d.ts +2 -0
  243. package/dist/recorder/toolbar/source/views-render.js +48 -0
  244. package/dist/recorder/toolbar/source.js +30 -856
  245. package/dist/recorder/toolbar/types.d.ts +70 -0
  246. package/dist/recorder/toolbar/types.js +1 -0
  247. package/dist/recorder/toolbar-bridge.d.ts +43 -0
  248. package/dist/recorder/toolbar-bridge.js +106 -0
  249. package/dist/runner/browsers.d.ts +0 -12
  250. package/dist/runner/browsers.js +1 -33
  251. package/dist/runner/options.d.ts +31 -0
  252. package/dist/runner/options.js +1 -0
  253. package/dist/runner/pooled-run.d.ts +43 -0
  254. package/dist/runner/pooled-run.js +75 -0
  255. package/dist/runner/ports.d.ts +2 -0
  256. package/dist/runner/ports.js +2 -0
  257. package/dist/runner/registry.d.ts +1 -1
  258. package/dist/runner/registry.js +1 -1
  259. package/dist/runner/reporter.d.ts +31 -10
  260. package/dist/runner/reporter.js +39 -6
  261. package/dist/runner/run-test.d.ts +16 -0
  262. package/dist/runner/run-test.js +178 -0
  263. package/dist/runner/run.d.ts +5 -3
  264. package/dist/runner/run.js +28 -29
  265. package/dist/runner/select.d.ts +24 -0
  266. package/dist/runner/select.js +77 -0
  267. package/dist/runner/test-dir.d.ts +13 -0
  268. package/dist/runner/test-dir.js +27 -0
  269. package/dist/runner/test-runner.d.ts +25 -56
  270. package/dist/runner/test-runner.js +81 -270
  271. package/dist/runner/worker-pool.d.ts +67 -0
  272. package/dist/runner/worker-pool.js +275 -0
  273. package/dist/runner/worker-process.d.ts +6 -0
  274. package/dist/runner/worker-process.js +54 -0
  275. package/dist/runner/worker-protocol.d.ts +67 -0
  276. package/dist/runner/worker-protocol.js +1 -0
  277. package/dist/runner/worker-session.d.ts +7 -0
  278. package/dist/runner/worker-session.js +60 -0
  279. package/dist/runner/worker.d.ts +26 -0
  280. package/dist/runner/worker.js +121 -0
  281. package/dist/runner/workers-option.d.ts +15 -0
  282. package/dist/runner/workers-option.js +22 -0
  283. package/dist/script/call-function.d.ts +1 -9
  284. package/dist/script/call-function.js +2 -19
  285. package/dist/script/element-handle.d.ts +1 -2
  286. package/dist/script/element-handle.js +1 -2
  287. package/dist/script/serialize.d.ts +3 -12
  288. package/dist/script/serialize.js +53 -62
  289. package/dist/steps/parse/call.d.ts +3 -0
  290. package/dist/steps/parse/call.js +81 -0
  291. package/dist/steps/parse/expectation.d.ts +5 -0
  292. package/dist/steps/parse/expectation.js +71 -0
  293. package/dist/steps/parse/file-entry.d.ts +4 -0
  294. package/dist/steps/parse/file-entry.js +74 -0
  295. package/dist/steps/parse/literals.d.ts +4 -0
  296. package/dist/steps/parse/literals.js +28 -0
  297. package/dist/steps/parse/step.d.ts +4 -0
  298. package/dist/steps/parse/step.js +81 -0
  299. package/dist/steps/parse.js +2 -323
  300. package/dist/transport/bidi-connection.d.ts +4 -15
  301. package/dist/transport/bidi-connection.js +9 -63
  302. package/dist/transport/log-redaction.d.ts +13 -0
  303. package/dist/transport/log-redaction.js +51 -0
  304. package/dist/transport/subscriptions.js +18 -13
  305. package/dist/transport/unsupported.d.ts +7 -0
  306. package/dist/transport/unsupported.js +13 -0
  307. package/dist/types/bidi-modules/browser.d.ts +1 -12
  308. package/dist/types/bidi-modules/browsing-context.d.ts +8 -18
  309. package/dist/types/bidi-modules/emulation.d.ts +29 -21
  310. package/dist/types/bidi-modules/input.d.ts +1 -9
  311. package/dist/types/bidi-modules/network.d.ts +29 -24
  312. package/dist/types/bidi-modules/script.d.ts +0 -9
  313. package/dist/types/bidi-modules/session.d.ts +0 -8
  314. package/dist/types/bidi-modules/storage.d.ts +0 -5
  315. package/dist/types/bidi.d.ts +7 -37
  316. package/dist/types/config.d.ts +12 -0
  317. package/dist/types/test.d.ts +8 -3
  318. package/dist/wait/wait-until.d.ts +1 -5
  319. package/dist/wait/wait-until.js +7 -16
  320. package/package.json +3 -2
  321. package/dist/config/default.d.ts +0 -2
  322. package/dist/config/default.js +0 -3
  323. package/dist/types/bidi-modules/web-extensions.d.ts +0 -9
  324. package/dist/types/bidi-protocols/command.d.ts +0 -7
  325. package/dist/types/bidi-protocols/event.d.ts +0 -17
  326. /package/dist/{script/helpers.d.ts → locator/helper-realm.d.ts} +0 -0
@@ -0,0 +1,43 @@
1
+ /** What a wait on the page ends with when the page was left first */
2
+ export const LEFT = Symbol("left the page");
3
+ /**
4
+ * Tracks which document the toolbar is working for. Work started for an older document must not be answered, and
5
+ * Chrome never answers a command into a document that is being replaced, so such work stops being waited for.
6
+ */
7
+ export class PageScope {
8
+ constructor() {
9
+ this.page = 0;
10
+ /** Wake-ups for the work waiting on the current page: called, and forgotten, when the page changes */
11
+ this.waiting = new Set();
12
+ }
13
+ /** Bumped on every page load */
14
+ get current() {
15
+ return this.page;
16
+ }
17
+ /** A new document loaded */
18
+ enter() {
19
+ this.page++;
20
+ this.leave();
21
+ }
22
+ /** Lets what waits on the current document stop waiting */
23
+ leave() {
24
+ const wakes = [...this.waiting];
25
+ this.waiting.clear();
26
+ for (const wake of wakes)
27
+ wake();
28
+ }
29
+ /** Runs `work`, but stops waiting for it when the page is left; resolves `LEFT` then */
30
+ async whileOn(work) {
31
+ let wake;
32
+ const left = new Promise((resolve) => {
33
+ wake = () => resolve(LEFT);
34
+ this.waiting.add(wake);
35
+ });
36
+ try {
37
+ return await Promise.race([work(), left]);
38
+ }
39
+ finally {
40
+ this.waiting.delete(wake);
41
+ }
42
+ }
43
+ }
@@ -3,7 +3,7 @@ import type { LocatorCall } from "../../steps/model.js";
3
3
  export declare const TOOLBAR_SANDBOX = "samurai-toolbar";
4
4
  /** `Symbol.for(TOOLBAR_KEY)` on the sandbox global holds the toolbar's hook: `update`, `reply`, `press`, `root` */
5
5
  export declare const TOOLBAR_KEY = "samurai.toolbar";
6
- export type MatcherChoice = "toBeVisible" | "toHaveText" | "toContainText" | "toHaveValue" | "toHaveAttribute" | "toHaveCount";
6
+ type MatcherChoice = "toBeVisible" | "toHaveText" | "toContainText" | "toHaveValue" | "toHaveAttribute" | "toHaveCount";
7
7
  /** What the toolbar draws; secrets are names only */
8
8
  export interface ToolbarState {
9
9
  status: "recording" | "paused";
@@ -143,3 +143,4 @@ export type ActionReply = {
143
143
  error: string;
144
144
  field?: "name";
145
145
  };
146
+ export {};
@@ -0,0 +1,2 @@
1
+ /** Attaching the host to the document. A slice of the toolbar function: it shares one closure with its neighbours, see `../source.ts`. */
2
+ export declare const ATTACH_JS: string;
@@ -0,0 +1,41 @@
1
+ /** Attaching the host to the document. A slice of the toolbar function: it shares one closure with its neighbours, see `../source.ts`. */
2
+ export const ATTACH_JS = String.raw ` // ---- Attach ----
3
+ let announced = false;
4
+ const attach = () => {
5
+ const html = document.documentElement;
6
+ if (!html) return false;
7
+ if (host.parentNode !== html) html.appendChild(host);
8
+ if (!announced) {
9
+ announced = true;
10
+ render();
11
+ post({ action: "ready", url: location.href });
12
+ }
13
+ return true;
14
+ };
15
+ const rebind = (fresh) => {
16
+ channel = fresh;
17
+ announced = false;
18
+ attach();
19
+ };
20
+ let watcher = null;
21
+ // The recording ended while this document stays open: take every listener and the host away
22
+ const dispose = () => {
23
+ endDrag();
24
+ for (const [target, type, handler, capture] of listening.splice(0)) target.removeEventListener(type, handler, capture);
25
+ if (watcher) watcher.disconnect();
26
+ clearTimeout(toastTimer);
27
+ channel = () => {};
28
+ host.remove();
29
+ if (globalThis[KEY] && globalThis[KEY].root === root) delete globalThis[KEY];
30
+ };
31
+ globalThis[KEY] = { root, update, reply, press, rebind, dispose, state: () => state, picking: () => ui.picking };
32
+
33
+ const focused = document.activeElement;
34
+ if (focused && focused.nodeType === 1 && isTextEntry(focused)) ui.field = focused;
35
+ if (!attach()) {
36
+ watcher = new MutationObserver(() => { if (attach()) watcher.disconnect(); });
37
+ watcher.observe(document, { childList: true });
38
+ }
39
+ // The page may replace the root element while it loads
40
+ listen(document, "DOMContentLoaded", attach);
41
+ listen(window, "load", attach);`;
@@ -0,0 +1,2 @@
1
+ /** DOM helpers: the shadow root, element builder and icons. A slice of the toolbar function: it shares one closure with its neighbours, see `../source.ts`. */
2
+ export declare const DOM_JS: string;
@@ -0,0 +1,47 @@
1
+ /** DOM helpers: the shadow root, element builder and icons. A slice of the toolbar function: it shares one closure with its neighbours, see `../source.ts`. */
2
+ export const DOM_JS = String.raw ` // ---- DOM ----
3
+ const h = (tag, props, ...kids) => {
4
+ const node = document.createElement(tag);
5
+ for (const key in props || {}) {
6
+ const value = props[key];
7
+ if (value === undefined || value === null || value === false) continue;
8
+ if (key === "class") node.className = value;
9
+ else if (key === "value" || key === "checked" || key === "disabled" || key === "selected") node[key] = value;
10
+ else node.setAttribute(key, value === true ? "" : String(value));
11
+ }
12
+ for (const kid of kids.flat(Infinity)) {
13
+ if (kid === null || kid === undefined || kid === false || kid === "") continue;
14
+ node.append(typeof kid === "string" ? document.createTextNode(kid) : kid);
15
+ }
16
+ return node;
17
+ };
18
+ const ico = (text) => h("span", { class: "ico" }, text);
19
+
20
+ // Styles go through the CSSOM (property by property, and a constructed sheet), which a page's CSP does not
21
+ // block, unlike a style attribute or a <style> element without 'unsafe-inline'
22
+ const host = document.createElement("samurai-recorder-toolbar");
23
+ const HOST_STYLE = [
24
+ ["all", "initial"], ["display", "block"], ["position", "fixed"], ["top", "0"], ["left", "0"], ["width", "0"],
25
+ ["height", "0"], ["overflow", "visible"], ["z-index", "2147483647"],
26
+ ];
27
+ for (const [name, value] of HOST_STYLE) host.style.setProperty(name, value, "important");
28
+ const root = host.attachShadow({ mode: "closed" });
29
+ const frame = h("div", { class: "frame" });
30
+ const container = h("div", { class: "ui" });
31
+ const outline = h("div", { class: "outline", hidden: true });
32
+ const toast = h("div", { class: "toast ui", hidden: true });
33
+ const notices = h("div", { class: "notices ui" });
34
+ let adopted = false;
35
+ try {
36
+ const sheet = new CSSStyleSheet();
37
+ sheet.replaceSync(CSS);
38
+ root.adoptedStyleSheets = [sheet];
39
+ adopted = root.adoptedStyleSheets.length === 1;
40
+ } catch (err) {
41
+ adopted = false;
42
+ }
43
+ // Without constructed sheets: a <style> element, which a strict CSP blocks
44
+ root.append(...(adopted ? [] : [h("style", null, CSS)]), frame, outline, container, toast, notices);
45
+
46
+ const viewport = () => ({ w: frame.clientWidth || innerWidth, h: frame.clientHeight || innerHeight });
47
+ `;
@@ -0,0 +1,2 @@
1
+ /** Dragging the rail. A slice of the toolbar function: it shares one closure with its neighbours, see `../source.ts`. */
2
+ export declare const DRAG_JS: string;
@@ -0,0 +1,32 @@
1
+ /** Dragging the rail. A slice of the toolbar function: it shares one closure with its neighbours, see `../source.ts`. */
2
+ export const DRAG_JS = String.raw ` // ---- Drag ----
3
+ const onDragMove = (event) => {
4
+ if (!ui.drag) return;
5
+ ui.drag.pos = { x: event.clientX - ui.drag.dx, y: event.clientY - ui.drag.dy };
6
+ ui.drag.moved = true;
7
+ placeAll();
8
+ };
9
+ const endDrag = () => {
10
+ if (!ui.drag) return;
11
+ const { moved, pointer } = ui.drag;
12
+ ui.drag = null;
13
+ window.removeEventListener("pointermove", onDragMove, true);
14
+ window.removeEventListener("pointerup", endDrag, true);
15
+ window.removeEventListener("pointercancel", endDrag, true);
16
+ try { host.releasePointerCapture(pointer); } catch (err) { /* already released */ }
17
+ if (!moved) return;
18
+ ui.dropped = { x: Math.round(railBox.x), y: Math.round(railBox.y) };
19
+ post({ action: "move", x: ui.dropped.x, y: ui.dropped.y });
20
+ placeAll();
21
+ };
22
+ const startDrag = (event) => {
23
+ if (event.button !== 0) return;
24
+ event.preventDefault();
25
+ ui.drag = { dx: event.clientX - railBox.x, dy: event.clientY - railBox.y, pos: { x: railBox.x, y: railBox.y }, moved: false, pointer: event.pointerId };
26
+ // Captured on the host, which re-renders never replace
27
+ try { host.setPointerCapture(event.pointerId); } catch (err) { /* moves still arrive while over the window */ }
28
+ window.addEventListener("pointermove", onDragMove, true);
29
+ window.addEventListener("pointerup", endDrag, true);
30
+ window.addEventListener("pointercancel", endDrag, true);
31
+ };
32
+ `;
@@ -0,0 +1,2 @@
1
+ /** Events on the page, seen in the capture phase. A slice of the toolbar function: it shares one closure with its neighbours, see `../source.ts`. */
2
+ export declare const EVENTS_PAGE_JS: string;
@@ -0,0 +1,67 @@
1
+ /** Events on the page, seen in the capture phase. A slice of the toolbar function: it shares one closure with its neighbours, see `../source.ts`. */
2
+ export const EVENTS_PAGE_JS = String.raw ` // ---- Events on the page (window capture runs before the page's and the capture script's listeners) ----
3
+ const swallow = (event) => {
4
+ event.preventDefault();
5
+ event.stopImmediatePropagation();
6
+ };
7
+ // Typing in the toolbar stays in the toolbar: these run first on the window (registered at document start,
8
+ // before any page script), so no page listener sees a key, typed text or the clipboard of a toolbar input
9
+ // (the New secret value among them). The browser's default action, the text going into the input, still
10
+ // happens. Outside the shadow root the event's target is the host, so the input is the focused element.
11
+ const TYPING = ["keydown", "keyup", "keypress", "beforeinput", "input", "compositionstart", "compositionupdate", "compositionend", "paste", "cut", "copy"];
12
+ for (const type of TYPING) {
13
+ listen(window, type, (event) => {
14
+ // An untrusted event was dispatched by the page itself: it neither carries the tester's typing nor acts
15
+ if (!event.isTrusted || !inToolbar(event)) return;
16
+ event.stopImmediatePropagation();
17
+ const target = root.activeElement;
18
+ if (type === "keydown") {
19
+ if (event.key === "Escape" && (ui.picking || ui.panel)) {
20
+ event.preventDefault();
21
+ handlers.escape();
22
+ } else onKeydown(event, target);
23
+ } else if (type === "input" && target && target.type !== "radio" && target.type !== "checkbox") {
24
+ // A radio or checkbox the pointer changed need not have the focus; its change event (below) updates it
25
+ onField(event, target);
26
+ }
27
+ }, true);
28
+ }
29
+ listen(window, "keydown", (event) => {
30
+ if (event.isTrusted && event.key === "Escape" && (ui.picking || ui.panel)) {
31
+ swallow(event);
32
+ handlers.escape();
33
+ }
34
+ }, true);
35
+ listen(window, "pointerover", (event) => {
36
+ if (!ui.picking) return;
37
+ if (inToolbar(event) || !event.target || event.target.nodeType !== 1) hideOutline();
38
+ else showOutline(event.target);
39
+ }, true);
40
+ for (const type of ["pointerdown", "mousedown", "pointerup", "mouseup", "dblclick", "auxclick", "contextmenu"]) {
41
+ listen(window, type, (event) => {
42
+ if (inToolbar(event)) return;
43
+ if (ui.picking) return swallow(event);
44
+ if (type === "pointerdown" && ui.panel) {
45
+ closePanel();
46
+ render();
47
+ }
48
+ }, true);
49
+ }
50
+ listen(window, "click", (event) => {
51
+ if (!ui.picking || inToolbar(event)) return;
52
+ swallow(event);
53
+ pick(event.target);
54
+ }, true);
55
+ listen(document, "focusin", (event) => {
56
+ if (inToolbar(event) || !event.target || event.target.nodeType !== 1) return;
57
+ ui.field = isTextEntry(event.target) ? event.target : null;
58
+ if (ui.panel === "insert" && !ui.insert.field && ui.field) {
59
+ ui.insert.field = ui.field;
60
+ render();
61
+ }
62
+ }, true);
63
+ listen(window, "resize", () => render());
64
+ listen(window, "scroll", () => {
65
+ if (ui.panel === "assert" && ui.assert && !outline.hidden) showOutline(ui.assert.target);
66
+ }, true);
67
+ `;
@@ -0,0 +1,2 @@
1
+ /** Events inside the toolbar. A slice of the toolbar function: it shares one closure with its neighbours, see `../source.ts`. */
2
+ export declare const EVENTS_TOOLBAR_JS: string;
@@ -0,0 +1,35 @@
1
+ /** Events inside the toolbar. A slice of the toolbar function: it shares one closure with its neighbours, see `../source.ts`. */
2
+ export const EVENTS_TOOLBAR_JS = String.raw ` // ---- Events inside the toolbar; none of them reach the page's bubbling listeners ----
3
+ root.addEventListener("click", (event) => {
4
+ event.stopPropagation();
5
+ const control = event.target.closest && event.target.closest("[data-action]");
6
+ if (!control || control.disabled) return;
7
+ press(control.getAttribute("data-action"), {
8
+ kind: control.getAttribute("data-kind"),
9
+ name: control.getAttribute("data-name"),
10
+ matcher: control.getAttribute("data-matcher"),
11
+ id: control.getAttribute("data-id"),
12
+ });
13
+ });
14
+ root.addEventListener("pointerdown", (event) => {
15
+ event.stopPropagation();
16
+ if (event.target.closest && event.target.closest("[data-grip]")) startDrag(event);
17
+ });
18
+ const onKeydown = (event, target) => {
19
+ const enter = event.key === "Enter" && target && target.getAttribute && target.getAttribute("data-enter");
20
+ if (!enter || event.isComposing) return;
21
+ event.preventDefault();
22
+ if (target.getAttribute("data-field")) onField(event, target);
23
+ press(enter);
24
+ };
25
+ // Typing reaches these only when it does not cross the shadow boundary (the window listeners below take the rest)
26
+ root.addEventListener("input", (event) => { event.stopPropagation(); onField(event); });
27
+ root.addEventListener("change", (event) => { event.stopPropagation(); onField(event); });
28
+ root.addEventListener("keydown", (event) => {
29
+ event.stopPropagation();
30
+ onKeydown(event, event.target);
31
+ });
32
+ for (const type of ["keyup", "keypress", "mousedown", "mouseup", "pointerup", "dblclick", "focusin", "focusout"]) {
33
+ root.addEventListener(type, (event) => event.stopPropagation());
34
+ }
35
+ `;
@@ -0,0 +1,2 @@
1
+ /** press() for tests and form field updates. A slice of the toolbar function: it shares one closure with its neighbours, see `../source.ts`. */
2
+ export declare const FIELDS_JS: string;
@@ -0,0 +1,44 @@
1
+ /** press() for tests and form field updates. A slice of the toolbar function: it shares one closure with its neighbours, see `../source.ts`. */
2
+ export const FIELDS_JS = String.raw ` const press = (action, detail) => {
3
+ const handler = handlers[action];
4
+ if (handler) handler(detail || {});
5
+ };
6
+
7
+ const onField = (event, target) => {
8
+ const t = target || event.target;
9
+ const field = t && t.getAttribute && t.getAttribute("data-field");
10
+ if (!field) return;
11
+ const value = t.value;
12
+ if (field === "expected" && ui.assert) ui.assert.expected = value;
13
+ else if (field === "attribute" && ui.assert) {
14
+ ui.assert.attribute = value;
15
+ ui.assert.expected = prefill(ui.assert, "toHaveAttribute");
16
+ render();
17
+ } else if (field === "countIndex" && ui.assert) {
18
+ ui.assert.countIndex = Number(value);
19
+ ui.assert.expected = prefill(ui.assert, "toHaveCount");
20
+ render();
21
+ } else if (field === "filter" && ui.insert) {
22
+ if (ui.insert.filter === value) return;
23
+ ui.insert.filter = value;
24
+ render();
25
+ } else if (field === "name" && ui.form) {
26
+ if (ui.form.name === value) return;
27
+ ui.form.name = value;
28
+ ui.form.nameError = null;
29
+ render();
30
+ } else if (field === "value" && ui.form) ui.form.value = value;
31
+ else if (field === "waitKind") {
32
+ ui.wait.kind = value === "time" ? "time" : "idle";
33
+ render();
34
+ } else if (field === "seconds") {
35
+ ui.wait.seconds = value;
36
+ ui.wait.kind = "time";
37
+ render();
38
+ } else if (field === "shotName") ui.shot.name = value;
39
+ else if (field === "fullPage") {
40
+ ui.shot.fullPage = value === "true";
41
+ render();
42
+ }
43
+ };
44
+ `;
@@ -0,0 +1,2 @@
1
+ /** Every toolbar action, as a click on its control runs it. A slice of the toolbar function: it shares one closure with its neighbours, see `../source.ts`. */
2
+ export declare const HANDLERS_JS: string;
@@ -0,0 +1,134 @@
1
+ /** Every toolbar action, as a click on its control runs it. A slice of the toolbar function: it shares one closure with its neighbours, see `../source.ts`. */
2
+ export const HANDLERS_JS = String.raw ` // Every action is what a click on its control does; press() runs the same functions
3
+ const handlers = {
4
+ dismiss: (d) => post({ action: "dismiss", id: Number(d.id) }),
5
+ pause: () => { if (!paused()) post({ action: "pause" }); },
6
+ resume: () => { if (paused()) post({ action: "resume" }); },
7
+ stop: () => {
8
+ stopPicking();
9
+ closePanel();
10
+ post({ action: "stop" });
11
+ render();
12
+ },
13
+ collapse: () => {
14
+ stopPicking();
15
+ closePanel();
16
+ post({ action: "collapse", collapsed: true });
17
+ render();
18
+ },
19
+ expand: () => post({ action: "collapse", collapsed: false }),
20
+ "resume-yes": () => { if (state.resumeQuestion) post({ action: "resume-goto", add: true }); },
21
+ "resume-no": () => { if (state.resumeQuestion) post({ action: "resume-goto", add: false }); },
22
+ assert: () => {
23
+ if (!usable()) return;
24
+ const wasOn = toolOn("assert");
25
+ stopPicking();
26
+ closePanel();
27
+ ui.picking = !wasOn;
28
+ render();
29
+ },
30
+ insert: () => openTool("insert"),
31
+ wait: () => openTool("wait"),
32
+ screenshot: () => openTool("screenshot"),
33
+ pick: (detail) => pick(detail.target),
34
+ matcher: (detail) => {
35
+ const a = ui.panel === "assert" && ui.assert;
36
+ if (!a || !a.info || !a.info.ok || unavailable(a.info, detail.matcher)) return;
37
+ a.matcher = detail.matcher;
38
+ a.expected = prefill(a, detail.matcher);
39
+ render();
40
+ },
41
+ not: () => {
42
+ if (ui.panel !== "assert" || !ui.assert || !ui.assert.info) return;
43
+ ui.assert.not = !ui.assert.not;
44
+ render();
45
+ },
46
+ "assert-add": () => {
47
+ const a = ui.panel === "assert" && ui.assert;
48
+ if (!a || ui.busy || !a.info || !a.info.ok) return;
49
+ const message = { action: "assert", id: a.id, target: a.target, candidates: a.candidates, matcher: a.matcher, not: a.not };
50
+ if (a.matcher !== "toBeVisible") message.expected = a.expected;
51
+ if (a.matcher === "toHaveAttribute") message.attribute = a.attribute;
52
+ if (a.matcher === "toHaveCount") message.countIndex = a.countIndex;
53
+ ui.busy = true;
54
+ a.error = null;
55
+ post(message);
56
+ render();
57
+ },
58
+ "insert-item": (detail) => {
59
+ if (ui.panel !== "insert" || ui.busy || !ui.insert.field) return;
60
+ if (detail.kind === "secret") {
61
+ const secret = state.secrets.find((s) => s.name === detail.name);
62
+ if (!secret) return;
63
+ if (!secret.set) return openForm("secret", secret.name, true);
64
+ } else if (!state.canAddVariables || !state.variables.some((v) => v.name === detail.name)) {
65
+ return;
66
+ }
67
+ sendInsert(detail.kind, detail.name);
68
+ },
69
+ "insert-first": () => {
70
+ if (ui.panel !== "insert" || !ui.insert.field) return;
71
+ const found = matches();
72
+ const first = found.variables[0] ? { kind: "env", name: found.variables[0].name } : found.secrets[0] && { kind: "secret", name: found.secrets[0].name };
73
+ if (first) handlers["insert-item"](first);
74
+ },
75
+ "insert-new": (detail) => {
76
+ if (ui.panel !== "insert" || !ui.insert.field) return;
77
+ const filter = ui.insert.filter.trim();
78
+ const base = filter || fieldName(ui.insert.field);
79
+ const name = detail.kind === "secret" ? snake(base) : filter ? variableName(filter) : camel(base);
80
+ openForm(detail.kind === "secret" ? "secret" : "env", name, false);
81
+ },
82
+ "new-kind": (detail) => {
83
+ const f = ui.panel === "new" && ui.form;
84
+ if (!f || ui.busy || f.kind === detail.kind) return;
85
+ f.kind = detail.kind === "secret" ? "secret" : "env";
86
+ f.name = f.kind === "secret" ? snake(f.name) : camel(f.name);
87
+ f.nameError = null;
88
+ f.error = null;
89
+ render();
90
+ },
91
+ "new-save": () => {
92
+ const f = ui.panel === "new" && ui.form;
93
+ if (!f || ui.busy || !f.name.trim() || (f.kind === "env" && !state.canAddVariables)) return;
94
+ const field = ui.insert.field;
95
+ const id = nextId++;
96
+ ui.pending = id;
97
+ ui.busy = true;
98
+ f.nameError = null;
99
+ f.error = null;
100
+ post({ action: "add-insertable", id, target: field, candidates: candidatesOf(field), kind: f.kind, name: f.name.trim(), value: f.value });
101
+ render();
102
+ },
103
+ "new-cancel": () => {
104
+ if (ui.panel !== "new") return;
105
+ ui.panel = "insert";
106
+ ui.form = null;
107
+ ui.busy = false;
108
+ ui.pending = 0;
109
+ ui.focus = "filter";
110
+ render();
111
+ },
112
+ "wait-add": () => {
113
+ if (ui.panel !== "wait" || ui.busy) return;
114
+ if (ui.wait.kind === "time" && !secondsValid()) return;
115
+ ui.busy = true;
116
+ ui.closeOnUpdate = true;
117
+ post(ui.wait.kind === "time" ? { action: "wait", kind: "time", ms: Math.round(Number(ui.wait.seconds) * 1000) } : { action: "wait", kind: "idle" });
118
+ render();
119
+ },
120
+ "screenshot-add": () => {
121
+ if (ui.panel !== "screenshot" || ui.busy) return;
122
+ ui.busy = true;
123
+ ui.closeOnUpdate = true;
124
+ post({ action: "screenshot", name: ui.shot.name.trim(), fullPage: ui.shot.fullPage });
125
+ render();
126
+ },
127
+ escape: () => {
128
+ if (!ui.picking && !ui.panel) return false;
129
+ stopPicking();
130
+ closePanel();
131
+ render();
132
+ return true;
133
+ },
134
+ };`;
@@ -0,0 +1,2 @@
1
+ /** Variable and field naming helpers. A slice of the toolbar function: it shares one closure with its neighbours, see `../source.ts`. */
2
+ export declare const NAMES_JS: string;
@@ -0,0 +1,12 @@
1
+ /** Variable and field naming helpers. A slice of the toolbar function: it shares one closure with its neighbours, see `../source.ts`. */
2
+ export const NAMES_JS = String.raw ` // ---- Names and labels ----
3
+ const words = (text) => String(text).replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).filter(Boolean);
4
+ const camel = (text) => words(text).map((w, i) => (i ? w[0].toUpperCase() + w.slice(1).toLowerCase() : w.toLowerCase())).join("");
5
+ const snake = (text) => words(text).map((w) => w.toUpperCase()).join("_");
6
+ const variableName = (text) => (/^[A-Za-z_$][\w$]*$/.test(text.trim()) ? text.trim() : camel(text));
7
+ const fieldName = (el) => labelTexts(el)[0] || el.getAttribute("name") || el.id || "";
8
+ const fieldLabel = (el) => {
9
+ const text = labelTexts(el)[0] || el.getAttribute("placeholder") || el.getAttribute("name") || el.id || "field";
10
+ return text.length > 40 ? text.slice(0, 39) + "…" : text;
11
+ };
12
+ `;
@@ -0,0 +1,2 @@
1
+ /** Opening and closing panels, and picking an element. A slice of the toolbar function: it shares one closure with its neighbours, see `../source.ts`. */
2
+ export declare const PICKING_JS: string;
@@ -0,0 +1,72 @@
1
+ /** Opening and closing panels, and picking an element. A slice of the toolbar function: it shares one closure with its neighbours, see `../source.ts`. */
2
+ export const PICKING_JS = String.raw ` // ---- Panels and picking ----
3
+ const closePanel = () => {
4
+ ui.panel = null;
5
+ ui.assert = null;
6
+ ui.busy = false;
7
+ ui.closeOnUpdate = false;
8
+ ui.pending = 0;
9
+ hideOutline();
10
+ };
11
+ const stopPicking = () => {
12
+ ui.picking = false;
13
+ if (ui.panel !== "assert") hideOutline();
14
+ };
15
+ const usable = () => !paused() && !state.collapsed;
16
+ const openTool = (tool) => {
17
+ if (!usable()) return;
18
+ const wasOpen = toolOn(tool);
19
+ stopPicking();
20
+ closePanel();
21
+ if (!wasOpen) {
22
+ ui.panel = tool;
23
+ if (tool === "insert") {
24
+ // The field focused now, else the last one (a toolbar button may have taken the focus)
25
+ const active = document.activeElement;
26
+ const focused = active && active !== host && active.nodeType === 1 && isTextEntry(active) ? active : null;
27
+ const field = focused || (ui.field && ui.field.isConnected ? ui.field : null);
28
+ ui.field = field;
29
+ ui.insert = { field, filter: "", error: null };
30
+ if (field) ui.focus = "filter";
31
+ } else if (tool === "wait") {
32
+ ui.wait = { kind: "idle", seconds: "2" };
33
+ } else if (tool === "screenshot") {
34
+ ui.shot = { name: state.nextScreenshot, fullPage: false };
35
+ ui.focus = "shotName";
36
+ }
37
+ }
38
+ render();
39
+ };
40
+ const pick = (target) => {
41
+ if (!ui.picking || !target || target.nodeType !== 1) return;
42
+ ui.picking = false;
43
+ closePanel();
44
+ const id = nextId++;
45
+ const candidates = candidatesOf(target);
46
+ const r = target.getBoundingClientRect();
47
+ ui.assert = {
48
+ id, target, candidates, info: null, matcher: "toBeVisible", expected: "", attribute: null, countIndex: 0, not: false, error: null,
49
+ rect: { left: r.left, top: r.top, right: r.right, bottom: r.bottom },
50
+ };
51
+ ui.panel = "assert";
52
+ ui.pending = id;
53
+ showOutline(target);
54
+ post({ action: "pick", id, target, candidates });
55
+ render();
56
+ };
57
+ const openForm = (kind, name, setValue) => {
58
+ ui.form = { kind, name, value: "", nameError: null, error: null, setValue };
59
+ ui.panel = "new";
60
+ ui.focus = name ? "value" : "name";
61
+ render();
62
+ };
63
+ const sendInsert = (kind, name) => {
64
+ const field = ui.insert.field;
65
+ const id = nextId++;
66
+ ui.pending = id;
67
+ ui.busy = true;
68
+ ui.insert.error = null;
69
+ post({ action: "insert", id, target: field, candidates: candidatesOf(field), kind, name });
70
+ render();
71
+ };
72
+ `;
@@ -0,0 +1,2 @@
1
+ /** Where the rail and panels sit in the window. A slice of the toolbar function: it shares one closure with its neighbours, see `../source.ts`. */
2
+ export declare const PLACEMENT_JS: string;
@@ -0,0 +1,45 @@
1
+ /** Where the rail and panels sit in the window. A slice of the toolbar function: it shares one closure with its neighbours, see `../source.ts`. */
2
+ export const PLACEMENT_JS = String.raw ` // ---- Placement ----
3
+ const place = (node, want) => {
4
+ const rect = node.getBoundingClientRect();
5
+ const v = viewport();
6
+ const x = clamp(want ? want.x : v.w - rect.width - 12, v.w - rect.width);
7
+ const y = clamp(want ? want.y : 12, v.h - rect.height);
8
+ node.style.left = x + "px";
9
+ node.style.top = y + "px";
10
+ return { x, y, w: rect.width, h: rect.height };
11
+ };
12
+ const wantedSpot = () => (ui.drag ? ui.drag.pos : ui.dropped || state.position);
13
+ const placePanel = (panel) => {
14
+ const rect = panel.getBoundingClientRect();
15
+ const v = viewport();
16
+ let x;
17
+ let y;
18
+ if (ui.panel === "assert" && ui.assert) {
19
+ const t = ui.assert.rect;
20
+ // Beside the element; below it (or above) when it is too wide for either side
21
+ if (t.right + 8 + rect.width <= v.w) [x, y] = [t.right + 8, t.top];
22
+ else if (t.left - 8 - rect.width >= 0) [x, y] = [t.left - 8 - rect.width, t.top];
23
+ else [x, y] = [t.left, t.bottom + 8 + rect.height <= v.h ? t.bottom + 8 : t.top - 8 - rect.height];
24
+ } else {
25
+ x = railBox.x + railBox.w / 2 > v.w / 2 ? railBox.x - 8 - rect.width : railBox.x + railBox.w + 8;
26
+ y = railBox.y;
27
+ }
28
+ panel.style.left = clamp(x, v.w - rect.width) + "px";
29
+ panel.style.top = clamp(y, v.h - rect.height) + "px";
30
+ };
31
+ const placeAll = () => {
32
+ const [rail, panel] = container.children;
33
+ if (rail) railBox = place(rail, wantedSpot());
34
+ if (panel) placePanel(panel);
35
+ };
36
+ const showOutline = (el) => {
37
+ const r = el.getBoundingClientRect();
38
+ outline.style.left = r.left + "px";
39
+ outline.style.top = r.top + "px";
40
+ outline.style.width = r.width + "px";
41
+ outline.style.height = r.height + "px";
42
+ outline.hidden = false;
43
+ };
44
+ const hideOutline = () => { outline.hidden = true; };
45
+ `;
@@ -0,0 +1,2 @@
1
+ /** The recorder's side of the hook: update, reply, rebind, dispose. A slice of the toolbar function: it shares one closure with its neighbours, see `../source.ts`. */
2
+ export declare const RECORDER_SIDE_JS: string;