mermaid ink image support

This commit is contained in:
2025-07-15 12:47:53 -06:00
parent 57b7d8ac1e
commit 43ed57e558
8 changed files with 68 additions and 9 deletions

View File

@@ -45,10 +45,10 @@ export default function CourseCalendar() {
className="
min-h-0
flex-grow
border-4
border-2
border-gray-900
rounded-lg
bg-slate-950
bg-slate-950/70
sm:p-1
"
>

View File

@@ -2,6 +2,7 @@ import MarkdownDisplay from "@/components/MarkdownDisplay";
import { LocalAssignment } from "@/models/local/assignment/localAssignment";
import { rubricItemIsExtraCredit } from "@/models/local/assignment/rubricItem";
import { assignmentPoints } from "@/models/local/assignment/utils/assignmentPointsUtils";
import { formatHumanReadableDate } from "@/services/utils/dateFormat";
import React, { Fragment } from "react";
export default function AssignmentPreview({
@@ -19,11 +20,15 @@ export default function AssignmentPreview({
<section>
<div className="flex">
<div className="flex-1 text-end pe-3">Due Date</div>
<div className="flex-1">{assignment.dueAt}</div>
<div className="flex-1">
{formatHumanReadableDate(assignment.dueAt)}
</div>
</div>
<div className="flex">
<div className="flex-1 text-end pe-3">Lock Date</div>
<div className="flex-1">{assignment.lockAt}</div>
<div className="flex-1">
{assignment.lockAt && formatHumanReadableDate(assignment.lockAt)}
</div>
</div>
<div className="flex">
<div className="flex-1 text-end pe-3">Assignment Group Name</div>

View File

@@ -14,14 +14,14 @@ export default function EditPageHeader({
return (
<div className="py-1 flex flex-row justify-start gap-3">
<Link
className="btn btn-thin"
className="btn"
href={getCourseUrl(courseName)}
shallow={true}
>
{courseName}
</Link>
<UpdatePageName pageName={pageName} moduleName={moduleName} />
<div>{pageName}</div>
<div className="my-auto">{pageName}</div>
</div>
);
}

View File

@@ -14,7 +14,7 @@ export default function EditQuizHeader({
return (
<div className="py-1 flex flex-row justify-start gap-3">
<Link
className="btn btn-thin"
className="btn"
href={getCourseUrl(courseName)}
shallow={true}
>

View File

@@ -1,9 +1,34 @@
"use client";
import { marked } from "marked";
import { marked, MarkedExtension } from "marked";
import DOMPurify from "isomorphic-dompurify";
import { LocalCourseSettings } from "@/models/local/localCourseSettings";
import markedKatex from "marked-katex-extension";
const mermaidExtension = {
name: "mermaid",
level: "block" as const,
start(src: string) {
return src.indexOf("```mermaid");
},
tokenizer(src: string) {
const rule = /^```mermaid\n([\s\S]+?)```(?:\n|$)/;
const match = rule.exec(src);
if (match) {
return {
type: "mermaid",
raw: match[0],
text: match[1].trim(),
};
}
},
renderer(token: { text: string }) {
const base64 = btoa(token.text);
const url = `https://mermaid.ink/img/${base64}?type=png`
console.log(token.text, url);
return `<img src="${url}" alt="Mermaid diagram" />`;
},
};
marked.use(
markedKatex({
throwOnError: false,
@@ -11,6 +36,8 @@ marked.use(
})
);
marked.use({ extensions: [mermaidExtension] });
export function extractImageSources(htmlString: string) {
const srcUrls = [];
const regex = /<img[^>]+src=["']?([^"'>]+)["']?/g;
@@ -22,6 +49,7 @@ export function extractImageSources(htmlString: string) {
return srcUrls;
}
export function convertImagesToCanvasImages(
html: string,
settings: LocalCourseSettings
@@ -37,7 +65,9 @@ export function convertImagesToCanvasImages(
for (const imageSrc of imageSources) {
const destinationUrl = imageLookup[imageSrc];
if (typeof destinationUrl === "undefined") {
console.log(`No image in settings for ${imageSrc}, do you have NEXT_PUBLIC_ENABLE_FILE_SYNC=true in your settings?`)
console.log(
`No image in settings for ${imageSrc}, do you have NEXT_PUBLIC_ENABLE_FILE_SYNC=true in your settings?`
);
}
// could error check here, but better to just not display an image...
// if (typeof destinationUrl === "undefined") {

View File

@@ -0,0 +1,15 @@
export function formatHumanReadableDate(date: Date | string): string {
const d = typeof date === "string" ? new Date(date) : date;
if (isNaN(d.getTime())) return "Invalid date";
const options: Intl.DateTimeFormatOptions = {
weekday: "short",
// year: "numeric",
month: "short",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
};
return d.toLocaleString(undefined, options);
}