@learnpack/learnpack 5.0.148 → 5.0.152
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 +11 -6
- package/lib/creatorDist/assets/{index-DUPYM87B.css → index-CztA582_.css} +0 -12
- package/lib/creatorDist/assets/{index-ETBXfIew.js → index-l0lFNoeD.js} +32788 -36295
- package/lib/creatorDist/index.html +2 -2
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/src/commands/serve.ts +15 -6
- package/src/creator/src/App.tsx +28 -12
- package/src/creator/src/components/TurnstileChallenge.tsx +2 -9
- package/src/creator/src/utils/creatorUtils.ts +1 -141
- package/src/creator/src/utils/rigo.ts +21 -377
- package/src/creatorDist/assets/{index-DUPYM87B.css → index-CztA582_.css} +0 -12
- package/src/creatorDist/assets/{index-ETBXfIew.js → index-l0lFNoeD.js} +32788 -36295
- package/src/creatorDist/index.html +2 -2
- package/src/ui/_app/app.js +241 -241
- package/src/ui/app.tar.gz +0 -0
@@ -1,418 +1,62 @@
|
|
1
|
+
import toast from "react-hot-toast"
|
1
2
|
import { RIGOBOT_HOST } from "./constants"
|
2
|
-
import axios from "axios"
|
3
|
-
import frontMatter from "front-matter"
|
4
|
-
import { syllable } from "syllable"
|
5
|
-
|
6
|
-
import * as yaml from "js-yaml"
|
3
|
+
import axios, { AxiosError } from "axios"
|
7
4
|
|
8
5
|
type TInteractiveCreationInputs = {
|
9
6
|
courseInfo: string
|
10
7
|
prevInteractions: string
|
11
8
|
}
|
9
|
+
|
12
10
|
export const interactiveCreation = async (
|
13
|
-
// token: string,
|
14
11
|
inputs: TInteractiveCreationInputs
|
15
|
-
) => {
|
16
|
-
const response = await axios.post(
|
17
|
-
`${RIGOBOT_HOST}/v1/prompting/public/completion/390/`,
|
18
|
-
{
|
19
|
-
inputs: inputs,
|
20
|
-
include_purpose_objective: false,
|
21
|
-
execute_async: false,
|
22
|
-
},
|
23
|
-
{
|
24
|
-
headers: {
|
25
|
-
"Content-Type": "application/json",
|
26
|
-
// Authorization: "Token " + token,
|
27
|
-
},
|
28
|
-
}
|
29
|
-
)
|
30
|
-
|
31
|
-
return response.data
|
32
|
-
}
|
33
|
-
|
34
|
-
type TCreateReadmeInputs = {
|
35
|
-
title: string
|
36
|
-
output_lang: string
|
37
|
-
list_of_exercises: string
|
38
|
-
tutorial_description: string
|
39
|
-
include_quiz: string
|
40
|
-
lesson_description: string
|
41
|
-
}
|
42
|
-
|
43
|
-
export const createReadme = async (
|
44
|
-
token: string,
|
45
|
-
inputs: TCreateReadmeInputs
|
46
|
-
) => {
|
12
|
+
): Promise<any | null> => {
|
47
13
|
try {
|
48
14
|
const response = await axios.post(
|
49
|
-
`${RIGOBOT_HOST}/v1/prompting/completion/
|
15
|
+
`${RIGOBOT_HOST}/v1/prompting/public/completion/390/`,
|
50
16
|
{
|
51
|
-
inputs,
|
17
|
+
inputs: inputs,
|
52
18
|
include_purpose_objective: false,
|
53
19
|
execute_async: false,
|
54
20
|
},
|
55
21
|
{
|
56
22
|
headers: {
|
57
23
|
"Content-Type": "application/json",
|
58
|
-
Authorization: "Token " + token,
|
59
24
|
},
|
60
25
|
}
|
61
26
|
)
|
62
|
-
return response.data
|
63
|
-
} catch (error) {
|
64
|
-
console.error(error)
|
65
|
-
return null
|
66
|
-
}
|
67
|
-
}
|
68
|
-
|
69
|
-
type TCreateCodingReadmeInputs = {
|
70
|
-
tutorial_description: string
|
71
|
-
list_of_exercises: string
|
72
|
-
output_lang: string
|
73
|
-
title: string
|
74
|
-
lesson_description: string
|
75
|
-
}
|
76
|
-
export const createCodingReadme = async (
|
77
|
-
token: string,
|
78
|
-
inputs: TCreateCodingReadmeInputs
|
79
|
-
) => {
|
80
|
-
const response = await axios.post(
|
81
|
-
`${RIGOBOT_HOST}/v1/prompting/completion/489/`,
|
82
|
-
{ inputs, include_purpose_objective: false, execute_async: false },
|
83
|
-
{
|
84
|
-
headers: {
|
85
|
-
"Content-Type": "application/json",
|
86
|
-
Authorization: "Token " + token,
|
87
|
-
},
|
88
|
-
}
|
89
|
-
)
|
90
|
-
|
91
|
-
return response.data
|
92
|
-
}
|
93
|
-
|
94
|
-
type TReadmeCreatorInputs = {
|
95
|
-
tutorial_description: string
|
96
|
-
list_of_exercises: string
|
97
|
-
output_lang: string
|
98
|
-
title: string
|
99
|
-
lesson_description: string
|
100
|
-
kind: string
|
101
|
-
}
|
102
|
-
|
103
|
-
export const readmeCreator = async (
|
104
|
-
token: string,
|
105
|
-
inputs: TReadmeCreatorInputs
|
106
|
-
) => {
|
107
|
-
if (inputs.kind === "quiz" || inputs.kind === "read") {
|
108
|
-
const createReadmeInputs: TCreateReadmeInputs = {
|
109
|
-
title: inputs.title,
|
110
|
-
output_lang: inputs.output_lang,
|
111
|
-
list_of_exercises: inputs.list_of_exercises,
|
112
|
-
tutorial_description: inputs.tutorial_description,
|
113
|
-
include_quiz: inputs.kind === "quiz" ? "true" : "false",
|
114
|
-
lesson_description: inputs.lesson_description,
|
115
|
-
}
|
116
|
-
return createReadme(token, createReadmeInputs)
|
117
|
-
}
|
118
27
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
output_lang: inputs.output_lang,
|
123
|
-
list_of_exercises: inputs.list_of_exercises,
|
124
|
-
tutorial_description: inputs.tutorial_description,
|
125
|
-
lesson_description: inputs.lesson_description,
|
126
|
-
})
|
127
|
-
}
|
128
|
-
|
129
|
-
throw new Error("Invalid kind of lesson")
|
130
|
-
}
|
131
|
-
|
132
|
-
type TEstimateReadingTimeReturns = {
|
133
|
-
minutes: number
|
134
|
-
words: number
|
135
|
-
}
|
136
|
-
|
137
|
-
export const estimateReadingTime = (
|
138
|
-
text: string,
|
139
|
-
wordsPerMinute = 150
|
140
|
-
): TEstimateReadingTimeReturns => {
|
141
|
-
const words = text.trim().split(/\s+/).length
|
142
|
-
const minutes = words / wordsPerMinute
|
28
|
+
return response.data
|
29
|
+
} catch (error: unknown) {
|
30
|
+
const err = error as AxiosError
|
143
31
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
words,
|
149
|
-
}
|
150
|
-
} else {
|
151
|
-
return {
|
152
|
-
minutes,
|
153
|
-
words,
|
32
|
+
if (err.response?.status === 403) {
|
33
|
+
toast.error("You've reached the limit. Please log in to continue.")
|
34
|
+
} else {
|
35
|
+
toast.error("Something went wrong while generating the course.")
|
154
36
|
}
|
155
|
-
}
|
156
37
|
|
157
|
-
|
158
|
-
minutes: 1,
|
159
|
-
words,
|
38
|
+
return null
|
160
39
|
}
|
161
40
|
}
|
162
41
|
|
163
|
-
|
164
|
-
text: string
|
165
|
-
fkgl: number
|
166
|
-
}
|
167
|
-
|
168
|
-
export function checkReadability(
|
169
|
-
markdown: string,
|
170
|
-
wordsPerMinute = 200,
|
171
|
-
maxMinutes = 1
|
172
|
-
): {
|
173
|
-
newMarkdown: string
|
174
|
-
exceedsThreshold: boolean
|
175
|
-
minutes: number
|
176
|
-
body: string
|
177
|
-
fkglResult: TFKGLResult
|
178
|
-
// readingEase: number
|
179
|
-
} {
|
180
|
-
const parsed = frontMatter(markdown)
|
181
|
-
|
182
|
-
const fkglResult = fleschKincaidGrade(parsed.body)
|
183
|
-
|
184
|
-
const readingTime = estimateReadingTime(parsed.body, wordsPerMinute)
|
185
|
-
|
186
|
-
// const readingEase = estimateReadingEase(parsed.body)
|
187
|
-
let attributes = parsed.attributes ? parsed.attributes : {}
|
188
|
-
|
189
|
-
if (typeof parsed.attributes !== "object") {
|
190
|
-
attributes = {}
|
191
|
-
}
|
192
|
-
|
193
|
-
const updatedAttributes = {
|
194
|
-
...attributes,
|
195
|
-
readingTime,
|
196
|
-
fkglResult: fkglResult.fkgl,
|
197
|
-
}
|
198
|
-
|
199
|
-
let yamlFrontMatter = ""
|
42
|
+
export const isHuman = async (token: string) => {
|
200
43
|
try {
|
201
|
-
|
202
|
-
|
203
|
-
// Console.error("Error dumping YAML front matter")
|
204
|
-
return {
|
205
|
-
newMarkdown: "",
|
206
|
-
exceedsThreshold: false,
|
207
|
-
minutes: 0,
|
208
|
-
body: "",
|
209
|
-
fkglResult,
|
210
|
-
// readingEase: 0,
|
44
|
+
const body = {
|
45
|
+
access_token: token,
|
211
46
|
}
|
212
|
-
}
|
213
|
-
|
214
|
-
const newMarkdown = `---\n${yamlFrontMatter}\n---\n\n${parsed.body}`
|
215
|
-
|
216
|
-
return {
|
217
|
-
newMarkdown,
|
218
|
-
exceedsThreshold: readingTime.minutes > maxMinutes,
|
219
|
-
minutes: readingTime.minutes,
|
220
|
-
body: parsed.body,
|
221
|
-
fkglResult,
|
222
|
-
}
|
223
|
-
}
|
224
|
-
|
225
|
-
export function extractImagesFromMarkdown(markdown: string) {
|
226
|
-
const imageRegex = /!\[([^\]]*)]\(([^)]+)\)/g
|
227
|
-
const images = []
|
228
|
-
let match
|
229
|
-
|
230
|
-
while ((match = imageRegex.exec(markdown)) !== null) {
|
231
|
-
const altText = match[1]
|
232
|
-
const url = match[2]
|
233
|
-
images.push({ alt: altText, url: url })
|
234
|
-
}
|
235
|
-
|
236
|
-
return images
|
237
|
-
}
|
238
|
-
|
239
|
-
export function estimateDuration(listOfSteps: string[]): number {
|
240
|
-
let duration = 0
|
241
|
-
|
242
|
-
for (const step of listOfSteps) {
|
243
|
-
if (step.includes("[READ:")) {
|
244
|
-
duration += 2
|
245
|
-
} else if (step.includes("[QUIZ:")) {
|
246
|
-
duration += 3
|
247
|
-
} else if (step.includes("[CODE:")) {
|
248
|
-
duration += 5
|
249
|
-
}
|
250
|
-
}
|
251
|
-
|
252
|
-
return duration
|
253
|
-
}
|
254
|
-
|
255
|
-
export function extractTextFromMarkdown(mdContent: string): string {
|
256
|
-
return mdContent
|
257
|
-
.replace(/!\[.*?]\(.*?\)/g, "") // Remove images
|
258
|
-
.replace(/\[.*?]\(.*?\)/g, "") // Remove links
|
259
|
-
.replace(/(```[\S\s]*?```|`.*?`)/g, "") // Remove inline & block code
|
260
|
-
.replace(/^#.*$/gm, "") // Remove headings
|
261
|
-
.replace(/(\*{1,2}|_{1,2})/g, "") // Remove bold/italic markers
|
262
|
-
.replace(/>\s?/g, "") // Remove blockquotes
|
263
|
-
.replace(/[*-]\s+/g, "") // Remove bullets from lists
|
264
|
-
.trim()
|
265
|
-
}
|
266
|
-
|
267
|
-
/**
|
268
|
-
* Splits a paragraph into words and separates each word into syllables.
|
269
|
-
* @param paragraph The input paragraph.
|
270
|
-
* @returns An array of words, each split into syllables.
|
271
|
-
*/
|
272
|
-
export function splitIntoSyllables(paragraph: string): string[] {
|
273
|
-
const words = paragraph.split(/\s+/)
|
274
|
-
const syllables: string[] = []
|
275
|
-
|
276
|
-
for (const word of words) {
|
277
|
-
syllables.push(...splitWordIntoSyllables(word))
|
278
|
-
}
|
279
|
-
|
280
|
-
return syllables
|
281
|
-
}
|
282
|
-
|
283
|
-
/**
|
284
|
-
* Splits a word into its syllables using a basic estimation.
|
285
|
-
* @param word The word to split.
|
286
|
-
* @returns An array of syllables.
|
287
|
-
*/
|
288
|
-
export function splitWordIntoSyllables(word: string): string[] {
|
289
|
-
const syllableCount = syllable(word)
|
290
|
-
|
291
|
-
// Simple heuristic: Split word into equal parts (not perfect, better with a dictionary-based approach)
|
292
|
-
if (syllableCount <= 1) return [word]
|
293
|
-
|
294
|
-
const approxLength = Math.ceil(word.length / syllableCount)
|
295
|
-
const syllables: string[] = []
|
296
|
-
for (let i = 0; i < word.length; i += approxLength) {
|
297
|
-
// eslint-disable-next-line
|
298
|
-
syllables.push(word.substring(i, i + approxLength))
|
299
|
-
}
|
300
|
-
|
301
|
-
return syllables
|
302
|
-
}
|
303
|
-
|
304
|
-
/**
|
305
|
-
* Extracts words from a given paragraph.
|
306
|
-
* @param paragraph The input text.
|
307
|
-
* @returns An array of words.
|
308
|
-
*/
|
309
|
-
export function extractWords(paragraph: string): string[] {
|
310
|
-
const words = paragraph.match(/\b\w+\b/g) // Match words using regex
|
311
|
-
return words ? words : [] // Return words or an empty array if none found
|
312
|
-
}
|
313
|
-
|
314
|
-
/**
|
315
|
-
* Calculates the Flesch-Kincaid Grade Level (FKGL) for a given text.
|
316
|
-
* @param text The input paragraph.
|
317
|
-
* @returns The FKGL score.
|
318
|
-
*/
|
319
|
-
export function fleschKincaidGrade(text: string): TFKGLResult {
|
320
|
-
const processableText = extractTextFromMarkdown(text)
|
321
|
-
const words = extractWords(processableText)
|
322
|
-
const numWords = words.length
|
323
|
-
const numSentences = countSentences(processableText)
|
324
|
-
const numSyllables = words.reduce((total, word) => total + syllable(word), 0)
|
325
|
-
|
326
|
-
if (numWords === 0 || numSentences === 0) {
|
327
|
-
return {
|
328
|
-
text,
|
329
|
-
fkgl: 0,
|
330
|
-
}
|
331
|
-
}
|
332
|
-
|
333
|
-
const fkgl =
|
334
|
-
// eslint-disable-next-line
|
335
|
-
0.39 * (numWords / numSentences) + 11.8 * (numSyllables / numWords) - 15.59
|
336
|
-
|
337
|
-
return {
|
338
|
-
text,
|
339
|
-
fkgl: parseFloat(fkgl.toFixed(2)),
|
340
|
-
}
|
341
|
-
}
|
342
|
-
|
343
|
-
/**
|
344
|
-
* Counts the number of sentences in a given text.
|
345
|
-
* @param text The input paragraph.
|
346
|
-
* @returns The total number of sentences.
|
347
|
-
*/
|
348
|
-
export function countSentences(text: string): number {
|
349
|
-
const sentences = text
|
350
|
-
.split(/[!.?]+/)
|
351
|
-
.filter((sentence) => sentence.trim().length > 0)
|
352
|
-
return sentences.length
|
353
|
-
}
|
354
|
-
|
355
|
-
export function howManyDifficultParagraphs(
|
356
|
-
paragraphs: TFKGLResult[],
|
357
|
-
maxFKGL: number
|
358
|
-
): number {
|
359
|
-
return paragraphs.filter((paragraph) => paragraph.fkgl > maxFKGL).length
|
360
|
-
}
|
361
|
-
|
362
|
-
type TReduceReadmeInputs = {
|
363
|
-
lesson: string
|
364
|
-
number_of_words: string
|
365
|
-
expected_number_words: string
|
366
|
-
fkgl_results: string
|
367
|
-
expected_grade_level: string
|
368
|
-
}
|
369
|
-
export async function makeReadmeReadable(
|
370
|
-
rigoToken: string,
|
371
|
-
inputs: TReduceReadmeInputs
|
372
|
-
) {
|
373
|
-
try {
|
374
47
|
const response = await axios.post(
|
375
|
-
`${RIGOBOT_HOST}/v1/
|
376
|
-
|
48
|
+
`${RIGOBOT_HOST}/v1/auth/verify/humanity`,
|
49
|
+
body,
|
377
50
|
{
|
378
51
|
headers: {
|
379
52
|
"Content-Type": "application/json",
|
380
|
-
Authorization: "Token " + rigoToken,
|
381
53
|
},
|
382
54
|
}
|
383
55
|
)
|
384
56
|
|
385
|
-
return response.
|
57
|
+
return response.status === 200
|
386
58
|
} catch (error) {
|
387
59
|
console.error(error)
|
388
|
-
|
389
|
-
return null
|
60
|
+
return false
|
390
61
|
}
|
391
62
|
}
|
392
|
-
|
393
|
-
type TCreateCodeFileInputs = {
|
394
|
-
readme: string
|
395
|
-
tutorial_info: string
|
396
|
-
}
|
397
|
-
|
398
|
-
export const createCodeFile = async (
|
399
|
-
token: string,
|
400
|
-
inputs: TCreateCodeFileInputs
|
401
|
-
) => {
|
402
|
-
const response = await axios.post(
|
403
|
-
`${RIGOBOT_HOST}/v1/prompting/completion/456/`,
|
404
|
-
{
|
405
|
-
inputs: inputs,
|
406
|
-
include_purpose_objective: false,
|
407
|
-
execute_async: false,
|
408
|
-
},
|
409
|
-
{
|
410
|
-
headers: {
|
411
|
-
"Content-Type": "application/json",
|
412
|
-
Authorization: "Token " + token,
|
413
|
-
},
|
414
|
-
}
|
415
|
-
)
|
416
|
-
|
417
|
-
return response.data
|
418
|
-
}
|
@@ -395,9 +395,6 @@
|
|
395
395
|
.top-2 {
|
396
396
|
top: calc(var(--spacing) * 2);
|
397
397
|
}
|
398
|
-
.right-0 {
|
399
|
-
right: calc(var(--spacing) * 0);
|
400
|
-
}
|
401
398
|
.right-2 {
|
402
399
|
right: calc(var(--spacing) * 2);
|
403
400
|
}
|
@@ -525,18 +522,12 @@
|
|
525
522
|
.ml-auto {
|
526
523
|
margin-left: auto;
|
527
524
|
}
|
528
|
-
.block {
|
529
|
-
display: block;
|
530
|
-
}
|
531
525
|
.flex {
|
532
526
|
display: flex;
|
533
527
|
}
|
534
528
|
.hidden {
|
535
529
|
display: none;
|
536
530
|
}
|
537
|
-
.inline {
|
538
|
-
display: inline;
|
539
|
-
}
|
540
531
|
.inline-block {
|
541
532
|
display: inline-block;
|
542
533
|
}
|
@@ -718,9 +709,6 @@
|
|
718
709
|
.gap-3 {
|
719
710
|
gap: calc(var(--spacing) * 3);
|
720
711
|
}
|
721
|
-
.gap-4 {
|
722
|
-
gap: calc(var(--spacing) * 4);
|
723
|
-
}
|
724
712
|
:where(.space-y-2 > :not(:last-child)) {
|
725
713
|
--tw-space-y-reverse: 0;
|
726
714
|
margin-block-start: calc(
|