@learnpack/learnpack 5.0.83 → 5.0.85

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.
Files changed (29) hide show
  1. package/README.md +13 -13
  2. package/lib/creatorDist/assets/{index-CwPh6b6M.js → index-D15TgYvM.js} +38846 -31631
  3. package/lib/creatorDist/assets/{index-DSLkFVbA.css → index-ldEC0yWM.css} +125 -46
  4. package/lib/creatorDist/index.html +2 -2
  5. package/lib/utils/api.js +0 -8
  6. package/oclif.manifest.json +1 -1
  7. package/package.json +1 -1
  8. package/src/creator/package-lock.json +1229 -61
  9. package/src/creator/package.json +1 -0
  10. package/src/creator/src/App.tsx +12 -6
  11. package/src/creator/src/components/FileCard.tsx +22 -0
  12. package/src/creator/src/components/LessonItem.tsx +20 -9
  13. package/src/creator/src/components/Login.tsx +6 -24
  14. package/src/creator/src/components/MarkdownRenderer.tsx +11 -0
  15. package/src/creator/src/components/Message.tsx +4 -2
  16. package/src/creator/src/components/syllabus/ContentIndex.tsx +45 -64
  17. package/src/creator/src/components/syllabus/Sidebar.tsx +21 -19
  18. package/src/creator/src/components/syllabus/SyllabusEditor.tsx +62 -33
  19. package/src/creator/src/index.css +40 -0
  20. package/src/creator/src/utils/creatorUtils.ts +5 -1
  21. package/src/creator/src/utils/lib.ts +20 -10
  22. package/src/creator/src/utils/store.ts +31 -22
  23. package/src/creatorDist/assets/{index-CwPh6b6M.js → index-D15TgYvM.js} +38846 -31631
  24. package/src/creatorDist/assets/{index-DSLkFVbA.css → index-ldEC0yWM.css} +125 -46
  25. package/src/creatorDist/index.html +2 -2
  26. package/src/ui/_app/app.css +1 -1
  27. package/src/ui/_app/app.js +344 -344
  28. package/src/ui/app.tar.gz +0 -0
  29. package/src/utils/api.ts +0 -9
@@ -22,6 +22,7 @@
22
22
  "react": "^19.0.0",
23
23
  "react-dom": "^19.0.0",
24
24
  "react-hot-toast": "^2.5.2",
25
+ "react-markdown": "^10.1.0",
25
26
  "react-router": "^7.5.0",
26
27
  "syllable": "^5.0.1",
27
28
  "zustand": "^5.0.3"
