@vue-skuilder/courseware 0.1.33 → 0.1.35
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/dist/CourseWare-BTFRjgBR.js +305 -0
- package/dist/CourseWare-BTFRjgBR.js.map +1 -0
- package/dist/CourseWare-DSeyTAtH.cjs +2 -0
- package/dist/CourseWare-DSeyTAtH.cjs.map +1 -0
- package/dist/assets/CourseWare.css +1 -0
- package/dist/assets/chess.css +1 -0
- package/dist/assets/index.css +1 -1
- package/dist/assets/math.css +1 -0
- package/dist/assets/piano.css +1 -0
- package/dist/assets/sightsing.css +1 -0
- package/dist/assets/typing.css +1 -0
- package/dist/backend.cjs.js +1 -1
- package/dist/backend.cjs.js.map +1 -1
- package/dist/backend.mjs +4 -3
- package/dist/backend.mjs.map +1 -1
- package/dist/chess/components/ChessBoard.vue.d.ts.map +1 -1
- package/dist/chess/composables/useChessgroundBounds.d.ts +24 -0
- package/dist/chess/composables/useChessgroundBounds.d.ts.map +1 -0
- package/dist/chess/questions/puzzle/puzzle.vue.d.ts.map +1 -1
- package/dist/chess-BbHATAzk.js +2949 -0
- package/dist/chess-BbHATAzk.js.map +1 -0
- package/dist/chess-BsgVZARV.cjs +7 -0
- package/dist/chess-BsgVZARV.cjs.map +1 -0
- package/dist/chess-C_2RQEuq.cjs +1 -0
- package/dist/chess-X1bmWmh3.js +3 -0
- package/dist/french-Bxet7hJm.cjs +2 -0
- package/dist/french-Bxet7hJm.cjs.map +1 -0
- package/dist/french-Dk7YG8Td.js +140 -0
- package/dist/french-Dk7YG8Td.js.map +1 -0
- package/dist/french-evUMlbbq.js +3 -0
- package/dist/french-x9P8RRn8.cjs +1 -0
- package/dist/index.cjs.js +2 -170
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +172 -62021
- package/dist/index.mjs.map +1 -1
- package/dist/math-B1oDAkOy.cjs +34 -0
- package/dist/math-B1oDAkOy.cjs.map +1 -0
- package/dist/math-B4HbgYf6.js +13391 -0
- package/dist/math-B4HbgYf6.js.map +1 -0
- package/dist/math-BWlIa6-l.cjs +1 -0
- package/dist/math-DYni7rRl.js +3 -0
- package/dist/piano-BN5Btq91.js +3 -0
- package/dist/piano-Btvkzjfz.cjs +1 -0
- package/dist/piano-CefQUc72.cjs +2 -0
- package/dist/piano-CefQUc72.cjs.map +1 -0
- package/dist/piano-DF1g6yaX.js +3603 -0
- package/dist/piano-DF1g6yaX.js.map +1 -0
- package/dist/pitch-C-lh9ezH.cjs +2 -0
- package/dist/pitch-C-lh9ezH.cjs.map +1 -0
- package/dist/pitch-CgGJFkZ1.js +106 -0
- package/dist/pitch-CgGJFkZ1.js.map +1 -0
- package/dist/pitch-Dn0iNqiS.js +3 -0
- package/dist/pitch-DsopN5IR.cjs +1 -0
- package/dist/{shapes-nszfsx3o.js → shapes-BQUVJdp5.js} +3 -21
- package/dist/shapes-BQUVJdp5.js.map +1 -0
- package/dist/shapes-C_-mvrCc.cjs +2 -0
- package/dist/shapes-C_-mvrCc.cjs.map +1 -0
- package/dist/shapes-DRq8J94A.cjs +2 -0
- package/dist/shapes-DRq8J94A.cjs.map +1 -0
- package/dist/shapes-ICeg46lr.js +25 -0
- package/dist/shapes-ICeg46lr.js.map +1 -0
- package/dist/sightsing-BFQ7HRig.js +41061 -0
- package/dist/sightsing-BFQ7HRig.js.map +1 -0
- package/dist/sightsing-CJX3k0Fd.js +3 -0
- package/dist/sightsing-Cr_SJ5Vc.cjs +133 -0
- package/dist/sightsing-Cr_SJ5Vc.cjs.map +1 -0
- package/dist/sightsing-CtagQ0FJ.cjs +1 -0
- package/dist/typing-BevtfWlp.js +184 -0
- package/dist/typing-BevtfWlp.js.map +1 -0
- package/dist/typing-CBPjtwrg.cjs +1 -0
- package/dist/typing-CK-glKhm.cjs +2 -0
- package/dist/typing-CK-glKhm.cjs.map +1 -0
- package/dist/typing-k-ojjO7G.js +3 -0
- package/dist/word-work-BOnRlZgd.js +108 -0
- package/dist/word-work-BOnRlZgd.js.map +1 -0
- package/dist/word-work-C5MTSapy.cjs +1 -0
- package/dist/word-work-Dd6tIKrt.js +3 -0
- package/dist/word-work-fNbCLNuN.cjs +2 -0
- package/dist/word-work-fNbCLNuN.cjs.map +1 -0
- package/package.json +6 -5
- package/dist/shapes-DJ-ujuDr.cjs +0 -2
- package/dist/shapes-DJ-ujuDr.cjs.map +0 -1
- package/dist/shapes-nszfsx3o.js.map +0 -1
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { r as e, t } from "./shapes-BQUVJdp5.js";
|
|
2
|
+
import { Fragment as n, computed as r, createBlock as i, createCommentVNode as a, createElementBlock as o, createElementVNode as s, createTextVNode as c, createVNode as l, defineAsyncComponent as u, defineComponent as d, onMounted as f, onUnmounted as p, openBlock as m, ref as h, renderList as g, resolveComponent as _, toDisplayString as v, watch as y, withCtx as b } from "vue";
|
|
3
|
+
import { AudioAutoPlayer as x, Question as S, RadioMultipleChoice as C, containsComponent as w, isInlineComponent as T, splitByDelimiters as E, useQuestionView as D, useViewable as O } from "@vue-skuilder/common-ui";
|
|
4
|
+
import "@vue-skuilder/common";
|
|
5
|
+
//#region src/math/utility/index.ts
|
|
6
|
+
function randomInt(e, t) {
|
|
7
|
+
return Math.floor(Math.random() * (t - e + 1)) + e;
|
|
8
|
+
}
|
|
9
|
+
function shuffle(e) {
|
|
10
|
+
let t = [...e];
|
|
11
|
+
for (let e = t.length - 1; e > 0; e--) {
|
|
12
|
+
let n = Math.floor(Math.random() * (e + 1));
|
|
13
|
+
[t[e], t[n]] = [t[n], t[e]];
|
|
14
|
+
}
|
|
15
|
+
return t;
|
|
16
|
+
}
|
|
17
|
+
function cos(e) {
|
|
18
|
+
return Math.cos(toRadians(e));
|
|
19
|
+
}
|
|
20
|
+
function sin(e) {
|
|
21
|
+
return Math.sin(toRadians(e));
|
|
22
|
+
}
|
|
23
|
+
function toRadians(e) {
|
|
24
|
+
return e / 360 * 2 * Math.PI;
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/default/questions/fillIn/blanksCorrection.ts
|
|
28
|
+
function gradeSpellingAttempt(e, t) {
|
|
29
|
+
let n = Array(t.length).fill("_"), r = e.split("");
|
|
30
|
+
console.log(e, t), console.log("Attempt char codes:", e.split("").map((e) => e.charCodeAt(0))), console.log("Answer char codes:", t.split("").map((e) => e.charCodeAt(0)));
|
|
31
|
+
for (let e = 0; e < t.length; e++) e < r.length && r[e] === t[e] && (n[e] = r[e], r[e] = "");
|
|
32
|
+
for (let e = t.length - 1; e >= 0; e--) if (n[e] === "_") {
|
|
33
|
+
let i = r.findIndex((n) => n === t[e]);
|
|
34
|
+
i !== -1 && (n[e] = t[e], r[i] = "");
|
|
35
|
+
}
|
|
36
|
+
return console.log(n.join(" ")), n.join(" ");
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/default/questions/fillIn/fillIn.vue?vue&type=script&lang.ts
|
|
40
|
+
var k = d({
|
|
41
|
+
name: "FillInView",
|
|
42
|
+
components: {
|
|
43
|
+
MarkdownRenderer: u(() => import("@vue-skuilder/common-ui").then((e) => e.MarkdownRenderer)),
|
|
44
|
+
RadioMultipleChoice: C,
|
|
45
|
+
AudioAutoPlayer: x
|
|
46
|
+
},
|
|
47
|
+
props: {
|
|
48
|
+
data: {
|
|
49
|
+
type: Array,
|
|
50
|
+
required: !0
|
|
51
|
+
},
|
|
52
|
+
modifyDifficulty: {
|
|
53
|
+
type: Number,
|
|
54
|
+
required: !1,
|
|
55
|
+
default: 0
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
setup(e, { emit: t }) {
|
|
59
|
+
let n = O(e, t, "FillInView"), i = D(n);
|
|
60
|
+
i.question.value = new I(e.data);
|
|
61
|
+
let a = h(), o = r(() => {
|
|
62
|
+
if (!i.question.value) throw n.logger.error("Question not initialized"), Error("Question not initialized");
|
|
63
|
+
return i.question.value;
|
|
64
|
+
});
|
|
65
|
+
f(() => {
|
|
66
|
+
try {
|
|
67
|
+
i.question.value || (i.question.value = new I(e.data)), i.question.value.options && (a.value = shuffle(getTruncatedList()));
|
|
68
|
+
} catch (e) {
|
|
69
|
+
n.logger.error("Failed to initialize question:", e);
|
|
70
|
+
}
|
|
71
|
+
}), p(() => {
|
|
72
|
+
i.question.value = void 0;
|
|
73
|
+
});
|
|
74
|
+
let s = r(() => a.value ?? []), c = r(() => !!e.data[0]["image-1"]), l = r(() => {
|
|
75
|
+
if (!c.value) return [""];
|
|
76
|
+
let t = [], n = 1;
|
|
77
|
+
for (; e.data[0][`image-${n}`];) t.push(URL.createObjectURL(e.data[0][`image-${n}`])), n++;
|
|
78
|
+
return t;
|
|
79
|
+
}), u = r(() => !!e.data[0]["audio-1"]), d = r(() => o.value?.mdText || ""), m = r(() => {
|
|
80
|
+
if (!u.value) return [""];
|
|
81
|
+
let t = [], n = 1;
|
|
82
|
+
for (; e.data[0][`audio-${n}`];) t.push(URL.createObjectURL(e.data[0][`audio-${n}`])), n++;
|
|
83
|
+
return t;
|
|
84
|
+
}), g = r(() => {
|
|
85
|
+
if (o.value.answers) return o.value.answers[Math.floor(o.value.answers.length * Math.random())];
|
|
86
|
+
throw Error("No answers provided");
|
|
87
|
+
}), _ = r(() => {
|
|
88
|
+
let e = g.value;
|
|
89
|
+
if (console.log(`Prior answers: ${i.priorAnswers.value}`), e && i.priorAnswers.value[0]?.[0] && i.priorAnswers.value[0][1] === "UserInputString") return gradeSpellingAttempt(i.priorAnswers.value[0][0], e);
|
|
90
|
+
if (console.log("found no UserInputString"), g.value) {
|
|
91
|
+
let e = "";
|
|
92
|
+
for (let t = 0; t < g.value.length; t++) e += "_ ";
|
|
93
|
+
return e;
|
|
94
|
+
}
|
|
95
|
+
return "";
|
|
96
|
+
}), v = r(() => !!(o.value.answers && o.value.answers.length > 0)), getTruncatedList = () => {
|
|
97
|
+
if (!o.value?.options) return;
|
|
98
|
+
if (o.value.options.length <= 6) return o.value.options;
|
|
99
|
+
let t = [];
|
|
100
|
+
t.push(o.value.answers[Math.floor(Math.random() * o.value.answers.length)]);
|
|
101
|
+
let n = shuffle(o.value.options.filter((e) => o.value.answers?.indexOf(e) === -1));
|
|
102
|
+
return e.modifyDifficulty && (console.log(`Modifying difficulty: ${e.modifyDifficulty}`), n = e.modifyDifficulty < -200 ? n.slice(0, 1) : e.modifyDifficulty < -150 ? n.slice(0, 2) : e.modifyDifficulty < -100 ? n.slice(0, 3) : e.modifyDifficulty < -50 ? n.slice(0, 4) : n.slice(0, 5)), t.push(...n.slice(0, 5)), t;
|
|
103
|
+
}, handleNext = () => {
|
|
104
|
+
i.submitAnswer("");
|
|
105
|
+
};
|
|
106
|
+
return y(() => e.data, (e) => {
|
|
107
|
+
try {
|
|
108
|
+
i.question.value = new I(e), i.question.value?.options && (a.value = shuffle(getTruncatedList()));
|
|
109
|
+
} catch (e) {
|
|
110
|
+
n.logger.error("Failed to initialize/update question:", e);
|
|
111
|
+
}
|
|
112
|
+
}, { deep: !0 }), {
|
|
113
|
+
...n,
|
|
114
|
+
...i,
|
|
115
|
+
question: o,
|
|
116
|
+
truncatedOptions: s,
|
|
117
|
+
hasImage: c,
|
|
118
|
+
imageURLs: l,
|
|
119
|
+
hasAudio: u,
|
|
120
|
+
audioURL: m,
|
|
121
|
+
obscuredAnswer: _,
|
|
122
|
+
someAnswer: g,
|
|
123
|
+
isQuestion: v,
|
|
124
|
+
handleNext,
|
|
125
|
+
markdownText: d
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}), A = /* @__PURE__ */ e((e, t) => {
|
|
129
|
+
let n = e.__vccOpts || e;
|
|
130
|
+
for (let [e, r] of t) n[e] = r;
|
|
131
|
+
return n;
|
|
132
|
+
}, "default"), j = { "data-viewable": "FillInView" }, M = ["src"], N = {
|
|
133
|
+
key: 4,
|
|
134
|
+
class: "text-center text-h6"
|
|
135
|
+
}, P = {
|
|
136
|
+
key: 5,
|
|
137
|
+
class: "text-center text-h6"
|
|
138
|
+
};
|
|
139
|
+
function _sfc_render(e, t, r, u, d, f) {
|
|
140
|
+
let p = _("audio-auto-player"), h = _("markdown-renderer"), y = _("radio-multiple-choice"), x = _("v-spacer"), S = _("v-btn"), C = _("v-card-actions");
|
|
141
|
+
return m(), o("div", j, [
|
|
142
|
+
e.hasAudio ? (m(), i(p, {
|
|
143
|
+
key: 0,
|
|
144
|
+
src: e.audioURL
|
|
145
|
+
}, null, 8, ["src"])) : a("", !0),
|
|
146
|
+
e.hasImage ? (m(!0), o(n, { key: 1 }, g(e.imageURLs, (e, t) => (m(), o("img", {
|
|
147
|
+
key: t,
|
|
148
|
+
src: e
|
|
149
|
+
}, null, 8, M))), 128)) : a("", !0),
|
|
150
|
+
e.markdownText ? (m(), i(h, {
|
|
151
|
+
key: 2,
|
|
152
|
+
md: e.markdownText
|
|
153
|
+
}, null, 8, ["md"])) : a("", !0),
|
|
154
|
+
e.question?.options ? (m(), i(y, {
|
|
155
|
+
key: 3,
|
|
156
|
+
"choice-list": e.truncatedOptions
|
|
157
|
+
}, null, 8, ["choice-list"])) : e.priorAttempts == 1 ? (m(), o("div", N, [s("span", null, v(e.obscuredAnswer), 1)])) : e.priorAttempts == 2 ? (m(), o("div", P, [s("span", null, v(e.someAnswer), 1)])) : a("", !0),
|
|
158
|
+
e.isQuestion ? a("", !0) : (m(), i(C, { key: 6 }, {
|
|
159
|
+
default: b(() => [l(x), l(S, {
|
|
160
|
+
color: "primary",
|
|
161
|
+
autofocus: "autofocus",
|
|
162
|
+
onClick: e.handleNext
|
|
163
|
+
}, {
|
|
164
|
+
default: b(() => t[0] ||= [c(" Next ")]),
|
|
165
|
+
_: 1
|
|
166
|
+
}, 8, ["onClick"])]),
|
|
167
|
+
_: 1
|
|
168
|
+
}))
|
|
169
|
+
]);
|
|
170
|
+
}
|
|
171
|
+
var F = /* @__PURE__ */ A(k, [["render", _sfc_render], ["__scopeId", "data-v-f91502f8"]]);
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region src/default/questions/fillIn/index.ts
|
|
174
|
+
function splitText(e, t, n) {
|
|
175
|
+
let r = e.split(t), i = r[0], a = r[1].split(n);
|
|
176
|
+
return {
|
|
177
|
+
left: i,
|
|
178
|
+
middle: a[0],
|
|
179
|
+
right: a[1]
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
var I = class BlanksCard extends S {
|
|
183
|
+
static dataShapes = t;
|
|
184
|
+
static views = [F];
|
|
185
|
+
mdText = "";
|
|
186
|
+
answers = null;
|
|
187
|
+
options = null;
|
|
188
|
+
splitTextToken(e) {
|
|
189
|
+
if (w(e)) {
|
|
190
|
+
let t = splitText(e.text, "{{", "}}"), n = splitText(e.raw, "{{", "}}"), r = [];
|
|
191
|
+
return n.left.length > 0 && r.push({
|
|
192
|
+
type: "text",
|
|
193
|
+
raw: n.left,
|
|
194
|
+
text: t.left
|
|
195
|
+
}), n.middle.length > 0 && r.push({
|
|
196
|
+
type: "text",
|
|
197
|
+
raw: "{{" + n.middle + "}}",
|
|
198
|
+
text: "{{" + t.middle + "}}"
|
|
199
|
+
}), n.right.length > 0 && r.push({
|
|
200
|
+
type: "text",
|
|
201
|
+
raw: n.right,
|
|
202
|
+
text: t.right
|
|
203
|
+
}), r;
|
|
204
|
+
} else return [e];
|
|
205
|
+
}
|
|
206
|
+
optionsFromString(e) {
|
|
207
|
+
if (!e.startsWith("{{") || !e.endsWith("}}")) throw Error(`string ${e} is not fill-in text - must look like "{{someText}}"`);
|
|
208
|
+
e = e.substring(2, e.length - 2);
|
|
209
|
+
let t = e.split("||");
|
|
210
|
+
if (t.length > 1) {
|
|
211
|
+
let e = t[0].split("|").map((e) => e.trim()), n = t[1].split("|").map((e) => e.trim()).filter((t) => !e.includes(t));
|
|
212
|
+
return n.push(e[randomInt(0, e.length - 1)]), {
|
|
213
|
+
answers: e,
|
|
214
|
+
options: shuffle(n)
|
|
215
|
+
};
|
|
216
|
+
} else return {
|
|
217
|
+
answers: [e.trim()],
|
|
218
|
+
options: null
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
constructor(e) {
|
|
222
|
+
super(e), this.mdText = e[0].Input, this.mdText === void 0 && (this.mdText = "");
|
|
223
|
+
let t = E(this.mdText, "{{", "}}"), n = [];
|
|
224
|
+
for (let e = 0; e < t.length; e++) {
|
|
225
|
+
let r = t[e], i = r.trim();
|
|
226
|
+
if (i.startsWith("{{") && i.endsWith("}}")) if (T(i.slice(2, -2).trim())) n.push(r);
|
|
227
|
+
else try {
|
|
228
|
+
let e = this.optionsFromString(r);
|
|
229
|
+
this.answers = e.answers, this.options = e.options, this.options?.length ? n.push("{{ || }}") : n.push("{{ }}");
|
|
230
|
+
} catch {
|
|
231
|
+
n.push(r);
|
|
232
|
+
}
|
|
233
|
+
else n.push(r);
|
|
234
|
+
}
|
|
235
|
+
this.mdText = n.join("");
|
|
236
|
+
}
|
|
237
|
+
isCorrect(e) {
|
|
238
|
+
if (console.log(`answers:${this.answers}\nuser answer: ${JSON.stringify(e)}`), typeof e == "string") {
|
|
239
|
+
if (this.answers) return this.answers.includes(e);
|
|
240
|
+
if (e === "") return !0;
|
|
241
|
+
throw Error("Question has no configured answers!");
|
|
242
|
+
} else if (Array.isArray(e)) {
|
|
243
|
+
if (this.answers) return e.every((e) => this.answers.includes(e));
|
|
244
|
+
throw Error("Question has no configured answers!");
|
|
245
|
+
} else return this.isCorrectRadio(e);
|
|
246
|
+
}
|
|
247
|
+
dataShapes() {
|
|
248
|
+
return BlanksCard.dataShapes;
|
|
249
|
+
}
|
|
250
|
+
views() {
|
|
251
|
+
return BlanksCard.views;
|
|
252
|
+
}
|
|
253
|
+
isCorrectRadio(e) {
|
|
254
|
+
return this.answers ? this.answers.includes(e.choiceList[e.selection]) : !1;
|
|
255
|
+
}
|
|
256
|
+
}, CourseWare = class {
|
|
257
|
+
get questions() {
|
|
258
|
+
return this.questionList;
|
|
259
|
+
}
|
|
260
|
+
get allViews() {
|
|
261
|
+
let e = [];
|
|
262
|
+
return this.questionList.forEach((t) => {
|
|
263
|
+
t.views.forEach((t) => {
|
|
264
|
+
e.push(t);
|
|
265
|
+
});
|
|
266
|
+
}), e;
|
|
267
|
+
}
|
|
268
|
+
get allViewsMap() {
|
|
269
|
+
let e = {};
|
|
270
|
+
return this.allViews.forEach((t) => {
|
|
271
|
+
if (t.name) e[t.name] = t;
|
|
272
|
+
else throw Error("View has no name");
|
|
273
|
+
}), e;
|
|
274
|
+
}
|
|
275
|
+
name;
|
|
276
|
+
questionList;
|
|
277
|
+
constructor(e, t) {
|
|
278
|
+
this.name = e, this.questionList = t, this.questionList = this.questionList.concat(this.getBaseQTypes());
|
|
279
|
+
}
|
|
280
|
+
getQuestion(e) {
|
|
281
|
+
let t = e.replace(/^_/, "").replace(/\d+$/, ""), n = this.questionList.map((e) => e.name), r = this.questionList.find((n) => {
|
|
282
|
+
let r = n.name.replace(/^_/, "").replace(/\d+$/, "");
|
|
283
|
+
return n.name === e || r === t;
|
|
284
|
+
});
|
|
285
|
+
if (r) return r.name !== e && console.debug(`[CourseWare.getQuestion] stripped-exact: "${e}" → "${r.name}" (registered: ${n.join(", ")})`), r;
|
|
286
|
+
let i = this.questionList.filter((n) => {
|
|
287
|
+
let r = n.name.replace(/^_/, "").replace(/\d+$/, "");
|
|
288
|
+
return n.name.includes(t) || e.includes(r);
|
|
289
|
+
});
|
|
290
|
+
if (i.length === 0) {
|
|
291
|
+
console.warn(`[CourseWare.getQuestion] NO MATCH: "${e}" (base: "${t}") not found in [${n.join(", ")}]`);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
i.sort((t, n) => Math.abs(t.name.length - e.length) - Math.abs(n.name.length - e.length));
|
|
295
|
+
let a = i[0];
|
|
296
|
+
return console.warn(`[CourseWare.getQuestion] FUZZY: "${e}" → "${a.name}" (candidates: ${i.map((e) => e.name).join(", ")}; registered: ${n.join(", ")})`), a;
|
|
297
|
+
}
|
|
298
|
+
getBaseQTypes() {
|
|
299
|
+
return [I];
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
//#endregion
|
|
303
|
+
export { cos as a, sin as c, gradeSpellingAttempt as i, I as n, randomInt as o, A as r, shuffle as s, CourseWare as t };
|
|
304
|
+
|
|
305
|
+
//# sourceMappingURL=CourseWare-BTFRjgBR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CourseWare-BTFRjgBR.js","names":[],"sources":["../src/math/utility/index.ts","../src/default/questions/fillIn/blanksCorrection.ts","../src/default/questions/fillIn/fillIn.vue","../src/default/questions/fillIn/fillIn.vue","../src/default/questions/fillIn/index.ts","../src/CourseWare.ts"],"sourcesContent":["import { Validator, Status } from '@vue-skuilder/common';\n\n/**\n * Returns an integer between (inclusive) the two inputs\n * @param min The smallest possible return value\n * @param max The largest possible return value\n */\nexport function randomInt(min: number, max: number): number {\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n\n/**\n * Fisher-Yates shuffle. Returns a new array with elements in random order.\n */\nexport function shuffle<T>(arr: T[]): T[] {\n const result = [...arr];\n for (let i = result.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [result[i], result[j]] = [result[j], result[i]];\n }\n return result;\n}\n\n/**\n * Returns the cosine of an angle measured in degrees\n * @param degrees the angle measure in degrees\n */\nexport function cos(degrees: number) {\n return Math.cos(toRadians(degrees));\n}\n\n/**\n * Returns the sine of an angle measured in degrees\n * @param degrees the angle measure in degrees\n */\nexport function sin(degrees: number) {\n return Math.sin(toRadians(degrees));\n}\n\nfunction toRadians(degrees: number) {\n return (degrees / 360) * 2 * Math.PI;\n}\n\nexport function intValidator(min: number, max: number): Validator {\n return {\n instructions: `This input must be an integer between ${min} and ${max}, inclusive.`,\n test: (value: string) => {\n if (Number.isInteger(Number(value))) {\n return {\n status: Status.ok,\n msg: '',\n };\n } else {\n return {\n status: Status.error,\n msg: `The value ${value} is not an integer.`,\n };\n }\n },\n };\n}","'use strict';\n\nfunction gradeSpellingAttempt(attempt: string, answer: string): string {\n // attempt = attempt.trim();\n const result: string[] = new Array(answer.length).fill('_');\n const attemptChars = attempt.split('');\n\n console.log(attempt, answer);\n\n console.log(\n 'Attempt char codes:',\n attempt.split('').map(c => c.charCodeAt(0))\n );\n console.log(\n 'Answer char codes:',\n answer.split('').map(c => c.charCodeAt(0))\n );\n\n // alert('hello');\n\n // First pass: match characters in correct positions\n for (let i = 0; i < answer.length; i++) {\n if (i < attemptChars.length && attemptChars[i] === answer[i]) {\n result[i] = attemptChars[i];\n attemptChars[i] = '';\n }\n }\n\n // Second pass: match remaining characters from right to left\n for (let i = answer.length - 1; i >= 0; i--) {\n if (result[i] === '_') {\n const charIndex = attemptChars.findIndex(char => char === answer[i]);\n if (charIndex !== -1) {\n result[i] = answer[i];\n attemptChars[charIndex] = '';\n }\n }\n }\n\n console.log(result.join(' '));\n\n return result.join(' ');\n}\n\nexport default gradeSpellingAttempt;\n","<template>\n <div data-viewable=\"FillInView\">\n <audio-auto-player v-if=\"hasAudio\" :src=\"audioURL\" />\n <template v-if=\"hasImage\">\n <img v-for=\"(url, index) in imageURLs\" :key=\"index\" :src=\"url\" />\n </template>\n <!-- Add v-if to prevent undefined markdown -->\n <markdown-renderer v-if=\"markdownText\" :md=\"markdownText\" />\n <radio-multiple-choice v-if=\"question?.options\" :choice-list=\"truncatedOptions\" />\n <div v-else-if=\"priorAttempts == 1\" class=\"text-center text-h6\">\n <span>{{ obscuredAnswer }}</span>\n </div>\n <div v-else-if=\"priorAttempts == 2\" class=\"text-center text-h6\">\n <span>{{ someAnswer }}</span>\n </div>\n <v-card-actions v-if=\"!isQuestion\">\n <v-spacer></v-spacer>\n <v-btn color=\"primary\" autofocus=\"autofocus\" @click=\"handleNext\"> Next </v-btn>\n </v-card-actions>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineAsyncComponent, defineComponent, ref, computed, PropType, onMounted, onUnmounted, watch } from 'vue';\nimport { useViewable, useQuestionView, AudioAutoPlayer, RadioMultipleChoice } from '@vue-skuilder/common-ui';\nimport { shuffle } from '@courseware/math/utility';\nimport { BlanksCard } from './index';\nimport gradeSpellingAttempt from './blanksCorrection';\nimport { ViewData } from '@vue-skuilder/common';\n\nexport default defineComponent({\n name: 'FillInView',\n\n components: {\n // Previously:\n // MarkdownRenderer: defineAsyncComponent(() => import('@courseware/base-course/Components/MarkdownRenderer.vue')),\n //\n // Another option:\n MarkdownRenderer: defineAsyncComponent(() =>\n import('@vue-skuilder/common-ui').then((module) => module.MarkdownRenderer)\n ),\n // MarkdownRenderer,\n RadioMultipleChoice,\n AudioAutoPlayer,\n },\n\n props: {\n data: {\n type: Array as PropType<ViewData[]>,\n required: true,\n },\n modifyDifficulty: {\n type: Number,\n required: false,\n default: 0,\n },\n },\n\n setup(props, { emit }) {\n const viewableUtils = useViewable(props, emit, 'FillInView');\n const questionUtils = useQuestionView<BlanksCard>(viewableUtils);\n\n questionUtils.question.value = new BlanksCard(props.data);\n\n // State\n const shuffledOptions = ref<string[] | undefined>();\n const question = computed(() => {\n if (!questionUtils.question.value) {\n viewableUtils.logger.error('Question not initialized');\n throw new Error('Question not initialized');\n }\n return questionUtils.question.value;\n });\n\n onMounted(() => {\n try {\n if (!questionUtils.question.value) {\n questionUtils.question.value = new BlanksCard(props.data);\n }\n\n if (questionUtils.question.value.options) {\n const truncatedList = getTruncatedList();\n shuffledOptions.value = shuffle(truncatedList);\n }\n } catch (error) {\n viewableUtils.logger.error('Failed to initialize question:', error);\n }\n });\n\n onUnmounted(() => {\n questionUtils.question.value = undefined;\n });\n\n // Computed properties\n const truncatedOptions = computed(() => shuffledOptions.value ?? []);\n\n const hasImage = computed(() => !!props.data[0]['image-1']);\n\n const imageURLs = computed(() => {\n if (!hasImage.value) return [''];\n\n const urls: string[] = [];\n let i = 1;\n while (props.data[0][`image-${i}`]) {\n urls.push(URL.createObjectURL(props.data[0][`image-${i}`] as Blob));\n i++;\n }\n return urls;\n });\n\n const hasAudio = computed(() => !!props.data[0]['audio-1']);\n\n const markdownText = computed(() => {\n return question.value?.mdText || ''; // Provide empty string as fallback\n });\n\n const audioURL = computed(() => {\n if (!hasAudio.value) return [''];\n\n const urls: string[] = [];\n let i = 1;\n while (props.data[0][`audio-${i}`]) {\n urls.push(URL.createObjectURL(props.data[0][`audio-${i}`] as Blob));\n i++;\n }\n return urls;\n });\n\n const someAnswer = computed(() => {\n if (question.value.answers) {\n return question.value.answers[Math.floor(question.value.answers.length * Math.random())];\n } else {\n throw new Error('No answers provided');\n }\n });\n\n const obscuredAnswer = computed(() => {\n const sa = someAnswer.value;\n\n console.log(`Prior answers: ${questionUtils.priorAnswers.value}`);\n\n if (\n sa &&\n questionUtils.priorAnswers.value[0]?.[0] &&\n questionUtils.priorAnswers.value[0][1] === 'UserInputString'\n ) {\n return gradeSpellingAttempt(questionUtils.priorAnswers.value[0][0] as string, sa);\n } else {\n console.log(`found no UserInputString`);\n }\n\n if (someAnswer.value) {\n let obscuredAnswer = '';\n for (let i = 0; i < someAnswer.value.length; i++) {\n obscuredAnswer += '_ ';\n }\n return obscuredAnswer;\n }\n\n return '';\n });\n\n const isQuestion = computed(() => {\n return !!(question.value!.answers && question.value.answers.length > 0);\n });\n\n // Methods\n const getTruncatedList = (): string[] | undefined => {\n if (!question.value?.options) return;\n\n if (question.value.options.length <= 6) {\n return question.value.options;\n }\n\n const truncatedList = [];\n // include one answer\n truncatedList.push(question.value.answers![Math.floor(Math.random() * question.value.answers!.length)]);\n\n // construct a list of all non-answers\n let distractors: string[] = shuffle(\n question.value.options.filter((o: string) => question.value.answers?.indexOf(o) === -1)\n );\n\n if (props.modifyDifficulty) {\n console.log(`Modifying difficulty: ${props.modifyDifficulty}`);\n\n // Adjust number of distractors based on difficulty\n if (props.modifyDifficulty < -200) {\n distractors = distractors.slice(0, 1);\n } else if (props.modifyDifficulty < -150) {\n distractors = distractors.slice(0, 2);\n } else if (props.modifyDifficulty < -100) {\n distractors = distractors.slice(0, 3);\n } else if (props.modifyDifficulty < -50) {\n distractors = distractors.slice(0, 4);\n } else {\n distractors = distractors.slice(0, 5);\n }\n }\n\n truncatedList.push(...distractors.slice(0, 5));\n return truncatedList;\n };\n\n const handleNext = () => {\n questionUtils.submitAnswer('');\n };\n\n /**\n * update the question whenever the data changes\n */\n watch(\n () => props.data,\n (newdata) => {\n try {\n questionUtils.question.value = new BlanksCard(newdata);\n // Update shuffled options whenever question changes\n if (questionUtils.question.value?.options) {\n const truncatedList = getTruncatedList();\n shuffledOptions.value = shuffle(truncatedList);\n }\n } catch (error) {\n viewableUtils.logger.error('Failed to initialize/update question:', error);\n }\n },\n { deep: true }\n );\n\n return {\n ...viewableUtils,\n ...questionUtils,\n question,\n truncatedOptions,\n hasImage,\n imageURLs,\n hasAudio,\n audioURL,\n obscuredAnswer,\n someAnswer,\n isQuestion,\n handleNext,\n markdownText,\n };\n },\n});\n</script>\n\n<style lang=\"css\" scoped>\ncanvas {\n display: block;\n margin-left: auto;\n margin-right: auto;\n padding: 10px;\n}\n\nimg {\n display: block;\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n max-height: 60vh;\n padding: 10px;\n}\n</style>\n","<template>\n <div data-viewable=\"FillInView\">\n <audio-auto-player v-if=\"hasAudio\" :src=\"audioURL\" />\n <template v-if=\"hasImage\">\n <img v-for=\"(url, index) in imageURLs\" :key=\"index\" :src=\"url\" />\n </template>\n <!-- Add v-if to prevent undefined markdown -->\n <markdown-renderer v-if=\"markdownText\" :md=\"markdownText\" />\n <radio-multiple-choice v-if=\"question?.options\" :choice-list=\"truncatedOptions\" />\n <div v-else-if=\"priorAttempts == 1\" class=\"text-center text-h6\">\n <span>{{ obscuredAnswer }}</span>\n </div>\n <div v-else-if=\"priorAttempts == 2\" class=\"text-center text-h6\">\n <span>{{ someAnswer }}</span>\n </div>\n <v-card-actions v-if=\"!isQuestion\">\n <v-spacer></v-spacer>\n <v-btn color=\"primary\" autofocus=\"autofocus\" @click=\"handleNext\"> Next </v-btn>\n </v-card-actions>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineAsyncComponent, defineComponent, ref, computed, PropType, onMounted, onUnmounted, watch } from 'vue';\nimport { useViewable, useQuestionView, AudioAutoPlayer, RadioMultipleChoice } from '@vue-skuilder/common-ui';\nimport { shuffle } from '@courseware/math/utility';\nimport { BlanksCard } from './index';\nimport gradeSpellingAttempt from './blanksCorrection';\nimport { ViewData } from '@vue-skuilder/common';\n\nexport default defineComponent({\n name: 'FillInView',\n\n components: {\n // Previously:\n // MarkdownRenderer: defineAsyncComponent(() => import('@courseware/base-course/Components/MarkdownRenderer.vue')),\n //\n // Another option:\n MarkdownRenderer: defineAsyncComponent(() =>\n import('@vue-skuilder/common-ui').then((module) => module.MarkdownRenderer)\n ),\n // MarkdownRenderer,\n RadioMultipleChoice,\n AudioAutoPlayer,\n },\n\n props: {\n data: {\n type: Array as PropType<ViewData[]>,\n required: true,\n },\n modifyDifficulty: {\n type: Number,\n required: false,\n default: 0,\n },\n },\n\n setup(props, { emit }) {\n const viewableUtils = useViewable(props, emit, 'FillInView');\n const questionUtils = useQuestionView<BlanksCard>(viewableUtils);\n\n questionUtils.question.value = new BlanksCard(props.data);\n\n // State\n const shuffledOptions = ref<string[] | undefined>();\n const question = computed(() => {\n if (!questionUtils.question.value) {\n viewableUtils.logger.error('Question not initialized');\n throw new Error('Question not initialized');\n }\n return questionUtils.question.value;\n });\n\n onMounted(() => {\n try {\n if (!questionUtils.question.value) {\n questionUtils.question.value = new BlanksCard(props.data);\n }\n\n if (questionUtils.question.value.options) {\n const truncatedList = getTruncatedList();\n shuffledOptions.value = shuffle(truncatedList);\n }\n } catch (error) {\n viewableUtils.logger.error('Failed to initialize question:', error);\n }\n });\n\n onUnmounted(() => {\n questionUtils.question.value = undefined;\n });\n\n // Computed properties\n const truncatedOptions = computed(() => shuffledOptions.value ?? []);\n\n const hasImage = computed(() => !!props.data[0]['image-1']);\n\n const imageURLs = computed(() => {\n if (!hasImage.value) return [''];\n\n const urls: string[] = [];\n let i = 1;\n while (props.data[0][`image-${i}`]) {\n urls.push(URL.createObjectURL(props.data[0][`image-${i}`] as Blob));\n i++;\n }\n return urls;\n });\n\n const hasAudio = computed(() => !!props.data[0]['audio-1']);\n\n const markdownText = computed(() => {\n return question.value?.mdText || ''; // Provide empty string as fallback\n });\n\n const audioURL = computed(() => {\n if (!hasAudio.value) return [''];\n\n const urls: string[] = [];\n let i = 1;\n while (props.data[0][`audio-${i}`]) {\n urls.push(URL.createObjectURL(props.data[0][`audio-${i}`] as Blob));\n i++;\n }\n return urls;\n });\n\n const someAnswer = computed(() => {\n if (question.value.answers) {\n return question.value.answers[Math.floor(question.value.answers.length * Math.random())];\n } else {\n throw new Error('No answers provided');\n }\n });\n\n const obscuredAnswer = computed(() => {\n const sa = someAnswer.value;\n\n console.log(`Prior answers: ${questionUtils.priorAnswers.value}`);\n\n if (\n sa &&\n questionUtils.priorAnswers.value[0]?.[0] &&\n questionUtils.priorAnswers.value[0][1] === 'UserInputString'\n ) {\n return gradeSpellingAttempt(questionUtils.priorAnswers.value[0][0] as string, sa);\n } else {\n console.log(`found no UserInputString`);\n }\n\n if (someAnswer.value) {\n let obscuredAnswer = '';\n for (let i = 0; i < someAnswer.value.length; i++) {\n obscuredAnswer += '_ ';\n }\n return obscuredAnswer;\n }\n\n return '';\n });\n\n const isQuestion = computed(() => {\n return !!(question.value!.answers && question.value.answers.length > 0);\n });\n\n // Methods\n const getTruncatedList = (): string[] | undefined => {\n if (!question.value?.options) return;\n\n if (question.value.options.length <= 6) {\n return question.value.options;\n }\n\n const truncatedList = [];\n // include one answer\n truncatedList.push(question.value.answers![Math.floor(Math.random() * question.value.answers!.length)]);\n\n // construct a list of all non-answers\n let distractors: string[] = shuffle(\n question.value.options.filter((o: string) => question.value.answers?.indexOf(o) === -1)\n );\n\n if (props.modifyDifficulty) {\n console.log(`Modifying difficulty: ${props.modifyDifficulty}`);\n\n // Adjust number of distractors based on difficulty\n if (props.modifyDifficulty < -200) {\n distractors = distractors.slice(0, 1);\n } else if (props.modifyDifficulty < -150) {\n distractors = distractors.slice(0, 2);\n } else if (props.modifyDifficulty < -100) {\n distractors = distractors.slice(0, 3);\n } else if (props.modifyDifficulty < -50) {\n distractors = distractors.slice(0, 4);\n } else {\n distractors = distractors.slice(0, 5);\n }\n }\n\n truncatedList.push(...distractors.slice(0, 5));\n return truncatedList;\n };\n\n const handleNext = () => {\n questionUtils.submitAnswer('');\n };\n\n /**\n * update the question whenever the data changes\n */\n watch(\n () => props.data,\n (newdata) => {\n try {\n questionUtils.question.value = new BlanksCard(newdata);\n // Update shuffled options whenever question changes\n if (questionUtils.question.value?.options) {\n const truncatedList = getTruncatedList();\n shuffledOptions.value = shuffle(truncatedList);\n }\n } catch (error) {\n viewableUtils.logger.error('Failed to initialize/update question:', error);\n }\n },\n { deep: true }\n );\n\n return {\n ...viewableUtils,\n ...questionUtils,\n question,\n truncatedOptions,\n hasImage,\n imageURLs,\n hasAudio,\n audioURL,\n obscuredAnswer,\n someAnswer,\n isQuestion,\n handleNext,\n markdownText,\n };\n },\n});\n</script>\n\n<style lang=\"css\" scoped>\ncanvas {\n display: block;\n margin-left: auto;\n margin-right: auto;\n padding: 10px;\n}\n\nimg {\n display: block;\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n max-height: 60vh;\n padding: 10px;\n}\n</style>\n","import { Question, splitByDelimiters, containsComponent, isInlineComponent } from '@vue-skuilder/common-ui';\nimport { Answer, RadioMultipleChoiceAnswer } from '@vue-skuilder/common';\nimport { Validator, ViewData } from '@vue-skuilder/common';\nimport { randomInt, shuffle } from '@courseware/math/utility';\nimport { Status } from '@vue-skuilder/common';\nimport { Tokens } from 'marked';\nimport FillInView from './fillIn.vue';\nimport { BlanksCardDataShapes } from './shapes.js';\n\n// Re-export for backward compatibility\nexport { BlanksCardDataShapes } from './shapes.js';\n\n// @ts-expect-error Legacy validator code kept for reference but not currently used\nconst _val: Validator = {\n test: (input) => {\n console.log(`Testing md input: ${input}`);\n // const sections = parseBlanksMarkdown(input);\n // let blanksCount: number = 0;\n // sections.forEach((section) => {\n // if (section.type === 'blank') {\n // blanksCount++;\n // }\n // });\n\n // if (blanksCount === 1) {\n return {\n status: Status.ok,\n msg: '',\n };\n // } else {\n // return {\n // status: Status.error,\n // msg: 'There must be exactly one blank of the form {{answer}} in the question'\n // };\n // }\n },\n};\n\ntype fillInSectionType = 'text' | 'blank';\n\nexport interface FillInSection {\n type: fillInSectionType;\n text: string;\n}\n\nfunction splitText(\n text: string,\n leftBound: string,\n rightBound: string\n): {\n left: string;\n middle: string;\n right: string;\n} {\n const leftSplit = text.split(leftBound);\n const left = leftSplit[0];\n\n const rightSplit = leftSplit[1].split(rightBound);\n const middle = rightSplit[0];\n const right = rightSplit[1];\n\n return { left, middle, right };\n}\n\nexport class BlanksCard extends Question {\n public static dataShapes = BlanksCardDataShapes;\n public static views = [FillInView];\n public mdText: string = '';\n\n public answers: string[] | null = null;\n public options: string[] | null = null;\n\n public splitTextToken(token: Tokens.Text): Tokens.Text[] {\n if (containsComponent(token)) {\n const text = splitText(token.text, '{{', '}}');\n const raw = splitText(token.raw, '{{', '}}');\n\n const ret: Tokens.Text[] = [];\n\n if (raw.left.length > 0) {\n ret.push({\n type: 'text',\n raw: raw.left,\n text: text.left,\n });\n }\n if (raw.middle.length > 0) {\n ret.push({\n type: 'text',\n raw: '{{' + raw.middle + '}}',\n text: '{{' + text.middle + '}}',\n });\n }\n if (raw.right.length > 0) {\n ret.push({\n type: 'text',\n raw: raw.right,\n text: text.right,\n });\n }\n\n return ret;\n } else {\n return [token];\n }\n }\n\n /**\n * Parses a string to extract answer options and distractors for a fill-in question.\n * The input string must be wrapped in curly braces and can contain multiple answers/distractors separated by | and ||.\n * For example: \"{{answer1|answer2||distractor1|distractor2}}\"\n * @param s The input string containing answers and optional distractors\n * @returns An object containing the parsed answers array and shuffled options array (or null if no distractors)\n * @throws Error if input string is not properly formatted with {{ }}\n */\n private optionsFromString(s: string) {\n if (!s.startsWith('{{') || !s.endsWith('}}')) {\n throw new Error(`string ${s} is not fill-in text - must look like \"{{someText}}\"`);\n }\n s = s.substring(2, s.length - 2);\n const split = s.split('||');\n if (split.length > 1) {\n const answers = split[0].split('|').map((a) => a.trim());\n // remove answers from distractors (makes for easier editing to allow answers in the distractor list)\n const distractors = split[1]\n .split('|')\n .map((d) => d.trim())\n .filter((d) => !answers.includes(d));\n\n const options = distractors;\n options.push(answers[randomInt(0, answers.length - 1)]);\n\n return {\n answers,\n options: shuffle(options),\n };\n } else {\n return {\n answers: [s.trim()],\n options: null,\n };\n }\n }\n\n constructor(data: ViewData[]) {\n super(data);\n this.mdText = data[0].Input as unknown as string;\n if (this.mdText === undefined) {\n this.mdText = '';\n }\n\n const splits = splitByDelimiters(this.mdText, '{{', '}}');\n const recombines = [];\n for (let i = 0; i < splits.length; i++) {\n const split = splits[i];\n const trimmed = split.trim();\n\n // If the split starts/ends with {{ }}, it's a delimited block\n if (trimmed.startsWith('{{') && trimmed.endsWith('}}')) {\n // Extract content between {{ and }}\n const content = trimmed.slice(2, -2).trim();\n\n if (isInlineComponent(content)) {\n // Preserve inline component syntax\n recombines.push(split);\n } else {\n // Try to parse as fillIn blank\n try {\n const parsedOptions = this.optionsFromString(split);\n this.answers = parsedOptions.answers;\n this.options = parsedOptions.options;\n if (this.options?.length) {\n recombines.push('{{ || }}'); // render a multiple-choice blank\n } else {\n recombines.push('{{ }}'); // render a text blank\n }\n } catch {\n // Not valid fillIn syntax, keep original\n recombines.push(split);\n }\n }\n } else {\n // Regular text, not delimited\n recombines.push(split);\n }\n }\n\n this.mdText = recombines.join('');\n }\n\n public isCorrect(answer: Answer) {\n console.log(`answers:${this.answers}\\nuser answer: ${JSON.stringify(answer)}`);\n\n if (typeof answer === 'string') {\n if (this.answers) {\n return this.answers.includes(answer);\n } else {\n if (answer === '') {\n return true;\n } else {\n throw new Error(`Question has no configured answers!`);\n }\n }\n } else if (Array.isArray(answer)) {\n if (this.answers) {\n return answer.every((a) => {\n return this.answers!.includes(a);\n });\n } else {\n throw new Error(`Question has no configured answers!`);\n }\n } else {\n return this.isCorrectRadio(answer as RadioMultipleChoiceAnswer);\n }\n }\n\n public dataShapes() {\n return BlanksCard.dataShapes;\n }\n public views() {\n return BlanksCard.views;\n }\n\n private isCorrectRadio(answer: RadioMultipleChoiceAnswer) {\n if (this.answers) {\n return this.answers.includes(answer.choiceList[answer.selection]);\n } else {\n return false;\n }\n }\n}\n","import { Displayable, ViewComponent } from '@vue-skuilder/common-ui';\nimport { BlanksCard } from './default/questions/fillIn/';\n\nexport class CourseWare {\n public get questions(): Array<typeof Displayable> {\n return this.questionList;\n }\n\n public get allViews(): Array<ViewComponent> {\n const ret = new Array<ViewComponent>();\n\n this.questionList.forEach((question) => {\n question.views.forEach((view) => {\n ret.push(view);\n });\n });\n\n return ret;\n }\n\n /**\n * This function returns the map {[index:string]: string} of display\n * components needed by the CardViewer component\n */\n public get allViewsMap(): { [index: string]: ViewComponent } {\n const ret: { [index: string]: ViewComponent } = {};\n\n this.allViews.forEach((view) => {\n if (view.name) {\n ret[view.name] = view;\n } else {\n throw new Error('View has no name');\n }\n });\n\n return ret;\n }\n public readonly name: string;\n private readonly questionList: Array<typeof Displayable>;\n\n constructor(name: string, questionList: Array<typeof Displayable>) {\n this.name = name;\n this.questionList = questionList;\n\n this.questionList = this.questionList.concat(this.getBaseQTypes());\n }\n\n public getQuestion(name: string): typeof Displayable | undefined {\n const searchBaseName = name.replace(/^_/, '').replace(/\\d+$/, '');\n const registered = this.questionList.map((q) => q.name);\n\n // Pass 1: exact or stripped-exact match (safe, no false positives)\n const exact = this.questionList.find((question) => {\n const questionBaseName = question.name.replace(/^_/, '').replace(/\\d+$/, '');\n return question.name === name || questionBaseName === searchBaseName;\n });\n if (exact) {\n if (exact.name !== name) {\n console.debug(\n `[CourseWare.getQuestion] stripped-exact: \"${name}\" → \"${exact.name}\" (registered: ${registered.join(', ')})`,\n );\n }\n return exact;\n }\n\n // Pass 2: fuzzy includes() fallback for bundler name-mangling resilience.\n // Prefer the shortest-name match to avoid \"GpcIntro\" matching \"DigraphGpcIntro\".\n const fuzzyMatches = this.questionList.filter((question) => {\n const questionBaseName = question.name.replace(/^_/, '').replace(/\\d+$/, '');\n return question.name.includes(searchBaseName) || name.includes(questionBaseName);\n });\n\n if (fuzzyMatches.length === 0) {\n console.warn(\n `[CourseWare.getQuestion] NO MATCH: \"${name}\" (base: \"${searchBaseName}\") not found in [${registered.join(', ')}]`,\n );\n return undefined;\n }\n\n // Among fuzzy matches, prefer the one whose name length is closest to the search name\n // (shortest delta wins). This prevents \"GpcIntro\" from matching \"DigraphGpcIntro\"\n // when \"GpcIntro\" itself is registered.\n fuzzyMatches.sort((a, b) =>\n Math.abs(a.name.length - name.length) - Math.abs(b.name.length - name.length)\n );\n const winner = fuzzyMatches[0];\n console.warn(\n `[CourseWare.getQuestion] FUZZY: \"${name}\" → \"${winner.name}\" (candidates: ${fuzzyMatches.map((q) => q.name).join(', ')}; registered: ${registered.join(', ')})`,\n );\n return winner;\n }\n\n public getBaseQTypes(): Array<typeof Displayable> {\n // #145 todo: return [BasicCard];\n // should: get 'default' course displayable types\n // return defaultCourse.getBaseQTypes();\n return [BlanksCard];\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require(`./shapes-C_-mvrCc.cjs`);let t=require(`vue`),n=require(`@vue-skuilder/common-ui`);require(`@vue-skuilder/common`);function randomInt(e,t){return Math.floor(Math.random()*(t-e+1))+e}function shuffle(e){let t=[...e];for(let e=t.length-1;e>0;e--){let n=Math.floor(Math.random()*(e+1));[t[e],t[n]]=[t[n],t[e]]}return t}function cos(e){return Math.cos(toRadians(e))}function sin(e){return Math.sin(toRadians(e))}function toRadians(e){return e/360*2*Math.PI}function gradeSpellingAttempt(e,t){let n=Array(t.length).fill(`_`),r=e.split(``);console.log(e,t),console.log(`Attempt char codes:`,e.split(``).map(e=>e.charCodeAt(0))),console.log(`Answer char codes:`,t.split(``).map(e=>e.charCodeAt(0)));for(let e=0;e<t.length;e++)e<r.length&&r[e]===t[e]&&(n[e]=r[e],r[e]=``);for(let e=t.length-1;e>=0;e--)if(n[e]===`_`){let i=r.findIndex(n=>n===t[e]);i!==-1&&(n[e]=t[e],r[i]=``)}return console.log(n.join(` `)),n.join(` `)}var r=(0,t.defineComponent)({name:`FillInView`,components:{MarkdownRenderer:(0,t.defineAsyncComponent)(()=>import(`@vue-skuilder/common-ui`).then(e=>e.MarkdownRenderer)),RadioMultipleChoice:n.RadioMultipleChoice,AudioAutoPlayer:n.AudioAutoPlayer},props:{data:{type:Array,required:!0},modifyDifficulty:{type:Number,required:!1,default:0}},setup(e,{emit:r}){let i=(0,n.useViewable)(e,r,`FillInView`),a=(0,n.useQuestionView)(i);a.question.value=new u(e.data);let o=(0,t.ref)(),s=(0,t.computed)(()=>{if(!a.question.value)throw i.logger.error(`Question not initialized`),Error(`Question not initialized`);return a.question.value});(0,t.onMounted)(()=>{try{a.question.value||(a.question.value=new u(e.data)),a.question.value.options&&(o.value=shuffle(getTruncatedList()))}catch(e){i.logger.error(`Failed to initialize question:`,e)}}),(0,t.onUnmounted)(()=>{a.question.value=void 0});let c=(0,t.computed)(()=>o.value??[]),l=(0,t.computed)(()=>!!e.data[0][`image-1`]),d=(0,t.computed)(()=>{if(!l.value)return[``];let t=[],n=1;for(;e.data[0][`image-${n}`];)t.push(URL.createObjectURL(e.data[0][`image-${n}`])),n++;return t}),f=(0,t.computed)(()=>!!e.data[0][`audio-1`]),p=(0,t.computed)(()=>s.value?.mdText||``),m=(0,t.computed)(()=>{if(!f.value)return[``];let t=[],n=1;for(;e.data[0][`audio-${n}`];)t.push(URL.createObjectURL(e.data[0][`audio-${n}`])),n++;return t}),h=(0,t.computed)(()=>{if(s.value.answers)return s.value.answers[Math.floor(s.value.answers.length*Math.random())];throw Error(`No answers provided`)}),g=(0,t.computed)(()=>{let e=h.value;if(console.log(`Prior answers: ${a.priorAnswers.value}`),e&&a.priorAnswers.value[0]?.[0]&&a.priorAnswers.value[0][1]===`UserInputString`)return gradeSpellingAttempt(a.priorAnswers.value[0][0],e);if(console.log(`found no UserInputString`),h.value){let e=``;for(let t=0;t<h.value.length;t++)e+=`_ `;return e}return``}),_=(0,t.computed)(()=>!!(s.value.answers&&s.value.answers.length>0)),getTruncatedList=()=>{if(!s.value?.options)return;if(s.value.options.length<=6)return s.value.options;let t=[];t.push(s.value.answers[Math.floor(Math.random()*s.value.answers.length)]);let n=shuffle(s.value.options.filter(e=>s.value.answers?.indexOf(e)===-1));return e.modifyDifficulty&&(console.log(`Modifying difficulty: ${e.modifyDifficulty}`),n=e.modifyDifficulty<-200?n.slice(0,1):e.modifyDifficulty<-150?n.slice(0,2):e.modifyDifficulty<-100?n.slice(0,3):e.modifyDifficulty<-50?n.slice(0,4):n.slice(0,5)),t.push(...n.slice(0,5)),t},handleNext=()=>{a.submitAnswer(``)};return(0,t.watch)(()=>e.data,e=>{try{a.question.value=new u(e),a.question.value?.options&&(o.value=shuffle(getTruncatedList()))}catch(e){i.logger.error(`Failed to initialize/update question:`,e)}},{deep:!0}),{...i,...a,question:s,truncatedOptions:c,hasImage:l,imageURLs:d,hasAudio:f,audioURL:m,obscuredAnswer:g,someAnswer:h,isQuestion:_,handleNext,markdownText:p}}}),i=e.r((e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n},`default`),a={"data-viewable":`FillInView`},o=[`src`],s={key:4,class:`text-center text-h6`},c={key:5,class:`text-center text-h6`};function _sfc_render(e,n,r,i,l,u){let d=(0,t.resolveComponent)(`audio-auto-player`),f=(0,t.resolveComponent)(`markdown-renderer`),p=(0,t.resolveComponent)(`radio-multiple-choice`),m=(0,t.resolveComponent)(`v-spacer`),h=(0,t.resolveComponent)(`v-btn`),g=(0,t.resolveComponent)(`v-card-actions`);return(0,t.openBlock)(),(0,t.createElementBlock)(`div`,a,[e.hasAudio?((0,t.openBlock)(),(0,t.createBlock)(d,{key:0,src:e.audioURL},null,8,[`src`])):(0,t.createCommentVNode)(``,!0),e.hasImage?((0,t.openBlock)(!0),(0,t.createElementBlock)(t.Fragment,{key:1},(0,t.renderList)(e.imageURLs,(e,n)=>((0,t.openBlock)(),(0,t.createElementBlock)(`img`,{key:n,src:e},null,8,o))),128)):(0,t.createCommentVNode)(``,!0),e.markdownText?((0,t.openBlock)(),(0,t.createBlock)(f,{key:2,md:e.markdownText},null,8,[`md`])):(0,t.createCommentVNode)(``,!0),e.question?.options?((0,t.openBlock)(),(0,t.createBlock)(p,{key:3,"choice-list":e.truncatedOptions},null,8,[`choice-list`])):e.priorAttempts==1?((0,t.openBlock)(),(0,t.createElementBlock)(`div`,s,[(0,t.createElementVNode)(`span`,null,(0,t.toDisplayString)(e.obscuredAnswer),1)])):e.priorAttempts==2?((0,t.openBlock)(),(0,t.createElementBlock)(`div`,c,[(0,t.createElementVNode)(`span`,null,(0,t.toDisplayString)(e.someAnswer),1)])):(0,t.createCommentVNode)(``,!0),e.isQuestion?(0,t.createCommentVNode)(``,!0):((0,t.openBlock)(),(0,t.createBlock)(g,{key:6},{default:(0,t.withCtx)(()=>[(0,t.createVNode)(m),(0,t.createVNode)(h,{color:`primary`,autofocus:`autofocus`,onClick:e.handleNext},{default:(0,t.withCtx)(()=>n[0]||=[(0,t.createTextVNode)(` Next `)]),_:1},8,[`onClick`])]),_:1}))])}var l=i(r,[[`render`,_sfc_render],[`__scopeId`,`data-v-f91502f8`]]);function splitText(e,t,n){let r=e.split(t),i=r[0],a=r[1].split(n);return{left:i,middle:a[0],right:a[1]}}var u=class BlanksCard extends n.Question{static dataShapes=e.t;static views=[l];mdText=``;answers=null;options=null;splitTextToken(e){if((0,n.containsComponent)(e)){let t=splitText(e.text,`{{`,`}}`),n=splitText(e.raw,`{{`,`}}`),r=[];return n.left.length>0&&r.push({type:`text`,raw:n.left,text:t.left}),n.middle.length>0&&r.push({type:`text`,raw:`{{`+n.middle+`}}`,text:`{{`+t.middle+`}}`}),n.right.length>0&&r.push({type:`text`,raw:n.right,text:t.right}),r}else return[e]}optionsFromString(e){if(!e.startsWith(`{{`)||!e.endsWith(`}}`))throw Error(`string ${e} is not fill-in text - must look like "{{someText}}"`);e=e.substring(2,e.length-2);let t=e.split(`||`);if(t.length>1){let e=t[0].split(`|`).map(e=>e.trim()),n=t[1].split(`|`).map(e=>e.trim()).filter(t=>!e.includes(t));return n.push(e[randomInt(0,e.length-1)]),{answers:e,options:shuffle(n)}}else return{answers:[e.trim()],options:null}}constructor(e){super(e),this.mdText=e[0].Input,this.mdText===void 0&&(this.mdText=``);let t=(0,n.splitByDelimiters)(this.mdText,`{{`,`}}`),r=[];for(let e=0;e<t.length;e++){let i=t[e],a=i.trim();if(a.startsWith(`{{`)&&a.endsWith(`}}`))if((0,n.isInlineComponent)(a.slice(2,-2).trim()))r.push(i);else try{let e=this.optionsFromString(i);this.answers=e.answers,this.options=e.options,this.options?.length?r.push(`{{ || }}`):r.push(`{{ }}`)}catch{r.push(i)}else r.push(i)}this.mdText=r.join(``)}isCorrect(e){if(console.log(`answers:${this.answers}\nuser answer: ${JSON.stringify(e)}`),typeof e==`string`){if(this.answers)return this.answers.includes(e);if(e===``)return!0;throw Error(`Question has no configured answers!`)}else if(Array.isArray(e)){if(this.answers)return e.every(e=>this.answers.includes(e));throw Error(`Question has no configured answers!`)}else return this.isCorrectRadio(e)}dataShapes(){return BlanksCard.dataShapes}views(){return BlanksCard.views}isCorrectRadio(e){return this.answers?this.answers.includes(e.choiceList[e.selection]):!1}},CourseWare=class{get questions(){return this.questionList}get allViews(){let e=[];return this.questionList.forEach(t=>{t.views.forEach(t=>{e.push(t)})}),e}get allViewsMap(){let e={};return this.allViews.forEach(t=>{if(t.name)e[t.name]=t;else throw Error(`View has no name`)}),e}name;questionList;constructor(e,t){this.name=e,this.questionList=t,this.questionList=this.questionList.concat(this.getBaseQTypes())}getQuestion(e){let t=e.replace(/^_/,``).replace(/\d+$/,``),n=this.questionList.map(e=>e.name),r=this.questionList.find(n=>{let r=n.name.replace(/^_/,``).replace(/\d+$/,``);return n.name===e||r===t});if(r)return r.name!==e&&console.debug(`[CourseWare.getQuestion] stripped-exact: "${e}" → "${r.name}" (registered: ${n.join(`, `)})`),r;let i=this.questionList.filter(n=>{let r=n.name.replace(/^_/,``).replace(/\d+$/,``);return n.name.includes(t)||e.includes(r)});if(i.length===0){console.warn(`[CourseWare.getQuestion] NO MATCH: "${e}" (base: "${t}") not found in [${n.join(`, `)}]`);return}i.sort((t,n)=>Math.abs(t.name.length-e.length)-Math.abs(n.name.length-e.length));let a=i[0];return console.warn(`[CourseWare.getQuestion] FUZZY: "${e}" → "${a.name}" (candidates: ${i.map(e=>e.name).join(`, `)}; registered: ${n.join(`, `)})`),a}getBaseQTypes(){return[u]}};Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return cos}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return sin}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return gradeSpellingAttempt}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return randomInt}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return i}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return shuffle}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return CourseWare}});
|
|
2
|
+
//# sourceMappingURL=CourseWare-DSeyTAtH.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CourseWare-DSeyTAtH.cjs","names":[],"sources":["../src/math/utility/index.ts","../src/default/questions/fillIn/blanksCorrection.ts","../src/default/questions/fillIn/fillIn.vue","../src/default/questions/fillIn/fillIn.vue","../src/default/questions/fillIn/index.ts","../src/CourseWare.ts"],"sourcesContent":["import { Validator, Status } from '@vue-skuilder/common';\n\n/**\n * Returns an integer between (inclusive) the two inputs\n * @param min The smallest possible return value\n * @param max The largest possible return value\n */\nexport function randomInt(min: number, max: number): number {\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n\n/**\n * Fisher-Yates shuffle. Returns a new array with elements in random order.\n */\nexport function shuffle<T>(arr: T[]): T[] {\n const result = [...arr];\n for (let i = result.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [result[i], result[j]] = [result[j], result[i]];\n }\n return result;\n}\n\n/**\n * Returns the cosine of an angle measured in degrees\n * @param degrees the angle measure in degrees\n */\nexport function cos(degrees: number) {\n return Math.cos(toRadians(degrees));\n}\n\n/**\n * Returns the sine of an angle measured in degrees\n * @param degrees the angle measure in degrees\n */\nexport function sin(degrees: number) {\n return Math.sin(toRadians(degrees));\n}\n\nfunction toRadians(degrees: number) {\n return (degrees / 360) * 2 * Math.PI;\n}\n\nexport function intValidator(min: number, max: number): Validator {\n return {\n instructions: `This input must be an integer between ${min} and ${max}, inclusive.`,\n test: (value: string) => {\n if (Number.isInteger(Number(value))) {\n return {\n status: Status.ok,\n msg: '',\n };\n } else {\n return {\n status: Status.error,\n msg: `The value ${value} is not an integer.`,\n };\n }\n },\n };\n}","'use strict';\n\nfunction gradeSpellingAttempt(attempt: string, answer: string): string {\n // attempt = attempt.trim();\n const result: string[] = new Array(answer.length).fill('_');\n const attemptChars = attempt.split('');\n\n console.log(attempt, answer);\n\n console.log(\n 'Attempt char codes:',\n attempt.split('').map(c => c.charCodeAt(0))\n );\n console.log(\n 'Answer char codes:',\n answer.split('').map(c => c.charCodeAt(0))\n );\n\n // alert('hello');\n\n // First pass: match characters in correct positions\n for (let i = 0; i < answer.length; i++) {\n if (i < attemptChars.length && attemptChars[i] === answer[i]) {\n result[i] = attemptChars[i];\n attemptChars[i] = '';\n }\n }\n\n // Second pass: match remaining characters from right to left\n for (let i = answer.length - 1; i >= 0; i--) {\n if (result[i] === '_') {\n const charIndex = attemptChars.findIndex(char => char === answer[i]);\n if (charIndex !== -1) {\n result[i] = answer[i];\n attemptChars[charIndex] = '';\n }\n }\n }\n\n console.log(result.join(' '));\n\n return result.join(' ');\n}\n\nexport default gradeSpellingAttempt;\n","<template>\n <div data-viewable=\"FillInView\">\n <audio-auto-player v-if=\"hasAudio\" :src=\"audioURL\" />\n <template v-if=\"hasImage\">\n <img v-for=\"(url, index) in imageURLs\" :key=\"index\" :src=\"url\" />\n </template>\n <!-- Add v-if to prevent undefined markdown -->\n <markdown-renderer v-if=\"markdownText\" :md=\"markdownText\" />\n <radio-multiple-choice v-if=\"question?.options\" :choice-list=\"truncatedOptions\" />\n <div v-else-if=\"priorAttempts == 1\" class=\"text-center text-h6\">\n <span>{{ obscuredAnswer }}</span>\n </div>\n <div v-else-if=\"priorAttempts == 2\" class=\"text-center text-h6\">\n <span>{{ someAnswer }}</span>\n </div>\n <v-card-actions v-if=\"!isQuestion\">\n <v-spacer></v-spacer>\n <v-btn color=\"primary\" autofocus=\"autofocus\" @click=\"handleNext\"> Next </v-btn>\n </v-card-actions>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineAsyncComponent, defineComponent, ref, computed, PropType, onMounted, onUnmounted, watch } from 'vue';\nimport { useViewable, useQuestionView, AudioAutoPlayer, RadioMultipleChoice } from '@vue-skuilder/common-ui';\nimport { shuffle } from '@courseware/math/utility';\nimport { BlanksCard } from './index';\nimport gradeSpellingAttempt from './blanksCorrection';\nimport { ViewData } from '@vue-skuilder/common';\n\nexport default defineComponent({\n name: 'FillInView',\n\n components: {\n // Previously:\n // MarkdownRenderer: defineAsyncComponent(() => import('@courseware/base-course/Components/MarkdownRenderer.vue')),\n //\n // Another option:\n MarkdownRenderer: defineAsyncComponent(() =>\n import('@vue-skuilder/common-ui').then((module) => module.MarkdownRenderer)\n ),\n // MarkdownRenderer,\n RadioMultipleChoice,\n AudioAutoPlayer,\n },\n\n props: {\n data: {\n type: Array as PropType<ViewData[]>,\n required: true,\n },\n modifyDifficulty: {\n type: Number,\n required: false,\n default: 0,\n },\n },\n\n setup(props, { emit }) {\n const viewableUtils = useViewable(props, emit, 'FillInView');\n const questionUtils = useQuestionView<BlanksCard>(viewableUtils);\n\n questionUtils.question.value = new BlanksCard(props.data);\n\n // State\n const shuffledOptions = ref<string[] | undefined>();\n const question = computed(() => {\n if (!questionUtils.question.value) {\n viewableUtils.logger.error('Question not initialized');\n throw new Error('Question not initialized');\n }\n return questionUtils.question.value;\n });\n\n onMounted(() => {\n try {\n if (!questionUtils.question.value) {\n questionUtils.question.value = new BlanksCard(props.data);\n }\n\n if (questionUtils.question.value.options) {\n const truncatedList = getTruncatedList();\n shuffledOptions.value = shuffle(truncatedList);\n }\n } catch (error) {\n viewableUtils.logger.error('Failed to initialize question:', error);\n }\n });\n\n onUnmounted(() => {\n questionUtils.question.value = undefined;\n });\n\n // Computed properties\n const truncatedOptions = computed(() => shuffledOptions.value ?? []);\n\n const hasImage = computed(() => !!props.data[0]['image-1']);\n\n const imageURLs = computed(() => {\n if (!hasImage.value) return [''];\n\n const urls: string[] = [];\n let i = 1;\n while (props.data[0][`image-${i}`]) {\n urls.push(URL.createObjectURL(props.data[0][`image-${i}`] as Blob));\n i++;\n }\n return urls;\n });\n\n const hasAudio = computed(() => !!props.data[0]['audio-1']);\n\n const markdownText = computed(() => {\n return question.value?.mdText || ''; // Provide empty string as fallback\n });\n\n const audioURL = computed(() => {\n if (!hasAudio.value) return [''];\n\n const urls: string[] = [];\n let i = 1;\n while (props.data[0][`audio-${i}`]) {\n urls.push(URL.createObjectURL(props.data[0][`audio-${i}`] as Blob));\n i++;\n }\n return urls;\n });\n\n const someAnswer = computed(() => {\n if (question.value.answers) {\n return question.value.answers[Math.floor(question.value.answers.length * Math.random())];\n } else {\n throw new Error('No answers provided');\n }\n });\n\n const obscuredAnswer = computed(() => {\n const sa = someAnswer.value;\n\n console.log(`Prior answers: ${questionUtils.priorAnswers.value}`);\n\n if (\n sa &&\n questionUtils.priorAnswers.value[0]?.[0] &&\n questionUtils.priorAnswers.value[0][1] === 'UserInputString'\n ) {\n return gradeSpellingAttempt(questionUtils.priorAnswers.value[0][0] as string, sa);\n } else {\n console.log(`found no UserInputString`);\n }\n\n if (someAnswer.value) {\n let obscuredAnswer = '';\n for (let i = 0; i < someAnswer.value.length; i++) {\n obscuredAnswer += '_ ';\n }\n return obscuredAnswer;\n }\n\n return '';\n });\n\n const isQuestion = computed(() => {\n return !!(question.value!.answers && question.value.answers.length > 0);\n });\n\n // Methods\n const getTruncatedList = (): string[] | undefined => {\n if (!question.value?.options) return;\n\n if (question.value.options.length <= 6) {\n return question.value.options;\n }\n\n const truncatedList = [];\n // include one answer\n truncatedList.push(question.value.answers![Math.floor(Math.random() * question.value.answers!.length)]);\n\n // construct a list of all non-answers\n let distractors: string[] = shuffle(\n question.value.options.filter((o: string) => question.value.answers?.indexOf(o) === -1)\n );\n\n if (props.modifyDifficulty) {\n console.log(`Modifying difficulty: ${props.modifyDifficulty}`);\n\n // Adjust number of distractors based on difficulty\n if (props.modifyDifficulty < -200) {\n distractors = distractors.slice(0, 1);\n } else if (props.modifyDifficulty < -150) {\n distractors = distractors.slice(0, 2);\n } else if (props.modifyDifficulty < -100) {\n distractors = distractors.slice(0, 3);\n } else if (props.modifyDifficulty < -50) {\n distractors = distractors.slice(0, 4);\n } else {\n distractors = distractors.slice(0, 5);\n }\n }\n\n truncatedList.push(...distractors.slice(0, 5));\n return truncatedList;\n };\n\n const handleNext = () => {\n questionUtils.submitAnswer('');\n };\n\n /**\n * update the question whenever the data changes\n */\n watch(\n () => props.data,\n (newdata) => {\n try {\n questionUtils.question.value = new BlanksCard(newdata);\n // Update shuffled options whenever question changes\n if (questionUtils.question.value?.options) {\n const truncatedList = getTruncatedList();\n shuffledOptions.value = shuffle(truncatedList);\n }\n } catch (error) {\n viewableUtils.logger.error('Failed to initialize/update question:', error);\n }\n },\n { deep: true }\n );\n\n return {\n ...viewableUtils,\n ...questionUtils,\n question,\n truncatedOptions,\n hasImage,\n imageURLs,\n hasAudio,\n audioURL,\n obscuredAnswer,\n someAnswer,\n isQuestion,\n handleNext,\n markdownText,\n };\n },\n});\n</script>\n\n<style lang=\"css\" scoped>\ncanvas {\n display: block;\n margin-left: auto;\n margin-right: auto;\n padding: 10px;\n}\n\nimg {\n display: block;\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n max-height: 60vh;\n padding: 10px;\n}\n</style>\n","<template>\n <div data-viewable=\"FillInView\">\n <audio-auto-player v-if=\"hasAudio\" :src=\"audioURL\" />\n <template v-if=\"hasImage\">\n <img v-for=\"(url, index) in imageURLs\" :key=\"index\" :src=\"url\" />\n </template>\n <!-- Add v-if to prevent undefined markdown -->\n <markdown-renderer v-if=\"markdownText\" :md=\"markdownText\" />\n <radio-multiple-choice v-if=\"question?.options\" :choice-list=\"truncatedOptions\" />\n <div v-else-if=\"priorAttempts == 1\" class=\"text-center text-h6\">\n <span>{{ obscuredAnswer }}</span>\n </div>\n <div v-else-if=\"priorAttempts == 2\" class=\"text-center text-h6\">\n <span>{{ someAnswer }}</span>\n </div>\n <v-card-actions v-if=\"!isQuestion\">\n <v-spacer></v-spacer>\n <v-btn color=\"primary\" autofocus=\"autofocus\" @click=\"handleNext\"> Next </v-btn>\n </v-card-actions>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineAsyncComponent, defineComponent, ref, computed, PropType, onMounted, onUnmounted, watch } from 'vue';\nimport { useViewable, useQuestionView, AudioAutoPlayer, RadioMultipleChoice } from '@vue-skuilder/common-ui';\nimport { shuffle } from '@courseware/math/utility';\nimport { BlanksCard } from './index';\nimport gradeSpellingAttempt from './blanksCorrection';\nimport { ViewData } from '@vue-skuilder/common';\n\nexport default defineComponent({\n name: 'FillInView',\n\n components: {\n // Previously:\n // MarkdownRenderer: defineAsyncComponent(() => import('@courseware/base-course/Components/MarkdownRenderer.vue')),\n //\n // Another option:\n MarkdownRenderer: defineAsyncComponent(() =>\n import('@vue-skuilder/common-ui').then((module) => module.MarkdownRenderer)\n ),\n // MarkdownRenderer,\n RadioMultipleChoice,\n AudioAutoPlayer,\n },\n\n props: {\n data: {\n type: Array as PropType<ViewData[]>,\n required: true,\n },\n modifyDifficulty: {\n type: Number,\n required: false,\n default: 0,\n },\n },\n\n setup(props, { emit }) {\n const viewableUtils = useViewable(props, emit, 'FillInView');\n const questionUtils = useQuestionView<BlanksCard>(viewableUtils);\n\n questionUtils.question.value = new BlanksCard(props.data);\n\n // State\n const shuffledOptions = ref<string[] | undefined>();\n const question = computed(() => {\n if (!questionUtils.question.value) {\n viewableUtils.logger.error('Question not initialized');\n throw new Error('Question not initialized');\n }\n return questionUtils.question.value;\n });\n\n onMounted(() => {\n try {\n if (!questionUtils.question.value) {\n questionUtils.question.value = new BlanksCard(props.data);\n }\n\n if (questionUtils.question.value.options) {\n const truncatedList = getTruncatedList();\n shuffledOptions.value = shuffle(truncatedList);\n }\n } catch (error) {\n viewableUtils.logger.error('Failed to initialize question:', error);\n }\n });\n\n onUnmounted(() => {\n questionUtils.question.value = undefined;\n });\n\n // Computed properties\n const truncatedOptions = computed(() => shuffledOptions.value ?? []);\n\n const hasImage = computed(() => !!props.data[0]['image-1']);\n\n const imageURLs = computed(() => {\n if (!hasImage.value) return [''];\n\n const urls: string[] = [];\n let i = 1;\n while (props.data[0][`image-${i}`]) {\n urls.push(URL.createObjectURL(props.data[0][`image-${i}`] as Blob));\n i++;\n }\n return urls;\n });\n\n const hasAudio = computed(() => !!props.data[0]['audio-1']);\n\n const markdownText = computed(() => {\n return question.value?.mdText || ''; // Provide empty string as fallback\n });\n\n const audioURL = computed(() => {\n if (!hasAudio.value) return [''];\n\n const urls: string[] = [];\n let i = 1;\n while (props.data[0][`audio-${i}`]) {\n urls.push(URL.createObjectURL(props.data[0][`audio-${i}`] as Blob));\n i++;\n }\n return urls;\n });\n\n const someAnswer = computed(() => {\n if (question.value.answers) {\n return question.value.answers[Math.floor(question.value.answers.length * Math.random())];\n } else {\n throw new Error('No answers provided');\n }\n });\n\n const obscuredAnswer = computed(() => {\n const sa = someAnswer.value;\n\n console.log(`Prior answers: ${questionUtils.priorAnswers.value}`);\n\n if (\n sa &&\n questionUtils.priorAnswers.value[0]?.[0] &&\n questionUtils.priorAnswers.value[0][1] === 'UserInputString'\n ) {\n return gradeSpellingAttempt(questionUtils.priorAnswers.value[0][0] as string, sa);\n } else {\n console.log(`found no UserInputString`);\n }\n\n if (someAnswer.value) {\n let obscuredAnswer = '';\n for (let i = 0; i < someAnswer.value.length; i++) {\n obscuredAnswer += '_ ';\n }\n return obscuredAnswer;\n }\n\n return '';\n });\n\n const isQuestion = computed(() => {\n return !!(question.value!.answers && question.value.answers.length > 0);\n });\n\n // Methods\n const getTruncatedList = (): string[] | undefined => {\n if (!question.value?.options) return;\n\n if (question.value.options.length <= 6) {\n return question.value.options;\n }\n\n const truncatedList = [];\n // include one answer\n truncatedList.push(question.value.answers![Math.floor(Math.random() * question.value.answers!.length)]);\n\n // construct a list of all non-answers\n let distractors: string[] = shuffle(\n question.value.options.filter((o: string) => question.value.answers?.indexOf(o) === -1)\n );\n\n if (props.modifyDifficulty) {\n console.log(`Modifying difficulty: ${props.modifyDifficulty}`);\n\n // Adjust number of distractors based on difficulty\n if (props.modifyDifficulty < -200) {\n distractors = distractors.slice(0, 1);\n } else if (props.modifyDifficulty < -150) {\n distractors = distractors.slice(0, 2);\n } else if (props.modifyDifficulty < -100) {\n distractors = distractors.slice(0, 3);\n } else if (props.modifyDifficulty < -50) {\n distractors = distractors.slice(0, 4);\n } else {\n distractors = distractors.slice(0, 5);\n }\n }\n\n truncatedList.push(...distractors.slice(0, 5));\n return truncatedList;\n };\n\n const handleNext = () => {\n questionUtils.submitAnswer('');\n };\n\n /**\n * update the question whenever the data changes\n */\n watch(\n () => props.data,\n (newdata) => {\n try {\n questionUtils.question.value = new BlanksCard(newdata);\n // Update shuffled options whenever question changes\n if (questionUtils.question.value?.options) {\n const truncatedList = getTruncatedList();\n shuffledOptions.value = shuffle(truncatedList);\n }\n } catch (error) {\n viewableUtils.logger.error('Failed to initialize/update question:', error);\n }\n },\n { deep: true }\n );\n\n return {\n ...viewableUtils,\n ...questionUtils,\n question,\n truncatedOptions,\n hasImage,\n imageURLs,\n hasAudio,\n audioURL,\n obscuredAnswer,\n someAnswer,\n isQuestion,\n handleNext,\n markdownText,\n };\n },\n});\n</script>\n\n<style lang=\"css\" scoped>\ncanvas {\n display: block;\n margin-left: auto;\n margin-right: auto;\n padding: 10px;\n}\n\nimg {\n display: block;\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n max-height: 60vh;\n padding: 10px;\n}\n</style>\n","import { Question, splitByDelimiters, containsComponent, isInlineComponent } from '@vue-skuilder/common-ui';\nimport { Answer, RadioMultipleChoiceAnswer } from '@vue-skuilder/common';\nimport { Validator, ViewData } from '@vue-skuilder/common';\nimport { randomInt, shuffle } from '@courseware/math/utility';\nimport { Status } from '@vue-skuilder/common';\nimport { Tokens } from 'marked';\nimport FillInView from './fillIn.vue';\nimport { BlanksCardDataShapes } from './shapes.js';\n\n// Re-export for backward compatibility\nexport { BlanksCardDataShapes } from './shapes.js';\n\n// @ts-expect-error Legacy validator code kept for reference but not currently used\nconst _val: Validator = {\n test: (input) => {\n console.log(`Testing md input: ${input}`);\n // const sections = parseBlanksMarkdown(input);\n // let blanksCount: number = 0;\n // sections.forEach((section) => {\n // if (section.type === 'blank') {\n // blanksCount++;\n // }\n // });\n\n // if (blanksCount === 1) {\n return {\n status: Status.ok,\n msg: '',\n };\n // } else {\n // return {\n // status: Status.error,\n // msg: 'There must be exactly one blank of the form {{answer}} in the question'\n // };\n // }\n },\n};\n\ntype fillInSectionType = 'text' | 'blank';\n\nexport interface FillInSection {\n type: fillInSectionType;\n text: string;\n}\n\nfunction splitText(\n text: string,\n leftBound: string,\n rightBound: string\n): {\n left: string;\n middle: string;\n right: string;\n} {\n const leftSplit = text.split(leftBound);\n const left = leftSplit[0];\n\n const rightSplit = leftSplit[1].split(rightBound);\n const middle = rightSplit[0];\n const right = rightSplit[1];\n\n return { left, middle, right };\n}\n\nexport class BlanksCard extends Question {\n public static dataShapes = BlanksCardDataShapes;\n public static views = [FillInView];\n public mdText: string = '';\n\n public answers: string[] | null = null;\n public options: string[] | null = null;\n\n public splitTextToken(token: Tokens.Text): Tokens.Text[] {\n if (containsComponent(token)) {\n const text = splitText(token.text, '{{', '}}');\n const raw = splitText(token.raw, '{{', '}}');\n\n const ret: Tokens.Text[] = [];\n\n if (raw.left.length > 0) {\n ret.push({\n type: 'text',\n raw: raw.left,\n text: text.left,\n });\n }\n if (raw.middle.length > 0) {\n ret.push({\n type: 'text',\n raw: '{{' + raw.middle + '}}',\n text: '{{' + text.middle + '}}',\n });\n }\n if (raw.right.length > 0) {\n ret.push({\n type: 'text',\n raw: raw.right,\n text: text.right,\n });\n }\n\n return ret;\n } else {\n return [token];\n }\n }\n\n /**\n * Parses a string to extract answer options and distractors for a fill-in question.\n * The input string must be wrapped in curly braces and can contain multiple answers/distractors separated by | and ||.\n * For example: \"{{answer1|answer2||distractor1|distractor2}}\"\n * @param s The input string containing answers and optional distractors\n * @returns An object containing the parsed answers array and shuffled options array (or null if no distractors)\n * @throws Error if input string is not properly formatted with {{ }}\n */\n private optionsFromString(s: string) {\n if (!s.startsWith('{{') || !s.endsWith('}}')) {\n throw new Error(`string ${s} is not fill-in text - must look like \"{{someText}}\"`);\n }\n s = s.substring(2, s.length - 2);\n const split = s.split('||');\n if (split.length > 1) {\n const answers = split[0].split('|').map((a) => a.trim());\n // remove answers from distractors (makes for easier editing to allow answers in the distractor list)\n const distractors = split[1]\n .split('|')\n .map((d) => d.trim())\n .filter((d) => !answers.includes(d));\n\n const options = distractors;\n options.push(answers[randomInt(0, answers.length - 1)]);\n\n return {\n answers,\n options: shuffle(options),\n };\n } else {\n return {\n answers: [s.trim()],\n options: null,\n };\n }\n }\n\n constructor(data: ViewData[]) {\n super(data);\n this.mdText = data[0].Input as unknown as string;\n if (this.mdText === undefined) {\n this.mdText = '';\n }\n\n const splits = splitByDelimiters(this.mdText, '{{', '}}');\n const recombines = [];\n for (let i = 0; i < splits.length; i++) {\n const split = splits[i];\n const trimmed = split.trim();\n\n // If the split starts/ends with {{ }}, it's a delimited block\n if (trimmed.startsWith('{{') && trimmed.endsWith('}}')) {\n // Extract content between {{ and }}\n const content = trimmed.slice(2, -2).trim();\n\n if (isInlineComponent(content)) {\n // Preserve inline component syntax\n recombines.push(split);\n } else {\n // Try to parse as fillIn blank\n try {\n const parsedOptions = this.optionsFromString(split);\n this.answers = parsedOptions.answers;\n this.options = parsedOptions.options;\n if (this.options?.length) {\n recombines.push('{{ || }}'); // render a multiple-choice blank\n } else {\n recombines.push('{{ }}'); // render a text blank\n }\n } catch {\n // Not valid fillIn syntax, keep original\n recombines.push(split);\n }\n }\n } else {\n // Regular text, not delimited\n recombines.push(split);\n }\n }\n\n this.mdText = recombines.join('');\n }\n\n public isCorrect(answer: Answer) {\n console.log(`answers:${this.answers}\\nuser answer: ${JSON.stringify(answer)}`);\n\n if (typeof answer === 'string') {\n if (this.answers) {\n return this.answers.includes(answer);\n } else {\n if (answer === '') {\n return true;\n } else {\n throw new Error(`Question has no configured answers!`);\n }\n }\n } else if (Array.isArray(answer)) {\n if (this.answers) {\n return answer.every((a) => {\n return this.answers!.includes(a);\n });\n } else {\n throw new Error(`Question has no configured answers!`);\n }\n } else {\n return this.isCorrectRadio(answer as RadioMultipleChoiceAnswer);\n }\n }\n\n public dataShapes() {\n return BlanksCard.dataShapes;\n }\n public views() {\n return BlanksCard.views;\n }\n\n private isCorrectRadio(answer: RadioMultipleChoiceAnswer) {\n if (this.answers) {\n return this.answers.includes(answer.choiceList[answer.selection]);\n } else {\n return false;\n }\n }\n}\n","import { Displayable, ViewComponent } from '@vue-skuilder/common-ui';\nimport { BlanksCard } from './default/questions/fillIn/';\n\nexport class CourseWare {\n public get questions(): Array<typeof Displayable> {\n return this.questionList;\n }\n\n public get allViews(): Array<ViewComponent> {\n const ret = new Array<ViewComponent>();\n\n this.questionList.forEach((question) => {\n question.views.forEach((view) => {\n ret.push(view);\n });\n });\n\n return ret;\n }\n\n /**\n * This function returns the map {[index:string]: string} of display\n * components needed by the CardViewer component\n */\n public get allViewsMap(): { [index: string]: ViewComponent } {\n const ret: { [index: string]: ViewComponent } = {};\n\n this.allViews.forEach((view) => {\n if (view.name) {\n ret[view.name] = view;\n } else {\n throw new Error('View has no name');\n }\n });\n\n return ret;\n }\n public readonly name: string;\n private readonly questionList: Array<typeof Displayable>;\n\n constructor(name: string, questionList: Array<typeof Displayable>) {\n this.name = name;\n this.questionList = questionList;\n\n this.questionList = this.questionList.concat(this.getBaseQTypes());\n }\n\n public getQuestion(name: string): typeof Displayable | undefined {\n const searchBaseName = name.replace(/^_/, '').replace(/\\d+$/, '');\n const registered = this.questionList.map((q) => q.name);\n\n // Pass 1: exact or stripped-exact match (safe, no false positives)\n const exact = this.questionList.find((question) => {\n const questionBaseName = question.name.replace(/^_/, '').replace(/\\d+$/, '');\n return question.name === name || questionBaseName === searchBaseName;\n });\n if (exact) {\n if (exact.name !== name) {\n console.debug(\n `[CourseWare.getQuestion] stripped-exact: \"${name}\" → \"${exact.name}\" (registered: ${registered.join(', ')})`,\n );\n }\n return exact;\n }\n\n // Pass 2: fuzzy includes() fallback for bundler name-mangling resilience.\n // Prefer the shortest-name match to avoid \"GpcIntro\" matching \"DigraphGpcIntro\".\n const fuzzyMatches = this.questionList.filter((question) => {\n const questionBaseName = question.name.replace(/^_/, '').replace(/\\d+$/, '');\n return question.name.includes(searchBaseName) || name.includes(questionBaseName);\n });\n\n if (fuzzyMatches.length === 0) {\n console.warn(\n `[CourseWare.getQuestion] NO MATCH: \"${name}\" (base: \"${searchBaseName}\") not found in [${registered.join(', ')}]`,\n );\n return undefined;\n }\n\n // Among fuzzy matches, prefer the one whose name length is closest to the search name\n // (shortest delta wins). This prevents \"GpcIntro\" from matching \"DigraphGpcIntro\"\n // when \"GpcIntro\" itself is registered.\n fuzzyMatches.sort((a, b) =>\n Math.abs(a.name.length - name.length) - Math.abs(b.name.length - name.length)\n );\n const winner = fuzzyMatches[0];\n console.warn(\n `[CourseWare.getQuestion] FUZZY: \"${name}\" → \"${winner.name}\" (candidates: ${fuzzyMatches.map((q) => q.name).join(', ')}; registered: ${registered.join(', ')})`,\n );\n return winner;\n }\n\n public getBaseQTypes(): Array<typeof Displayable> {\n // #145 todo: return [BasicCard];\n // should: get 'default' course displayable types\n // return defaultCourse.getBaseQTypes();\n return [BlanksCard];\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
canvas[data-v-f91502f8]{margin-left:auto;margin-right:auto;padding:10px;display:block}img[data-v-f91502f8]{max-width:100%;max-height:60vh;margin-left:auto;margin-right:auto;padding:10px;display:block}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.cg-wrap{box-sizing:content-box;display:block;position:relative}cg-container{width:100%;height:100%;display:block;position:absolute;top:0}cg-board{-webkit-user-select:none;user-select:none;background-size:cover;width:100%;height:100%;line-height:0;position:absolute;top:0;left:0}.cg-wrap.manipulable cg-board{cursor:pointer}cg-board square{pointer-events:none;width:12.5%;height:12.5%;position:absolute;top:0;left:0}cg-board square.move-dest{pointer-events:auto}cg-board square.last-move{will-change:transform}.cg-wrap piece{z-index:2;will-change:transform;pointer-events:none;background-size:cover;width:12.5%;height:12.5%;position:absolute;top:0;left:0}cg-board piece.dragging{cursor:move;z-index:11!important}piece.anim{z-index:8}piece.fading{z-index:1;opacity:.5}.cg-wrap piece.ghost{opacity:.3}.cg-wrap piece svg{pointer-events:none;z-index:2;opacity:.6;width:100%;height:100%;position:relative;top:0;left:0;overflow:hidden}.cg-wrap cg-auto-pieces,.cg-wrap .cg-shapes,.cg-wrap .cg-custom-svgs{pointer-events:none;width:100%;height:100%;position:absolute;top:0;left:0;overflow:visible}.cg-wrap cg-auto-pieces{z-index:2}.cg-wrap cg-auto-pieces piece{opacity:.3}.cg-wrap .cg-shapes{opacity:.6;z-index:2;overflow:hidden}.cg-wrap .cg-custom-svgs{z-index:9}.cg-wrap .cg-custom-svgs svg{overflow:visible}.cg-wrap coords{pointer-events:none;opacity:.8;font-family:sans-serif;font-size:9px;display:flex;position:absolute}.cg-wrap coords.ranks{flex-flow:column-reverse;width:12px;height:100%;top:-20px;left:4px}.cg-wrap coords.ranks.black{flex-flow:column}.cg-wrap coords.ranks.left{align-items:flex-end;left:-15px}.cg-wrap coords.files{text-transform:uppercase;text-align:center;flex-flow:row;width:100%;height:16px;bottom:-4px;left:24px}.cg-wrap coords.files.black{flex-flow:row-reverse}.cg-wrap coords coord{flex:auto}.cg-wrap coords.ranks coord{transform:translateY(39%)}.cg-wrap coords.squares{text-transform:uppercase;text-align:right;flex-flow:column-reverse;width:12.5%;height:100%;bottom:0;left:0}.cg-wrap coords.squares.black{flex-flow:column}.cg-wrap coords.squares.left{text-align:left}.cg-wrap coords.squares coord{padding:6% 4%}.cg-wrap coords.squares.rank2{transform:translate(100%)}.cg-wrap coords.squares.rank3{transform:translate(200%)}.cg-wrap coords.squares.rank4{transform:translate(300%)}.cg-wrap coords.squares.rank5{transform:translate(400%)}.cg-wrap coords.squares.rank6{transform:translate(500%)}.cg-wrap coords.squares.rank7{transform:translate(600%)}.cg-wrap coords.squares.rank8{transform:translate(700%)}cg-board{background-color:#f0d9b5;background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4PSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIgogICAgIHZpZXdCb3g9IjAgMCA4IDgiIHNoYXBlLXJlbmRlcmluZz0iY3Jpc3BFZGdlcyI+CjxnIGlkPSJhIj4KICA8ZyBpZD0iYiI+CiAgICA8ZyBpZD0iYyI+CiAgICAgIDxnIGlkPSJkIj4KICAgICAgICA8cmVjdCB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBpZD0iZSIgb3BhY2l0eT0iMCIvPgogICAgICAgIDx1c2UgeD0iMSIgeT0iMSIgaHJlZj0iI2UiIHg6aHJlZj0iI2UiLz4KICAgICAgICA8cmVjdCB5PSIxIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBpZD0iZiIgb3BhY2l0eT0iMC4yIi8+CiAgICAgICAgPHVzZSB4PSIxIiB5PSItMSIgaHJlZj0iI2YiIHg6aHJlZj0iI2YiLz4KICAgICAgPC9nPgogICAgICA8dXNlIHg9IjIiIGhyZWY9IiNkIiB4OmhyZWY9IiNkIi8+CiAgICA8L2c+CiAgICA8dXNlIHg9IjQiIGhyZWY9IiNjIiB4OmhyZWY9IiNjIi8+CiAgPC9nPgogIDx1c2UgeT0iMiIgaHJlZj0iI2IiIHg6aHJlZj0iI2IiLz4KPC9nPgo8dXNlIHk9IjQiIGhyZWY9IiNhIiB4OmhyZWY9IiNhIi8+Cjwvc3ZnPg==)}cg-board square.move-dest{background:radial-gradient(#14551e80 22%,#208530 0,#0000004d 0,#0000 0)}cg-board square.premove-dest{background:radial-gradient(#141e5580 22%,#203085 0,#0000004d 0,#0000 0)}cg-board square.oc.move-dest{background:radial-gradient(#0000 0% 80%,#1455004d 80%)}cg-board square.oc.premove-dest{background:radial-gradient(#0000 0% 80%,#141e5533 80%)}cg-board square.move-dest:hover{background:#14551e4d}cg-board square.premove-dest:hover{background:#141e5533}cg-board square.last-move{background-color:#9bc70069}cg-board square.selected{background-color:#14551e80}cg-board square.check{background:radial-gradient(red 0%,#e70000 25%,#a9000000 89%,#9e000000 100%)}cg-board square.current-premove{background-color:#141e5580}.cg-wrap coords:nth-child(odd) coord:nth-child(2n),.cg-wrap coords.squares:nth-child(2n) coord:nth-child(odd),.cg-wrap.orientation-black coords.files:nth-child(2n) coord:nth-child(odd),.cg-wrap coords.files:nth-child(2n) coord:nth-child(2n){color:#484848cc}.cg-wrap coords:nth-child(odd) coord:nth-child(odd),.cg-wrap coords.squares:nth-child(2n) coord:nth-child(2n),.cg-wrap.orientation-black coords.files:nth-child(2n) coord:nth-child(2n),.cg-wrap coords.files:nth-child(2n) coord:nth-child(odd){color:#fffc}.cg-wrap piece.pawn.cg-white{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PHBhdGggZD0iTTIyLjUgOWMtMi4yMSAwLTQgMS43OS00IDQgMCAuODkuMjkgMS43MS43OCAyLjM4QzE3LjMzIDE2LjUgMTYgMTguNTkgMTYgMjFjMCAyLjAzLjk0IDMuODQgMi40MSA1LjAzLTMgMS4wNi03LjQxIDUuNTUtNy40MSAxMy40N2gyM2MwLTcuOTItNC40MS0xMi40MS03LjQxLTEzLjQ3IDEuNDctMS4xOSAyLjQxLTMgMi40MS01LjAzIDAtMi40MS0xLjMzLTQuNS0zLjI4LTUuNjIuNDktLjY3Ljc4LTEuNDkuNzgtMi4zOCAwLTIuMjEtMS43OS00LTQtNHoiIGZpbGw9IiNmZmYiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjwvc3ZnPg==)}.cg-wrap piece.bishop.cg-white{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxnIGZpbGw9IiNmZmYiIHN0cm9rZS1saW5lY2FwPSJidXR0Ij48cGF0aCBkPSJNOSAzNmMzLjM5LS45NyAxMC4xMS40MyAxMy41LTIgMy4zOSAyLjQzIDEwLjExIDEuMDMgMTMuNSAyIDAgMCAxLjY1LjU0IDMgMi0uNjguOTctMS42NS45OS0zIC41LTMuMzktLjk3LTEwLjExLjQ2LTEzLjUtMS0zLjM5IDEuNDYtMTAuMTEuMDMtMTMuNSAxLTEuMzU0LjQ5LTIuMzIzLjQ3LTMtLjUgMS4zNTQtMS45NCAzLTIgMy0yeiIvPjxwYXRoIGQ9Ik0xNSAzMmMyLjUgMi41IDEyLjUgMi41IDE1IDAgLjUtMS41IDAtMiAwLTIgMC0yLjUtMi41LTQtMi41LTQgNS41LTEuNSA2LTExLjUtNS0xNS41LTExIDQtMTAuNSAxNC01IDE1LjUgMCAwLTIuNSAxLjUtMi41IDQgMCAwLS41LjUgMCAyeiIvPjxwYXRoIGQ9Ik0yNSA4YTIuNSAyLjUgMCAxIDEtNSAwIDIuNSAyLjUgMCAxIDEgNSAweiIvPjwvZz48cGF0aCBkPSJNMTcuNSAyNmgxME0xNSAzMGgxNW0tNy41LTE0LjV2NU0yMCAxOGg1IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIi8+PC9nPjwvc3ZnPg==)}.cg-wrap piece.knight.cg-white{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0yMiAxMGMxMC41IDEgMTYuNSA4IDE2IDI5SDE1YzAtOSAxMC02LjUgOC0yMSIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Ik0yNCAxOGMuMzggMi45MS01LjU1IDcuMzctOCA5LTMgMi0yLjgyIDQuMzQtNSA0LTEuMDQyLS45NCAxLjQxLTMuMDQgMC0zLTEgMCAuMTkgMS4yMy0xIDItMSAwLTQuMDAzIDEtNC00IDAtMiA2LTEyIDYtMTJzMS44OS0xLjkgMi0zLjVjLS43My0uOTk0LS41LTItLjUtMyAxLTEgMyAyLjUgMyAyLjVoMnMuNzgtMS45OTIgMi41LTNjMSAwIDEgMyAxIDMiIGZpbGw9IiNmZmYiLz48cGF0aCBkPSJNOS41IDI1LjVhLjUuNSAwIDEgMS0xIDAgLjUuNSAwIDEgMSAxIDB6bTUuNDMzLTkuNzVhLjUgMS41IDMwIDEgMS0uODY2LS41LjUgMS41IDMwIDEgMSAuODY2LjV6IiBmaWxsPSIjMDAwIi8+PC9nPjwvc3ZnPg==)}.cg-wrap piece.rook.cg-white{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0iI2ZmZiIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik05IDM5aDI3di0zSDl2M3ptMy0zdi00aDIxdjRIMTJ6bS0xLTIyVjloNHYyaDVWOWg1djJoNVY5aDR2NSIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiLz48cGF0aCBkPSJNMzQgMTRsLTMgM0gxNGwtMy0zIi8+PHBhdGggZD0iTTMxIDE3djEyLjVIMTRWMTciIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIi8+PHBhdGggZD0iTTMxIDI5LjVsMS41IDIuNWgtMjBsMS41LTIuNSIvPjxwYXRoIGQ9Ik0xMSAxNGgyMyIgZmlsbD0ibm9uZSIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIvPjwvZz48L3N2Zz4=)}.cg-wrap piece.queen.cg-white{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0iI2ZmZiIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik04IDEyYTIgMiAwIDEgMS00IDAgMiAyIDAgMSAxIDQgMHptMTYuNS00LjVhMiAyIDAgMSAxLTQgMCAyIDIgMCAxIDEgNCAwek00MSAxMmEyIDIgMCAxIDEtNCAwIDIgMiAwIDEgMSA0IDB6TTE2IDguNWEyIDIgMCAxIDEtNCAwIDIgMiAwIDEgMSA0IDB6TTMzIDlhMiAyIDAgMSAxLTQgMCAyIDIgMCAxIDEgNCAweiIvPjxwYXRoIGQ9Ik05IDI2YzguNS0xLjUgMjEtMS41IDI3IDBsMi0xMi03IDExVjExbC01LjUgMTMuNS0zLTE1LTMgMTUtNS41LTE0VjI1TDcgMTRsMiAxMnoiIHN0cm9rZS1saW5lY2FwPSJidXR0Ii8+PHBhdGggZD0iTTkgMjZjMCAyIDEuNSAyIDIuNSA0IDEgMS41IDEgMSAuNSAzLjUtMS41IDEtMS41IDIuNS0xLjUgMi41LTEuNSAxLjUuNSAyLjUuNSAyLjUgNi41IDEgMTYuNSAxIDIzIDAgMCAwIDEuNS0xIDAtMi41IDAgMCAuNS0xLjUtMS0yLjUtLjUtMi41LS41LTIgLjUtMy41IDEtMiAyLjUtMiAyLjUtNC04LjUtMS41LTE4LjUtMS41LTI3IDB6IiBzdHJva2UtbGluZWNhcD0iYnV0dCIvPjxwYXRoIGQ9Ik0xMS41IDMwYzMuNS0xIDE4LjUtMSAyMiAwTTEyIDMzLjVjNi0xIDE1LTEgMjEgMCIgZmlsbD0ibm9uZSIvPjwvZz48L3N2Zz4=)}.cg-wrap piece.king.cg-white{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0yMi41IDExLjYzVjZNMjAgOGg1IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIi8+PHBhdGggZD0iTTIyLjUgMjVzNC41LTcuNSAzLTEwLjVjMCAwLTEtMi41LTMtMi41cy0zIDIuNS0zIDIuNWMtMS41IDMgMyAxMC41IDMgMTAuNSIgZmlsbD0iI2ZmZiIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiLz48cGF0aCBkPSJNMTEuNSAzN2M1LjUgMy41IDE1LjUgMy41IDIxIDB2LTdzOS00LjUgNi0xMC41Yy00LTYuNS0xMy41LTMuNS0xNiA0VjI3di0zLjVjLTMuNS03LjUtMTMtMTAuNS0xNi00LTMgNiA1IDEwIDUgMTBWMzd6IiBmaWxsPSIjZmZmIi8+PHBhdGggZD0iTTExLjUgMzBjNS41LTMgMTUuNS0zIDIxIDBtLTIxIDMuNWM1LjUtMyAxNS41LTMgMjEgMG0tMjEgMy41YzUuNS0zIDE1LjUtMyAyMSAwIi8+PC9nPjwvc3ZnPg==)}.cg-wrap piece.pawn.cg-black{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PHBhdGggZD0iTTIyLjUgOWMtMi4yMSAwLTQgMS43OS00IDQgMCAuODkuMjkgMS43MS43OCAyLjM4QzE3LjMzIDE2LjUgMTYgMTguNTkgMTYgMjFjMCAyLjAzLjk0IDMuODQgMi40MSA1LjAzLTMgMS4wNi03LjQxIDUuNTUtNy40MSAxMy40N2gyM2MwLTcuOTItNC40MS0xMi40MS03LjQxLTEzLjQ3IDEuNDctMS4xOSAyLjQxLTMgMi40MS01LjAzIDAtMi40MS0xLjMzLTQuNS0zLjI4LTUuNjIuNDktLjY3Ljc4LTEuNDkuNzgtMi4zOCAwLTIuMjEtMS43OS00LTQtNHoiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjwvc3ZnPg==)}.cg-wrap piece.bishop.cg-black{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxnIGZpbGw9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJidXR0Ij48cGF0aCBkPSJNOSAzNmMzLjM5LS45NyAxMC4xMS40MyAxMy41LTIgMy4zOSAyLjQzIDEwLjExIDEuMDMgMTMuNSAyIDAgMCAxLjY1LjU0IDMgMi0uNjguOTctMS42NS45OS0zIC41LTMuMzktLjk3LTEwLjExLjQ2LTEzLjUtMS0zLjM5IDEuNDYtMTAuMTEuMDMtMTMuNSAxLTEuMzU0LjQ5LTIuMzIzLjQ3LTMtLjUgMS4zNTQtMS45NCAzLTIgMy0yeiIvPjxwYXRoIGQ9Ik0xNSAzMmMyLjUgMi41IDEyLjUgMi41IDE1IDAgLjUtMS41IDAtMiAwLTIgMC0yLjUtMi41LTQtMi41LTQgNS41LTEuNSA2LTExLjUtNS0xNS41LTExIDQtMTAuNSAxNC01IDE1LjUgMCAwLTIuNSAxLjUtMi41IDQgMCAwLS41LjUgMCAyeiIvPjxwYXRoIGQ9Ik0yNSA4YTIuNSAyLjUgMCAxIDEtNSAwIDIuNSAyLjUgMCAxIDEgNSAweiIvPjwvZz48cGF0aCBkPSJNMTcuNSAyNmgxME0xNSAzMGgxNW0tNy41LTE0LjV2NU0yMCAxOGg1IiBzdHJva2U9IiNlY2VjZWMiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiLz48L2c+PC9zdmc+)}.cg-wrap piece.knight.cg-black{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0yMiAxMGMxMC41IDEgMTYuNSA4IDE2IDI5SDE1YzAtOSAxMC02LjUgOC0yMSIgZmlsbD0iIzAwMCIvPjxwYXRoIGQ9Ik0yNCAxOGMuMzggMi45MS01LjU1IDcuMzctOCA5LTMgMi0yLjgyIDQuMzQtNSA0LTEuMDQyLS45NCAxLjQxLTMuMDQgMC0zLTEgMCAuMTkgMS4yMy0xIDItMSAwLTQuMDAzIDEtNC00IDAtMiA2LTEyIDYtMTJzMS44OS0xLjkgMi0zLjVjLS43My0uOTk0LS41LTItLjUtMyAxLTEgMyAyLjUgMyAyLjVoMnMuNzgtMS45OTIgMi41LTNjMSAwIDEgMyAxIDMiIGZpbGw9IiMwMDAiLz48cGF0aCBkPSJNOS41IDI1LjVhLjUuNSAwIDEgMS0xIDAgLjUuNSAwIDEgMSAxIDB6bTUuNDMzLTkuNzVhLjUgMS41IDMwIDEgMS0uODY2LS41LjUgMS41IDMwIDEgMSAuODY2LjV6IiBmaWxsPSIjZWNlY2VjIiBzdHJva2U9IiNlY2VjZWMiLz48cGF0aCBkPSJNMjQuNTUgMTAuNGwtLjQ1IDEuNDUuNS4xNWMzLjE1IDEgNS42NSAyLjQ5IDcuOSA2Ljc1UzM1Ljc1IDI5LjA2IDM1LjI1IDM5bC0uMDUuNWgyLjI1bC4wNS0uNWMuNS0xMC4wNi0uODgtMTYuODUtMy4yNS0yMS4zNC0yLjM3LTQuNDktNS43OS02LjY0LTkuMTktNy4xNmwtLjUxLS4xeiIgZmlsbD0iI2VjZWNlYyIgc3Ryb2tlPSJub25lIi8+PC9nPjwvc3ZnPg==)}.cg-wrap piece.rook.cg-black{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik05IDM5aDI3di0zSDl2M3ptMy41LTdsMS41LTIuNWgxN2wxLjUgMi41aC0yMHptLS41IDR2LTRoMjF2NEgxMnoiIHN0cm9rZS1saW5lY2FwPSJidXR0Ii8+PHBhdGggZD0iTTE0IDI5LjV2LTEzaDE3djEzSDE0eiIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiLz48cGF0aCBkPSJNMTQgMTYuNUwxMSAxNGgyM2wtMyAyLjVIMTR6TTExIDE0VjloNHYyaDVWOWg1djJoNVY5aDR2NUgxMXoiIHN0cm9rZS1saW5lY2FwPSJidXR0Ii8+PHBhdGggZD0iTTEyIDM1LjVoMjFtLTIwLTRoMTltLTE4LTJoMTdtLTE3LTEzaDE3TTExIDE0aDIzIiBmaWxsPSJub25lIiBzdHJva2U9IiNlY2VjZWMiIHN0cm9rZS13aWR0aD0iMSIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIvPjwvZz48L3N2Zz4=)}.cg-wrap piece.queen.cg-black{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxnIHN0cm9rZT0ibm9uZSI+PGNpcmNsZSBjeD0iNiIgY3k9IjEyIiByPSIyLjc1Ii8+PGNpcmNsZSBjeD0iMTQiIGN5PSI5IiByPSIyLjc1Ii8+PGNpcmNsZSBjeD0iMjIuNSIgY3k9IjgiIHI9IjIuNzUiLz48Y2lyY2xlIGN4PSIzMSIgY3k9IjkiIHI9IjIuNzUiLz48Y2lyY2xlIGN4PSIzOSIgY3k9IjEyIiByPSIyLjc1Ii8+PC9nPjxwYXRoIGQ9Ik05IDI2YzguNS0xLjUgMjEtMS41IDI3IDBsMi41LTEyLjVMMzEgMjVsLS4zLTE0LjEtNS4yIDEzLjYtMy0xNC41LTMgMTQuNS01LjItMTMuNkwxNCAyNSA2LjUgMTMuNSA5IDI2eiIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiLz48cGF0aCBkPSJNOSAyNmMwIDIgMS41IDIgMi41IDQgMSAxLjUgMSAxIC41IDMuNS0xLjUgMS0xLjUgMi41LTEuNSAyLjUtMS41IDEuNS41IDIuNS41IDIuNSA2LjUgMSAxNi41IDEgMjMgMCAwIDAgMS41LTEgMC0yLjUgMCAwIC41LTEuNS0xLTIuNS0uNS0yLjUtLjUtMiAuNS0zLjUgMS0yIDIuNS0yIDIuNS00LTguNS0xLjUtMTguNS0xLjUtMjcgMHoiIHN0cm9rZS1saW5lY2FwPSJidXR0Ii8+PHBhdGggZD0iTTExIDM4LjVhMzUgMzUgMSAwIDAgMjMgMCIgZmlsbD0ibm9uZSIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiLz48cGF0aCBkPSJNMTEgMjlhMzUgMzUgMSAwIDEgMjMgMG0tMjEuNSAyLjVoMjBtLTIxIDNhMzUgMzUgMSAwIDAgMjIgMG0tMjMgM2EzNSAzNSAxIDAgMCAyNCAwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlY2VjZWMiLz48L2c+PC9zdmc+)}.cg-wrap piece.king.cg-black{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0yMi41IDExLjYzVjYiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiLz48cGF0aCBkPSJNMjIuNSAyNXM0LjUtNy41IDMtMTAuNWMwIDAtMS0yLjUtMy0yLjVzLTMgMi41LTMgMi41Yy0xLjUgMyAzIDEwLjUgMyAxMC41IiBmaWxsPSIjMDAwIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIvPjxwYXRoIGQ9Ik0xMS41IDM3YzUuNSAzLjUgMTUuNSAzLjUgMjEgMHYtN3M5LTQuNSA2LTEwLjVjLTQtNi41LTEzLjUtMy41LTE2IDRWMjd2LTMuNWMtMy41LTcuNS0xMy0xMC41LTE2LTQtMyA2IDUgMTAgNSAxMFYzN3oiIGZpbGw9IiMwMDAiLz48cGF0aCBkPSJNMjAgOGg1IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIi8+PHBhdGggZD0iTTMyIDI5LjVzOC41LTQgNi4wMy05LjY1QzM0LjE1IDE0IDI1IDE4IDIyLjUgMjQuNWwuMDEgMi4xLS4wMS0yLjFDMjAgMTggOS45MDYgMTQgNi45OTcgMTkuODVjLTIuNDk3IDUuNjUgNC44NTMgOSA0Ljg1MyA5IiBzdHJva2U9IiNlY2VjZWMiLz48cGF0aCBkPSJNMTEuNSAzMGM1LjUtMyAxNS41LTMgMjEgMG0tMjEgMy41YzUuNS0zIDE1LjUtMyAyMSAwbS0yMSAzLjVjNS41LTMgMTUuNS0zIDIxIDAiIHN0cm9rZT0iI2VjZWNlYyIvPjwvZz48L3N2Zz4=)}.puzzle-promotion-dialog{z-index:1000;background:#fffffff2;border-radius:8px;flex-direction:column;gap:8px;padding:15px;display:flex;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);box-shadow:0 4px 12px #0003}.puzzle-promotion-dialog button{cursor:pointer;background:#f0f0f0;border:none;border-radius:4px;justify-content:center;align-items:center;gap:8px;width:150px;padding:10px 20px;font-size:16px;display:flex}.puzzle-promotion-dialog button:hover{background:#e0e0e0}.puzzle-promotion-dialog button:before{content:attr(data-piece);font-size:24px}.puzzle-board-wrapper{align-items:center;width:440px;display:flex}.puzzle-ranks-labels{flex-direction:column;justify-content:center;height:400px;margin-right:10px;display:flex}.puzzle-ranks-labels.reversed{flex-direction:column-reverse}.puzzle-ranks-labels div{justify-content:center;align-items:center;height:50px;display:flex}.puzzle-board-and-files{flex-direction:column;display:flex}.puzzle-files-labels{justify-content:center;height:20px;display:flex}.puzzle-files-labels.reversed{flex-direction:row-reverse}.puzzle-files-labels div{justify-content:center;align-items:center;width:50px;display:flex}#cg{width:400px;height:400px}
|
package/dist/assets/index.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
canvas[data-v-f91502f8]{margin-left:auto;margin-right:auto;padding:10px;display:block}img[data-v-f91502f8]{max-width:100%;max-height:60vh;margin-left:auto;margin-right:auto;padding:10px;display:block}.cg-wrap{box-sizing:content-box;display:block;position:relative}cg-container{width:100%;height:100%;display:block;position:absolute;top:0}cg-board{-webkit-user-select:none;user-select:none;background-size:cover;width:100%;height:100%;line-height:0;position:absolute;top:0;left:0}.cg-wrap.manipulable cg-board{cursor:pointer}cg-board square{pointer-events:none;width:12.5%;height:12.5%;position:absolute;top:0;left:0}cg-board square.move-dest{pointer-events:auto}cg-board square.last-move{will-change:transform}.cg-wrap piece{z-index:2;will-change:transform;pointer-events:none;background-size:cover;width:12.5%;height:12.5%;position:absolute;top:0;left:0}cg-board piece.dragging{cursor:move;z-index:11!important}piece.anim{z-index:8}piece.fading{z-index:1;opacity:.5}.cg-wrap piece.ghost{opacity:.3}.cg-wrap piece svg{pointer-events:none;z-index:2;opacity:.6;width:100%;height:100%;position:relative;top:0;left:0;overflow:hidden}.cg-wrap cg-auto-pieces,.cg-wrap .cg-shapes,.cg-wrap .cg-custom-svgs{pointer-events:none;width:100%;height:100%;position:absolute;top:0;left:0;overflow:visible}.cg-wrap cg-auto-pieces{z-index:2}.cg-wrap cg-auto-pieces piece{opacity:.3}.cg-wrap .cg-shapes{opacity:.6;z-index:2;overflow:hidden}.cg-wrap .cg-custom-svgs{z-index:9}.cg-wrap .cg-custom-svgs svg{overflow:visible}.cg-wrap coords{pointer-events:none;opacity:.8;font-family:sans-serif;font-size:9px;display:flex;position:absolute}.cg-wrap coords.ranks{flex-flow:column-reverse;width:12px;height:100%;top:-20px;left:4px}.cg-wrap coords.ranks.black{flex-flow:column}.cg-wrap coords.ranks.left{align-items:flex-end;left:-15px}.cg-wrap coords.files{text-transform:uppercase;text-align:center;flex-flow:row;width:100%;height:16px;bottom:-4px;left:24px}.cg-wrap coords.files.black{flex-flow:row-reverse}.cg-wrap coords coord{flex:auto}.cg-wrap coords.ranks coord{transform:translateY(39%)}.cg-wrap coords.squares{text-transform:uppercase;text-align:right;flex-flow:column-reverse;width:12.5%;height:100%;bottom:0;left:0}.cg-wrap coords.squares.black{flex-flow:column}.cg-wrap coords.squares.left{text-align:left}.cg-wrap coords.squares coord{padding:6% 4%}.cg-wrap coords.squares.rank2{transform:translate(100%)}.cg-wrap coords.squares.rank3{transform:translate(200%)}.cg-wrap coords.squares.rank4{transform:translate(300%)}.cg-wrap coords.squares.rank5{transform:translate(400%)}.cg-wrap coords.squares.rank6{transform:translate(500%)}.cg-wrap coords.squares.rank7{transform:translate(600%)}.cg-wrap coords.squares.rank8{transform:translate(700%)}cg-board{background-color:#f0d9b5;background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4PSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIgogICAgIHZpZXdCb3g9IjAgMCA4IDgiIHNoYXBlLXJlbmRlcmluZz0iY3Jpc3BFZGdlcyI+CjxnIGlkPSJhIj4KICA8ZyBpZD0iYiI+CiAgICA8ZyBpZD0iYyI+CiAgICAgIDxnIGlkPSJkIj4KICAgICAgICA8cmVjdCB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBpZD0iZSIgb3BhY2l0eT0iMCIvPgogICAgICAgIDx1c2UgeD0iMSIgeT0iMSIgaHJlZj0iI2UiIHg6aHJlZj0iI2UiLz4KICAgICAgICA8cmVjdCB5PSIxIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBpZD0iZiIgb3BhY2l0eT0iMC4yIi8+CiAgICAgICAgPHVzZSB4PSIxIiB5PSItMSIgaHJlZj0iI2YiIHg6aHJlZj0iI2YiLz4KICAgICAgPC9nPgogICAgICA8dXNlIHg9IjIiIGhyZWY9IiNkIiB4OmhyZWY9IiNkIi8+CiAgICA8L2c+CiAgICA8dXNlIHg9IjQiIGhyZWY9IiNjIiB4OmhyZWY9IiNjIi8+CiAgPC9nPgogIDx1c2UgeT0iMiIgaHJlZj0iI2IiIHg6aHJlZj0iI2IiLz4KPC9nPgo8dXNlIHk9IjQiIGhyZWY9IiNhIiB4OmhyZWY9IiNhIi8+Cjwvc3ZnPg==)}cg-board square.move-dest{background:radial-gradient(#14551e80 22%,#208530 0,#0000004d 0,#0000 0)}cg-board square.premove-dest{background:radial-gradient(#141e5580 22%,#203085 0,#0000004d 0,#0000 0)}cg-board square.oc.move-dest{background:radial-gradient(#0000 0% 80%,#1455004d 80%)}cg-board square.oc.premove-dest{background:radial-gradient(#0000 0% 80%,#141e5533 80%)}cg-board square.move-dest:hover{background:#14551e4d}cg-board square.premove-dest:hover{background:#141e5533}cg-board square.last-move{background-color:#9bc70069}cg-board square.selected{background-color:#14551e80}cg-board square.check{background:radial-gradient(red 0%,#e70000 25%,#a9000000 89%,#9e000000 100%)}cg-board square.current-premove{background-color:#141e5580}.cg-wrap coords:nth-child(odd) coord:nth-child(2n),.cg-wrap coords.squares:nth-child(2n) coord:nth-child(odd),.cg-wrap.orientation-black coords.files:nth-child(2n) coord:nth-child(odd),.cg-wrap coords.files:nth-child(2n) coord:nth-child(2n){color:#484848cc}.cg-wrap coords:nth-child(odd) coord:nth-child(odd),.cg-wrap coords.squares:nth-child(2n) coord:nth-child(2n),.cg-wrap.orientation-black coords.files:nth-child(2n) coord:nth-child(2n),.cg-wrap coords.files:nth-child(2n) coord:nth-child(odd){color:#fffc}.cg-wrap piece.pawn.cg-white{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PHBhdGggZD0iTTIyLjUgOWMtMi4yMSAwLTQgMS43OS00IDQgMCAuODkuMjkgMS43MS43OCAyLjM4QzE3LjMzIDE2LjUgMTYgMTguNTkgMTYgMjFjMCAyLjAzLjk0IDMuODQgMi40MSA1LjAzLTMgMS4wNi03LjQxIDUuNTUtNy40MSAxMy40N2gyM2MwLTcuOTItNC40MS0xMi40MS03LjQxLTEzLjQ3IDEuNDctMS4xOSAyLjQxLTMgMi40MS01LjAzIDAtMi40MS0xLjMzLTQuNS0zLjI4LTUuNjIuNDktLjY3Ljc4LTEuNDkuNzgtMi4zOCAwLTIuMjEtMS43OS00LTQtNHoiIGZpbGw9IiNmZmYiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjwvc3ZnPg==)}.cg-wrap piece.bishop.cg-white{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxnIGZpbGw9IiNmZmYiIHN0cm9rZS1saW5lY2FwPSJidXR0Ij48cGF0aCBkPSJNOSAzNmMzLjM5LS45NyAxMC4xMS40MyAxMy41LTIgMy4zOSAyLjQzIDEwLjExIDEuMDMgMTMuNSAyIDAgMCAxLjY1LjU0IDMgMi0uNjguOTctMS42NS45OS0zIC41LTMuMzktLjk3LTEwLjExLjQ2LTEzLjUtMS0zLjM5IDEuNDYtMTAuMTEuMDMtMTMuNSAxLTEuMzU0LjQ5LTIuMzIzLjQ3LTMtLjUgMS4zNTQtMS45NCAzLTIgMy0yeiIvPjxwYXRoIGQ9Ik0xNSAzMmMyLjUgMi41IDEyLjUgMi41IDE1IDAgLjUtMS41IDAtMiAwLTIgMC0yLjUtMi41LTQtMi41LTQgNS41LTEuNSA2LTExLjUtNS0xNS41LTExIDQtMTAuNSAxNC01IDE1LjUgMCAwLTIuNSAxLjUtMi41IDQgMCAwLS41LjUgMCAyeiIvPjxwYXRoIGQ9Ik0yNSA4YTIuNSAyLjUgMCAxIDEtNSAwIDIuNSAyLjUgMCAxIDEgNSAweiIvPjwvZz48cGF0aCBkPSJNMTcuNSAyNmgxME0xNSAzMGgxNW0tNy41LTE0LjV2NU0yMCAxOGg1IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIi8+PC9nPjwvc3ZnPg==)}.cg-wrap piece.knight.cg-white{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0yMiAxMGMxMC41IDEgMTYuNSA4IDE2IDI5SDE1YzAtOSAxMC02LjUgOC0yMSIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Ik0yNCAxOGMuMzggMi45MS01LjU1IDcuMzctOCA5LTMgMi0yLjgyIDQuMzQtNSA0LTEuMDQyLS45NCAxLjQxLTMuMDQgMC0zLTEgMCAuMTkgMS4yMy0xIDItMSAwLTQuMDAzIDEtNC00IDAtMiA2LTEyIDYtMTJzMS44OS0xLjkgMi0zLjVjLS43My0uOTk0LS41LTItLjUtMyAxLTEgMyAyLjUgMyAyLjVoMnMuNzgtMS45OTIgMi41LTNjMSAwIDEgMyAxIDMiIGZpbGw9IiNmZmYiLz48cGF0aCBkPSJNOS41IDI1LjVhLjUuNSAwIDEgMS0xIDAgLjUuNSAwIDEgMSAxIDB6bTUuNDMzLTkuNzVhLjUgMS41IDMwIDEgMS0uODY2LS41LjUgMS41IDMwIDEgMSAuODY2LjV6IiBmaWxsPSIjMDAwIi8+PC9nPjwvc3ZnPg==)}.cg-wrap piece.rook.cg-white{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0iI2ZmZiIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik05IDM5aDI3di0zSDl2M3ptMy0zdi00aDIxdjRIMTJ6bS0xLTIyVjloNHYyaDVWOWg1djJoNVY5aDR2NSIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiLz48cGF0aCBkPSJNMzQgMTRsLTMgM0gxNGwtMy0zIi8+PHBhdGggZD0iTTMxIDE3djEyLjVIMTRWMTciIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIi8+PHBhdGggZD0iTTMxIDI5LjVsMS41IDIuNWgtMjBsMS41LTIuNSIvPjxwYXRoIGQ9Ik0xMSAxNGgyMyIgZmlsbD0ibm9uZSIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIvPjwvZz48L3N2Zz4=)}.cg-wrap piece.queen.cg-white{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0iI2ZmZiIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik04IDEyYTIgMiAwIDEgMS00IDAgMiAyIDAgMSAxIDQgMHptMTYuNS00LjVhMiAyIDAgMSAxLTQgMCAyIDIgMCAxIDEgNCAwek00MSAxMmEyIDIgMCAxIDEtNCAwIDIgMiAwIDEgMSA0IDB6TTE2IDguNWEyIDIgMCAxIDEtNCAwIDIgMiAwIDEgMSA0IDB6TTMzIDlhMiAyIDAgMSAxLTQgMCAyIDIgMCAxIDEgNCAweiIvPjxwYXRoIGQ9Ik05IDI2YzguNS0xLjUgMjEtMS41IDI3IDBsMi0xMi03IDExVjExbC01LjUgMTMuNS0zLTE1LTMgMTUtNS41LTE0VjI1TDcgMTRsMiAxMnoiIHN0cm9rZS1saW5lY2FwPSJidXR0Ii8+PHBhdGggZD0iTTkgMjZjMCAyIDEuNSAyIDIuNSA0IDEgMS41IDEgMSAuNSAzLjUtMS41IDEtMS41IDIuNS0xLjUgMi41LTEuNSAxLjUuNSAyLjUuNSAyLjUgNi41IDEgMTYuNSAxIDIzIDAgMCAwIDEuNS0xIDAtMi41IDAgMCAuNS0xLjUtMS0yLjUtLjUtMi41LS41LTIgLjUtMy41IDEtMiAyLjUtMiAyLjUtNC04LjUtMS41LTE4LjUtMS41LTI3IDB6IiBzdHJva2UtbGluZWNhcD0iYnV0dCIvPjxwYXRoIGQ9Ik0xMS41IDMwYzMuNS0xIDE4LjUtMSAyMiAwTTEyIDMzLjVjNi0xIDE1LTEgMjEgMCIgZmlsbD0ibm9uZSIvPjwvZz48L3N2Zz4=)}.cg-wrap piece.king.cg-white{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0yMi41IDExLjYzVjZNMjAgOGg1IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIi8+PHBhdGggZD0iTTIyLjUgMjVzNC41LTcuNSAzLTEwLjVjMCAwLTEtMi41LTMtMi41cy0zIDIuNS0zIDIuNWMtMS41IDMgMyAxMC41IDMgMTAuNSIgZmlsbD0iI2ZmZiIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiLz48cGF0aCBkPSJNMTEuNSAzN2M1LjUgMy41IDE1LjUgMy41IDIxIDB2LTdzOS00LjUgNi0xMC41Yy00LTYuNS0xMy41LTMuNS0xNiA0VjI3di0zLjVjLTMuNS03LjUtMTMtMTAuNS0xNi00LTMgNiA1IDEwIDUgMTBWMzd6IiBmaWxsPSIjZmZmIi8+PHBhdGggZD0iTTExLjUgMzBjNS41LTMgMTUuNS0zIDIxIDBtLTIxIDMuNWM1LjUtMyAxNS41LTMgMjEgMG0tMjEgMy41YzUuNS0zIDE1LjUtMyAyMSAwIi8+PC9nPjwvc3ZnPg==)}.cg-wrap piece.pawn.cg-black{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PHBhdGggZD0iTTIyLjUgOWMtMi4yMSAwLTQgMS43OS00IDQgMCAuODkuMjkgMS43MS43OCAyLjM4QzE3LjMzIDE2LjUgMTYgMTguNTkgMTYgMjFjMCAyLjAzLjk0IDMuODQgMi40MSA1LjAzLTMgMS4wNi03LjQxIDUuNTUtNy40MSAxMy40N2gyM2MwLTcuOTItNC40MS0xMi40MS03LjQxLTEzLjQ3IDEuNDctMS4xOSAyLjQxLTMgMi40MS01LjAzIDAtMi40MS0xLjMzLTQuNS0zLjI4LTUuNjIuNDktLjY3Ljc4LTEuNDkuNzgtMi4zOCAwLTIuMjEtMS43OS00LTQtNHoiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjwvc3ZnPg==)}.cg-wrap piece.bishop.cg-black{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxnIGZpbGw9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJidXR0Ij48cGF0aCBkPSJNOSAzNmMzLjM5LS45NyAxMC4xMS40MyAxMy41LTIgMy4zOSAyLjQzIDEwLjExIDEuMDMgMTMuNSAyIDAgMCAxLjY1LjU0IDMgMi0uNjguOTctMS42NS45OS0zIC41LTMuMzktLjk3LTEwLjExLjQ2LTEzLjUtMS0zLjM5IDEuNDYtMTAuMTEuMDMtMTMuNSAxLTEuMzU0LjQ5LTIuMzIzLjQ3LTMtLjUgMS4zNTQtMS45NCAzLTIgMy0yeiIvPjxwYXRoIGQ9Ik0xNSAzMmMyLjUgMi41IDEyLjUgMi41IDE1IDAgLjUtMS41IDAtMiAwLTIgMC0yLjUtMi41LTQtMi41LTQgNS41LTEuNSA2LTExLjUtNS0xNS41LTExIDQtMTAuNSAxNC01IDE1LjUgMCAwLTIuNSAxLjUtMi41IDQgMCAwLS41LjUgMCAyeiIvPjxwYXRoIGQ9Ik0yNSA4YTIuNSAyLjUgMCAxIDEtNSAwIDIuNSAyLjUgMCAxIDEgNSAweiIvPjwvZz48cGF0aCBkPSJNMTcuNSAyNmgxME0xNSAzMGgxNW0tNy41LTE0LjV2NU0yMCAxOGg1IiBzdHJva2U9IiNlY2VjZWMiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiLz48L2c+PC9zdmc+)}.cg-wrap piece.knight.cg-black{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0yMiAxMGMxMC41IDEgMTYuNSA4IDE2IDI5SDE1YzAtOSAxMC02LjUgOC0yMSIgZmlsbD0iIzAwMCIvPjxwYXRoIGQ9Ik0yNCAxOGMuMzggMi45MS01LjU1IDcuMzctOCA5LTMgMi0yLjgyIDQuMzQtNSA0LTEuMDQyLS45NCAxLjQxLTMuMDQgMC0zLTEgMCAuMTkgMS4yMy0xIDItMSAwLTQuMDAzIDEtNC00IDAtMiA2LTEyIDYtMTJzMS44OS0xLjkgMi0zLjVjLS43My0uOTk0LS41LTItLjUtMyAxLTEgMyAyLjUgMyAyLjVoMnMuNzgtMS45OTIgMi41LTNjMSAwIDEgMyAxIDMiIGZpbGw9IiMwMDAiLz48cGF0aCBkPSJNOS41IDI1LjVhLjUuNSAwIDEgMS0xIDAgLjUuNSAwIDEgMSAxIDB6bTUuNDMzLTkuNzVhLjUgMS41IDMwIDEgMS0uODY2LS41LjUgMS41IDMwIDEgMSAuODY2LjV6IiBmaWxsPSIjZWNlY2VjIiBzdHJva2U9IiNlY2VjZWMiLz48cGF0aCBkPSJNMjQuNTUgMTAuNGwtLjQ1IDEuNDUuNS4xNWMzLjE1IDEgNS42NSAyLjQ5IDcuOSA2Ljc1UzM1Ljc1IDI5LjA2IDM1LjI1IDM5bC0uMDUuNWgyLjI1bC4wNS0uNWMuNS0xMC4wNi0uODgtMTYuODUtMy4yNS0yMS4zNC0yLjM3LTQuNDktNS43OS02LjY0LTkuMTktNy4xNmwtLjUxLS4xeiIgZmlsbD0iI2VjZWNlYyIgc3Ryb2tlPSJub25lIi8+PC9nPjwvc3ZnPg==)}.cg-wrap piece.rook.cg-black{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik05IDM5aDI3di0zSDl2M3ptMy41LTdsMS41LTIuNWgxN2wxLjUgMi41aC0yMHptLS41IDR2LTRoMjF2NEgxMnoiIHN0cm9rZS1saW5lY2FwPSJidXR0Ii8+PHBhdGggZD0iTTE0IDI5LjV2LTEzaDE3djEzSDE0eiIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiLz48cGF0aCBkPSJNMTQgMTYuNUwxMSAxNGgyM2wtMyAyLjVIMTR6TTExIDE0VjloNHYyaDVWOWg1djJoNVY5aDR2NUgxMXoiIHN0cm9rZS1saW5lY2FwPSJidXR0Ii8+PHBhdGggZD0iTTEyIDM1LjVoMjFtLTIwLTRoMTltLTE4LTJoMTdtLTE3LTEzaDE3TTExIDE0aDIzIiBmaWxsPSJub25lIiBzdHJva2U9IiNlY2VjZWMiIHN0cm9rZS13aWR0aD0iMSIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIvPjwvZz48L3N2Zz4=)}.cg-wrap piece.queen.cg-black{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxnIHN0cm9rZT0ibm9uZSI+PGNpcmNsZSBjeD0iNiIgY3k9IjEyIiByPSIyLjc1Ii8+PGNpcmNsZSBjeD0iMTQiIGN5PSI5IiByPSIyLjc1Ii8+PGNpcmNsZSBjeD0iMjIuNSIgY3k9IjgiIHI9IjIuNzUiLz48Y2lyY2xlIGN4PSIzMSIgY3k9IjkiIHI9IjIuNzUiLz48Y2lyY2xlIGN4PSIzOSIgY3k9IjEyIiByPSIyLjc1Ii8+PC9nPjxwYXRoIGQ9Ik05IDI2YzguNS0xLjUgMjEtMS41IDI3IDBsMi41LTEyLjVMMzEgMjVsLS4zLTE0LjEtNS4yIDEzLjYtMy0xNC41LTMgMTQuNS01LjItMTMuNkwxNCAyNSA2LjUgMTMuNSA5IDI2eiIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiLz48cGF0aCBkPSJNOSAyNmMwIDIgMS41IDIgMi41IDQgMSAxLjUgMSAxIC41IDMuNS0xLjUgMS0xLjUgMi41LTEuNSAyLjUtMS41IDEuNS41IDIuNS41IDIuNSA2LjUgMSAxNi41IDEgMjMgMCAwIDAgMS41LTEgMC0yLjUgMCAwIC41LTEuNS0xLTIuNS0uNS0yLjUtLjUtMiAuNS0zLjUgMS0yIDIuNS0yIDIuNS00LTguNS0xLjUtMTguNS0xLjUtMjcgMHoiIHN0cm9rZS1saW5lY2FwPSJidXR0Ii8+PHBhdGggZD0iTTExIDM4LjVhMzUgMzUgMSAwIDAgMjMgMCIgZmlsbD0ibm9uZSIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiLz48cGF0aCBkPSJNMTEgMjlhMzUgMzUgMSAwIDEgMjMgMG0tMjEuNSAyLjVoMjBtLTIxIDNhMzUgMzUgMSAwIDAgMjIgMG0tMjMgM2EzNSAzNSAxIDAgMCAyNCAwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlY2VjZWMiLz48L2c+PC9zdmc+)}.cg-wrap piece.king.cg-black{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NSIgaGVpZ2h0PSI0NSI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0yMi41IDExLjYzVjYiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiLz48cGF0aCBkPSJNMjIuNSAyNXM0LjUtNy41IDMtMTAuNWMwIDAtMS0yLjUtMy0yLjVzLTMgMi41LTMgMi41Yy0xLjUgMyAzIDEwLjUgMyAxMC41IiBmaWxsPSIjMDAwIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIvPjxwYXRoIGQ9Ik0xMS41IDM3YzUuNSAzLjUgMTUuNSAzLjUgMjEgMHYtN3M5LTQuNSA2LTEwLjVjLTQtNi41LTEzLjUtMy41LTE2IDRWMjd2LTMuNWMtMy41LTcuNS0xMy0xMC41LTE2LTQtMyA2IDUgMTAgNSAxMFYzN3oiIGZpbGw9IiMwMDAiLz48cGF0aCBkPSJNMjAgOGg1IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIi8+PHBhdGggZD0iTTMyIDI5LjVzOC41LTQgNi4wMy05LjY1QzM0LjE1IDE0IDI1IDE4IDIyLjUgMjQuNWwuMDEgMi4xLS4wMS0yLjFDMjAgMTggOS45MDYgMTQgNi45OTcgMTkuODVjLTIuNDk3IDUuNjUgNC44NTMgOSA0Ljg1MyA5IiBzdHJva2U9IiNlY2VjZWMiLz48cGF0aCBkPSJNMTEuNSAzMGM1LjUtMyAxNS41LTMgMjEgMG0tMjEgMy41YzUuNS0zIDE1LjUtMyAyMSAwbS0yMSAzLjVjNS41LTMgMTUuNS0zIDIxIDAiIHN0cm9rZT0iI2VjZWNlYyIvPjwvZz48L3N2Zz4=)}.puzzle-promotion-dialog{z-index:1000;background:#fffffff2;border-radius:8px;flex-direction:column;gap:8px;padding:15px;display:flex;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);box-shadow:0 4px 12px #0003}.puzzle-promotion-dialog button{cursor:pointer;background:#f0f0f0;border:none;border-radius:4px;justify-content:center;align-items:center;gap:8px;width:150px;padding:10px 20px;font-size:16px;display:flex}.puzzle-promotion-dialog button:hover{background:#e0e0e0}.puzzle-promotion-dialog button:before{content:attr(data-piece);font-size:24px}.puzzle-board-wrapper{align-items:center;width:440px;display:flex}.puzzle-ranks-labels{flex-direction:column;justify-content:center;height:400px;margin-right:10px;display:flex}.puzzle-ranks-labels.reversed{flex-direction:column-reverse}.puzzle-ranks-labels div{justify-content:center;align-items:center;height:50px;display:flex}.puzzle-board-and-files{flex-direction:column;display:flex}.puzzle-files-labels{justify-content:center;height:20px;display:flex}.puzzle-files-labels.reversed{flex-direction:row-reverse}.puzzle-files-labels div{justify-content:center;align-items:center;width:50px;display:flex}#cg{width:400px;height:400px}#canvas[data-v-57f16e4a]{margin-left:auto;margin-right:auto;display:block}canvas[data-v-4f8ac466]{margin-left:auto;margin-right:auto;padding:10px;display:block}input[data-v-0452c3a5]{text-align:center;border-style:solid;border-width:1px;border-bottom-color:#000;width:3em}.incorrect[data-v-0452c3a5]{box-shadow:0 0 5px #b4005f}.correct[data-v-0452c3a5]{box-shadow:0 0 5px green}svg[data-v-2f79c080]{border:1px}@keyframes progress{0%{width:0%}to{width:100%}}.progressContainer{background-color:#eee;height:5px}#progress{background-color:#00f;width:100%;height:5px;animation-timing-function:linear}.hidden{opacity:0}#abc{width:100%;height:100%}.letter-display[data-v-4ae4db54]{color:#333;text-transform:uppercase;background-color:#f8f9fa;border:2px solid #dee2e6;border-radius:8px;justify-content:center;align-items:center;width:80px;height:80px;margin:20px auto;font-family:monospace;font-size:48px;font-weight:700;display:flex;box-shadow:0 2px #dee2e6,0 3px 3px #0003}.letter-display.pressed[data-v-4ae4db54]{transform:translateY(2px);box-shadow:0 0 #dee2e6,0 1px 2px #0003}.game-container[data-v-41b85e8b]{width:100%;height:400px;position:relative}.game-area[data-v-41b85e8b]{z-index:0;background-color:#87ceeb;border:2px solid #dee2e6;border-radius:8px;width:100%;height:100%;position:relative;overflow:hidden}.grass-container[data-v-41b85e8b]{z-index:3;width:100%;height:40px;position:absolute;bottom:0;left:0;overflow:hidden}.grass[data-v-41b85e8b]{background-color:#4caf50;border-top:2px solid #388e3c;width:100%;height:30px;position:absolute;bottom:0;left:0}.grass-overlay[data-v-41b85e8b]{z-index:3;background:repeating-linear-gradient(80deg,#0000,#0000 5px,#4caf50cc 5px 10px),repeating-linear-gradient(-80deg,#0000,#0000 5px,#4caf50cc 5px 10px);width:100%;height:40px;position:absolute;bottom:0;left:0}.falling-letter[data-v-41b85e8b]{z-index:1;width:40px;height:45px;transition:top 16ms linear;position:absolute}.meteor-effect[data-v-41b85e8b]{background:radial-gradient(circle at 40% 40%,#ff6b6b,#c92a2a);border-radius:50%;width:100%;height:100%;animation:1s linear infinite meteorSpin-41b85e8b;position:absolute;box-shadow:0 0 10px #ff8f8f,0 0 20px #ff6b6b,0 0 30px #c92a2a,0 4px 8px #0006}.meteor-effect[data-v-41b85e8b]:before{content:"";filter:blur(1px);background:linear-gradient(#0000,#ff6b6b66,#ff6b6bcc);width:2px;height:20px;position:absolute;top:-20px;left:50%;transform:translate(-50%)}.meteor-effect[data-v-41b85e8b]:after{content:"";filter:blur(2px);background:radial-gradient(circle,#fffc,#ff8f8f66,#0000);width:15px;height:15px;position:absolute;top:-5px;left:50%;transform:translate(-50%)}.letter-text[data-v-41b85e8b]{color:#fff;text-shadow:0 0 4px #00000080,0 0 8px #0000004d;z-index:1;justify-content:center;align-items:center;width:100%;height:100%;font-size:24px;font-weight:700;display:flex;position:absolute}@keyframes meteorSpin-41b85e8b{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.stats[data-v-41b85e8b]{z-index:1;color:#333;z-index:4;background-color:#fffc;border-radius:4px;padding:5px 10px;font-size:18px;position:absolute;top:10px;left:10px}.game-over[data-v-41b85e8b]{z-index:3;color:#333;z-index:4;background-color:#ffffffe6;border-radius:8px;padding:20px;font-size:36px;font-weight:700;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.time[data-v-41b85e8b],.score[data-v-41b85e8b]{margin:5px 0}.trees[data-v-41b85e8b]{z-index:2;width:100%;height:0;position:absolute;bottom:20px}.tree[data-v-41b85e8b]{transform-origin:bottom;position:absolute;bottom:0}.tree-crown[data-v-41b85e8b]{z-index:2;background:radial-gradient(circle at 50% 25%,#2d5a27,#1a472a);border-radius:50% 50% 20% 20%;width:30px;height:40px;position:absolute;bottom:15px;left:50%;transform:translate(-50%);box-shadow:-2px 3px #1a472a,2px 3px #1a472a}.tree-trunk[data-v-41b85e8b]{z-index:2;background:linear-gradient(90deg,#3e2723 20%,#5d4037 50%,#3e2723 80%);border-radius:2px;width:6px;height:15px;position:absolute;bottom:0;left:50%;transform:translate(-50%)}.grass[data-v-41b85e8b],.grass-overlay[data-v-41b85e8b]{z-index:3}.explosion[data-v-41b85e8b]{pointer-events:none;z-index:2;width:40px;height:40px;position:absolute}.particle[data-v-41b85e8b]{background:linear-gradient(90deg,#ff8f8f,#ff6b6b);border-radius:50%;width:4px;height:4px;animation:.3s ease-out forwards explode-41b85e8b;position:absolute;top:50%;left:50%;box-shadow:0 0 10px #ff6b6b,0 0 20px #ff8f8f}@keyframes explode-41b85e8b{0%{opacity:1;transform:translate(-50%,-50%)scale(1)}to{opacity:0;transform:translate(-50%,-50%)scale(0)rotate(45deg)}}.particle[data-v-41b85e8b]:first-child{transform:rotate(0)translate(20px)}.particle[data-v-41b85e8b]:nth-child(2){transform:rotate(30deg)translate(20px)}.particle[data-v-41b85e8b]:nth-child(3){transform:rotate(60deg)translate(20px)}.particle[data-v-41b85e8b]:nth-child(4){transform:rotate(90deg)translate(20px)}.particle[data-v-41b85e8b]:nth-child(5){transform:rotate(120deg)translate(20px)}.particle[data-v-41b85e8b]:nth-child(6){transform:rotate(150deg)translate(20px)}.particle[data-v-41b85e8b]:nth-child(7){transform:rotate(180deg)translate(20px)}.particle[data-v-41b85e8b]:nth-child(8){transform:rotate(210deg)translate(20px)}.particle[data-v-41b85e8b]:nth-child(9){transform:rotate(240deg)translate(20px)}.particle[data-v-41b85e8b]:nth-child(10){transform:rotate(270deg)translate(20px)}.particle[data-v-41b85e8b]:nth-child(11){transform:rotate(300deg)translate(20px)}.particle[data-v-41b85e8b]:nth-child(12){transform:rotate(330deg)translate(20px)}.piano-range-container[data-v-4b232a8b]{margin:20px 0}.piano-wrapper[data-v-4b232a8b]{width:100%;margin:10px 0;overflow-x:hidden}.piano-svg[data-v-4b232a8b]{width:100%;height:auto;display:block}.white-key[data-v-4b232a8b]{fill:#fff}.black-key[data-v-4b232a8b]{fill:#333}.white-key.active[data-v-4b232a8b]{fill:#e6f2ff}.black-key.active[data-v-4b232a8b]{fill:#1a1a1a}.range-marker[data-v-4b232a8b]{fill:#ff5252}.note-label[data-v-4b232a8b]{fill:#333;font-size:12px;font-weight:700}.save-button[data-v-6ee9020f]{opacity:1;transition:opacity .5s ease-out}.save-button.v-btn--disabled[data-v-6ee9020f]:not(.v-btn--loading){opacity:0}
|
|
1
|
+
.piano-range-container[data-v-4b232a8b]{margin:20px 0}.piano-wrapper[data-v-4b232a8b]{width:100%;margin:10px 0;overflow-x:hidden}.piano-svg[data-v-4b232a8b]{width:100%;height:auto;display:block}.white-key[data-v-4b232a8b]{fill:#fff}.black-key[data-v-4b232a8b]{fill:#333}.white-key.active[data-v-4b232a8b]{fill:#e6f2ff}.black-key.active[data-v-4b232a8b]{fill:#1a1a1a}.range-marker[data-v-4b232a8b]{fill:#ff5252}.note-label[data-v-4b232a8b]{fill:#333;font-size:12px;font-weight:700}.save-button[data-v-6ee9020f]{opacity:1;transition:opacity .5s ease-out}.save-button.v-btn--disabled[data-v-6ee9020f]:not(.v-btn--loading){opacity:0}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#canvas[data-v-57f16e4a]{margin-left:auto;margin-right:auto;display:block}canvas[data-v-4f8ac466]{margin-left:auto;margin-right:auto;padding:10px;display:block}input[data-v-0452c3a5]{text-align:center;border-style:solid;border-width:1px;border-bottom-color:#000;width:3em}.incorrect[data-v-0452c3a5]{box-shadow:0 0 5px #b4005f}.correct[data-v-0452c3a5]{box-shadow:0 0 5px green}
|