@learnpack/learnpack 5.0.234 → 5.0.240
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/commands/init.js +7 -2
- package/lib/commands/publish.d.ts +1 -1
- package/lib/commands/publish.js +5 -4
- package/lib/commands/serve.d.ts +2 -4
- package/lib/commands/serve.js +79 -116
- package/lib/creatorDist/assets/{index-CFK5bQP2.js → index-DJn8b8wj.js} +8168 -8115
- package/lib/creatorDist/index.html +1 -1
- package/lib/utils/api.d.ts +1 -0
- package/lib/utils/api.js +1 -0
- package/lib/utils/rigoActions.d.ts +6 -1
- package/lib/utils/rigoActions.js +19 -2
- package/package.json +2 -2
- package/src/commands/init.ts +12 -2
- package/src/commands/publish.ts +12 -4
- package/src/commands/serve.ts +114 -181
- package/src/creator/src/App.tsx +67 -62
- package/src/creator/src/components/LessonItem.tsx +9 -3
- package/src/creator/src/components/NotificationListener.tsx +32 -0
- package/src/creator/src/components/syllabus/SyllabusEditor.tsx +42 -44
- package/src/creator/src/utils/creatorUtils.ts +2 -2
- package/src/creator/src/utils/rigo.ts +12 -3
- package/src/creator/src/utils/store.ts +13 -2
- package/src/creatorDist/assets/{index-CFK5bQP2.js → index-DJn8b8wj.js} +8168 -8115
- package/src/creatorDist/index.html +1 -1
- package/src/ui/_app/app.css +1 -1
- package/src/ui/_app/app.js +131 -131
- package/src/ui/app.tar.gz +0 -0
- package/src/utils/api.ts +2 -0
- package/src/utils/configBuilder.ts +1 -2
- package/src/utils/rigoActions.ts +32 -1
package/src/creator/src/App.tsx
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { useEffect } from "react"
|
1
|
+
import { useEffect, useState } from "react"
|
2
2
|
import StepWizard from "./components/StepWizard"
|
3
3
|
import SelectableCard from "./components/SelectableCard"
|
4
4
|
import Loader from "./components/Loader"
|
@@ -25,6 +25,7 @@ import TurnstileChallenge from "./components/TurnstileChallenge"
|
|
25
25
|
import ResumeCourseModal from "./components/ResumeCourseModal"
|
26
26
|
import { possiblePurposes, PurposeSelector } from "./components/PurposeSelector"
|
27
27
|
import { useTranslation } from "react-i18next"
|
28
|
+
import NotificationListener from "./components/NotificationListener"
|
28
29
|
|
29
30
|
function App() {
|
30
31
|
const navigate = useNavigate()
|
@@ -64,6 +65,8 @@ function App() {
|
|
64
65
|
}))
|
65
66
|
)
|
66
67
|
|
68
|
+
const [notificationId, setNotificationId] = useState<string>("")
|
69
|
+
|
67
70
|
useEffect(() => {
|
68
71
|
if (formState.isCompleted) {
|
69
72
|
handleCreateTutorial()
|
@@ -111,7 +114,6 @@ function App() {
|
|
111
114
|
cleanAll()
|
112
115
|
}
|
113
116
|
|
114
|
-
|
115
117
|
if (description) {
|
116
118
|
console.log("description", description)
|
117
119
|
setFormState({
|
@@ -187,51 +189,9 @@ function App() {
|
|
187
189
|
formState.purpose || "learnpack-lesson-writer",
|
188
190
|
auth.rigoToken && isAuthenticated ? false : true
|
189
191
|
)
|
190
|
-
|
191
|
-
return parseLesson(lesson, [])
|
192
|
-
})
|
193
|
-
|
194
|
-
push({
|
195
|
-
lessons,
|
196
|
-
courseInfo: {
|
197
|
-
...formState,
|
198
|
-
title: fixTitleLength(res.parsed.title),
|
199
|
-
description: res.parsed.description,
|
200
|
-
language: res.parsed.languageCode || formState.language || "en",
|
201
|
-
technologies:
|
202
|
-
res.parsed.technologies.length > 0
|
203
|
-
? res.parsed.technologies
|
204
|
-
: ["education", "quizzes"],
|
205
|
-
},
|
206
|
-
})
|
192
|
+
console.log("RES", res)
|
207
193
|
|
208
|
-
|
209
|
-
i18n.changeLanguage(res.parsed.languageCode)
|
210
|
-
}
|
211
|
-
|
212
|
-
navigate("/creator/syllabus")
|
213
|
-
setFormState({
|
214
|
-
isCompleted: false,
|
215
|
-
currentStep: "description",
|
216
|
-
})
|
217
|
-
setMessages([
|
218
|
-
{
|
219
|
-
type: "user",
|
220
|
-
content: formState.description,
|
221
|
-
},
|
222
|
-
{
|
223
|
-
type: "assistant",
|
224
|
-
content: res.parsed.aiMessage,
|
225
|
-
},
|
226
|
-
{
|
227
|
-
type: "assistant",
|
228
|
-
content: t("contentIndex.okMessage"),
|
229
|
-
},
|
230
|
-
{
|
231
|
-
type: "assistant",
|
232
|
-
content: t("contentIndex.instructionsMessage"),
|
233
|
-
},
|
234
|
-
])
|
194
|
+
setNotificationId(res.notificationId)
|
235
195
|
} catch (error) {
|
236
196
|
console.error(error, "ERROR CREATING TUTORIAL")
|
237
197
|
toast.error("Something went wrong. Please try again.")
|
@@ -432,24 +392,69 @@ function App() {
|
|
432
392
|
<>
|
433
393
|
<ParamsChecker />
|
434
394
|
{formState.isCompleted && history.length === 0 ? (
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
395
|
+
<>
|
396
|
+
<Loader
|
397
|
+
text={t("loader.text")}
|
398
|
+
icon={<img src={RIGO_FLOAT_GIF} alt="rigo" className="w-20 h-20" />}
|
399
|
+
/>
|
400
|
+
{notificationId && (
|
401
|
+
<NotificationListener
|
402
|
+
onNotification={(res) => {
|
403
|
+
console.log("Async response", res)
|
404
|
+
toast.success("Initial course created successfully")
|
405
|
+
const lessons = res.parsed.listOfSteps.map((lesson: any) => {
|
406
|
+
return parseLesson(lesson, [])
|
407
|
+
})
|
408
|
+
|
409
|
+
push({
|
410
|
+
lessons,
|
411
|
+
courseInfo: {
|
412
|
+
...formState,
|
413
|
+
title: fixTitleLength(res.parsed.title),
|
414
|
+
description: res.parsed.description,
|
415
|
+
language:
|
416
|
+
res.parsed.languageCode || formState.language || "en",
|
417
|
+
technologies:
|
418
|
+
res.parsed.technologies.length > 0
|
419
|
+
? res.parsed.technologies
|
420
|
+
: ["education", "quizzes"],
|
421
|
+
},
|
422
|
+
})
|
423
|
+
|
424
|
+
if (res.parsed.languageCode) {
|
425
|
+
i18n.changeLanguage(res.parsed.languageCode)
|
426
|
+
}
|
427
|
+
|
428
|
+
setMessages([
|
429
|
+
{
|
430
|
+
type: "user",
|
431
|
+
content: formState.description,
|
432
|
+
},
|
433
|
+
{
|
434
|
+
type: "assistant",
|
435
|
+
content: res.parsed.aiMessage,
|
436
|
+
},
|
437
|
+
{
|
438
|
+
type: "assistant",
|
439
|
+
content: t("contentIndex.okMessage"),
|
440
|
+
},
|
441
|
+
{
|
442
|
+
type: "assistant",
|
443
|
+
content: t("contentIndex.instructionsMessage"),
|
444
|
+
},
|
445
|
+
])
|
446
|
+
navigate("/creator/syllabus")
|
447
|
+
setFormState({
|
448
|
+
isCompleted: false,
|
449
|
+
currentStep: "description",
|
450
|
+
})
|
451
|
+
}}
|
452
|
+
notificationId={notificationId}
|
453
|
+
/>
|
454
|
+
)}
|
455
|
+
</>
|
439
456
|
) : (
|
440
457
|
<>
|
441
|
-
{/* {formState.language && (
|
442
|
-
<div className="flex flex-col items-center justify-center">
|
443
|
-
<p>
|
444
|
-
<span className="font-bold">Language:</span>{" "}
|
445
|
-
{formState.language}
|
446
|
-
</p>
|
447
|
-
<p>
|
448
|
-
<span className="font-bold">Technologies:</span>{" "}
|
449
|
-
{formState.technologies?.join(", ")}
|
450
|
-
</p>
|
451
|
-
</div>
|
452
|
-
)} */}
|
453
458
|
{history.length > 0 && (
|
454
459
|
<ResumeCourseModal
|
455
460
|
onContinue={() => {
|
@@ -15,6 +15,12 @@ export interface Lesson {
|
|
15
15
|
duration?: number
|
16
16
|
}
|
17
17
|
|
18
|
+
const typeToEmoji: Record<string, string> = {
|
19
|
+
read: "📖",
|
20
|
+
code: "💻",
|
21
|
+
quiz: "🧠",
|
22
|
+
}
|
23
|
+
|
18
24
|
interface LessonItemProps {
|
19
25
|
lesson: Lesson
|
20
26
|
isNew: boolean
|
@@ -58,12 +64,12 @@ export const LessonItem: React.FC<LessonItemProps> = ({
|
|
58
64
|
{mode === "teacher" && (
|
59
65
|
<>
|
60
66
|
<span className="index-circle">{cleanFloatString(lesson.id)}</span>
|
61
|
-
<span className="text-gray-500 text-sm">
|
62
|
-
{lesson.type[0] + lesson.type.slice(1).toLowerCase()} ●
|
63
|
-
</span>
|
64
67
|
</>
|
65
68
|
)}
|
66
69
|
|
70
|
+
<span className="text-gray-500 text-sm">
|
71
|
+
{typeToEmoji[lesson.type.toLowerCase()]}
|
72
|
+
</span>
|
67
73
|
{isEditing ? (
|
68
74
|
<input
|
69
75
|
defaultValue={lesson.title}
|
@@ -0,0 +1,32 @@
|
|
1
|
+
import { useEffect } from "react"
|
2
|
+
import CreatorSocket from "../utils/socket"
|
3
|
+
|
4
|
+
interface NotificationListenerProps {
|
5
|
+
notificationId: string
|
6
|
+
onNotification: (data: any) => void
|
7
|
+
}
|
8
|
+
|
9
|
+
const socketClient = new CreatorSocket("")
|
10
|
+
|
11
|
+
const NotificationListener: React.FC<NotificationListenerProps> = ({
|
12
|
+
notificationId,
|
13
|
+
onNotification,
|
14
|
+
}) => {
|
15
|
+
useEffect(() => {
|
16
|
+
console.log("NOTIFICATION ID listening", notificationId)
|
17
|
+
if (!notificationId) return
|
18
|
+
|
19
|
+
socketClient.connect()
|
20
|
+
socketClient.on(notificationId, onNotification)
|
21
|
+
socketClient.emit("registerNotification", { notificationId })
|
22
|
+
|
23
|
+
return () => {
|
24
|
+
socketClient.off(notificationId, onNotification)
|
25
|
+
socketClient.disconnect()
|
26
|
+
}
|
27
|
+
}, [notificationId, onNotification])
|
28
|
+
|
29
|
+
return null
|
30
|
+
}
|
31
|
+
|
32
|
+
export default NotificationListener
|
@@ -29,6 +29,7 @@ import { ParamsChecker } from "../ParamsChecker"
|
|
29
29
|
import { randomUUID, slugify } from "../../utils/creatorUtils"
|
30
30
|
import { RIGO_FLOAT_GIF } from "../../utils/constants"
|
31
31
|
import { useTranslation } from "react-i18next"
|
32
|
+
import NotificationListener from "../NotificationListener"
|
32
33
|
|
33
34
|
const SyllabusEditor: React.FC = () => {
|
34
35
|
const navigate = useNavigate()
|
@@ -60,6 +61,7 @@ const SyllabusEditor: React.FC = () => {
|
|
60
61
|
const [isGenerating, setIsGenerating] = useState(false)
|
61
62
|
const [showLoginModal, setShowLoginModal] = useState(false)
|
62
63
|
const [isThinking, setIsThinking] = useState(false)
|
64
|
+
const [notificationId, setNotificationId] = useState<string>("")
|
63
65
|
|
64
66
|
const syllabus = history[history.length - 1]
|
65
67
|
|
@@ -89,8 +91,6 @@ const SyllabusEditor: React.FC = () => {
|
|
89
91
|
}, [])
|
90
92
|
|
91
93
|
const checkSlug = async () => {
|
92
|
-
console.log("Checking slug")
|
93
|
-
|
94
94
|
if (!syllabus.courseInfo.title) {
|
95
95
|
toast.error("Please provide a title for the course")
|
96
96
|
return
|
@@ -109,7 +109,6 @@ const SyllabusEditor: React.FC = () => {
|
|
109
109
|
slug = slugify(newTitle)
|
110
110
|
isAvailable = await isSlugAvailable(slug)
|
111
111
|
}
|
112
|
-
console.log("Slug is available", slug)
|
113
112
|
|
114
113
|
push({
|
115
114
|
...syllabus,
|
@@ -162,44 +161,9 @@ const SyllabusEditor: React.FC = () => {
|
|
162
161
|
auth.rigoToken && isAuthenticated ? false : true
|
163
162
|
)
|
164
163
|
|
165
|
-
|
166
|
-
parseLesson(step, syllabus.lessons)
|
167
|
-
)
|
168
|
-
push({
|
169
|
-
...syllabus,
|
170
|
-
lessons: lessons,
|
171
|
-
courseInfo: {
|
172
|
-
...syllabus.courseInfo,
|
173
|
-
title: fixTitleLength(res.parsed.title) || syllabus.courseInfo.title,
|
174
|
-
description:
|
175
|
-
res.parsed.description || syllabus.courseInfo.description,
|
176
|
-
language:
|
177
|
-
res.parsed.languageCode || syllabus.courseInfo.language || "en",
|
178
|
-
technologies:
|
179
|
-
res.parsed.technologies || syllabus.courseInfo.technologies || [],
|
180
|
-
},
|
181
|
-
})
|
182
|
-
|
183
|
-
if (res.parsed.languageCode) {
|
184
|
-
i18n.changeLanguage(res.parsed.languageCode)
|
185
|
-
}
|
186
|
-
|
187
|
-
const newMessages: TMessage[] = [
|
188
|
-
...messages,
|
189
|
-
{
|
190
|
-
type: "user",
|
191
|
-
content: prompt,
|
192
|
-
},
|
193
|
-
{
|
194
|
-
type: "assistant",
|
195
|
-
content: res.parsed.aiMessage,
|
196
|
-
},
|
197
|
-
]
|
198
|
-
setMessages(newMessages)
|
199
|
-
setIsThinking(false)
|
164
|
+
setNotificationId(res.notificationId)
|
200
165
|
} catch (error) {
|
201
166
|
console.error(error)
|
202
|
-
// Remove the last message
|
203
167
|
const newMessages = [...messages]
|
204
168
|
newMessages.pop()
|
205
169
|
setMessages(newMessages as TMessage[])
|
@@ -248,7 +212,6 @@ const SyllabusEditor: React.FC = () => {
|
|
248
212
|
}
|
249
213
|
|
250
214
|
setIsGenerating(true)
|
251
|
-
console.log("Timeout set")
|
252
215
|
|
253
216
|
const timeout = setTimeout(() => {
|
254
217
|
cleanAll()
|
@@ -257,10 +220,8 @@ const SyllabusEditor: React.FC = () => {
|
|
257
220
|
)}?token=${auth.bcToken}&language=${syllabus.courseInfo.language}`
|
258
221
|
}, 8000)
|
259
222
|
try {
|
260
|
-
console.log("Creating course")
|
261
223
|
await createCourse(syllabus, tokenToUse, auth.bcToken)
|
262
224
|
} catch (error) {
|
263
|
-
console.log("Failed to create course, clearing timeout")
|
264
225
|
console.error("Failed to create course:", error)
|
265
226
|
clearTimeout(timeout)
|
266
227
|
setIsGenerating(false)
|
@@ -269,8 +230,6 @@ const SyllabusEditor: React.FC = () => {
|
|
269
230
|
|
270
231
|
if (!syllabus) return null
|
271
232
|
|
272
|
-
console.log(syllabus, "SYLLABUS")
|
273
|
-
|
274
233
|
return isGenerating ? (
|
275
234
|
<>
|
276
235
|
<Loader
|
@@ -284,6 +243,45 @@ It may take a moment..."
|
|
284
243
|
) : (
|
285
244
|
<div className="flex w-full bg-white rounded-md shadow-md overflow-hidden h-screen ">
|
286
245
|
<ParamsChecker />
|
246
|
+
<NotificationListener
|
247
|
+
onNotification={(res) => {
|
248
|
+
const lessons: Lesson[] = res.parsed.listOfSteps.map((step: any) =>
|
249
|
+
parseLesson(step, syllabus.lessons)
|
250
|
+
)
|
251
|
+
push({
|
252
|
+
...syllabus,
|
253
|
+
lessons: lessons,
|
254
|
+
courseInfo: {
|
255
|
+
...syllabus.courseInfo,
|
256
|
+
title:
|
257
|
+
fixTitleLength(res.parsed.title) || syllabus.courseInfo.title,
|
258
|
+
description:
|
259
|
+
res.parsed.description || syllabus.courseInfo.description,
|
260
|
+
language:
|
261
|
+
res.parsed.languageCode || syllabus.courseInfo.language || "en",
|
262
|
+
technologies:
|
263
|
+
res.parsed.technologies.length > 0
|
264
|
+
? res.parsed.technologies
|
265
|
+
: syllabus.courseInfo.technologies || [],
|
266
|
+
},
|
267
|
+
})
|
268
|
+
|
269
|
+
if (res.parsed.languageCode) {
|
270
|
+
i18n.changeLanguage(res.parsed.languageCode)
|
271
|
+
}
|
272
|
+
|
273
|
+
setMessages((prev) => [
|
274
|
+
...prev.filter((m) => Boolean(m.content)),
|
275
|
+
{
|
276
|
+
type: "assistant",
|
277
|
+
content: res.parsed.aiMessage,
|
278
|
+
},
|
279
|
+
])
|
280
|
+
setIsThinking(false)
|
281
|
+
setNotificationId("")
|
282
|
+
}}
|
283
|
+
notificationId={notificationId}
|
284
|
+
/>
|
287
285
|
{showLoginModal && (
|
288
286
|
<Login
|
289
287
|
onFinish={() => {
|
@@ -14,8 +14,8 @@ export const slugify = (text: string) => {
|
|
14
14
|
.replace(/^-+|-+$/g, "") // Trim hyphens from start/end
|
15
15
|
}
|
16
16
|
|
17
|
-
export const randomUUID = () => {
|
18
|
-
return Math.random().toString(36).substring(2,
|
17
|
+
export const randomUUID = (length = 15) => {
|
18
|
+
return Math.random().toString(36).substring(2, length)
|
19
19
|
}
|
20
20
|
|
21
21
|
export const processImage = async (
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import toast from "react-hot-toast"
|
2
2
|
import { RIGOBOT_HOST, DEV_MODE } from "./constants"
|
3
3
|
import axios, { AxiosError } from "axios"
|
4
|
+
import { randomUUID } from "./creatorUtils"
|
4
5
|
|
5
6
|
type TInteractiveCreationInputs = {
|
6
7
|
courseInfo: string
|
@@ -14,6 +15,14 @@ export const publicInteractiveCreation = async (
|
|
14
15
|
publicRequest: boolean = true
|
15
16
|
): Promise<any | null> => {
|
16
17
|
try {
|
18
|
+
const randomUID = randomUUID(15)
|
19
|
+
const webhookUrl = `${
|
20
|
+
DEV_MODE
|
21
|
+
? "https://9cw5zmww-3000.use2.devtunnels.ms"
|
22
|
+
: window.location.origin
|
23
|
+
// "https://9cw5zmww-3000.use2.devtunnels.ms"
|
24
|
+
}/notifications/${randomUID}`
|
25
|
+
|
17
26
|
const response = await axios.post(
|
18
27
|
`${RIGOBOT_HOST}/v1/prompting${
|
19
28
|
publicRequest ? "/public" : ""
|
@@ -21,8 +30,8 @@ export const publicInteractiveCreation = async (
|
|
21
30
|
{
|
22
31
|
inputs: inputs,
|
23
32
|
include_purpose_objective: true,
|
24
|
-
execute_async: false,
|
25
33
|
purpose_slug: purposeSlug,
|
34
|
+
webhook_url: webhookUrl,
|
26
35
|
},
|
27
36
|
{
|
28
37
|
headers: {
|
@@ -32,10 +41,10 @@ export const publicInteractiveCreation = async (
|
|
32
41
|
}
|
33
42
|
)
|
34
43
|
|
35
|
-
return response.data
|
44
|
+
return { res: response.data, notificationId: randomUID }
|
36
45
|
} catch (error: unknown) {
|
37
46
|
const err = error as AxiosError
|
38
|
-
console.log("error", err)
|
47
|
+
console.log("error trying to create course", err)
|
39
48
|
|
40
49
|
if (err.response?.status === 403) {
|
41
50
|
toast.error("You've reached the limit. Please log in to continue.")
|
@@ -53,7 +53,9 @@ type Store = {
|
|
53
53
|
uploadedFiles: ParsedFile[]
|
54
54
|
setUploadedFiles: (uploadedFiles: ParsedFile[]) => void
|
55
55
|
messages: TMessage[]
|
56
|
-
setMessages: (
|
56
|
+
setMessages: (
|
57
|
+
messages: TMessage[] | ((prev: TMessage[]) => TMessage[])
|
58
|
+
) => void
|
57
59
|
cleanHistory: () => void
|
58
60
|
history: Syllabus[]
|
59
61
|
technologies: TTechnology[]
|
@@ -102,7 +104,16 @@ const useStore = create<Store>()(
|
|
102
104
|
messages: [],
|
103
105
|
technologies: [],
|
104
106
|
setTechnologies: (technologies: TTechnology[]) => set({ technologies }),
|
105
|
-
setMessages: (
|
107
|
+
setMessages: (
|
108
|
+
messages: TMessage[] | ((prev: TMessage[]) => TMessage[])
|
109
|
+
) => {
|
110
|
+
set((state) => ({
|
111
|
+
messages:
|
112
|
+
typeof messages === "function"
|
113
|
+
? messages(state.messages)
|
114
|
+
: messages,
|
115
|
+
}))
|
116
|
+
},
|
106
117
|
setFormState: (formState: Partial<FormState>) =>
|
107
118
|
set((state) => ({ formState: { ...state.formState, ...formState } })),
|
108
119
|
resetFormState: () =>
|