@learnpack/learnpack 5.0.66 → 5.0.68

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 (53) hide show
  1. package/README.md +13 -13
  2. package/lib/commands/serve.js +15 -15
  3. package/lib/creatorDist/assets/index-B01XTAAq.js +75129 -0
  4. package/{src/creatorDist/assets/index-tt9JBVY0.css → lib/creatorDist/assets/index-t6ma_gVm.css} +118 -20
  5. package/lib/creatorDist/assets/pdf.worker-DSVOJ9H9.js +56037 -0
  6. package/lib/creatorDist/index.html +10 -5
  7. package/lib/creatorDist/logo-192 copy.png +0 -0
  8. package/lib/creatorDist/logo.png +0 -0
  9. package/oclif.manifest.json +1 -1
  10. package/package.json +1 -1
  11. package/src/commands/serve.ts +24 -22
  12. package/src/creator/index.html +8 -3
  13. package/src/creator/package-lock.json +394 -0
  14. package/src/creator/package.json +3 -0
  15. package/src/creator/public/logo-192 copy.png +0 -0
  16. package/src/creator/public/logo.png +0 -0
  17. package/src/creator/src/App.tsx +30 -4
  18. package/src/creator/src/assets/svgs.tsx +138 -0
  19. package/src/creator/src/components/ConsumablesManager.tsx +21 -0
  20. package/src/creator/src/components/FileUploader.tsx +91 -0
  21. package/src/creator/src/components/LessonItem.tsx +70 -0
  22. package/src/creator/src/components/Loader.tsx +64 -19
  23. package/src/creator/src/components/Login.tsx +6 -12
  24. package/src/creator/src/components/Message.tsx +28 -0
  25. package/src/creator/src/components/RigoLoader.tsx +14 -0
  26. package/src/creator/src/components/StepWizard.tsx +1 -0
  27. package/src/creator/src/components/SyllabusEditor.tsx +135 -266
  28. package/src/creator/src/index.css +64 -0
  29. package/src/creator/src/utils/creatorUtils.ts +136 -0
  30. package/src/creator/src/utils/eventBus.ts +2 -0
  31. package/src/creator/src/utils/lib.ts +86 -0
  32. package/src/creator/src/utils/store.ts +25 -1
  33. package/src/creatorDist/assets/index-B01XTAAq.js +75129 -0
  34. package/{lib/creatorDist/assets/index-tt9JBVY0.css → src/creatorDist/assets/index-t6ma_gVm.css} +118 -20
  35. package/src/creatorDist/assets/pdf.worker-DSVOJ9H9.js +56037 -0
  36. package/src/creatorDist/index.html +10 -5
  37. package/src/creatorDist/logo-192 copy.png +0 -0
  38. package/src/creatorDist/logo.png +0 -0
  39. package/src/ui/_app/app.css +1 -0
  40. package/src/ui/_app/app.js +3027 -0
  41. package/src/ui/_app/favicon.ico +0 -0
  42. package/src/ui/_app/index.html +109 -0
  43. package/src/ui/_app/index.html.backup +91 -0
  44. package/src/ui/_app/learnpack.svg +7 -0
  45. package/src/ui/_app/logo-192.png +0 -0
  46. package/src/ui/_app/logo-512.png +0 -0
  47. package/src/ui/_app/logo.png +0 -0
  48. package/src/ui/_app/manifest.webmanifest +21 -0
  49. package/src/ui/_app/sw.js +30 -0
  50. package/src/ui/app.tar.gz +0 -0
  51. package/lib/creatorDist/assets/index-CrrS9sA3.js +0 -23718
  52. package/src/creator/src/App.css +0 -42
  53. package/src/creatorDist/assets/index-CrrS9sA3.js +0 -23718
