@learnpack/learnpack 5.0.363 → 5.0.365
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/lib/commands/serve.js +482 -286
- package/lib/models/creator.d.ts +1 -0
- package/lib/utils/continueGeneration.d.ts +65 -0
- package/lib/utils/continueGeneration.js +82 -0
- package/lib/utils/generationRetry.d.ts +48 -0
- package/lib/utils/generationRetry.js +81 -0
- package/lib/utils/interactivity/componentsGuide.d.ts +50 -0
- package/lib/utils/interactivity/componentsGuide.js +193 -0
- package/lib/utils/interactivity/interactivityPlan.d.ts +20 -0
- package/lib/utils/interactivity/interactivityPlan.js +33 -0
- package/lib/utils/interactivity/structuredComponents.d.ts +34 -0
- package/lib/utils/interactivity/structuredComponents.js +792 -0
- package/lib/utils/rigoActions.d.ts +5 -2
- package/lib/utils/rigoActions.js +5 -3
- package/package.json +1 -1
- package/src/commands/serve.ts +720 -408
- package/src/models/creator.ts +87 -84
- package/src/ui/_app/app.js +130 -72
- package/src/ui/app.tar.gz +0 -0
- package/src/utils/continueGeneration.ts +108 -0
- package/src/utils/generationRetry.ts +100 -0
- package/src/utils/interactivity/componentsGuide.ts +294 -0
- package/src/utils/interactivity/interactivityPlan.ts +57 -0
- package/src/utils/interactivity/structuredComponents.ts +1004 -0
- package/src/utils/rigoActions.ts +8 -3
package/src/ui/_app/app.js
CHANGED
|
@@ -131,23 +131,39 @@ Error generating stack: `+i.message+`
|
|
|
131
131
|
components:\r
|
|
132
132
|
- name: open_text_question\r
|
|
133
133
|
description: >-\r
|
|
134
|
-
The open text question is a type of interactive question where users can write down a text answer. The component consists in a markdown code block with the language set to \`question\` and has a metadata property eval in the metadata section of the code block with instructions on how to evaluate the answer. Inside the code block, you must write down 3-5 example answers with CORRECT: or INCORRECT: prefix. The
|
|
134
|
+
The open text question is a type of interactive question where users can write down a text answer. The component consists in a markdown code block with the language set to \`question\` and has a metadata property eval in the metadata section of the code block with instructions on how to evaluate the answer. Inside the code block, you must write down 3-5 example answers with CORRECT: or INCORRECT: prefix. The question must be written before the code block, because the learner sees neither the eval nor the example answers. It must include everything needed to answer: the situation or the text to work on.\r
|
|
135
135
|
avoid:\r
|
|
136
136
|
- multiple choice\r
|
|
137
137
|
- true/false\r
|
|
138
138
|
- fill in the blank\r
|
|
139
139
|
- this component is intended only for free form answers\r
|
|
140
140
|
- never use for visual questions\r
|
|
141
|
-
|
|
141
|
+
goodFor:
|
|
142
|
+
- explaining a concept in the learner's own words
|
|
143
|
+
- applying a concept by producing a short text
|
|
144
|
+
- writing or rewriting a short piece, such as a prompt, a reply or a paraphrase
|
|
145
|
+
- justifying a decision against clear criteria
|
|
146
|
+
props:
|
|
142
147
|
- eval: string (required)\r
|
|
143
|
-
example:
|
|
144
|
-
###
|
|
145
|
-
|
|
146
|
-
\`\`\`question eval="The
|
|
147
|
-
CORRECT:
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
example: |
|
|
149
|
+
### In your own words, what is a variable expense?
|
|
150
|
+
|
|
151
|
+
\`\`\`question eval="The answer must explain, in the learner's own words, that a variable expense changes from month to month"
|
|
152
|
+
CORRECT: It is money you spend that is not the same every month, like groceries.
|
|
153
|
+
CORRECT: An expense whose amount changes depending on what you use or buy.
|
|
154
|
+
INCORRECT: It is a payment that stays the same every month.
|
|
155
|
+
\`\`\`
|
|
156
|
+
|
|
157
|
+
--- Second Example ---
|
|
158
|
+
|
|
159
|
+
### Write a prompt asking an AI assistant to summarize a customer's complaint email in three bullet points for your supervisor.
|
|
160
|
+
|
|
161
|
+
\`\`\`question eval="The prompt must state the task, the text to work on, the three bullet point format and the supervisor as the audience"
|
|
162
|
+
CORRECT: Summarize the customer complaint email below in three bullet points for my supervisor, covering the problem and what the customer asks for.
|
|
163
|
+
CORRECT: I need to brief my supervisor. Read this complaint email and write a summary of three bullet points.
|
|
164
|
+
INCORRECT: Summarize this.
|
|
165
|
+
INCORRECT: Write something about customer complaints.
|
|
166
|
+
\`\`\`
|
|
151
167
|
rules:\r
|
|
152
168
|
- MUST only require a textual answer that can be evaluated without images.\r
|
|
153
169
|
- MUST NOT ask the learner to draw, sketch, diagram, circle, shade, underline, point, annotate, mark on paper, or \`visualize and show\`\r
|
|
@@ -156,6 +172,7 @@ components:\r
|
|
|
156
172
|
- Answers can be as short as 1 word or as long as a full paragraph.\r
|
|
157
173
|
- Only use for textual easy to evaluate questions\r
|
|
158
174
|
- Never use this component for reflection questions, only for knowledge assessment questions.\r
|
|
175
|
+
- Every CORRECT example must meet the eval.\r
|
|
159
176
|
\r
|
|
160
177
|
- name: multiple_choice_question\r
|
|
161
178
|
intendedUse:\r
|
|
@@ -182,47 +199,56 @@ components:\r
|
|
|
182
199
|
rules:\r
|
|
183
200
|
- Avoid questions with multiple correct answers. Each question must have an unique correct answer.\r
|
|
184
201
|
- Avoid questions with too many options. Each question should have between 3 and 5 options.\r
|
|
185
|
-
|
|
186
|
-
-
|
|
187
|
-
-
|
|
188
|
-
-
|
|
189
|
-
-
|
|
202
|
+
- The question text must not contain the correct answer or repeat an option.\r
|
|
203
|
+
- Incorrect options must be plausible and as specific as the correct one.\r
|
|
204
|
+
- The question always goes in the numbered line, never in the heading above it. A heading is optional and is only a short title.\r
|
|
205
|
+
- Number only multiple choice questions. Headings and other components are never numbered, and the numbering restarts at 1 after any other component.\r
|
|
206
|
+
- Each option holds its full text on its own checkbox line. Never write the answers as numbered lines with checkboxes such as - [x] 1 below them.\r
|
|
207
|
+
goodFor:
|
|
208
|
+
- recall
|
|
209
|
+
- judging a situation\r
|
|
190
210
|
\r
|
|
191
|
-
example:
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
211
|
+
example: |
|
|
212
|
+
### Put your knowledge to the test!
|
|
213
|
+
|
|
214
|
+
1. Which expense is variable?
|
|
215
|
+
|
|
216
|
+
- [ ] Monthly rent
|
|
217
|
+
- [x] Groceries
|
|
218
|
+
- [ ] Phone plan
|
|
219
|
+
|
|
220
|
+
2. What should you list first when building a budget?
|
|
221
|
+
|
|
222
|
+
- [ ] Your savings goal
|
|
223
|
+
- [ ] Your largest expense
|
|
224
|
+
- [x] Your monthly income
|
|
225
|
+
|
|
226
|
+
--- Second Example (a step that mixes components: each one has its own ### title, and only the multiple choice questions are numbered) ---
|
|
227
|
+
|
|
228
|
+
### Check your basics
|
|
229
|
+
|
|
230
|
+
1. Which expense is variable?
|
|
231
|
+
|
|
232
|
+
- [ ] Monthly rent
|
|
233
|
+
- [x] Groceries
|
|
234
|
+
- [ ] Phone plan
|
|
235
|
+
|
|
236
|
+
### Complete the sentences
|
|
237
|
+
|
|
238
|
+
\`\`\`select_the_blank 1="fixed|variable|annual" 2="income|expense|saving" 3="budget|receipt|invoice"
|
|
239
|
+
Rent is a _1_ expense.
|
|
240
|
+
A monthly salary counts as _2_.
|
|
241
|
+
A plan for how money will be used is called a _3_.
|
|
242
|
+
\`\`\`
|
|
243
|
+
|
|
244
|
+
### Choose the best option
|
|
245
|
+
|
|
246
|
+
1. What should you list first when building a budget?
|
|
247
|
+
|
|
248
|
+
- [ ] Your largest expense
|
|
249
|
+
- [x] Your monthly income
|
|
250
|
+
- [ ] Your savings goal
|
|
251
|
+
|
|
226
252
|
- name: select_the_blank\r
|
|
227
253
|
measures:\r
|
|
228
254
|
- rubric: content_mastery\r
|
|
@@ -232,30 +258,41 @@ components:\r
|
|
|
232
258
|
- rubric: application_problem_solving\r
|
|
233
259
|
weight: 0.15\r
|
|
234
260
|
description: >-\r
|
|
235
|
-
A select the blank is a component where the user completes a sentence by choosing the correct option from a dropdown for each blank, instead of typing free text. Because the user picks from a predefined list, grading is fully deterministic: the user can never fail due to typos, punctuation, casing or incomplete answers, and the available options give the learner guidance. It consists in a markdown code block with the language set to \`select_the_blank\`. Inside the code block, you must write the question with the blank (or blanks) to be filled. Each blank consists in _1_, _2_, _3_, etc. In the metadata section, for each blank you must provide the list of options separated by the pipe character \`|\`. IMPORTANT: the FIRST option of each blank is the correct one. You MUST provide at least 3 options per blank: 1 correct answer and at least 2 plausible distractors. You can also use select the blank for numerical questions.\r
|
|
261
|
+
Use it to complete sentences with key terms or categories of a few words. It is not for whole sentences or explanations: use open_text_question for those. A select the blank is a component where the user completes a sentence by choosing the correct option from a dropdown for each blank, instead of typing free text. Because the user picks from a predefined list, grading is fully deterministic: the user can never fail due to typos, punctuation, casing or incomplete answers, and the available options give the learner guidance. It consists in a markdown code block with the language set to \`select_the_blank\`. Inside the code block, you must write the question with the blank (or blanks) to be filled. Each blank consists in _1_, _2_, _3_, etc. In the metadata section, for each blank you must provide the list of options separated by the pipe character \`|\`. IMPORTANT: the FIRST option of each blank is the correct one. You MUST provide at least 3 options per blank: 1 correct answer and at least 2 plausible distractors. You can also use select the blank for numerical questions.\r
|
|
236
262
|
rules:\r
|
|
237
263
|
- The FIRST option of each blank is always the correct answer; the rest are distractors.\r
|
|
238
264
|
- Each blank MUST have at least 3 options (1 correct + at least 2 distractors).\r
|
|
239
265
|
- Options are separated by the pipe character \`|\`, NOT by commas (commas can be part of an option's text).\r
|
|
240
266
|
- Distractors must be plausible and related to the topic, never obviously wrong.\r
|
|
241
|
-
-
|
|
267
|
+
- Each option is a term or short phrase of at most four words, never a whole sentence.\r
|
|
268
|
+
- The sentences go inside the code block and must be understandable on their own.\r
|
|
269
|
+
- Every number in the metadata must appear in the text as _1_, _2_, etc.; a block without blanks renders nothing.\r
|
|
270
|
+
- Use at least 3 blanks in total: with fewer, the exercise is too easy.\r
|
|
242
271
|
- READ-BACK CHECK - When the correct option is inserted into its blank, the WHOLE sentence MUST be grammatically correct and factually true. Re-read each completed sentence to verify before finalizing.\r
|
|
243
272
|
- UNIQUENESS - Each blank must have exactly ONE correct option given the surrounding text, and that text must contain enough information to identify it unambiguously. NEVER write two blanks whose surrounding text is identical or interchangeable (e.g. the same description reused for two different answers).\r
|
|
244
273
|
- DISTRACTORS MUST BE WRONG IN CONTEXT - Each distractor, if placed in the blank, MUST make the sentence false or wrong in that specific context. If a distractor would also be a valid answer, the sentence is missing disambiguating information; add it.\r
|
|
245
274
|
- CONSISTENT FRAMING - What the learner selects (the blank) must be exactly what the instruction asks them to identify. Do not place the blank where a different part of speech belongs (e.g. asking to pick a method but leaving the blank where a position noun like "end"/"beginning" should go).\r
|
|
275
|
+
goodFor:
|
|
276
|
+
- recalling key terms
|
|
277
|
+
- classifying something into categories
|
|
278
|
+
- completing a definition or a rule
|
|
279
|
+
- numerical answers
|
|
246
280
|
avoid:\r
|
|
247
281
|
- free text answers (use open_text_question for those)\r
|
|
248
282
|
- blanks with fewer than 3 options\r
|
|
283
|
+
- a blank with only its correct answer, such as 1="fixed", because the learner has nothing to choose\r
|
|
249
284
|
- never use for visual questions\r
|
|
250
285
|
- blanks where two or more options would be equally valid given the surrounding text\r
|
|
251
286
|
- sentences that read incorrectly once the correct option is inserted\r
|
|
252
287
|
example: |\r
|
|
253
|
-
###
|
|
254
|
-
|
|
255
|
-
\`\`\`select_the_blank 1="
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
288
|
+
### Complete the sentences
|
|
289
|
+
|
|
290
|
+
\`\`\`select_the_blank 1="fixed|variable|annual" 2="variable|fixed|annual" 3="income|expense|saving" 4="budget|receipt|invoice"
|
|
291
|
+
Rent is a _1_ expense, while groceries are a _2_ expense.
|
|
292
|
+
A monthly salary counts as _3_.
|
|
293
|
+
A plan for how money will be used in a month is called a _4_.
|
|
294
|
+
\`\`\`
|
|
295
|
+
|
|
259
296
|
--- Second Example ---\r
|
|
260
297
|
\r
|
|
261
298
|
### Are you really learning? Let's check it!\r
|
|
@@ -287,15 +324,23 @@ components:\r
|
|
|
287
324
|
- rubric: critical_thinking\r
|
|
288
325
|
weight: 0.15\r
|
|
289
326
|
description: >-\r
|
|
290
|
-
An ordering component asks the user to arrange a set of items into the correct sequence. It is ideal for processes, step-by-step procedures
|
|
327
|
+
An ordering component asks the user to arrange a set of items into the correct sequence. It is ideal for processes, step-by-step procedures or chronological events (for example: the lifecycle of an HTTP request, the steps of a git workflow, the steps of an installation process, the steps of returning a purchase, etc). Grading is fully deterministic: the user's order is compared position by position with the correct one. It consists in a markdown code block with the language set to \`ordering\`. Inside the code block, write one item per line, IN THE CORRECT ORDER (the renderer shuffles them before showing them to the user, and the user reorders them with up/down arrows). You can optionally add a \`title\` in the metadata to describe what must be ordered.\r
|
|
291
328
|
rules:\r
|
|
292
329
|
- Write the items in the CORRECT order inside the code block; the renderer shuffles them automatically.\r
|
|
293
330
|
- Write exactly one item per line. Items may optionally start with a number or a list marker (it is stripped automatically).\r
|
|
294
331
|
- Use between 3 and 6 items; fewer than 3 makes the exercise trivial, more than 6 becomes tedious.\r
|
|
295
|
-
- There must be a single, unambiguous correct order. Do not use it when several orders could be valid.\r
|
|
332
|
+
- There must be a single, unambiguous correct order: swapping any two items must make the sequence wrong. Do not use it when several orders could be valid.\r
|
|
296
333
|
- Keep each item short and self-contained.\r
|
|
334
|
+
- Use it only for a sequence the lesson teaches as steps, never for categories, priorities, opinions or tips.\r
|
|
335
|
+
- No item may reveal its position.\r
|
|
336
|
+
goodFor:
|
|
337
|
+
- the steps of a procedure
|
|
338
|
+
- the stages of a process
|
|
339
|
+
- events in time
|
|
297
340
|
avoid:\r
|
|
298
341
|
- sets where more than one order is acceptable\r
|
|
342
|
+
- actions or attitudes that happen at the same time\r
|
|
343
|
+
- lists of tips, qualities or good practices, even if the lesson names them one after another\r
|
|
299
344
|
- fewer than 3 items\r
|
|
300
345
|
- never use for visual questions\r
|
|
301
346
|
example: |\r
|
|
@@ -317,6 +362,17 @@ components:\r
|
|
|
317
362
|
git commit -m "Save changes"\r
|
|
318
363
|
git push\r
|
|
319
364
|
\`\`\`\r
|
|
365
|
+
\r
|
|
366
|
+
--- Third Example ---\r
|
|
367
|
+
\r
|
|
368
|
+
### Let's get your money back!\r
|
|
369
|
+
\r
|
|
370
|
+
\`\`\`ordering title="Order the steps to return an online purchase"\r
|
|
371
|
+
Request the return on the website\r
|
|
372
|
+
Pack the item\r
|
|
373
|
+
Drop the box at the pickup point\r
|
|
374
|
+
Check that the refund arrived\r
|
|
375
|
+
\`\`\`\r
|
|
320
376
|
\r
|
|
321
377
|
- name: code_challenge_proposal\r
|
|
322
378
|
intendedUse:\r
|
|
@@ -330,6 +386,9 @@ components:\r
|
|
|
330
386
|
rules:\r
|
|
331
387
|
- The main file must be always be named app.extension, where extension is the extension of the file, for example, app.js, app.html, app.css, etc.\r
|
|
332
388
|
- Keep challenges simple and easy to understand for the student, it must be related to the lesson content and the exercise instructions.\r
|
|
389
|
+
goodFor:
|
|
390
|
+
- practicing a programming concept by writing code
|
|
391
|
+
- tasks that need a code editor
|
|
333
392
|
avoid:\r
|
|
334
393
|
- complex code that might not be inferred correctly by an AI model\r
|
|
335
394
|
example: |\r
|
|
@@ -353,12 +412,12 @@ components:\r
|
|
|
353
412
|
- process explanation\r
|
|
354
413
|
- system architecture\r
|
|
355
414
|
- data flow\r
|
|
356
|
-
whenToUse: Use it
|
|
415
|
+
whenToUse: Use it for processes, sequences and decisions that read better as a diagram.\r
|
|
357
416
|
avoid:\r
|
|
358
417
|
- complex diagrams that might not render well\r
|
|
359
|
-
- use for simple to medium complexity diagrams\r
|
|
360
418
|
rules:\r
|
|
361
419
|
- NEVER under ANY circumstances use or write parentheses inside [] because it will break the mermaid syntax and won't render properly\r
|
|
420
|
+
- Keep diagrams simple to medium in complexity.\r
|
|
362
421
|
example: |\r
|
|
363
422
|
\`\`\`mermaid\r
|
|
364
423
|
graph TD\r
|
|
@@ -378,10 +437,10 @@ components:\r
|
|
|
378
437
|
- providing hints\r
|
|
379
438
|
- AI assistance\r
|
|
380
439
|
- learning support\r
|
|
440
|
+
- exploring a concept or applying it to the learner's own situation\r
|
|
381
441
|
avoid:\r
|
|
382
442
|
- overuse\r
|
|
383
443
|
- should be used sparingly to encourage independent thinking\r
|
|
384
|
-
usage: Automatically appears in quiz and test components, no manual configuration needed\r
|
|
385
444
|
example: |\r
|
|
386
445
|
[Ask Rigo how to declare a variable in JavaScript](https://4geeks.com/ask?query=can-you-explain-me-in-simple-terms-how-to-declare-a-variable-in-javascript)\r
|
|
387
446
|
\r
|
|
@@ -427,10 +486,10 @@ components:\r
|
|
|
427
486
|
- name: conceptual_illustration\r
|
|
428
487
|
intendedUse:\r
|
|
429
488
|
- abstract concept visualization\r
|
|
430
|
-
- metaphorical representation of
|
|
489
|
+
- metaphorical representation of ideas\r
|
|
431
490
|
- simplified educational illustrations\r
|
|
432
491
|
description: >-\r
|
|
433
|
-
Conceptual illustrations transform abstract
|
|
492
|
+
Conceptual illustrations transform abstract concepts into visual metaphors\r
|
|
434
493
|
and simplified representations. They use friendly, approachable aesthetics with flat\r
|
|
435
494
|
colors and minimal details to make complex ideas accessible. Focus on clarity and\r
|
|
436
495
|
educational value through visual metaphors.\r
|
|
@@ -439,7 +498,7 @@ components:\r
|
|
|
439
498
|
aesthetic, vector art style with smooth shapes, focus on clarity over realism,\r
|
|
440
499
|
educational and welcoming visual language\r
|
|
441
500
|
goodFor:\r
|
|
442
|
-
- abstract
|
|
501
|
+
- abstract concepts\r
|
|
443
502
|
- memory models and heap/stack visualization\r
|
|
444
503
|
- conceptual relationships between ideas\r
|
|
445
504
|
- visual metaphors for complex topics\r
|
|
@@ -453,13 +512,11 @@ components:\r
|
|
|
453
512
|
rules:\r
|
|
454
513
|
- Must use standard markdown image syntax => \r
|
|
455
514
|
- Alt text must contain at least 30 words describing the concept and visual metaphor\r
|
|
456
|
-
- Must specify visual elements, e.g. "shown as", "represented by", "color-coded to differentiate"\r
|
|
457
515
|
- Image URL must start with "./.learn/assets/"\r
|
|
458
516
|
- Should simplify complex ideas into basic visual metaphors\r
|
|
459
|
-
-
|
|
460
|
-
- Include annotations that bridge the metaphor to the actual concept\r
|
|
517
|
+
- Do not include text unless the image cannot be understood without it: no titles, captions, quotes or dialogue.\r
|
|
461
518
|
example: |\r
|
|
462
|
-
\r
|
|
463
520
|
\r
|
|
464
521
|
- name: realistic_photography\r
|
|
465
522
|
intendedUse:\r
|
|
@@ -497,8 +554,9 @@ components:\r
|
|
|
497
554
|
- Should include context that explains the educational relevance\r
|
|
498
555
|
- If showing UI/screens, specify what elements to highlight or annotate\r
|
|
499
556
|
- Maintain professional, clean aesthetic appropriate for learning\r
|
|
557
|
+
- Do not include text unless the image cannot be understood without it: no titles, captions, quotes or dialogue.\r
|
|
500
558
|
example: |\r
|
|
501
|
-
\r
|
|
502
560
|
`,Y5=!1,fa="";function Xp(e,t){return e==="localStorage"||e==="scorm"||e==="creatorWeb"&&t!=="creator"}function Ku(e){return e==="localStorage"||e==="scorm"||e==="creatorWeb"}const jvt="https://ai.4geeks.com";function i9e(e){Array.isArray(e)||(e=[e]);const t=new URLSearchParams(window.location.search);let r={};return e.forEach(a=>r[a]=t.get(a)),e.length==1?r[e[0]]:r}const qvt=location.protocol+"//"+location.hostname+(location.port?":"+location.port:""),Iv=function(){const e=window.location.pathname.endsWith("/config/index.html");let t=i9e("config");t&&t!==""&&(t=JSON.parse(atob(t)));let r=t?`${t.address}:${t.port}`:i9e("host")||qvt;return e&&(r=window.location.href.replace("/config/index.html","")),r};let Zp="localhost";const o9e=async()=>{var r;const e=Iv();if(window.location.pathname.endsWith("/config/index.html"))try{const n=await fetch(`${e}/.learn/config.json`);if(n.ok)return await n.json(),Zp="scorm",document.dispatchEvent(new CustomEvent("environment-change",{detail:{environment:"scorm"}})),"scorm"}catch(n){console.error("SCORM config fetch failed, falling back to legacy detection",n)}try{const n=Jr(),a=await fetch(`${e}/config?slug=${n}`),i=(r=a.headers.get("Content-Type"))==null?void 0:r.includes("application/json");if(a.ok&&i){let o="localhost";a.headers.get("X-Creator-Web")&&(o="creatorWeb"),Zp=o;const l=new CustomEvent("environment-change",{detail:{environment:o}});return document.dispatchEvent(l),o}else throw Error("The response was unsuccessful")}catch{Zp="localStorage";try{const a=await fetch(`${e}/config.json`);if(!a.ok)throw new Error("config.json not found");await a.json();const i=new CustomEvent("environment-change",{detail:{environment:"localStorage"}});return document.dispatchEvent(i),"localStorage"}catch{try{const i=await fetch(`${e}/.learn/config.json`);if(!i.ok)throw new Error(".learn/config.json not found");await i.json(),Zp="scorm";const o=new CustomEvent("environment-change",{detail:{environment:"scorm"}});return document.dispatchEvent(o),"scorm"}catch(i){return console.error("Error fetching scorm config, impossible to detect environment",yr(i)),"localStorage"}}}};o9e();const di="https://rigobot.herokuapp.com",s9e=3e4,Vvt=2e3,Gvt=3,Qvt=1e3,Wvt=3,Rv="https://breathecode.herokuapp.com",Yvt=()=>{const e=document.querySelector(".sidebar-component");e&&(window.getComputedStyle(e).left==="0px"?(e.classList.remove("sidebar-appear"),e.classList.add("sidebar-disappear")):(e==null||e.classList.remove("sidebar-disappear"),e==null||e.classList.add("sidebar-appear")),e==null||e.offsetWidth)},Kvt=async e=>await fetch(`${Iv()}/exercise/${e}`),Xvt=async(e,t)=>{const r=di+"/v1/conversation/?purpose="+e,n={method:"POST",headers:{"Content-Type":"application/json",Authorization:"Token "+t}};return await(await fetch(r,n)).json()},l9e=()=>{const e=document.querySelector("#socket-disconnected");e&&(e.style.display="block")},Zvt=()=>{const e=document.querySelector("#socket-disconnected");e&&(e.style.display="none")},Jvt=()=>{const e=new URLSearchParams(window.location.search);let t={};for(const[r,n]of e.entries())t[r]=n;return t},PC=()=>Jvt(),Ol=(e,t)=>{let r,n=null;const a=(...i)=>{n=i,clearTimeout(r),r=setTimeout(()=>{n=null,e(...i)},t)};return a.cancel=()=>{clearTimeout(r),n=null},a.flush=()=>{n!==null&&(clearTimeout(r),e(...n),n=null)},a},c9e=e=>e.replace(/\x1B[@-_][0-?]*[ -/]*[@-~]/g,""),u9e=(e,t,r)=>{const n=new RegExp(t,"g");return e.replace(n,r)},d9e=(e,t,r)=>{let n=u9e(e,"{{publicUrl}}",t);if(r&&typeof r=="object")for(const[a,i]of Object.entries(r))n=u9e(n,`{{${a}}}`,i);return n},Z1=e=>{const t=new URLSearchParams;Object.entries(e).forEach(([a,i])=>{i!=null&&i!==""&&t.set(a,String(i))});const r=t.toString(),n=r?`${window.location.pathname}?${r}${window.location.hash}`:`${window.location.pathname}${window.location.hash}`;window.history.replaceState(null,"",n)};function fce(e,t){return Math.random()*(t-e)+e}class $C extends Error{constructor(t){super(t),this.name="MissingRigobotAccountError"}}class h9e extends Error{constructor(t){super(t),this.name="TokenExpiredError"}}const pce=(e,t="ai-conversation-message")=>{const r=e.voids.find(n=>n.slug===t);return r?r.balance.unit:0},K5=async e=>{const r=new TextEncoder().encode(e),n=await crypto.subtle.digest("SHA-256",r);return Array.from(new Uint8Array(n)).map(o=>o.toString(16).padStart(2,"0")).join("")};async function vh(e,t=2e3){const r=Date.now()-e;r<t&&await new Promise(n=>setTimeout(n,t-r))}const e4t=e=>{const t=new URL(window.location.href);t.searchParams.delete(e);const r=new URLSearchParams(t.hash.slice(1));r.delete(e),r.toString()?t.hash=`#${r.toString()}`:t.hash="",window.history.replaceState({},"",t.toString())},f9e=e=>{Xle.dataLayer(e)},Xu=(e,t)=>`---
|
|
503
561
|
`+a9e(e)+`---
|
|
504
562
|
|
|
@@ -753,7 +811,7 @@ ${t}</body>`,n):`
|
|
|
753
811
|
`+e.slice(o+1):l+=e.slice(a),l.slice(1)}function Rwt(e){for(var t="",r,n,a,i=0;i<e.length;i++){if(r=e.charCodeAt(i),r>=55296&&r<=56319&&(n=e.charCodeAt(i+1),n>=56320&&n<=57343)){t+=IEe((r-55296)*1024+n-56320+65536),i++;continue}a=jo[r],t+=!a&&jv(r)?e[i]:a||IEe(r)}return t}function Bwt(e,t,r){var n="",a=e.tag,i,o;for(i=0,o=r.length;i<o;i+=1)r0(e,t,r[i],!1,!1)&&(i!==0&&(n+=","+(e.condenseFlow?"":" ")),n+=e.dump);e.tag=a,e.dump="["+n+"]"}function Owt(e,t,r,n){var a="",i=e.tag,o,s;for(o=0,s=r.length;o<s;o+=1)r0(e,t+1,r[o],!0,!0)&&((!n||o!==0)&&(a+=qce(e,t)),e.dump&&dw===e.dump.charCodeAt(0)?a+="-":a+="- ",a+=e.dump);e.tag=i,e.dump=a||"[]"}function Dwt(e,t,r){var n="",a=e.tag,i=Object.keys(r),o,s,l,u,d;for(o=0,s=i.length;o<s;o+=1)d="",o!==0&&(d+=", "),e.condenseFlow&&(d+='"'),l=i[o],u=r[l],r0(e,t,l,!1,!1)&&(e.dump.length>1024&&(d+="? "),d+=e.dump+(e.condenseFlow?'"':"")+":"+(e.condenseFlow?"":" "),r0(e,t,u,!1,!1)&&(d+=e.dump,n+=d));e.tag=a,e.dump="{"+n+"}"}function Fwt(e,t,r,n){var a="",i=e.tag,o=Object.keys(r),s,l,u,d,h,f;if(e.sortKeys===!0)o.sort();else if(typeof e.sortKeys=="function")o.sort(e.sortKeys);else if(e.sortKeys)throw new uw("sortKeys must be a boolean or a function");for(s=0,l=o.length;s<l;s+=1)f="",(!n||s!==0)&&(f+=qce(e,t)),u=o[s],d=r[u],r0(e,t+1,u,!0,!0,!0)&&(h=e.tag!==null&&e.tag!=="?"||e.dump&&e.dump.length>1024,h&&(e.dump&&dw===e.dump.charCodeAt(0)?f+="?":f+="? "),f+=e.dump,h&&(f+=qce(e,t)),r0(e,t+1,d,!0,h)&&(e.dump&&dw===e.dump.charCodeAt(0)?f+=":":f+=": ",f+=e.dump,a+=f));e.tag=i,e.dump=a||"{}"}function jEe(e,t,r){var n,a,i,o,s,l;for(a=r?e.explicitTypes:e.implicitTypes,i=0,o=a.length;i<o;i+=1)if(s=a[i],(s.instanceOf||s.predicate)&&(!s.instanceOf||typeof t=="object"&&t instanceof s.instanceOf)&&(!s.predicate||s.predicate(t))){if(e.tag=r?s.tag:"?",s.represent){if(l=e.styleMap[s.tag]||s.defaultStyle,xEe.call(s.represent)==="[object Function]")n=s.represent(t,l);else if(_Ee.call(s.represent,l))n=s.represent[l](t,l);else throw new uw("!<"+s.tag+'> tag resolver accepts not "'+l+'" style');e.dump=n}return!0}return!1}function r0(e,t,r,n,a,i){e.tag=null,e.dump=r,jEe(e,r,!1)||jEe(e,r,!0);var o=xEe.call(e.dump);n&&(n=e.flowLevel<0||e.flowLevel>t);var s=o==="[object Object]"||o==="[object Array]",l,u;if(s&&(l=e.duplicates.indexOf(r),u=l!==-1),(e.tag!==null&&e.tag!=="?"||u||e.indent!==2&&t>0)&&(a=!1),u&&e.usedDuplicates[l])e.dump="*ref_"+l;else{if(s&&u&&!e.usedDuplicates[l]&&(e.usedDuplicates[l]=!0),o==="[object Object]")n&&Object.keys(e.dump).length!==0?(Fwt(e,t,e.dump,a),u&&(e.dump="&ref_"+l+e.dump)):(Dwt(e,t,e.dump),u&&(e.dump="&ref_"+l+" "+e.dump));else if(o==="[object Array]"){var d=e.noArrayIndent&&t>0?t-1:t;n&&e.dump.length!==0?(Owt(e,d,e.dump,a),u&&(e.dump="&ref_"+l+e.dump)):(Bwt(e,d,e.dump),u&&(e.dump="&ref_"+l+" "+e.dump))}else if(o==="[object String]")e.tag!=="?"&&Mwt(e,e.dump,t,i);else{if(e.skipInvalid)return!1;throw new uw("unacceptable kind of an object to dump "+o)}e.tag!==null&&e.tag!=="?"&&(e.dump="!<"+e.tag+"> "+e.dump)}return!0}function Pwt(e,t){var r=[],n=[],a,i;for(Gce(e,r,n),a=0,i=n.length;a<i;a+=1)t.duplicates.push(r[n[a]]);t.usedDuplicates=new Array(i)}function Gce(e,t,r){var n,a,i;if(e!==null&&typeof e=="object")if(a=t.indexOf(e),a!==-1)r.indexOf(a)===-1&&r.push(a);else if(t.push(e),Array.isArray(e))for(a=0,i=e.length;a<i;a+=1)Gce(e[a],t,r);else for(n=Object.keys(e),a=0,i=n.length;a<i;a+=1)Gce(e[n[a]],t,r)}function qEe(e,t){t=t||{};var r=new Ewt(t);return r.noRefs||Pwt(e,r),r0(r,0,e,!0,!0)?r.dump+`
|
|
754
812
|
`:""}function $wt(e,t){return qEe(e,cw.extend({schema:swt},t))}jce.dump=qEe,jce.safeDump=$wt;var c9=iw,VEe=jce;function u9(e){return function(){throw new Error("Function "+e+" is deprecated and cannot be used.")}}ja.Type=to,ja.Schema=Pv,ja.FAILSAFE_SCHEMA=Pce,ja.JSON_SCHEMA=J9e,ja.CORE_SCHEMA=eEe,ja.DEFAULT_SAFE_SCHEMA=lw,ja.DEFAULT_FULL_SCHEMA=n9,ja.load=c9.load,ja.loadAll=c9.loadAll,ja.safeLoad=c9.safeLoad,ja.safeLoadAll=c9.safeLoadAll,ja.dump=VEe.dump,ja.safeDump=VEe.safeDump,ja.YAMLException=sw,ja.MINIMAL_SCHEMA=Pce,ja.SAFE_SCHEMA=lw,ja.DEFAULT_SCHEMA=n9,ja.scan=u9("scan"),ja.parse=u9("parse"),ja.compose=u9("compose"),ja.addConstructor=u9("addConstructor");var Uwt=ja,zwt=Uwt,GEe=zwt,Hwt="\\ufeff?",jwt=typeof process<"u"?process.platform:"",qwt="^("+Hwt+"(= yaml =|---)$([\\s\\S]*?)^(?:\\2|\\.\\.\\.)\\s*$"+(jwt==="win32"?"\\r?":"")+"(?:\\n)?)",QEe=new RegExp(qwt,"m");Oce.exports=Vwt,Oce.exports.test=Wwt;function Vwt(e,t){e=e||"";var r={allowUnsafe:!1};t=t instanceof Object?{...r,...t}:r,t.allowUnsafe=!!t.allowUnsafe;var n=e.split(/(\r?\n)/);return n[0]&&/= yaml =|---/.test(n[0])?Qwt(e,t.allowUnsafe):{attributes:{},body:e,bodyBegin:1}}function Gwt(e,t){for(var r=1,n=t.indexOf(`
|
|
755
813
|
`),a=e.index+e[0].length;n!==-1;){if(n>=a)return r;r++,n=t.indexOf(`
|
|
756
|
-
`,n+1)}return r}function Qwt(e,t){var r=QEe.exec(e);if(!r)return{attributes:{},body:e,bodyBegin:1};var n=t?GEe.load:GEe.safeLoad,a=r[r.length-1].replace(/^\s+|\s+$/g,""),i=n(a)||{},o=e.replace(r[0],""),s=Gwt(r,e);return{attributes:i,body:o,bodyBegin:s,frontmatter:a}}function Wwt(e){return e=e||"",QEe.test(e)}var Ywt=Oce.exports;const WEe=La(Ywt),Kwt=1;class Xwt{constructor(){hr(this,"name","engagement_indicator")}calculateStepIndicator(t,r){return r.status!=="skipped"&&r.status!=="unread"?Math.min(r.time_spent/3600*100,100):0}calculateGlobalIndicator(t,r){const n={total_time:.3,num_sessions:.2,completion_rate:.3,interactions:.2},a=Math.min(r.total_time_on_platform/36e3,1)*100,i=Math.min(r.num_sessions/5,1)*100,o=r.completion_rate,s=Math.min(r.total_interactions/100,1)*100;return(a*n.total_time+i*n.num_sessions+o*n.completion_rate+s*n.interactions)/100*100}}class Zwt{constructor(){hr(this,"name","frustration_indicator")}calculateStepIndicator(t,r){return r.status==="completed"?Math.min((r.comp_struggles+r.test_struggles)/20*100,100):r.status==="attempted"?Math.min(r.time_spent/1800*100,100):r.status==="skipped"?25:0}calculateGlobalIndicator(t,r){const n={steps_not_completed:.3,time_on_incomplete:.2,avg_comp_struggles:.2,avg_test_struggles:.2,skipped_steps:.1},a=r.total_steps>0?r.steps_not_completed/r.total_steps*100:0,i=Math.min(r.time_on_incomplete/18e3,1)*100,o=Math.min(r.avg_comp_struggles/10,1)*100,s=Math.min(r.avg_test_struggles/10,1)*100,l=r.total_steps>0?r.skipped_steps/r.total_steps*100:0;return(a*n.steps_not_completed+i*n.time_on_incomplete+o*n.avg_comp_struggles+s*n.avg_test_struggles+l*n.skipped_steps)/100*100}}function YEe(e){var x,w,C,S,N,L,M,B,F,I,O,P,D,R,U,$;let t=0;e.sessions&&e.sessions.length>0&&(t=e.sessions.reduce((z,q)=>z+q,0));let r=t;!t&&e.completed_at&&e.opened_at&&(r=e.completed_at-e.opened_at,t=r);let n=e.completed_at?"completed":e.opened_at?"attempted":"unread";const a=((x=e.compilations)==null?void 0:x.filter(z=>z.exit_code!==0).length)||0,i=((w=e.compilations)==null?void 0:w.filter(z=>z.exit_code===0).length)||0,o=((C=e.tests)==null?void 0:C.filter(z=>z.exit_code!==0).length)||0,s=((S=e.tests)==null?void 0:S.filter(z=>z.exit_code===0).length)||0,l=((N=e.quiz_submissions)==null?void 0:N.filter(z=>z.status==="ERROR").length)||0,u=((L=e.quiz_submissions)==null?void 0:L.filter(z=>z.status==="SUCCESS").length)||0;let d=0,h=0;for(let z=((M=e.compilations)==null?void 0:M.length)-1;z>=0&&e.compilations[z].exit_code!==0;z--)d++;for(let z=((B=e.compilations)==null?void 0:B.length)-1;z>=0&&e.compilations[z].exit_code===0;z--)h++;let f=0,p=0;for(let z=((F=e.tests)==null?void 0:F.length)-1;z>=0&&e.tests[z].exit_code!==0;z--)f++;for(let z=((I=e.tests)==null?void 0:I.length)-1;z>=0&&e.tests[z].exit_code===0;z--)p++;let g=0,A=0;for(let z=((O=e.quiz_submissions)==null?void 0:O.length)-1;z>=0&&e.quiz_submissions[z].status==="ERROR";z--)g++;for(let z=((P=e.quiz_submissions)==null?void 0:P.length)-1;z>=0&&e.quiz_submissions[z].status==="SUCCESS";z--)A++;const m=e.is_testeable||e.testeable_elements&&e.testeable_elements.length>0,y=(((D=e.quiz_submissions)==null?void 0:D.length)||0)===0&&(((R=e.tests)==null?void 0:R.length)||0)===0&&(((U=e.compilations)==null?void 0:U.length)||0)===0,v=!!m&&y,b=(($=e.ai_interactions)==null?void 0:$.length)||0,_=a+o+l>0&&i+s+u===0;return{status:n,time_spent:r,total_duration:t,comp_struggles:a,comp_success:i,total_comp_attempts:i+a,test_struggles:o,test_success:s,total_test_attempts:s+o,quiz_struggles:l,quiz_success:u,total_quiz_attempts:u+l,streak_comp_struggles:d,streak_comp_success:h,streak_test_struggle:f,streak_test_success:p,streak_quiz_struggles:g,streak_quiz_success:A,user_skipped:v,total_ai_interactions:b,ai_used:b>0,is_abandoned:_}}function Jwt(e){const t=e.length,r=e.map(YEe),n=r.filter(g=>g.status==="completed").length,a=r.filter(g=>g.status==="attempted").length,i=r.filter(g=>g.status==="skipped").length,o=r.filter(g=>g.status==="unread").length,s=t?n/t*100:0,l=e.reduce((g,A)=>{var m,y,v,b;return g+(((m=A.compilations)==null?void 0:m.length)||0)+(((y=A.tests)==null?void 0:y.length)||0)+(((v=A.quiz_submissions)==null?void 0:v.length)||0)+(((b=A.ai_interactions)==null?void 0:b.length)||0)},0),u=r.reduce((g,A)=>g+A.time_spent,0),d=a+i+o,h=t?r.reduce((g,A)=>g+A.comp_struggles,0)/t:0,f=t?r.reduce((g,A)=>g+A.test_struggles,0)/t:0;let p=0;for(const g of e){let A=0;for(const m of g.tests||[])m.exit_code===0?A++:A=0,p=Math.max(p,A)}return{total_time_on_platform:u,num_sessions:1,completion_rate:s,total_interactions:l,steps_not_completed:d,time_on_incomplete:u,avg_comp_struggles:h,avg_test_struggles:f,skipped_steps:i,total_steps:t,global_test_success_streak:p}}function qv(e){const t=[new Xwt,new Zwt],r=e.steps.map(o=>YEe(o)),n=Jwt(e.steps),a=e.steps.map((o,s)=>{const l=r[s],u={engagement_indicator:0,frustration_indicator:0};return t.forEach(d=>{u[d.name]=d.calculateStepIndicator(o,l)}),{slug:o.slug,metrics:l,indicators:u}}),i={engagement_indicator:0,frustration_indicator:0};return t.forEach(o=>{i[o.name]=o.calculateGlobalIndicator(a.map(s=>s.indicators[o.name]),n)}),{algo_version:Kwt,global:{metrics:n,indicators:i},steps:a}}function ekt(e,t){var d,h,f,p;if(!e.testeable_elements)return;const r=e.testeable_elements.find(g=>g.hash===t);if(!r)return;const n=r.type==="quiz"?((d=e.quiz_submissions.find(g=>g.quiz_hash===t))==null?void 0:d.ended_at)||e.opened_at||0:((h=e.tests.shift())==null?void 0:h.started_at)||e.opened_at||0,a=r.type==="quiz"?((f=e.quiz_submissions.findLast(g=>g.quiz_hash===t))==null?void 0:f.ended_at)||e.completed_at||1:((p=e.tests.shift())==null?void 0:p.started_at)||e.completed_at||1,i=Number((a-n)/1e3),o=e.ai_interactions.filter(g=>g.started_at>=n&&g.ended_at<=a).length,s=r.type==="quiz"?e.quiz_submissions.filter(g=>g.quiz_hash===t).length:e.tests.length,l=r.type==="quiz"?e.quiz_submissions.filter(g=>g.quiz_hash===t&&g.status==="ERROR").length:e.tests.filter(g=>g.exit_code!==1).length,u=r.type==="quiz"?e.quiz_submissions.filter(g=>g.quiz_hash===t&&g.status==="SUCCESS").length:e.tests.filter(g=>g.exit_code===1).length;return{started_at:n,ended_at:a,resolution_time_seconds:i,n_ai_interactions:o,n_tries:s,n_fail:l,n_success:u}}const KEe={name:"learnpack-ai",private:!0,version:"5.0.414",scripts:{dev:"node src/utils/development.js --turn on && vite",build:"node src/utils/development.js --turn off && tsc && vite build && node src/utils/uploadLearnpack.js && node bin/publish.js && node src/utils/createVersionsFile.js ","dev-build":"node src/utils/development.js --turn on && tsc && vite build",lint:"eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",preview:"vite preview",watch:"vite build --watch","compile-command":"tsc src/utils/development.ts && node src/utils/development.js --turn on"},dependencies:{"@dnd-kit/core":"^6.3.1","@dnd-kit/sortable":"^10.0.0","@dnd-kit/utilities":"^3.2.2","@monaco-editor/react":"^4.6.0","@radix-ui/react-tooltip":"^1.2.8",axios:"^1.7.7","class-variance-authority":"^0.7.1",clsx:"^2.1.1",dompurify:"^3.3.0","front-matter":"^4.0.2","highlight.js":"^11.11.1",html2canvas:"^1.4.1",i18next:"^23.10.1","js-yaml":"^4.1.0","lucide-react":"^0.545.0",mermaid:"^11.6.0",mitt:"^3.0.1","monaco-editor":"^0.50.0","prompt-sync":"^4.2.0","pusher-js":"7.6.0","radix-ui":"^1.4.3",react:"^18.2.0","react-dom":"^18.2.0","react-draggable":"^4.4.6","react-gtm-module":"^2.0.11","react-hot-toast":"^2.4.1","react-i18next":"^14.1.0","react-markdown":"^9.0.1","react-monaco-editor":"^0.56.1","react-router-dom":"^6.27.0","react-syntax-highlighter":"^15.6.1","rehype-katex":"^7.0.1","remark-emoji":"^5.0.1","remark-gfm":"^4.0.0","remark-math":"^6.0.0","socket.io":"^4.7.2","socket.io-client":"^4.7.2","tailwind-merge":"^3.3.1","tailwindcss-animate":"^1.0.7",uuid:"^10.0.0",zustand:"^4.4.1"},devDependencies:{"@aws-sdk/client-cloudfront":"^3.840.0","@aws-sdk/client-s3":"^3.709.0","@types/dompurify":"^3.0.5","@types/js-yaml":"^4.0.9","@types/markdown-it":"^14.1.2","@types/node":"^24.9.1","@types/react":"^18.2.15","@types/react-dom":"^18.2.7","@types/react-gtm-module":"^2.0.3","@types/react-syntax-highlighter":"^15.5.13","@types/remarkable":"^2.0.3","@types/uuid":"^10.0.0","@typescript-eslint/eslint-plugin":"^6.0.0","@typescript-eslint/parser":"^6.0.0","@vitejs/plugin-react-swc":"^3.3.2",archiver:"^6.0.1",autoprefixer:"^10.4.21",dotenv:"^16.4.7",eslint:"^8.45.0","eslint-plugin-react-hooks":"^4.6.0","eslint-plugin-react-refresh":"^0.4.3",inquirer:"^9.2.12",postcss:"^8.5.6",tailwindcss:"^3.4.18",typescript:"^5.0.2",vite:"^4.5.13"}};function tkt(e){return{all:e=e||new Map,on:function(t,r){var n=e.get(t);n?n.push(r):e.set(t,[r])},off:function(t,r){var n=e.get(t);n&&(r?n.splice(n.indexOf(r)>>>0,1):e.set(t,[]))},emit:function(t,r){var n=e.get(t);n&&n.slice().map(function(a){a(r)}),(n=e.get("*"))&&n.slice().map(function(a){a(t,r)})}}}const _r=tkt(),Qce=async(e,t)=>{if(!t)throw new Error("Slug is required");const r=`${di}/v1/learnpack/session/?slug=${t}`;try{return(await Hr.get(r,{headers:{Authorization:`Token ${e}`}})).data}catch(n){throw console.error("Error fetching session:",yr(n)),n}},Wce=async(e,t,r,n,a)=>{const i=`${di}/v1/learnpack/session/${a}/`,o={tab_hash:t,config_json:n,package_slug:r};try{return(await Hr.put(i,o,{headers:{Authorization:`Token ${e}`,"Content-Type":"application/json"}})).data}catch(s){throw console.error("Error updating session:",yr(s)),s}},rkt=async(e,t,r,n)=>{const a=`${di}/v1/learnpack/session/`,i={tab_hash:t,config_json:n,package_slug:r};try{return(await Hr.post(a,i,{headers:{Authorization:`Token ${e}`,"Content-Type":"application/json"}})).data}catch(o){throw console.error("Error creating session:",yr(o)),o}};async function nkt(e){const t=`${Rv}/v1/payments/me/service/consumable?virtual=true`,r={Authorization:`Token ${e}`};try{return(await Hr.get(t,{headers:r})).data}catch(n){throw console.error("Error fetching consumables:",yr(n)),n}}async function akt(e,t="ai-conversation-message"){const r=`${Rv}/v1/payments/me/service/${t}/consumptionsession`,n={Authorization:`Token ${e}`};try{const a=await Hr.put(r,{},{headers:n});return a.status>=200&&a.status<300?(a.data,`${t}`,!0):(console.error(`Request failed with status code: ${a.status}`),console.error(`Response: ${a.data}`),!1)}catch(a){return console.error(`Error consuming ${t}:`,yr(a)),!1}}function ikt(e){if(!Array.isArray(e))return[];const t=[];for(const r of e){const n=typeof r=="number"?r:Number(r);Number.isFinite(n)&&t.push(n)}return t}async function Yce(e,t){var a;const r={id:null,assetIds:[]};if(!(e!=null&&e.trim())||!t)return r;const n=`${di}/v1/learnpack/package/${encodeURIComponent(t)}/assets/`;try{const o=(await Hr.get(n,{headers:{Authorization:`Token ${e.trim()}`}})).data;return{id:(o==null?void 0:o.id)!=null?String(o.id):null,assetIds:ikt(o==null?void 0:o.asset_ids)}}catch(i){return Hr.isAxiosError(i)&&((a=i.response)==null?void 0:a.status)===404||console.warn("fetchLearnpackPackageInfo failed:",yr(i)),r}}const okt=async(e,t)=>{var a;const r=`${di}/v1/learnpack/package/${t}/`,n={Authorization:`Token ${e}`};try{const i=await Hr.get(r,{headers:n});return{isAuthor:i.status===200,status:i.status}}catch(i){const o=((a=i==null?void 0:i.response)==null?void 0:a.status)||500;return console.error("Error fetching package:",yr(i)),{isAuthor:!1,status:o}}},skt=async(e,t,r=di)=>{const n=await Hr.get(`${r}/v1/prompting/completion/${t}/`,{headers:{Authorization:`Token ${e}`,"Content-Type":"application/json"}});return n.data,n.data};function lkt(e,t){if(t.length===0)return e;const r=e.includes("?")?"&":"?";return`${e}${r}asset_id=${t.join(",")}`}const ckt=async function(e,t,r,n){if(!e||!r){console.error("URL and token are required");return}r=r.trim();const a={"Content-Type":"application/json",Authorization:`Token ${r}`},i=lkt(e,n??[]);try{return(await Hr.post(i,t,{headers:a})).data}catch(o){throw console.error("Error while sending batch telemetry",yr(o)),o}},ukt=async function(e,t){const r=`${di}/v1/learnpack/telemetry`,n={"Content-Type":"application/json",Authorization:`Token ${t}`};try{return(await Hr.post(r,e,{headers:n})).data}catch(a){throw console.error("Error while sending batch telemetry",yr(a)),a}},dkt=5e3,hkt=6e4,fkt=5*60*1e3;async function XEe(e){var u;const{userId:t,packageSlug:r,rigoToken:n}=e;if(!t||!n||!r)return null;const a=n.trim(),i=new URLSearchParams;i.set("user_ids",String(t)),i.set("include_buffer","true"),i.set("include_steps","true"),i.set("package_slug",r);const o=`${di}/v1/learnpack/telemetry?${i.toString()}`,s=new AbortController,l=setTimeout(()=>s.abort(),dkt);try{const d=await fetch(o,{method:"GET",headers:{Authorization:`Token ${a}`,Accept:"application/json"},signal:s.signal});if(!d.ok)return console.warn("fetchTelemetryFromServer: non-OK response",d.status),null;const h=await d.json(),f=(u=h==null?void 0:h.results)==null?void 0:u[0];return!f||typeof f!="object"?null:f}catch(d){return d.name==="AbortError"?console.warn("fetchTelemetryFromServer: timeout or aborted"):console.warn("fetchTelemetryFromServer: failed",yr(d)),null}finally{clearTimeout(l)}}async function pkt(e){var u;const{userId:t,packageSlug:r,rigoToken:n}=e;if(!t||!n||!r)return{status:"empty"};const a=n.trim(),i=new URLSearchParams;i.set("user_ids",String(t)),i.set("include_buffer","true"),i.set("include_steps","true"),i.set("package_slug",r);const o=`${di}/v1/learnpack/telemetry?${i.toString()}`,s=new AbortController,l=setTimeout(()=>s.abort(),hkt);try{const d=await fetch(o,{method:"GET",headers:{Authorization:`Token ${a}`,Accept:"application/json"},signal:s.signal});if(!d.ok)return console.warn("fetchTelemetryWithStatus: non-OK response",d.status),{status:"server_error",code:d.status};const h=await d.json(),f=(u=h==null?void 0:h.results)==null?void 0:u[0];return!f||typeof f!="object"?{status:"empty"}:{status:"ok",data:f}}catch(d){return d.name==="AbortError"?(console.warn("fetchTelemetryWithStatus: timeout"),{status:"timeout"}):(console.warn("fetchTelemetryWithStatus: network error",yr(d)),{status:"timeout"})}finally{clearTimeout(l)}}function ZEe(e){return!e||typeof e.last_interaction_at!="number"?0:e.last_interaction_at}function d9(){return Math.random().toString(36).slice(2,10)+Math.random().toString(36).slice(2,10)}function JEe(e,t){const r=new Map;for(const n of t)n.slug&&r.set(n.slug,n);return e.map(n=>{var o,s,l,u,d,h,f,p,g,A;const a=r.get(n.slug);if(!a)return n;const i={...n};if(!i.completed_at&&a.completed_at&&(i.completed_at=a.completed_at),!i.is_completed&&a.is_completed&&(i.is_completed=!0),!i.opened_at&&a.opened_at&&(i.opened_at=a.opened_at),(o=a.testeable_elements)!=null&&o.length){const m=new Map((i.testeable_elements??[]).map(v=>[v.hash,v])),y=[...i.testeable_elements??[]];for(const v of a.testeable_elements){const b=m.get(v.hash);if(!b)y.push(v);else if(v.is_completed&&!b.is_completed){const _=y.findIndex(x=>x.hash===v.hash);_!==-1&&(y[_]={...b,is_completed:!0,metrics:b.metrics??v.metrics})}}i.testeable_elements=y}if((s=a.quiz_submissions)!=null&&s.length){const m=new Set((i.quiz_submissions??[]).map(v=>v.quiz_hash)),y=a.quiz_submissions.filter(v=>!m.has(v.quiz_hash));y.length>0&&(i.quiz_submissions=[...i.quiz_submissions??[],...y])}return!((l=i.tests)!=null&&l.length)&&((u=a.tests)!=null&&u.length)&&(i.tests=[...a.tests]),!((d=i.compilations)!=null&&d.length)&&((h=a.compilations)!=null&&h.length)&&(i.compilations=[...a.compilations]),!((f=i.ai_interactions)!=null&&f.length)&&((p=a.ai_interactions)!=null&&p.length)&&(i.ai_interactions=[...a.ai_interactions]),!((g=i.sessions)!=null&&g.length)&&((A=a.sessions)!=null&&A.length)&&(i.sessions=[...a.sessions]),i})}function gkt(e,t,r,n,a,i){const o=!!e,s=t&&t.slug===n;if(o&&!s)return{telemetry:n0({...e},r,n,a,i),source:"server"};if(!o&&s)return{telemetry:n0({...t},r,n,a,i),source:"local"};if(o&&s){const u=ZEe(e),d=ZEe(t),[h,f,p]=d>u?[t,e,"local"]:[e,t,"server"],g=n0({...h},r,n,a,i);return g.steps=JEe(g.steps,f.steps??[]),{telemetry:g,source:p}}const l=Date.now();return{telemetry:n0({telemetry_id:d9(),slug:n,version:`${i}`,agent:a,tutorial_started_at:l,last_interaction_at:l,steps:r,workout_session:[{started_at:l}],fullname:"",cohort_id:null,academy_id:null},r,n,a,i),source:"new"}}function Akt(e,t,r){if(!e||!r)return;const n=r.trim();fetch(e,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Token ${n}`},body:JSON.stringify(t),keepalive:!0}).catch(()=>{})}const mkt=async function(e,t,r){!e||!r||fetch(e,{method:"POST",body:JSON.stringify(t)}).then(n=>n).catch(n=>{console.error("Error while sending stream Telemetry",yr(n))})},ykt=async function(){try{return(await Hr.get(`${fa}/telemetry`)).data}catch(e){return console.error("Error while retrieving telemetry from CLI",yr(e)),null}},vkt=async function(e){try{const t=await Hr.post(`${fa}/telemetry`,e);return t.data,t.data}catch(t){return console.error("Error while saving telemetry in CLI",yr(t)),null}};function Kce(e){if(typeof e!="string")throw new TypeError("Input must be a string");return btoa(encodeURIComponent(e))}const bkt=(e,t)=>{let r={...t};return typeof r.starting_at<"u"&&typeof r.started_at>"u"&&(r.started_at=r.starting_at,delete r.starting_at),typeof r.ending_at<"u"&&typeof r.ended_at>"u"&&(r.ended_at=r.ending_at,delete r.ending_at),typeof r.started_at>"u"&&e!=="open_step"&&(r.started_at=Date.now()),typeof r.ended_at>"u"&&e!=="open_step"&&(r.ended_at=Date.now()),r.source_code&&(r.source_code=Kce(r.source_code)),r.stdout&&(r.stdout=Kce(r.stdout)),r.stderr&&(r.stderr=Kce(r.stderr)),Object.prototype.hasOwnProperty.call(r,"exitCode")&&(r.exit_code=r.exitCode,r.exitCode=void 0),r},wkt=["telemetry_id","user_id","fullname","slug","package_id","version","cohort_id","academy_id","agent","tutorial_started_at","last_interaction_at","steps","workout_session","global_metrics","global_indicators"];function eSe(e){return e==null||typeof e=="string"&&e.trim()===""?null:typeof e=="number"&&!Number.isNaN(e)?String(e):typeof e=="string"?e:null}function kkt(e,t){if(!Array.isArray(e))return[{started_at:t}];if(e.length===0)return[{started_at:t}];const r=e.filter(n=>!!n&&typeof n=="object"&&typeof n.started_at=="number");return r.length===0?[{started_at:t}]:r}function xkt(e,t){const r=new Map;for(const n of t)n.slug&&r.set(n.slug,n);return e.map(n=>{const a=r.get(n.slug);return a?{slug:n.slug,position:n.position,files:n.files,is_testeable:n.is_testeable,compilations:a.compilations??[],tests:a.tests??[],ai_interactions:a.ai_interactions??[],quiz_submissions:a.quiz_submissions??[],testeable_elements:a.testeable_elements??[],is_completed:a.is_completed??!1,completed_at:a.completed_at,opened_at:a.opened_at,sessions:a.sessions??[]}:n})}function n0(e,t,r,n,a){const i=e,o={};for(const g of wkt)Object.prototype.hasOwnProperty.call(i,g)&&(o[g]=i[g]);const s=Date.now(),l=r,u=typeof o.version=="string"&&o.version.trim()!==""?o.version:`${a}`,d=typeof o.tutorial_started_at=="number"?o.tutorial_started_at:s,h=typeof o.last_interaction_at=="number"?o.last_interaction_at:s,f=Array.isArray(o.steps)&&o.steps.length>0?xkt(t,o.steps):t,p=kkt(o.workout_session,s);return{telemetry_id:o.telemetry_id,user_id:o.user_id,fullname:o.fullname,slug:l,package_id:o.package_id,version:u,cohort_id:eSe(o.cohort_id),academy_id:eSe(o.academy_id),agent:typeof o.agent=="string"?o.agent:n,tutorial_started_at:d,last_interaction_at:h,steps:f,workout_session:p,global_metrics:o.global_metrics,global_indicators:o.global_indicators}}function tSe(e,t){e.telemetry_id||(e.telemetry_id=d9());const r=qv(e),n=e.steps.map((a,i)=>{const o=e.telemetry_id||d9();return{...a,telemetry_id:o,metrics:r.steps[i].metrics,indicators:r.steps[i].indicators}});return{...e,steps:n,global_metrics:r.global.metrics,global_indicators:r.global.indicators,user_id:t}}const cr={current:null,urls:{},telemetryKey:"",packageAssetIds:[],listeners:{},agent:"cloud",prevStep:void 0,prevStepStartedAt:void 0,activeHashes:new Map,currentReadyAt:void 0,prunedCompletions:new Set,nonTesteableSlugs:new Set,_lessonRenderedDebounce:null,user:{token:"",rigo_token:"",id:"",fullname:""},tutorialSlug:"",started:!1,reconciling:!1,version:`${KEe.version}`||"CLOUD:0.0.0",salute:e=>{},start:function(e,t,r,n,a,i){return this.packageAssetIds=[],this.telemetryKey=n,this.tutorialSlug=r,this.agent=e,this.user.id=a.user_id,this.user.token=a.token,this.user.rigo_token=a.rigo_token,this.user.fullname=a.fullname,this.current?Promise.resolve():(this.activeHashes=new Map,this.prunedCompletions=new Set,this.currentReadyAt=void 0,e==="cloud"?(this.reconciling=!0,(async()=>{var s;let o=!1;try{const l=jr.get(this.telemetryKey),u=String(l==null?void 0:l.user_id)===String(a==null?void 0:a.user_id),d=l&&l.slug===r&&u?l:null,h=!!((s=a.rigo_token)!=null&&s.trim()&&r),f=i!==void 0,[p,g]=await Promise.all([f?Promise.resolve(i):a.rigo_token&&a.user_id?XEe({userId:a.user_id,packageSlug:r,rigoToken:a.rigo_token}):Promise.resolve(null),h?Yce(a.rigo_token,r):Promise.resolve({id:null,assetIds:[]})]);this.packageAssetIds=g.assetIds;const{telemetry:A,source:m}=gkt(p,d,t,r,e,this.version);if(this.current=A,this.currentReadyAt=Date.now(),this.current.user_id=a.user_id,this.current.fullname=a.fullname,this.current.cohort_id=a.cohort_id||null,this.current.academy_id=a.academy_id||null,this.current.version||(this.current.version=`CLOUD:${this.version}`),g.id!=null&&this.mergePackageIdIfMissing(g.id),this.finishWorkoutSession(),this.save(),this.started=!0,!a.user_id){console.warn("No user ID found, impossible to submit telemetry at start");return}o=m==="local"}catch(l){throw console.error("There was a problem starting the Telemetry",yr(l)),new Error(`There was a problem starting, reload LearnPack
|
|
814
|
+
`,n+1)}return r}function Qwt(e,t){var r=QEe.exec(e);if(!r)return{attributes:{},body:e,bodyBegin:1};var n=t?GEe.load:GEe.safeLoad,a=r[r.length-1].replace(/^\s+|\s+$/g,""),i=n(a)||{},o=e.replace(r[0],""),s=Gwt(r,e);return{attributes:i,body:o,bodyBegin:s,frontmatter:a}}function Wwt(e){return e=e||"",QEe.test(e)}var Ywt=Oce.exports;const WEe=La(Ywt),Kwt=1;class Xwt{constructor(){hr(this,"name","engagement_indicator")}calculateStepIndicator(t,r){return r.status!=="skipped"&&r.status!=="unread"?Math.min(r.time_spent/3600*100,100):0}calculateGlobalIndicator(t,r){const n={total_time:.3,num_sessions:.2,completion_rate:.3,interactions:.2},a=Math.min(r.total_time_on_platform/36e3,1)*100,i=Math.min(r.num_sessions/5,1)*100,o=r.completion_rate,s=Math.min(r.total_interactions/100,1)*100;return(a*n.total_time+i*n.num_sessions+o*n.completion_rate+s*n.interactions)/100*100}}class Zwt{constructor(){hr(this,"name","frustration_indicator")}calculateStepIndicator(t,r){return r.status==="completed"?Math.min((r.comp_struggles+r.test_struggles)/20*100,100):r.status==="attempted"?Math.min(r.time_spent/1800*100,100):r.status==="skipped"?25:0}calculateGlobalIndicator(t,r){const n={steps_not_completed:.3,time_on_incomplete:.2,avg_comp_struggles:.2,avg_test_struggles:.2,skipped_steps:.1},a=r.total_steps>0?r.steps_not_completed/r.total_steps*100:0,i=Math.min(r.time_on_incomplete/18e3,1)*100,o=Math.min(r.avg_comp_struggles/10,1)*100,s=Math.min(r.avg_test_struggles/10,1)*100,l=r.total_steps>0?r.skipped_steps/r.total_steps*100:0;return(a*n.steps_not_completed+i*n.time_on_incomplete+o*n.avg_comp_struggles+s*n.avg_test_struggles+l*n.skipped_steps)/100*100}}function YEe(e){var x,w,C,S,N,L,M,B,F,I,O,P,D,R,U,$;let t=0;e.sessions&&e.sessions.length>0&&(t=e.sessions.reduce((z,q)=>z+q,0));let r=t;!t&&e.completed_at&&e.opened_at&&(r=e.completed_at-e.opened_at,t=r);let n=e.completed_at?"completed":e.opened_at?"attempted":"unread";const a=((x=e.compilations)==null?void 0:x.filter(z=>z.exit_code!==0).length)||0,i=((w=e.compilations)==null?void 0:w.filter(z=>z.exit_code===0).length)||0,o=((C=e.tests)==null?void 0:C.filter(z=>z.exit_code!==0).length)||0,s=((S=e.tests)==null?void 0:S.filter(z=>z.exit_code===0).length)||0,l=((N=e.quiz_submissions)==null?void 0:N.filter(z=>z.status==="ERROR").length)||0,u=((L=e.quiz_submissions)==null?void 0:L.filter(z=>z.status==="SUCCESS").length)||0;let d=0,h=0;for(let z=((M=e.compilations)==null?void 0:M.length)-1;z>=0&&e.compilations[z].exit_code!==0;z--)d++;for(let z=((B=e.compilations)==null?void 0:B.length)-1;z>=0&&e.compilations[z].exit_code===0;z--)h++;let f=0,p=0;for(let z=((F=e.tests)==null?void 0:F.length)-1;z>=0&&e.tests[z].exit_code!==0;z--)f++;for(let z=((I=e.tests)==null?void 0:I.length)-1;z>=0&&e.tests[z].exit_code===0;z--)p++;let g=0,A=0;for(let z=((O=e.quiz_submissions)==null?void 0:O.length)-1;z>=0&&e.quiz_submissions[z].status==="ERROR";z--)g++;for(let z=((P=e.quiz_submissions)==null?void 0:P.length)-1;z>=0&&e.quiz_submissions[z].status==="SUCCESS";z--)A++;const m=e.is_testeable||e.testeable_elements&&e.testeable_elements.length>0,y=(((D=e.quiz_submissions)==null?void 0:D.length)||0)===0&&(((R=e.tests)==null?void 0:R.length)||0)===0&&(((U=e.compilations)==null?void 0:U.length)||0)===0,v=!!m&&y,b=(($=e.ai_interactions)==null?void 0:$.length)||0,_=a+o+l>0&&i+s+u===0;return{status:n,time_spent:r,total_duration:t,comp_struggles:a,comp_success:i,total_comp_attempts:i+a,test_struggles:o,test_success:s,total_test_attempts:s+o,quiz_struggles:l,quiz_success:u,total_quiz_attempts:u+l,streak_comp_struggles:d,streak_comp_success:h,streak_test_struggle:f,streak_test_success:p,streak_quiz_struggles:g,streak_quiz_success:A,user_skipped:v,total_ai_interactions:b,ai_used:b>0,is_abandoned:_}}function Jwt(e){const t=e.length,r=e.map(YEe),n=r.filter(g=>g.status==="completed").length,a=r.filter(g=>g.status==="attempted").length,i=r.filter(g=>g.status==="skipped").length,o=r.filter(g=>g.status==="unread").length,s=t?n/t*100:0,l=e.reduce((g,A)=>{var m,y,v,b;return g+(((m=A.compilations)==null?void 0:m.length)||0)+(((y=A.tests)==null?void 0:y.length)||0)+(((v=A.quiz_submissions)==null?void 0:v.length)||0)+(((b=A.ai_interactions)==null?void 0:b.length)||0)},0),u=r.reduce((g,A)=>g+A.time_spent,0),d=a+i+o,h=t?r.reduce((g,A)=>g+A.comp_struggles,0)/t:0,f=t?r.reduce((g,A)=>g+A.test_struggles,0)/t:0;let p=0;for(const g of e){let A=0;for(const m of g.tests||[])m.exit_code===0?A++:A=0,p=Math.max(p,A)}return{total_time_on_platform:u,num_sessions:1,completion_rate:s,total_interactions:l,steps_not_completed:d,time_on_incomplete:u,avg_comp_struggles:h,avg_test_struggles:f,skipped_steps:i,total_steps:t,global_test_success_streak:p}}function qv(e){const t=[new Xwt,new Zwt],r=e.steps.map(o=>YEe(o)),n=Jwt(e.steps),a=e.steps.map((o,s)=>{const l=r[s],u={engagement_indicator:0,frustration_indicator:0};return t.forEach(d=>{u[d.name]=d.calculateStepIndicator(o,l)}),{slug:o.slug,metrics:l,indicators:u}}),i={engagement_indicator:0,frustration_indicator:0};return t.forEach(o=>{i[o.name]=o.calculateGlobalIndicator(a.map(s=>s.indicators[o.name]),n)}),{algo_version:Kwt,global:{metrics:n,indicators:i},steps:a}}function ekt(e,t){var d,h,f,p;if(!e.testeable_elements)return;const r=e.testeable_elements.find(g=>g.hash===t);if(!r)return;const n=r.type==="quiz"?((d=e.quiz_submissions.find(g=>g.quiz_hash===t))==null?void 0:d.ended_at)||e.opened_at||0:((h=e.tests.shift())==null?void 0:h.started_at)||e.opened_at||0,a=r.type==="quiz"?((f=e.quiz_submissions.findLast(g=>g.quiz_hash===t))==null?void 0:f.ended_at)||e.completed_at||1:((p=e.tests.shift())==null?void 0:p.started_at)||e.completed_at||1,i=Number((a-n)/1e3),o=e.ai_interactions.filter(g=>g.started_at>=n&&g.ended_at<=a).length,s=r.type==="quiz"?e.quiz_submissions.filter(g=>g.quiz_hash===t).length:e.tests.length,l=r.type==="quiz"?e.quiz_submissions.filter(g=>g.quiz_hash===t&&g.status==="ERROR").length:e.tests.filter(g=>g.exit_code!==1).length,u=r.type==="quiz"?e.quiz_submissions.filter(g=>g.quiz_hash===t&&g.status==="SUCCESS").length:e.tests.filter(g=>g.exit_code===1).length;return{started_at:n,ended_at:a,resolution_time_seconds:i,n_ai_interactions:o,n_tries:s,n_fail:l,n_success:u}}const KEe={name:"learnpack-ai",private:!0,version:"5.0.415",scripts:{dev:"node src/utils/development.js --turn on && vite",build:"node src/utils/development.js --turn off && tsc && vite build && node src/utils/uploadLearnpack.js && node bin/publish.js && node src/utils/createVersionsFile.js ","dev-build":"node src/utils/development.js --turn on && tsc && vite build",lint:"eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",preview:"vite preview",watch:"vite build --watch","compile-command":"tsc src/utils/development.ts && node src/utils/development.js --turn on"},dependencies:{"@dnd-kit/core":"^6.3.1","@dnd-kit/sortable":"^10.0.0","@dnd-kit/utilities":"^3.2.2","@monaco-editor/react":"^4.6.0","@radix-ui/react-tooltip":"^1.2.8",axios:"^1.7.7","class-variance-authority":"^0.7.1",clsx:"^2.1.1",dompurify:"^3.3.0","front-matter":"^4.0.2","highlight.js":"^11.11.1",html2canvas:"^1.4.1",i18next:"^23.10.1","js-yaml":"^4.1.0","lucide-react":"^0.545.0",mermaid:"^11.6.0",mitt:"^3.0.1","monaco-editor":"^0.50.0","prompt-sync":"^4.2.0","pusher-js":"7.6.0","radix-ui":"^1.4.3",react:"^18.2.0","react-dom":"^18.2.0","react-draggable":"^4.4.6","react-gtm-module":"^2.0.11","react-hot-toast":"^2.4.1","react-i18next":"^14.1.0","react-markdown":"^9.0.1","react-monaco-editor":"^0.56.1","react-router-dom":"^6.27.0","react-syntax-highlighter":"^15.6.1","rehype-katex":"^7.0.1","remark-emoji":"^5.0.1","remark-gfm":"^4.0.0","remark-math":"^6.0.0","socket.io":"^4.7.2","socket.io-client":"^4.7.2","tailwind-merge":"^3.3.1","tailwindcss-animate":"^1.0.7",uuid:"^10.0.0",zustand:"^4.4.1"},devDependencies:{"@aws-sdk/client-cloudfront":"^3.840.0","@aws-sdk/client-s3":"^3.709.0","@types/dompurify":"^3.0.5","@types/js-yaml":"^4.0.9","@types/markdown-it":"^14.1.2","@types/node":"^24.9.1","@types/react":"^18.2.15","@types/react-dom":"^18.2.7","@types/react-gtm-module":"^2.0.3","@types/react-syntax-highlighter":"^15.5.13","@types/remarkable":"^2.0.3","@types/uuid":"^10.0.0","@typescript-eslint/eslint-plugin":"^6.0.0","@typescript-eslint/parser":"^6.0.0","@vitejs/plugin-react-swc":"^3.3.2",archiver:"^6.0.1",autoprefixer:"^10.4.21",dotenv:"^16.4.7",eslint:"^8.45.0","eslint-plugin-react-hooks":"^4.6.0","eslint-plugin-react-refresh":"^0.4.3",inquirer:"^9.2.12",postcss:"^8.5.6",tailwindcss:"^3.4.18",typescript:"^5.0.2",vite:"^4.5.13"}};function tkt(e){return{all:e=e||new Map,on:function(t,r){var n=e.get(t);n?n.push(r):e.set(t,[r])},off:function(t,r){var n=e.get(t);n&&(r?n.splice(n.indexOf(r)>>>0,1):e.set(t,[]))},emit:function(t,r){var n=e.get(t);n&&n.slice().map(function(a){a(r)}),(n=e.get("*"))&&n.slice().map(function(a){a(t,r)})}}}const _r=tkt(),Qce=async(e,t)=>{if(!t)throw new Error("Slug is required");const r=`${di}/v1/learnpack/session/?slug=${t}`;try{return(await Hr.get(r,{headers:{Authorization:`Token ${e}`}})).data}catch(n){throw console.error("Error fetching session:",yr(n)),n}},Wce=async(e,t,r,n,a)=>{const i=`${di}/v1/learnpack/session/${a}/`,o={tab_hash:t,config_json:n,package_slug:r};try{return(await Hr.put(i,o,{headers:{Authorization:`Token ${e}`,"Content-Type":"application/json"}})).data}catch(s){throw console.error("Error updating session:",yr(s)),s}},rkt=async(e,t,r,n)=>{const a=`${di}/v1/learnpack/session/`,i={tab_hash:t,config_json:n,package_slug:r};try{return(await Hr.post(a,i,{headers:{Authorization:`Token ${e}`,"Content-Type":"application/json"}})).data}catch(o){throw console.error("Error creating session:",yr(o)),o}};async function nkt(e){const t=`${Rv}/v1/payments/me/service/consumable?virtual=true`,r={Authorization:`Token ${e}`};try{return(await Hr.get(t,{headers:r})).data}catch(n){throw console.error("Error fetching consumables:",yr(n)),n}}async function akt(e,t="ai-conversation-message"){const r=`${Rv}/v1/payments/me/service/${t}/consumptionsession`,n={Authorization:`Token ${e}`};try{const a=await Hr.put(r,{},{headers:n});return a.status>=200&&a.status<300?(a.data,`${t}`,!0):(console.error(`Request failed with status code: ${a.status}`),console.error(`Response: ${a.data}`),!1)}catch(a){return console.error(`Error consuming ${t}:`,yr(a)),!1}}function ikt(e){if(!Array.isArray(e))return[];const t=[];for(const r of e){const n=typeof r=="number"?r:Number(r);Number.isFinite(n)&&t.push(n)}return t}async function Yce(e,t){var a;const r={id:null,assetIds:[]};if(!(e!=null&&e.trim())||!t)return r;const n=`${di}/v1/learnpack/package/${encodeURIComponent(t)}/assets/`;try{const o=(await Hr.get(n,{headers:{Authorization:`Token ${e.trim()}`}})).data;return{id:(o==null?void 0:o.id)!=null?String(o.id):null,assetIds:ikt(o==null?void 0:o.asset_ids)}}catch(i){return Hr.isAxiosError(i)&&((a=i.response)==null?void 0:a.status)===404||console.warn("fetchLearnpackPackageInfo failed:",yr(i)),r}}const okt=async(e,t)=>{var a;const r=`${di}/v1/learnpack/package/${t}/`,n={Authorization:`Token ${e}`};try{const i=await Hr.get(r,{headers:n});return{isAuthor:i.status===200,status:i.status}}catch(i){const o=((a=i==null?void 0:i.response)==null?void 0:a.status)||500;return console.error("Error fetching package:",yr(i)),{isAuthor:!1,status:o}}},skt=async(e,t,r=di)=>{const n=await Hr.get(`${r}/v1/prompting/completion/${t}/`,{headers:{Authorization:`Token ${e}`,"Content-Type":"application/json"}});return n.data,n.data};function lkt(e,t){if(t.length===0)return e;const r=e.includes("?")?"&":"?";return`${e}${r}asset_id=${t.join(",")}`}const ckt=async function(e,t,r,n){if(!e||!r){console.error("URL and token are required");return}r=r.trim();const a={"Content-Type":"application/json",Authorization:`Token ${r}`},i=lkt(e,n??[]);try{return(await Hr.post(i,t,{headers:a})).data}catch(o){throw console.error("Error while sending batch telemetry",yr(o)),o}},ukt=async function(e,t){const r=`${di}/v1/learnpack/telemetry`,n={"Content-Type":"application/json",Authorization:`Token ${t}`};try{return(await Hr.post(r,e,{headers:n})).data}catch(a){throw console.error("Error while sending batch telemetry",yr(a)),a}},dkt=5e3,hkt=6e4,fkt=5*60*1e3;async function XEe(e){var u;const{userId:t,packageSlug:r,rigoToken:n}=e;if(!t||!n||!r)return null;const a=n.trim(),i=new URLSearchParams;i.set("user_ids",String(t)),i.set("include_buffer","true"),i.set("include_steps","true"),i.set("package_slug",r);const o=`${di}/v1/learnpack/telemetry?${i.toString()}`,s=new AbortController,l=setTimeout(()=>s.abort(),dkt);try{const d=await fetch(o,{method:"GET",headers:{Authorization:`Token ${a}`,Accept:"application/json"},signal:s.signal});if(!d.ok)return console.warn("fetchTelemetryFromServer: non-OK response",d.status),null;const h=await d.json(),f=(u=h==null?void 0:h.results)==null?void 0:u[0];return!f||typeof f!="object"?null:f}catch(d){return d.name==="AbortError"?console.warn("fetchTelemetryFromServer: timeout or aborted"):console.warn("fetchTelemetryFromServer: failed",yr(d)),null}finally{clearTimeout(l)}}async function pkt(e){var u;const{userId:t,packageSlug:r,rigoToken:n}=e;if(!t||!n||!r)return{status:"empty"};const a=n.trim(),i=new URLSearchParams;i.set("user_ids",String(t)),i.set("include_buffer","true"),i.set("include_steps","true"),i.set("package_slug",r);const o=`${di}/v1/learnpack/telemetry?${i.toString()}`,s=new AbortController,l=setTimeout(()=>s.abort(),hkt);try{const d=await fetch(o,{method:"GET",headers:{Authorization:`Token ${a}`,Accept:"application/json"},signal:s.signal});if(!d.ok)return console.warn("fetchTelemetryWithStatus: non-OK response",d.status),{status:"server_error",code:d.status};const h=await d.json(),f=(u=h==null?void 0:h.results)==null?void 0:u[0];return!f||typeof f!="object"?{status:"empty"}:{status:"ok",data:f}}catch(d){return d.name==="AbortError"?(console.warn("fetchTelemetryWithStatus: timeout"),{status:"timeout"}):(console.warn("fetchTelemetryWithStatus: network error",yr(d)),{status:"timeout"})}finally{clearTimeout(l)}}function ZEe(e){return!e||typeof e.last_interaction_at!="number"?0:e.last_interaction_at}function d9(){return Math.random().toString(36).slice(2,10)+Math.random().toString(36).slice(2,10)}function JEe(e,t){const r=new Map;for(const n of t)n.slug&&r.set(n.slug,n);return e.map(n=>{var o,s,l,u,d,h,f,p,g,A;const a=r.get(n.slug);if(!a)return n;const i={...n};if(!i.completed_at&&a.completed_at&&(i.completed_at=a.completed_at),!i.is_completed&&a.is_completed&&(i.is_completed=!0),!i.opened_at&&a.opened_at&&(i.opened_at=a.opened_at),(o=a.testeable_elements)!=null&&o.length){const m=new Map((i.testeable_elements??[]).map(v=>[v.hash,v])),y=[...i.testeable_elements??[]];for(const v of a.testeable_elements){const b=m.get(v.hash);if(!b)y.push(v);else if(v.is_completed&&!b.is_completed){const _=y.findIndex(x=>x.hash===v.hash);_!==-1&&(y[_]={...b,is_completed:!0,metrics:b.metrics??v.metrics})}}i.testeable_elements=y}if((s=a.quiz_submissions)!=null&&s.length){const m=new Set((i.quiz_submissions??[]).map(v=>v.quiz_hash)),y=a.quiz_submissions.filter(v=>!m.has(v.quiz_hash));y.length>0&&(i.quiz_submissions=[...i.quiz_submissions??[],...y])}return!((l=i.tests)!=null&&l.length)&&((u=a.tests)!=null&&u.length)&&(i.tests=[...a.tests]),!((d=i.compilations)!=null&&d.length)&&((h=a.compilations)!=null&&h.length)&&(i.compilations=[...a.compilations]),!((f=i.ai_interactions)!=null&&f.length)&&((p=a.ai_interactions)!=null&&p.length)&&(i.ai_interactions=[...a.ai_interactions]),!((g=i.sessions)!=null&&g.length)&&((A=a.sessions)!=null&&A.length)&&(i.sessions=[...a.sessions]),i})}function gkt(e,t,r,n,a,i){const o=!!e,s=t&&t.slug===n;if(o&&!s)return{telemetry:n0({...e},r,n,a,i),source:"server"};if(!o&&s)return{telemetry:n0({...t},r,n,a,i),source:"local"};if(o&&s){const u=ZEe(e),d=ZEe(t),[h,f,p]=d>u?[t,e,"local"]:[e,t,"server"],g=n0({...h},r,n,a,i);return g.steps=JEe(g.steps,f.steps??[]),{telemetry:g,source:p}}const l=Date.now();return{telemetry:n0({telemetry_id:d9(),slug:n,version:`${i}`,agent:a,tutorial_started_at:l,last_interaction_at:l,steps:r,workout_session:[{started_at:l}],fullname:"",cohort_id:null,academy_id:null},r,n,a,i),source:"new"}}function Akt(e,t,r){if(!e||!r)return;const n=r.trim();fetch(e,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Token ${n}`},body:JSON.stringify(t),keepalive:!0}).catch(()=>{})}const mkt=async function(e,t,r){!e||!r||fetch(e,{method:"POST",body:JSON.stringify(t)}).then(n=>n).catch(n=>{console.error("Error while sending stream Telemetry",yr(n))})},ykt=async function(){try{return(await Hr.get(`${fa}/telemetry`)).data}catch(e){return console.error("Error while retrieving telemetry from CLI",yr(e)),null}},vkt=async function(e){try{const t=await Hr.post(`${fa}/telemetry`,e);return t.data,t.data}catch(t){return console.error("Error while saving telemetry in CLI",yr(t)),null}};function Kce(e){if(typeof e!="string")throw new TypeError("Input must be a string");return btoa(encodeURIComponent(e))}const bkt=(e,t)=>{let r={...t};return typeof r.starting_at<"u"&&typeof r.started_at>"u"&&(r.started_at=r.starting_at,delete r.starting_at),typeof r.ending_at<"u"&&typeof r.ended_at>"u"&&(r.ended_at=r.ending_at,delete r.ending_at),typeof r.started_at>"u"&&e!=="open_step"&&(r.started_at=Date.now()),typeof r.ended_at>"u"&&e!=="open_step"&&(r.ended_at=Date.now()),r.source_code&&(r.source_code=Kce(r.source_code)),r.stdout&&(r.stdout=Kce(r.stdout)),r.stderr&&(r.stderr=Kce(r.stderr)),Object.prototype.hasOwnProperty.call(r,"exitCode")&&(r.exit_code=r.exitCode,r.exitCode=void 0),r},wkt=["telemetry_id","user_id","fullname","slug","package_id","version","cohort_id","academy_id","agent","tutorial_started_at","last_interaction_at","steps","workout_session","global_metrics","global_indicators"];function eSe(e){return e==null||typeof e=="string"&&e.trim()===""?null:typeof e=="number"&&!Number.isNaN(e)?String(e):typeof e=="string"?e:null}function kkt(e,t){if(!Array.isArray(e))return[{started_at:t}];if(e.length===0)return[{started_at:t}];const r=e.filter(n=>!!n&&typeof n=="object"&&typeof n.started_at=="number");return r.length===0?[{started_at:t}]:r}function xkt(e,t){const r=new Map;for(const n of t)n.slug&&r.set(n.slug,n);return e.map(n=>{const a=r.get(n.slug);return a?{slug:n.slug,position:n.position,files:n.files,is_testeable:n.is_testeable,compilations:a.compilations??[],tests:a.tests??[],ai_interactions:a.ai_interactions??[],quiz_submissions:a.quiz_submissions??[],testeable_elements:a.testeable_elements??[],is_completed:a.is_completed??!1,completed_at:a.completed_at,opened_at:a.opened_at,sessions:a.sessions??[]}:n})}function n0(e,t,r,n,a){const i=e,o={};for(const g of wkt)Object.prototype.hasOwnProperty.call(i,g)&&(o[g]=i[g]);const s=Date.now(),l=r,u=typeof o.version=="string"&&o.version.trim()!==""?o.version:`${a}`,d=typeof o.tutorial_started_at=="number"?o.tutorial_started_at:s,h=typeof o.last_interaction_at=="number"?o.last_interaction_at:s,f=Array.isArray(o.steps)&&o.steps.length>0?xkt(t,o.steps):t,p=kkt(o.workout_session,s);return{telemetry_id:o.telemetry_id,user_id:o.user_id,fullname:o.fullname,slug:l,package_id:o.package_id,version:u,cohort_id:eSe(o.cohort_id),academy_id:eSe(o.academy_id),agent:typeof o.agent=="string"?o.agent:n,tutorial_started_at:d,last_interaction_at:h,steps:f,workout_session:p,global_metrics:o.global_metrics,global_indicators:o.global_indicators}}function tSe(e,t){e.telemetry_id||(e.telemetry_id=d9());const r=qv(e),n=e.steps.map((a,i)=>{const o=e.telemetry_id||d9();return{...a,telemetry_id:o,metrics:r.steps[i].metrics,indicators:r.steps[i].indicators}});return{...e,steps:n,global_metrics:r.global.metrics,global_indicators:r.global.indicators,user_id:t}}const cr={current:null,urls:{},telemetryKey:"",packageAssetIds:[],listeners:{},agent:"cloud",prevStep:void 0,prevStepStartedAt:void 0,activeHashes:new Map,currentReadyAt:void 0,prunedCompletions:new Set,nonTesteableSlugs:new Set,_lessonRenderedDebounce:null,user:{token:"",rigo_token:"",id:"",fullname:""},tutorialSlug:"",started:!1,reconciling:!1,version:`${KEe.version}`||"CLOUD:0.0.0",salute:e=>{},start:function(e,t,r,n,a,i){return this.packageAssetIds=[],this.telemetryKey=n,this.tutorialSlug=r,this.agent=e,this.user.id=a.user_id,this.user.token=a.token,this.user.rigo_token=a.rigo_token,this.user.fullname=a.fullname,this.current?Promise.resolve():(this.activeHashes=new Map,this.prunedCompletions=new Set,this.currentReadyAt=void 0,e==="cloud"?(this.reconciling=!0,(async()=>{var s;let o=!1;try{const l=jr.get(this.telemetryKey),u=String(l==null?void 0:l.user_id)===String(a==null?void 0:a.user_id),d=l&&l.slug===r&&u?l:null,h=!!((s=a.rigo_token)!=null&&s.trim()&&r),f=i!==void 0,[p,g]=await Promise.all([f?Promise.resolve(i):a.rigo_token&&a.user_id?XEe({userId:a.user_id,packageSlug:r,rigoToken:a.rigo_token}):Promise.resolve(null),h?Yce(a.rigo_token,r):Promise.resolve({id:null,assetIds:[]})]);this.packageAssetIds=g.assetIds;const{telemetry:A,source:m}=gkt(p,d,t,r,e,this.version);if(this.current=A,this.currentReadyAt=Date.now(),this.current.user_id=a.user_id,this.current.fullname=a.fullname,this.current.cohort_id=a.cohort_id||null,this.current.academy_id=a.academy_id||null,this.current.version||(this.current.version=`CLOUD:${this.version}`),g.id!=null&&this.mergePackageIdIfMissing(g.id),this.finishWorkoutSession(),this.save(),this.started=!0,!a.user_id){console.warn("No user ID found, impossible to submit telemetry at start");return}o=m==="local"}catch(l){throw console.error("There was a problem starting the Telemetry",yr(l)),new Error(`There was a problem starting, reload LearnPack
|
|
757
815
|
Run
|
|
758
816
|
$ learnpack start`)}finally{this.reconciling=!1}o&&a.user_id&&await this.submit()})()):this.retrieve().then(async o=>{var s;if(o?(this.current=n0(o,t,r,e,this.version),this.finishWorkoutSession()):this.current=n0({telemetry_id:d9(),slug:r,version:`${this.version}`,agent:e,tutorial_started_at:Date.now(),last_interaction_at:Date.now(),steps:t,workout_session:[{started_at:Date.now()}],fullname:this.user.fullname,cohort_id:null,academy_id:null},t,r,e,this.version),this.currentReadyAt=Date.now(),this.current.user_id=this.user.id,this.current.fullname=this.user.fullname,this.current.cohort_id=a.cohort_id||null,this.current.academy_id=a.academy_id||null,this.current.version||(this.current.version=`CLOUD:${this.version}`),this.save(),this.started=!0,!this.user.id){console.warn("No user ID found, impossible to submit telemetry at start");return}if((s=this.user.rigo_token)!=null&&s.trim()&&r){const l=await Yce(this.user.rigo_token,r);this.packageAssetIds=l.assetIds,l.id!=null&&this.mergePackageIdIfMissing(l.id)}await this.submit()}).catch(o=>{throw console.error("There was a problem starting the Telemetry",yr(o)),new Error(`There was a problem starting, reload LearnPack
|
|
759
817
|
Run
|
package/src/ui/app.tar.gz
CHANGED
|
Binary file
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rules for `POST /actions/continue-generating`, the request behind the
|
|
3
|
+
* creator's continue and retry buttons. Without them, every extra click starts
|
|
4
|
+
* another paid job for a lesson that is already being generated.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* How long a PENDING job counts as running. Each phase of a lesson takes 1 to
|
|
9
|
+
* 3 minutes, so a job PENDING for longer is most likely stuck, for example
|
|
10
|
+
* after a worker restart. A stuck job holds the whole course, because the next
|
|
11
|
+
* lesson only starts after this one finishes. Replacing a slow job that was
|
|
12
|
+
* still alive only costs one extra job: serve ignores the older job's webhook.
|
|
13
|
+
*/
|
|
14
|
+
export const RUNNING_JOB_MAX_AGE_MS = 5 * 60 * 1000
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* How long a finished job counts as running. Rigobot saves the result before
|
|
18
|
+
* it sends the webhook, and serve may still be handling it.
|
|
19
|
+
*/
|
|
20
|
+
export const WEBHOOK_GRACE_MS = 90 * 1000
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* How long a lesson counts as running when its job cannot be checked: it has
|
|
24
|
+
* no job id yet, or Rigobot did not answer.
|
|
25
|
+
*/
|
|
26
|
+
export const UNCHECKED_JOB_GRACE_MS = 2 * 60 * 1000
|
|
27
|
+
|
|
28
|
+
export type GenerationMode = "next-three" | "continue-with-all";
|
|
29
|
+
|
|
30
|
+
type LessonState = {
|
|
31
|
+
status?: string;
|
|
32
|
+
translations?: {
|
|
33
|
+
[language: string]: { completionId?: number; startedAt?: number };
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** The fields of a Rigobot completion job these rules read. */
|
|
38
|
+
export type JobState = { status?: string; ended_at?: string | null };
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The job to check before starting another one.
|
|
42
|
+
* @param lesson - The lesson as saved in the syllabus.
|
|
43
|
+
* @param language - The course language.
|
|
44
|
+
* @returns The id of the job the lesson waits for, or null if it has none.
|
|
45
|
+
*/
|
|
46
|
+
export const currentJobId = (
|
|
47
|
+
lesson: LessonState,
|
|
48
|
+
language: string
|
|
49
|
+
): number | null => {
|
|
50
|
+
const id = lesson.translations?.[language]?.completionId
|
|
51
|
+
return typeof id === "number" && id > 0 ? id : null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Whether a lesson is still being generated, so a new job must not start.
|
|
56
|
+
* @param lesson - The lesson as saved in the syllabus.
|
|
57
|
+
* @param language - The course language.
|
|
58
|
+
* @param job - Its job as Rigobot reports it, or null if it was not checked.
|
|
59
|
+
* @param now - The current time in milliseconds.
|
|
60
|
+
* @returns True when the lesson must wait for its current job.
|
|
61
|
+
*/
|
|
62
|
+
export const isGenerationRunning = (
|
|
63
|
+
lesson: LessonState,
|
|
64
|
+
language: string,
|
|
65
|
+
job: JobState | null,
|
|
66
|
+
now: number
|
|
67
|
+
): boolean => {
|
|
68
|
+
if (lesson.status !== "GENERATING") return false
|
|
69
|
+
|
|
70
|
+
const startedAt = lesson.translations?.[language]?.startedAt
|
|
71
|
+
const startedAgo =
|
|
72
|
+
typeof startedAt === "number" && startedAt > 0 ?
|
|
73
|
+
now - startedAt :
|
|
74
|
+
Number.POSITIVE_INFINITY
|
|
75
|
+
|
|
76
|
+
if (!job) return startedAgo < UNCHECKED_JOB_GRACE_MS
|
|
77
|
+
if (job.status === "PENDING") return startedAgo < RUNNING_JOB_MAX_AGE_MS
|
|
78
|
+
|
|
79
|
+
// A finished job. Its webhook may still be on its way to serve.
|
|
80
|
+
const endedAt = job.ended_at ? Date.parse(job.ended_at) : Number.NaN
|
|
81
|
+
if (Number.isNaN(endedAt)) return startedAgo < UNCHECKED_JOB_GRACE_MS
|
|
82
|
+
|
|
83
|
+
return now - endedAt < WEBHOOK_GRACE_MS
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The generation mode to save. The user only picks a mode on a PENDING
|
|
88
|
+
* lesson. The retry buttons offer no choice, so a retry keeps the mode of the
|
|
89
|
+
* course instead of resetting it.
|
|
90
|
+
* @param lessonStatus - The status of the lesson before this request.
|
|
91
|
+
* @param current - The mode saved in the syllabus.
|
|
92
|
+
* @param requested - The mode in the request.
|
|
93
|
+
* @returns The mode to save, or undefined when the course has none.
|
|
94
|
+
*/
|
|
95
|
+
export const resolveGenerationMode = (
|
|
96
|
+
lessonStatus: string | undefined,
|
|
97
|
+
current: unknown,
|
|
98
|
+
requested: unknown
|
|
99
|
+
): GenerationMode | undefined => {
|
|
100
|
+
const isMode = (mode: unknown): mode is GenerationMode =>
|
|
101
|
+
mode === "next-three" || mode === "continue-with-all"
|
|
102
|
+
|
|
103
|
+
if (isMode(requested) && (lessonStatus === "PENDING" || !isMode(current))) {
|
|
104
|
+
return requested
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return isMode(current) ? current : undefined
|
|
108
|
+
}
|