@learnpack/learnpack 5.0.70 → 5.0.72
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/commands/init.js +1 -1
- package/lib/commands/serve.js +60 -4
- package/lib/creatorDist/assets/{index-Dqo9u2iR.css → index-BJ2JJzVC.css} +53 -26
- package/lib/creatorDist/assets/{index-Chx6V3zd.js → index-CKBeex0S.js} +35878 -29623
- package/lib/creatorDist/index.html +2 -2
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/src/commands/init.ts +1 -1
- package/src/commands/serve.ts +70 -6
- package/src/creator/package-lock.json +49 -0
- package/src/creator/package.json +1 -0
- package/src/creator/src/App.tsx +28 -21
- package/src/creator/src/assets/svgs.tsx +1 -1
- package/src/creator/src/components/ConsumablesManager.tsx +12 -2
- package/src/creator/src/components/LessonItem.tsx +3 -2
- package/src/creator/src/components/Loader.tsx +5 -1
- package/src/creator/src/components/Login.tsx +58 -151
- package/src/creator/src/components/Message.tsx +11 -1
- package/src/creator/src/components/Redirector.tsx +12 -0
- package/src/creator/src/components/syllabus/ContentIndex.tsx +88 -58
- package/src/creator/src/components/syllabus/Sidebar.tsx +3 -12
- package/src/creator/src/components/syllabus/SyllabusEditor.tsx +63 -7
- package/src/creator/src/index.css +15 -0
- package/src/creator/src/main.tsx +0 -1
- package/src/creator/src/utils/creatorUtils.ts +33 -3
- package/src/creator/src/utils/lib.ts +156 -2
- package/src/creator/src/utils/rigo.ts +3 -3
- package/src/creator/src/utils/store.ts +2 -1
- package/src/creatorDist/assets/{index-Dqo9u2iR.css → index-BJ2JJzVC.css} +53 -26
- package/src/creatorDist/assets/{index-Chx6V3zd.js → index-CKBeex0S.js} +35878 -29623
- package/src/creatorDist/index.html +2 -2
- package/src/ui/_app/app.css +1 -1
- package/src/ui/_app/app.js +529 -529
- package/src/ui/app.tar.gz +0 -0
@@ -1,131 +1,45 @@
|
|
1
1
|
import { useEffect, useState } from "react"
|
2
|
-
import { BREATHECODE_HOST
|
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 } from "../utils/lib"
|
8
|
-
|
9
|
-
type LoginInfo = {
|
10
|
-
email: string
|
11
|
-
password: string
|
12
|
-
}
|
13
|
-
|
14
|
-
const getRigobotJSON = async (breathecodeToken: string) => {
|
15
|
-
const rigoUrl = `${RIGOBOT_HOST}/v1/auth/me/token?breathecode_token=${breathecodeToken}`
|
16
|
-
const rigoResp = await fetch(rigoUrl)
|
17
|
-
if (!rigoResp.ok) {
|
18
|
-
throw new Error("Unable to obtain Rigobot token")
|
19
|
-
}
|
20
|
-
const rigobotJson = await rigoResp.json()
|
21
|
-
return rigobotJson
|
22
|
-
}
|
23
|
-
const validateUser = async (breathecodeToken: string) => {
|
24
|
-
const config = {
|
25
|
-
method: "GET",
|
26
|
-
headers: {
|
27
|
-
"Content-Type": "application/json",
|
28
|
-
Authorization: `Token ${breathecodeToken}`,
|
29
|
-
},
|
30
|
-
}
|
31
|
-
|
32
|
-
const res = await fetch(`${BREATHECODE_HOST}/v1/auth/user/me`, config)
|
33
|
-
if (!res.ok) {
|
34
|
-
console.log("ERROR", res)
|
35
|
-
return null
|
36
|
-
}
|
37
|
-
const json = await res.json()
|
38
|
-
|
39
|
-
if ("roles" in json) {
|
40
|
-
delete json.roles
|
41
|
-
}
|
42
|
-
if ("permissions" in json) {
|
43
|
-
delete json.permissions
|
44
|
-
}
|
45
|
-
if ("settings" in json) {
|
46
|
-
delete json.settings
|
47
|
-
}
|
48
|
-
|
49
|
-
return json
|
50
|
-
}
|
51
|
-
|
52
|
-
const login4Geeks = async (loginInfo: LoginInfo) => {
|
53
|
-
const url = `${BREATHECODE_HOST}/v1/auth/login/`
|
54
|
-
|
55
|
-
const res = await fetch(url, {
|
56
|
-
body: JSON.stringify(loginInfo),
|
57
|
-
method: "post",
|
58
|
-
headers: {
|
59
|
-
"Content-Type": "application/json",
|
60
|
-
},
|
61
|
-
})
|
62
|
-
|
63
|
-
if (!res.ok) {
|
64
|
-
throw Error("Unable to login with provided credentials")
|
65
|
-
}
|
66
|
-
|
67
|
-
const json = await res.json()
|
68
|
-
|
69
|
-
const rigoJson = await getRigobotJSON(json.token)
|
70
|
-
|
71
|
-
const user = await validateUser(json.token)
|
72
|
-
const returns = { ...json, rigobot: { ...rigoJson }, user }
|
73
|
-
|
74
|
-
return returns
|
75
|
-
}
|
76
|
-
|
77
|
-
export const loginWithToken = async (token: string) => {
|
78
|
-
const rigoJson = await getRigobotJSON(token)
|
79
|
-
|
80
|
-
const user = await validateUser(token)
|
81
|
-
|
82
|
-
const returns = { rigobot: { ...rigoJson }, ...user }
|
83
|
-
|
84
|
-
return returns
|
85
|
-
}
|
7
|
+
import { checkParams, login4Geeks, loginWithToken } from "../utils/lib"
|
86
8
|
|
87
9
|
export default function Login({ onFinish }: { onFinish: () => void }) {
|
88
10
|
const [email, setEmail] = useState("")
|
89
11
|
const [password, setPassword] = useState("")
|
90
12
|
const [isLoading, setIsLoading] = useState(false)
|
91
|
-
|
92
13
|
const [showForm, setShowForm] = useState(false)
|
93
14
|
|
94
15
|
const { setAuth } = useStore(
|
95
|
-
useShallow((state) => ({
|
96
|
-
setAuth: state.setAuth,
|
97
|
-
}))
|
16
|
+
useShallow((state) => ({ setAuth: state.setAuth }))
|
98
17
|
)
|
99
18
|
|
100
|
-
const login = async (e:
|
101
|
-
setIsLoading(true)
|
102
|
-
|
103
|
-
const tid = toast.loading("Logging in...")
|
19
|
+
const login = async (e: React.FormEvent) => {
|
104
20
|
e.preventDefault()
|
21
|
+
setIsLoading(true)
|
22
|
+
const tid = toast.loading("Logging in…")
|
105
23
|
|
106
24
|
if (!email || !password) {
|
107
|
-
// toast.error(t("please-fill-all-fields"))
|
108
25
|
setIsLoading(false)
|
26
|
+
toast.error("Please fill all fields", { id: tid })
|
109
27
|
return
|
110
28
|
}
|
111
29
|
|
112
|
-
const
|
113
|
-
|
114
|
-
password: password,
|
115
|
-
})
|
116
|
-
|
117
|
-
if (!isLoggedId) {
|
30
|
+
const resp = await login4Geeks({ email, password })
|
31
|
+
if (!resp) {
|
118
32
|
setIsLoading(false)
|
119
|
-
toast.error("
|
33
|
+
toast.error("Invalid credentials", { id: tid })
|
120
34
|
return
|
121
35
|
}
|
122
36
|
|
123
37
|
toast.success("Logged in successfully", { id: tid })
|
124
|
-
|
125
38
|
setAuth({
|
126
|
-
bcToken:
|
127
|
-
userId:
|
128
|
-
rigoToken:
|
39
|
+
bcToken: resp.token,
|
40
|
+
userId: resp.user.id,
|
41
|
+
rigoToken: resp.rigobot.key,
|
42
|
+
user: resp.user,
|
129
43
|
})
|
130
44
|
setIsLoading(false)
|
131
45
|
onFinish()
|
@@ -136,66 +50,46 @@ export default function Login({ onFinish }: { onFinish: () => void }) {
|
|
136
50
|
}
|
137
51
|
|
138
52
|
function getCurrentUrlWithQueryParams() {
|
139
|
-
// let currentUrl = window.location.origin + window.location.pathname
|
140
|
-
|
141
53
|
return window.location.href
|
142
54
|
}
|
143
55
|
|
144
56
|
const redirectGithub = () => {
|
145
|
-
|
146
|
-
|
147
|
-
window.location.href = `${BREATHECODE_HOST}/v1/auth/github/?url=${stringToBase64(
|
148
|
-
currentUrl
|
149
|
-
)}`
|
57
|
+
const url = stringToBase64(getCurrentUrlWithQueryParams())
|
58
|
+
window.location.href = `${BREATHECODE_HOST}/v1/auth/github/?url=${url}`
|
150
59
|
}
|
151
60
|
|
152
61
|
useEffect(() => {
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
})
|
167
|
-
onFinish()
|
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
|
+
}
|
168
75
|
}
|
169
|
-
}
|
170
|
-
}
|
76
|
+
})()
|
77
|
+
}, [])
|
171
78
|
|
172
79
|
return (
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
className="text-blue-600 font-medium"
|
182
|
-
>
|
183
|
-
Register here.
|
184
|
-
</a>
|
185
|
-
</div>
|
186
|
-
</div>
|
187
|
-
|
188
|
-
<p className="text-gray-600 mb-6">
|
189
|
-
Log in to 4Geeks to get performance statistics, access to our AI
|
190
|
-
mentor, and many other benefits
|
191
|
-
</p>
|
192
|
-
|
80
|
+
<div
|
81
|
+
className="fixed inset-0 bg-black/50 flex items-center justify-center z-1000"
|
82
|
+
onClick={onFinish}
|
83
|
+
>
|
84
|
+
<div
|
85
|
+
className="bg-white p-8 rounded-xl shadow-md max-w-sm w-full"
|
86
|
+
onClick={(e) => e.stopPropagation()}
|
87
|
+
>
|
193
88
|
<button
|
194
89
|
onClick={redirectGithub}
|
195
|
-
className="w-full border border-gray-300 py-2 rounded-md font-semibold flex items-center justify-center gap-2 mb-4
|
90
|
+
className="w-full border border-gray-300 py-2 rounded-md font-semibold flex items-center justify-center gap-2 mb-4"
|
196
91
|
>
|
197
|
-
{SVGS.github}
|
198
|
-
LOGIN WITH GITHUB
|
92
|
+
{SVGS.github} LOGIN WITH GITHUB
|
199
93
|
</button>
|
200
94
|
|
201
95
|
<div className="flex items-center mb-4">
|
@@ -221,14 +115,14 @@ export default function Login({ onFinish }: { onFinish: () => void }) {
|
|
221
115
|
<div className="flex gap-2 mt-4">
|
222
116
|
<button
|
223
117
|
type="submit"
|
224
|
-
className="flex-1 bg-sky-500 text-white py-2 rounded-md font-semibold
|
118
|
+
className="flex-1 bg-sky-500 text-white py-2 rounded-md font-semibold"
|
225
119
|
>
|
226
120
|
{isLoading ? "Logging in..." : "Log in"}
|
227
121
|
</button>
|
228
122
|
<button
|
229
123
|
type="button"
|
230
124
|
onClick={() => setShowForm(false)}
|
231
|
-
className="flex-1 border border-sky-500 text-sky-600 py-2 rounded-md font-semibold
|
125
|
+
className="flex-1 border border-sky-500 text-sky-600 py-2 rounded-md font-semibold"
|
232
126
|
>
|
233
127
|
Skip
|
234
128
|
</button>
|
@@ -246,12 +140,25 @@ export default function Login({ onFinish }: { onFinish: () => void }) {
|
|
246
140
|
) : (
|
247
141
|
<button
|
248
142
|
onClick={() => setShowForm(true)}
|
249
|
-
className="w-full bg-blue-600 text-white py-2 rounded-md font-semibold
|
143
|
+
className="w-full bg-blue-600 text-white py-2 rounded-md font-semibold"
|
250
144
|
>
|
251
145
|
Login with Email
|
252
146
|
</button>
|
253
147
|
)}
|
148
|
+
|
149
|
+
<div className="flex justify-between items-center mt-4">
|
150
|
+
<div className="bg-blue-50 text-sm p-2 rounded text-left">
|
151
|
+
<p className="text-gray-700 m-0">You don't have an account?</p>
|
152
|
+
<a
|
153
|
+
href="https://4geeks.com/checkout?plan=4geeks-creator"
|
154
|
+
target="_blank"
|
155
|
+
className="text-blue-600 font-medium"
|
156
|
+
>
|
157
|
+
Register here.
|
158
|
+
</a>
|
159
|
+
</div>
|
160
|
+
</div>
|
254
161
|
</div>
|
255
|
-
|
162
|
+
</div>
|
256
163
|
)
|
257
164
|
}
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import { RigoLoader } from "./RigoLoader"
|
2
2
|
|
3
3
|
import { SVGS } from "../assets/svgs"
|
4
|
+
import useStore from "../utils/store"
|
4
5
|
|
5
6
|
export type TMessage = {
|
6
7
|
type: "user" | "assistant"
|
@@ -8,9 +9,12 @@ export type TMessage = {
|
|
8
9
|
}
|
9
10
|
|
10
11
|
export const Message: React.FC<TMessage> = ({ type, content }) => {
|
12
|
+
const user = useStore((state) => state.auth.user)
|
11
13
|
const isAI = type === "assistant"
|
12
14
|
|
13
15
|
const isLoading = isAI && !content
|
16
|
+
|
17
|
+
console.log("user", user)
|
14
18
|
return isLoading ? (
|
15
19
|
<RigoLoader text="Thinking..." svg={<img src="rigo-float.gif" />} />
|
16
20
|
) : (
|
@@ -21,7 +25,13 @@ export const Message: React.FC<TMessage> = ({ type, content }) => {
|
|
21
25
|
: "bg-blue-50 border-blue-200 text-blue-900"
|
22
26
|
}`}
|
23
27
|
>
|
24
|
-
|
28
|
+
{isAI ? (
|
29
|
+
<span className="mt-1">{SVGS.rigoSoftBlue}</span>
|
30
|
+
) : user?.profile?.avatar_url ? (
|
31
|
+
<img src={user?.profile?.avatar_url} className="w-6 h-6 rounded-full" />
|
32
|
+
) : (
|
33
|
+
<span className="mt-1">{SVGS.user}</span>
|
34
|
+
)}
|
25
35
|
<p className="text-sm leading-relaxed">{content}</p>
|
26
36
|
</div>
|
27
37
|
)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { useEffect } from "react"
|
2
|
+
import toast from "react-hot-toast"
|
3
|
+
export const Redirector = ({ to }: { to: string }) => {
|
4
|
+
useEffect(() => {
|
5
|
+
window.location.href = to
|
6
|
+
window.location.reload()
|
7
|
+
toast.success("Redirecting to " + to)
|
8
|
+
console.log("Redirecting to " + to)
|
9
|
+
}, [])
|
10
|
+
|
11
|
+
return <h1>Redirecting to {to}...</h1>
|
12
|
+
}
|
@@ -3,6 +3,8 @@ import useStore, { Syllabus } from "../../utils/store"
|
|
3
3
|
import { Lesson, LessonItem } from "../LessonItem"
|
4
4
|
import { SVGS } from "../../assets/svgs"
|
5
5
|
import { TMessage } from "../Message"
|
6
|
+
import Loader from "../Loader"
|
7
|
+
import { motion, AnimatePresence } from "framer-motion"
|
6
8
|
|
7
9
|
const ContentIndexHeader = ({
|
8
10
|
messages,
|
@@ -11,22 +13,48 @@ const ContentIndexHeader = ({
|
|
11
13
|
messages: TMessage[]
|
12
14
|
syllabus: Syllabus
|
13
15
|
}) => {
|
16
|
+
const isFirst =
|
17
|
+
messages.filter((m) => m.type === "assistant" && m.content.length > 0)
|
18
|
+
.length === 2
|
19
|
+
|
20
|
+
const headerText = isFirst
|
21
|
+
? "I've created a detailed structure for your course."
|
22
|
+
: "I've updated the structure based on your feedback."
|
23
|
+
|
24
|
+
const subText = isFirst
|
25
|
+
? `It includes a mix of reading, coding exercises, and quizzes. Give
|
26
|
+
it a look and let me know if it aligns with your expectations or if
|
27
|
+
there are any changes you'd like to make.`
|
28
|
+
: "Based on your input, here is the new syllabus, updates are highlighted in yellow"
|
29
|
+
|
14
30
|
return (
|
15
|
-
<div>
|
16
|
-
<
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
:
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
31
|
+
<div className="mt-2">
|
32
|
+
<AnimatePresence mode="wait">
|
33
|
+
<motion.h2
|
34
|
+
key={headerText}
|
35
|
+
initial={{ opacity: 0, y: -10 }}
|
36
|
+
animate={{ opacity: 1, y: 0 }}
|
37
|
+
exit={{ opacity: 0, y: 10 }}
|
38
|
+
transition={{ duration: 0.2 }}
|
39
|
+
className="text-lg font-semibold"
|
40
|
+
>
|
41
|
+
{headerText}
|
42
|
+
</motion.h2>
|
43
|
+
</AnimatePresence>
|
44
|
+
|
45
|
+
<AnimatePresence mode="wait">
|
46
|
+
<motion.p
|
47
|
+
key={subText}
|
48
|
+
initial={{ opacity: 0 }}
|
49
|
+
animate={{ opacity: 1 }}
|
50
|
+
exit={{ opacity: 0 }}
|
51
|
+
transition={{ duration: 0.2, delay: 0.1 }}
|
52
|
+
className="text-sm text-gray-600 mt-1"
|
53
|
+
>
|
54
|
+
{subText}
|
55
|
+
</motion.p>
|
56
|
+
</AnimatePresence>
|
57
|
+
|
30
58
|
<h3 className="text-sm text-gray-600 mt-2 font-bold">
|
31
59
|
{syllabus.courseInfo.title}
|
32
60
|
</h3>
|
@@ -34,6 +62,8 @@ const ContentIndexHeader = ({
|
|
34
62
|
)
|
35
63
|
}
|
36
64
|
|
65
|
+
export default ContentIndexHeader
|
66
|
+
|
37
67
|
export const GenerateButton = ({
|
38
68
|
handleSubmit,
|
39
69
|
}: {
|
@@ -57,10 +87,12 @@ export const ContentIndex = ({
|
|
57
87
|
prevLessons,
|
58
88
|
handleSubmit,
|
59
89
|
messages,
|
90
|
+
isThinking,
|
60
91
|
}: {
|
61
92
|
prevLessons?: Lesson[]
|
62
93
|
handleSubmit: () => void
|
63
94
|
messages: TMessage[]
|
95
|
+
isThinking: boolean
|
64
96
|
}) => {
|
65
97
|
const syllabus = useStore((state) => state.syllabus)
|
66
98
|
const setSyllabus = useStore((state) => state.setSyllabus)
|
@@ -139,61 +171,59 @@ export const ContentIndex = ({
|
|
139
171
|
<ContentIndexHeader messages={messages} syllabus={syllabus} />
|
140
172
|
<div
|
141
173
|
ref={containerRef}
|
142
|
-
className=" space-y-3 overflow-y-auto max-h-[
|
174
|
+
className=" space-y-3 overflow-y-auto max-h-[80vh] pr-2 scrollbar-hide relative pb-5"
|
143
175
|
>
|
144
|
-
{
|
145
|
-
<
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
176
|
+
{isThinking ? (
|
177
|
+
<Loader text="Thinking..." minheight="min-h-[70vh]" />
|
178
|
+
) : (
|
179
|
+
<>
|
180
|
+
{syllabus.lessons.map((lesson, index) => (
|
181
|
+
<div key={lesson.id}>
|
182
|
+
<LessonItem
|
183
|
+
key={lesson.id + index + lesson.title}
|
184
|
+
lesson={lesson}
|
185
|
+
onChange={handleChange}
|
186
|
+
onRemove={() => handleRemove(lesson)}
|
187
|
+
isNew={Boolean(
|
188
|
+
prevLessons &&
|
189
|
+
prevLessons.length > 0 &&
|
190
|
+
!prevLessons.some(
|
191
|
+
(l) =>
|
192
|
+
l.id === lesson.id &&
|
193
|
+
l.title === lesson.title &&
|
194
|
+
l.type === lesson.type
|
195
|
+
)
|
196
|
+
)}
|
197
|
+
/>
|
198
|
+
<div className="relative h-6">
|
199
|
+
<div className="absolute left-1/2 -translate-x-1/2 -top-3">
|
200
|
+
<button
|
201
|
+
onClick={() => addLessonAfter(index, lesson.id)}
|
202
|
+
className="w-6 h-6 flex items-center justify-center bg-blue-100 text-blue-600 rounded hover:bg-blue-200 shadow-sm text-sm font-semibold cursor-pointer"
|
203
|
+
>
|
204
|
+
+
|
205
|
+
</button>
|
206
|
+
</div>
|
207
|
+
</div>
|
170
208
|
</div>
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
209
|
+
))}
|
210
|
+
<GenerateButton handleSubmit={handleSubmit} />
|
211
|
+
</>
|
212
|
+
)}
|
175
213
|
</div>
|
176
214
|
|
177
|
-
{showScrollHint && (
|
215
|
+
{showScrollHint && !isThinking && (
|
178
216
|
<div className="pointer-events-none relative">
|
179
|
-
<div className="absolute bottom-0 left-0 w-full h-
|
217
|
+
<div className="absolute bottom-0 left-0 w-full h-60 bg-gradient-to-t from-white to-transparent z-10" />
|
180
218
|
<div className="absolute bottom-3 left-0 w-full flex justify-center z-20">
|
181
219
|
<button
|
182
220
|
style={{ color: "#0084FF" }}
|
183
|
-
onClick={() => scrollToBottom("
|
184
|
-
className="px-4 py-1 bg-white text-sm rounded
|
221
|
+
onClick={() => scrollToBottom("bottom")}
|
222
|
+
className="px-4 py-1 bg-white text-sm rounded hover:bg-blue-50 cursor-pointer pointer-events-auto font-bold flex items-center gap-2"
|
185
223
|
>
|
186
224
|
Continue scrolling
|
187
225
|
{SVGS.downArrow}
|
188
226
|
</button>
|
189
|
-
<button
|
190
|
-
style={{ color: "#0084FF" }}
|
191
|
-
onClick={() => scrollToBottom("bottom")}
|
192
|
-
className="px-4 py-1 bg-white text-sm rounded shadow hover:bg-blue-50 cursor-pointer pointer-events-auto font-bold flex items-center gap-2"
|
193
|
-
>
|
194
|
-
Scroll to bottom
|
195
|
-
{SVGS.bottom}
|
196
|
-
</button>
|
197
227
|
</div>
|
198
228
|
</div>
|
199
229
|
)}
|
@@ -24,10 +24,10 @@ export const Sidebar = ({
|
|
24
24
|
<>
|
25
25
|
{!isOpen && (
|
26
26
|
<button
|
27
|
-
className="fixed top-2 left-2 z-50 lg:hidden bg-white p-1 rounded shadow-md cursor-pointer"
|
27
|
+
className="fixed top-2 left-2 z-50 lg:hidden bg-white p-1 rounded shadow-md cursor-pointer p-2"
|
28
28
|
onClick={() => setIsOpen(true)}
|
29
29
|
>
|
30
|
-
|
30
|
+
{SVGS.rigoSoftBlue}
|
31
31
|
</button>
|
32
32
|
)}
|
33
33
|
|
@@ -44,17 +44,8 @@ export const Sidebar = ({
|
|
44
44
|
✕
|
45
45
|
</button>
|
46
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
47
|
|
57
|
-
<div className="space-y-2 pb-32 h-[
|
48
|
+
<div className="space-y-2 pb-32 h-[85%] overflow-y-auto scrollbar-hide">
|
58
49
|
{messages.map((message, index) => (
|
59
50
|
<Message
|
60
51
|
key={index}
|