@learnpack/learnpack 5.0.361 → 5.0.363

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 (41) hide show
  1. package/lib/commands/serve.js +44 -7
  2. package/lib/creatorDist/assets/{index-B7s5Nmq8.css → index-Cco3XhDR.css} +96 -0
  3. package/lib/creatorDist/assets/{index-CqVEKiJR.js → index-o3Cw0bsF.js} +69682 -66808
  4. package/lib/creatorDist/index.html +2 -2
  5. package/lib/utils/api.d.ts +0 -9
  6. package/lib/utils/api.js +1 -65
  7. package/lib/utils/lessonRecovery.d.ts +18 -0
  8. package/lib/utils/lessonRecovery.js +31 -0
  9. package/package.json +1 -1
  10. package/src/commands/serve.ts +65 -18
  11. package/src/creator/package-lock.json +297 -3
  12. package/src/creator/package.json +1 -0
  13. package/src/creator/src/App.test.tsx +124 -1
  14. package/src/creator/src/App.tsx +52 -36
  15. package/src/creator/src/components/FileCard.tsx +63 -19
  16. package/src/creator/src/components/FileReadListener.test.tsx +128 -0
  17. package/src/creator/src/components/FileReadListener.tsx +75 -0
  18. package/src/creator/src/components/FileReviewModal.test.tsx +193 -0
  19. package/src/creator/src/components/FileReviewModal.tsx +128 -0
  20. package/src/creator/src/components/FileUploader.test.tsx +229 -0
  21. package/src/creator/src/components/FileUploader.tsx +212 -58
  22. package/src/creator/src/components/GenerationErrorCard.tsx +57 -0
  23. package/src/creator/src/components/MarkdownRenderer.tsx +4 -2
  24. package/src/creator/src/components/syllabus/SyllabusEditor.tsx +6 -9
  25. package/src/creator/src/index.css +45 -0
  26. package/src/creator/src/locales/en.json +27 -5
  27. package/src/creator/src/locales/es.json +27 -5
  28. package/src/creator/src/main.tsx +2 -0
  29. package/src/creator/src/utils/completionResult.test.ts +61 -0
  30. package/src/creator/src/utils/completionResult.ts +61 -2
  31. package/src/creator/src/utils/courseLanguage.test.ts +6 -0
  32. package/src/creator/src/utils/creationRequest.test.ts +53 -18
  33. package/src/creator/src/utils/creationRequest.ts +17 -12
  34. package/src/creator/src/utils/fileRead.ts +60 -0
  35. package/src/creator/src/utils/lib.ts +0 -5
  36. package/src/creator/src/utils/store.ts +0 -10
  37. package/src/creatorDist/assets/{index-B7s5Nmq8.css → index-Cco3XhDR.css} +96 -0
  38. package/src/creatorDist/assets/{index-CqVEKiJR.js → index-o3Cw0bsF.js} +69682 -66808
  39. package/src/creatorDist/index.html +2 -2
  40. package/src/utils/api.ts +0 -83
  41. package/src/utils/lessonRecovery.ts +32 -0
@@ -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-CqVEKiJR.js"></script>
14
- <link rel="stylesheet" crossorigin href="/creator/assets/index-B7s5Nmq8.css">
13
+ <script type="module" crossorigin src="/creator/assets/index-o3Cw0bsF.js"></script>
14
+ <link rel="stylesheet" crossorigin href="/creator/assets/index-Cco3XhDR.css">
15
15
  </head>
16
16
  <body>
17
17
  <div id="root"></div>
@@ -110,13 +110,6 @@ export declare const getAssetById: (token: string, assetId: number) => Promise<A
110
110
  * @returns `found` with the asset, `missing` on 404, `error` when it could not be checked.
111
111
  */
112
112
  export declare const getAssetBySlug: (token: string, assetSlug: string) => Promise<AssetLookup>;