@@ -17,13 +17,12 @@ import FileUploader from "./components/FileUploader"
17
17
  function App() {
18
18
  const navigate = useNavigate()
19
19
 
20
- const { formState, setFormState, setSyllabus, setAuth } = useStore(
20
+ const { formState, setFormState, setAuth, push } = useStore(
21
21
  useShallow((state) => ({
22
- // auth: state.auth,
23
22
  formState: state.formState,
24
23
  setFormState: state.setFormState,
25
- setSyllabus: state.setSyllabus,
26
24
  setAuth: state.setAuth,
25
+ push: state.push,
27
26
  }))
28
27
  )
29
28
 
@@ -74,9 +73,10 @@ function App() {
74
73
  prevInteractions: "",
75
74
  })
76
75
  const lessons = res.parsed.listOfSteps.map((lesson: any) => {
77
- return parseLesson(lesson)
76
+ return parseLesson(lesson, [])
78
77
  })
79
- setSyllabus({
78
+
79
+ push({
80
80
  lessons,
81
81
  courseInfo: {
82
82
  ...formState,
@@ -266,7 +266,13 @@ function App() {
266
266
  {formState.isCompleted ? (
267
267
  <Loader
268
268
  text="Learnpack is setting up your tutorial. It may take a moment..."
269
- icon={<img src={"creator/rigo-float.gif"} alt="rigo" className="w-20 h-20" />}
269
+ icon={
270
+ <img
271
+ src={"creator/rigo-float.gif"}
272
+ alt="rigo"
273
+ className="w-20 h-20"
274
+ />
275
+ }
270
276
  />
271
277
  ) : (
272
278
  <StepWizard
@@ -0,0 +1,22 @@
1
+ import { SVGS } from "../assets/svgs"
2
+ import { UploadedFile } from "../utils/store"
3
+
4
+ export const FileCard = ({
5
+ file,
6
+ handleRemove,
7
+ }: {
8
+ file: UploadedFile
9
+ handleRemove: () => void
10
+ }) => (
11
+ <div className="flex items-center justify-between bg-white shadow-sm p-2 rounded-lg w-[100px]">
12
+ <span title={file.name} className="text-xs text-gray-800 truncate">
13
+ {file.name}
14
+ </span>
15
+ <button
16
+ onClick={handleRemove}
17
+ className="p-1 text-red-500 hover:text-red-700 transition-colors cursor-pointer "
18
+ >
19
+ {SVGS.trash}
20
+ </button>
21
+ </div>
22
+ )
@@ -1,8 +1,9 @@
1
- import { useState } from "react"
1
+ import { useState, useRef } from "react"
2
2
  import { SVGS } from "../assets/svgs"
3
3
 
4
4
  export interface Lesson {
5
5
  id: string
6
+ uid: string
6
7
  title: string
7
8
  type: "READ" | "CODE" | "QUIZ"
8
9
  description: string
@@ -12,8 +13,8 @@ export interface Lesson {
12
13
  interface LessonItemProps {
13
14
  lesson: Lesson
14
15
  isNew: boolean
15
- onChange: (id: string, newTitle: string) => void
16
- onRemove: (id: string) => void
16
+ onChange: (uid: string, newTitle: string) => void
17
+ onRemove: () => void
17
18
  }
18
19
 
19
20
  function cleanFloatString(input: string): string {
@@ -31,12 +32,13 @@ export const LessonItem: React.FC<LessonItemProps> = ({
31
32
  onRemove,
32
33
  isNew,
33
34
  }) => {
35
+ const inputRef = useRef<HTMLInputElement>(null)
34
36
  const [isEditing, setIsEditing] = useState(false)
35
37
 
36
38
  return (
37
39
  <div
38
40
  className={`flex items-center space-x-2 relative rounded-md p-3 ${
39
- isNew ? "border border-learnpack-blue" : "border border-gray-200"
41
+ isNew ? "border border-learnpack-blue appear" : "border border-gray-200"
40
42
  }`}
41
43
  >
42
44
  {isNew && <span className="red-ball"></span>}
@@ -47,9 +49,13 @@ export const LessonItem: React.FC<LessonItemProps> = ({
47
49
 
48
50
  {isEditing ? (
49
51
  <input
50
- value={lesson.title}
51
- onChange={(e) => onChange(lesson.id, e.target.value)}
52
- onBlur={() => setIsEditing(false)}
52
+ defaultValue={lesson.title}
53
+ ref={inputRef}
54
+ onBlur={() => {
55
+ if (inputRef.current) {
56
+ onChange(lesson.uid, inputRef.current.value)
57
+ }
58
+ }}
53
59
  autoFocus
54
60
  className="flex-1 bg-white border border-gray-300 rounded-md px-2 py-1 text-sm"
55
61
  />
@@ -62,13 +68,18 @@ export const LessonItem: React.FC<LessonItemProps> = ({
62
68
  </span>
63
69
 
64
70
  <button
65
- onClick={() => setIsEditing(!isEditing)}
71
+ onClick={() => {
72
+ setIsEditing(!isEditing)
73
+ if (inputRef.current) {
74
+ onChange(lesson.uid, inputRef.current.value)
75
+ }
76
+ }}
66
77
  className="text-gray-500 hover:text-blue-500 cursor-pointer "
67
78
  >
68
79
  {SVGS.pen}
69
80
  </button>
70
81
  <button
71
- onClick={() => onRemove(lesson.id)}
82
+ onClick={() => onRemove()}
72
83
  className="text-red-500 hover:text-red-700 cursor-pointer"
73
84
  >
74
85
  {SVGS.trash}
@@ -1,10 +1,10 @@
1
- import { useEffect, useState } from "react"
1
+ import { useState } from "react"
2
2
  import { BREATHECODE_HOST } from "../utils/constants"
3
3
  import { SVGS } from "../assets/svgs"
4
4
  import toast from "react-hot-toast"
5
5
  import useStore from "../utils/store"
6
6
  import { useShallow } from "zustand/react/shallow"
7
- import { checkParams, login4Geeks, loginWithToken } from "../utils/lib"
7
+ import { login4Geeks } from "../utils/lib"
8
8
 
9
9
  export default function Login({ onFinish }: { onFinish: () => void }) {
10
10
  const [email, setEmail] = useState("")
@@ -58,24 +58,6 @@ export default function Login({ onFinish }: { onFinish: () => void }) {
58
58
  window.location.href = `${BREATHECODE_HOST}/v1/auth/github/?url=${url}`
59
59
  }
60
60
 
61
- useEffect(() => {
62
- ;(async () => {
63
- const { token } = checkParams()
64
- if (token) {
65
- const user = await loginWithToken(token)
66
- if (user) {
67
- setAuth({
68
- bcToken: token,
69
- userId: user.id,
70
- rigoToken: user.rigobot.key,
71
- user,
72
- })
73
- onFinish()
74
- }
75
- }
76
- })()
77
- }, [])
78
-
79
61
  return (
80
62
  <div
81
63
  className="fixed inset-0 bg-black/50 flex items-center justify-center z-1000"
@@ -91,7 +73,7 @@ export default function Login({ onFinish }: { onFinish: () => void }) {
91
73
  </p>
92
74
  <button
93
75
  onClick={redirectGithub}
94
- className="w-full border border-gray-300 py-2 rounded-md font-semibold flex items-center justify-center gap-2 mb-4"
76
+ className="w-full border border-gray-300 py-2 rounded-md font-semibold flex items-center justify-center gap-2 mb-4 cursor-pointer"
95
77
  >
96
78
  {SVGS.github} LOGIN WITH GITHUB
97
79
  </button>
@@ -119,14 +101,14 @@ export default function Login({ onFinish }: { onFinish: () => void }) {
119
101
  <div className="flex gap-2 mt-4">
120
102
  <button
121
103
  type="submit"
122
- className="flex-1 bg-sky-500 text-white py-2 rounded-md font-semibold"
104
+ className="flex-1 bg-sky-500 text-white py-2 rounded-md font-semibold cursor-pointer"
123
105
  >
124
106
  {isLoading ? "Logging in..." : "Log in"}
125
107
  </button>
126
108
  <button
127
109
  type="button"
128
110
  onClick={() => setShowForm(false)}
129
- className="flex-1 border border-sky-500 text-sky-600 py-2 rounded-md font-semibold"
111
+ className="flex-1 border border-sky-500 text-sky-600 py-2 rounded-md font-semibold cursor-pointer"
130
112
  >
131
113
  Skip
132
114
  </button>
@@ -144,7 +126,7 @@ export default function Login({ onFinish }: { onFinish: () => void }) {
144
126
  ) : (
145
127
  <button
146
128
  onClick={() => setShowForm(true)}
147
- className="w-full bg-blue-600 text-white py-2 rounded-md font-semibold"
129
+ className="w-full bg-blue-600 text-white py-2 rounded-md font-semibold cursor-pointer"
148
130
  >
149
131
  Login with Email
150
132
  </button>
@@ -0,0 +1,11 @@
1
+ import Markdown from "react-markdown"
2
+
3
+ export const MarkdownRenderer: React.FC<{
4
+ content: string
5
+ }> = ({ content }) => {
6
+ return (
7
+ <div className="markdown-renderer">
8
+ <Markdown>{content}</Markdown>
9
+ </div>
10
+ )
11
+ }
@@ -2,6 +2,7 @@ import { RigoLoader } from "./RigoLoader"
2
2
 
3
3
  import { SVGS } from "../assets/svgs"
4
4
  import useStore from "../utils/store"
5
+ import { MarkdownRenderer } from "./MarkdownRenderer"
5
6
 
6
7
  export type TMessage = {
7
8
  type: "user" | "assistant"
@@ -14,7 +15,6 @@ export const Message: React.FC<TMessage> = ({ type, content }) => {
14
15
 
15
16
  const isLoading = isAI && !content
16
17
 
17
- console.log("user", user)
18
18
  return isLoading ? (
19
19
  <RigoLoader text="Thinking..." svg={<img src="rigo-float.gif" />} />
20
20
  ) : (
@@ -32,7 +32,9 @@ export const Message: React.FC<TMessage> = ({ type, content }) => {
32
32
  ) : (
33
33
  <span className="mt-1">{SVGS.user}</span>
34
34
  )}
35
- <p className="text-sm leading-relaxed">{content}</p>
35
+ <div className="w-full break-words overflow-x-auto ">
36
+ <MarkdownRenderer content={content} />
37
+ </div>
36
38
  </div>
37
39
  )
38
40
  }
@@ -5,6 +5,8 @@ import { SVGS } from "../../assets/svgs"
5
5
  import { TMessage } from "../Message"
6
6
  import Loader from "../Loader"
7
7
  import { motion, AnimatePresence } from "framer-motion"
8
+ import { randomUUID } from "../../utils/creatorUtils"
9
+ import toast from "react-hot-toast"
8
10
 
9
11
  const ContentIndexHeader = ({
10
12
  messages,
@@ -28,7 +30,7 @@ const ContentIndexHeader = ({
28
30
  : "Based on your input, here is the new syllabus, updates are highlighted in yellow"
29
31
 
30
32
  return (
31
- <div className="mt-2">
33
+ <div className="mt-2 ">
32
34
  <AnimatePresence mode="wait">
33
35
  <motion.h2
34
36
  key={headerText}
@@ -36,7 +38,7 @@ const ContentIndexHeader = ({
36
38
  animate={{ opacity: 1, y: 0 }}
37
39
  exit={{ opacity: 0, y: 10 }}
38
40
  transition={{ duration: 0.2 }}
39
- className="text-lg font-semibold "
41
+ className="text-xs font-semibold sm:text-base md:text-lg"
40
42
  >
41
43
  {headerText}
42
44
  </motion.h2>
@@ -49,7 +51,7 @@ const ContentIndexHeader = ({
49
51
  animate={{ opacity: 1 }}
50
52
  exit={{ opacity: 0 }}
51
53
  transition={{ duration: 0.2, delay: 0.1 }}
52
- className="text-sm text-gray-600 mt-1 mb-5"
54
+ className="text-xs sm:text-sm text-gray-600 mt-1 mb-5"
53
55
  >
54
56
  {subText}
55
57
  </motion.p>
@@ -58,33 +60,30 @@ const ContentIndexHeader = ({
58
60
  )
59
61
  }
60
62
 
61
- export default ContentIndexHeader
62
-
63
63
  export const GenerateButton = ({
64
64
  handleSubmit,
65
- handleUndo,
66
- hasChanges,
67
65
  }: {
68
66
  handleSubmit: () => void
69
- handleUndo: () => void
70
- hasChanges: boolean
71
67
  }) => {
68
+ const history = useStore((state) => state.history)
69
+ const undo = useStore((state) => state.undo)
72
70
  return (
73
- <div className="flex justify-end mt-6">
74
- {hasChanges && (
71
+ <div className="flex flex-col space-y-2 sm:flex-row sm:space-y-0 sm:space-x-4 justify-end mt-6">
72
+ {history.length > 1 && (
75
73
  <button
76
- className="text-gray-500 mr-4 cursor-pointer bg-gray-200 rounded px-4 py-2 hover:bg-gray-300 flex items-center gap-2"
77
- onClick={() => handleUndo()}
74
+ onClick={() => {
75
+ undo()
76
+ toast.success("Changes reverted!")
77
+ }}
78
+ className="w-full sm:w-auto text-gray-500 bg-gray-200 rounded px-4 py-2 hover:bg-gray-300 flex items-center justify-center gap-2 cursor-pointer"
78
79
  >
79
80
  {SVGS.undo}
80
81
  Revert changes
81
82
  </button>
82
83
  )}
83
84
  <button
84
- onClick={async () => {
85
- handleSubmit()
86
- }}
87
- className="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 cursor-pointer flex items-center gap-2"
85
+ onClick={handleSubmit}
86
+ className="w-full sm:w-auto bg-blue-600 text-white rounded px-4 py-2 hover:bg-blue-700 flex items-center justify-center gap-2 cursor-pointer"
88
87
  >
89
88
  <span>I'm ready. Create the course for me!</span>
90
89
  {SVGS.rigoSoftBlue}
@@ -93,52 +92,36 @@ export const GenerateButton = ({
93
92
  )
94
93
  }
95
94
 
96
- function hasChanges(current: Lesson[], previous: Lesson[]): boolean {
97
- return current.some(
98
- (c) =>
99
- !previous.some(
100
- (p) =>
101
- p.id === c.id &&
102
- p.title === c.title &&
103
- p.type === c.type &&
104
- p.duration === c.duration &&
105
- p.description === c.description
106
- )
107
- )
108
- }
109
-
110
95
  export const ContentIndex = ({
111
- prevLessons,
112
96
  handleSubmit,
113
97
  messages,
114
98
  isThinking,
115
- handleUndo,
116
99
  }: {
117
- prevLessons?: Lesson[]
118
100
  handleSubmit: () => void
119
101
  messages: TMessage[]
120
102
  isThinking: boolean
121
- handleUndo: () => void
122
103
  }) => {
123
- const syllabus = useStore((state) => state.syllabus)
124
- const setSyllabus = useStore((state) => state.setSyllabus)
104
+ const history = useStore((state) => state.history)
105
+ const push = useStore((state) => state.push)
125
106
  const containerRef = useRef<HTMLDivElement>(null)
126
107
  const [showScrollHint, setShowScrollHint] = useState(false)
127
108
 
109
+ const syllabus = history[history.length - 1]
110
+
111
+ const previousSyllabus = history[history.length - 2] || null
112
+
128
113
  const handleRemove = (lesson: Lesson) => {
129
- setSyllabus({
114
+ push({
130
115
  ...syllabus,
131
- lessons: syllabus.lessons.filter(
132
- (l) => l.id !== lesson.id && l.title !== lesson.title
133
- ),
116
+ lessons: syllabus.lessons.filter((l) => l.uid !== lesson.uid),
134
117
  })
135
118
  }
136
119
 
137
- const handleChange = (id: string, newTitle: string) => {
138
- setSyllabus({
120
+ const handleChange = (uid: string, newTitle: string) => {
121
+ push({
139
122
  ...syllabus,
140
123
  lessons: syllabus.lessons.map((lesson) =>
141
- lesson.id === id ? { ...lesson, title: newTitle } : lesson
124
+ lesson.uid === uid ? { ...lesson, title: newTitle } : lesson
142
125
  ),
143
126
  })
144
127
  }
@@ -147,14 +130,15 @@ export const ContentIndex = ({
147
130
  const newLesson: Lesson = {
148
131
  id: (parseFloat(id) + 0.1).toFixed(1),
149
132
  title: "Hello World",
133
+ uid: randomUUID(),
150
134
  type: "READ",
151
135
  duration: 2,
152
136
  description: "Hello World",
153
137
  }
154
138
  const updated = [...syllabus.lessons]
155
139
  updated.splice(index + 1, 0, newLesson)
156
-
157
- setSyllabus({
140
+ push({
141
+ ...syllabus,
158
142
  lessons: updated,
159
143
  })
160
144
  }
@@ -193,31 +177,30 @@ export const ContentIndex = ({
193
177
  }
194
178
 
195
179
  return (
196
- <div className="relative ">
180
+ <div className="relative w-full p-2 sm:p-4 md:p-6 lg:p-8 space-y-6">
197
181
  <ContentIndexHeader messages={messages} syllabus={syllabus} />
198
182
  <div
199
183
  ref={containerRef}
200
- className=" space-y-3 overflow-y-auto max-h-[75vh] pr-2 scrollbar-hide relative pb-5"
184
+ className="space-y-3 overflow-y-auto max-h-[78vh] pr-2 scrollbar-hide relative pb-20"
201
185
  >
202
186
  {isThinking ? (
203
- <Loader text="Thinking..." minheight="min-h-[70vh]" />
187
+ <Loader text="Thinking..." minheight="min-h-[69vh]" />
204
188
  ) : (
205
189
  <>
206
190
  {syllabus.lessons.map((lesson, index) => (
207
- <div key={lesson.id}>
191
+ <div key={lesson.uid + index}>
208
192
  <LessonItem
209
- key={lesson.id + index + lesson.title}
193
+ key={lesson.uid}
210
194
  lesson={lesson}
211
195
  onChange={handleChange}
212
196
  onRemove={() => handleRemove(lesson)}
213
197
  isNew={Boolean(
214
- prevLessons &&
215
- prevLessons.length > 0 &&
216
- !prevLessons.some(
217
- (l) =>
218
- l.id === lesson.id &&
219
- l.title === lesson.title &&
220
- l.type === lesson.type
198
+ previousSyllabus &&
199
+ previousSyllabus &&
200
+ previousSyllabus.lessons &&
201
+ previousSyllabus.lessons.length > 0 &&
202
+ !previousSyllabus.lessons.some(
203
+ (l) => l.uid === lesson.uid
221
204
  )
222
205
  )}
223
206
  />
@@ -233,11 +216,9 @@ export const ContentIndex = ({
233
216
  </div>
234
217
  </div>
235
218
  ))}
236
- <GenerateButton
237
- handleSubmit={handleSubmit}
238
- handleUndo={handleUndo}
239
- hasChanges={hasChanges(syllabus.lessons, prevLessons || [])}
240
- />
219
+ {syllabus.lessons.length > 0 && (
220
+ <GenerateButton handleSubmit={handleSubmit} />
221
+ )}
241
222
  </>
242
223
  )}
243
224
  </div>
@@ -245,7 +226,7 @@ export const ContentIndex = ({
245
226
  {showScrollHint && !isThinking && (
246
227
  <div className="pointer-events-none relative">
247
228
  <div className="absolute bottom-0 left-0 w-full h-60 bg-gradient-to-t from-white to-transparent z-10" />
248
- <div className="absolute bottom-3 left-0 w-full flex justify-center z-20">
229
+ <div className="absolute bottom-10 left-0 w-full flex justify-center z-20">
249
230
  <button
250
231
  style={{ color: "#0084FF" }}
251
232
  onClick={() => scrollToBottom("bottom")}
@@ -4,6 +4,7 @@ import { TMessage } from "../Message"
4
4
  import FileUploader from "../FileUploader"
5
5
  import { SVGS } from "../../assets/svgs"
6
6
  import { Message } from "../Message"
7
+ import { FileCard } from "../FileCard"
7
8
 
8
9
  export const Sidebar = ({
9
10
  messages,
@@ -15,8 +16,8 @@ export const Sidebar = ({
15
16
  handleSubmit: () => void
16
17
  }) => {
17
18
  const inputRef = useRef<HTMLTextAreaElement>(null)
18
- const syllabus = useStore((state) => state.syllabus)
19
- const setSyllabus = useStore((state) => state.setSyllabus)
19
+ const uploadedFiles = useStore((state) => state.uploadedFiles)
20
+ const setUploadedFiles = useStore((state) => state.setUploadedFiles)
20
21
 
21
22
  const [isOpen, setIsOpen] = useState(false)
22
23
 
@@ -32,7 +33,7 @@ export const Sidebar = ({
32
33
  )}
33
34
 
34
35
  <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
+ 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 border-C8DBFC overflow-y-auto scrollbar-hide p-6 transition-transform duration-300 ease-in-out lg:relative lg:transform-none lg:w-1/3 ${
36
37
  isOpen ? "translate-x-0" : "-translate-x-full lg:translate-x-0"
37
38
  }`}
38
39
  >
@@ -56,10 +57,25 @@ export const Sidebar = ({
56
57
  </div>
57
58
 
58
59
  <div className="relative w-full rounded-md bg-white text-gray-700 h-24">
60
+ {uploadedFiles?.length > 0 && (
61
+ <div className="absolute bottom-[100%] flex gap-2 w-full pb-1 rounded-md overflow-x-auto scrollbar-hide">
62
+ {uploadedFiles.map((file, index) => (
63
+ <FileCard
64
+ key={index}
65
+ file={file}
66
+ handleRemove={() => {
67
+ const newFiles = [...uploadedFiles]
68
+ newFiles.splice(index, 1)
69
+ setUploadedFiles(newFiles)
70
+ }}
71
+ />
72
+ ))}
73
+ </div>
74
+ )}
59
75
  <textarea
60
76
  ref={inputRef}
61
77
  style={{ resize: "none" }}
62
- className="w-full h-full p-2"
78
+ className="w-full h-full p-2 outline-blue rounded"
63
79
  placeholder="How can Learnpack help you?"
64
80
  autoFocus
65
81
  onKeyUp={(e) => {
@@ -79,23 +95,9 @@ export const Sidebar = ({
79
95
  />
80
96
  <div className="absolute bottom-2 right-2 flex gap-1 items-center">
81
97
  <div className="relative inline-block">
82
- {syllabus.uploadedFiles?.length > 0 && (
83
- <span
84
- 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"
85
- title="Remove uploaded files"
86
- onClick={() => {
87
- setSyllabus({ ...syllabus, uploadedFiles: [] })
88
- }}
89
- >
90
- {syllabus.uploadedFiles?.length}
91
- </span>
92
- )}
93
98
  <FileUploader
94
99
  onResult={(res) => {
95
- setSyllabus({
96
- ...syllabus,
97
- uploadedFiles: [...syllabus.uploadedFiles, ...res],
98
- })
100
+ setUploadedFiles([...(uploadedFiles || []), ...res])
99
101
  }}
100
102
  />
101
103
  </div>