@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.
- package/lib/creatorDist/assets/{index-7zTdUX04.js → index-CQMtxRqZ.js} +4828 -4797
- package/lib/creatorDist/index.html +1 -1
- package/lib/managers/session.js +2 -2
- package/lib/utils/export/epub.js +22 -5
- package/package.json +1 -1
- package/src/creator/src/App.tsx +64 -58
- package/src/creator/src/components/PassphraseValidator.tsx +47 -0
- package/src/creator/src/components/syllabus/SyllabusEditor.tsx +21 -1
- package/src/creator/src/utils/lib.ts +477 -468
- package/src/creator/src/utils/rigo.ts +85 -85
- package/src/creator/src/utils/store.ts +223 -222
- package/src/creatorDist/assets/{index-7zTdUX04.js → index-CQMtxRqZ.js} +4828 -4797
- package/src/creatorDist/index.html +1 -1
- package/src/managers/session.ts +184 -182
- package/src/ui/_app/app.js +429 -421
- package/src/ui/app.tar.gz +0 -0
- package/src/utils/export/epub.ts +29 -5
@@ -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-
|
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>
|
package/lib/managers/session.js
CHANGED
@@ -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.
|
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.
|
127
|
+
console_1.default.success(`Successfully logged in as user ${payload.user_id}`);
|
128
128
|
}
|
129
129
|
},
|
130
130
|
destroy: async function () {
|
package/lib/utils/export/epub.js
CHANGED
@@ -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
|
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
|
-
|
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.
|
4
|
+
"version": "5.0.286",
|
5
5
|
"author": "Alejandro Sanchez @alesanchezr",
|
6
6
|
"contributors": [
|
7
7
|
{
|
package/src/creator/src/App.tsx
CHANGED
@@ -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
|
206
|
-
const isPublicTokenValid = await isValidPublicToken(auth.publicToken)
|
207
|
-
if (isPublicTokenValid) {
|
208
|
-
|
209
|
-
|
210
|
-
|
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: "
|
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: "
|
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: "
|
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: "
|
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
|
-
|
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
|
-
|
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")
|