improving replace url features

This commit is contained in:
2025-08-21 08:55:29 -06:00
parent 224cc9cd2a
commit d1a768393c
6 changed files with 90 additions and 17 deletions

View File

@@ -108,6 +108,12 @@ export const getStatus = ({
markdownToHTMLSafe({ markdownToHTMLSafe({
markdownString: assignment.description, markdownString: assignment.description,
settings, settings,
replaceText: [
{
source: "insert_github_classroom_url",
destination: assignment.githubClassroomAssignmentShareLink || "",
},
],
}), }),
canvasAssignment.description canvasAssignment.description
); );

View File

@@ -59,7 +59,15 @@ export default function AssignmentPreview({
<hr /> <hr />
<br /> <br />
<section> <section>
<MarkdownDisplay markdown={assignment.description} /> <MarkdownDisplay
markdown={assignment.description}
replaceText={[
{
source: "insert_github_classroom_url",
destination: assignment.githubClassroomAssignmentShareLink || "",
},
]}
/>
</section> </section>
<hr /> <hr />
<section> <section>

View File

@@ -6,9 +6,14 @@ import { LocalCourseSettings } from "@/features/local/course/localCourseSettings
export default function MarkdownDisplay({ export default function MarkdownDisplay({
markdown, markdown,
className = "", className = "",
replaceText = [],
}: { }: {
markdown: string; markdown: string;
className?: string; className?: string;
replaceText?: {
source: string;
destination: string;
}[];
}) { }) {
const { data: settings } = useLocalCourseSettingsQuery(); const { data: settings } = useLocalCourseSettingsQuery();
return ( return (
@@ -17,6 +22,7 @@ export default function MarkdownDisplay({
markdown={markdown} markdown={markdown}
settings={settings} settings={settings}
className={className} className={className}
replaceText={replaceText}
/> />
</SuspenseAndErrorHandling> </SuspenseAndErrorHandling>
); );
@@ -26,16 +32,25 @@ function DangerousInnerMarkdown({
markdown, markdown,
settings, settings,
className, className,
replaceText,
}: { }: {
markdown: string; markdown: string;
settings: LocalCourseSettings; settings: LocalCourseSettings;
className: string; className: string;
replaceText: {
source: string;
destination: string;
}[];
}) { }) {
return ( return (
<div <div
className={"markdownPreview " + className} className={"markdownPreview " + className}
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: markdownToHTMLSafe({ markdownString: markdown, settings }), __html: markdownToHTMLSafe({
markdownString: markdown,
settings,
replaceText,
}),
}} }}
></div> ></div>
); );

View File

@@ -32,16 +32,14 @@ export const canvasAssignmentService = {
const content = markdownToHTMLSafe({ const content = markdownToHTMLSafe({
markdownString: localAssignment.description, markdownString: localAssignment.description,
settings, settings,
replaceText: [
{
source: "insert_github_classroom_url",
destination: localAssignment.githubClassroomAssignmentShareLink || "",
},
],
}); });
const contentWithClassroomLinks =
localAssignment.githubClassroomAssignmentShareLink
? content.replaceAll(
"insert_github_classroom_url",
localAssignment.githubClassroomAssignmentShareLink
)
: content;
const body = { const body = {
assignment: { assignment: {
name: localAssignment.name, name: localAssignment.name,
@@ -51,7 +49,7 @@ export const canvasAssignmentService = {
allowed_extensions: localAssignment.allowedFileUploadExtensions.map( allowed_extensions: localAssignment.allowedFileUploadExtensions.map(
(e) => e.toString() (e) => e.toString()
), ),
description: contentWithClassroomLinks, description: content,
due_at: getDateFromString(localAssignment.dueAt)?.toISOString(), due_at: getDateFromString(localAssignment.dueAt)?.toISOString(),
lock_at: lock_at:
localAssignment.lockAt && localAssignment.lockAt &&
@@ -90,6 +88,13 @@ export const canvasAssignmentService = {
description: markdownToHTMLSafe({ description: markdownToHTMLSafe({
markdownString: localAssignment.description, markdownString: localAssignment.description,
settings, settings,
replaceText: [
{
source: "insert_github_classroom_url",
destination:
localAssignment.githubClassroomAssignmentShareLink || "",
},
],
}), }),
due_at: getDateFromString(localAssignment.dueAt)?.toISOString(), due_at: getDateFromString(localAssignment.dueAt)?.toISOString(),
lock_at: lock_at:

View File

@@ -90,14 +90,12 @@ export function markdownToHTMLSafe({
replaceText?: { source: string; destination: string }[]; replaceText?: { source: string; destination: string }[];
}) { }) {
const html = markdownToHtmlNoImages(markdownString); const html = markdownToHtmlNoImages(markdownString);
if (convertImages) return convertImagesToCanvasImages(html, settings);
const replacedHtml = replaceText.reduce( const replacedHtml = replaceText.reduce(
(acc, { source, destination }) => acc.replaceAll(source, destination), (acc, { source, destination }) => acc.replaceAll(source, destination),
html html
); );
// return html;
if (convertImages) return convertImagesToCanvasImages(replacedHtml, settings);
return replacedHtml; return replacedHtml;
} }

View File

@@ -1,6 +1,7 @@
const scriptRegex = /<script.*?<\/script>/g; const scriptRegex = /<script.*?<\/script>/g;
const linkTagRegex = /<link\s+rel="[^"]*"\s+href="[^"]*"[^>]*>/g; const linkTagRegex = /<link\s+rel="[^"]*"\s+href="[^"]*"[^>]*>/g;
const htmlAttribute = /\s+\w+="[^"]*"|\s+\w+='[^']*'|\s+\w+=[^\s>]+/g; const nonHrefAttribute =
/\s+(?!href\s*=)\w+="[^"]*"|\s+(?!href\s*=)\w+='[^']*'|\s+(?!href\s*=)\w+=[^\s>]+/g;
function replaceUnicodeEscapes(input: string) { function replaceUnicodeEscapes(input: string) {
return input.replace(/\\u[\dA-Fa-f]{4}/g, (match) => { return input.replace(/\\u[\dA-Fa-f]{4}/g, (match) => {
@@ -14,7 +15,7 @@ export const removeHtmlDetails = (html: string) => {
return withoutUnicode return withoutUnicode
.replaceAll(scriptRegex, "") .replaceAll(scriptRegex, "")
.replaceAll(linkTagRegex, "") .replaceAll(linkTagRegex, "")
.replaceAll(htmlAttribute, "") .replaceAll(nonHrefAttribute, "")
.replaceAll(/\\"/g, '"') .replaceAll(/\\"/g, '"')
.replaceAll(/\s/g, "") .replaceAll(/\s/g, "")
.replaceAll(/<hr\s*\/?>/g, "<hr>") .replaceAll(/<hr\s*\/?>/g, "<hr>")
@@ -32,5 +33,45 @@ export const removeHtmlDetails = (html: string) => {
export const htmlIsCloseEnough = (html1: string, html2: string) => { export const htmlIsCloseEnough = (html1: string, html2: string) => {
const simple1 = removeHtmlDetails(html1); const simple1 = removeHtmlDetails(html1);
const simple2 = removeHtmlDetails(html2); const simple2 = removeHtmlDetails(html2);
if (simple1 !== simple2) {
const len1 = simple1.length;
const len2 = simple2.length;
const maxLen = Math.max(len1, len2);
let firstDiff = -1;
const diffs: Array<{ index: number; a: string; b: string }> = [];
for (let i = 0; i < maxLen && diffs.length < 10; i++) {
const a = simple1[i] ?? "∅";
const b = simple2[i] ?? "∅";
if (a !== b) {
if (firstDiff === -1) firstDiff = i;
diffs.push({ index: i, a, b });
}
}
const ctx = 30;
const start = Math.max(0, (firstDiff === -1 ? 0 : firstDiff) - ctx);
const end1 = Math.min(len1, (firstDiff === -1 ? 0 : firstDiff) + ctx);
const end2 = Math.min(len2, (firstDiff === -1 ? 0 : firstDiff) + ctx);
const mark = (s: string, sStart: number, idx: number, sEnd: number) => {
if (idx < 0) return s.slice(sStart, sEnd);
const before = s.slice(sStart, idx);
const ch = s[idx] ?? "∅";
const after = s.slice(idx + 1, sEnd);
return `${before}[${ch}]${after}`;
};
console.log("htmlIsCloseEnough: differences detected");
console.log(`len1=${len1}, len2=${len2}`);
if (firstDiff !== -1) {
console.log(`firstDiffAt=${firstDiff}`);
console.log("s1:", mark(simple1, start, firstDiff, end1));
console.log("s2:", mark(simple2, start, firstDiff, end2));
}
console.log("first 10 diffs:", diffs);
}
return simple1 === simple2; return simple1 === simple2;
}; };