This commit is contained in:
Adam Teichert
2025-01-14 18:35:40 -07:00
parent ada36c143c
commit ade3f4dca4
4 changed files with 8591 additions and 6 deletions

8552
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,7 @@
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"form-data": "^4.0.1", "form-data": "^4.0.1",
"jsdom": "^25.0.0", "jsdom": "^25.0.0",
"marked-katex-extension": "^5.1.4",
"next": "^15.0.2", "next": "^15.0.2",
"react": "^19", "react": "^19",
"react-dom": "^19", "react-dom": "^19",

View File

@@ -3,6 +3,7 @@ import { LocalQuiz } from "../../quiz/localQuiz";
import { quizMarkdownUtils } from "../../quiz/utils/quizMarkdownUtils"; import { quizMarkdownUtils } from "../../quiz/utils/quizMarkdownUtils";
import { QuestionType } from "@/models/local/quiz/localQuizQuestion"; import { QuestionType } from "@/models/local/quiz/localQuizQuestion";
import { quizQuestionMarkdownUtils } from "@/models/local/quiz/utils/quizQuestionMarkdownUtils"; import { quizQuestionMarkdownUtils } from "@/models/local/quiz/utils/quizQuestionMarkdownUtils";
import { markdownToHtmlNoImages, markdownToHTMLSafe } from "@/services/htmlMarkdownUtils";
// Test suite for QuizMarkdown // Test suite for QuizMarkdown
describe("QuizMarkdownTests", () => { describe("QuizMarkdownTests", () => {
@@ -253,4 +254,33 @@ short answer
const firstQuestion = quiz.questions[0]; const firstQuestion = quiz.questions[0];
expect(firstQuestion.points).toBe(4.56); expect(firstQuestion.points).toBe(4.56);
}); });
it("can parse quiz with latex in a question", () => {
const name = "Test Quiz";
const rawMarkdownQuiz = `
ShuffleAnswers: true
OneQuestionAtATime: false
DueAt: 08/21/2023 23:59:00
LockAt: 08/21/2023 23:59:00
AssignmentGroup: Assignments
AllowedAttempts: -1
Description: this is the
multi line
description
---
Points: 2
This is latex: $x_2$
*a) true
b) false
endline`;
const quizHtml = markdownToHtmlNoImages(rawMarkdownQuiz);
expect(quizHtml).not.toContain("$");
expect(quizHtml).toContain("<mi>x</mi>");
expect(quizHtml).not.toContain("x_2");
});
}); });

View File

@@ -2,6 +2,12 @@
import { marked } from "marked"; import { marked } from "marked";
import DOMPurify from "isomorphic-dompurify"; import DOMPurify from "isomorphic-dompurify";
import { LocalCourseSettings } from "@/models/local/localCourseSettings"; import { LocalCourseSettings } from "@/models/local/localCourseSettings";
import markedKatex from "marked-katex-extension";
marked.use(markedKatex({
throwOnError: false,
output: "mathml"
}));
export function extractImageSources(htmlString: string) { export function extractImageSources(htmlString: string) {
const srcUrls = []; const srcUrls = [];
@@ -40,16 +46,12 @@ export function markdownToHTMLSafe(
markdownString: string, markdownString: string,
settings: LocalCourseSettings settings: LocalCourseSettings
) { ) {
const clean = DOMPurify.sanitize( return markdownToHtmlNoImages(markdownString);
marked.parse(markdownString, { async: false, pedantic: false, gfm: true })
);
// return convertImagesToCanvasImages(clean, settings);
return clean;
} }
export function markdownToHtmlNoImages(markdownString: string) { export function markdownToHtmlNoImages(markdownString: string) {
const clean = DOMPurify.sanitize( const clean = DOMPurify.sanitize(
marked.parse(markdownString, { async: false, pedantic: false, gfm: true }) marked.parse(markdownString, { async: false, pedantic: false, gfm: true })
); ).replaceAll(/>[^<>]*<\/math>/g, "></math>");
return clean; return clean;
} }