@wodzik/cubecore 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (279) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +118 -0
  3. package/analyze/client.d.ts +18 -0
  4. package/analyze/client.js +42 -0
  5. package/analyze/index.d.ts +114 -0
  6. package/analyze/index.js +151 -0
  7. package/analyze/rotations.d.ts +3 -0
  8. package/analyze/rotations.js +15 -0
  9. package/analyze/roux.d.ts +67 -0
  10. package/analyze/roux.js +95 -0
  11. package/analyze/worker.d.ts +6 -0
  12. package/analyze/worker.js +31 -0
  13. package/analyze/zz.d.ts +47 -0
  14. package/analyze/zz.js +102 -0
  15. package/bld/index.d.ts +14 -0
  16. package/bld/index.js +14 -0
  17. package/bld/memo.d.ts +66 -0
  18. package/bld/memo.js +139 -0
  19. package/bld/scheme.d.ts +30 -0
  20. package/bld/scheme.js +35 -0
  21. package/bld/skin.d.ts +26 -0
  22. package/bld/skin.js +46 -0
  23. package/bld/tracker.d.ts +68 -0
  24. package/bld/tracker.js +145 -0
  25. package/bluetooth/clock.d.ts +22 -0
  26. package/bluetooth/clock.js +37 -0
  27. package/bluetooth/cubeSkins.d.ts +31 -0
  28. package/bluetooth/cubeSkins.js +32 -0
  29. package/bluetooth/gyro.d.ts +31 -0
  30. package/bluetooth/gyro.js +46 -0
  31. package/bluetooth/index.d.ts +5 -0
  32. package/bluetooth/index.js +5 -0
  33. package/bluetooth/session.d.ts +140 -0
  34. package/bluetooth/session.js +201 -0
  35. package/bluetooth/simulated.d.ts +52 -0
  36. package/bluetooth/simulated.js +80 -0
  37. package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-definitions.d.ts +38 -0
  38. package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-definitions.js +51 -0
  39. package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-encrypter.d.ts +35 -0
  40. package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-encrypter.js +70 -0
  41. package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-protocol.d.ts +273 -0
  42. package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-protocol.js +846 -0
  43. package/bluetooth/vendor/smartcube-web-bluetooth/gan-gen1.d.ts +38 -0
  44. package/bluetooth/vendor/smartcube-web-bluetooth/gan-gen1.js +340 -0
  45. package/bluetooth/vendor/smartcube-web-bluetooth/gan-gen234-packet-validate.d.ts +11 -0
  46. package/bluetooth/vendor/smartcube-web-bluetooth/gan-gen234-packet-validate.js +118 -0
  47. package/bluetooth/vendor/smartcube-web-bluetooth/gan-mac-salt.d.ts +2 -0
  48. package/bluetooth/vendor/smartcube-web-bluetooth/gan-mac-salt.js +15 -0
  49. package/bluetooth/vendor/smartcube-web-bluetooth/gan-smart-cube.d.ts +18 -0
  50. package/bluetooth/vendor/smartcube-web-bluetooth/gan-smart-cube.js +152 -0
  51. package/bluetooth/vendor/smartcube-web-bluetooth/gan-smart-timer.d.ts +74 -0
  52. package/bluetooth/vendor/smartcube-web-bluetooth/gan-smart-timer.js +161 -0
  53. package/bluetooth/vendor/smartcube-web-bluetooth/gan-write-queue.d.ts +7 -0
  54. package/bluetooth/vendor/smartcube-web-bluetooth/gan-write-queue.js +11 -0
  55. package/bluetooth/vendor/smartcube-web-bluetooth/gatt-characteristic-write.d.ts +2 -0
  56. package/bluetooth/vendor/smartcube-web-bluetooth/gatt-characteristic-write.js +36 -0
  57. package/bluetooth/vendor/smartcube-web-bluetooth/index.d.ts +4 -0
  58. package/bluetooth/vendor/smartcube-web-bluetooth/index.js +4 -0
  59. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/address-hints.d.ts +12 -0
  60. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/address-hints.js +122 -0
  61. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/build-picker-options.d.ts +11 -0
  62. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/build-picker-options.js +55 -0
  63. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/default-optional-services.d.ts +21 -0
  64. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/default-optional-services.js +47 -0
  65. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/device-name-scan-filters.d.ts +6 -0
  66. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/device-name-scan-filters.js +24 -0
  67. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/gatt-snapshot.d.ts +6 -0
  68. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/gatt-snapshot.js +57 -0
  69. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-candidates.d.ts +11 -0
  70. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-candidates.js +67 -0
  71. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-probe-moyu32.d.ts +8 -0
  72. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-probe-moyu32.js +98 -0
  73. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-probe-qiyi.d.ts +8 -0
  74. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-probe-qiyi.js +128 -0
  75. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/moyu32-session-crypto.d.ts +5 -0
  76. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/moyu32-session-crypto.js +60 -0
  77. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/normalize-uuid.d.ts +2 -0
  78. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/normalize-uuid.js +8 -0
  79. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/packet-sanity.d.ts +7 -0
  80. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/packet-sanity.js +142 -0
  81. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/profile-rank.d.ts +6 -0
  82. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/profile-rank.js +21 -0
  83. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/types.d.ts +32 -0
  84. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/types.js +1 -0
  85. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/ble-utils.d.ts +6 -0
  86. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/ble-utils.js +68 -0
  87. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/connect.d.ts +3 -0
  88. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/connect.js +178 -0
  89. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/cubie-cube.d.ts +16 -0
  90. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/cubie-cube.js +141 -0
  91. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/index.d.ts +11 -0
  92. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/index.js +10 -0
  93. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocol.d.ts +26 -0
  94. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocol.js +8 -0
  95. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/gan.d.ts +3 -0
  96. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/gan.js +265 -0
  97. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/giiker.d.ts +3 -0
  98. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/giiker.js +345 -0
  99. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/gocube.d.ts +15 -0
  100. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/gocube.js +382 -0
  101. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu-mhc.d.ts +3 -0
  102. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu-mhc.js +352 -0
  103. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu-v1.d.ts +57 -0
  104. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu-v1.js +243 -0
  105. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu32.d.ts +3 -0
  106. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu32.js +454 -0
  107. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/qiyi.d.ts +3 -0
  108. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/qiyi.js +424 -0
  109. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/types.d.ts +78 -0
  110. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/types.js +1 -0
  111. package/bluetooth/vendor/smartcube-web-bluetooth/utils.d.ts +45 -0
  112. package/bluetooth/vendor/smartcube-web-bluetooth/utils.js +150 -0
  113. package/cfop/caseData.d.ts +16 -0
  114. package/cfop/caseData.js +88 -0
  115. package/cfop/cases.d.ts +42 -0
  116. package/cfop/cases.js +116 -0
  117. package/cfop/f2l.d.ts +28 -0
  118. package/cfop/f2l.js +88 -0
  119. package/cfop/f2lData.d.ts +15 -0
  120. package/cfop/f2lData.js +49 -0
  121. package/cfop/index.d.ts +41 -0
  122. package/cfop/index.js +110 -0
  123. package/cfop/slots.d.ts +2 -0
  124. package/cfop/slots.js +1 -0
  125. package/core/arrows.d.ts +24 -0
  126. package/core/arrows.js +22 -0
  127. package/core/bytes.d.ts +27 -0
  128. package/core/bytes.js +145 -0
  129. package/core/centers.d.ts +32 -0
  130. package/core/centers.js +83 -0
  131. package/core/checks.d.ts +53 -0
  132. package/core/checks.js +85 -0
  133. package/core/cubies.d.ts +45 -0
  134. package/core/cubies.js +169 -0
  135. package/core/frames.d.ts +61 -0
  136. package/core/frames.js +126 -0
  137. package/core/geometry.d.ts +58 -0
  138. package/core/geometry.js +132 -0
  139. package/core/index.d.ts +19 -0
  140. package/core/index.js +19 -0
  141. package/core/masks.d.ts +32 -0
  142. package/core/masks.js +39 -0
  143. package/core/method.d.ts +35 -0
  144. package/core/method.js +30 -0
  145. package/core/moves.d.ts +48 -0
  146. package/core/moves.js +88 -0
  147. package/core/notation.d.ts +48 -0
  148. package/core/notation.js +226 -0
  149. package/core/physical.d.ts +53 -0
  150. package/core/physical.js +99 -0
  151. package/core/practice.d.ts +62 -0
  152. package/core/practice.js +87 -0
  153. package/core/sequence.d.ts +90 -0
  154. package/core/sequence.js +157 -0
  155. package/core/stages.d.ts +90 -0
  156. package/core/stages.js +21 -0
  157. package/core/state.d.ts +55 -0
  158. package/core/state.js +140 -0
  159. package/core/stateCodec.d.ts +12 -0
  160. package/core/stateCodec.js +51 -0
  161. package/core/stream.d.ts +45 -0
  162. package/core/stream.js +75 -0
  163. package/core/tracker.d.ts +62 -0
  164. package/core/tracker.js +121 -0
  165. package/element/alg.d.ts +45 -0
  166. package/element/alg.js +150 -0
  167. package/element/base.d.ts +2 -0
  168. package/element/base.js +3 -0
  169. package/element/bld.d.ts +82 -0
  170. package/element/bld.js +222 -0
  171. package/element/index.d.ts +8 -0
  172. package/element/index.js +21 -0
  173. package/element/model.d.ts +36 -0
  174. package/element/model.js +64 -0
  175. package/element/player.d.ts +196 -0
  176. package/element/player.js +680 -0
  177. package/element/practice.d.ts +75 -0
  178. package/element/practice.js +150 -0
  179. package/element/scramble.d.ts +50 -0
  180. package/element/scramble.js +107 -0
  181. package/element/sequence.d.ts +126 -0
  182. package/element/sequence.js +254 -0
  183. package/element/styles.d.ts +32 -0
  184. package/element/styles.js +196 -0
  185. package/image/cache.d.ts +21 -0
  186. package/image/cache.js +66 -0
  187. package/image/index.d.ts +2 -0
  188. package/image/index.js +2 -0
  189. package/image/png-node.d.ts +6 -0
  190. package/image/png-node.js +10 -0
  191. package/image/svg.d.ts +36 -0
  192. package/image/svg.js +215 -0
  193. package/lbl/index.d.ts +10 -0
  194. package/lbl/index.js +31 -0
  195. package/methods/index.d.ts +20 -0
  196. package/methods/index.js +33 -0
  197. package/package.json +133 -0
  198. package/petrus/index.d.ts +11 -0
  199. package/petrus/index.js +27 -0
  200. package/react/index.d.ts +176 -0
  201. package/react/index.js +228 -0
  202. package/render/arrows.d.ts +68 -0
  203. package/render/arrows.js +190 -0
  204. package/render/build/attachments.d.ts +35 -0
  205. package/render/build/attachments.js +94 -0
  206. package/render/build/models.d.ts +9 -0
  207. package/render/build/models.js +27 -0
  208. package/render/build/tiles.d.ts +48 -0
  209. package/render/build/tiles.js +194 -0
  210. package/render/gltf.d.ts +10 -0
  211. package/render/gltf.js +106 -0
  212. package/render/index.d.ts +12 -0
  213. package/render/index.js +12 -0
  214. package/render/layers.d.ts +21 -0
  215. package/render/layers.js +24 -0
  216. package/render/pictures.d.ts +46 -0
  217. package/render/pictures.js +80 -0
  218. package/render/pieceModels.d.ts +38 -0
  219. package/render/pieceModels.js +43 -0
  220. package/render/renderer.d.ts +165 -0
  221. package/render/renderer.js +647 -0
  222. package/render/tile.d.ts +114 -0
  223. package/render/tile.js +390 -0
  224. package/render/viewports.d.ts +25 -0
  225. package/render/viewports.js +26 -0
  226. package/roux/cmll.d.ts +35 -0
  227. package/roux/cmll.js +121 -0
  228. package/roux/cmllData.d.ts +12 -0
  229. package/roux/cmllData.js +49 -0
  230. package/roux/index.d.ts +52 -0
  231. package/roux/index.js +175 -0
  232. package/skin/attachments.d.ts +82 -0
  233. package/skin/attachments.js +110 -0
  234. package/skin/index.d.ts +3 -0
  235. package/skin/index.js +3 -0
  236. package/skin/shapes.d.ts +112 -0
  237. package/skin/shapes.js +147 -0
  238. package/skin/skin.d.ts +911 -0
  239. package/skin/skin.js +412 -0
  240. package/solve/client.d.ts +58 -0
  241. package/solve/client.js +56 -0
  242. package/solve/handle.d.ts +6 -0
  243. package/solve/handle.js +38 -0
  244. package/solve/index.d.ts +8 -0
  245. package/solve/index.js +7 -0
  246. package/solve/lse.d.ts +42 -0
  247. package/solve/lse.js +151 -0
  248. package/solve/pieces.d.ts +18 -0
  249. package/solve/pieces.js +24 -0
  250. package/solve/protocol.d.ts +70 -0
  251. package/solve/protocol.js +16 -0
  252. package/solve/scramble.d.ts +76 -0
  253. package/solve/scramble.js +187 -0
  254. package/solve/stage.d.ts +108 -0
  255. package/solve/stage.js +532 -0
  256. package/solve/twophase.d.ts +36 -0
  257. package/solve/twophase.js +229 -0
  258. package/solve/worker.d.ts +6 -0
  259. package/solve/worker.js +15 -0
  260. package/timeline/codec.d.ts +23 -0
  261. package/timeline/codec.js +84 -0
  262. package/timeline/index.d.ts +7 -0
  263. package/timeline/index.js +7 -0
  264. package/timeline/recording.d.ts +19 -0
  265. package/timeline/recording.js +20 -0
  266. package/timeline/replay.d.ts +68 -0
  267. package/timeline/replay.js +145 -0
  268. package/timeline/segments.d.ts +31 -0
  269. package/timeline/segments.js +39 -0
  270. package/timeline/share.d.ts +30 -0
  271. package/timeline/share.js +62 -0
  272. package/timeline/stats.d.ts +34 -0
  273. package/timeline/stats.js +56 -0
  274. package/timeline/timings.d.ts +30 -0
  275. package/timeline/timings.js +54 -0
  276. package/zz/index.d.ts +29 -0
  277. package/zz/index.js +68 -0
  278. package/zz/seeds.d.ts +9 -0
  279. package/zz/seeds.js +29 -0