113
- type TTechnology = {
114
- slug: string;
115
- lang: string;
116
- };
117
- export declare const fetchTechnologies: () => Promise<any[]>;
118
- declare function updateTechnologiesPeriodically(): Promise<void>;
119
- export declare const getCurrentTechnologies: () => TTechnology[];
120
113
  declare const _default: {
121
114
  login: (identification: string, password: string) => Promise<any>;
122
115
  publish: (config: any) => Promise<any>;
@@ -144,7 +137,5 @@ declare const _default: {
144
137
  asset_ids?: number[];
145
138
  }) => Promise<any>;
146
139
  createRigoPackage: (token: string, slug: string, config: any) => Promise<any>;
147
- getCurrentTechnologies: () => TTechnology[];
148
- updateTechnologiesPeriodically: typeof updateTechnologiesPeriodically;
149
140
  };
150
141
  export default _default;
package/lib/utils/api.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getCurrentTechnologies = exports.fetchTechnologies = exports.getAssetBySlug = exports.getAssetById = exports.createAsset = exports.validateToken = exports.listUserAcademies = exports.getConsumable = exports.countConsumables = exports.RIGOBOT_REALTIME_HOST = exports.RIGOBOT_HOST = exports.BREATHECODE_EVENTS_URL = exports.BREATHECODE_TELEMETRY_URL = exports.BREATHECODE_HOST = void 0;
3
+ exports.getAssetBySlug = exports.getAssetById = exports.createAsset = exports.validateToken = exports.listUserAcademies = exports.getConsumable = exports.countConsumables = exports.RIGOBOT_REALTIME_HOST = exports.RIGOBOT_HOST = exports.BREATHECODE_EVENTS_URL = exports.BREATHECODE_TELEMETRY_URL = exports.BREATHECODE_HOST = void 0;
4
4
  exports.describeRequestError = describeRequestError;
5
5
  exports.getRigoPackage = getRigoPackage;
6
6
  const console_1 = require("../utils/console");
@@ -592,68 +592,6 @@ const createRigoPackage = async (token, slug, config) => {
592
592
  throw error;
593
593
  }
594
594
  };
