@learnpack/learnpack 5.0.297 → 5.0.300
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/README.md +409 -409
- package/lib/commands/audit.js +15 -15
- package/lib/commands/breakToken.js +19 -19
- package/lib/commands/clean.js +3 -3
- package/lib/commands/logout.js +3 -3
- package/lib/commands/serve.js +32 -11
- package/lib/creatorDist/assets/{index-D25zkBaN.js → index-DoYRptnk.js} +11875 -11992
- package/lib/creatorDist/index.html +1 -1
- package/lib/managers/config/index.js +77 -77
- package/lib/utils/creatorUtilities.js +14 -14
- package/lib/utils/templates/isolated/exercises/01-hello-world/README.es.md +26 -26
- package/lib/utils/templates/isolated/exercises/01-hello-world/README.md +26 -26
- package/lib/utils/templates/scorm/adlcp_rootv1p2.xsd +110 -110
- package/lib/utils/templates/scorm/config/index.html +209 -209
- package/lib/utils/templates/scorm/ims_xml.xsd +1 -1
- package/lib/utils/templates/scorm/imscp_rootv1p1p2.xsd +345 -345
- package/lib/utils/templates/scorm/imsmanifest.xml +38 -38
- package/lib/utils/templates/scorm/imsmd_rootv1p2p1.xsd +573 -573
- package/package.json +1 -1
- package/src/commands/audit.ts +487 -487
- package/src/commands/breakToken.ts +67 -67
- package/src/commands/clean.ts +30 -30
- package/src/commands/logout.ts +38 -38
- package/src/commands/serve.ts +49 -26
- package/src/commands/start.ts +333 -333
- package/src/commands/translate.ts +123 -123
- package/src/creator/README.md +54 -54
- package/src/creator/package-lock.json +6621 -6621
- package/src/creator/package.json +55 -55
- package/src/creator/src/App.tsx +569 -569
- package/src/creator/src/components/FileUploader.tsx +302 -302
- package/src/creator/src/components/Icon.tsx +18 -18
- package/src/creator/src/components/LessonItem.tsx +152 -152
- package/src/creator/src/components/Login.tsx +259 -259
- package/src/creator/src/components/syllabus/ContentIndex.tsx +323 -323
- package/src/creator/src/components/syllabus/SyllabusEditor.tsx +337 -337
- package/src/creator/src/i18n.ts +28 -28
- package/src/creator/src/locales/en.json +127 -127
- package/src/creator/src/locales/es.json +127 -127
- package/src/creator/src/utils/configTypes.ts +122 -122
- package/src/creator/src/utils/constants.ts +13 -13
- package/src/creator/src/utils/creatorUtils.ts +46 -46
- package/src/creator/src/utils/eventBus.ts +2 -2
- package/src/creator/src/utils/socket.ts +61 -61
- package/src/creator/src/utils/store.ts +222 -222
- package/src/creator/src/vite-env.d.ts +1 -1
- package/src/creator/vite.config.ts +13 -13
- package/src/creatorDist/assets/{index-D25zkBaN.js → index-DoYRptnk.js} +11875 -11992
- package/src/creatorDist/index.html +1 -1
- package/src/managers/config/defaults.ts +49 -49
- package/src/managers/config/exercise.ts +364 -364
- package/src/managers/config/index.ts +775 -775
- package/src/managers/file.ts +236 -236
- package/src/managers/server/routes.ts +554 -554
- package/src/managers/telemetry.ts +188 -188
- package/src/models/action.ts +13 -13
- package/src/models/config-manager.ts +28 -28
- package/src/models/config.ts +106 -106
- package/src/models/exercise-obj.ts +30 -30
- package/src/models/session.ts +39 -39
- package/src/models/socket.ts +61 -61
- package/src/models/status.ts +16 -16
- package/src/utils/BaseCommand.ts +56 -56
- package/src/utils/audit.ts +392 -392
- package/src/utils/checkNotInstalled.ts +267 -267
- package/src/utils/convertCreds.js +34 -34
- package/src/utils/creatorUtilities.ts +504 -504
- package/src/utils/export/README.md +178 -178
- package/src/utils/incrementVersion.js +74 -74
- package/src/utils/misc.ts +58 -58
- package/src/utils/sidebarGenerator.ts +195 -195
- package/src/utils/templates/isolated/exercises/01-hello-world/README.es.md +26 -26
- package/src/utils/templates/isolated/exercises/01-hello-world/README.md +26 -26
- package/src/utils/templates/scorm/adlcp_rootv1p2.xsd +110 -110
- package/src/utils/templates/scorm/config/index.html +209 -209
- package/src/utils/templates/scorm/ims_xml.xsd +1 -1
- package/src/utils/templates/scorm/imscp_rootv1p1p2.xsd +345 -345
- package/src/utils/templates/scorm/imsmanifest.xml +38 -38
- package/src/utils/templates/scorm/imsmd_rootv1p2p1.xsd +573 -573
@@ -1,337 +1,337 @@
|
|
1
|
-
import React, { useState, useEffect } from "react"
|
2
|
-
import { useShallow } from "zustand/react/shallow"
|
3
|
-
import useStore from "../../utils/store"
|
4
|
-
import { publicInteractiveCreation } from "../../utils/rigo"
|
5
|
-
import {
|
6
|
-
parseLesson,
|
7
|
-
// useConsumableCall,
|
8
|
-
validateTokens,
|
9
|
-
checkParams,
|
10
|
-
loginWithToken,
|
11
|
-
createCourse,
|
12
|
-
isSlugAvailable,
|
13
|
-
// reWriteTitle,
|
14
|
-
useConsumableCall,
|
15
|
-
isValidRigoToken,
|
16
|
-
isValidPublicToken,
|
17
|
-
} from "../../utils/lib"
|
18
|
-
|
19
|
-
import Loader from "../Loader"
|
20
|
-
import { TMessage } from "../Message"
|
21
|
-
import { Lesson } from "../LessonItem"
|
22
|
-
import { ConsumablesManager } from "../ConsumablesManager"
|
23
|
-
import toast from "react-hot-toast"
|
24
|
-
import { ContentIndex } from "./ContentIndex"
|
25
|
-
import { Sidebar } from "./Sidebar"
|
26
|
-
import Login from "../Login"
|
27
|
-
import { useNavigate } from "react-router"
|
28
|
-
import { ParamsChecker } from "../ParamsChecker"
|
29
|
-
import { randomUUID } from "../../utils/creatorUtils"
|
30
|
-
import { RIGO_FLOAT_GIF } from "../../utils/constants"
|
31
|
-
import { useTranslation } from "react-i18next"
|
32
|
-
import NotificationListener from "../NotificationListener"
|
33
|
-
|
34
|
-
const SyllabusEditor: React.FC = () => {
|
35
|
-
const navigate = useNavigate()
|
36
|
-
const { i18n } = useTranslation()
|
37
|
-
|
38
|
-
const {
|
39
|
-
history,
|
40
|
-
auth,
|
41
|
-
setAuth,
|
42
|
-
push,
|
43
|
-
cleanAll,
|
44
|
-
messages,
|
45
|
-
setMessages,
|
46
|
-
technologies,
|
47
|
-
mode,
|
48
|
-
} = useStore(
|
49
|
-
useShallow((state) => ({
|
50
|
-
history: state.history,
|
51
|
-
auth: state.auth,
|
52
|
-
setAuth: state.setAuth,
|
53
|
-
push: state.push,
|
54
|
-
cleanAll: state.cleanAll,
|
55
|
-
messages: state.messages,
|
56
|
-
// formState: state.formState,
|
57
|
-
setMessages: state.setMessages,
|
58
|
-
technologies: state.technologies,
|
59
|
-
mode: state.mode,
|
60
|
-
}))
|
61
|
-
)
|
62
|
-
|
63
|
-
const [isGenerating, setIsGenerating] = useState(false)
|
64
|
-
const [showLoginModal, setShowLoginModal] = useState(false)
|
65
|
-
const [isThinking, setIsThinking] = useState(false)
|
66
|
-
const [notificationId, setNotificationId] = useState<string>("")
|
67
|
-
|
68
|
-
const syllabus = history[history.length - 1]
|
69
|
-
|
70
|
-
useEffect(() => {
|
71
|
-
if (!syllabus) {
|
72
|
-
navigate("/creator", { replace: true })
|
73
|
-
}
|
74
|
-
}, [syllabus, navigate])
|
75
|
-
|
76
|
-
useEffect(() => {
|
77
|
-
;(async () => {
|
78
|
-
const { token } = checkParams(["token"])
|
79
|
-
if (token) {
|
80
|
-
const user = await loginWithToken(token)
|
81
|
-
if (user) {
|
82
|
-
setAuth({
|
83
|
-
bcToken: token,
|
84
|
-
userId: user.id,
|
85
|
-
rigoToken: user.rigobot.key,
|
86
|
-
user,
|
87
|
-
publicToken: "",
|
88
|
-
})
|
89
|
-
}
|
90
|
-
}
|
91
|
-
})()
|
92
|
-
checkSlug()
|
93
|
-
}, [])
|
94
|
-
|
95
|
-
const checkSlug = async () => {
|
96
|
-
if (!syllabus.courseInfo.title) {
|
97
|
-
toast.error("Please provide a title for the course")
|
98
|
-
return
|
99
|
-
}
|
100
|
-
|
101
|
-
let newSlug = syllabus.courseInfo.slug
|
102
|
-
let isAvailable = await isSlugAvailable(syllabus.courseInfo.slug)
|
103
|
-
|
104
|
-
while (!isAvailable) {
|
105
|
-
if (newSlug.length > 50) {
|
106
|
-
newSlug = newSlug.slice(0, 44)
|
107
|
-
}
|
108
|
-
newSlug = newSlug + "-" + randomUUID()
|
109
|
-
newSlug = newSlug.slice(0, 50)
|
110
|
-
isAvailable = await isSlugAvailable(newSlug)
|
111
|
-
}
|
112
|
-
|
113
|
-
push({
|
114
|
-
...syllabus,
|
115
|
-
courseInfo: {
|
116
|
-
...syllabus.courseInfo,
|
117
|
-
slug: newSlug,
|
118
|
-
},
|
119
|
-
})
|
120
|
-
}
|
121
|
-
|
122
|
-
const sendPrompt = async (prompt: string) => {
|
123
|
-
setIsThinking(true)
|
124
|
-
|
125
|
-
try {
|
126
|
-
let isAuthenticated = false
|
127
|
-
let tokenType = ""
|
128
|
-
let tokenToUse = ""
|
129
|
-
if (auth.rigoToken) {
|
130
|
-
const isRigoTokenValid = await isValidRigoToken(auth.rigoToken)
|
131
|
-
if (isRigoTokenValid) {
|
132
|
-
isAuthenticated = true
|
133
|
-
tokenType = "rigo"
|
134
|
-
tokenToUse = auth.rigoToken
|
135
|
-
} else {
|
136
|
-
setAuth({
|
137
|
-
...auth,
|
138
|
-
rigoToken: "",
|
139
|
-
bcToken: "",
|
140
|
-
userId: "",
|
141
|
-
user: null,
|
142
|
-
})
|
143
|
-
}
|
144
|
-
}
|
145
|
-
|
146
|
-
if (auth.publicToken && !isAuthenticated) {
|
147
|
-
const isPublicTokenValid = await isValidPublicToken(auth.publicToken)
|
148
|
-
if (isPublicTokenValid) {
|
149
|
-
isAuthenticated = true
|
150
|
-
tokenType = "public"
|
151
|
-
tokenToUse = auth.publicToken
|
152
|
-
}
|
153
|
-
}
|
154
|
-
|
155
|
-
if (!isAuthenticated) {
|
156
|
-
setShowLoginModal(true)
|
157
|
-
return
|
158
|
-
}
|
159
|
-
|
160
|
-
setMessages([
|
161
|
-
...messages,
|
162
|
-
{ type: "user", content: prompt },
|
163
|
-
{ type: "assistant", content: "" },
|
164
|
-
])
|
165
|
-
|
166
|
-
const lessonsText = JSON.stringify(syllabus.lessons)
|
167
|
-
|
168
|
-
const res = await publicInteractiveCreation(
|
169
|
-
{
|
170
|
-
courseInfo:
|
171
|
-
JSON.stringify(syllabus.courseInfo) +
|
172
|
-
`\nThe following technologies are available, choose up to 3 from the following list: <techs>${technologies
|
173
|
-
.filter((t) => t.lang === syllabus.courseInfo.language)
|
174
|
-
.map((t) => t.slug)
|
175
|
-
.join(", ")}</techs> \nThe following lessons are already in the syllabus: ${lessonsText}
|
176
|
-
|
177
|
-
You must NEVER delete lessons that are marked as locked.
|
178
|
-
`,
|
179
|
-
prevInteractions:
|
180
|
-
messages
|
181
|
-
.map((message) => `${message.type}: ${message.content}`)
|
182
|
-
.join("\n") + `\nUSER: ${prompt}`,
|
183
|
-
},
|
184
|
-
tokenToUse,
|
185
|
-
syllabus?.courseInfo?.purpose || "learnpack-lesson-writer",
|
186
|
-
tokenType === "rigo" ? false : true
|
187
|
-
)
|
188
|
-
|
189
|
-
setNotificationId(res.notificationId)
|
190
|
-
} catch (error) {
|
191
|
-
console.error(error)
|
192
|
-
const newMessages = [...messages]
|
193
|
-
newMessages.pop()
|
194
|
-
setMessages(newMessages as TMessage[])
|
195
|
-
setIsThinking(false)
|
196
|
-
}
|
197
|
-
}
|
198
|
-
|
199
|
-
const handleSubmit = async () => {
|
200
|
-
if (!auth.bcToken || !auth.rigoToken) {
|
201
|
-
setShowLoginModal(true)
|
202
|
-
return
|
203
|
-
}
|
204
|
-
|
205
|
-
if (!syllabus.courseInfo.title) {
|
206
|
-
toast.error("Please provide a title for the course")
|
207
|
-
return
|
208
|
-
}
|
209
|
-
|
210
|
-
let tokenToUse = auth.rigoToken
|
211
|
-
const onValidRigoToken = (rigotoken: string) => {
|
212
|
-
setAuth({
|
213
|
-
...auth,
|
214
|
-
rigoToken: rigotoken,
|
215
|
-
})
|
216
|
-
tokenToUse = rigotoken
|
217
|
-
}
|
218
|
-
const isValid = await validateTokens(auth.bcToken, onValidRigoToken)
|
219
|
-
if (!isValid) {
|
220
|
-
setShowLoginModal(true)
|
221
|
-
return
|
222
|
-
}
|
223
|
-
const success = await useConsumableCall(
|
224
|
-
auth.bcToken,
|
225
|
-
"ai-course-generation"
|
226
|
-
)
|
227
|
-
if (!success) {
|
228
|
-
toast.error(
|
229
|
-
"You don't have enough credits to generate a course! Please add more credits to your account."
|
230
|
-
)
|
231
|
-
toast.loading("You will be redirected to the settings page in 5 seconds.")
|
232
|
-
setTimeout(() => {
|
233
|
-
window.location.href =
|
234
|
-
"https://learnpack.co/settings?token=" + auth.bcToken
|
235
|
-
}, 5000)
|
236
|
-
return
|
237
|
-
}
|
238
|
-
|
239
|
-
setIsGenerating(true)
|
240
|
-
|
241
|
-
const timeout = setTimeout(() => {
|
242
|
-
cleanAll()
|
243
|
-
window.location.href = `/preview/${syllabus.courseInfo.slug}?token=${auth.bcToken}&language=${syllabus.courseInfo.language}&mode=${mode}`
|
244
|
-
}, 8000)
|
245
|
-
try {
|
246
|
-
await createCourse(syllabus, tokenToUse, auth.bcToken)
|
247
|
-
} catch (error) {
|
248
|
-
console.error("Failed to create course:", error)
|
249
|
-
clearTimeout(timeout)
|
250
|
-
setIsGenerating(false)
|
251
|
-
}
|
252
|
-
}
|
253
|
-
|
254
|
-
if (!syllabus) return null
|
255
|
-
|
256
|
-
console.log(syllabus.courseInfo)
|
257
|
-
|
258
|
-
return isGenerating ? (
|
259
|
-
<>
|
260
|
-
<Loader
|
261
|
-
listeningTo="course-generation"
|
262
|
-
icon={<img src={RIGO_FLOAT_GIF} alt="rigo" className="w-20 h-20" />}
|
263
|
-
initialBuffer="🚀 Starting course generation..."
|
264
|
-
text="Learnpack is setting up your tutorial.
|
265
|
-
It may take a moment..."
|
266
|
-
/>
|
267
|
-
</>
|
268
|
-
) : (
|
269
|
-
<div className="flex w-full bg-white rounded-md shadow-md overflow-hidden h-screen ">
|
270
|
-
<ParamsChecker />
|
271
|
-
<NotificationListener
|
272
|
-
onNotification={(res) => {
|
273
|
-
const lessons: Lesson[] = res.parsed.listOfSteps.map((step: any) =>
|
274
|
-
parseLesson(step, syllabus.lessons)
|
275
|
-
)
|
276
|
-
push({
|
277
|
-
...syllabus,
|
278
|
-
lessons: lessons,
|
279
|
-
courseInfo: {
|
280
|
-
...syllabus.courseInfo,
|
281
|
-
title: res.parsed.title || syllabus.courseInfo.title,
|
282
|
-
description:
|
283
|
-
res.parsed.description || syllabus.courseInfo.description,
|
284
|
-
language:
|
285
|
-
res.parsed.languageCode || syllabus.courseInfo.language || "en",
|
286
|
-
technologies:
|
287
|
-
res.parsed.technologies.length > 0
|
288
|
-
? res.parsed.technologies
|
289
|
-
: syllabus.courseInfo.technologies || [],
|
290
|
-
},
|
291
|
-
})
|
292
|
-
|
293
|
-
if (res.parsed.languageCode) {
|
294
|
-
i18n.changeLanguage(res.parsed.languageCode)
|
295
|
-
}
|
296
|
-
|
297
|
-
setMessages((prev) => [
|
298
|
-
...prev.filter((m) => Boolean(m.content)),
|
299
|
-
{
|
300
|
-
type: "assistant",
|
301
|
-
content: res.parsed.aiMessage,
|
302
|
-
},
|
303
|
-
])
|
304
|
-
setIsThinking(false)
|
305
|
-
setNotificationId("")
|
306
|
-
}}
|
307
|
-
notificationId={notificationId}
|
308
|
-
/>
|
309
|
-
{showLoginModal && (
|
310
|
-
<Login
|
311
|
-
onFinish={() => {
|
312
|
-
setShowLoginModal(false)
|
313
|
-
}}
|
314
|
-
/>
|
315
|
-
)}
|
316
|
-
|
317
|
-
<ConsumablesManager />
|
318
|
-
|
319
|
-
<Sidebar
|
320
|
-
// messages={messages}
|
321
|
-
sendPrompt={sendPrompt}
|
322
|
-
handleSubmit={handleSubmit}
|
323
|
-
/>
|
324
|
-
|
325
|
-
<ContentIndex
|
326
|
-
handleSubmit={handleSubmit}
|
327
|
-
messages={messages}
|
328
|
-
isThinking={isThinking}
|
329
|
-
openLogin={() => {
|
330
|
-
setShowLoginModal(true)
|
331
|
-
}}
|
332
|
-
/>
|
333
|
-
</div>
|
334
|
-
)
|
335
|
-
}
|
336
|
-
|
337
|
-
export default SyllabusEditor
|
1
|
+
import React, { useState, useEffect } from "react"
|
2
|
+
import { useShallow } from "zustand/react/shallow"
|
3
|
+
import useStore from "../../utils/store"
|
4
|
+
import { publicInteractiveCreation } from "../../utils/rigo"
|
5
|
+
import {
|
6
|
+
parseLesson,
|
7
|
+
// useConsumableCall,
|
8
|
+
validateTokens,
|
9
|
+
checkParams,
|
10
|
+
loginWithToken,
|
11
|
+
createCourse,
|
12
|
+
isSlugAvailable,
|
13
|
+
// reWriteTitle,
|
14
|
+
useConsumableCall,
|
15
|
+
isValidRigoToken,
|
16
|
+
isValidPublicToken,
|
17
|
+
} from "../../utils/lib"
|
18
|
+
|
19
|
+
import Loader from "../Loader"
|
20
|
+
import { TMessage } from "../Message"
|
21
|
+
import { Lesson } from "../LessonItem"
|
22
|
+
import { ConsumablesManager } from "../ConsumablesManager"
|
23
|
+
import toast from "react-hot-toast"
|
24
|
+
import { ContentIndex } from "./ContentIndex"
|
25
|
+
import { Sidebar } from "./Sidebar"
|
26
|
+
import Login from "../Login"
|
27
|
+
import { useNavigate } from "react-router"
|
28
|
+
import { ParamsChecker } from "../ParamsChecker"
|
29
|
+
import { randomUUID } from "../../utils/creatorUtils"
|
30
|
+
import { RIGO_FLOAT_GIF } from "../../utils/constants"
|
31
|
+
import { useTranslation } from "react-i18next"
|
32
|
+
import NotificationListener from "../NotificationListener"
|
33
|
+
|
34
|
+
const SyllabusEditor: React.FC = () => {
|
35
|
+
const navigate = useNavigate()
|
36
|
+
const { i18n } = useTranslation()
|
37
|
+
|
38
|
+
const {
|
39
|
+
history,
|
40
|
+
auth,
|
41
|
+
setAuth,
|
42
|
+
push,
|
43
|
+
cleanAll,
|
44
|
+
messages,
|
45
|
+
setMessages,
|
46
|
+
technologies,
|
47
|
+
mode,
|
48
|
+
} = useStore(
|
49
|
+
useShallow((state) => ({
|
50
|
+
history: state.history,
|
51
|
+
auth: state.auth,
|
52
|
+
setAuth: state.setAuth,
|
53
|
+
push: state.push,
|
54
|
+
cleanAll: state.cleanAll,
|
55
|
+
messages: state.messages,
|
56
|
+
// formState: state.formState,
|
57
|
+
setMessages: state.setMessages,
|
58
|
+
technologies: state.technologies,
|
59
|
+
mode: state.mode,
|
60
|
+
}))
|
61
|
+
)
|
62
|
+
|
63
|
+
const [isGenerating, setIsGenerating] = useState(false)
|
64
|
+
const [showLoginModal, setShowLoginModal] = useState(false)
|
65
|
+
const [isThinking, setIsThinking] = useState(false)
|
66
|
+
const [notificationId, setNotificationId] = useState<string>("")
|
67
|
+
|
68
|
+
const syllabus = history[history.length - 1]
|
69
|
+
|
70
|
+
useEffect(() => {
|
71
|
+
if (!syllabus) {
|
72
|
+
navigate("/creator", { replace: true })
|
73
|
+
}
|
74
|
+
}, [syllabus, navigate])
|
75
|
+
|
76
|
+
useEffect(() => {
|
77
|
+
;(async () => {
|
78
|
+
const { token } = checkParams(["token"])
|
79
|
+
if (token) {
|
80
|
+
const user = await loginWithToken(token)
|
81
|
+
if (user) {
|
82
|
+
setAuth({
|
83
|
+
bcToken: token,
|
84
|
+
userId: user.id,
|
85
|
+
rigoToken: user.rigobot.key,
|
86
|
+
user,
|
87
|
+
publicToken: "",
|
88
|
+
})
|
89
|
+
}
|
90
|
+
}
|
91
|
+
})()
|
92
|
+
checkSlug()
|
93
|
+
}, [])
|
94
|
+
|
95
|
+
const checkSlug = async () => {
|
96
|
+
if (!syllabus.courseInfo.title) {
|
97
|
+
toast.error("Please provide a title for the course")
|
98
|
+
return
|
99
|
+
}
|
100
|
+
|
101
|
+
let newSlug = syllabus.courseInfo.slug
|
102
|
+
let isAvailable = await isSlugAvailable(syllabus.courseInfo.slug)
|
103
|
+
|
104
|
+
while (!isAvailable) {
|
105
|
+
if (newSlug.length > 50) {
|
106
|
+
newSlug = newSlug.slice(0, 44)
|
107
|
+
}
|
108
|
+
newSlug = newSlug + "-" + randomUUID()
|
109
|
+
newSlug = newSlug.slice(0, 50)
|
110
|
+
isAvailable = await isSlugAvailable(newSlug)
|
111
|
+
}
|
112
|
+
|
113
|
+
push({
|
114
|
+
...syllabus,
|
115
|
+
courseInfo: {
|
116
|
+
...syllabus.courseInfo,
|
117
|
+
slug: newSlug,
|
118
|
+
},
|
119
|
+
})
|
120
|
+
}
|
121
|
+
|
122
|
+
const sendPrompt = async (prompt: string) => {
|
123
|
+
setIsThinking(true)
|
124
|
+
|
125
|
+
try {
|
126
|
+
let isAuthenticated = false
|
127
|
+
let tokenType = ""
|
128
|
+
let tokenToUse = ""
|
129
|
+
if (auth.rigoToken) {
|
130
|
+
const isRigoTokenValid = await isValidRigoToken(auth.rigoToken)
|
131
|
+
if (isRigoTokenValid) {
|
132
|
+
isAuthenticated = true
|
133
|
+
tokenType = "rigo"
|
134
|
+
tokenToUse = auth.rigoToken
|
135
|
+
} else {
|
136
|
+
setAuth({
|
137
|
+
...auth,
|
138
|
+
rigoToken: "",
|
139
|
+
bcToken: "",
|
140
|
+
userId: "",
|
141
|
+
user: null,
|
142
|
+
})
|
143
|
+
}
|
144
|
+
}
|
145
|
+
|
146
|
+
if (auth.publicToken && !isAuthenticated) {
|
147
|
+
const isPublicTokenValid = await isValidPublicToken(auth.publicToken)
|
148
|
+
if (isPublicTokenValid) {
|
149
|
+
isAuthenticated = true
|
150
|
+
tokenType = "public"
|
151
|
+
tokenToUse = auth.publicToken
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
155
|
+
if (!isAuthenticated) {
|
156
|
+
setShowLoginModal(true)
|
157
|
+
return
|
158
|
+
}
|
159
|
+
|
160
|
+
setMessages([
|
161
|
+
...messages,
|
162
|
+
{ type: "user", content: prompt },
|
163
|
+
{ type: "assistant", content: "" },
|
164
|
+
])
|
165
|
+
|
166
|
+
const lessonsText = JSON.stringify(syllabus.lessons)
|
167
|
+
|
168
|
+
const res = await publicInteractiveCreation(
|
169
|
+
{
|
170
|
+
courseInfo:
|
171
|
+
JSON.stringify(syllabus.courseInfo) +
|
172
|
+
`\nThe following technologies are available, choose up to 3 from the following list: <techs>${technologies
|
173
|
+
.filter((t) => t.lang === syllabus.courseInfo.language)
|
174
|
+
.map((t) => t.slug)
|
175
|
+
.join(", ")}</techs> \nThe following lessons are already in the syllabus: ${lessonsText}
|
176
|
+
|
177
|
+
You must NEVER delete lessons that are marked as locked.
|
178
|
+
`,
|
179
|
+
prevInteractions:
|
180
|
+
messages
|
181
|
+
.map((message) => `${message.type}: ${message.content}`)
|
182
|
+
.join("\n") + `\nUSER: ${prompt}`,
|
183
|
+
},
|
184
|
+
tokenToUse,
|
185
|
+
syllabus?.courseInfo?.purpose || "learnpack-lesson-writer",
|
186
|
+
tokenType === "rigo" ? false : true
|
187
|
+
)
|
188
|
+
|
189
|
+
setNotificationId(res.notificationId)
|
190
|
+
} catch (error) {
|
191
|
+
console.error(error)
|
192
|
+
const newMessages = [...messages]
|
193
|
+
newMessages.pop()
|
194
|
+
setMessages(newMessages as TMessage[])
|
195
|
+
setIsThinking(false)
|
196
|
+
}
|
197
|
+
}
|
198
|
+
|
199
|
+
const handleSubmit = async () => {
|
200
|
+
if (!auth.bcToken || !auth.rigoToken) {
|
201
|
+
setShowLoginModal(true)
|
202
|
+
return
|
203
|
+
}
|
204
|
+
|
205
|
+
if (!syllabus.courseInfo.title) {
|
206
|
+
toast.error("Please provide a title for the course")
|
207
|
+
return
|
208
|
+
}
|
209
|
+
|
210
|
+
let tokenToUse = auth.rigoToken
|
211
|
+
const onValidRigoToken = (rigotoken: string) => {
|
212
|
+
setAuth({
|
213
|
+
...auth,
|
214
|
+
rigoToken: rigotoken,
|
215
|
+
})
|
216
|
+
tokenToUse = rigotoken
|
217
|
+
}
|
218
|
+
const isValid = await validateTokens(auth.bcToken, onValidRigoToken)
|
219
|
+
if (!isValid) {
|
220
|
+
setShowLoginModal(true)
|
221
|
+
return
|
222
|
+
}
|
223
|
+
const success = await useConsumableCall(
|
224
|
+
auth.bcToken,
|
225
|
+
"ai-course-generation"
|
226
|
+
)
|
227
|
+
if (!success) {
|
228
|
+
toast.error(
|
229
|
+
"You don't have enough credits to generate a course! Please add more credits to your account."
|
230
|
+
)
|
231
|
+
toast.loading("You will be redirected to the settings page in 5 seconds.")
|
232
|
+
setTimeout(() => {
|
233
|
+
window.location.href =
|
234
|
+
"https://learnpack.co/settings?token=" + auth.bcToken
|
235
|
+
}, 5000)
|
236
|
+
return
|
237
|
+
}
|
238
|
+
|
239
|
+
setIsGenerating(true)
|
240
|
+
|
241
|
+
const timeout = setTimeout(() => {
|
242
|
+
cleanAll()
|
243
|
+
window.location.href = `/preview/${syllabus.courseInfo.slug}?token=${auth.bcToken}&language=${syllabus.courseInfo.language}&mode=${mode}`
|
244
|
+
}, 8000)
|
245
|
+
try {
|
246
|
+
await createCourse(syllabus, tokenToUse, auth.bcToken)
|
247
|
+
} catch (error) {
|
248
|
+
console.error("Failed to create course:", error)
|
249
|
+
clearTimeout(timeout)
|
250
|
+
setIsGenerating(false)
|
251
|
+
}
|
252
|
+
}
|
253
|
+
|
254
|
+
if (!syllabus) return null
|
255
|
+
|
256
|
+
console.log(syllabus.courseInfo)
|
257
|
+
|
258
|
+
return isGenerating ? (
|
259
|
+
<>
|
260
|
+
<Loader
|
261
|
+
listeningTo="course-generation"
|
262
|
+
icon={<img src={RIGO_FLOAT_GIF} alt="rigo" className="w-20 h-20" />}
|
263
|
+
initialBuffer="🚀 Starting course generation..."
|
264
|
+
text="Learnpack is setting up your tutorial.
|
265
|
+
It may take a moment..."
|
266
|
+
/>
|
267
|
+
</>
|
268
|
+
) : (
|
269
|
+
<div className="flex w-full bg-white rounded-md shadow-md overflow-hidden h-screen ">
|
270
|
+
<ParamsChecker />
|
271
|
+
<NotificationListener
|
272
|
+
onNotification={(res) => {
|
273
|
+
const lessons: Lesson[] = res.parsed.listOfSteps.map((step: any) =>
|
274
|
+
parseLesson(step, syllabus.lessons)
|
275
|
+
)
|
276
|
+
push({
|
277
|
+
...syllabus,
|
278
|
+
lessons: lessons,
|
279
|
+
courseInfo: {
|
280
|
+
...syllabus.courseInfo,
|
281
|
+
title: res.parsed.title || syllabus.courseInfo.title,
|
282
|
+
description:
|
283
|
+
res.parsed.description || syllabus.courseInfo.description,
|
284
|
+
language:
|
285
|
+
res.parsed.languageCode || syllabus.courseInfo.language || "en",
|
286
|
+
technologies:
|
287
|
+
res.parsed.technologies.length > 0
|
288
|
+
? res.parsed.technologies
|
289
|
+
: syllabus.courseInfo.technologies || [],
|
290
|
+
},
|
291
|
+
})
|
292
|
+
|
293
|
+
if (res.parsed.languageCode) {
|
294
|
+
i18n.changeLanguage(res.parsed.languageCode)
|
295
|
+
}
|
296
|
+
|
297
|
+
setMessages((prev) => [
|
298
|
+
...prev.filter((m) => Boolean(m.content)),
|
299
|
+
{
|
300
|
+
type: "assistant",
|
301
|
+
content: res.parsed.aiMessage,
|
302
|
+
},
|
303
|
+
])
|
304
|
+
setIsThinking(false)
|
305
|
+
setNotificationId("")
|
306
|
+
}}
|
307
|
+
notificationId={notificationId}
|
308
|
+
/>
|
309
|
+
{showLoginModal && (
|
310
|
+
<Login
|
311
|
+
onFinish={() => {
|
312
|
+
setShowLoginModal(false)
|
313
|
+
}}
|
314
|
+
/>
|
315
|
+
)}
|
316
|
+
|
317
|
+
<ConsumablesManager />
|
318
|
+
|
319
|
+
<Sidebar
|
320
|
+
// messages={messages}
|
321
|
+
sendPrompt={sendPrompt}
|
322
|
+
handleSubmit={handleSubmit}
|
323
|
+
/>
|
324
|
+
|
325
|
+
<ContentIndex
|
326
|
+
handleSubmit={handleSubmit}
|
327
|
+
messages={messages}
|
328
|
+
isThinking={isThinking}
|
329
|
+
openLogin={() => {
|
330
|
+
setShowLoginModal(true)
|
331
|
+
}}
|
332
|
+
/>
|
333
|
+
</div>
|
334
|
+
)
|
335
|
+
}
|
336
|
+
|
337
|
+
export default SyllabusEditor
|