@learnpack/learnpack 5.0.267 → 5.0.269

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,8 +10,8 @@
10
10
  />
11
11
 
12
12
  <title>Learnpack Creator: Craft tutorials in seconds!</title>
13
- <script type="module" crossorigin src="/creator/assets/index-BXp9oelr.js"></script>
14
- <link rel="stylesheet" crossorigin href="/creator/assets/index-DmpsXknz.css">
13
+ <script type="module" crossorigin src="/creator/assets/index-CQXTTbaZ.js"></script>
14
+ <link rel="stylesheet" crossorigin href="/creator/assets/index-B4khtb0r.css">
15
15
  </head>
16
16
  <body>
17
17
  <div id="root"></div>
@@ -6,6 +6,7 @@ export interface Lesson {
6
6
  description: string;
7
7
  duration?: number;
8
8
  generated?: boolean;
9
+ status?: "PENDING" | "GENERATING" | "DONE" | "ERROR";
9
10
  }
10
11
  export interface ParsedLink {
11
12
  name: string;
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.267",
4
+ "version": "5.0.269",
5
5
  "author": "Alejandro Sanchez @alesanchezr",
6
6
  "contributors": [
7
7
  {
@@ -608,6 +608,64 @@ export default class ServeCommand extends SessionCommand {
608
608
  }
609
609
  })
610
610
 
611
+ app.post(
612
+ "/actions/continue-generating/:courseSlug/:position",
613
+ async (req, res) => {
614
+ const { courseSlug, position } = req.params
615
+ const { feedback } = req.body
616
+ const rigoToken = req.header("x-rigo-token")
617
+
618
+ if (!rigoToken) {
619
+ return res.status(400).json({
620
+ error: "Rigo token is required. x-rigo-token header is missing",
621
+ })
622
+ }
623
+
624
+ const syllabus = await bucket.file(
625
+ `courses/${courseSlug}/.learn/initialSyllabus.json`
626
+ )
627
+ const [content] = await syllabus.download()
628
+ const syllabusJson: Syllabus = JSON.parse(content.toString())
629
+
630
+ const exercise = syllabusJson.lessons[parseInt(position)]
631
+
632
+ // previous exercise
633
+ let previousReadme = "---"
634
+ const previousExercise = syllabusJson.lessons[parseInt(position) - 1]
635
+ if (previousExercise) {
636
+ // Get the readme of the previous exercise
637
+ const exSlug = slugify(
638
+ previousExercise.id + "-" + previousExercise.title
639
+ )
640
+ // llist the files un
641
+ const [files] = await bucket.getFiles({
642
+ prefix: `courses/${courseSlug}/exercises/${exSlug}/README`,
643
+ })
644
+ // select any README
645
+ const readmeFiles = files.map(f => f.name)
646
+ const readmeFile = readmeFiles.find(f => f.includes("README"))
647
+ if (readmeFile) {
648
+ const [content] = await bucket.file(readmeFile).download()
649
+ previousReadme = content.toString()
650
+ }
651
+ }
652
+
653
+ await startExerciseGeneration(
654
+ bucket,
655
+ rigoToken,
656
+ syllabusJson.lessons,
657
+ syllabusJson.courseInfo,
658
+ exercise,
659
+ `courses/${courseSlug}`,
660
+ courseSlug,
661
+ syllabusJson.courseInfo.purpose,
662
+ previousReadme +
663
+ "\n\nThe user provided the following feedback related to the content of the course so far: \n\n" +
664
+ feedback
665
+ )
666
+ }
667
+ )
668
+
611
669
  app.post(
612
670
  "/webhooks/:courseSlug/exercise-processor/:lessonID/:rigoToken",
613
671
  async (req, res) => {
@@ -620,6 +678,15 @@ export default class ServeCommand extends SessionCommand {
620
678
  )
621
679
  const [content] = await syllabus.download()
622
680
  const syllabusJson: Syllabus = JSON.parse(content.toString())
681
+
682
+ if (readme.status === "ERROR") {
683
+ emitToCourse(courseSlug, "course-creation", {
684
+ lesson: lessonID,
685
+ status: "error",
686
+ log: `❌ Error generating the lesson ${lessonID}`,
687
+ })
688
+ }
689
+
623
690
  const exerciseIndex = syllabusJson.lessons.findIndex(
624
691
  lesson => lesson.id === lessonID
625
692
  )
@@ -632,17 +699,15 @@ export default class ServeCommand extends SessionCommand {
632
699
  }
633
700
 
634
701
  const exercise = syllabusJson.lessons[exerciseIndex]
635
-
636
- const nextExercise = syllabusJson.lessons[exerciseIndex + 1] || null
637
-
638
702
  if (!exercise) {
639
- console.log(
640
- "Exercise not found receiving webhook, this should not happen",
641
- lessonID
642
- )
643
- return res.json({ status: "SUCCESS" })
703
+ return res.json({
704
+ status: "ERROR",
705
+ error: "Exercise not found or is invalid",
706
+ })
644
707
  }
645
708
 
709
+ const nextExercise = syllabusJson.lessons[exerciseIndex + 1] || null
710
+
646
711
  const exSlug = slugify(exercise.id + "-" + exercise.title)
647
712
 
648
713
  const readability = checkReadability(
@@ -660,11 +725,9 @@ export default class ServeCommand extends SessionCommand {
660
725
  const exercisesDir = `courses/${courseSlug}/exercises`
661
726
  const targetDir = `${exercisesDir}/${exSlug}`
662
727
 
663
- const readmeFilename = `README.${
664
- readme.parsed.language_code && readme.parsed.language_code !== "en" ?
665
- `${readme.parsed.language_code}.` :
666
- ""
667
- }md`
728
+ const readmeFilename = `README${getReadmeExtension(
729
+ readme.parsed.language_code
730
+ )}`
668
731
 
669
732
  await uploadFileToBucket(
670
733
  bucket,
@@ -689,12 +752,16 @@ export default class ServeCommand extends SessionCommand {
689
752
  )
690
753
  emitToCourse(courseSlug, "course-creation", {
691
754
  lesson: exSlug,
692
- status: "done",
755
+ status: "generating",
693
756
  log: `✅ Code file created for ${exercise.title}`,
694
757
  })
695
758
  }
696
759
 
697
- if (nextExercise) {
760
+ let nextStarted = false
761
+ if (
762
+ nextExercise &&
763
+ (exerciseIndex === 0 || !(exerciseIndex % 3 === 0))
764
+ ) {
698
765
  startExerciseGeneration(
699
766
  bucket,
700
767
  rigoToken,
@@ -706,6 +773,14 @@ export default class ServeCommand extends SessionCommand {
706
773
  syllabusJson.courseInfo.purpose,
707
774
  readme.parsed.content
708
775
  )
776
+ nextStarted = true
777
+ } else {
778
+ console.log(
779
+ "Stopping generation process at",
780
+ exerciseIndex,
781
+ exercise.title,
782
+ "because it's a multiple of 3"
783
+ )
709
784
  }
710
785
 
711
786
  const imagesArray: any[] = extractImagesFromMarkdown(
@@ -724,29 +799,31 @@ export default class ServeCommand extends SessionCommand {
724
799
  }
725
800
  }
726
801
 
727
- emitToCourse(courseSlug, "course-creation", {
728
- lesson: exSlug,
729
- status: "done",
730
- log: `✅ The lesson ${exercise.id} - ${exercise.title} has been generated successfully!`,
731
- })
732
-
733
802
  const newSyllabus = {
734
803
  ...syllabusJson,
735
804
  lessons: syllabusJson.lessons.map(lesson => {
736
805
  if (lesson.id === exercise.id) {
737
- return { ...lesson, generated: true }
806
+ return { ...lesson, generated: true, status: "DONE" }
738
807
  }
739
808
 
740
- return lesson
809
+ if (nextExercise && nextExercise.id === lesson.id && !nextStarted) {
810
+ return { ...lesson, generated: false, status: "GENERATING" }
811
+ }
812
+
813
+ return { ...lesson }
741
814
  }),
742
815
  }
743
-
744
816
  await uploadFileToBucket(
745
817
  bucket,
746
818
  JSON.stringify(newSyllabus),
747
819
  `courses/${courseSlug}/.learn/initialSyllabus.json`
748
820
  )
749
821
 
822
+ emitToCourse(courseSlug, "course-creation", {
823
+ lesson: exSlug,
824
+ status: "done",
825
+ log: `✅ The lesson ${exercise.id} - ${exercise.title} has been generated successfully!`,
826
+ })
750
827
  res.json({ status: "SUCCESS" })
751
828
  }
752
829
  )
@@ -1321,12 +1398,13 @@ export default class ServeCommand extends SessionCommand {
1321
1398
  ...syllabus,
1322
1399
  lessons: syllabus.lessons.map((lesson, index) => {
1323
1400
  if (index < 1) {
1324
- return { ...lesson, generated: true }
1401
+ return { ...lesson, generated: false, status: "GENERATING" }
1325
1402
  }
1326
1403
 
1327
1404
  return {
1328
1405
  ...lesson,
1329
1406
  generated: false,
1407
+ status: "PENDING",
1330
1408
  }
1331
1409
  }),
1332
1410
  }
@@ -1343,7 +1421,7 @@ export default class ServeCommand extends SessionCommand {
1343
1421
  )
1344
1422
  const firstLesson = syllabus.lessons[0]
1345
1423
 
1346
- const lastResult = "Nothing"
1424
+ const lastResult = "---"
1347
1425
 
1348
1426
  await startExerciseGeneration(
1349
1427
  bucket,
@@ -1364,7 +1442,7 @@ export default class ServeCommand extends SessionCommand {
1364
1442
  )
1365
1443
 
1366
1444
  return res.json({
1367
- message: "Course created",
1445
+ message: "Course generation started",
1368
1446
  slug: syllabus.courseInfo.slug,
1369
1447
  })
1370
1448
  })
@@ -30,6 +30,7 @@ import { useTranslation } from "react-i18next"
30
30
  import NotificationListener from "./components/NotificationListener"
31
31
  import { slugify } from "./utils/creatorUtils"
32
32
  import TurnstileModal from "./components/TurnstileModal"
33
+ import { TMessage } from "./components/Message"
33
34
 
34
35
  function App() {
35
36
  const navigate = useNavigate()
@@ -503,7 +504,7 @@ function App() {
503
504
  i18n.changeLanguage(res.parsed.languageCode)
504
505
  }
505
506
 
506
- setMessages([
507
+ let initialMessages: TMessage[] = [
507
508
  {
508
509
  type: "user",
509
510
  content: formState.description,
@@ -512,15 +513,20 @@ function App() {
512
513
  type: "assistant",
513
514
  content: res.parsed.aiMessage,
514
515
  },
515
- {
516
+ ]
517
+
518
+ if (lessons.length > 0) {
519
+ initialMessages.push({
516
520
  type: "assistant",
517
521
  content: t("contentIndex.okMessage"),
518
- },
519
- {
522
+ })
523
+ initialMessages.push({
520
524
  type: "assistant",
521
525
  content: t("contentIndex.instructionsMessage"),
522
- },
523
- ])
526
+ })
527
+ }
528
+
529
+ setMessages(initialMessages)
524
530
  navigate("/creator/syllabus")
525
531
  setFormState({
526
532
  isCompleted: false,
@@ -246,6 +246,11 @@ export const ContentIndex = ({
246
246
  ) : (
247
247
  <>
248
248
  <ContentSecondaryHeader messages={messages} />
249
+ {syllabus.lessons.length === 0 && messages.length < 3 && (
250
+ <div className="text-center text-gray-200 font-bold text-lg">
251
+ {t("contentIndex.noLessons")}
252
+ </div>
253
+ )}
249
254
  {syllabus.lessons.map((lesson, index) => (
250
255
  <div key={lesson.uid + index}>
251
256
  <LessonItem
@@ -81,7 +81,8 @@
81
81
  "contentIndex": {
82
82
  "subText": {
83
83
  "first": "It includes a mix of reading, coding exercises, and quizzes. Give it a look and let me know if it aligns with your expectations or if there are any changes you'd like to make.",
84
- "second": "Based on your input, here is the new syllabus, updates are highlighted in yellow"
84
+ "second": "Based on your input, here is the new syllabus, updates are highlighted in yellow",
85
+ "noLessons": "The content could not be generated, please talk with Rigobot to give it more context."
85
86
  },
86
87
  "revertChanges": "Revert changes",
87
88
  "changesReverted": "Changes reverted!",
@@ -81,7 +81,8 @@
81
81
  "contentIndex": {
82
82
  "subText": {
83
83
  "first": "Incluye una mezcla de lectura, ejercicios de codificación y cuestionarios. Dále un vistazo y dímelo si coincide con tus expectativas o si hay algún cambio que te gustaría hacer.",
84
- "second": "Basado en tu entrada, aquí está el nuevo plan de estudios, los cambios se resaltan en amarillo"
84
+ "second": "Basado en tu entrada, aquí está el nuevo plan de estudios, los cambios se resaltan en amarillo",
85
+ "noLessons": "El contenido no pudo ser generado, por favor habla con Rigobot para darle más contexto."
85
86
  },
86
87
  "revertChanges": "Revertir cambios",
87
88
  "changesReverted": "Cambios revertidos!",
@@ -20,7 +20,6 @@ export const publicInteractiveCreation = async (
20
20
  DEV_MODE
21
21
  ? "https://9cw5zmww-3000.use2.devtunnels.ms"
22
22
  : window.location.origin
23
- // : window.location.origin
24
23
  // "https://9cw5zmww-3000.use2.devtunnels.ms"
25
24
  }/notifications/${randomUID}`
26
25
 
@@ -1043,6 +1043,9 @@
1043
1043
  .text-blue-900 {
1044
1044
  color: var(--color-blue-900);
1045
1045
  }
1046
+ .text-gray-200 {
1047
+ color: var(--color-gray-200);
1048
+ }
1046
1049
  .text-gray-400 {
1047
1050
  color: var(--color-gray-400);
1048
1051
  }