@@ -0,0 +1,136 @@
1
+ import { Lesson } from "../components/LessonItem"
2
+ import { eventBus } from "./eventBus"
3
+ import { uploadFileToBucket } from "./lib"
4
+ import { makeReadmeReadable, readmeCreator, checkReadability } from "./rigo"
5
+ import { FormState } from "./store"
6
+
7
+ export const slugify = (text: string) => {
8
+ return text
9
+ .toLowerCase()
10
+ .replace(/ /g, "-")
11
+ .replace(/[^\w.-]+/g, "")
12
+ }
13
+
14
+ export const createLearnJson = (courseInfo: FormState) => {
15
+ const learnJson = {
16
+ slug: slugify(courseInfo.title || randomUUID()),
17
+ title: {
18
+ us: courseInfo.title,
19
+ },
20
+ description: {
21
+ us: courseInfo.description,
22
+ },
23
+ grading: "isolated",
24
+ }
25
+ return learnJson
26
+ }
27
+
28
+ const PARAMS = {
29
+ expected_grade_level: "6",
30
+ max_fkgl: 8,
31
+ max_words: 200,
32
+ max_rewrite_attempts: 3,
33
+ max_title_length: 50,
34
+ }
35
+ export async function processExercise(
36
+ rigoToken: string,
37
+ steps: Lesson[],
38
+ packageContext: string,
39
+ exercise: Lesson,
40
+ exercisesDir: string
41
+ ): Promise<string> {
42
+ // const tid = toast.loading("Generating lesson...")
43
+ setTimeout(() => {
44
+ eventBus.emit("course-generation", {
45
+ message: `✍🏻 Generating lesson ${exercise.id} - ${exercise.title}...`,
46
+ })
47
+ }, 500)
48
+ const readme = await readmeCreator(rigoToken, {
49
+ title: `${exercise.id} - ${exercise.title}`,
50
+ output_lang: "en",
51
+ list_of_exercises: JSON.stringify(steps),
52
+ tutorial_description: packageContext,
53
+ lesson_description: exercise.description,
54
+ kind: exercise.type.toLowerCase(),
55
+ })
56
+
57
+ const duration = exercise.duration
58
+ let attempts = 0
59
+ let readability = checkReadability(readme.parsed.content, 200, duration || 1)
60
+
61
+ while (
62
+ readability.fkglResult.fkgl > PARAMS.max_fkgl &&
63
+ attempts < PARAMS.max_rewrite_attempts
64
+ ) {
65
+ setTimeout(() => {
66
+ eventBus.emit("course-generation", {
67
+ message: `🔄 The lesson ${exercise.id} - ${
68
+ exercise.title
69
+ } has a readability score of ${
70
+ readability.fkglResult.fkgl
71
+ }. Rewriting it... (Attempt ${attempts + 1})`,
72
+ })
73
+ }, 500)
74
+ // eslint-disable-next-line
75
+ const reducedReadme = await makeReadmeReadable(rigoToken, {
76
+ lesson: readability.body,
77
+ number_of_words: readability.minutes.toString(),
78
+ expected_number_words: PARAMS.max_words.toString(),
79
+ fkgl_results: JSON.stringify(readability.fkglResult),
80
+ expected_grade_level: PARAMS.expected_grade_level,
81
+ })
82
+
83
+ // console.log("REDUCED README START", reducedReadme, "REDUCED README END")
84
+
85
+ if (!reducedReadme) break
86
+
87
+ readability = checkReadability(
88
+ reducedReadme.parsed.content,
89
+ PARAMS.max_words,
90
+ duration || 1
91
+ )
92
+
93
+ attempts++
94
+ }
95
+
96
+ setTimeout(() => {
97
+ eventBus.emit("course-generation", {
98
+ message: `✅ After ${attempts} attempts, the lesson ${
99
+ exercise.title
100
+ } has a readability score of ${
101
+ readability.fkglResult.fkgl
102
+ } using FKGL. And it has ${readability.minutes.toFixed(
103
+ 2
104
+ )} minutes of reading time.`,
105
+ })
106
+ }, 500)
107
+
108
+ const readmeFilename = "README.md"
109
+ await uploadFileToBucket(
110
+ readability.newMarkdown,
111
+ `${exercisesDir}/${slugify(
112
+ exercise.id + "-" + exercise.title
113
+ )}/${readmeFilename}`
114
+ )
115
+
116
+ if (exercise.type.toLowerCase() === "code") {
117
+ // const codeFile = await createCodeFile(rigoToken, {
118
+ // readme: readability.newMarkdown,
119
+ // tutorial_info: packageContext,
120
+ // })
121
+ // fs.writeFileSync(
122
+ // path.join(
123
+ // exerciseDir,
124
+ // `app.${codeFile.parsed.extension.replace(".", "")}`
125
+ // ),
126
+ // codeFile.parsed.content
127
+ // )
128
+ }
129
+
130
+ // toast.success("Lesson generated successfully", { id: tid })
131
+ return readability.newMarkdown
132
+ }
133
+
134
+ export const randomUUID = () => {
135
+ return Math.random().toString(36).substring(2, 15)
136
+ }
@@ -0,0 +1,2 @@
1
+ import mitt from "mitt"
2
+ export const eventBus = mitt()
@@ -1,4 +1,5 @@
1
1
  import axios from "axios"
2
+ import { BREATHECODE_HOST } from "./constants"
2
3
 
3
4
  type ParsedLesson = {
4
5
  id: string
@@ -34,3 +35,88 @@ export const uploadFileToBucket = async (content: string, path: string) => {
34
35
  })
35
36
  return response.data
36
37
  }