595
- let technologiesCache = [];
596
- /**
597
- * Strip .env comments (e.g. "token # comment") so the token is sent without spaces.
598
- * @param token - Raw token value from env, possibly with trailing comment.
599
- * @returns Trimmed token with inline `#` comments removed, or empty string if missing.
600
- */
601
- function sanitizeToken(token) {
602
- if (!token)
603
- return "";
604
- const trimmed = token.trim();
605
- const beforeComment = trimmed.split(/\s+#/)[0].trim();
606
- return beforeComment;
607
- }
608
- const fetchTechnologies = async () => {
609
- const rawToken = process.env.BREATHECODE_PERMANENT_TOKEN;
610
- const BREATHECODE_PERMANENT_TOKEN = sanitizeToken(rawToken);
611
- const LANGS = ["en", "es"];
612
- if (!BREATHECODE_PERMANENT_TOKEN) {
613
- throw new Error("BREATHECODE_PERMANENT_TOKEN is not defined in environment variables");
614
- }
615
- const headers = {
616
- Authorization: `Token ${BREATHECODE_PERMANENT_TOKEN}`,
617
- };
618
- const results = await Promise.all(LANGS.map(lang => axios_1.default
619
- .get(`${HOST}/v1/registry/technology?lang=${lang}`, { headers })
620
- .then(res => {
621
- return res.data;
622
- })
623
- .then(data => data.map((item) => ({
624
- slug: item.slug,
625
- lang: lang,
626
- })))));
627
- const allItems = results.flat();
628
- // Remove duplicates by slug+lang combination
629
- const unique = [];
630
- const seen = new Set();
631
- for (const item of allItems) {
632
- const key = `${item.slug}:${item.lang}`;
633
- if (!seen.has(key)) {
634
- seen.add(key);
635
- unique.push(item);
636
- }
637
- }
638
- return unique;
639
- };
640
- exports.fetchTechnologies = fetchTechnologies;
641
- // Function to update the cache and schedule the next update
642
- async function updateTechnologiesPeriodically() {
643
- try {
644
- technologiesCache = await (0, exports.fetchTechnologies)();
645
- // Uncomment for debugging:
646
- // console.log('Technologies list updated:', technologiesCache);
647
- }
648
- catch (error) {
649
- console.error("Error updating technologies list:", error);
650
- }
651
- finally {
652
- setTimeout(updateTechnologiesPeriodically, 24 * 60 * 60 * 1000);
653
- }
654
- }
655
- const getCurrentTechnologies = () => technologiesCache;
656
- exports.getCurrentTechnologies = getCurrentTechnologies;
657
595
  exports.default = {
658
596
  login,
659
597
  publish,
@@ -673,6 +611,4 @@ exports.default = {
673
611
  getCategories,
674
612
  updateRigoPackage,
675
613
  createRigoPackage,
676
- getCurrentTechnologies: exports.getCurrentTechnologies,
677
- updateTechnologiesPeriodically,
678
614
  };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Whether a lesson README holds generated content. The file starts as a
3
+ * `loader` block, and the generated content replaces it.
4
+ * @param readme - The README text, or null when the file does not exist.
5
+ * @returns True when the lesson has its generated content.
6
+ */
7
+ export declare const hasGeneratedContent: (readme: string | null) => boolean;
8
+ /**
9
+ * Finds the lessons marked as ERROR that were generated anyway. Publishing and
10
+ * translating wait for every lesson, and the retry screen only shows while the
11
+ * README has its `loader`, so these lessons would otherwise stay stuck.
12
+ * @param lessons - The lessons of the syllabus.
13
+ * @param readReadme - Reads the README of a lesson, or null if it has none.
14
+ * @returns The lessons to mark as done.
15
+ */
16
+ export declare const findFinishedLessons: <T extends {
17
+ status?: string;
18
+ }>(lessons: T[], readReadme: (lesson: T) => Promise<string | null>) => Promise<T[]>;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findFinishedLessons = exports.hasGeneratedContent = void 0;
4
+ /**
5
+ * Whether a lesson README holds generated content. The file starts as a
6
+ * `loader` block, and the generated content replaces it.
7
+ * @param readme - The README text, or null when the file does not exist.
8
+ * @returns True when the lesson has its generated content.
9
+ */
10
+ const hasGeneratedContent = (readme) => readme !== null && readme.trim() !== "" && !readme.includes("```loader");
11
+ exports.hasGeneratedContent = hasGeneratedContent;
12
+ /**
13
+ * Finds the lessons marked as ERROR that were generated anyway. Publishing and
14
+ * translating wait for every lesson, and the retry screen only shows while the
15
+ * README has its `loader`, so these lessons would otherwise stay stuck.
16
+ * @param lessons - The lessons of the syllabus.
17
+ * @param readReadme - Reads the README of a lesson, or null if it has none.
18
+ * @returns The lessons to mark as done.
19
+ */
20
+ const findFinishedLessons = async (lessons, readReadme) => {
21
+ const finished = [];
22
+ for (const lesson of lessons) {
23
+ if (lesson.status !== "ERROR")
24
+ continue;
25
+ // eslint-disable-next-line no-await-in-loop
26
+ if ((0, exports.hasGeneratedContent)(await readReadme(lesson)))
27
+ finished.push(lesson);
28
+ }
29
+ return finished;
30
+ };
31
+ exports.findFinishedLessons = findFinishedLessons;
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.361",
4
+ "version": "5.0.363",
5
5
  "author": "Alejandro Sanchez @alesanchezr",
6
6
  "contributors": [
7
7
  {
@@ -48,6 +48,7 @@ import {
48
48
  getReadmeExtension,
49
49
  insertStepInCorrectPosition,
50
50
  } from "../utils/creatorUtilities"
51
+ import { findFinishedLessons } from "../utils/lessonRecovery"
51
52
  // import { handleAssetCreation } from "./publish"
52
53
  import axios from "axios"
53
54
  import * as FormData from "form-data"
@@ -680,6 +681,10 @@ async function startInitialContentGeneration(
680
681
  )
681
682
 
682
683
  console.log("INITIAL CONTENT GENERATOR RES", res)
684
+ if (!res) {
685
+ throw new Error(`Could not start generating lesson ${exSlug}`)
686
+ }
687
+
683
688
  return res.id
684
689
  }
685
690
 
@@ -1020,6 +1025,45 @@ async function updateLessonStatusToDone(
1020
1025
  }
1021
1026
  }
1022
1027
 
1028
+ const lessonSlug = (lesson: Lesson) => slugify(lesson.id + "-" + lesson.title)
1029
+
1030
+ async function repairFinishedLessons(
1031
+ courseSlug: string,
1032
+ syllabus: Syllabus,
1033
+ bucket: Bucket
1034
+ ): Promise<void> {
1035
+ const language = syllabus.courseInfo.language || "en"
1036
+
1037
+ try {
1038
+ const finished = await findFinishedLessons(
1039
+ syllabus.lessons ?? [],
1040
+ async lesson => {
1041
+ const readme = bucket.file(
1042
+ `courses/${courseSlug}/exercises/${lessonSlug(
1043
+ lesson
1044
+ )}/README${getReadmeExtension(language)}`
1045
+ )
1046
+ const [exists] = await readme.exists()
1047
+ if (!exists) return null
1048
+
1049
+ const [content] = await readme.download()
1050
+ return content.toString()
1051
+ }
1052
+ )
1053
+
1054
+ for (const lesson of finished) {
1055
+ // eslint-disable-next-line no-await-in-loop
1056
+ await updateLessonStatusToDone(courseSlug, lessonSlug(lesson), bucket)
1057
+ lesson.status = "DONE"
1058
+ lesson.generated = true
1059
+ }
1060
+ } catch (error) {
1061
+ // Loading the course matters more than repairing it now. The next load
1062
+ // tries again.
1063
+ console.error(`Error repairing lessons of ${courseSlug}:`, error)
1064
+ }
1065
+ }
1066
+
1023
1067
  async function continueWithNextLesson(
1024
1068
  courseSlug: string,
1025
1069
  currentExerciseIndex: number,
@@ -1043,15 +1087,26 @@ async function continueWithNextLesson(
1043
1087
  feedback = `\n\nThe user added the following feedback with relation to the previous generations: ${syllabus.feedback}`
1044
1088
  }
1045
1089
 
1046
- nextCompletionId = await startInitialContentGeneration(
1047
- rigoToken,
1048
- syllabus.lessons,
1049
- syllabus.courseInfo,
1050
- nextExercise,
1051
- courseSlug,
1052
- syllabus.courseInfo.purpose,
1053
- finalContent + "\n\n" + feedback
1054
- )
1090
+ try {
1091
+ nextCompletionId = await startInitialContentGeneration(
1092
+ rigoToken,
1093
+ syllabus.lessons,
1094
+ syllabus.courseInfo,
1095
+ nextExercise,
1096
+ courseSlug,
1097
+ syllabus.courseInfo.purpose,
1098
+ finalContent + "\n\n" + feedback
1099
+ )
1100
+ } catch (error) {
1101
+ // The current lesson is finished either way. The next one stays PENDING,
1102
+ // and its screen offers to continue.
1103
+ console.error("Error starting the next lesson:", error)
1104
+ emitToCourse(courseSlug, "course-creation", {
1105
+ lesson: slugify(nextExercise.id + "-" + nextExercise.title),
1106
+ status: "error",
1107
+ log: `❌ Could not start lesson ${nextExercise.title}`,
1108
+ })
1109
+ }
1055
1110
  } else {
1056
1111
  console.log(
1057
1112
  "Stopping generation process at",
@@ -1537,8 +1592,6 @@ class ServeCommand extends SessionCommand {
1537
1592
  process.exit(1)
1538
1593
  }
1539
1594
 
1540
- await api.updateTechnologiesPeriodically()
1541
-
1542
1595
  const credentials = JSON.parse(crendsEnv)
1543
1596
  const bucketStorage = new Storage({
1544
1597
  credentials,
@@ -5079,13 +5132,6 @@ class ServeCommand extends SessionCommand {
5079
5132
  }
5080
5133
  })
5081
5134
 
5082
- app.get("/technologies", async (req, res) => {
5083
- console.log("GET /technologies")
5084
-
5085
- const technologies = await api.getCurrentTechnologies()
5086
- res.json(technologies)
5087
- })
5088
-
5089
5135
  app.get("/assets/:file", (req, res) => {
5090
5136
  const file = path.join(localAppPath, req.params.file)
5091
5137
  res.sendFile(file)
@@ -5239,6 +5285,7 @@ class ServeCommand extends SessionCommand {
5239
5285
  const [content] = await syllabus.download()
5240
5286
  const syllabusJson: Syllabus = JSON.parse(content.toString())
5241
5287
  sanitizeSyllabusFromUnknown(syllabusJson)
5288
+ await repairFinishedLessons(courseSlug, syllabusJson, bucket)
5242
5289
  res.json(syllabusJson)
5243
5290
  } catch (error) {
5244
5291
  console.error("Error getting syllabus:", error)
@@ -30,6 +30,7 @@
30
30
  "react-i18next": "^15.5.2",
31
31
  "react-markdown": "^10.1.0",
32
32
  "react-router": "^7.5.0",
33
+ "remark-gfm": "^4.0.1",
33
34
  "socket.io-client": "^4.8.1",
34
35
  "syllable": "^5.0.1",
35
36
  "tailwind-merge": "^3.3.1",
@@ -5622,6 +5623,16 @@
5622
5623
  "sprintf-js": "~1.0.2"
5623
5624
  }
5624
5625
  },
5626
+ "node_modules/markdown-table": {
5627
+ "version": "3.0.4",
5628
+ "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz",
5629
+ "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==",
5630
+ "license": "MIT",
5631
+ "funding": {
5632
+ "type": "github",
5633
+ "url": "https://github.com/sponsors/wooorm"
5634
+ }
5635
+ },
5625
5636
  "node_modules/math-intrinsics": {
5626
5637
  "version": "1.1.0",
5627
5638
  "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -5631,6 +5642,34 @@
5631
5642
  "node": ">= 0.4"
5632
5643
  }
5633
5644
  },
5645
+ "node_modules/mdast-util-find-and-replace": {
5646
+ "version": "3.0.4",
5647
+ "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.4.tgz",
5648
+ "integrity": "sha512-z4B5c6jrnJ1ltOQemhWYPBSAo6y42Z8Gt2yEgfgNTMcLSU59B8bfj8Z8AnAlJrBGLnqkdAUZ7jdDEqQw3KuipQ==",
5649
+ "license": "MIT",
5650
+ "dependencies": {
5651
+ "@types/mdast": "^4.0.0",
5652
+ "escape-string-regexp": "^5.0.0",
5653
+ "unist-util-is": "^6.0.0",
5654
+ "unist-util-visit-parents": "^6.1.0"
5655
+ },
5656
+ "funding": {
5657
+ "type": "opencollective",
5658
+ "url": "https://opencollective.com/unified"
5659
+ }
5660
+ },
5661
+ "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": {
5662
+ "version": "5.0.0",
5663
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
5664
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
5665
+ "license": "MIT",
5666
+ "engines": {
5667
+ "node": ">=12"
5668
+ },
5669
+ "funding": {
5670
+ "url": "https://github.com/sponsors/sindresorhus"
5671
+ }
5672
+ },
5634
5673
  "node_modules/mdast-util-from-markdown": {
5635
5674
  "version": "2.0.2",
5636
5675
  "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
@@ -5655,6 +5694,107 @@
5655
5694
  "url": "https://opencollective.com/unified"
5656
5695
  }
5657
5696
  },
5697
+ "node_modules/mdast-util-gfm": {
5698
+ "version": "3.1.0",
5699
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz",
5700
+ "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==",
5701
+ "license": "MIT",
5702
+ "dependencies": {
5703
+ "mdast-util-from-markdown": "^2.0.0",
5704
+ "mdast-util-gfm-autolink-literal": "^2.0.0",
5705
+ "mdast-util-gfm-footnote": "^2.0.0",
5706
+ "mdast-util-gfm-strikethrough": "^2.0.0",
5707
+ "mdast-util-gfm-table": "^2.0.0",
5708
+ "mdast-util-gfm-task-list-item": "^2.0.0",
5709
+ "mdast-util-to-markdown": "^2.0.0"
5710
+ },
5711
+ "funding": {
5712
+ "type": "opencollective",
5713
+ "url": "https://opencollective.com/unified"
5714
+ }
5715
+ },
5716
+ "node_modules/mdast-util-gfm-autolink-literal": {
5717
+ "version": "2.0.1",
5718
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz",
5719
+ "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==",
5720
+ "license": "MIT",
5721
+ "dependencies": {
5722
+ "@types/mdast": "^4.0.0",
5723
+ "ccount": "^2.0.0",
5724
+ "devlop": "^1.0.0",
5725
+ "mdast-util-find-and-replace": "^3.0.0",
5726
+ "micromark-util-character": "^2.0.0"
5727
+ },
5728
+ "funding": {
5729
+ "type": "opencollective",
5730
+ "url": "https://opencollective.com/unified"
5731
+ }
5732
+ },
5733
+ "node_modules/mdast-util-gfm-footnote": {
5734
+ "version": "2.1.0",
5735
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz",
5736
+ "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==",
5737
+ "license": "MIT",
5738
+ "dependencies": {
5739
+ "@types/mdast": "^4.0.0",
5740
+ "devlop": "^1.1.0",
5741
+ "mdast-util-from-markdown": "^2.0.0",
5742
+ "mdast-util-to-markdown": "^2.0.0",
5743
+ "micromark-util-normalize-identifier": "^2.0.0"
5744
+ },
5745
+ "funding": {
5746
+ "type": "opencollective",
5747
+ "url": "https://opencollective.com/unified"
5748
+ }
5749
+ },
5750
+ "node_modules/mdast-util-gfm-strikethrough": {
5751
+ "version": "2.0.1",
5752
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.1.tgz",
5753
+ "integrity": "sha512-OuJHqvr455pwu2OaOrir7dbzqs4jlWKOtlu9L6GjcIjjQwNyw02VntQsr/Ek6/A13vgnXfUhBekWRUP1OkNivw==",
5754
+ "license": "MIT",
5755
+ "dependencies": {
5756
+ "@types/mdast": "^4.0.0",
5757
+ "mdast-util-from-markdown": "^2.0.0",
5758
+ "mdast-util-to-markdown": "^2.0.0"
5759
+ },
5760
+ "funding": {
5761
+ "type": "opencollective",
5762
+ "url": "https://opencollective.com/unified"
5763
+ }
5764
+ },
5765
+ "node_modules/mdast-util-gfm-table": {
5766
+ "version": "2.0.0",
5767
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz",
5768
+ "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==",
5769
+ "license": "MIT",
5770
+ "dependencies": {
5771
+ "@types/mdast": "^4.0.0",
5772
+ "devlop": "^1.0.0",
5773
+ "markdown-table": "^3.0.0",
5774
+ "mdast-util-from-markdown": "^2.0.0",
5775
+ "mdast-util-to-markdown": "^2.0.0"
5776
+ },
5777
+ "funding": {
5778
+ "type": "opencollective",
5779
+ "url": "https://opencollective.com/unified"
5780
+ }
5781
+ },
5782
+ "node_modules/mdast-util-gfm-task-list-item": {
5783
+ "version": "2.0.0",
5784
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz",
5785
+ "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==",
5786
+ "license": "MIT",
5787
+ "dependencies": {
5788
+ "@types/mdast": "^4.0.0",
5789
+ "devlop": "^1.0.0",
5790
+ "mdast-util-from-markdown": "^2.0.0",
5791
+ "mdast-util-to-markdown": "^2.0.0"
5792
+ },
5793
+ "funding": {
5794
+ "type": "opencollective",
5795
+ "url": "https://opencollective.com/unified"
5796
+ }
5797
+ },
5658
5798
  "node_modules/mdast-util-mdx-expression": {
5659
5799
  "version": "2.0.1",
5660
5800
  "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
@@ -5870,6 +6010,127 @@
5870
6010
  "micromark-util-types": "^2.0.0"
5871
6011
  }
5872
6012
  },
6013
+ "node_modules/micromark-extension-gfm": {
6014
+ "version": "3.0.0",
6015
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz",
6016
+ "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==",
6017
+ "license": "MIT",
6018
+ "dependencies": {
6019
+ "micromark-extension-gfm-autolink-literal": "^2.0.0",
6020
+ "micromark-extension-gfm-footnote": "^2.0.0",
6021
+ "micromark-extension-gfm-strikethrough": "^2.0.0",
6022
+ "micromark-extension-gfm-table": "^2.0.0",
6023
+ "micromark-extension-gfm-tagfilter": "^2.0.0",
6024
+ "micromark-extension-gfm-task-list-item": "^2.0.0",
6025
+ "micromark-util-combine-extensions": "^2.0.0",
6026
+ "micromark-util-types": "^2.0.0"
6027
+ },
6028
+ "funding": {
6029
+ "type": "opencollective",
6030
+ "url": "https://opencollective.com/unified"
6031
+ }
6032
+ },
6033
+ "node_modules/micromark-extension-gfm-autolink-literal": {
6034
+ "version": "2.1.0",
6035
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
6036
+ "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
6037
+ "license": "MIT",
6038
+ "dependencies": {
6039
+ "micromark-util-character": "^2.0.0",
6040
+ "micromark-util-sanitize-uri": "^2.0.0",
6041
+ "micromark-util-symbol": "^2.0.0",
6042
+ "micromark-util-types": "^2.0.0"
6043
+ },
6044
+ "funding": {
6045
+ "type": "opencollective",
6046
+ "url": "https://opencollective.com/unified"
6047
+ }
6048
+ },
6049
+ "node_modules/micromark-extension-gfm-footnote": {
6050
+ "version": "2.1.0",
6051
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
6052
+ "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
6053
+ "license": "MIT",
6054
+ "dependencies": {
6055
+ "devlop": "^1.0.0",
6056
+ "micromark-core-commonmark": "^2.0.0",
6057
+ "micromark-factory-space": "^2.0.0",
6058
+ "micromark-util-character": "^2.0.0",
6059
+ "micromark-util-normalize-identifier": "^2.0.0",
6060
+ "micromark-util-sanitize-uri": "^2.0.0",
6061
+ "micromark-util-symbol": "^2.0.0",
6062
+ "micromark-util-types": "^2.0.0"
6063
+ },
6064
+ "funding": {
6065
+ "type": "opencollective",
6066
+ "url": "https://opencollective.com/unified"
6067
+ }
6068
+ },
6069
+ "node_modules/micromark-extension-gfm-strikethrough": {
6070
+ "version": "2.1.0",
6071
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz",
6072
+ "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==",
6073
+ "license": "MIT",
6074
+ "dependencies": {
6075
+ "devlop": "^1.0.0",
6076
+ "micromark-util-chunked": "^2.0.0",
6077
+ "micromark-util-classify-character": "^2.0.0",
6078
+ "micromark-util-resolve-all": "^2.0.0",
6079
+ "micromark-util-symbol": "^2.0.0",
6080
+ "micromark-util-types": "^2.0.0"
6081
+ },
6082
+ "funding": {
6083
+ "type": "opencollective",
6084
+ "url": "https://opencollective.com/unified"
6085
+ }
6086
+ },
6087
+ "node_modules/micromark-extension-gfm-table": {
6088
+ "version": "2.1.2",
6089
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.2.tgz",
6090
+ "integrity": "sha512-pRzm4kDTu0MjlmBkxmS9yYhw60nncfcEwu9NNdPFSQEFXS95ZKyIIyTSHu/o3ReBUrLKYEq+7YaXCRn/bPB4MA==",
6091
+ "license": "MIT",
6092
+ "dependencies": {
6093
+ "devlop": "^1.0.0",
6094
+ "micromark-factory-space": "^2.0.0",
6095
+ "micromark-util-character": "^2.0.0",
6096
+ "micromark-util-symbol": "^2.0.0",
6097
+ "micromark-util-types": "^2.0.0"
6098
+ },
6099
+ "funding": {
6100
+ "type": "opencollective",
6101
+ "url": "https://opencollective.com/unified"
6102
+ }
6103
+ },
6104
+ "node_modules/micromark-extension-gfm-tagfilter": {
6105
+ "version": "2.0.0",
6106
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz",
6107
+ "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==",
6108
+ "license": "MIT",
6109
+ "dependencies": {
6110
+ "micromark-util-types": "^2.0.0"
6111
+ },
6112
+ "funding": {
6113
+ "type": "opencollective",
6114
+ "url": "https://opencollective.com/unified"
6115
+ }
6116
+ },
6117
+ "node_modules/micromark-extension-gfm-task-list-item": {
6118
+ "version": "2.1.0",
6119
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz",
6120
+ "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==",
6121
+ "license": "MIT",
6122
+ "dependencies": {
6123
+ "devlop": "^1.0.0",
6124
+ "micromark-factory-space": "^2.0.0",
6125
+ "micromark-util-character": "^2.0.0",
6126
+ "micromark-util-symbol": "^2.0.0",
6127
+ "micromark-util-types": "^2.0.0"
6128
+ },
6129
+ "funding": {
6130
+ "type": "opencollective",
6131
+ "url": "https://opencollective.com/unified"
6132
+ }
6133
+ },
5873
6134
  "node_modules/micromark-factory-destination": {
5874
6135
  "version": "2.0.1",
5875
6136
  "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
@@ -6884,6 +7145,24 @@
6884
7145
  "node": ">= 6"
6885
7146
  }
6886
7147
  },
7148
+ "node_modules/remark-gfm": {
7149
+ "version": "4.0.1",
7150
+ "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
7151
+ "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==",
7152
+ "license": "MIT",
7153
+ "dependencies": {
7154
+ "@types/mdast": "^4.0.0",
7155
+ "mdast-util-gfm": "^3.0.0",
7156
+ "micromark-extension-gfm": "^3.0.0",
7157
+ "remark-parse": "^11.0.0",
7158
+ "remark-stringify": "^11.0.0",
7159
+ "unified": "^11.0.0"
7160
+ },
7161
+ "funding": {
7162
+ "type": "opencollective",
7163
+ "url": "https://opencollective.com/unified"
7164
+ }
7165
+ },
6887
7166
  "node_modules/remark-parse": {
6888
7167
  "version": "11.0.0",
6889
7168
  "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
@@ -6917,6 +7196,21 @@
6917
7196
  "url": "https://opencollective.com/unified"
6918
7197
  }
6919
7198
  },
