@learnpack/learnpack 5.0.69 → 5.0.70
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 +13 -13
- package/lib/creatorDist/assets/{index-C7bLE5wU.js → index-Chx6V3zd.js} +9427 -9322
- package/lib/creatorDist/assets/{index-C_Rp91QE.css → index-Dqo9u2iR.css} +98 -31
- package/lib/creatorDist/index.html +2 -2
- package/lib/creatorDist/rigo-float.gif +0 -0
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/src/creator/public/rigo-float.gif +0 -0
- package/src/creator/src/App.tsx +202 -163
- package/src/creator/src/assets/svgs.tsx +1 -1
- package/src/creator/src/components/LessonItem.tsx +10 -3
- package/src/creator/src/components/Message.tsx +1 -1
- package/src/creator/src/components/StepWizard.tsx +32 -18
- package/src/creator/src/components/syllabus/ContentIndex.tsx +44 -9
- package/src/creator/src/components/syllabus/Sidebar.tsx +123 -0
- package/src/creator/src/components/syllabus/SyllabusEditor.tsx +6 -124
- package/src/creator/src/index.css +2 -6
- package/src/creator/src/utils/store.ts +12 -3
- package/src/creatorDist/assets/{index-C7bLE5wU.js → index-Chx6V3zd.js} +9427 -9322
- package/src/creatorDist/assets/{index-C_Rp91QE.css → index-Dqo9u2iR.css} +98 -31
- package/src/creatorDist/index.html +2 -2
- package/src/creatorDist/rigo-float.gif +0 -0
@@ -0,0 +1,123 @@
|
|
1
|
+
import { useRef, useState } from "react"
|
2
|
+
import useStore from "../../utils/store"
|
3
|
+
import { TMessage } from "../Message"
|
4
|
+
import FileUploader from "../FileUploader"
|
5
|
+
import { SVGS } from "../../assets/svgs"
|
6
|
+
import { Message } from "../Message"
|
7
|
+
|
8
|
+
export const Sidebar = ({
|
9
|
+
messages,
|
10
|
+
sendPrompt,
|
11
|
+
handleSubmit,
|
12
|
+
}: {
|
13
|
+
messages: TMessage[]
|
14
|
+
sendPrompt: (prompt: string) => void
|
15
|
+
handleSubmit: () => void
|
16
|
+
}) => {
|
17
|
+
const inputRef = useRef<HTMLTextAreaElement>(null)
|
18
|
+
const syllabus = useStore((state) => state.syllabus)
|
19
|
+
const setSyllabus = useStore((state) => state.setSyllabus)
|
20
|
+
|
21
|
+
const [isOpen, setIsOpen] = useState(false)
|
22
|
+
|
23
|
+
return (
|
24
|
+
<>
|
25
|
+
{!isOpen && (
|
26
|
+
<button
|
27
|
+
className="fixed top-2 left-2 z-50 lg:hidden bg-white p-1 rounded shadow-md cursor-pointer"
|
28
|
+
onClick={() => setIsOpen(true)}
|
29
|
+
>
|
30
|
+
☰
|
31
|
+
</button>
|
32
|
+
)}
|
33
|
+
|
34
|
+
<div
|
35
|
+
className={`fixed z-40 top-0 left-0 h-full w-4/5 max-w-sm bg-learnpack-blue text-sm text-gray-700 border-r overflow-y-auto scrollbar-hide p-6 transition-transform duration-300 ease-in-out lg:relative lg:transform-none lg:w-1/3 ${
|
36
|
+
isOpen ? "translate-x-0" : "-translate-x-full lg:translate-x-0"
|
37
|
+
}`}
|
38
|
+
>
|
39
|
+
{isOpen && (
|
40
|
+
<button
|
41
|
+
className="lg:hidden bg-white p-1 rounded shadow-md mb-4 cursor-pointer absolute top-2 left-2"
|
42
|
+
onClick={() => setIsOpen(false)}
|
43
|
+
>
|
44
|
+
✕
|
45
|
+
</button>
|
46
|
+
)}
|
47
|
+
{/* This should have the same width as the input area */}
|
48
|
+
<div className="space-y-2 mb-6 ">
|
49
|
+
<p className="w-full bg-white p-2 rounded">
|
50
|
+
If you're satisfied, type "OK" in the chat.
|
51
|
+
</p>
|
52
|
+
<p className="w-full bg-white p-2 rounded">
|
53
|
+
If not, use the chat to give more context.
|
54
|
+
</p>
|
55
|
+
</div>
|
56
|
+
|
57
|
+
<div className="space-y-2 pb-32 h-[70%] overflow-y-auto scrollbar-hide">
|
58
|
+
{messages.map((message, index) => (
|
59
|
+
<Message
|
60
|
+
key={index}
|
61
|
+
type={message.type}
|
62
|
+
content={message.content}
|
63
|
+
/>
|
64
|
+
))}
|
65
|
+
</div>
|
66
|
+
|
67
|
+
<div className="relative w-full rounded-md bg-white text-gray-700 h-24">
|
68
|
+
<textarea
|
69
|
+
ref={inputRef}
|
70
|
+
style={{ resize: "none" }}
|
71
|
+
className="w-full h-full p-2"
|
72
|
+
placeholder="How can Learnpack help you?"
|
73
|
+
autoFocus
|
74
|
+
onKeyUp={(e) => {
|
75
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
76
|
+
e.preventDefault()
|
77
|
+
sendPrompt(inputRef.current?.value || "")
|
78
|
+
inputRef.current!.value = ""
|
79
|
+
}
|
80
|
+
if (
|
81
|
+
e.key.toLowerCase() === "k" &&
|
82
|
+
inputRef.current?.value.toLowerCase().trim() === "ok"
|
83
|
+
) {
|
84
|
+
e.preventDefault()
|
85
|
+
handleSubmit()
|
86
|
+
}
|
87
|
+
}}
|
88
|
+
/>
|
89
|
+
<div className="absolute bottom-2 right-2 flex gap-1 items-center">
|
90
|
+
<div className="relative inline-block">
|
91
|
+
{syllabus.uploadedFiles?.length > 0 && (
|
92
|
+
<span
|
93
|
+
className="absolute -top-1 right-0 inline-flex items-center justify-center w-3 h-3 text-[10px] text-white bg-blue-200 rounded-full hover:bg-red-300 cursor-pointer"
|
94
|
+
title="Remove uploaded files"
|
95
|
+
onClick={() => {
|
96
|
+
setSyllabus({ ...syllabus, uploadedFiles: [] })
|
97
|
+
}}
|
98
|
+
>
|
99
|
+
{syllabus.uploadedFiles?.length}
|
100
|
+
</span>
|
101
|
+
)}
|
102
|
+
<FileUploader
|
103
|
+
onResult={(res) => {
|
104
|
+
setSyllabus({
|
105
|
+
...syllabus,
|
106
|
+
uploadedFiles: [...syllabus.uploadedFiles, ...res],
|
107
|
+
})
|
108
|
+
}}
|
109
|
+
/>
|
110
|
+
</div>
|
111
|
+
|
112
|
+
<button
|
113
|
+
className="cursor-pointer blue-on-hover flex items-center justify-center w-6 h-6"
|
114
|
+
onClick={() => sendPrompt(inputRef.current?.value || "")}
|
115
|
+
>
|
116
|
+
{SVGS.send}
|
117
|
+
</button>
|
118
|
+
</div>
|
119
|
+
</div>
|
120
|
+
</div>
|
121
|
+
</>
|
122
|
+
)
|
123
|
+
}
|
@@ -1,5 +1,4 @@
|
|
1
1
|
import React, { useRef, useState } from "react"
|
2
|
-
import { SVGS } from "../../assets/svgs"
|
3
2
|
import { useShallow } from "zustand/react/shallow"
|
4
3
|
import useStore from "../../utils/store"
|
5
4
|
import { interactiveCreation } from "../../utils/rigo"
|
@@ -16,129 +15,12 @@ import {
|
|
16
15
|
} from "../../utils/creatorUtils"
|
17
16
|
|
18
17
|
import Loader from "../Loader"
|
19
|
-
import {
|
18
|
+
import { TMessage } from "../Message"
|
20
19
|
import { Lesson } from "../LessonItem"
|
21
|
-
import FileUploader from "../FileUploader"
|
22
20
|
import { ConsumablesManager } from "../ConsumablesManager"
|
23
21
|
import toast from "react-hot-toast"
|
24
22
|
import { ContentIndex } from "./ContentIndex"
|
25
|
-
import {
|
26
|
-
|
27
|
-
const ContentIndexHeader = ({
|
28
|
-
messages,
|
29
|
-
syllabus,
|
30
|
-
}: {
|
31
|
-
messages: TMessage[]
|
32
|
-
syllabus: Syllabus
|
33
|
-
}) => {
|
34
|
-
return (
|
35
|
-
<div>
|
36
|
-
<h2 className="text-lg font-semibold">
|
37
|
-
{messages.filter((m) => m.type === "assistant" && m.content.length > 0)
|
38
|
-
.length === 0
|
39
|
-
? "I've created a detailed structure for your course."
|
40
|
-
: "I've updated the structure based on your feedback."}
|
41
|
-
</h2>
|
42
|
-
<p className="text-sm text-gray-600">
|
43
|
-
{messages.filter((m) => m.type === "assistant" && m.content.length > 0)
|
44
|
-
.length === 0
|
45
|
-
? `It includes a mix of reading, coding exercises, and quizzes. Give
|
46
|
-
it a look and let me know if it aligns with your expectations or if
|
47
|
-
there are any changes you'd like to make.`
|
48
|
-
: "Based on your input, here is the new syllabus, updates are highlighted in yellow"}
|
49
|
-
</p>
|
50
|
-
<h3 className="text-sm text-gray-600 mt-2 font-bold">
|
51
|
-
{syllabus.courseInfo.title}
|
52
|
-
</h3>
|
53
|
-
</div>
|
54
|
-
)
|
55
|
-
}
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
const Sidebar = ({
|
60
|
-
messages,
|
61
|
-
sendPrompt,
|
62
|
-
handleSubmit,
|
63
|
-
}: {
|
64
|
-
messages: TMessage[]
|
65
|
-
sendPrompt: (prompt: string) => void
|
66
|
-
handleSubmit: () => void
|
67
|
-
}) => {
|
68
|
-
const inputRef = useRef<HTMLTextAreaElement>(null)
|
69
|
-
const syllabus = useStore((state) => state.syllabus)
|
70
|
-
const setSyllabus = useStore((state) => state.setSyllabus)
|
71
|
-
return (
|
72
|
-
<div className="w-1/3 p-6 text-sm text-gray-700 border-r bg-learnpack-blue h-screen overflow-y-auto scrollbar-hide relative">
|
73
|
-
<p className="mt-2">If you're satisfied, type "OK" in the chat.</p>
|
74
|
-
<p className="mt-2">If not, use the chat to give more context.</p>
|
75
|
-
|
76
|
-
<div className="mt-10 space-y-2 pb-16">
|
77
|
-
{messages.map((message, index) => (
|
78
|
-
<Message key={index} type={message.type} content={message.content} />
|
79
|
-
))}
|
80
|
-
</div>
|
81
|
-
|
82
|
-
<div className="absolute bottom-4 left-1/2 transform -translate-x-1/2 w-[90%] border rounded-md bg-white text-gray-400 resize-none h-24 ">
|
83
|
-
<textarea
|
84
|
-
ref={inputRef}
|
85
|
-
style={{ resize: "none" }}
|
86
|
-
className="w-full h-full p-2"
|
87
|
-
placeholder="How can Learnpack help you?"
|
88
|
-
autoFocus
|
89
|
-
onKeyUp={(e) => {
|
90
|
-
if (e.key === "Enter" && !e.shiftKey) {
|
91
|
-
e.preventDefault()
|
92
|
-
sendPrompt(inputRef.current?.value || "")
|
93
|
-
inputRef.current!.value = ""
|
94
|
-
}
|
95
|
-
// if the pressed key is K or k
|
96
|
-
if (e.key === "K" || e.key === "k") {
|
97
|
-
e.preventDefault()
|
98
|
-
|
99
|
-
if (inputRef.current?.value.toLowerCase().trim() === "ok") {
|
100
|
-
handleSubmit()
|
101
|
-
}
|
102
|
-
}
|
103
|
-
}}
|
104
|
-
/>
|
105
|
-
<div className="absolute bottom-2 right-2 flex gap-1 items-center">
|
106
|
-
<div className="relative inline-block">
|
107
|
-
{syllabus.uploadedFiles?.length > 0 && (
|
108
|
-
<span
|
109
|
-
className="absolute -top-1 right-0 inline-flex items-center justify-center w-3 h-3 text-[10px] text-white bg-blue-200 rounded-full hover:bg-red-300 cursor-pointer"
|
110
|
-
title="Remove uploaded files"
|
111
|
-
onClick={() => {
|
112
|
-
setSyllabus({
|
113
|
-
...syllabus,
|
114
|
-
uploadedFiles: [],
|
115
|
-
})
|
116
|
-
}}
|
117
|
-
>
|
118
|
-
{syllabus.uploadedFiles?.length}
|
119
|
-
</span>
|
120
|
-
)}
|
121
|
-
<FileUploader
|
122
|
-
onResult={(res) => {
|
123
|
-
setSyllabus({
|
124
|
-
...syllabus,
|
125
|
-
uploadedFiles: [...syllabus.uploadedFiles, ...res],
|
126
|
-
})
|
127
|
-
}}
|
128
|
-
/>
|
129
|
-
</div>
|
130
|
-
|
131
|
-
<button
|
132
|
-
className="cursor-pointer blue-on-hover flex items-center justify-center w-6 h-6"
|
133
|
-
onClick={() => sendPrompt(inputRef.current?.value || "")}
|
134
|
-
>
|
135
|
-
{SVGS.send}
|
136
|
-
</button>
|
137
|
-
</div>
|
138
|
-
</div>
|
139
|
-
</div>
|
140
|
-
)
|
141
|
-
}
|
23
|
+
import { Sidebar } from "./Sidebar"
|
142
24
|
|
143
25
|
const SyllabusEditor: React.FC = () => {
|
144
26
|
const [messages, setMessages] = useState<TMessage[]>([])
|
@@ -223,7 +105,7 @@ const SyllabusEditor: React.FC = () => {
|
|
223
105
|
return isGenerating ? (
|
224
106
|
<Loader
|
225
107
|
listeningTo="course-generation"
|
226
|
-
icon={
|
108
|
+
icon={<img src={"rigo-float.gif"} alt="rigo" className="w-20 h-20" />}
|
227
109
|
initialBuffer="🚀 Starting course generation..."
|
228
110
|
text="Learnpack is setting up your tutorial.
|
229
111
|
It may take a moment..."
|
@@ -231,18 +113,18 @@ It may take a moment..."
|
|
231
113
|
) : (
|
232
114
|
<div className="flex w-full bg-white rounded-md shadow-md overflow-hidden h-screen ">
|
233
115
|
<ConsumablesManager />
|
234
|
-
|
116
|
+
|
235
117
|
<Sidebar
|
236
118
|
messages={messages}
|
237
119
|
sendPrompt={sendPrompt}
|
238
120
|
handleSubmit={handleSubmit}
|
239
121
|
/>
|
240
122
|
|
241
|
-
<div className="w-
|
242
|
-
<ContentIndexHeader messages={messages} syllabus={syllabus} />
|
123
|
+
<div className="w-full p-8 space-y-6">
|
243
124
|
<ContentIndex
|
244
125
|
prevLessons={prevLessons.current}
|
245
126
|
handleSubmit={handleSubmit}
|
127
|
+
messages={messages}
|
246
128
|
/>
|
247
129
|
</div>
|
248
130
|
</div>
|
@@ -88,15 +88,12 @@ h1 {
|
|
88
88
|
.loader-icon {
|
89
89
|
width: 40px;
|
90
90
|
height: 40px;
|
91
|
-
|
92
|
-
border: 2px solid var(--loader-color);
|
93
|
-
border-top-color: transparent;
|
94
91
|
position: relative;
|
95
92
|
display: flex;
|
96
93
|
align-items: center;
|
97
94
|
justify-content: center;
|
98
95
|
}
|
99
|
-
.loader-icon::after {
|
96
|
+
/* .loader-icon::after {
|
100
97
|
content: "";
|
101
98
|
display: block;
|
102
99
|
width: 100%;
|
@@ -106,11 +103,10 @@ h1 {
|
|
106
103
|
height: 100%;
|
107
104
|
border: 2px solid var(--gray-text);
|
108
105
|
border-top: 2px solid var(--learnpack-blue);
|
109
|
-
/* background-color: red; */
|
110
106
|
|
111
107
|
border-radius: 50%;
|
112
108
|
animation: spin 2s linear infinite;
|
113
|
-
}
|
109
|
+
} */
|
114
110
|
|
115
111
|
@keyframes glowing {
|
116
112
|
0% {
|
@@ -9,7 +9,8 @@ export type FormState = {
|
|
9
9
|
hasContentIndex: boolean
|
10
10
|
contentIndex: string
|
11
11
|
isCompleted: boolean
|
12
|
-
|
12
|
+
variables: string[]
|
13
|
+
currentStep: string
|
13
14
|
title?: string
|
14
15
|
}
|
15
16
|
|
@@ -57,7 +58,14 @@ const useStore = create<Store>()(
|
|
57
58
|
hasContentIndex: false,
|
58
59
|
contentIndex: "",
|
59
60
|
isCompleted: false,
|
60
|
-
currentStep:
|
61
|
+
currentStep: "description",
|
62
|
+
variables: [
|
63
|
+
"description",
|
64
|
+
"duration",
|
65
|
+
"login",
|
66
|
+
"targetAudience",
|
67
|
+
"hasContentIndex",
|
68
|
+
],
|
61
69
|
},
|
62
70
|
setFormState: (formState: Partial<FormState>) =>
|
63
71
|
set((state) => ({ formState: { ...state.formState, ...formState } })),
|
@@ -71,8 +79,9 @@ const useStore = create<Store>()(
|
|
71
79
|
hasContentIndex: false,
|
72
80
|
contentIndex: "",
|
73
81
|
isCompleted: false,
|
74
|
-
currentStep:
|
82
|
+
currentStep: "description",
|
75
83
|
title: "",
|
84
|
+
variables: [],
|
76
85
|
},
|
77
86
|
uploadedFiles: [],
|
78
87
|
},
|