38
+
39
+ export const checkParams = () => {
40
+ const urlParams = new URLSearchParams(window.location.search)
41
+ const token = urlParams.get("token")
42
+ return { token }
43
+ }
44
+
45
+ export async function getConsumables(token: string): Promise<any> {
46
+ const url = `${BREATHECODE_HOST}/v1/payments/me/service/consumable?virtual=true`
47
+
48
+ const headers = {
49
+ Authorization: `Token ${token}`,
50
+ }
51
+
52
+ try {
53
+ const response = await axios.get(url, { headers })
54
+ return response.data
55
+ } catch (error) {
56
+ console.error("Error fetching consumables:", error)
57
+ throw error
58
+ }
59
+ }
60
+
61
+ type ConsumableSlug =
62
+ | "ai-conversation-message"
63
+ | "ai-compilation"
64
+ | "ai-generation"
65
+ | "ai-course-generation"
66
+
67
+ export async function useConsumableCall(
68
+ breathecodeToken: string,
69
+ consumableSlug: ConsumableSlug = "ai-conversation-message"
70
+ ): Promise<boolean> {
71
+ const url = `${BREATHECODE_HOST}/v1/payments/me/service/${consumableSlug}/consumptionsession`
72
+
73
+ const headers = {
74
+ Authorization: `Token ${breathecodeToken}`,
75
+ }
76
+
77
+ try {
78
+ const response = await axios.put(url, {}, { headers })
79
+
80
+ if (response.status >= 200 && response.status < 300) {
81
+ console.log(response.data)
82
+ console.log(`Successfully consumed ${consumableSlug}`)
83
+ return true
84
+ } else {
85
+ console.error(`Request failed with status code: ${response.status}`)
86
+ console.error(`Response: ${response.data}`)
87
+ return false
88
+ }
89
+ } catch (error) {
90
+ console.error(`Error consuming ${consumableSlug}:`, error)
91
+ return false
92
+ }
93
+ }
94
+
95
+ type ConsumableItem = {
96
+ id: number
97
+ how_many: number
98
+ unit_type: string
99
+ valid_until: string | null
100
+ }
101
+
102
+ type VoidEntry = {
103
+ id: number
104
+ slug: string
105
+ balance: { unit: number }
106
+ items: ConsumableItem[]
107
+ }
108
+
109
+ export const parseConsumables = (
110
+ voids: VoidEntry[]
111
+ ): Record<string, number> => {
112
+ const result: Record<string, number> = {}
113
+
114
+ voids.forEach((entry) => {
115
+ const maxHowMany = entry.items.length
116
+ ? Math.max(...entry.items.map((item) => item.how_many))
117
+ : 0
118
+ result[entry.slug] = maxHowMany
119
+ })
120
+
121
+ return result
122
+ }
@@ -1,6 +1,6 @@
1
1
  import { create } from "zustand"
2
2
  import { persist } from "zustand/middleware"
3
- import { Lesson } from "../components/SyllabusEditor"
3
+ import { Lesson } from "../components/LessonItem"
4
4
 
5
5
  export type FormState = {
6
6
  description: string
@@ -21,6 +21,14 @@ type Auth = {
21
21
  type Syllabus = {
22
22
  lessons: Lesson[]
23
23
  courseInfo: FormState
24
+ uploadedFiles: {
25
+ name: string
26
+ text: string
27
+ }[]
28
+ }
29
+
30
+ type Consumables = {
31
+ [key: string]: number
24
32
  }
25
33
 
26
34
  type Store = {
@@ -30,6 +38,8 @@ type Store = {
30
38
  syllabus: Syllabus
31
39
  setSyllabus: (syllabus: Partial<Syllabus>) => void
32
40
  setFormState: (formState: Partial<FormState>) => void
41
+ consumables: Consumables
42
+ setConsumables: (consumables: Partial<Consumables>) => void
33
43
  }
34
44
 
35
45
  const useStore = create<Store>()(
@@ -64,7 +74,21 @@ const useStore = create<Store>()(
64
74
  currentStep: 0,
65
75
  title: "",
66
76
  },
77
+ uploadedFiles: [],
67
78
  },
79
+ consumables: {},
80
+ setConsumables: (consumables: Partial<Consumables>) =>
81
+ set((state) => {
82
+ const sanitized: Consumables = Object.fromEntries(
83
+ Object.entries(consumables).map(([k, v]) => [k, v ?? 0])
84
+ )
85
+ return {
86
+ consumables: {
87
+ ...state.consumables,
88
+ ...sanitized,
89
+ },
90
+ }
91
+ }),
68
92
  setSyllabus: (syllabus: Partial<Syllabus>) =>
69
93
  set((state) => ({ syllabus: { ...state.syllabus, ...syllabus } })),
70
94
  setAuth: (auth: Auth) => set({ auth }),