@@ -0,0 +1,75 @@
1
+ /**
2
+ * <cube-alg-practice> — practising an algorithm on a smart cube. Set the
3
+ * case up (e.g. with <cube-scramble>), then:
4
+ *
5
+ * el.alg = "R U R' U R U2 R'";
6
+ * el.attach(session); // tracks from the cube's current state
7
+ * el.addEventListener("complete", (e) => …); // e.detail.practice: { elapsedMs, turns, tps, mistakes, differentAlg }
8
+ *
9
+ * reveal="all" every move shown (learning)
10
+ * reveal="done" dots, each move shown once done (default)
11
+ * reveal="none" dots to the end (you know it)
12
+ * A slip counts a mistake, shows what to undo and (unless
13
+ * hint-on-mistake="off") the move that should have come. Another algorithm
14
+ * that reaches the same end state completes it too, flagged `differentAlg`.
15
+ *
16
+ * Default controls: Hint, Show / Hide, Restart (parts button-hint,
17
+ * button-reveal, button-restart). Customising: see sequence.ts.
18
+ * Events: progress, mistake, complete (detail: the progress). Messages:
19
+ * undo, reset, complete, hint, show, hide, restart, differentAlg, and
20
+ * `formatStats(practice)` for the line under the moves.
21
+ */
22
+ import { type Move, type NextTurn, type PracticeProgress, type State } from "../core/index.js";
23
+ import { CubeSequenceElement, type Decorations, type SequenceMessages, type ShownToken } from "./sequence.js";
24
+ export interface PracticeMessages extends SequenceMessages {
25
+ hint: string;
26
+ show: string;
27
+ hide: string;
28
+ restart: string;
29
+ differentAlg: string;
30
+ }
31
+ export declare class CubeAlgPractice extends CubeSequenceElement {
32
+ static observedAttributes: string[];
33
+ private tracker;
34
+ private moves;
35
+ private shownAll;
36
+ private _decorations;
37
+ private extra;
38
+ /** The line under the moves once done (and while going). */
39
+ formatStats: (p: PracticeProgress["practice"], complete: boolean) => string;
40
+ constructor();
41
+ attributeChangedCallback(): void;
42
+ get alg(): string;
43
+ set alg(v: string | readonly Move[]);
44
+ /**
45
+ * Text around written moves, e.g. trigger groups "F (R U R' U') F'":
46
+ * `{ 1: { prefix: "(" }, 4: { suffix: ")" } }` (by move index; part "decoration").
47
+ */
48
+ get decorations(): Decorations;
49
+ set decorations(d: Decorations | null);
50
+ protected decorationOf(index: number): {
51
+ prefix?: string;
52
+ suffix?: string;
53
+ } | undefined;
54
+ get messages(): PracticeMessages;
55
+ set messages(m: Partial<PracticeMessages>);
56
+ get progress(): PracticeProgress | null;
57
+ /** Show the next move. */
58
+ hint(): void;
59
+ /** Show every move / back to the reveal mode. */
60
+ toggleShown(): void;
61
+ protected startTracking(start: State): void;
62
+ protected track(move: Move, time: number): PracticeProgress;
63
+ protected nextTurn(): NextTurn | null;
64
+ protected tokens(): ShownToken[];
65
+ protected extraMessage(): string;
66
+ protected onAction(action: string): void;
67
+ private revealMode;
68
+ private labelButtons;
69
+ }
70
+ export declare function defineCubeAlgPractice(tag?: string): void;
71
+ declare global {
72
+ interface HTMLElementTagNameMap {
73
+ "cube-alg-practice": CubeAlgPractice;
74
+ }
75
+ }
@@ -0,0 +1,150 @@
1
+ /**
2
+ * <cube-alg-practice> — practising an algorithm on a smart cube. Set the
3
+ * case up (e.g. with <cube-scramble>), then:
4
+ *
5
+ * el.alg = "R U R' U R U2 R'";
6
+ * el.attach(session); // tracks from the cube's current state
7
+ * el.addEventListener("complete", (e) => …); // e.detail.practice: { elapsedMs, turns, tps, mistakes, differentAlg }
8
+ *
9
+ * reveal="all" every move shown (learning)
10
+ * reveal="done" dots, each move shown once done (default)
11
+ * reveal="none" dots to the end (you know it)
12
+ * A slip counts a mistake, shows what to undo and (unless
13
+ * hint-on-mistake="off") the move that should have come. Another algorithm
14
+ * that reaches the same end state completes it too, flagged `differentAlg`.
15
+ *
16
+ * Default controls: Hint, Show / Hide, Restart (parts button-hint,
17
+ * button-reveal, button-restart). Customising: see sequence.ts.
18
+ * Events: progress, mistake, complete (detail: the progress). Messages:
19
+ * undo, reset, complete, hint, show, hide, restart, differentAlg, and
20
+ * `formatStats(practice)` for the line under the moves.
21
+ */
22
+ import { PracticeTracker, formatMove, parseAlg } from "../core/index.js";
23
+ import { CubeSequenceElement } from "./sequence.js";
24
+ export class CubeAlgPractice extends CubeSequenceElement {
25
+ static observedAttributes = [...CubeSequenceElement.observedAttributes, "reveal", "hint-on-mistake"];
26
+ tracker = null;
27
+ moves = [];
28
+ shownAll = false;
29
+ _decorations = {};
30
+ extra = { hint: "Hint", show: "Show", hide: "Hide", restart: "Restart", differentAlg: "solved another way" };
31
+ /** The line under the moves once done (and while going). */
32
+ formatStats = (p, complete) => {
33
+ if (!p.turns)
34
+ return "";
35
+ const parts = [`${(p.elapsedMs / 1000).toFixed(2)} s`, `${p.turns} turn${p.turns === 1 ? "" : "s"}`, `${p.tps.toFixed(1)} TPS`];
36
+ if (p.mistakes)
37
+ parts.push(`${p.mistakes} mistake${p.mistakes === 1 ? "" : "s"}`);
38
+ if (complete && p.differentAlg)
39
+ parts.push(this.extra.differentAlg);
40
+ return parts.join(" · ");
41
+ };
42
+ constructor() {
43
+ super("", `<button part="button button-hint" data-action="hint"></button><button part="button button-reveal" data-action="reveal" aria-pressed="false"></button><button part="button button-restart" data-action="restart"></button>`);
44
+ this.labelButtons();
45
+ }
46
+ attributeChangedCallback() {
47
+ this.tracker?.setReveal(this.revealMode());
48
+ super.attributeChangedCallback();
49
+ }
50
+ get alg() {
51
+ return this.moves.map(formatMove).join(" ");
52
+ }
53
+ set alg(v) {
54
+ this.moves = typeof v === "string" ? parseAlg(v) : [...v];
55
+ this.shownAll = false;
56
+ this.reset();
57
+ }
58
+ /**
59
+ * Text around written moves, e.g. trigger groups "F (R U R' U') F'":
60
+ * `{ 1: { prefix: "(" }, 4: { suffix: ")" } }` (by move index; part "decoration").
61
+ */
62
+ get decorations() {
63
+ return this._decorations;
64
+ }
65
+ set decorations(d) {
66
+ this._decorations = d ?? {};
67
+ this.update();
68
+ }
69
+ decorationOf(index) {
70
+ return this._decorations[index];
71
+ }
72
+ get messages() {
73
+ return { ...this.messagesBase, ...this.extra };
74
+ }
75
+ set messages(m) {
76
+ const { hint, show, hide, restart, differentAlg, ...base } = m;
77
+ this.extra = { ...this.extra, ...Object.fromEntries(Object.entries({ hint, show, hide, restart, differentAlg }).filter(([, v]) => v !== undefined)) };
78
+ this.labelButtons();
79
+ this.setMessages(base);
80
+ }
81
+ get progress() {
82
+ return this.tracker?.progress ?? null;
83
+ }
84
+ /** Show the next move. */
85
+ hint() {
86
+ this.tracker?.hint();
87
+ this.update();
88
+ }
89
+ /** Show every move / back to the reveal mode. */
90
+ toggleShown() {
91
+ this.shownAll = !this.shownAll;
92
+ this.tracker?.setReveal(this.revealMode());
93
+ this.labelButtons();
94
+ this.update();
95
+ }
96
+ startTracking(start) {
97
+ this.tracker = new PracticeTracker(this.moves, start, { frame: this.startFrame, reveal: this.revealMode(), hintOnMistake: this.getAttribute("hint-on-mistake") !== "off" });
98
+ }
99
+ track(move, time) {
100
+ const before = this.tracker.progress.practice.mistakes;
101
+ const p = this.tracker.push(move, time);
102
+ if (p.practice.mistakes > before)
103
+ this.dispatchEvent(new CustomEvent("mistake", { detail: p }));
104
+ return p;
105
+ }
106
+ nextTurn() {
107
+ return this.tracker?.nextTurn ?? null;
108
+ }
109
+ tokens() {
110
+ const p = this.tracker?.progress;
111
+ return this.moves.map((m, i) => {
112
+ const t = p?.practice.tokens[i];
113
+ return { text: formatMove(m), status: t?.status ?? "todo", visible: t?.visible ?? true };
114
+ });
115
+ }
116
+ extraMessage() {
117
+ const p = this.tracker?.progress;
118
+ return p ? this.formatStats(p.practice, p.complete) : "";
119
+ }
120
+ onAction(action) {
121
+ if (action === "hint")
122
+ this.hint();
123
+ else if (action === "reveal")
124
+ this.toggleShown();
125
+ else if (action === "restart")
126
+ this.reset();
127
+ }
128
+ revealMode() {
129
+ if (this.shownAll)
130
+ return "all";
131
+ const r = this.getAttribute("reveal");
132
+ return r === "all" || r === "none" ? r : "done";
133
+ }
134
+ labelButtons() {
135
+ const set = (sel, text) => {
136
+ const b = this.root.querySelector(`[data-action="${sel}"]`);
137
+ if (b)
138
+ b.textContent = text;
139
+ };
140
+ set("hint", this.extra.hint);
141
+ set("reveal", this.shownAll ? this.extra.hide : this.extra.show);
142
+ set("restart", this.extra.restart);
143
+ this.root.querySelector('[data-action="reveal"]')?.setAttribute("aria-pressed", String(this.shownAll));
144
+ }
145
+ }
146
+ export function defineCubeAlgPractice(tag = "cube-alg-practice") {
147
+ if (!customElements.get(tag))
148
+ customElements.define(tag, class extends CubeAlgPractice {
149
+ });
150
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * <cube-scramble> — shows a scramble and follows it on a smart cube: done
3
+ * moves fade, the next one is highlighted, half of a half turn shows as
4
+ * partial, after a slip it shows what to undo. The scramble is always shown
5
+ * in full (never hidden).
6
+ *
7
+ * el.scramble = "R U2 F' …"; // or Move[]
8
+ * el.attach(session); // a SmartCubeSession, or anything with state + "move" events
9
+ * el.addEventListener("complete", startInspection);
10
+ *
11
+ * `editable`: an input to paste or type your own scramble (competition,
12
+ * another timer…) — validated as you type; a valid one replaces the
13
+ * scramble and fires `change` (detail: { moves, text }).
14
+ *
15
+ * Customising: see sequence.ts (CSS variables, ::part, slots, headless).
16
+ * Own parts: input, error. Messages: undo, reset, complete, placeholder, invalid.
17
+ * What happens when it's done (inspection, timer…) is the app's call.
18
+ */
19
+ import { type Move, type NextTurn, type SequenceProgress, type State } from "../core/index.js";
20
+ import { CubeSequenceElement, type SequenceMessages, type ShownToken } from "./sequence.js";
21
+ export interface ScrambleMessages extends SequenceMessages {
22
+ placeholder: string;
23
+ invalid: string;
24
+ }
25
+ export declare class CubeScramble extends CubeSequenceElement {
26
+ static observedAttributes: string[];
27
+ private tracker;
28
+ private moves;
29
+ private extra;
30
+ constructor();
31
+ /** The scramble (written notation or moves). */
32
+ get scramble(): string;
33
+ set scramble(v: string | readonly Move[]);
34
+ private setMoves;
35
+ get messages(): ScrambleMessages;
36
+ set messages(m: Partial<ScrambleMessages>);
37
+ get progress(): SequenceProgress | null;
38
+ protected startTracking(start: State): void;
39
+ protected track(move: Move): SequenceProgress;
40
+ protected nextTurn(): NextTurn | null;
41
+ protected tokens(): ShownToken[];
42
+ /** A pasted / typed scramble: take it if it parses (and isn't empty). */
43
+ private fromText;
44
+ }
45
+ export declare function defineCubeScramble(tag?: string): void;
46
+ declare global {
47
+ interface HTMLElementTagNameMap {
48
+ "cube-scramble": CubeScramble;
49
+ }
50
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * <cube-scramble> — shows a scramble and follows it on a smart cube: done
3
+ * moves fade, the next one is highlighted, half of a half turn shows as
4
+ * partial, after a slip it shows what to undo. The scramble is always shown
5
+ * in full (never hidden).
6
+ *
7
+ * el.scramble = "R U2 F' …"; // or Move[]
8
+ * el.attach(session); // a SmartCubeSession, or anything with state + "move" events
9
+ * el.addEventListener("complete", startInspection);
10
+ *
11
+ * `editable`: an input to paste or type your own scramble (competition,
12
+ * another timer…) — validated as you type; a valid one replaces the
13
+ * scramble and fires `change` (detail: { moves, text }).
14
+ *
15
+ * Customising: see sequence.ts (CSS variables, ::part, slots, headless).
16
+ * Own parts: input, error. Messages: undo, reset, complete, placeholder, invalid.
17
+ * What happens when it's done (inspection, timer…) is the app's call.
18
+ */
19
+ import { SequenceTracker, formatMove, parseAlg } from "../core/index.js";
20
+ import { CubeSequenceElement } from "./sequence.js";
21
+ const STYLES = /* css */ `
22
+ .input { flex: 1; min-width: 12em; display: none; }
23
+ :host([editable]) .input { display: block; }
24
+ .input input {
25
+ width: 100%; box-sizing: border-box;
26
+ background: color-mix(in srgb, currentColor 6%, transparent); color: inherit;
27
+ border: 1px solid color-mix(in srgb, currentColor 20%, transparent); border-radius: var(--cc-control-radius);
28
+ padding: 7px 9px; font: 13px var(--cc-seq-font);
29
+ }
30
+ .error { display: none; color: var(--cc-seq-undo); font: 500 12px/1.3 system-ui, sans-serif; width: 100%; }
31
+ .error.on { display: block; }
32
+ `;
33
+ export class CubeScramble extends CubeSequenceElement {
34
+ static observedAttributes = [...CubeSequenceElement.observedAttributes, "editable"];
35
+ tracker = null;
36
+ moves = [];
37
+ extra = { placeholder: "Paste or type your own scramble", invalid: "Not a scramble:" };
38
+ constructor() {
39
+ super(STYLES, `<div class="input" part="input-container"><input part="input" type="text" spellcheck="false" autocomplete="off" /></div><div class="error" part="error"></div>`);
40
+ const input = this.root.querySelector("input");
41
+ input.placeholder = this.extra.placeholder;
42
+ input.addEventListener("input", () => this.fromText(input.value));
43
+ }
44
+ /** The scramble (written notation or moves). */
45
+ get scramble() {
46
+ return this.moves.map(formatMove).join(" ");
47
+ }
48
+ set scramble(v) {
49
+ this.setMoves(typeof v === "string" ? parseAlg(v) : [...v]);
50
+ // Set from outside (a new generated one): the typed text is stale.
51
+ this.root.querySelector("input").value = "";
52
+ this.root.querySelector(".error").classList.remove("on");
53
+ }
54
+ setMoves(moves) {
55
+ this.moves = moves;
56
+ this.reset();
57
+ }
58
+ get messages() {
59
+ return { ...this.messagesBase, ...this.extra };
60
+ }
61
+ set messages(m) {
62
+ const { placeholder, invalid, ...base } = m;
63
+ if (placeholder !== undefined)
64
+ this.extra.placeholder = placeholder;
65
+ if (invalid !== undefined)
66
+ this.extra.invalid = invalid;
67
+ this.root.querySelector("input").placeholder = this.extra.placeholder;
68
+ this.setMessages(base);
69
+ }
70
+ get progress() {
71
+ return this.tracker?.progress ?? null;
72
+ }
73
+ startTracking(start) {
74
+ this.tracker = new SequenceTracker(this.moves, start, { frame: this.startFrame });
75
+ }
76
+ track(move) {
77
+ return this.tracker.push(move);
78
+ }
79
+ nextTurn() {
80
+ return this.tracker?.nextTurn ?? null;
81
+ }
82
+ tokens() {
83
+ const p = this.tracker?.progress;
84
+ return this.moves.map((m, i) => ({ text: formatMove(m), status: p?.tokens[i] ?? "todo", visible: true }));
85
+ }
86
+ /** A pasted / typed scramble: take it if it parses (and isn't empty). */
87
+ fromText(text) {
88
+ const error = this.root.querySelector(".error");
89
+ try {
90
+ const moves = parseAlg(text);
91
+ error.classList.remove("on");
92
+ if (!moves.length)
93
+ return;
94
+ this.setMoves(moves);
95
+ this.dispatchEvent(new CustomEvent("change", { detail: { moves, text } }));
96
+ }
97
+ catch (e) {
98
+ error.textContent = `${this.extra.invalid} ${e instanceof Error ? e.message : String(e)}`;
99
+ error.classList.add("on");
100
+ }
101
+ }
102
+ }
103
+ export function defineCubeScramble(tag = "cube-scramble") {
104
+ if (!customElements.get(tag))
105
+ customElements.define(tag, class extends CubeScramble {
106
+ });
107
+ }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * CubeSequenceElement — what <cube-scramble> and <cube-alg-practice> share:
3
+ * a list of moves followed on a smart cube (core SequenceTracker under the
4
+ * hood), with the slip / undo line, a status message, controls, events and
5
+ * one styling vocabulary. Not registered on its own.
6
+ *
7
+ * Three levels of customising, like <cube-player>:
8
+ * 1. CSS variables (on the element or any ancestor):
9
+ * --cc-seq-font, --cc-seq-size, --cc-seq-gap text
10
+ * --cc-seq-done, --cc-seq-current, --cc-seq-current-bg, --cc-seq-partial,
11
+ * --cc-seq-todo, --cc-seq-hidden (dots), --cc-seq-undo,
12
+ * --cc-seq-wrong-way (the move due, turned the wrong way) move colours
13
+ * --cc-accent, --cc-control-bg, --cc-control-radius controls
14
+ * 2. ::part(): container, moves, move, move-done / -current / -partial / -todo /
15
+ * -hidden, undo, undo-label, undo-move, message, controls, button (+ button-<name>),
16
+ * and each element's own (scramble: input, error).
17
+ * 3. Replace pieces: children with slot="undo", slot="message" or
18
+ * slot="controls" take those places (controls="none" hides the default
19
+ * controls); `headless` hides the whole built-in view — build yours
20
+ * from the `progress` event (tokens with text / status / visibility,
21
+ * undo, needsReset, complete, …) and call the element's methods.
22
+ *
23
+ * Arrows: with `arrows` set (the element following a cube — attached, or
24
+ * fed with push()) and a player (`player="id"` of a <cube-player>,
25
+ * or the `player` property), the 3D cube shows the next turn — one arrow per
26
+ * turning layer (two for a wide r), as many heads as quarter turns, in
27
+ * --cc-arrow; after a slip the undo move in --cc-arrow-undo, or in
28
+ * --cc-arrow-wrong-way when the right face went the wrong way.
29
+ * `arrow-shape="circle"` for arcs instead of ribbons along the faces.
30
+ *
31
+ * After a slip the moves give way to the way back: the undo moves in their
32
+ * place (--cc-seq-undo), until the cube is back on track —
33
+ * `undo-display="below"` keeps the moves and shows the undo line under them.
34
+ *
35
+ * Events: progress (detail: the progress), complete.
36
+ */
37
+ import { type Frame, type Move, type NextTurn, type SequenceProgress, type State, type TokenStatus, type TurnArrow } from "../core/index.js";
38
+ import type { ArrowStyle } from "../render/index.js";
39
+ import { ElementBase } from "./base.js";
40
+ /** Where turn arrows go — a <cube-player> fits. */
41
+ export interface ArrowTarget {
42
+ showTurnArrows(arrows: readonly TurnArrow[] | null, style?: ArrowStyle, owner?: unknown): void;
43
+ }
44
+ /** What `attach` needs: the cube's current state and its moves (a SmartCubeSession fits). */
45
+ export interface MoveSource {
46
+ readonly state: State;
47
+ on(type: "move", listener: (e: {
48
+ move: Move;
49
+ time?: number;
50
+ }) => void): () => void;
51
+ }
52
+ /** Text shown before / after written moves, by move index. */
53
+ export type Decorations = Partial<Record<number, {
54
+ prefix?: string;
55
+ suffix?: string;
56
+ }>>;
57
+ /** One move as shown: its text, where it stands, whether it's shown (else a dot). */
58
+ export interface ShownToken {
59
+ text: string;
60
+ status: TokenStatus;
61
+ visible: boolean;
62
+ }
63
+ export interface SequenceMessages {
64
+ undo: string;
65
+ reset: string;
66
+ complete: string;
67
+ }
68
+ export declare const SEQUENCE_STYLES = "\n:host {\n --cc-seq-font: ui-monospace, \"SF Mono\", Menlo, monospace;\n --cc-seq-size: 22px;\n --cc-seq-gap: 0.35em;\n --cc-seq-done: color-mix(in srgb, currentColor 35%, transparent);\n --cc-seq-current: currentColor;\n --cc-seq-current-bg: color-mix(in srgb, var(--cc-accent) 24%, transparent);\n --cc-seq-partial: var(--cc-accent);\n --cc-seq-todo: color-mix(in srgb, currentColor 82%, transparent);\n --cc-seq-hidden: color-mix(in srgb, currentColor 45%, transparent);\n --cc-seq-undo: #ff5a5a;\n --cc-seq-wrong-way: #ff9a1f;\n --cc-seq-decoration: color-mix(in srgb, currentColor 70%, transparent);\n --cc-arrow: #2f8bff;\n --cc-arrow-undo: #ff4545;\n --cc-arrow-wrong-way: #ff9a1f;\n --cc-accent: #4f8cff;\n --cc-control-bg: color-mix(in srgb, currentColor 8%, transparent);\n --cc-control-bg-hover: color-mix(in srgb, currentColor 15%, transparent);\n --cc-control-radius: 8px;\n display: block;\n}\n:host([headless]) .container { display: none; }\n.group { display: inline-flex; align-items: baseline; }\n.decoration { color: var(--cc-seq-decoration); }\n.container { display: flex; flex-direction: column; gap: 0.5em; }\n.container.slip .moves { display: none; }\n.container.slip .undo { font-size: var(--cc-seq-size); line-height: 1.5; }\n.moves { display: flex; flex-wrap: wrap; gap: 0.1em var(--cc-seq-gap); font: 600 var(--cc-seq-size) / 1.5 var(--cc-seq-font); }\n.move { padding: 0 0.18em; border-radius: 0.25em; transition: color 0.15s ease, background 0.15s ease; }\n.done { color: var(--cc-seq-done); }\n.current { color: var(--cc-seq-current); background: var(--cc-seq-current-bg); }\n.partial { color: var(--cc-seq-partial); background: var(--cc-seq-current-bg); }\n.wrong-way { color: var(--cc-seq-wrong-way); background: var(--cc-seq-current-bg); }\n.todo { color: var(--cc-seq-todo); }\n.hidden { color: var(--cc-seq-hidden); }\n.undo { display: none; color: var(--cc-seq-undo); gap: var(--cc-seq-gap); flex-wrap: wrap; align-items: baseline; font: 600 calc(var(--cc-seq-size) * 0.85) / 1.4 var(--cc-seq-font); }\n.undo.on { display: flex; }\n.undo-label { font: 600 0.65em/1 system-ui, sans-serif; text-transform: uppercase; letter-spacing: 0.06em; margin-right: 0.3em; }\n.message { display: none; font: 500 13px/1.4 system-ui, sans-serif; opacity: 0.85; }\n.message.on { display: block; }\n.controls { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }\n.controls:empty { display: none; }\n:host([controls=\"none\"]) .controls { display: none; }\nbutton {\n border: 0; border-radius: var(--cc-control-radius);\n background: var(--cc-control-bg); color: inherit;\n padding: 6px 11px; font: 600 12px/1 system-ui, sans-serif; cursor: pointer;\n}\nbutton:hover { background: var(--cc-control-bg-hover); }\nbutton:focus-visible, input:focus-visible { outline: 2px solid var(--cc-accent); outline-offset: 2px; }\nbutton[aria-pressed=\"true\"] { background: color-mix(in srgb, var(--cc-accent) 25%, transparent); }\n";
69
+ export declare abstract class CubeSequenceElement extends ElementBase {
70
+ protected readonly root: ShadowRoot;
71
+ protected source: MoveSource | null;
72
+ private off;
73
+ protected messagesBase: SequenceMessages;
74
+ private completed;
75
+ private _player;
76
+ /** How the cube is held when the sequence starts (see `frame`). */
77
+ protected startFrame: Frame;
78
+ static observedAttributes: string[];
79
+ attributeChangedCallback(): void;
80
+ constructor(extraStyles?: string, controlsHtml?: string);
81
+ /** Start following from `start`. */
82
+ protected abstract startTracking(start: State): void;
83
+ /** Feed one face turn; return the progress. */
84
+ protected abstract track(move: Move, time: number): SequenceProgress;
85
+ /** Current progress (null before anything is set). */
86
+ abstract get progress(): SequenceProgress | null;
87
+ /** The moves as shown. */
88
+ protected abstract tokens(): ShownToken[];
89
+ /** What to turn now (null: nothing, or nothing to give away). */
90
+ protected abstract nextTurn(): NextTurn | null;
91
+ /** Extra message (e.g. stats) when nothing else is said. */
92
+ protected extraMessage(): string;
93
+ protected onAction(_action: string): void;
94
+ /** Follow a smart cube from its current state. Returns detach. */
95
+ attach(source: MoveSource): () => void;
96
+ detach(): void;
97
+ /**
98
+ * How the cube is held when the sequence starts (default: its own U on top,
99
+ * F in front) — for an algorithm right after one with a net rotation.
100
+ * Setting it starts again.
101
+ */
102
+ get frame(): Frame;
103
+ set frame(f: Frame | null);
104
+ /** Start again from `start` (default: the attached cube's state, else solved). */
105
+ reset(start?: State): void;
106
+ /** A face turn from the cube (called for you when attached). Once complete, further turns are
107
+ * ignored (the solve / next attempt has started) until a new sequence or `reset()`. */
108
+ push(move: Move, time?: number): void;
109
+ /** Player for the arrows (else the `player` attribute's id). */
110
+ get player(): ArrowTarget | null;
111
+ set player(p: ArrowTarget | null);
112
+ private arrowTarget;
113
+ /** Draw the arrows again — e.g. after changing --cc-arrow* (CSS changes don't notify the element). */
114
+ refreshArrows(): void;
115
+ private syncArrows;
116
+ get messages(): SequenceMessages;
117
+ set messages(m: Partial<SequenceMessages>);
118
+ protected setMessages(m: Partial<SequenceMessages>): void;
119
+ /** Text around written move `index` (none here; <cube-alg-practice> has trigger groups). */
120
+ protected decorationOf(_index: number): {
121
+ prefix?: string;
122
+ suffix?: string;
123
+ } | undefined;
124
+ protected update(): void;
125
+ }
126
+ export declare const escapeHtml: (s: string) => string;