@learnpack/learnpack 5.0.71 → 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/serve.js +48 -1
- package/lib/creatorDist/assets/{index-k_eF99Sf.css → index-BJ2JJzVC.css} +48 -12
- package/lib/creatorDist/assets/{index-Dm2fdeOs.js → index-CKBeex0S.js} +36853 -30578
- package/lib/creatorDist/index.html +2 -2
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/src/commands/serve.ts +57 -1
- package/src/creator/package-lock.json +49 -0
- package/src/creator/package.json +1 -0
- package/src/creator/src/App.tsx +27 -21
- 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 +46 -135
- package/src/creator/src/components/syllabus/ContentIndex.tsx +84 -46
- package/src/creator/src/components/syllabus/SyllabusEditor.tsx +55 -12
- package/src/creator/src/index.css +15 -0
- 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 +0 -1
- package/src/creatorDist/assets/{index-k_eF99Sf.css → index-BJ2JJzVC.css} +48 -12
- package/src/creatorDist/assets/{index-Dm2fdeOs.js → index-CKBeex0S.js} +36853 -30578
- 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
- package/src/utils/creds.json +0 -13
@@ -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
31
|
<div className="mt-2">
|
16
|
-
<
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
:
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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)
|
@@ -141,42 +173,48 @@ export const ContentIndex = ({
|
|
141
173
|
ref={containerRef}
|
142
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" }}
|
@@ -6,12 +6,15 @@ import {
|
|
6
6
|
parseLesson,
|
7
7
|
uploadFileToBucket,
|
8
8
|
useConsumableCall,
|
9
|
+
validateTokens,
|
10
|
+
extractImagesFromMarkdown,
|
9
11
|
} from "../../utils/lib"
|
10
12
|
import {
|
11
13
|
createLearnJson,
|
12
14
|
processExercise,
|
13
15
|
slugify,
|
14
16
|
randomUUID,
|
17
|
+
processImage,
|
15
18
|
} from "../../utils/creatorUtils"
|
16
19
|
|
17
20
|
import Loader from "../Loader"
|
@@ -21,6 +24,8 @@ import { ConsumablesManager } from "../ConsumablesManager"
|
|
21
24
|
import toast from "react-hot-toast"
|
22
25
|
import { ContentIndex } from "./ContentIndex"
|
23
26
|
import { Sidebar } from "./Sidebar"
|
27
|
+
import Login from "../Login"
|
28
|
+
import { eventBus } from "../../utils/eventBus"
|
24
29
|
|
25
30
|
const SyllabusEditor: React.FC = () => {
|
26
31
|
const [messages, setMessages] = useState<TMessage[]>([
|
@@ -30,14 +35,14 @@ const SyllabusEditor: React.FC = () => {
|
|
30
35
|
},
|
31
36
|
{
|
32
37
|
type: "assistant",
|
33
|
-
content:
|
34
|
-
|
35
|
-
{
|
36
|
-
type: "user",
|
37
|
-
content: "OK",
|
38
|
+
content:
|
39
|
+
"If not, what would you like me to change? You can sat things like: 'Add more exercises', 'Make it more difficult', 'Remove step 1.1 and replace it with a new step that explains the concept of X'",
|
38
40
|
},
|
39
41
|
])
|
40
42
|
const [isGenerating, setIsGenerating] = useState(false)
|
43
|
+
const [showLoginModal, setShowLoginModal] = useState(false)
|
44
|
+
const [isThinking, setIsThinking] = useState(false)
|
45
|
+
|
41
46
|
const prevLessons = useRef<Lesson[]>([])
|
42
47
|
const { syllabus, setSyllabus, auth } = useStore(
|
43
48
|
useShallow((state) => ({
|
@@ -48,20 +53,22 @@ const SyllabusEditor: React.FC = () => {
|
|
48
53
|
)
|
49
54
|
|
50
55
|
const sendPrompt = async (prompt: string) => {
|
56
|
+
setIsThinking(true)
|
57
|
+
|
51
58
|
setMessages([
|
52
59
|
...messages,
|
53
60
|
{ type: "user", content: prompt },
|
54
61
|
{ type: "assistant", content: "" },
|
55
62
|
])
|
56
63
|
prevLessons.current = syllabus.lessons
|
57
|
-
const res = await interactiveCreation(
|
64
|
+
const res = await interactiveCreation({
|
58
65
|
courseInfo: JSON.stringify(syllabus),
|
59
66
|
prevInteractions:
|
60
67
|
messages
|
61
68
|
.map((message) => `${message.type}: ${message.content}`)
|
62
69
|
.join("\n") + `\nUSER: ${prompt}`,
|
63
70
|
})
|
64
|
-
|
71
|
+
|
65
72
|
const lessons: Lesson[] = res.parsed.listOfSteps.map((step: any) =>
|
66
73
|
parseLesson(step)
|
67
74
|
)
|
@@ -78,9 +85,19 @@ const SyllabusEditor: React.FC = () => {
|
|
78
85
|
newMessages[newMessages.length - 1].content = res.parsed.aiMessage
|
79
86
|
return newMessages
|
80
87
|
})
|
88
|
+
setIsThinking(false)
|
81
89
|
}
|
82
90
|
|
83
91
|
const handleSubmit = async () => {
|
92
|
+
if (!auth.bcToken || !auth.rigoToken) {
|
93
|
+
setShowLoginModal(true)
|
94
|
+
return
|
95
|
+
}
|
96
|
+
const isValid = await validateTokens(auth.bcToken)
|
97
|
+
if (!isValid) {
|
98
|
+
setShowLoginModal(true)
|
99
|
+
return
|
100
|
+
}
|
84
101
|
const success = await useConsumableCall(auth.bcToken, "ai-generation")
|
85
102
|
if (!success) {
|
86
103
|
toast.error("You don't have enough credits to generate a course!")
|
@@ -88,18 +105,35 @@ const SyllabusEditor: React.FC = () => {
|
|
88
105
|
}
|
89
106
|
setIsGenerating(true)
|
90
107
|
|
108
|
+
const tutorialDir =
|
109
|
+
"courses/" + slugify(syllabus.courseInfo.title || randomUUID())
|
91
110
|
const lessonsPromises = syllabus.lessons.map((lesson) =>
|
92
111
|
processExercise(
|
93
112
|
auth.rigoToken,
|
94
113
|
syllabus.lessons,
|
95
114
|
JSON.stringify(syllabus.courseInfo),
|
96
115
|
lesson,
|
97
|
-
"
|
98
|
-
slugify(syllabus.courseInfo.title || randomUUID()) +
|
99
|
-
"/exercises"
|
116
|
+
tutorialDir + "/exercises"
|
100
117
|
)
|
101
118
|
)
|
102
|
-
await Promise.all(lessonsPromises)
|
119
|
+
const readmeContents = await Promise.all(lessonsPromises)
|
120
|
+
|
121
|
+
let imagesArray: any[] = []
|
122
|
+
|
123
|
+
for (const content of readmeContents) {
|
124
|
+
imagesArray = [...imagesArray, ...extractImagesFromMarkdown(content)]
|
125
|
+
}
|
126
|
+
|
127
|
+
eventBus.emit("course-generation", {
|
128
|
+
message: "📷 Generating images...",
|
129
|
+
})
|
130
|
+
|
131
|
+
const imagePromises = imagesArray.map(
|
132
|
+
async (image: { alt: string; url: string }) => {
|
133
|
+
return processImage(tutorialDir, image.url, image.alt, auth.rigoToken)
|
134
|
+
}
|
135
|
+
)
|
136
|
+
await Promise.all(imagePromises)
|
103
137
|
|
104
138
|
const learnJson = createLearnJson(syllabus.courseInfo)
|
105
139
|
await uploadFileToBucket(
|
@@ -118,13 +152,21 @@ const SyllabusEditor: React.FC = () => {
|
|
118
152
|
return isGenerating ? (
|
119
153
|
<Loader
|
120
154
|
listeningTo="course-generation"
|
121
|
-
icon={<img src={"
|
155
|
+
icon={<img src={"rigo-float.gif"} alt="rigo" className="w-20 h-20" />}
|
122
156
|
initialBuffer="🚀 Starting course generation..."
|
123
157
|
text="Learnpack is setting up your tutorial.
|
124
158
|
It may take a moment..."
|
125
159
|
/>
|
126
160
|
) : (
|
127
161
|
<div className="flex w-full bg-white rounded-md shadow-md overflow-hidden h-screen ">
|
162
|
+
{showLoginModal && (
|
163
|
+
<Login
|
164
|
+
onFinish={() => {
|
165
|
+
setShowLoginModal(false)
|
166
|
+
}}
|
167
|
+
/>
|
168
|
+
)}
|
169
|
+
|
128
170
|
<ConsumablesManager />
|
129
171
|
|
130
172
|
<Sidebar
|
@@ -138,6 +180,7 @@ It may take a moment..."
|
|
138
180
|
prevLessons={prevLessons.current}
|
139
181
|
handleSubmit={handleSubmit}
|
140
182
|
messages={messages}
|
183
|
+
isThinking={isThinking}
|
141
184
|
/>
|
142
185
|
</div>
|
143
186
|
</div>
|
@@ -126,3 +126,18 @@ h1 {
|
|
126
126
|
}
|
127
127
|
}
|
128
128
|
}
|
129
|
+
|
130
|
+
.border-learnpack-blue {
|
131
|
+
border-color: var(--learnpack-blue);
|
132
|
+
}
|
133
|
+
|
134
|
+
.red-ball {
|
135
|
+
width: 16px;
|
136
|
+
height: 16px;
|
137
|
+
border: 2px solid white;
|
138
|
+
background-color: #eb5757;
|
139
|
+
border-radius: 50%;
|
140
|
+
position: absolute;
|
141
|
+
top: -10px;
|
142
|
+
left: 10px;
|
143
|
+
}
|
@@ -1,6 +1,11 @@
|
|
1
1
|
import { Lesson } from "../components/LessonItem"
|
2
2
|
import { eventBus } from "./eventBus"
|
3
|
-
import {
|
3
|
+
import {
|
4
|
+
generateImage,
|
5
|
+
getFilenameFromUrl,
|
6
|
+
uploadFileToBucket,
|
7
|
+
uploadImageToBucket,
|
8
|
+
} from "./lib"
|
4
9
|
import { makeReadmeReadable, readmeCreator, checkReadability } from "./rigo"
|
5
10
|
import { FormState } from "./store"
|
6
11
|
|
@@ -80,8 +85,6 @@ export async function processExercise(
|
|
80
85
|
expected_grade_level: PARAMS.expected_grade_level,
|
81
86
|
})
|
82
87
|
|
83
|
-
// console.log("REDUCED README START", reducedReadme, "REDUCED README END")
|
84
|
-
|
85
88
|
if (!reducedReadme) break
|
86
89
|
|
87
90
|
readability = checkReadability(
|
@@ -134,3 +137,30 @@ export async function processExercise(
|
|
134
137
|
export const randomUUID = () => {
|
135
138
|
return Math.random().toString(36).substring(2, 15)
|
136
139
|
}
|
140
|
+
|
141
|
+
export const processImage = async (
|
142
|
+
tutorialDir: string,
|
143
|
+
url: string,
|
144
|
+
description: string,
|
145
|
+
rigoToken: string
|
146
|
+
) => {
|
147
|
+
try {
|
148
|
+
const filename = getFilenameFromUrl(url)
|
149
|
+
|
150
|
+
const imagePath = tutorialDir + "/.learn" + "/assets/" + filename
|
151
|
+
|
152
|
+
eventBus.emit("course-generation", {
|
153
|
+
message: `🖼️ Generating image ${imagePath}`,
|
154
|
+
})
|
155
|
+
|
156
|
+
const res = await generateImage(rigoToken, { prompt: description })
|
157
|
+
await uploadImageToBucket(res.image_url, imagePath)
|
158
|
+
|
159
|
+
eventBus.emit("course-generation", {
|
160
|
+
message: `✅ Image ${imagePath} generated successfully!`,
|
161
|
+
})
|
162
|
+
return true
|
163
|
+
} catch {
|
164
|
+
return false
|
165
|
+
}
|
166
|
+
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import axios from "axios"
|
2
|
-
import { BREATHECODE_HOST } from "./constants"
|
2
|
+
import { BREATHECODE_HOST, RIGOBOT_HOST } from "./constants"
|
3
3
|
|
4
4
|
type ParsedLesson = {
|
5
5
|
id: string
|
@@ -26,7 +26,7 @@ export function parseLesson(input: string): ParsedLesson | null {
|
|
26
26
|
}
|
27
27
|
}
|
28
28
|
|
29
|
-
|
29
|
+
export const CREATOR_API_URL = "http://localhost:3000"
|
30
30
|
|
31
31
|
export const uploadFileToBucket = async (content: string, path: string) => {
|
32
32
|
const response = await axios.post(`/upload`, {
|
@@ -35,6 +35,13 @@ export const uploadFileToBucket = async (content: string, path: string) => {
|
|
35
35
|
})
|
36
36
|
return response.data
|
37
37
|
}
|
38
|
+
export const uploadImageToBucket = async (imageUrl: string, path: string) => {
|
39
|
+
const response = await axios.post(`/upload-image`, {
|
40
|
+
image_url: imageUrl,
|
41
|
+
destination: path,
|
42
|
+
})
|
43
|
+
return response.data
|
44
|
+
}
|
38
45
|
|
39
46
|
export const checkParams = () => {
|
40
47
|
const urlParams = new URLSearchParams(window.location.search)
|
@@ -120,3 +127,150 @@ export const parseConsumables = (
|
|
120
127
|
|
121
128
|
return result
|
122
129
|
}
|
130
|
+
|
131
|
+
type LoginInfo = {
|
132
|
+
email: string
|
133
|
+
password: string
|
134
|
+
}
|
135
|
+
|
136
|
+
export const getRigobotJSON = async (breathecodeToken: string) => {
|
137
|
+
const rigoUrl = `${RIGOBOT_HOST}/v1/auth/me/token?breathecode_token=${breathecodeToken}`
|
138
|
+
const rigoResp = await fetch(rigoUrl)
|
139
|
+
if (!rigoResp.ok) {
|
140
|
+
throw new Error("Unable to obtain Rigobot token")
|
141
|
+
}
|
142
|
+
const rigobotJson = await rigoResp.json()
|
143
|
+
return rigobotJson
|
144
|
+
}
|
145
|
+
export const validateUser = async (breathecodeToken: string) => {
|
146
|
+
const config = {
|
147
|
+
method: "GET",
|
148
|
+
headers: {
|
149
|
+
"Content-Type": "application/json",
|
150
|
+
Authorization: `Token ${breathecodeToken}`,
|
151
|
+
},
|
152
|
+
}
|
153
|
+
|
154
|
+
const res = await fetch(`${BREATHECODE_HOST}/v1/auth/user/me`, config)
|
155
|
+
if (!res.ok) {
|
156
|
+
console.log("ERROR", res)
|
157
|
+
return null
|
158
|
+
}
|
159
|
+
const json = await res.json()
|
160
|
+
|
161
|
+
if ("roles" in json) {
|
162
|
+
delete json.roles
|
163
|
+
}
|
164
|
+
if ("permissions" in json) {
|
165
|
+
delete json.permissions
|
166
|
+
}
|
167
|
+
if ("settings" in json) {
|
168
|
+
delete json.settings
|
169
|
+
}
|
170
|
+
|
171
|
+
return json
|
172
|
+
}
|
173
|
+
|
174
|
+
export const login4Geeks = async (loginInfo: LoginInfo) => {
|
175
|
+
const url = `${BREATHECODE_HOST}/v1/auth/login/`
|
176
|
+
|
177
|
+
const res = await fetch(url, {
|
178
|
+
body: JSON.stringify(loginInfo),
|
179
|
+
method: "post",
|
180
|
+
headers: {
|
181
|
+
"Content-Type": "application/json",
|
182
|
+
},
|
183
|
+
})
|
184
|
+
|
185
|
+
if (!res.ok) {
|
186
|
+
throw Error("Unable to login with provided credentials")
|
187
|
+
}
|
188
|
+
|
189
|
+
const json = await res.json()
|
190
|
+
|
191
|
+
const rigoJson = await getRigobotJSON(json.token)
|
192
|
+
|
193
|
+
const user = await validateUser(json.token)
|
194
|
+
const returns = { ...json, rigobot: { ...rigoJson }, user }
|
195
|
+
|
196
|
+
return returns
|
197
|
+
}
|
198
|
+
|
199
|
+
export const loginWithToken = async (token: string) => {
|
200
|
+
const rigoJson = await getRigobotJSON(token)
|
201
|
+
|
202
|
+
const user = await validateUser(token)
|
203
|
+
|
204
|
+
const returns = { rigobot: { ...rigoJson }, ...user }
|
205
|
+
|
206
|
+
return returns
|
207
|
+
}
|
208
|
+
|
209
|
+
export const validateTokens = async (breathecodeToken: string) => {
|
210
|
+
const user = await validateUser(breathecodeToken)
|
211
|
+
console.log("USER", user)
|
212
|
+
if (!user) {
|
213
|
+
return false
|
214
|
+
}
|
215
|
+
|
216
|
+
const rigobotJson = await getRigobotJSON(breathecodeToken)
|
217
|
+
console.log("RIGOBOT", rigobotJson)
|
218
|
+
|
219
|
+
return true
|
220
|
+
}
|
221
|
+
|
222
|
+
export function extractImagesFromMarkdown(markdown: string) {
|
223
|
+
const imageRegex = /!\[([^\]]*)]\(([^)]+)\)/g
|
224
|
+
const images = []
|
225
|
+
let match
|
226
|
+
|
227
|
+
while ((match = imageRegex.exec(markdown)) !== null) {
|
228
|
+
const altText = match[1]
|
229
|
+
const url = match[2]
|
230
|
+
images.push({ alt: altText, url: url })
|
231
|
+
}
|
232
|
+
|
233
|
+
return images
|
234
|
+
}
|
235
|
+
|
236
|
+
export function getFilenameFromUrl(url: string): string {
|
237
|
+
try {
|
238
|
+
// 1) Use the URL constructor to strip off protocol/host/search/hash
|
239
|
+
const pathname = new URL(url, location.href).pathname
|
240
|
+
// 2) Grab everything after the last “/”
|
241
|
+
return pathname.substring(pathname.lastIndexOf("/") + 1)
|
242
|
+
} catch {
|
243
|
+
// Fallback for non-absolute URLs or invalid inputs
|
244
|
+
const clean = url.split("?")[0].split("#")[0]
|
245
|
+
return clean.substring(clean.lastIndexOf("/") + 1)
|
246
|
+
}
|
247
|
+
}
|
248
|
+
|
249
|
+
type TGenerateImageParams = {
|
250
|
+
prompt: string
|
251
|
+
}
|
252
|
+
|
253
|
+
export const generateImage = async (
|
254
|
+
token: string,
|
255
|
+
{ prompt }: TGenerateImageParams
|
256
|
+
) => {
|
257
|
+
try {
|
258
|
+
const response = await axios.post(
|
259
|
+
`${RIGOBOT_HOST}/v1/learnpack/tools/images`,
|
260
|
+
{
|
261
|
+
prompt,
|
262
|
+
},
|
263
|
+
{
|
264
|
+
headers: {
|
265
|
+
"Content-Type": "application/json",
|
266
|
+
Authorization: "Token " + token,
|
267
|
+
},
|
268
|
+
}
|
269
|
+
)
|
270
|
+
|
271
|
+
return response.data
|
272
|
+
} catch (error) {
|
273
|
+
console.error("Error generating image:", error)
|
274
|
+
return null
|
275
|
+
}
|
276
|
+
}
|
@@ -10,11 +10,11 @@ type TInteractiveCreationInputs = {
|
|
10
10
|
prevInteractions: string
|
11
11
|
}
|
12
12
|
export const interactiveCreation = async (
|
13
|
-
token: string,
|
13
|
+
// token: string,
|
14
14
|
inputs: TInteractiveCreationInputs
|
15
15
|
) => {
|
16
16
|
const response = await axios.post(
|
17
|
-
`${RIGOBOT_HOST}/v1/prompting/completion/390/`,
|
17
|
+
`${RIGOBOT_HOST}/v1/prompting/public/completion/390/`,
|
18
18
|
{
|
19
19
|
inputs: inputs,
|
20
20
|
include_purpose_objective: false,
|
@@ -23,7 +23,7 @@ export const interactiveCreation = async (
|
|
23
23
|
{
|
24
24
|
headers: {
|
25
25
|
"Content-Type": "application/json",
|
26
|
-
Authorization: "Token " + token,
|
26
|
+
// Authorization: "Token " + token,
|
27
27
|
},
|
28
28
|
}
|
29
29
|
)
|