7199
+ "node_modules/remark-stringify": {
7200
+ "version": "11.0.0",
7201
+ "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
7202
+ "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==",
7203
+ "license": "MIT",
7204
+ "dependencies": {
7205
+ "@types/mdast": "^4.0.0",
7206
+ "mdast-util-to-markdown": "^2.0.0",
7207
+ "unified": "^11.0.0"
7208
+ },
7209
+ "funding": {
7210
+ "type": "opencollective",
7211
+ "url": "https://opencollective.com/unified"
7212
+ }
7213
+ },
6920
7214
  "node_modules/require-from-string": {
6921
7215
  "version": "2.0.2",
6922
7216
  "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
@@ -7774,9 +8068,9 @@
7774
8068
  }
7775
8069
  },
7776
8070
  "node_modules/unist-util-visit-parents": {
7777
- "version": "6.0.1",
7778
- "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz",
7779
- "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==",
8071
+ "version": "6.1.0",
8072
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.1.0.tgz",
8073
+ "integrity": "sha512-vKXxz6O38sKDj1mtx/k2cWvkkFBuYGx6uGrlzBkVeWCRF/F8sdI0j3Wbkz/5rVNRRuVc8UrgRdbCxNhmYXgP3w==",
7780
8074
  "license": "MIT",
7781
8075
  "dependencies": {
7782
8076
  "@types/unist": "^3.0.0",
@@ -34,6 +34,7 @@
34
34
  "react-i18next": "^15.5.2",
35
35
  "react-markdown": "^10.1.0",
36
36
  "react-router": "^7.5.0",
37
+ "remark-gfm": "^4.0.1",
37
38
  "socket.io-client": "^4.8.1",
38
39
  "syllable": "^5.0.1",
39
40
  "tailwind-merge": "^3.3.1",