@learnpack/learnpack 5.0.283 → 5.0.284

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.
@@ -526,6 +526,18 @@ class ServeCommand extends SessionCommand_1.default {
526
526
  status: "generating",
527
527
  log: `✅ Code file created for ${exercise.title}`,
528
528
  });
529
+ if (readme.parsed.solution_content) {
530
+ const codeFileName = readme.parsed.codefile_name
531
+ .toLowerCase()
532
+ .trim();
533
+ const solutionFileName = "solution.hide." + codeFileName;
534
+ await uploadFileToBucket(bucket, readme.parsed.solution_content, `${targetDir}/${solutionFileName}`);
535
+ (0, creatorSocket_1.emitToCourse)(courseSlug, "course-creation", {
536
+ lesson: exSlug,
537
+ status: "generating",
538
+ log: `✅ Solution file created for ${exercise.title}`,
539
+ });
540
+ }
529
541
  }
530
542
  let nextCompletionId = null;
531
543
  if (nextExercise &&
@@ -541,7 +553,6 @@ class ServeCommand extends SessionCommand_1.default {
541
553
  else {
542
554
  console.log("Stopping generation process at", exerciseIndex, exercise.title, "because it's a multiple of 3");
543
555
  }
544
- const imagesArray = (0, creatorUtilities_1.extractImagesFromMarkdown)(readability.newMarkdown);
545
556
  const newSyllabus = Object.assign(Object.assign({}, syllabusJson), { lessons: syllabusJson.lessons.map((lesson, index) => {
546
557
  if (index === exerciseIndex) {
547
558
  const currentTranslations = lesson.translations || {};
@@ -1306,6 +1317,64 @@ class ServeCommand extends SessionCommand_1.default {
1306
1317
  res.status(500).json({ error: "Failed to fetch the resource" });
1307
1318
  }
1308
1319
  });
1320
+ app.get("/memory-bank/:courseSlug", async (req, res) => {
1321
+ const { courseSlug } = req.params;
1322
+ const rigoToken = req.header("x-rigo-token");
1323
+ if (!rigoToken) {
1324
+ return res.status(400).json({
1325
+ error: "Rigo token is required. x-rigo-token header is missing",
1326
+ });
1327
+ }
1328
+ const { isAuthor } = await (0, rigoActions_1.isPackageAuthor)(rigoToken, courseSlug);
1329
+ if (!isAuthor) {
1330
+ return res.status(403).json({
1331
+ error: "You are not authorized to access the memory bank",
1332
+ });
1333
+ }
1334
+ try {
1335
+ const memoryBankPath = `courses/${courseSlug}/.learn/memory_bank.txt`;
1336
+ const memoryBankFile = bucket.file(memoryBankPath);
1337
+ const [exists] = await memoryBankFile.exists();
1338
+ if (!exists) {
1339
+ return res.json({ content: "" });
1340
+ }
1341
+ const [content] = await memoryBankFile.download();
1342
+ res.json({ content: content.toString() });
1343
+ }
1344
+ catch (error) {
1345
+ console.error("❌ Error fetching memory bank:", error);
1346
+ res.status(500).json({ error: "Failed to fetch memory bank" });
1347
+ }
1348
+ });
1349
+ app.put("/memory-bank/:courseSlug", async (req, res) => {
1350
+ const { courseSlug } = req.params;
1351
+ const { content } = req.body;
1352
+ const rigoToken = req.header("x-rigo-token");
1353
+ if (!rigoToken) {
1354
+ return res.status(400).json({
1355
+ error: "Rigo token is required. x-rigo-token header is missing",
1356
+ });
1357
+ }
1358
+ if (!content) {
1359
+ return res.status(400).json({ error: "Content is required" });
1360
+ }
1361
+ const { isAuthor } = await (0, rigoActions_1.isPackageAuthor)(rigoToken, courseSlug);
1362
+ if (!isAuthor) {
1363
+ return res.status(403).json({
1364
+ error: "You are not authorized to update the memory bank",
1365
+ });
1366
+ }
1367
+ try {
1368
+ const memoryBankPath = `courses/${courseSlug}/.learn/memory_bank.txt`;
1369
+ await uploadFileToBucket(bucket, content, memoryBankPath);
1370
+ console.log(`✅ Memory bank updated for course: ${courseSlug}`);
1371
+ res.json({ message: "Memory bank updated successfully" });
1372
+ }
1373
+ catch (error) {
1374
+ console.error("❌ Error updating memory bank:", error);
1375
+ res.status(500).json({ error: "Failed to update memory bank" });
1376
+ }
1377
+ });
1309
1378
  // Export endpoint (POST) - supports both SCORM and EPUB
1310
1379
  app.post("/export/:course_slug/:format", async (req, res) => {
1311
1380
  const { course_slug, format } = req.params;
@@ -21655,7 +21655,7 @@ function OT() {
21655
21655
  if (
21656
21656
  (oe && oe.toLowerCase().trim() === "true" && b(),
21657
21657
  I && (console.log("description", I), s({ description: I })),
21658
- ee && I && !isNaN(parseInt(ee)))
21658
+ ee && !isNaN(parseInt(ee)))
21659
21659
  )
21660
21660
  if (["30", "60", "120"].includes(ee)) {
21661
21661
  const Q = parseInt(ee);
@@ -21667,8 +21667,8 @@ function OT() {
21667
21667
  U &&
21668
21668
  ["easy", "beginner", "intermediate", "hard"].includes(U) &&
21669
21669
  s({ difficulty: U }),
21670
- I && ee && ne && s({ currentStep: "hasContentIndex" }),
21671
- I && ee && !ne && s({ currentStep: "purpose" });
21670
+ I && ne && s({ currentStep: "duration" }),
21671
+ I && !ne && s({ currentStep: "purpose" });
21672
21672
  },
21673
21673
  F = async () => {
21674
21674
  try {
@@ -21747,6 +21747,20 @@ function OT() {
21747
21747
  },
21748
21748
  }),
21749
21749
  },
21750
+ {
21751
+ title: t("stepWizard.purpose"),
21752
+ slug: "purpose",
21753
+ isCompleted:
21754
+ ((ee = i == null ? void 0 : i.purpose) == null
21755
+ ? void 0
21756
+ : ee.length) > 0,
21757
+ required: !0,
21758
+ content: E.jsx(DT, {
21759
+ onFinish: (te) => {
21760
+ s({ purpose: te, currentStep: "verifyHuman" });
21761
+ },
21762
+ }),
21763
+ },
21750
21764
  {
21751
21765
  title: t("stepWizard.duration"),
21752
21766
  slug: "duration",
@@ -21758,41 +21772,27 @@ function OT() {
21758
21772
  E.jsx(kr, {
21759
21773
  title: t("stepWizard.durationCard.30"),
21760
21774
  onClick: () => {
21761
- s({ duration: 30, currentStep: "purpose" });
21775
+ s({ duration: 30, currentStep: "verifyHuman" });
21762
21776
  },
21763
21777
  selected: i.duration === 30,
21764
21778
  }),
21765
21779
  E.jsx(kr, {
21766
21780
  title: t("stepWizard.durationCard.60"),
21767
21781
  onClick: () => {
21768
- s({ duration: 60, currentStep: "purpose" });
21782
+ s({ duration: 60, currentStep: "verifyHuman" });
21769
21783
  },
21770
21784
  selected: i.duration === 60,
21771
21785
  }),
21772
21786
  E.jsx(kr, {
21773
21787
  title: t("stepWizard.durationCard.120"),
21774
21788
  onClick: () => {
21775
- s({ duration: 120, currentStep: "purpose" });
21789
+ s({ duration: 120, currentStep: "verifyHuman" });
21776
21790
  },
21777
21791
  selected: i.duration === 120,
21778
21792
  }),
21779
21793
  ],
21780
21794
  }),
21781
21795
  },
21782
- {
21783
- title: t("stepWizard.purpose"),
21784
- slug: "purpose",
21785
- isCompleted:
21786
- ((ee = i == null ? void 0 : i.purpose) == null
21787
- ? void 0
21788
- : ee.length) > 0,
21789
- required: !0,
21790
- content: E.jsx(DT, {
21791
- onFinish: (te) => {
21792
- s({ purpose: te, currentStep: "verifyHuman" });
21793
- },
21794
- }),
21795
- },
21796
21796
  {
21797
21797
  title: t("stepWizard.verifyHuman"),
21798
21798
  slug: "verifyHuman",
@@ -21807,11 +21807,11 @@ function OT() {
21807
21807
  console.log("JWT TOKEN received", oe),
21808
21808
  l({ ...y, publicToken: oe }),
21809
21809
  s({ currentStep: "hasContentIndex" }))
21810
- : (Ge.error(re), s({ currentStep: "purpose" }));
21810
+ : (Ge.error(re), s({ currentStep: "duration" }));
21811
21811
  },
21812
21812
  onError: () => {
21813
21813
  Ge.error(t("turnstileModal.error"), { duration: 1e4 }),
21814
- s({ currentStep: "purpose" });
21814
+ s({ currentStep: "duration" });
21815
21815
  },
21816
21816
  }),
21817
21817
  },
@@ -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-BfLyIQVh.js"></script>
13
+ <script type="module" crossorigin src="/creator/assets/index-7zTdUX04.js"></script>
14
14
  <link rel="stylesheet" crossorigin href="/creator/assets/index-C39zeF3W.css">
15
15
  </head>
16
16
  <body>
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.283",
4
+ "version": "5.0.284",
5
5
  "author": "Alejandro Sanchez @alesanchezr",
6
6
  "contributors": [
7
7
  {
@@ -808,6 +808,23 @@ export default class ServeCommand extends SessionCommand {
808
808
  status: "generating",
809
809
  log: `✅ Code file created for ${exercise.title}`,
810
810
  })
811
+
812
+ if (readme.parsed.solution_content) {
813
+ const codeFileName = readme.parsed.codefile_name
814
+ .toLowerCase()
815
+ .trim()
816
+ const solutionFileName = "solution.hide." + codeFileName
817
+ await uploadFileToBucket(
818
+ bucket,
819
+ readme.parsed.solution_content,
820
+ `${targetDir}/${solutionFileName}`
821
+ )
822
+ emitToCourse(courseSlug, "course-creation", {
823
+ lesson: exSlug,
824
+ status: "generating",
825
+ log: `✅ Solution file created for ${exercise.title}`,
826
+ })
827
+ }
811
828
  }
812
829
 
813
830
  let nextCompletionId: number | null = null
@@ -840,10 +857,6 @@ export default class ServeCommand extends SessionCommand {
840
857
  )
841
858
  }
842
859
 
843
- const imagesArray: any[] = extractImagesFromMarkdown(
844
- readability.newMarkdown
845
- )
846
-
847
860
  const newSyllabus = {
848
861
  ...syllabusJson,
849
862
  lessons: syllabusJson.lessons.map((lesson, index) => {
@@ -1956,6 +1969,76 @@ export default class ServeCommand extends SessionCommand {
1956
1969
  }
1957
1970
  })
1958
1971
 
1972
+ app.get("/memory-bank/:courseSlug", async (req, res) => {
1973
+ const { courseSlug } = req.params
1974
+ const rigoToken = req.header("x-rigo-token")
1975
+
1976
+ if (!rigoToken) {
1977
+ return res.status(400).json({
1978
+ error: "Rigo token is required. x-rigo-token header is missing",
1979
+ })
1980
+ }
1981
+
1982
+ const { isAuthor } = await isPackageAuthor(rigoToken, courseSlug)
1983
+
1984
+ if (!isAuthor) {
1985
+ return res.status(403).json({
1986
+ error: "You are not authorized to access the memory bank",
1987
+ })
1988
+ }
1989
+
1990
+ try {
1991
+ const memoryBankPath = `courses/${courseSlug}/.learn/memory_bank.txt`
1992
+ const memoryBankFile = bucket.file(memoryBankPath)
1993
+ const [exists] = await memoryBankFile.exists()
1994
+
1995
+ if (!exists) {
1996
+ return res.json({ content: "" })
1997
+ }
1998
+
1999
+ const [content] = await memoryBankFile.download()
2000
+ res.json({ content: content.toString() })
2001
+ } catch (error) {
2002
+ console.error("❌ Error fetching memory bank:", error)
2003
+ res.status(500).json({ error: "Failed to fetch memory bank" })
2004
+ }
2005
+ })
2006
+
2007
+ app.put("/memory-bank/:courseSlug", async (req, res) => {
2008
+ const { courseSlug } = req.params
2009
+ const { content } = req.body
2010
+ const rigoToken = req.header("x-rigo-token")
2011
+
2012
+ if (!rigoToken) {
2013
+ return res.status(400).json({
2014
+ error: "Rigo token is required. x-rigo-token header is missing",
2015
+ })
2016
+ }
2017
+
2018
+ if (!content) {
2019
+ return res.status(400).json({ error: "Content is required" })
2020
+ }
2021
+
2022
+ const { isAuthor } = await isPackageAuthor(rigoToken, courseSlug)
2023
+
2024
+ if (!isAuthor) {
2025
+ return res.status(403).json({
2026
+ error: "You are not authorized to update the memory bank",
2027
+ })
2028
+ }
2029
+
2030
+ try {
2031
+ const memoryBankPath = `courses/${courseSlug}/.learn/memory_bank.txt`
2032
+ await uploadFileToBucket(bucket, content, memoryBankPath)
2033
+
2034
+ console.log(`✅ Memory bank updated for course: ${courseSlug}`)
2035
+ res.json({ message: "Memory bank updated successfully" })
2036
+ } catch (error) {
2037
+ console.error("❌ Error updating memory bank:", error)
2038
+ res.status(500).json({ error: "Failed to update memory bank" })
2039
+ }
2040
+ })
2041
+
1959
2042
  // Export endpoint (POST) - supports both SCORM and EPUB
1960
2043
  app.post("/export/:course_slug/:format", async (req, res) => {
1961
2044
  const { course_slug, format } = req.params
@@ -128,7 +128,7 @@ function App() {
128
128
  description: description,
129
129
  })
130
130
  }
131
- if (duration && description && !isNaN(parseInt(duration))) {
131
+ if (duration && !isNaN(parseInt(duration))) {
132
132
  if (["30", "60", "120"].includes(duration)) {
133
133
  const durationInt = parseInt(duration)
134
134
  console.log("duration", durationInt)
@@ -167,12 +167,12 @@ function App() {
167
167
  })
168
168
  }
169
169
 
170
- if (description && duration && purpose) {
170
+ if (description && purpose) {
171
171
  setFormState({
172
- currentStep: "hasContentIndex",
172
+ currentStep: "duration",
173
173
  })
174
174
  }
175
- if (description && duration && !purpose) {
175
+ if (description && !purpose) {
176
176
  setFormState({
177
177
  currentStep: "purpose",
178
178
  })
@@ -285,7 +285,22 @@ function App() {
285
285
  />
286
286
  ),
287
287
  },
288
-
288
+ {
289
+ title: t("stepWizard.purpose"),
290
+ slug: "purpose",
291
+ isCompleted: formState?.purpose?.length > 0,
292
+ required: true,
293
+ content: (
294
+ <PurposeSelector
295
+ onFinish={(purpose) => {
296
+ setFormState({
297
+ purpose: purpose,
298
+ currentStep: "verifyHuman",
299
+ })
300
+ }}
301
+ />
302
+ ),
303
+ },
289
304
  {
290
305
  title: t("stepWizard.duration"),
291
306
  slug: "duration",
@@ -299,7 +314,7 @@ function App() {
299
314
  onClick={() => {
300
315
  setFormState({
301
316
  duration: 30,
302
- currentStep: "purpose",
317
+ currentStep: "verifyHuman",
303
318
  })
304
319
  }}
305
320
  selected={formState.duration === 30}
@@ -310,7 +325,7 @@ function App() {
310
325
  onClick={() => {
311
326
  setFormState({
312
327
  duration: 60,
313
- currentStep: "purpose",
328
+ currentStep: "verifyHuman",
314
329
  })
315
330
  }}
316
331
  selected={formState.duration === 60}
@@ -321,7 +336,7 @@ function App() {
321
336
  onClick={() => {
322
337
  setFormState({
323
338
  duration: 120,
324
- currentStep: "purpose",
339
+ currentStep: "verifyHuman",
325
340
  })
326
341
  }}
327
342
  selected={formState.duration === 120}
@@ -329,22 +344,6 @@ function App() {
329
344
  </div>
330
345
  ),
331
346
  },
332
- {
333
- title: t("stepWizard.purpose"),
334
- slug: "purpose",
335
- isCompleted: formState?.purpose?.length > 0,
336
- required: true,
337
- content: (
338
- <PurposeSelector
339
- onFinish={(purpose) => {
340
- setFormState({
341
- purpose: purpose,
342
- currentStep: "verifyHuman",
343
- })
344
- }}
345
- />
346
- ),
347
- },
348
347
  {
349
348
  title: t("stepWizard.verifyHuman"),
350
349
  slug: "verifyHuman",
@@ -371,7 +370,7 @@ function App() {
371
370
  } else {
372
371
  toast.error(message)
373
372
  setFormState({
374
- currentStep: "purpose",
373
+ currentStep: "duration",
375
374
  })
376
375
  }
377
376
  }}
@@ -380,7 +379,7 @@ function App() {
380
379
  duration: 10000,
381
380
  })
382
381
  setFormState({
383
- currentStep: "purpose",
382
+ currentStep: "duration",
384
383
  })
385
384
  }}
386
385
  />
@@ -21655,7 +21655,7 @@ function OT() {
21655
21655
  if (
21656
21656
  (oe && oe.toLowerCase().trim() === "true" && b(),
21657
21657
  I && (console.log("description", I), s({ description: I })),
21658
- ee && I && !isNaN(parseInt(ee)))
21658
+ ee && !isNaN(parseInt(ee)))
21659
21659
  )
21660
21660
  if (["30", "60", "120"].includes(ee)) {
21661
21661
  const Q = parseInt(ee);
@@ -21667,8 +21667,8 @@ function OT() {
21667
21667
  U &&
21668
21668
  ["easy", "beginner", "intermediate", "hard"].includes(U) &&
21669
21669
  s({ difficulty: U }),
21670
- I && ee && ne && s({ currentStep: "hasContentIndex" }),
21671
- I && ee && !ne && s({ currentStep: "purpose" });
21670
+ I && ne && s({ currentStep: "duration" }),
21671
+ I && !ne && s({ currentStep: "purpose" });
21672
21672
  },
21673
21673
  F = async () => {
21674
21674
  try {
@@ -21747,6 +21747,20 @@ function OT() {
21747
21747
  },
21748
21748
  }),
21749
21749
  },
21750
+ {
21751
+ title: t("stepWizard.purpose"),
21752
+ slug: "purpose",
21753
+ isCompleted:
21754
+ ((ee = i == null ? void 0 : i.purpose) == null
21755
+ ? void 0
21756
+ : ee.length) > 0,
21757
+ required: !0,
21758
+ content: E.jsx(DT, {
21759
+ onFinish: (te) => {
21760
+ s({ purpose: te, currentStep: "verifyHuman" });
21761
+ },
21762
+ }),
21763
+ },
21750
21764
  {
21751
21765
  title: t("stepWizard.duration"),
21752
21766
  slug: "duration",
@@ -21758,41 +21772,27 @@ function OT() {
21758
21772
  E.jsx(kr, {
21759
21773
  title: t("stepWizard.durationCard.30"),
21760
21774
  onClick: () => {
21761
- s({ duration: 30, currentStep: "purpose" });
21775
+ s({ duration: 30, currentStep: "verifyHuman" });
21762
21776
  },
21763
21777
  selected: i.duration === 30,
21764
21778
  }),
21765
21779
  E.jsx(kr, {
21766
21780
  title: t("stepWizard.durationCard.60"),
21767
21781
  onClick: () => {
21768
- s({ duration: 60, currentStep: "purpose" });
21782
+ s({ duration: 60, currentStep: "verifyHuman" });
21769
21783
  },
21770
21784
  selected: i.duration === 60,
21771
21785
  }),
21772
21786
  E.jsx(kr, {
21773
21787
  title: t("stepWizard.durationCard.120"),
21774
21788
  onClick: () => {
21775
- s({ duration: 120, currentStep: "purpose" });
21789
+ s({ duration: 120, currentStep: "verifyHuman" });
21776
21790
  },
21777
21791
  selected: i.duration === 120,
21778
21792
  }),
21779
21793
  ],
21780
21794
  }),
21781
21795
  },
21782
- {
21783
- title: t("stepWizard.purpose"),
21784
- slug: "purpose",
21785
- isCompleted:
21786
- ((ee = i == null ? void 0 : i.purpose) == null
21787
- ? void 0
21788
- : ee.length) > 0,
21789
- required: !0,
21790
- content: E.jsx(DT, {
21791
- onFinish: (te) => {
21792
- s({ purpose: te, currentStep: "verifyHuman" });
21793
- },
21794
- }),
21795
- },
21796
21796
  {
21797
21797
  title: t("stepWizard.verifyHuman"),
21798
21798
  slug: "verifyHuman",
@@ -21807,11 +21807,11 @@ function OT() {
21807
21807
  console.log("JWT TOKEN received", oe),
21808
21808
  l({ ...y, publicToken: oe }),
21809
21809
  s({ currentStep: "hasContentIndex" }))
21810
- : (Ge.error(re), s({ currentStep: "purpose" }));
21810
+ : (Ge.error(re), s({ currentStep: "duration" }));
21811
21811
  },
21812
21812
  onError: () => {
21813
21813
  Ge.error(t("turnstileModal.error"), { duration: 1e4 }),
21814
- s({ currentStep: "purpose" });
21814
+ s({ currentStep: "duration" });
21815
21815
  },
21816
21816
  }),
21817
21817
  },
@@ -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-BfLyIQVh.js"></script>
13
+ <script type="module" crossorigin src="/creator/assets/index-7zTdUX04.js"></script>
14
14
  <link rel="stylesheet" crossorigin href="/creator/assets/index-C39zeF3W.css">
15
15
  </head>
16
16
  <body>