@cssints/apg-specs 0.0.0-stage → 0.5.2

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 (260) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +65 -2
  3. package/dist/apg/accordion.bridge.d.mts +35 -0
  4. package/dist/apg/accordion.bridge.mjs +326 -0
  5. package/dist/apg/accordion.model.json +8397 -0
  6. package/dist/apg/alert.bridge.d.mts +22 -0
  7. package/dist/apg/alert.bridge.mjs +86 -0
  8. package/dist/apg/alert.model.json +871 -0
  9. package/dist/apg/alertdialog.bridge.d.mts +21 -0
  10. package/dist/apg/alertdialog.bridge.mjs +221 -0
  11. package/dist/apg/alertdialog.model.json +5232 -0
  12. package/dist/apg/breadcrumb.bridge.d.mts +26 -0
  13. package/dist/apg/breadcrumb.bridge.mjs +120 -0
  14. package/dist/apg/breadcrumb.model.json +574 -0
  15. package/dist/apg/button.bridge.d.mts +35 -0
  16. package/dist/apg/button.bridge.mjs +208 -0
  17. package/dist/apg/button.model.json +5814 -0
  18. package/dist/apg/carousel.bridge.d.mts +44 -0
  19. package/dist/apg/carousel.bridge.mjs +262 -0
  20. package/dist/apg/carousel.model.json +8963 -0
  21. package/dist/apg/checkbox-group.bridge.d.mts +25 -0
  22. package/dist/apg/checkbox-group.bridge.mjs +276 -0
  23. package/dist/apg/checkbox-group.model.json +3863 -0
  24. package/dist/apg/checkbox.bridge.d.mts +34 -0
  25. package/dist/apg/checkbox.bridge.mjs +253 -0
  26. package/dist/apg/checkbox.model.json +3158 -0
  27. package/dist/apg/combobox-autocomplete.bridge.d.mts +14 -0
  28. package/dist/apg/combobox-autocomplete.bridge.mjs +326 -0
  29. package/dist/apg/combobox-autocomplete.model.json +11586 -0
  30. package/dist/apg/combobox-dialog.bridge.d.mts +17 -0
  31. package/dist/apg/combobox-dialog.bridge.mjs +250 -0
  32. package/dist/apg/combobox-dialog.model.json +8981 -0
  33. package/dist/apg/combobox-grid.bridge.d.mts +17 -0
  34. package/dist/apg/combobox-grid.bridge.mjs +227 -0
  35. package/dist/apg/combobox-grid.model.json +8554 -0
  36. package/dist/apg/combobox-select-only.bridge.d.mts +24 -0
  37. package/dist/apg/combobox-select-only.bridge.mjs +304 -0
  38. package/dist/apg/combobox-select-only.model.json +7846 -0
  39. package/dist/apg/combobox-tree.bridge.d.mts +17 -0
  40. package/dist/apg/combobox-tree.bridge.mjs +211 -0
  41. package/dist/apg/combobox-tree.model.json +9345 -0
  42. package/dist/apg/dialog-modal.bridge.d.mts +35 -0
  43. package/dist/apg/dialog-modal.bridge.mjs +186 -0
  44. package/dist/apg/dialog-modal.model.json +7580 -0
  45. package/dist/apg/disclosure.bridge.d.mts +25 -0
  46. package/dist/apg/disclosure.bridge.mjs +202 -0
  47. package/dist/apg/disclosure.model.json +4703 -0
  48. package/dist/apg/feed.bridge.d.mts +31 -0
  49. package/dist/apg/feed.bridge.mjs +246 -0
  50. package/dist/apg/feed.model.json +4523 -0
  51. package/dist/apg/grid-selection.bridge.d.mts +13 -0
  52. package/dist/apg/grid-selection.bridge.mjs +160 -0
  53. package/dist/apg/grid-selection.model.json +6974 -0
  54. package/dist/apg/grid-sort.bridge.d.mts +12 -0
  55. package/dist/apg/grid-sort.bridge.mjs +138 -0
  56. package/dist/apg/grid-sort.model.json +3799 -0
  57. package/dist/apg/grid.bridge.d.mts +54 -0
  58. package/dist/apg/grid.bridge.mjs +441 -0
  59. package/dist/apg/grid.model.json +3802 -0
  60. package/dist/apg/link.bridge.d.mts +24 -0
  61. package/dist/apg/link.bridge.mjs +126 -0
  62. package/dist/apg/link.model.json +694 -0
  63. package/dist/apg/listbox-multi.bridge.d.mts +10 -0
  64. package/dist/apg/listbox-multi.bridge.mjs +261 -0
  65. package/dist/apg/listbox-multi.model.json +10893 -0
  66. package/dist/apg/listbox-rearrangeable.bridge.d.mts +20 -0
  67. package/dist/apg/listbox-rearrangeable.bridge.mjs +237 -0
  68. package/dist/apg/listbox-rearrangeable.model.json +17592 -0
  69. package/dist/apg/listbox-shared.d.mts +58 -0
  70. package/dist/apg/listbox-shared.mjs +171 -0
  71. package/dist/apg/listbox-single.bridge.d.mts +10 -0
  72. package/dist/apg/listbox-single.bridge.mjs +196 -0
  73. package/dist/apg/listbox-single.model.json +8024 -0
  74. package/dist/apg/listbox-typeahead.bridge.d.mts +10 -0
  75. package/dist/apg/listbox-typeahead.bridge.mjs +153 -0
  76. package/dist/apg/listbox-typeahead.model.json +5615 -0
  77. package/dist/apg/menu-button.bridge.d.mts +29 -0
  78. package/dist/apg/menu-button.bridge.mjs +259 -0
  79. package/dist/apg/menu-button.model.json +5706 -0
  80. package/dist/apg/menu-checkable.bridge.d.mts +25 -0
  81. package/dist/apg/menu-checkable.bridge.mjs +226 -0
  82. package/dist/apg/menu-checkable.model.json +4277 -0
  83. package/dist/apg/menu-menubar.bridge.d.mts +27 -0
  84. package/dist/apg/menu-menubar.bridge.mjs +273 -0
  85. package/dist/apg/menu-menubar.model.json +9540 -0
  86. package/dist/apg/meter.bridge.d.mts +28 -0
  87. package/dist/apg/meter.bridge.mjs +159 -0
  88. package/dist/apg/meter.model.json +1185 -0
  89. package/dist/apg/player.bridge.d.mts +24 -0
  90. package/dist/apg/player.bridge.mjs +342 -0
  91. package/dist/apg/player.model.json +11013 -0
  92. package/dist/apg/radio.bridge.d.mts +35 -0
  93. package/dist/apg/radio.bridge.mjs +371 -0
  94. package/dist/apg/radio.model.json +5011 -0
  95. package/dist/apg/slider-multithumb.bridge.d.mts +34 -0
  96. package/dist/apg/slider-multithumb.bridge.mjs +365 -0
  97. package/dist/apg/slider-multithumb.model.json +5774 -0
  98. package/dist/apg/slider.bridge.d.mts +54 -0
  99. package/dist/apg/slider.bridge.mjs +359 -0
  100. package/dist/apg/slider.model.json +4881 -0
  101. package/dist/apg/spinbutton.bridge.d.mts +41 -0
  102. package/dist/apg/spinbutton.bridge.mjs +327 -0
  103. package/dist/apg/spinbutton.model.json +7571 -0
  104. package/dist/apg/switch.bridge.d.mts +30 -0
  105. package/dist/apg/switch.bridge.mjs +243 -0
  106. package/dist/apg/switch.model.json +3159 -0
  107. package/dist/apg/tabs.bridge.d.mts +41 -0
  108. package/dist/apg/tabs.bridge.mjs +294 -0
  109. package/dist/apg/tabs.model.json +7659 -0
  110. package/dist/apg/timeline.bridge.d.mts +36 -0
  111. package/dist/apg/timeline.bridge.mjs +265 -0
  112. package/dist/apg/timeline.model.json +6189 -0
  113. package/dist/apg/toolbar.bridge.d.mts +25 -0
  114. package/dist/apg/toolbar.bridge.mjs +307 -0
  115. package/dist/apg/toolbar.model.json +6616 -0
  116. package/dist/apg/tooltip.bridge.d.mts +36 -0
  117. package/dist/apg/tooltip.bridge.mjs +208 -0
  118. package/dist/apg/tooltip.model.json +8295 -0
  119. package/dist/apg/treegrid.bridge.d.mts +28 -0
  120. package/dist/apg/treegrid.bridge.mjs +311 -0
  121. package/dist/apg/treegrid.model.json +17358 -0
  122. package/dist/apg/treeview.bridge.d.mts +26 -0
  123. package/dist/apg/treeview.bridge.mjs +396 -0
  124. package/dist/apg/treeview.model.json +13745 -0
  125. package/dist/apg/windowsplitter.bridge.d.mts +36 -0
  126. package/dist/apg/windowsplitter.bridge.mjs +242 -0
  127. package/dist/apg/windowsplitter.model.json +6253 -0
  128. package/dist/canvas/camera.model.json +6568 -0
  129. package/dist/canvas/canvas.bridge.d.mts +190 -0
  130. package/dist/canvas/canvas.bridge.mjs +729 -0
  131. package/dist/canvas/pointer.model.json +17095 -0
  132. package/dist/scenario.d.mts +233 -0
  133. package/dist/scenario.mjs +313 -0
  134. package/package.json +57 -4
  135. package/scenarios/apg/README.md +221 -0
  136. package/scenarios/apg/accordion.base-ui.json +1005 -0
  137. package/scenarios/apg/accordion.react-aria.json +929 -0
  138. package/scenarios/apg/alert.base-ui.json +81 -0
  139. package/scenarios/apg/alert.react-aria.json +98 -0
  140. package/scenarios/apg/alertdialog.base-ui.json +331 -0
  141. package/scenarios/apg/alertdialog.react-aria.json +225 -0
  142. package/scenarios/apg/breadcrumb.react-aria.json +312 -0
  143. package/scenarios/apg/button.ariakit.json +136 -0
  144. package/scenarios/apg/button.base-ui.json +747 -0
  145. package/scenarios/apg/button.react-aria.json +443 -0
  146. package/scenarios/apg/carousel.apg.json +909 -0
  147. package/scenarios/apg/checkbox.ariakit.json +299 -0
  148. package/scenarios/apg/checkbox.base-ui.json +1126 -0
  149. package/scenarios/apg/checkbox.react-aria.json +518 -0
  150. package/scenarios/apg/combobox.ariakit.json +945 -0
  151. package/scenarios/apg/combobox.base-ui.json +2444 -0
  152. package/scenarios/apg/combobox.react-aria.json +845 -0
  153. package/scenarios/apg/dialog-modal.ariakit.json +1102 -0
  154. package/scenarios/apg/dialog-modal.base-ui.json +775 -0
  155. package/scenarios/apg/dialog-modal.react-aria.json +638 -0
  156. package/scenarios/apg/disclosure.apg.json +349 -0
  157. package/scenarios/apg/disclosure.ariakit.json +163 -0
  158. package/scenarios/apg/disclosure.base-ui.json +451 -0
  159. package/scenarios/apg/disclosure.react-aria.json +643 -0
  160. package/scenarios/apg/feed.apg.json +442 -0
  161. package/scenarios/apg/grid.ariakit.json +197 -0
  162. package/scenarios/apg/grid.base-ui.json +720 -0
  163. package/scenarios/apg/grid.react-aria.json +969 -0
  164. package/scenarios/apg/landmarks.ariakit.json +93 -0
  165. package/scenarios/apg/landmarks.react-aria.json +330 -0
  166. package/scenarios/apg/link.ariakit.json +83 -0
  167. package/scenarios/apg/link.base-ui.json +88 -0
  168. package/scenarios/apg/link.react-aria.json +186 -0
  169. package/scenarios/apg/listbox.ariakit.json +78 -0
  170. package/scenarios/apg/listbox.base-ui.json +163 -0
  171. package/scenarios/apg/listbox.react-aria.json +1118 -0
  172. package/scenarios/apg/menu-button.ariakit.json +924 -0
  173. package/scenarios/apg/menu-button.base-ui.json +1155 -0
  174. package/scenarios/apg/menu-button.react-aria.json +1080 -0
  175. package/scenarios/apg/menubar.ariakit.json +758 -0
  176. package/scenarios/apg/menubar.base-ui.json +673 -0
  177. package/scenarios/apg/meter.base-ui.json +243 -0
  178. package/scenarios/apg/meter.react-aria.json +201 -0
  179. package/scenarios/apg/radio.ariakit.json +459 -0
  180. package/scenarios/apg/radio.base-ui.json +581 -0
  181. package/scenarios/apg/radio.react-aria.json +576 -0
  182. package/scenarios/apg/slider-multithumb.base-ui.json +316 -0
  183. package/scenarios/apg/slider-multithumb.react-aria.json +363 -0
  184. package/scenarios/apg/slider.base-ui.json +544 -0
  185. package/scenarios/apg/slider.react-aria.json +456 -0
  186. package/scenarios/apg/spinbutton.base-ui.json +1688 -0
  187. package/scenarios/apg/spinbutton.react-aria.json +426 -0
  188. package/scenarios/apg/switch.base-ui.json +456 -0
  189. package/scenarios/apg/switch.react-aria.json +303 -0
  190. package/scenarios/apg/table.ariakit.json +65 -0
  191. package/scenarios/apg/table.react-aria.json +221 -0
  192. package/scenarios/apg/tabs.ariakit.json +329 -0
  193. package/scenarios/apg/tabs.base-ui.json +875 -0
  194. package/scenarios/apg/tabs.react-aria.json +644 -0
  195. package/scenarios/apg/toolbar.ariakit.json +595 -0
  196. package/scenarios/apg/toolbar.base-ui.json +1189 -0
  197. package/scenarios/apg/toolbar.react-aria.json +383 -0
  198. package/scenarios/apg/tooltip.ariakit.json +441 -0
  199. package/scenarios/apg/tooltip.base-ui.json +411 -0
  200. package/scenarios/apg/tooltip.react-aria.json +454 -0
  201. package/scenarios/apg/treegrid.apg.json +2558 -0
  202. package/scenarios/apg/treegrid.react-aria.json +5094 -0
  203. package/scenarios/apg/treeview.apg.json +928 -0
  204. package/scenarios/apg/windowsplitter.apg.json +557 -0
  205. package/scenarios/canvas/README.md +69 -0
  206. package/scenarios/canvas/camera.contract.json +237 -0
  207. package/scenarios/canvas/camera.excalidraw.json +57 -0
  208. package/scenarios/canvas/marquee.excalidraw.json +122 -0
  209. package/scenarios/canvas/pan.excalidraw.json +46 -0
  210. package/scenarios/canvas/pointer.contract.json +528 -0
  211. package/scenarios/parts.json +291 -0
  212. package/shared/aria.qnt +186 -0
  213. package/shared/details-group.qnt +50 -0
  214. package/shared/focusgroup.qnt +152 -0
  215. package/shared/invokers.qnt +81 -0
  216. package/shared/popover.qnt +71 -0
  217. package/specs/apg/STATIC.md +103 -0
  218. package/specs/apg/accordion.qnt +578 -0
  219. package/specs/apg/alert.qnt +131 -0
  220. package/specs/apg/alertdialog.qnt +455 -0
  221. package/specs/apg/breadcrumb.qnt +88 -0
  222. package/specs/apg/button.qnt +445 -0
  223. package/specs/apg/carousel.qnt +732 -0
  224. package/specs/apg/checkbox-group.qnt +388 -0
  225. package/specs/apg/checkbox.qnt +394 -0
  226. package/specs/apg/combobox-autocomplete.qnt +764 -0
  227. package/specs/apg/combobox-dialog.qnt +627 -0
  228. package/specs/apg/combobox-grid.qnt +632 -0
  229. package/specs/apg/combobox-select-only.qnt +626 -0
  230. package/specs/apg/combobox-tree.qnt +633 -0
  231. package/specs/apg/dialog-modal.qnt +499 -0
  232. package/specs/apg/disclosure.qnt +309 -0
  233. package/specs/apg/feed.qnt +485 -0
  234. package/specs/apg/grid-selection.qnt +512 -0
  235. package/specs/apg/grid-sort.qnt +330 -0
  236. package/specs/apg/grid.qnt +360 -0
  237. package/specs/apg/link.qnt +119 -0
  238. package/specs/apg/listbox-multi.qnt +799 -0
  239. package/specs/apg/listbox-rearrangeable.qnt +801 -0
  240. package/specs/apg/listbox-single.qnt +511 -0
  241. package/specs/apg/listbox-typeahead.qnt +396 -0
  242. package/specs/apg/menu-button.qnt +363 -0
  243. package/specs/apg/menu-checkable.qnt +304 -0
  244. package/specs/apg/menu-menubar.qnt +531 -0
  245. package/specs/apg/meter.qnt +129 -0
  246. package/specs/apg/player.qnt +1081 -0
  247. package/specs/apg/radio.qnt +394 -0
  248. package/specs/apg/slider-multithumb.qnt +441 -0
  249. package/specs/apg/slider.qnt +396 -0
  250. package/specs/apg/spinbutton.qnt +494 -0
  251. package/specs/apg/switch.qnt +389 -0
  252. package/specs/apg/tabs.qnt +412 -0
  253. package/specs/apg/timeline.qnt +612 -0
  254. package/specs/apg/toolbar.qnt +364 -0
  255. package/specs/apg/tooltip.qnt +553 -0
  256. package/specs/apg/treegrid.qnt +983 -0
  257. package/specs/apg/treeview.qnt +1004 -0
  258. package/specs/apg/windowsplitter.qnt +487 -0
  259. package/specs/canvas/camera.qnt +407 -0
  260. package/specs/canvas/pointer.qnt +841 -0
