@wodzik/cubecore 0.1.11 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/sequence.d.ts +29 -9
- package/core/sequence.js +102 -41
- package/element/sequence.d.ts +4 -2
- package/element/sequence.js +4 -1
- package/package.json +1 -1
package/core/sequence.d.ts
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* It matches CUBE STATES, not move strings, so everything that reaches the
|
|
6
6
|
* same state counts:
|
|
7
7
|
* - a half turn done as two quarter turns, either way (half done = `partial`);
|
|
8
|
-
* -
|
|
8
|
+
* - moves on one axis in any order, even interleaved (R2 L2 done as
|
|
9
|
+
* L R L R, or L2 R2): they commute — each face just has to get there;
|
|
9
10
|
* - an algorithm written with rotations, slices and wide moves (converted
|
|
10
11
|
* with toFaceTurns: what the smart cube will actually report).
|
|
11
12
|
* Off the path, `undo` is the way back to the last matching point (the
|
|
@@ -13,7 +14,7 @@
|
|
|
13
14
|
*/
|
|
14
15
|
import { type TurnArrow } from "./arrows.js";
|
|
15
16
|
import type { Frame } from "./frames.js";
|
|
16
|
-
import type
|
|
17
|
+
import { type Move } from "./moves.js";
|
|
17
18
|
import { type State } from "./state.js";
|
|
18
19
|
export interface SequenceStep {
|
|
19
20
|
/** The face turn the cube will report. */
|
|
@@ -27,7 +28,10 @@ export interface SequenceProgress {
|
|
|
27
28
|
/** Face-turn steps fully done. */
|
|
28
29
|
done: number;
|
|
29
30
|
total: number;
|
|
30
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Part way through the next steps: half of a half turn, or moves on one
|
|
33
|
+
* axis (R2 L2) done out of order / each part of the way.
|
|
34
|
+
*/
|
|
31
35
|
partial: boolean;
|
|
32
36
|
/** Status of each written move (for display). */
|
|
33
37
|
tokens: TokenStatus[];
|
|
@@ -42,11 +46,13 @@ export interface SequenceProgress {
|
|
|
42
46
|
/** What to turn now — for arrows on a 3D cube (see arrows.ts). */
|
|
43
47
|
export interface NextTurn {
|
|
44
48
|
/**
|
|
45
|
-
* "next": the next move of the sequence; "
|
|
46
|
-
*
|
|
47
|
-
* the
|
|
49
|
+
* "next": the next move of the sequence; "finish": the rest of a half turn
|
|
50
|
+
* already started (with R2 L2 both at once, when both are half done);
|
|
51
|
+
* "undo": the first move back after a slip; "wrong-way": the same, when
|
|
52
|
+
* the slip was the right face turned the wrong way (or too far) — worth a
|
|
53
|
+
* colour of its own.
|
|
48
54
|
*/
|
|
49
|
-
kind: "next" | "undo" | "wrong-way";
|
|
55
|
+
kind: "next" | "finish" | "undo" | "wrong-way";
|
|
50
56
|
/** In the cube's own coordinates. Usually one; the rest of an M done half-way can be two. */
|
|
51
57
|
arrows: TurnArrow[];
|
|
52
58
|
/** The written move it belongs to (null for undo). */
|
|
@@ -69,8 +75,11 @@ export declare class SequenceTracker {
|
|
|
69
75
|
readonly frames: Frame[];
|
|
70
76
|
private readonly path;
|
|
71
77
|
private cur;
|
|
78
|
+
private readonly groups;
|
|
79
|
+
private readonly groupOf;
|
|
72
80
|
private anchor;
|
|
73
|
-
|
|
81
|
+
/** At the anchor, inside an axis group: how far each of its faces got (quarters). */
|
|
82
|
+
private anchorGroup;
|
|
74
83
|
private offPath;
|
|
75
84
|
private readonly maxCorrection;
|
|
76
85
|
constructor(target: string | readonly Move[], start: State, options?: SequenceOptions);
|
|
@@ -78,6 +87,13 @@ export declare class SequenceTracker {
|
|
|
78
87
|
/** A face turn reported by the cube. */
|
|
79
88
|
push(move: Move): SequenceProgress;
|
|
80
89
|
get progress(): SequenceProgress;
|
|
90
|
+
/** A step at the anchor: done, part way (half of a half turn), or not started. */
|
|
91
|
+
private stepStatus;
|
|
92
|
+
/**
|
|
93
|
+
* The only slip is a face due (the next step, or one on its axis still to
|
|
94
|
+
* do) turned the wrong way or too far — the step it belongs to, else null.
|
|
95
|
+
*/
|
|
96
|
+
private wrongWayStep;
|
|
81
97
|
/**
|
|
82
98
|
* What to turn now, for an arrow: after a slip the first undo move; half way
|
|
83
99
|
* through a half turn the remaining quarter (the way it was started); else
|
|
@@ -85,6 +101,10 @@ export declare class SequenceTracker {
|
|
|
85
101
|
* middle one — placed where it is on the cube as now held. Null when complete.
|
|
86
102
|
*/
|
|
87
103
|
get nextTurn(): NextTurn | null;
|
|
88
|
-
/**
|
|
104
|
+
/**
|
|
105
|
+
* Where the current state is on the path, if anywhere: after `done` steps —
|
|
106
|
+
* or inside an axis group, each of its faces some way to its target (a
|
|
107
|
+
* half turn: any part, either way; a quarter: not yet or done).
|
|
108
|
+
*/
|
|
89
109
|
private locate;
|
|
90
110
|
}
|
package/core/sequence.js
CHANGED
|
@@ -5,13 +5,15 @@
|
|
|
5
5
|
* It matches CUBE STATES, not move strings, so everything that reaches the
|
|
6
6
|
* same state counts:
|
|
7
7
|
* - a half turn done as two quarter turns, either way (half done = `partial`);
|
|
8
|
-
* -
|
|
8
|
+
* - moves on one axis in any order, even interleaved (R2 L2 done as
|
|
9
|
+
* L R L R, or L2 R2): they commute — each face just has to get there;
|
|
9
10
|
* - an algorithm written with rotations, slices and wide moves (converted
|
|
10
11
|
* with toFaceTurns: what the smart cube will actually report).
|
|
11
12
|
* Off the path, `undo` is the way back to the last matching point (the
|
|
12
13
|
* slip, simplified: R R' cancels) and `needsReset` says it's too long.
|
|
13
14
|
*/
|
|
14
15
|
import { turnArrow } from "./arrows.js";
|
|
16
|
+
import { amountQuarters } from "./moves.js";
|
|
15
17
|
import { invert, parseAlg, simplify } from "./notation.js";
|
|
16
18
|
import { OrientationTracker } from "./physical.js";
|
|
17
19
|
import { applyMove, statesEqual } from "./state.js";
|
|
@@ -22,8 +24,11 @@ export class SequenceTracker {
|
|
|
22
24
|
frames;
|
|
23
25
|
path; // state after k steps
|
|
24
26
|
cur;
|
|
27
|
+
groups = [];
|
|
28
|
+
groupOf = []; // step index → its group
|
|
25
29
|
anchor = 0; // last matching step index
|
|
26
|
-
|
|
30
|
+
/** At the anchor, inside an axis group: how far each of its faces got (quarters). */
|
|
31
|
+
anchorGroup = null;
|
|
27
32
|
offPath = []; // moves since the last matching point
|
|
28
33
|
maxCorrection;
|
|
29
34
|
constructor(target, start, options = {}) {
|
|
@@ -39,6 +44,22 @@ export class SequenceTracker {
|
|
|
39
44
|
for (const s of this.steps)
|
|
40
45
|
this.path.push(applyMove(this.path[this.path.length - 1], s.move));
|
|
41
46
|
this.cur = new Uint8Array(start);
|
|
47
|
+
for (let i = 0; i < this.steps.length;) {
|
|
48
|
+
let j = i + 1;
|
|
49
|
+
while (j < this.steps.length && sameAxis(this.steps[i].move, this.steps[j].move))
|
|
50
|
+
j++;
|
|
51
|
+
const group = { start: i, end: j, faces: [], target: [] };
|
|
52
|
+
for (let k = i; k < j; k++) {
|
|
53
|
+
const { family, amount } = this.steps[k].move;
|
|
54
|
+
let f = group.faces.indexOf(family);
|
|
55
|
+
if (f < 0)
|
|
56
|
+
f = group.faces.push(family) - 1;
|
|
57
|
+
group.target[f] = ((group.target[f] ?? 0) + amountQuarters(amount)) % 4;
|
|
58
|
+
this.groupOf[k] = this.groups.length;
|
|
59
|
+
}
|
|
60
|
+
this.groups.push(group);
|
|
61
|
+
i = j;
|
|
62
|
+
}
|
|
42
63
|
}
|
|
43
64
|
get state() {
|
|
44
65
|
return this.cur;
|
|
@@ -49,7 +70,7 @@ export class SequenceTracker {
|
|
|
49
70
|
const on = this.locate();
|
|
50
71
|
if (on) {
|
|
51
72
|
this.anchor = on.done;
|
|
52
|
-
this.
|
|
73
|
+
this.anchorGroup = on.group;
|
|
53
74
|
this.offPath = [];
|
|
54
75
|
}
|
|
55
76
|
else {
|
|
@@ -61,24 +82,46 @@ export class SequenceTracker {
|
|
|
61
82
|
const total = this.steps.length;
|
|
62
83
|
const undo = this.offPath.length ? invert(simplify(this.offPath)) : [];
|
|
63
84
|
const done = this.anchor;
|
|
64
|
-
const partial = this.
|
|
85
|
+
const partial = this.anchorGroup !== null && !this.offPath.length;
|
|
65
86
|
const next = done < total ? this.steps[done].token : this.written.length;
|
|
66
|
-
const
|
|
67
|
-
const
|
|
87
|
+
const ww = this.wrongWayStep(undo);
|
|
88
|
+
const status = (i) => (this.offPath.length ? (i < done ? "done" : "todo") : this.stepStatus(i));
|
|
68
89
|
const tokens = this.written.map((_, t) => {
|
|
69
90
|
const idx = this.steps.map((s, i) => (s.token === t ? i : -1)).filter((i) => i >= 0);
|
|
70
91
|
if (!idx.length)
|
|
71
92
|
return t < next ? "done" : "todo"; // a rotation: done once the cube gets past it
|
|
72
|
-
const
|
|
73
|
-
if (
|
|
93
|
+
const st = idx.map(status);
|
|
94
|
+
if (st.every((x) => x === "done"))
|
|
74
95
|
return "done";
|
|
75
|
-
if (
|
|
76
|
-
return "partial"; // M half done, or half of an R2
|
|
77
|
-
if (first === done && wrongWay)
|
|
96
|
+
if (ww !== null && idx.includes(ww))
|
|
78
97
|
return "wrong-way";
|
|
79
|
-
|
|
98
|
+
if (st.some((x) => x !== "todo"))
|
|
99
|
+
return "partial"; // half of an R2, R2 L2 started either way, M half done
|
|
100
|
+
return idx[0] === done ? "current" : "todo";
|
|
80
101
|
});
|
|
81
|
-
return { done, total, partial, tokens, undo, needsReset: undo.length > this.maxCorrection, wrongWay, complete: done === total && !this.offPath.length };
|
|
102
|
+
return { done, total, partial, tokens, undo, needsReset: undo.length > this.maxCorrection, wrongWay: ww !== null, complete: done === total && !this.offPath.length };
|
|
103
|
+
}
|
|
104
|
+
/** A step at the anchor: done, part way (half of a half turn), or not started. */
|
|
105
|
+
stepStatus(i) {
|
|
106
|
+
const g = this.anchorGroup;
|
|
107
|
+
if (g && i >= g.group.start && i < g.group.end) {
|
|
108
|
+
const f = g.group.faces.indexOf(this.steps[i].move.family);
|
|
109
|
+
return g.got[f] === g.group.target[f] ? "done" : g.got[f] === 0 ? "todo" : "partial";
|
|
110
|
+
}
|
|
111
|
+
return i < this.anchor ? "done" : "todo";
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The only slip is a face due (the next step, or one on its axis still to
|
|
115
|
+
* do) turned the wrong way or too far — the step it belongs to, else null.
|
|
116
|
+
*/
|
|
117
|
+
wrongWayStep(undo) {
|
|
118
|
+
if (undo.length !== 1 || this.anchor >= this.steps.length)
|
|
119
|
+
return null;
|
|
120
|
+
const end = this.groups[this.groupOf[this.anchor]].end;
|
|
121
|
+
for (let i = this.anchor; i < end; i++)
|
|
122
|
+
if (this.steps[i].move.family === undo[0].family && this.stepStatus(i) !== "done")
|
|
123
|
+
return i;
|
|
124
|
+
return null;
|
|
82
125
|
}
|
|
83
126
|
/**
|
|
84
127
|
* What to turn now, for an arrow: after a slip the first undo move; half way
|
|
@@ -90,17 +133,18 @@ export class SequenceTracker {
|
|
|
90
133
|
const p = this.progress;
|
|
91
134
|
if (p.complete)
|
|
92
135
|
return null;
|
|
93
|
-
|
|
136
|
+
const ww = this.wrongWayStep(p.undo);
|
|
137
|
+
if (ww !== null) {
|
|
94
138
|
// The face due went the wrong way (or too far): the turn to where it should be —
|
|
95
139
|
// back and on in one go (U instead of U': a U2 the way U' goes).
|
|
96
|
-
const expected = this.steps[
|
|
140
|
+
const expected = this.steps[ww].move;
|
|
97
141
|
const back = p.undo[0];
|
|
98
142
|
const signed = (m) => (m.amount === 2 ? 2 * Math.sign(m.written ?? 2) : m.amount);
|
|
99
143
|
let q = signed(back) + (expected.amount === 2 ? 2 * (Math.sign(signed(back)) || 1) : expected.amount);
|
|
100
144
|
q = ((q % 4) + 4) % 4;
|
|
101
145
|
const amount = (q === 3 ? -1 : q);
|
|
102
146
|
const fix = amount === 2 ? { family: expected.family, amount: 2, written: 2 * (Math.sign(signed(expected)) || 1) } : { family: expected.family, amount };
|
|
103
|
-
return { kind: "wrong-way", arrows: [turnArrow(fix)], token: this.steps[
|
|
147
|
+
return { kind: "wrong-way", arrows: [turnArrow(fix)], token: this.steps[ww].token };
|
|
104
148
|
}
|
|
105
149
|
if (p.undo.length) {
|
|
106
150
|
return { kind: "undo", arrows: [turnArrow(p.undo[0])], token: null };
|
|
@@ -110,15 +154,18 @@ export class SequenceTracker {
|
|
|
110
154
|
if (!step)
|
|
111
155
|
return null;
|
|
112
156
|
const next = (moves) => ({ kind: "next", arrows: moves.map((m) => turnArrow(m)), token: step.token });
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
157
|
+
const g = this.anchorGroup;
|
|
158
|
+
if (g) {
|
|
159
|
+
// Half turns started (R2 L2: one or both half done): the rest of each, the way it was started.
|
|
160
|
+
const halves = g.group.faces.flatMap((family, f) => {
|
|
161
|
+
const got = g.got[f];
|
|
162
|
+
return got !== 0 && got !== g.group.target[f] ? [{ family, amount: got === 1 ? 1 : -1 }] : [];
|
|
163
|
+
});
|
|
164
|
+
if (halves.length) {
|
|
165
|
+
const first = this.steps.findIndex((s, i) => i >= g.group.start && s.move.family === halves[0].family);
|
|
166
|
+
return { kind: "finish", arrows: halves.map((m) => turnArrow(m)), token: this.steps[first].token };
|
|
120
167
|
}
|
|
121
|
-
|
|
168
|
+
// Else a face of the axis went first (R L done as L R): the next one left, as written.
|
|
122
169
|
}
|
|
123
170
|
const first = this.steps.findIndex((s) => s.token === step.token);
|
|
124
171
|
if (first < k) {
|
|
@@ -127,31 +174,45 @@ export class SequenceTracker {
|
|
|
127
174
|
}
|
|
128
175
|
return { kind: "next", arrows: [turnArrow(this.written[step.token], this.frames[step.token])], token: step.token };
|
|
129
176
|
}
|
|
130
|
-
/**
|
|
177
|
+
/**
|
|
178
|
+
* Where the current state is on the path, if anywhere: after `done` steps —
|
|
179
|
+
* or inside an axis group, each of its faces some way to its target (a
|
|
180
|
+
* half turn: any part, either way; a quarter: not yet or done).
|
|
181
|
+
*/
|
|
131
182
|
locate() {
|
|
132
183
|
for (let k = this.path.length - 1; k >= 0; k--)
|
|
133
184
|
if (statesEqual(this.path[k], this.cur))
|
|
134
|
-
return { done: k,
|
|
135
|
-
for (let
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
185
|
+
return { done: k, group: null };
|
|
186
|
+
for (let gi = this.groups.length - 1; gi >= 0; gi--) {
|
|
187
|
+
const group = this.groups[gi];
|
|
188
|
+
const options = group.target.map((t) => (t === 2 ? [0, 1, 2, 3] : t === 0 ? [0] : [0, t]));
|
|
189
|
+
for (const got of combinations(options)) {
|
|
190
|
+
if (got.every((q) => q === 0) || got.every((q, f) => q === group.target[f]))
|
|
191
|
+
continue; // on the path: found above
|
|
192
|
+
let s = this.path[group.start];
|
|
193
|
+
got.forEach((q, f) => {
|
|
194
|
+
if (q)
|
|
195
|
+
s = applyMove(s, { family: group.faces[f], amount: q === 3 ? -1 : q });
|
|
196
|
+
});
|
|
197
|
+
if (!statesEqual(s, this.cur))
|
|
198
|
+
continue;
|
|
199
|
+
const at = { group, got };
|
|
200
|
+
let done = group.start;
|
|
201
|
+
while (done < group.end) {
|
|
202
|
+
const f = group.faces.indexOf(this.steps[done].move.family);
|
|
203
|
+
if (got[f] !== group.target[f])
|
|
204
|
+
break;
|
|
205
|
+
done++;
|
|
142
206
|
}
|
|
143
|
-
|
|
144
|
-
// The next step on the opposite face done first (R L done as L R): count as progress on k.
|
|
145
|
-
const n = this.steps[k + 1];
|
|
146
|
-
if (n && opposite(s, n.move)) {
|
|
147
|
-
if (statesEqual(applyMove(this.path[k], n.move), this.cur))
|
|
148
|
-
return { done: k, partial: true };
|
|
207
|
+
return { done, group: at };
|
|
149
208
|
}
|
|
150
209
|
}
|
|
151
210
|
return null;
|
|
152
211
|
}
|
|
153
212
|
}
|
|
154
213
|
const AXIS = { U: 1, D: 1, R: 0, L: 0, F: 2, B: 2 };
|
|
155
|
-
|
|
156
|
-
|
|
214
|
+
const sameAxis = (a, b) => AXIS[a.family] !== undefined && AXIS[a.family] === AXIS[b.family];
|
|
215
|
+
/** Every pick of one value from each list. */
|
|
216
|
+
function combinations(options) {
|
|
217
|
+
return options.reduce((acc, xs) => acc.flatMap((a) => xs.map((x) => [...a, x])), [[]]);
|
|
157
218
|
}
|
package/element/sequence.d.ts
CHANGED
|
@@ -25,7 +25,9 @@
|
|
|
25
25
|
* or the `player` property), the 3D cube shows the next turn — one arrow per
|
|
26
26
|
* turning layer (two for a wide r), as many heads as quarter turns, in
|
|
27
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
|
|
28
|
+
* --cc-arrow-wrong-way when the right face went the wrong way; the rest of
|
|
29
|
+
* a half turn already started (R2 L2 half done, in any order) in
|
|
30
|
+
* --cc-arrow-finish.
|
|
29
31
|
* `arrow-shape="circle"` for arcs instead of ribbons along the faces.
|
|
30
32
|
*
|
|
31
33
|
* After a slip the moves give way to the way back: the undo moves in their
|
|
@@ -65,7 +67,7 @@ export interface SequenceMessages {
|
|
|
65
67
|
reset: string;
|
|
66
68
|
complete: string;
|
|
67
69
|
}
|
|
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";
|
|
70
|
+
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-arrow-finish: #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
71
|
export declare abstract class CubeSequenceElement extends ElementBase {
|
|
70
72
|
protected readonly root: ShadowRoot;
|
|
71
73
|
protected source: MoveSource | null;
|
package/element/sequence.js
CHANGED
|
@@ -25,7 +25,9 @@
|
|
|
25
25
|
* or the `player` property), the 3D cube shows the next turn — one arrow per
|
|
26
26
|
* turning layer (two for a wide r), as many heads as quarter turns, in
|
|
27
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
|
|
28
|
+
* --cc-arrow-wrong-way when the right face went the wrong way; the rest of
|
|
29
|
+
* a half turn already started (R2 L2 half done, in any order) in
|
|
30
|
+
* --cc-arrow-finish.
|
|
29
31
|
* `arrow-shape="circle"` for arcs instead of ribbons along the faces.
|
|
30
32
|
*
|
|
31
33
|
* After a slip the moves give way to the way back: the undo moves in their
|
|
@@ -53,6 +55,7 @@ export const SEQUENCE_STYLES = /* css */ `
|
|
|
53
55
|
--cc-arrow: #2f8bff;
|
|
54
56
|
--cc-arrow-undo: #ff4545;
|
|
55
57
|
--cc-arrow-wrong-way: #ff9a1f;
|
|
58
|
+
--cc-arrow-finish: #ff9a1f;
|
|
56
59
|
--cc-accent: #4f8cff;
|
|
57
60
|
--cc-control-bg: color-mix(in srgb, currentColor 8%, transparent);
|
|
58
61
|
--cc-control-bg-hover: color-mix(in srgb, currentColor 15%, transparent);
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.12",
|
|
7
7
|
"description": "A 3×3×3 cube library for the web, built around smart (Bluetooth) cubes: state, notation, methods, solvers and scrambles, a three.js renderer with skins, pictures, web components, React bindings.",
|
|
8
8
|
"license": "MPL-2.0",
|
|
9
9
|
"author": "Tomasz Wodzikowski <wodziszczakow@gmail.com>",
|