@learnpack/learnpack 5.0.284 → 5.0.286

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.
@@ -10,7 +10,7 @@
10
10
  />
11
11
 
12
12
  <title>Learnpack Creator: Craft tutorials in seconds!</title>
13
- <script type="module" crossorigin src="/creator/assets/index-7zTdUX04.js"></script>
13
+ <script type="module" crossorigin src="/creator/assets/index-CQMtxRqZ.js"></script>
14
14
  <link rel="stylesheet" crossorigin href="/creator/assets/index-C39zeF3W.css">
15
15
  </head>
16
16
  <body>
@@ -50,7 +50,7 @@ const Session = {
50
50
  setPayload: async function (value) {
51
51
  await this.initialize();
52
52
  await storage.setItem("bc-payload", Object.assign({}, value));
53
- console_1.default.debug("Payload successfuly found and set for " + value.email);
53
+ console_1.default.debug("Payload successfuly found and set for user " + value.user_id);
54
54
  // TelemetryManager.setStudent({
55
55
  // user_id: value.user_id.toString(),
56
56
  // email: value.email,
@@ -124,7 +124,7 @@ const Session = {
124
124
  }
125
125
  this.token = token;
126
126
  if (payload && (await this.setPayload(payload))) {
127
- console_1.default.success(`Successfully logged in as ${payload.email}`);
127
+ console_1.default.success(`Successfully logged in as user ${payload.user_id}`);
128
128
  }
129
129
  },
130
130
  destroy: async function () {
@@ -8,13 +8,30 @@ const rimraf = require("rimraf");
8
8
  const uuid_1 = require("uuid");
9
9
  const child_process_1 = require("child_process");
10
10
  const shared_1 = require("./shared");
11
- const replaceQuestionsFromMarkdown = (markdown) => {
11
+ const getLocalizedText = (language) => {
12
+ const translations = {
13
+ en: "Write your answer here",
14
+ es: "Escribe tu respuesta aquí",
15
+ fr: "Écrivez votre réponse ici",
16
+ de: "Schreiben Sie Ihre Antwort hier",
17
+ it: "Scrivi la tua risposta qui",
18
+ pt: "Escreva sua resposta aqui",
19
+ ja: "ここに答えを書いてください",
20
+ ko: "여기에 답을 작성하세요",
21
+ zh: "在这里写下你的答案",
22
+ ar: "اكتب إجابتك هنا",
23
+ ru: "Напишите свой ответ здесь",
24
+ };
25
+ return translations[language] || translations.en;
26
+ };
27
+ const replaceQuestionsFromMarkdown = (markdown, language = "en") => {
12
28
  // Search for all code blocks like this (No breaking lines sensitive)
13
29
  // ```question some parameters
14
30
  // some text
15
31
  // ```
16
32
  const questionRegex = /```question([\s\S]*?)```/g;
17
- return markdown.replace(questionRegex, '<div type="text" class="open-question">Right your answer here</div>');
33
+ const localizedText = getLocalizedText(language);
34
+ return markdown.replace(questionRegex, `<div type="text" class="open-question">${localizedText}</div>`);
18
35
  };
19
36
  const replaceMermaidFromMarkdown = (markdown) => {
20
37
  // Search for all code blocks like this (No breaking lines sensitive)
@@ -186,7 +203,7 @@ async function processExercises(exercisesDir, outputDir, language = "en", learnA
186
203
  }
187
204
  // Process markdown content and save to output directory
188
205
  console.log("Processing markdown file:", relativeFilePath);
189
- const processedContent = processMarkdownContent(fullPath, learnAssetsDir);
206
+ const processedContent = processMarkdownContent(fullPath, learnAssetsDir, language);
190
207
  const processedMarkdownPath = path.join(outputDir, relativeFilePath.replace(/\.(md|markdown)$/, ".processed.md"));
191
208
  console.log("Processed markdown output path:", processedMarkdownPath);
192
209
  mkdirp.sync(path.dirname(processedMarkdownPath));
@@ -213,7 +230,7 @@ async function processExercises(exercisesDir, outputDir, language = "en", learnA
213
230
  return processedMarkdownFiles;
214
231
  }
215
232
  // Process markdown content for EPUB conversion
216
- function processMarkdownContent(markdownPath, learnAssetsDir) {
233
+ function processMarkdownContent(markdownPath, learnAssetsDir, language = "en") {
217
234
  let markdownContent = fs.readFileSync(markdownPath, "utf-8");
218
235
  if (learnAssetsDir && fs.existsSync(learnAssetsDir)) {
219
236
  // Find all image references in markdown
@@ -248,7 +265,7 @@ function processMarkdownContent(markdownPath, learnAssetsDir) {
248
265
  }
249
266
  }
250
267
  }
251
- markdownContent = replaceQuestionsFromMarkdown(markdownContent);
268
+ markdownContent = replaceQuestionsFromMarkdown(markdownContent, language);
252
269
  markdownContent = replaceMermaidFromMarkdown(markdownContent);
253
270
  markdownContent = cleanQuizOptions(markdownContent);
254
271
  return markdownContent;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@learnpack/learnpack",
3
3
  "description": "Seamlessly build, sell and/or take interactive & auto-graded tutorials, start learning now or build a new tutorial to your audience.",
4
- "version": "5.0.284",
4
+ "version": "5.0.286",
5
5
  "author": "Alejandro Sanchez @alesanchezr",
6
6
  "contributors": [
7
7
  {
@@ -31,6 +31,7 @@ import NotificationListener from "./components/NotificationListener"
31
31
  import { slugify } from "./utils/creatorUtils"
32
32
  import TurnstileModal from "./components/TurnstileModal"
33
33
  import { TMessage } from "./components/Message"
34
+ import PassphraseValidator from "./components/PassphraseValidator"
34
35
 
35
36
  function App() {
36
37
  const navigate = useNavigate()
@@ -85,6 +86,22 @@ function App() {
85
86
  checkTechs()
86
87
  }, [])
87
88
 
89
+ const tokenVerification = async () => {
90
+ const isValid = await isValidPublicToken(auth.publicToken)
91
+ if (!isValid) {
92
+ setAuth({
93
+ ...auth,
94
+ publicToken: ""
95
+ })
96
+ }
97
+ }
98
+
99
+ useEffect(() => {
100
+ if (auth.publicToken) {
101
+ tokenVerification()
102
+ }
103
+ }, [auth])
104
+
88
105
  const verifyToken = async () => {
89
106
  const { token } = checkParams(["token"])
90
107
  if (token) {
@@ -99,6 +116,29 @@ function App() {
99
116
  }
100
117
  }
101
118
 
119
+ const getGenerationMode = (): string => {
120
+ const urlParams = new URLSearchParams(window.location.search);
121
+ const configParams = urlParams.get('configParams');
122
+
123
+ if (!configParams) {
124
+ return 'next-three';
125
+ }
126
+
127
+ try {
128
+ const decodedConfig = decodeURIComponent(configParams);
129
+ const config = JSON.parse(decodedConfig);
130
+
131
+ if (config?.page?.generation === 'auto') {
132
+ return 'continue-with-all';
133
+ }
134
+
135
+ return 'next-three';
136
+ } catch (error) {
137
+ console.error('Error parsing configParams:', error);
138
+ return 'next-three';
139
+ }
140
+ };
141
+
102
142
  const checkQueryParams = () => {
103
143
  const {
104
144
  description,
@@ -123,7 +163,6 @@ function App() {
123
163
  }
124
164
 
125
165
  if (description) {
126
- console.log("description", description)
127
166
  setFormState({
128
167
  description: description,
129
168
  })
@@ -131,12 +170,9 @@ function App() {
131
170
  if (duration && !isNaN(parseInt(duration))) {
132
171
  if (["30", "60", "120"].includes(duration)) {
133
172
  const durationInt = parseInt(duration)
134
- console.log("duration", durationInt)
135
173
  setFormState({
136
174
  duration: durationInt,
137
175
  })
138
- } else {
139
- console.log("Invalid duration received in params", duration)
140
176
  }
141
177
  }
142
178
 
@@ -202,13 +238,13 @@ function App() {
202
238
  }
203
239
  }
204
240
 
205
- if (auth.publicToken && !isAuthenticated) {
206
- const isPublicTokenValid = await isValidPublicToken(auth.publicToken)
207
- if (isPublicTokenValid) {
208
- tokenToUse = auth.publicToken
209
- isAuthenticated = true
210
- tokenType = "public"
211
- }
241
+ if (auth.publicToken) {
242
+ // const isPublicTokenValid = await isValidPublicToken(auth.publicToken)
243
+ // if (isPublicTokenValid) {
244
+ // }
245
+ tokenToUse = auth.publicToken
246
+ isAuthenticated = true
247
+ tokenType = "public"
212
248
  }
213
249
  if (!isAuthenticated) {
214
250
  setShowTurnstileModal(true)
@@ -295,7 +331,7 @@ function App() {
295
331
  onFinish={(purpose) => {
296
332
  setFormState({
297
333
  purpose: purpose,
298
- currentStep: "verifyHuman",
334
+ currentStep: "duration",
299
335
  })
300
336
  }}
301
337
  />
@@ -314,7 +350,7 @@ function App() {
314
350
  onClick={() => {
315
351
  setFormState({
316
352
  duration: 30,
317
- currentStep: "verifyHuman",
353
+ currentStep: "hasContentIndex",
318
354
  })
319
355
  }}
320
356
  selected={formState.duration === 30}
@@ -325,7 +361,7 @@ function App() {
325
361
  onClick={() => {
326
362
  setFormState({
327
363
  duration: 60,
328
- currentStep: "verifyHuman",
364
+ currentStep: "hasContentIndex",
329
365
  })
330
366
  }}
331
367
  selected={formState.duration === 60}
@@ -336,7 +372,7 @@ function App() {
336
372
  onClick={() => {
337
373
  setFormState({
338
374
  duration: 120,
339
- currentStep: "verifyHuman",
375
+ currentStep: "hasContentIndex",
340
376
  })
341
377
  }}
342
378
  selected={formState.duration === 120}
@@ -344,53 +380,21 @@ function App() {
344
380
  </div>
345
381
  ),
346
382
  },
347
- {
348
- title: t("stepWizard.verifyHuman"),
349
- slug: "verifyHuman",
350
- isCompleted: false,
351
- required: true,
352
- content: (
353
- <TurnstileChallenge
354
- siteKey={
355
- DEV_MODE ? "0x4AAAAAABeKMBYYinMU4Ib0" : "0x4AAAAAABeZ9tjEevGBsJFU"
356
- }
357
- onSuccess={async (token) => {
358
- const { human, message, token: jwtToken } = await isHuman(token)
359
- if (human) {
360
- toast.success(t("stepWizard.humanSuccess"))
361
-
362
- console.log("JWT TOKEN received", jwtToken)
363
- setAuth({
364
- ...auth,
365
- publicToken: jwtToken,
366
- })
367
- setFormState({
368
- currentStep: "hasContentIndex",
369
- })
370
- } else {
371
- toast.error(message)
372
- setFormState({
373
- currentStep: "duration",
374
- })
375
- }
376
- }}
377
- onError={() => {
378
- toast.error(t("turnstileModal.error"), {
379
- duration: 10000,
380
- })
381
- setFormState({
382
- currentStep: "duration",
383
- })
384
- }}
385
- />
386
- ),
387
- },
388
383
  {
389
384
  title: t("stepWizard.hasContentIndex"),
390
385
  slug: "hasContentIndex",
391
386
  isCompleted: false,
392
387
  content: (
393
388
  <>
389
+ {!auth.publicToken && (
390
+ <PassphraseValidator onSuccess={(res) => {
391
+ console.log("RES FROM RIGO", res.public_access_token)
392
+ setAuth({
393
+ ...auth,
394
+ publicToken: res.public_access_token,
395
+ })
396
+ }} />
397
+ )}
394
398
  <div className="flex flex-col md:flex-row gap-2 justify-center">
395
399
  <SelectableCard
396
400
  title={t("stepWizard.hasContentIndexCard.no")}
@@ -405,7 +409,7 @@ function App() {
405
409
  isCompleted: true,
406
410
  })
407
411
  }}
408
- // selected={formState.hasContentIndex === false}
412
+ // selected={formState.hasContentIndex === false}
409
413
  />
410
414
  <SelectableCard
411
415
  title={t("stepWizard.hasContentIndexCard.yes")}
@@ -416,7 +420,7 @@ function App() {
416
420
  variables: [...formState.variables, "contentIndex"],
417
421
  })
418
422
  }}
419
- // selected={formState.hasContentIndex === true}
423
+ // selected={formState.hasContentIndex === true}
420
424
  />
421
425
  </div>
422
426
  </>
@@ -485,9 +489,11 @@ function App() {
485
489
 
486
490
  push({
487
491
  lessons,
492
+ generationMode: getGenerationMode() as "next-three" | "continue-with-all",
488
493
  courseInfo: {
489
494
  ...formState,
490
495
  title: res.parsed.title,
496
+
491
497
  slug: slugify(fixTitleLength(res.parsed.title)),
492
498
  description: res.parsed.description,
493
499
  language:
@@ -0,0 +1,47 @@
1
+ import React, { useEffect } from 'react';
2
+ import { RIGOBOT_HOST } from '../utils/constants';
3
+
4
+ interface PassphraseValidatorProps {
5
+ onSuccess?: (response: any) => void;
6
+ onError?: (error: any) => void;
7
+ }
8
+
9
+ const PassphraseValidator: React.FC<PassphraseValidatorProps> = ({
10
+ onSuccess,
11
+ onError
12
+ }) => {
13
+ useEffect(() => {
14
+ const validatePassphrase = async () => {
15
+ try {
16
+ const response = await fetch(`${RIGOBOT_HOST}/v1/auth/public/token`, {
17
+ method: 'POST',
18
+ headers: {
19
+ 'Content-Type': 'application/json',
20
+ },
21
+ body: JSON.stringify({
22
+ passphrase: 'bm29vnv4h43n'
23
+ })
24
+ });
25
+
26
+ const data = await response.json();
27
+
28
+ if (response.ok) {
29
+ onSuccess?.(data);
30
+ } else {
31
+ onError?.(data);
32
+ }
33
+ } catch (error) {
34
+ onError?.(error);
35
+ }
36
+ };
37
+
38
+ validatePassphrase();
39
+ }, [onSuccess, onError]);
40
+
41
+ return (
42
+ <div>
43
+ </div>
44
+ );
45
+ };
46
+
47
+ export default PassphraseValidator;
@@ -14,6 +14,7 @@ import {
14
14
  useConsumableCall,
15
15
  isValidRigoToken,
16
16
  isValidPublicToken,
17
+ getMyPackages,
17
18
  } from "../../utils/lib"
18
19
 
19
20
  import Loader from "../Loader"
@@ -74,7 +75,7 @@ const SyllabusEditor: React.FC = () => {
74
75
  }, [syllabus, navigate])
75
76
 
76
77
  useEffect(() => {
77
- ;(async () => {
78
+ ; (async () => {
78
79
  const { token } = checkParams(["token"])
79
80
  if (token) {
80
81
  const user = await loginWithToken(token)
@@ -92,6 +93,25 @@ const SyllabusEditor: React.FC = () => {
92
93
  checkSlug()
93
94
  }, [])
94
95
 
96
+
97
+ const checkMyPackages = async () => {
98
+ if (auth.rigoToken) {
99
+ const packages = await getMyPackages(auth.rigoToken)
100
+ if (Array.isArray(packages) && packages.length === 0) {
101
+ push({
102
+ ...syllabus,
103
+ generationMode: "continue-with-all",
104
+ })
105
+ }
106
+ }
107
+ }
108
+
109
+ useEffect(() => {
110
+ if (auth.rigoToken) {
111
+ checkMyPackages()
112
+ }
113
+ }, [auth])
114
+
95
115
  const checkSlug = async () => {
96
116
  if (!syllabus.courseInfo.title) {
97
117
  toast.error("Please provide a title for the course")