@learnpack/learnpack 5.0.132 → 5.0.136

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-wpTTgviz.js"></script>
13
+ <script type="module" crossorigin src="/creator/assets/index-nI-XBrtB.js"></script>
14
14
  <link rel="stylesheet" crossorigin href="/creator/assets/index-1sKQbOKY.css">
15
15
  </head>
16
16
  <body>
@@ -0,0 +1,4 @@
1
+ import { Server as SocketIOServer } from "socket.io";
2
+ export declare function initSocketIO(server: any): SocketIOServer<import("socket.io/dist/typed-events").DefaultEventsMap, import("socket.io/dist/typed-events").DefaultEventsMap, import("socket.io/dist/typed-events").DefaultEventsMap, any>;
3
+ export declare function emitToCourse(courseSlug: string, event: string, payload: any): void;
4
+ export declare function getSocketIO(): SocketIOServer<import("socket.io/dist/typed-events").DefaultEventsMap, import("socket.io/dist/typed-events").DefaultEventsMap, import("socket.io/dist/typed-events").DefaultEventsMap, any>;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.initSocketIO = initSocketIO;
4
+ exports.emitToCourse = emitToCourse;
5
+ exports.getSocketIO = getSocketIO;
6
+ const socket_io_1 = require("socket.io");
7
+ const courseSocketMap = new Map(); // slug -> Set<socket.id>
8
+ const socketStore = new Map(); // socket.id -> socket
9
+ let io = null;
10
+ function initSocketIO(server) {
11
+ io = new socket_io_1.Server(server, {
12
+ cors: {
13
+ origin: "*",
14
+ methods: ["GET", "POST"],
15
+ },
16
+ path: "/sockete",
17
+ });
18
+ io.on("connection", socket => {
19
+ console.log("🧠 Socket connected:", socket.id);
20
+ socketStore.set(socket.id, socket);
21
+ socket.on("register", (data) => {
22
+ var _a;
23
+ const { courseSlug } = data;
24
+ if (!courseSlug)
25
+ return;
26
+ if (!courseSocketMap.has(courseSlug)) {
27
+ courseSocketMap.set(courseSlug, new Set());
28
+ }
29
+ (_a = courseSocketMap.get(courseSlug)) === null || _a === void 0 ? void 0 : _a.add(socket.id);
30
+ console.log(`📦 Socket ${socket.id} registered to course: ${courseSlug}`);
31
+ });
32
+ socket.on("disconnect", () => {
33
+ console.log("🔥 Socket disconnected:", socket.id);
34
+ socketStore.delete(socket.id);
35
+ for (const set of courseSocketMap.values()) {
36
+ set.delete(socket.id);
37
+ }
38
+ });
39
+ });
40
+ return io;
41
+ }
42
+ function emitToCourse(courseSlug, event, payload) {
43
+ const socketIds = courseSocketMap.get(courseSlug);
44
+ if (!socketIds || socketIds.size === 0)
45
+ return;
46
+ for (const id of socketIds) {
47
+ const socket = socketStore.get(id);
48
+ if (socket)
49
+ socket.emit(event, payload);
50
+ }
51
+ }
52
+ function getSocketIO() {
53
+ if (!io)
54
+ throw new Error("Socket.IO not initialized");
55
+ return io;
56
+ }
@@ -1 +1 @@
1
- {"version":"5.0.132","commands":{"audit":{"id":"audit","description":"learnpack audit is the command in charge of creating an auditory of the repository\n...\nlearnpack audit checks for the following information in a repository:\n 1. The configuration object has slug, repository and description. (Error)\n 2. The command learnpack clean has been run. (Error)\n 3. If a markdown or test file doesn't have any content. (Error)\n 4. The links are accessing to valid servers. (Error)\n 5. The relative images are working (If they have the shortest path to the image or if the images exists in the assets). (Error)\n 6. The external images are working (If they are pointing to a valid server). (Error)\n 7. The exercises directory names are valid. (Error)\n 8. If an exercise doesn't have a README file. (Error)\n 9. The exercises array (Of the config file) has content. (Error)\n 10. The exercses have the same translations. (Warning)\n 11. The .gitignore file exists. (Warning)\n 12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"strict":{"name":"strict","type":"boolean","char":"s","description":"strict mode","allowNo":false}},"args":[]},"breakToken":{"id":"breakToken","description":"Break the token","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"clean":{"id":"clean","description":"Clean the configuration object\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"download":{"id":"download","description":"Describe the command here\n...\nExtra documentation goes here\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"init":{"id":"init","description":"Create a new learning package: Book, Tutorial or Exercise","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"login":{"id":"login","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"logout":{"id":"logout","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"publish":{"id":"publish","description":"Builds the project by copying necessary files and directories into a zip file","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"strict":{"name":"strict","type":"boolean","char":"s","description":"strict mode","allowNo":false},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"serve":{"id":"serve","description":"Runs a small server to build tutorials","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"port":{"name":"port","type":"option","char":"p","description":"server port"},"host":{"name":"host","type":"option","char":"h","description":"server host"},"debug":{"name":"debug","type":"boolean","char":"d","description":"debugger mode for more verbage","allowNo":false}},"args":[]},"start":{"id":"start","description":"Runs a small server with all the exercise instructions","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"port":{"name":"port","type":"option","char":"p","description":"server port"},"host":{"name":"host","type":"option","char":"h","description":"server host"},"disableGrading":{"name":"disableGrading","type":"boolean","char":"D","description":"disble grading functionality","allowNo":false},"watch":{"name":"watch","type":"boolean","char":"w","description":"Watch for file changes","allowNo":false},"editor":{"name":"editor","type":"option","char":"e","description":"[preview, extension]","options":["extension","preview"]},"version":{"name":"version","type":"option","char":"v","description":"E.g: 1.0.1"},"grading":{"name":"grading","type":"option","char":"g","description":"[isolated, incremental]","options":["isolated","incremental"]},"debug":{"name":"debug","type":"boolean","char":"d","description":"debugger mode for more verbage","allowNo":false}},"args":[]},"test":{"id":"test","description":"Test exercises","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false}},"args":[{"name":"exerciseSlug","description":"The name of the exercise to test","required":false,"hidden":false}]},"translate":{"id":"translate","description":"List all the lessons, the user is able of select many of them to translate to the given languages","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false}},"args":[]}}}
1
+ {"version":"5.0.136","commands":{"audit":{"id":"audit","description":"learnpack audit is the command in charge of creating an auditory of the repository\n...\nlearnpack audit checks for the following information in a repository:\n 1. The configuration object has slug, repository and description. (Error)\n 2. The command learnpack clean has been run. (Error)\n 3. If a markdown or test file doesn't have any content. (Error)\n 4. The links are accessing to valid servers. (Error)\n 5. The relative images are working (If they have the shortest path to the image or if the images exists in the assets). (Error)\n 6. The external images are working (If they are pointing to a valid server). (Error)\n 7. The exercises directory names are valid. (Error)\n 8. If an exercise doesn't have a README file. (Error)\n 9. The exercises array (Of the config file) has content. (Error)\n 10. The exercses have the same translations. (Warning)\n 11. The .gitignore file exists. (Warning)\n 12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"strict":{"name":"strict","type":"boolean","char":"s","description":"strict mode","allowNo":false}},"args":[]},"breakToken":{"id":"breakToken","description":"Break the token","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"clean":{"id":"clean","description":"Clean the configuration object\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"download":{"id":"download","description":"Describe the command here\n...\nExtra documentation goes here\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"init":{"id":"init","description":"Create a new learning package: Book, Tutorial or Exercise","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"login":{"id":"login","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"logout":{"id":"logout","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"publish":{"id":"publish","description":"Builds the project by copying necessary files and directories into a zip file","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"strict":{"name":"strict","type":"boolean","char":"s","description":"strict mode","allowNo":false},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"serve":{"id":"serve","description":"Runs a small server to build tutorials","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"port":{"name":"port","type":"option","char":"p","description":"server port"},"host":{"name":"host","type":"option","char":"h","description":"server host"},"debug":{"name":"debug","type":"boolean","char":"d","description":"debugger mode for more verbage","allowNo":false}},"args":[]},"start":{"id":"start","description":"Runs a small server with all the exercise instructions","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"port":{"name":"port","type":"option","char":"p","description":"server port"},"host":{"name":"host","type":"option","char":"h","description":"server host"},"disableGrading":{"name":"disableGrading","type":"boolean","char":"D","description":"disble grading functionality","allowNo":false},"watch":{"name":"watch","type":"boolean","char":"w","description":"Watch for file changes","allowNo":false},"editor":{"name":"editor","type":"option","char":"e","description":"[preview, extension]","options":["extension","preview"]},"version":{"name":"version","type":"option","char":"v","description":"E.g: 1.0.1"},"grading":{"name":"grading","type":"option","char":"g","description":"[isolated, incremental]","options":["isolated","incremental"]},"debug":{"name":"debug","type":"boolean","char":"d","description":"debugger mode for more verbage","allowNo":false}},"args":[]},"test":{"id":"test","description":"Test exercises","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false}},"args":[{"name":"exerciseSlug","description":"The name of the exercise to test","required":false,"hidden":false}]},"translate":{"id":"translate","description":"List all the lessons, the user is able of select many of them to translate to the given languages","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false}},"args":[]}}}
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.132",
4
+ "version": "5.0.136",
5
5
  "author": "Alejandro Sanchez @alesanchezr",
6
6
  "contributors": [
7
7
  {
@@ -4,6 +4,10 @@ import { YoutubeTranscript } from "youtube-transcript"
4
4
  import * as express from "express"
5
5
  import * as cors from "cors"
6
6
  import * as path from "path"
7
+ import * as http from "http"
8
+
9
+ import { initSocketIO, emitToCourse } from "../utils/creatorSocket"
10
+
7
11
  import * as os from "os"
8
12
  import * as archiver from "archiver"
9
13
  import * as mkdirp from "mkdirp"
@@ -127,7 +131,8 @@ export async function processExercise(
127
131
  steps: Lesson[],
128
132
  packageContext: string,
129
133
  exercise: Lesson,
130
- exercisesDir: string
134
+ exercisesDir: string,
135
+ courseSlug: string
131
136
  ): Promise<string> {
132
137
  // const tid = toast.loading("Generating lesson...")
133
138
  const exSlug = slugify(exercise.id + "-" + exercise.title)
@@ -135,12 +140,12 @@ export async function processExercise(
135
140
  const targetDir = `${exercisesDir}/${exSlug}`
136
141
 
137
142
  console.log("✍🏻 Generating lesson", exercise.id, exercise.title)
138
- const isGeneratingText = `The lesson ${exercise.id} - ${exercise.title} is being generated by Rigobot, wait a sec!
139
-
143
+
144
+ const isGeneratingText = `
140
145
  \`\`\`loader slug="${exSlug}"
141
- # This lesson is being generated by Rigobot, wait a sec!
146
+ :rigo
142
147
  \`\`\`
143
- `
148
+ `
144
149
 
145
150
  await uploadFileToBucket(
146
151
  bucket,
@@ -165,13 +170,11 @@ export async function processExercise(
165
170
  readability.fkglResult.fkgl > PARAMS.max_fkgl &&
166
171
  attempts < PARAMS.max_rewrite_attempts
167
172
  ) {
168
- console.log(
169
- "🔄 The lesson",
170
- exercise.id,
171
- exercise.title,
172
- "has a readability score of",
173
- readability.fkglResult.fkgl
174
- )
173
+ emitToCourse(courseSlug, "course-creation", {
174
+ lesson: exSlug,
175
+ status: "generating",
176
+ log: `🔄 The lesson ${exercise.id} - ${exercise.title} has a readability score of ${readability.fkglResult.fkgl}`,
177
+ })
175
178
 
176
179
  // eslint-disable-next-line
177
180
  const reducedReadme = await makeReadmeReadable(rigoToken, {
@@ -194,21 +197,16 @@ break
194
197
  attempts++
195
198
  }
196
199
 
197
- console.log(
198
- `✅ After ${attempts} attempts, the lesson ${
199
- exercise.title
200
- } has a readability score of ${
201
- readability.fkglResult.fkgl
202
- } using FKGL. And it has ${readability.minutes.toFixed(
203
- 2
204
- )} minutes of reading time.`
205
- )
206
-
207
200
  await uploadFileToBucket(
208
201
  bucket,
209
202
  readability.newMarkdown,
210
203
  `${targetDir}/${readmeFilename}`
211
204
  )
205
+ emitToCourse(courseSlug, "course-creation", {
206
+ lesson: exSlug,
207
+ status: "done",
208
+ log: `✅ The lesson ${exercise.id} - ${exercise.title} has been generated successfully!`,
209
+ })
212
210
 
213
211
  if (exercise.type.toLowerCase() === "code") {
214
212
  console.log("🔍 Creating code file for", exercise.title)
@@ -222,6 +220,11 @@ break
222
220
  codeFile.parsed.content,
223
221
  `${targetDir}/index.${codeFile.parsed.extension.replace(".", "")}`
224
222
  )
223
+ emitToCourse(courseSlug, "course-creation", {
224
+ lesson: exSlug,
225
+ status: "done",
226
+ log: `✅ The code file for ${exercise.title} has been generated successfully!`,
227
+ })
225
228
  }
226
229
 
227
230
  return readability.newMarkdown
@@ -326,6 +329,9 @@ export default class ServeCommand extends SessionCommand {
326
329
  // }
327
330
 
328
331
  const app = express()
332
+ const server = http.createServer(app)
333
+ initSocketIO(server)
334
+
329
335
  const PORT = process.env.PORT || 3000
330
336
 
331
337
  const distPath = path.resolve(__dirname, "../creatorDist")
@@ -445,7 +451,7 @@ export default class ServeCommand extends SessionCommand {
445
451
  })
446
452
 
447
453
  app.get("/preview/:slug", async (req, res) => {
448
- console.log("GET COURSE /")
454
+ console.log("GET COURSE PREVIEW /")
449
455
 
450
456
  const file = path.resolve(__dirname, "../ui/_app/index.html")
451
457
  res.sendFile(file)
@@ -458,6 +464,7 @@ export default class ServeCommand extends SessionCommand {
458
464
  }
459
465
 
460
466
  try {
467
+ console.log("GET CONFIG, COURSE SLUG", courseSlug)
461
468
  const { config, exercises } = await buildConfig(bucket, courseSlug)
462
469
  res.set("X-Creator-Web", "true")
463
470
  res.set("Access-Control-Expose-Headers", "X-Creator-Web")
@@ -645,6 +652,35 @@ export default class ServeCommand extends SessionCommand {
645
652
  res.send({ message: "Files deleted" })
646
653
  })
647
654
 
655
+ app.get("/test/:slug", (req, res) => {
656
+ emitToCourse(req.params.slug, "course-creation", {
657
+ lesson: "000-welcome-to-bird-domestication",
658
+ status: "generating",
659
+ log: "Hello",
660
+ })
661
+ emitToCourse(req.params.slug, "course-creation", {
662
+ lesson: "000-welcome-to-bird-domestication",
663
+ status: "generating",
664
+ log: "Hello",
665
+ })
666
+ emitToCourse(req.params.slug, "course-creation", {
667
+ lesson: "000-welcome-to-bird-domestication",
668
+ status: "generating",
669
+ log: "Hello broder",
670
+ })
671
+ emitToCourse(req.params.slug, "course-creation", {
672
+ lesson: "000-welcome-to-bird-domestication",
673
+ status: "done",
674
+ log: "Hello broder",
675
+ })
676
+ emitToCourse(req.params.slug, "course-creation", {
677
+ lesson: "other",
678
+ status: "generating",
679
+ log: "Hello",
680
+ })
681
+ res.send({ message: "Course creation started" })
682
+ })
683
+
648
684
  app.get("/assets/:file", (req, res) => {
649
685
  const file = path.join(localAppPath, req.params.file)
650
686
  res.sendFile(file)
@@ -683,8 +719,9 @@ export default class ServeCommand extends SessionCommand {
683
719
  await uploadFileToBucket(
684
720
  bucket,
685
721
  JSON.stringify(learnJson),
686
- `courses/${slugify(syllabus.courseInfo.title)}/learn.json`
722
+ `${tutorialDir}/learn.json`
687
723
  )
724
+ console.log("🔄 Learn.json uploaded to bucket to", tutorialDir)
688
725
 
689
726
  const lessonsPromises = syllabus.lessons.map(lesson =>
690
727
  processExercise(
@@ -693,7 +730,8 @@ export default class ServeCommand extends SessionCommand {
693
730
  syllabus.lessons,
694
731
  JSON.stringify(syllabus.courseInfo),
695
732
  lesson,
696
- tutorialDir + "/exercises"
733
+ tutorialDir + "/exercises",
734
+ slugify(syllabus.courseInfo.title)
697
735
  )
698
736
  )
699
737
  const readmeContents = await Promise.all(lessonsPromises)
@@ -973,7 +1011,7 @@ export default class ServeCommand extends SessionCommand {
973
1011
  }
974
1012
  })
975
1013
 
976
- app.listen(PORT, () => {
1014
+ server.listen(PORT, () => {
977
1015
  console.log(
978
1016
  `🚀 Creator UI server running at http://localhost:${PORT}/creator`
979
1017
  )
@@ -7,7 +7,8 @@
7
7
  "dev": "vite",
8
8
  "build": "tsc -b && vite build",
9
9
  "lint": "eslint .",
10
- "preview": "vite preview"
10
+ "preview": "vite preview",
11
+ "watch": "vite build --watch"
11
12
  },
12
13
  "dependencies": {
13
14
  "@google-cloud/storage": "^7.16.0",
@@ -22,14 +22,14 @@ interface LessonItemProps {
22
22
  onRemove: () => void
23
23
  }
24
24
 
25
- // function cleanFloatString(input: string): string {
26
- // const num = parseFloat(input)
27
- // const isInteger = Number.isInteger(num)
25
+ function cleanFloatString(input: string): string {
26
+ const num = parseFloat(input)
27
+ const isInteger = Number.isInteger(num)
28
28
 
29
- // return isInteger
30
- // ? Math.floor(num).toString().padStart(2, "0")
31
- // : num.toString()
32
- // }
29
+ return isInteger
30
+ ? Math.floor(num).toString().padStart(2, "0")
31
+ : num.toString()
32
+ }
33
33
 
34
34
  export const LessonItem: React.FC<LessonItemProps> = ({
35
35
  lesson,
@@ -48,11 +48,14 @@ export const LessonItem: React.FC<LessonItemProps> = ({
48
48
  }`}
49
49
  >
50
50
  {isNew && <span className="red-ball"></span>}
51
- {/* <span className="index-circle">{cleanFloatString(lesson.id)}</span> */}
51
+
52
52
  {mode === "teacher" && (
53
- <span className="text-gray-500 text-sm">
54
- {lesson.type[0] + lesson.type.slice(1).toLowerCase()}
55
- </span>
53
+ <>
54
+ <span className="index-circle">{cleanFloatString(lesson.id)}</span>
55
+ <span className="text-gray-500 text-sm">
56
+ {lesson.type[0] + lesson.type.slice(1).toLowerCase()} ●
57
+ </span>
58
+ </>
56
59
  )}
57
60
 
58
61
  {isEditing ? (
@@ -34,10 +34,10 @@ const PreviewGenerator: React.FC = () => {
34
34
  html2canvas(previewElement, {
35
35
  useCORS: true,
36
36
  }).then(async (canvas) => {
37
- // const anchor = document.createElement("a")
38
- // anchor.href = canvas.toDataURL("image/png")
39
- // anchor.download = "preview.png"
40
- // anchor.click()
37
+ // const anchor = document.createElement("a")
38
+ // anchor.href = canvas.toDataURL("image/png")
39
+ // anchor.download = "preview.png"
40
+ // anchor.click()
41
41
 
42
42
  const imageUrl = canvas.toDataURL("image/png")
43
43
 
@@ -159,7 +159,7 @@ const SyllabusEditor: React.FC = () => {
159
159
  window.location.href = `/preview/${slugify(
160
160
  syllabus.courseInfo.title || ""
161
161
  )}?token=${auth.bcToken}`
162
- }, 1000)
162
+ }, 3000)
163
163
  }
164
164
 
165
165
  if (!syllabus) return null
@@ -2,6 +2,7 @@ import axios from "axios"
2
2
  import { BREATHECODE_HOST, RIGOBOT_HOST } from "./constants"
3
3
  import { Lesson } from "../components/LessonItem"
4
4
  import { randomUUID } from "./creatorUtils"
5
+ import { Syllabus } from "./store"
5
6
 
6
7
  export function parseLesson(input: string, previous: Lesson[]): Lesson | null {
7
8
  const pattern = /^([\d.]+)\s*-\s*(.*?)\s*\[(\w+):\s*(.+)\]$/
@@ -289,3 +290,23 @@ export const generateImage = async (
289
290
  return null
290
291
  }
291
292
  }
293
+
294
+ export const createCourse = async (
295
+ syllabus: Syllabus,
296
+ token: string,
297
+ breathecodeToken: string
298
+ ) => {
299
+ const response = await axios.post(
300
+ `/actions/create-course`,
301
+ {
302
+ syllabus,
303
+ },
304
+ {
305
+ headers: {
306
+ "x-breathecode-token": breathecodeToken,
307
+ "x-rigo-token": token,
308
+ },
309
+ }
310
+ )
311
+ return response.data
312
+ }