@@ -0,0 +1,24 @@
1
+ import { ActionStep, Probe, ScenarioConfig, SpecBridge } from "../scenario.mjs";
2
+ //#region src/apg/player.bridge.d.ts
3
+ /**
4
+ * Player vocabulary <-> `specs/apg/player.qnt`, shared by the spec driver and
5
+ * the DOM driver. `config.duration` seeds `loadedmetadata` so both start ready.
6
+ * Parts: `surface`, `play`, `seek`, `volume`, `rate`, `mute`, `fullscreen`.
7
+ * Adapter events arrive as `prop: emit` steps (e.g. `{ event: 'prop', prop:
8
+ * 'emit', value: { type: 'ended' } }`), so the walk can advance the machine.
9
+ *
10
+ * `controlled` puts volume / muted / rate / fullscreen in a host; it accepts
11
+ * every request (`accepts`, the default) or ignores them all. The `prop*`
12
+ * steps are the host writing a controlled value (inert while uncontrolled).
13
+ */
14
+ export interface PlayerConfig {
15
+ readonly duration: number;
16
+ readonly controlled: boolean;
17
+ readonly accepts: boolean;
18
+ }
19
+ export declare const playerConfig: (config: ScenarioConfig) => PlayerConfig;
20
+ export declare const playerBridge: SpecBridge;
21
+ /** The user-level steps a differential walk draws from. */
22
+ export declare const playerSteps: ReadonlyArray<ActionStep>;
23
+ export declare const playerProbe: Probe;
24
+ //#endregion
@@ -0,0 +1,342 @@
1
+ import { Unsupported } from "../scenario.mjs";
2
+ //#region src/apg/player.bridge.ts
3
+ const specEvent = (tag, params = {}) => ({
4
+ _tag: tag,
5
+ ...params
6
+ });
7
+ const num = (value) => value === void 0 ? 0 : Number(value);
8
+ const mss = (seconds) => `${Math.floor(seconds / 60)}:${String(Math.floor(seconds) % 60).padStart(2, "0")}`;
9
+ const playerConfig = (config) => {
10
+ let duration = 90;
11
+ let controlled = false;
12
+ let accepts = true;
13
+ for (const [key, value] of Object.entries(config)) if (key === "duration" && typeof value === "number") duration = value;
14
+ else if (key === "controlled" && typeof value === "boolean") controlled = value;
15
+ else if (key === "accepts" && typeof value === "boolean") accepts = value;
16
+ else throw new Unsupported(`config ${key}=${JSON.stringify(value)}`);
17
+ return {
18
+ duration,
19
+ controlled,
20
+ accepts
21
+ };
22
+ };
23
+ const SEEK_KEYS = /* @__PURE__ */ new Set([
24
+ "ArrowLeft",
25
+ "ArrowRight",
26
+ "ArrowUp",
27
+ "ArrowDown",
28
+ "PageUp",
29
+ "PageDown",
30
+ "Home",
31
+ "End"
32
+ ]);
33
+ const SURFACE_KEYS = /* @__PURE__ */ new Set([
34
+ "Space",
35
+ " ",
36
+ "k",
37
+ "K"
38
+ ]);
39
+ const partOf = (target) => (target ?? "").replace(/:0$/, "").toLowerCase();
40
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
41
+ const emitEvent = (value) => {
42
+ if (value === void 0 || !isRecord(value)) throw new Unsupported(`emit ${JSON.stringify(value)}`);
43
+ const type = value["type"];
44
+ switch (type) {
45
+ case "loadedmetadata": return specEvent("onLoadedMetadata", { d: BigInt(Number(value["duration"])) });
46
+ case "canplay":
47
+ case "playing":
48
+ case "waiting":
49
+ case "pause":
50
+ case "ended":
51
+ case "seeked":
52
+ case "error": return specEvent(`on${type.charAt(0).toUpperCase()}${type.slice(1)}`);
53
+ case "timeupdate": return specEvent("onTimeUpdate", { t: BigInt(Number(value["currentTime"])) });
54
+ default: throw new Unsupported(`emit type ${String(type)}`);
55
+ }
56
+ };
57
+ const programmatic = (prop, value) => {
58
+ switch (prop) {
59
+ case "emit": return [emitEvent(value)];
60
+ case "play": return [specEvent("play")];
61
+ case "pause": return [specEvent("pause")];
62
+ case "toggle": return [specEvent("togglePlay")];
63
+ case "toggleMute": return [specEvent("toggleMute")];
64
+ case "toggleFullscreen": return [specEvent("toggleFullscreen")];
65
+ case "setVolume": return [specEvent("setVolume", { v: BigInt(Number(value)) })];
66
+ case "setRate": return [specEvent("setRate", { r: BigInt(Number(value)) })];
67
+ case "propVolume": return [specEvent("propVolume", { v: BigInt(Number(value)) })];
68
+ case "propMuted": return [specEvent("propMuted", { m: value === true })];
69
+ case "propRate": return [specEvent("propRate", { r: BigInt(Number(value)) })];
70
+ case "propFullscreen": return [specEvent("propFullscreen", { f: value === true })];
71
+ case "focus": {
72
+ const scope = String(value);
73
+ if (scope === "surface" || scope === "seek" || scope === "control") return [specEvent("focusOn", { target: scope })];
74
+ throw new Unsupported(`focus ${scope}`);
75
+ }
76
+ default: throw new Unsupported(`prop ${prop ?? ""}`);
77
+ }
78
+ };
79
+ const playerBridge = {
80
+ setup: (config) => {
81
+ const { duration, controlled, accepts } = playerConfig(config);
82
+ return [
83
+ specEvent("onLoadedMetadata", { d: BigInt(duration) }),
84
+ ...controlled ? [specEvent("control")] : [],
85
+ ...controlled && accepts ? [specEvent("acceptRequests")] : []
86
+ ];
87
+ },
88
+ event: (step) => {
89
+ switch (step.event) {
90
+ case "focus": {
91
+ const part = partOf(step.target);
92
+ if (part === "surface" || part === "seek" || part === "control") return [specEvent("focusOn", { target: part })];
93
+ throw new Unsupported(`focus on ${part}`);
94
+ }
95
+ case "blur": return [specEvent("blur")];
96
+ case "click": {
97
+ const part = partOf(step.target);
98
+ if (part === "play" || part === "playpause") return [specEvent("togglePlay")];
99
+ if (part === "mute" || part === "volumebutton") return [specEvent("toggleMute")];
100
+ if (part === "fullscreen") return [specEvent("toggleFullscreen")];
101
+ throw new Unsupported(`click on ${part}`);
102
+ }
103
+ case "key": {
104
+ const part = partOf(step.target);
105
+ const key = step.key ?? "";
106
+ if (part === "seek") {
107
+ if (!SEEK_KEYS.has(key)) throw new Unsupported(`key ${key} on seek`);
108
+ return [specEvent("focusOn", { target: "seek" }), specEvent("seekKey", { key })];
109
+ }
110
+ if (part === "surface") {
111
+ if (SURFACE_KEYS.has(key)) return [specEvent("focusOn", { target: "surface" }), specEvent("togglePlay")];
112
+ if (key === "ArrowLeft" || key === "ArrowRight") return [specEvent("focusOn", { target: "surface" }), specEvent("surfaceSeekKey", { key })];
113
+ throw new Unsupported(`key ${key} on surface`);
114
+ }
115
+ throw new Unsupported(`key ${key} on ${part}`);
116
+ }
117
+ case "hover":
118
+ case "unhover": return [];
119
+ case "prop": return programmatic(step.prop, step.value);
120
+ default: throw new Unsupported(`event ${step.event}`);
121
+ }
122
+ },
123
+ value: (state) => ({
124
+ position: num(state["pos"]),
125
+ duration: num(state["dur"]),
126
+ paused: state["paused"] === true,
127
+ ended: state["ended"] === true,
128
+ muted: state["muted"] === true,
129
+ rate: num(state["rate"]),
130
+ fullscreen: state["full"] === true
131
+ }),
132
+ focus: (state) => {
133
+ const scope = state["focus"];
134
+ if (scope === "surface" || scope === "seek") return scope;
135
+ if (scope === "control") throw new Unsupported("control focus is not modelled as a part");
136
+ return null;
137
+ },
138
+ attr: (state, target, name) => {
139
+ const part = partOf(target);
140
+ if (part === "play" || part === "playpause") {
141
+ if (name === "aria-label") return state["stage"] === "playing" || state["stage"] === "buffering" ? "Pause" : "Play";
142
+ } else if (part === "mute" || part === "volumebutton") {
143
+ if (name === "aria-pressed") return state["muted"] === true ? "true" : "false";
144
+ } else if (part === "fullscreen") {
145
+ if (name === "aria-pressed") return state["full"] === true ? "true" : "false";
146
+ } else if (part === "seek") {
147
+ if (name === "aria-valuemin") return "0";
148
+ if (name === "aria-valuenow") return String(num(state["pos"]));
149
+ if (name === "aria-valuemax") return String(num(state["dur"]));
150
+ if (name === "aria-valuetext") return mss(num(state["pos"]));
151
+ } else if (part === "volume" && name === "aria-valuenow") return String(num(state["vol"]));
152
+ else if (part === "rate" && name === "aria-valuenow") return String(num(state["rate"]));
153
+ throw new Unsupported(`attribute ${name} on ${target}`);
154
+ },
155
+ visible: () => [],
156
+ events: (before, after) => {
157
+ const out = [];
158
+ const plays = num(after["plays"]) - num(before["plays"]);
159
+ for (let i = 0; i < plays; i += 1) out.push(["onPlay", true]);
160
+ const pauses = num(after["pauses"]) - num(before["pauses"]);
161
+ for (let i = 0; i < pauses; i += 1) out.push(["onPause", true]);
162
+ const seeks = num(after["seeks"]) - num(before["seeks"]);
163
+ for (let i = 0; i < seeks; i += 1) out.push(["onSeek", num(after["pos"])]);
164
+ if (after["reqs"] !== before["reqs"]) {
165
+ const ask = num(after["askNum"]);
166
+ switch (after["reqKind"]) {
167
+ case "volume":
168
+ out.push(["onVolumeChange", ask]);
169
+ break;
170
+ case "muted":
171
+ out.push(["onMutedChange", ask === 1]);
172
+ break;
173
+ case "rate":
174
+ out.push(["onRateChange", ask]);
175
+ break;
176
+ case "fullscreen": out.push(["onFullscreenChange", ask === 1]);
177
+ }
178
+ }
179
+ return out;
180
+ }
181
+ };
182
+ /** The user-level steps a differential walk draws from. */
183
+ const playerSteps = [
184
+ {
185
+ event: "focus",
186
+ target: "surface"
187
+ },
188
+ {
189
+ event: "focus",
190
+ target: "seek"
191
+ },
192
+ {
193
+ event: "key",
194
+ key: "Space",
195
+ target: "surface"
196
+ },
197
+ {
198
+ event: "key",
199
+ key: "k",
200
+ target: "surface"
201
+ },
202
+ {
203
+ event: "key",
204
+ key: "ArrowRight",
205
+ target: "surface"
206
+ },
207
+ {
208
+ event: "key",
209
+ key: "ArrowLeft",
210
+ target: "surface"
211
+ },
212
+ {
213
+ event: "key",
214
+ key: "ArrowRight",
215
+ target: "seek"
216
+ },
217
+ {
218
+ event: "key",
219
+ key: "PageUp",
220
+ target: "seek"
221
+ },
222
+ {
223
+ event: "key",
224
+ key: "Home",
225
+ target: "seek"
226
+ },
227
+ {
228
+ event: "key",
229
+ key: "End",
230
+ target: "seek"
231
+ },
232
+ {
233
+ event: "prop",
234
+ prop: "emit",
235
+ value: { type: "waiting" }
236
+ },
237
+ {
238
+ event: "prop",
239
+ prop: "emit",
240
+ value: { type: "playing" }
241
+ },
242
+ {
243
+ event: "prop",
244
+ prop: "emit",
245
+ value: {
246
+ type: "timeupdate",
247
+ currentTime: 30
248
+ }
249
+ },
250
+ {
251
+ event: "prop",
252
+ prop: "emit",
253
+ value: { type: "ended" }
254
+ },
255
+ {
256
+ event: "prop",
257
+ prop: "emit",
258
+ value: { type: "seeked" }
259
+ },
260
+ {
261
+ event: "prop",
262
+ prop: "setVolume",
263
+ value: 40
264
+ },
265
+ {
266
+ event: "prop",
267
+ prop: "toggleMute"
268
+ },
269
+ {
270
+ event: "prop",
271
+ prop: "setRate",
272
+ value: 150
273
+ },
274
+ {
275
+ event: "prop",
276
+ prop: "toggleFullscreen"
277
+ },
278
+ {
279
+ event: "prop",
280
+ prop: "setVolume",
281
+ value: 25
282
+ },
283
+ {
284
+ event: "prop",
285
+ prop: "setRate",
286
+ value: 200
287
+ },
288
+ {
289
+ event: "prop",
290
+ prop: "propVolume",
291
+ value: 75
292
+ },
293
+ {
294
+ event: "prop",
295
+ prop: "propMuted",
296
+ value: true
297
+ },
298
+ {
299
+ event: "prop",
300
+ prop: "propMuted",
301
+ value: false
302
+ },
303
+ {
304
+ event: "prop",
305
+ prop: "propRate",
306
+ value: 50
307
+ },
308
+ {
309
+ event: "prop",
310
+ prop: "propFullscreen",
311
+ value: true
312
+ },
313
+ {
314
+ event: "prop",
315
+ prop: "propFullscreen",
316
+ value: false
317
+ }
318
+ ];
319
+ const playerProbe = {
320
+ attrs: [
321
+ ["play", "aria-label"],
322
+ ["mute", "aria-pressed"],
323
+ ["fullscreen", "aria-pressed"],
324
+ ["volume", "aria-valuenow"],
325
+ ["rate", "aria-valuenow"],
326
+ ["seek", "aria-valuemin"],
327
+ ["seek", "aria-valuenow"],
328
+ ["seek", "aria-valuemax"],
329
+ ["seek", "aria-valuetext"]
330
+ ],
331
+ events: [
332
+ "onPlay",
333
+ "onPause",
334
+ "onSeek",
335
+ "onVolumeChange",
336
+ "onMutedChange",
337
+ "onRateChange",
338
+ "onFullscreenChange"
339
+ ]
340
+ };
341
+ //#endregion
342
+ export { playerBridge, playerConfig, playerProbe, playerSteps };