@learnpack/learnpack 5.0.293 → 5.0.295
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/lib/commands/serve.js +132 -0
- package/lib/creatorDist/assets/index-CwOHnpZp.js +74438 -0
- package/lib/creatorDist/assets/{index-CacFtcN8.css → index-DTjdV1LF.css} +0 -8
- package/lib/creatorDist/index.html +2 -2
- package/lib/utils/rigoActions.d.ts +5 -0
- package/lib/utils/rigoActions.js +22 -1
- package/package.json +1 -1
- package/src/commands/serve.ts +184 -0
- package/src/creator/package-lock.json +10 -0
- package/src/creator/package.json +1 -0
- package/src/creator/src/components/Icon.tsx +18 -0
- package/src/creator/src/components/LessonItem.tsx +7 -10
- package/src/creatorDist/assets/index-CwOHnpZp.js +74438 -0
- package/src/creatorDist/assets/{index-CacFtcN8.css → index-DTjdV1LF.css} +0 -8
- package/src/creatorDist/index.html +2 -2
- package/src/ui/_app/app.css +1 -1
- package/src/ui/_app/app.js +8677 -458
- package/src/ui/app.tar.gz +0 -0
- package/src/utils/rigoActions.ts +33 -0
- package/lib/creatorDist/assets/index-DOEfLGDQ.js +0 -39343
- package/src/creatorDist/assets/index-DOEfLGDQ.js +0 -39343
@@ -59,8 +59,6 @@
|
|
59
59
|
--color-red-500: oklch(63.7% 0.237 25.331);
|
60
60
|
--color-red-600: oklch(57.7% 0.245 27.325);
|
61
61
|
--color-red-700: oklch(50.5% 0.213 27.518);
|
62
|
-
--color-yellow-50: oklch(98.7% 0.026 102.212);
|
63
|
-
--color-yellow-300: oklch(90.5% 0.182 98.111);
|
64
62
|
--color-yellow-600: oklch(68.1% 0.162 75.834);
|
65
63
|
--color-yellow-700: oklch(55.4% 0.135 66.442);
|
66
64
|
--color-blue-50: oklch(97% 0.014 254.604);
|
@@ -831,9 +829,6 @@
|
|
831
829
|
.border-transparent {
|
832
830
|
border-color: #0000;
|
833
831
|
}
|
834
|
-
.border-yellow-300 {
|
835
|
-
border-color: var(--color-yellow-300);
|
836
|
-
}
|
837
832
|
.border-t-transparent {
|
838
833
|
border-top-color: #0000;
|
839
834
|
}
|
@@ -900,9 +895,6 @@
|
|
900
895
|
.bg-white {
|
901
896
|
background-color: var(--color-white);
|
902
897
|
}
|
903
|
-
.bg-yellow-50 {
|
904
|
-
background-color: var(--color-yellow-50);
|
905
|
-
}
|
906
898
|
.bg-gradient-to-t {
|
907
899
|
--tw-gradient-position: to top in oklab;
|
908
900
|
background-image: linear-gradient(var(--tw-gradient-stops));
|
@@ -10,8 +10,8 @@
|
|
10
10
|
/>
|
11
11
|
|
12
12
|
<title>Learnpack Creator: Craft tutorials in seconds!</title>
|
13
|
-
<script type="module" crossorigin src="/creator/assets/index-
|
14
|
-
<link rel="stylesheet" crossorigin href="/creator/assets/index-
|
13
|
+
<script type="module" crossorigin src="/creator/assets/index-CwOHnpZp.js"></script>
|
14
|
+
<link rel="stylesheet" crossorigin href="/creator/assets/index-DTjdV1LF.css">
|
15
15
|
</head>
|
16
16
|
<body>
|
17
17
|
<div id="root"></div>
|
@@ -105,4 +105,9 @@ type TAddInteractivityInputs = {
|
|
105
105
|
current_syllabus: string;
|
106
106
|
};
|
107
107
|
export declare const addInteractivity: (token: string, inputs: TAddInteractivityInputs, webhookUrl?: string) => Promise<any>;
|
108
|
+
type TGenerateCodeChallengeInputs = {
|
109
|
+
lesson_content: string;
|
110
|
+
challenge_proposal: string;
|
111
|
+
};
|
112
|
+
export declare const generateCodeChallenge: (token: string, inputs: TGenerateCodeChallengeInputs, webhookUrl?: string) => Promise<any>;
|
108
113
|
export {};
|
package/lib/utils/rigoActions.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.addInteractivity = exports.initialContentGenerator = exports.getLanguageCodes = exports.isPackageAuthor = exports.fillSidebarJSON = exports.generateCourseShortName = exports.isValidRigoToken = exports.translateCourseMetadata = exports.createStructuredPreviewReadme = exports.readmeCreator = exports.createCodingReadme = exports.createCodeFile = exports.interactiveCreation = exports.generateCourseIntroduction = exports.translateExercise = exports.generateImage = exports.hasCreatorPermission = exports.createReadme = void 0;
|
3
|
+
exports.generateCodeChallenge = exports.addInteractivity = exports.initialContentGenerator = exports.getLanguageCodes = exports.isPackageAuthor = exports.fillSidebarJSON = exports.generateCourseShortName = exports.isValidRigoToken = exports.translateCourseMetadata = exports.createStructuredPreviewReadme = exports.readmeCreator = exports.createCodingReadme = exports.createCodeFile = exports.interactiveCreation = exports.generateCourseIntroduction = exports.translateExercise = exports.generateImage = exports.hasCreatorPermission = exports.createReadme = void 0;
|
4
4
|
exports.downloadImage = downloadImage;
|
5
5
|
exports.createPreviewReadme = createPreviewReadme;
|
6
6
|
exports.makeReadmeReadable = makeReadmeReadable;
|
@@ -325,3 +325,24 @@ const addInteractivity = async (token, inputs, webhookUrl) => {
|
|
325
325
|
}
|
326
326
|
};
|
327
327
|
exports.addInteractivity = addInteractivity;
|
328
|
+
const generateCodeChallenge = async (token, inputs, webhookUrl) => {
|
329
|
+
try {
|
330
|
+
const response = await axios_1.default.post(`${api_1.RIGOBOT_HOST}/v1/prompting/completion/generate-code-challenge-files/`, {
|
331
|
+
inputs,
|
332
|
+
include_purpose_objective: false,
|
333
|
+
execute_async: !!webhookUrl,
|
334
|
+
webhook_url: webhookUrl,
|
335
|
+
}, {
|
336
|
+
headers: {
|
337
|
+
"Content-Type": "application/json",
|
338
|
+
Authorization: "Token " + token.trim(),
|
339
|
+
},
|
340
|
+
});
|
341
|
+
return response.data;
|
342
|
+
}
|
343
|
+
catch (error) {
|
344
|
+
console.error("Error in generateCodeChallenge:", error);
|
345
|
+
return null;
|
346
|
+
}
|
347
|
+
};
|
348
|
+
exports.generateCodeChallenge = generateCodeChallenge;
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@learnpack/learnpack",
|
3
3
|
"description": "Seamlessly build, sell and/or take interactive & auto-graded tutorials, start learning now or build a new tutorial to your audience.",
|
4
|
-
"version": "5.0.
|
4
|
+
"version": "5.0.295",
|
5
5
|
"author": "Alejandro Sanchez @alesanchezr",
|
6
6
|
"contributors": [
|
7
7
|
{
|
package/src/commands/serve.ts
CHANGED
@@ -36,6 +36,7 @@ import {
|
|
36
36
|
getLanguageCodes,
|
37
37
|
initialContentGenerator,
|
38
38
|
addInteractivity,
|
39
|
+
generateCodeChallenge,
|
39
40
|
} from "../utils/rigoActions"
|
40
41
|
import * as dotenv from "dotenv"
|
41
42
|
|
@@ -994,6 +995,115 @@ export default class ServeCommand extends SessionCommand {
|
|
994
995
|
}
|
995
996
|
})
|
996
997
|
|
998
|
+
app.post(
|
999
|
+
"/webhooks/:courseSlug/:exercisePosition/process-coding-challege/",
|
1000
|
+
async (req, res) => {
|
1001
|
+
const { courseSlug, exercisePosition } = req.params
|
1002
|
+
const body = req.body
|
1003
|
+
|
1004
|
+
console.log(
|
1005
|
+
"RECEIVING CODING CHALLENGE WEBHOOK for course:",
|
1006
|
+
courseSlug,
|
1007
|
+
"exercise position:",
|
1008
|
+
exercisePosition
|
1009
|
+
)
|
1010
|
+
console.log("Webhook body:", JSON.stringify(body, null, 2))
|
1011
|
+
|
1012
|
+
try {
|
1013
|
+
if (body.status === "ERROR") {
|
1014
|
+
console.error(
|
1015
|
+
"❌ Error in coding challenge generation:",
|
1016
|
+
body.error
|
1017
|
+
)
|
1018
|
+
return res.json({ status: "ERROR" })
|
1019
|
+
}
|
1020
|
+
|
1021
|
+
// Parse the RigoBot response
|
1022
|
+
if (body.parsed) {
|
1023
|
+
const { files, reasoning } = body.parsed
|
1024
|
+
|
1025
|
+
console.log("📋 Reasoning:", reasoning)
|
1026
|
+
console.log("📁 Files received:", files)
|
1027
|
+
|
1028
|
+
if (files && Array.isArray(files)) {
|
1029
|
+
console.log("✅ Processing files for coding challenge...")
|
1030
|
+
|
1031
|
+
// Get the current exercise info to determine the exercise directory
|
1032
|
+
const syllabus = await getSyllabus(courseSlug, bucket)
|
1033
|
+
const exercise = syllabus.lessons[parseInt(exercisePosition)]
|
1034
|
+
|
1035
|
+
if (!exercise) {
|
1036
|
+
console.error(
|
1037
|
+
`❌ Exercise not found at position ${exercisePosition}`
|
1038
|
+
)
|
1039
|
+
return res.status(404).json({ error: "Exercise not found" })
|
1040
|
+
}
|
1041
|
+
|
1042
|
+
const exSlug = slugify(exercise.id + "-" + exercise.title)
|
1043
|
+
const exerciseDir = `courses/${courseSlug}/exercises/${exSlug}`
|
1044
|
+
|
1045
|
+
for (const fileStr of files) {
|
1046
|
+
try {
|
1047
|
+
const fileObj = JSON.parse(fileStr)
|
1048
|
+
console.log(`📄 Processing file: ${fileObj.name}`)
|
1049
|
+
|
1050
|
+
// Save the main file with content
|
1051
|
+
if (fileObj.name && fileObj.content) {
|
1052
|
+
const filePath = `${exerciseDir}/${fileObj.name}`
|
1053
|
+
// eslint-disable-next-line no-await-in-loop
|
1054
|
+
await uploadFileToBucket(bucket, fileObj.content, filePath)
|
1055
|
+
console.log(`✅ Saved file: ${filePath}`)
|
1056
|
+
}
|
1057
|
+
|
1058
|
+
// Save the solution file if it exists
|
1059
|
+
if (fileObj.name && fileObj.solution) {
|
1060
|
+
const nameParts = fileObj.name.split(".")
|
1061
|
+
if (nameParts.length > 1) {
|
1062
|
+
const extension = nameParts.pop()
|
1063
|
+
const baseName = nameParts.join(".")
|
1064
|
+
const solutionFileName = `${baseName}.solution.hide.${extension}`
|
1065
|
+
const solutionFilePath = `${exerciseDir}/${solutionFileName}`
|
1066
|
+
// eslint-disable-next-line no-await-in-loop
|
1067
|
+
await uploadFileToBucket(
|
1068
|
+
bucket,
|
1069
|
+
fileObj.solution,
|
1070
|
+
solutionFilePath
|
1071
|
+
)
|
1072
|
+
console.log(
|
1073
|
+
`✅ Saved solution file: ${solutionFilePath}`
|
1074
|
+
)
|
1075
|
+
} else {
|
1076
|
+
// If no extension, just add .solution.hide
|
1077
|
+
const solutionFileName = `${fileObj.name}.solution.hide`
|
1078
|
+
const solutionFilePath = `${exerciseDir}/${solutionFileName}`
|
1079
|
+
// eslint-disable-next-line no-await-in-loop
|
1080
|
+
await uploadFileToBucket(
|
1081
|
+
bucket,
|
1082
|
+
fileObj.solution,
|
1083
|
+
solutionFilePath
|
1084
|
+
)
|
1085
|
+
console.log(
|
1086
|
+
`✅ Saved solution file: ${solutionFilePath}`
|
1087
|
+
)
|
1088
|
+
}
|
1089
|
+
}
|
1090
|
+
} catch (parseError) {
|
1091
|
+
console.error(`❌ Error parsing file:`, parseError)
|
1092
|
+
}
|
1093
|
+
}
|
1094
|
+
|
1095
|
+
console.log("✅ All coding challenge files saved successfully")
|
1096
|
+
}
|
1097
|
+
}
|
1098
|
+
|
1099
|
+
res.json({ status: "SUCCESS" })
|
1100
|
+
} catch (error) {
|
1101
|
+
console.error("❌ Error processing coding challenge webhook:", error)
|
1102
|
+
res.status(500).json({ error: (error as Error).message })
|
1103
|
+
}
|
1104
|
+
}
|
1105
|
+
)
|
1106
|
+
|
997
1107
|
app.post(
|
998
1108
|
"/actions/continue-generating/:courseSlug/:position",
|
999
1109
|
async (req, res) => {
|
@@ -1090,6 +1200,80 @@ export default class ServeCommand extends SessionCommand {
|
|
1090
1200
|
res.json({ status: "QUEUED" })
|
1091
1201
|
})
|
1092
1202
|
|
1203
|
+
app.post("/actions/generate-code-challenge", async (req, res) => {
|
1204
|
+
const rigoToken = req.header("x-rigo-token")
|
1205
|
+
const { code_challenge, lesson_content, exercise_position, course_slug } =
|
1206
|
+
req.body
|
1207
|
+
|
1208
|
+
if (!rigoToken) {
|
1209
|
+
return res.status(400).json({
|
1210
|
+
error: "Rigo token is required. x-rigo-token header is missing",
|
1211
|
+
})
|
1212
|
+
}
|
1213
|
+
|
1214
|
+
if (!code_challenge) {
|
1215
|
+
return res.status(400).json({
|
1216
|
+
error: "code_challenge is required",
|
1217
|
+
})
|
1218
|
+
}
|
1219
|
+
|
1220
|
+
if (!lesson_content) {
|
1221
|
+
return res.status(400).json({
|
1222
|
+
error: "lesson_content is required",
|
1223
|
+
})
|
1224
|
+
}
|
1225
|
+
|
1226
|
+
if (!course_slug) {
|
1227
|
+
return res.status(400).json({
|
1228
|
+
error: "course_slug is required",
|
1229
|
+
})
|
1230
|
+
}
|
1231
|
+
|
1232
|
+
if (exercise_position === undefined || exercise_position === null) {
|
1233
|
+
return res.status(400).json({
|
1234
|
+
error: "exercise_position is required",
|
1235
|
+
})
|
1236
|
+
}
|
1237
|
+
|
1238
|
+
if (typeof exercise_position !== "number" || exercise_position < 0) {
|
1239
|
+
return res.status(400).json({
|
1240
|
+
error: "exercise_position must be a valid number >= 0",
|
1241
|
+
})
|
1242
|
+
}
|
1243
|
+
|
1244
|
+
try {
|
1245
|
+
const webhookUrl = `${process.env.HOST}/webhooks/${course_slug}/${exercise_position}/process-coding-challege/`
|
1246
|
+
|
1247
|
+
const result = await generateCodeChallenge(
|
1248
|
+
rigoToken,
|
1249
|
+
{
|
1250
|
+
lesson_content,
|
1251
|
+
challenge_proposal: code_challenge,
|
1252
|
+
},
|
1253
|
+
webhookUrl
|
1254
|
+
)
|
1255
|
+
|
1256
|
+
if (!result) {
|
1257
|
+
return res.status(500).json({
|
1258
|
+
error: "Failed to generate code challenge",
|
1259
|
+
})
|
1260
|
+
}
|
1261
|
+
|
1262
|
+
console.log("Code challenge generation started:", result)
|
1263
|
+
res.json({
|
1264
|
+
status: "QUEUED",
|
1265
|
+
id: result.id,
|
1266
|
+
message: "Code challenge generation started",
|
1267
|
+
})
|
1268
|
+
} catch (error) {
|
1269
|
+
console.error("Error generating code challenge:", error)
|
1270
|
+
res.status(500).json({
|
1271
|
+
error: "Failed to start code challenge generation",
|
1272
|
+
details: (error as Error).message,
|
1273
|
+
})
|
1274
|
+
}
|
1275
|
+
})
|
1276
|
+
|
1093
1277
|
app.post(
|
1094
1278
|
"/webhooks/:courseSlug/exercise-processor/:lessonID/:rigoToken",
|
1095
1279
|
async (req, res) => {
|
@@ -18,6 +18,7 @@
|
|
18
18
|
"i18next": "^25.2.1",
|
19
19
|
"i18next-browser-languagedetector": "^8.2.0",
|
20
20
|
"js-yaml": "^4.1.0",
|
21
|
+
"lucide-react": "^0.545.0",
|
21
22
|
"mammoth": "^1.9.0",
|
22
23
|
"mitt": "^3.0.1",
|
23
24
|
"pdfjs-dist": "^5.1.91",
|
@@ -4348,6 +4349,15 @@
|
|
4348
4349
|
"underscore": "^1.13.1"
|
4349
4350
|
}
|
4350
4351
|
},
|
4352
|
+
"node_modules/lucide-react": {
|
4353
|
+
"version": "0.545.0",
|
4354
|
+
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.545.0.tgz",
|
4355
|
+
"integrity": "sha512-7r1/yUuflQDSt4f1bpn5ZAocyIxcTyVyBBChSVtBKn5M+392cPmI5YJMWOJKk/HUWGm5wg83chlAZtCcGbEZtw==",
|
4356
|
+
"license": "ISC",
|
4357
|
+
"peerDependencies": {
|
4358
|
+
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
4359
|
+
}
|
4360
|
+
},
|
4351
4361
|
"node_modules/mammoth": {
|
4352
4362
|
"version": "1.9.0",
|
4353
4363
|
"resolved": "https://registry.npmjs.org/mammoth/-/mammoth-1.9.0.tgz",
|
package/src/creator/package.json
CHANGED
@@ -0,0 +1,18 @@
|
|
1
|
+
import * as LucideIcons from "lucide-react";
|
2
|
+
import { LucideProps } from "lucide-react";
|
3
|
+
|
4
|
+
export interface IconProps extends Omit<LucideProps, 'ref'> {
|
5
|
+
name: keyof typeof LucideIcons;
|
6
|
+
}
|
7
|
+
|
8
|
+
export const Icon = ({ name, ...props }: IconProps) => {
|
9
|
+
const LucideIcon = LucideIcons[name] as React.ComponentType<LucideProps>;
|
10
|
+
|
11
|
+
if (!LucideIcon) {
|
12
|
+
console.warn(`Icon "${name}" not found in lucide-react`);
|
13
|
+
return null;
|
14
|
+
}
|
15
|
+
|
16
|
+
return <LucideIcon {...props} />;
|
17
|
+
};
|
18
|
+
|
@@ -5,6 +5,7 @@ import { useState } from "react"
|
|
5
5
|
import { useRef } from "react"
|
6
6
|
import { SVGS } from "../assets/svgs"
|
7
7
|
import useStore from "../utils/store"
|
8
|
+
import { Icon } from "./Icon"
|
8
9
|
|
9
10
|
export interface Lesson {
|
10
11
|
id: string
|
@@ -60,20 +61,13 @@ export const LessonItem: React.FC<LessonItemProps> = ({
|
|
60
61
|
<div
|
61
62
|
className={`flex items-center space-x-2 relative rounded-md p-3 ${
|
62
63
|
isNew ? "border border-learnpack-blue appear" : "border border-gray-200"
|
63
|
-
} ${hasDecimalPart(lesson.id.toString() || "0") ? "ml-6" : ""}
|
64
|
-
lesson.locked ? "bg-yellow-50 border-yellow-300" : ""
|
65
|
-
}`}
|
64
|
+
} ${hasDecimalPart(lesson.id.toString() || "0") ? "ml-6" : ""}`}
|
66
65
|
>
|
67
66
|
{isNew && <span className="yellow-ball"></span>}
|
68
67
|
|
69
68
|
{mode === "teacher" && (
|
70
69
|
<>
|
71
70
|
<span className="index-circle">{cleanFloatString(lesson.id)}</span>
|
72
|
-
{lesson.locked && (
|
73
|
-
<span className="text-yellow-600 text-sm" title="Lesson is locked">
|
74
|
-
🔒
|
75
|
-
</span>
|
76
|
-
)}
|
77
71
|
</>
|
78
72
|
)}
|
79
73
|
|
@@ -111,7 +105,10 @@ export const LessonItem: React.FC<LessonItemProps> = ({
|
|
111
105
|
}`}
|
112
106
|
title={lesson.locked ? "Unlock lesson" : "Lock lesson"}
|
113
107
|
>
|
114
|
-
|
108
|
+
<Icon
|
109
|
+
name={lesson.locked ? "Lock" : "Unlock"}
|
110
|
+
size={16}
|
111
|
+
/>
|
115
112
|
</button>
|
116
113
|
<button
|
117
114
|
onClick={() => {
|
@@ -130,7 +127,7 @@ export const LessonItem: React.FC<LessonItemProps> = ({
|
|
130
127
|
disabled={lesson.locked}
|
131
128
|
title={lesson.locked ? "Cannot edit locked lesson" : "Edit lesson"}
|
132
129
|
>
|
133
|
-
{
|
130
|
+
<Icon name="Edit" size={16} />
|
134
131
|
</button>
|
135
132
|
<button
|
136
133
|
onClick={() => {
|