@raspberrypifoundation/rpf-markdown-core 0.1.1 → 0.1.2
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/dist/index.cjs +287 -237
- package/dist/index.js +288 -238
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -45,12 +45,16 @@ function escapeHtml(value) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
// src/block_renderers.ts
|
|
48
|
+
var parseMarkdown = (input) => import_marked.marked.parse(input);
|
|
49
|
+
function setMarkdownParser(parse) {
|
|
50
|
+
parseMarkdown = parse;
|
|
51
|
+
}
|
|
48
52
|
var CALLOUT_HEADINGS = {
|
|
49
53
|
debug: "Debugging",
|
|
50
54
|
tip: "Tip"
|
|
51
55
|
};
|
|
52
56
|
function renderMarkdown(input) {
|
|
53
|
-
return
|
|
57
|
+
return parseMarkdown(input);
|
|
54
58
|
}
|
|
55
59
|
function renderAccordion({
|
|
56
60
|
modifier,
|
|
@@ -408,7 +412,8 @@ function getOrCreateScratchblocksRenderer() {
|
|
|
408
412
|
return rendererCache;
|
|
409
413
|
}
|
|
410
414
|
function parseScratchblocksBlock(fencedBlock) {
|
|
411
|
-
const
|
|
415
|
+
const normalizedBlock = fencedBlock.replace(/\r\n/g, "\n").trimEnd();
|
|
416
|
+
const fenceMatch = normalizedBlock.match(SCRATCHBLOCKS_FENCED_BLOCK_REGEX);
|
|
412
417
|
if (!fenceMatch) {
|
|
413
418
|
throw new Error(SCRATCHBLOCKS_PARSE_ERROR);
|
|
414
419
|
}
|
|
@@ -416,14 +421,14 @@ function parseScratchblocksBlock(fencedBlock) {
|
|
|
416
421
|
const code = fenceMatch[3] ?? "";
|
|
417
422
|
const style = mapFenceInfoToStyle(infoString);
|
|
418
423
|
try {
|
|
419
|
-
const
|
|
420
|
-
const doc =
|
|
421
|
-
const svgElement =
|
|
424
|
+
const renderer2 = getOrCreateScratchblocksRenderer();
|
|
425
|
+
const doc = renderer2.api.parse(code, { languages: ["en"] });
|
|
426
|
+
const svgElement = renderer2.api.render(doc, {
|
|
422
427
|
style,
|
|
423
428
|
languages: ["en"],
|
|
424
429
|
scale: 1
|
|
425
430
|
});
|
|
426
|
-
const svg = embedStylesIntoSvg(svgElement.outerHTML,
|
|
431
|
+
const svg = embedStylesIntoSvg(svgElement.outerHTML, renderer2.stylesHtml);
|
|
427
432
|
const html = `<div class="c-project-scratchblocks" data-style="${style}">${svg}</div>`;
|
|
428
433
|
return { html, style, svg };
|
|
429
434
|
} catch (error) {
|
|
@@ -976,268 +981,313 @@ function createLegacyBlockExtension(name, blockName, render, appendNewline = tru
|
|
|
976
981
|
}
|
|
977
982
|
};
|
|
978
983
|
}
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
)
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
984
|
+
var RAW_HTML_BLOCK_TAGS = "article|aside|blockquote|body|details|dialog|div|dl|fieldset|figure|footer|form|h[1-6]|head|header|hr|html|iframe|main|nav|ol|p|pre|script|section|style|svg|table|ul|video";
|
|
985
|
+
var RAW_HTML_VOID_TAGS = /* @__PURE__ */ new Set(["hr", "br", "img", "input", "wbr", "col"]);
|
|
986
|
+
var RAW_HTML_BLOCK_AT_START = new RegExp(
|
|
987
|
+
`^[ \\t]*(?:<!--|<!doctype\\b|<(?:${RAW_HTML_BLOCK_TAGS})(?=[\\s/>]))`,
|
|
988
|
+
"i"
|
|
989
|
+
);
|
|
990
|
+
var RAW_HTML_BLOCK_SEARCH = new RegExp(RAW_HTML_BLOCK_AT_START.source, "im");
|
|
991
|
+
function consumeTrailingNewline(src, end) {
|
|
992
|
+
const after = /^[ \t]*\r?\n/.exec(src.slice(end));
|
|
993
|
+
return src.slice(0, end + (after?.[0].length ?? 0));
|
|
994
|
+
}
|
|
995
|
+
function matchRawHtmlBlock(src) {
|
|
996
|
+
if (!RAW_HTML_BLOCK_AT_START.test(src)) return void 0;
|
|
997
|
+
const leading = /^[ \t]*/.exec(src)?.[0].length ?? 0;
|
|
998
|
+
const rest = src.slice(leading);
|
|
999
|
+
if (rest.startsWith("<!--")) {
|
|
1000
|
+
const end = rest.indexOf("-->");
|
|
1001
|
+
if (end === -1) return void 0;
|
|
1002
|
+
return consumeTrailingNewline(src, leading + end + 3);
|
|
987
1003
|
}
|
|
988
|
-
|
|
989
|
-
|
|
1004
|
+
const doctype = /^<!doctype\b[^>]*>/i.exec(rest);
|
|
1005
|
+
if (doctype) {
|
|
1006
|
+
return consumeTrailingNewline(src, leading + doctype[0].length);
|
|
990
1007
|
}
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
function processHtmlContent(content) {
|
|
997
|
-
if (typeof document === "undefined") {
|
|
998
|
-
return applyInlineCodeClasses(content);
|
|
1008
|
+
const open = /^<([a-z][a-z0-9]*)(?=[\s/>])[^>]*>/i.exec(rest);
|
|
1009
|
+
if (!open) return void 0;
|
|
1010
|
+
const tag = (open[1] ?? "").toLowerCase();
|
|
1011
|
+
if (open[0].endsWith("/>") || RAW_HTML_VOID_TAGS.has(tag)) {
|
|
1012
|
+
return consumeTrailingNewline(src, leading + open[0].length);
|
|
999
1013
|
}
|
|
1000
|
-
const
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1014
|
+
const tagRe = new RegExp(`<(/?)${tag}(?=[\\s/>])[^>]*>`, "ig");
|
|
1015
|
+
tagRe.lastIndex = leading;
|
|
1016
|
+
let depth = 0;
|
|
1017
|
+
let match;
|
|
1018
|
+
while ((match = tagRe.exec(src)) !== null) {
|
|
1019
|
+
if (match[1] === "/") {
|
|
1020
|
+
depth--;
|
|
1021
|
+
} else if (!match[0].endsWith("/>")) {
|
|
1022
|
+
depth++;
|
|
1023
|
+
}
|
|
1024
|
+
if (depth === 0) {
|
|
1025
|
+
return consumeTrailingNewline(src, tagRe.lastIndex);
|
|
1026
|
+
}
|
|
1007
1027
|
}
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1028
|
+
return void 0;
|
|
1029
|
+
}
|
|
1030
|
+
var renderer = new import_marked2.Marked();
|
|
1031
|
+
renderer.use(markedSmartypantsLite());
|
|
1032
|
+
renderer.use({
|
|
1033
|
+
extensions: [
|
|
1034
|
+
// Generic raw HTML passthrough. marked registers extension tokenizers by
|
|
1035
|
+
// unshifting, so dispatch order is the reverse of this array — placing
|
|
1036
|
+
// this first means it is tried LAST, letting the specific HTML
|
|
1037
|
+
// transformers below (callouts, output, wrapped code) take precedence.
|
|
1038
|
+
// Any other top-level HTML element is captured whole and emitted verbatim
|
|
1039
|
+
// so its interior is never re-parsed as markdown.
|
|
1040
|
+
{
|
|
1041
|
+
name: "rawHtmlBlock",
|
|
1042
|
+
level: "block",
|
|
1043
|
+
start(src) {
|
|
1044
|
+
const index = src.search(RAW_HTML_BLOCK_SEARCH);
|
|
1045
|
+
return index < 0 ? void 0 : index;
|
|
1046
|
+
},
|
|
1047
|
+
tokenizer(src) {
|
|
1048
|
+
const raw = matchRawHtmlBlock(src);
|
|
1049
|
+
if (raw) {
|
|
1050
|
+
return { type: "rawHtmlBlock", raw, text: raw };
|
|
1025
1051
|
}
|
|
1026
1052
|
},
|
|
1027
|
-
{
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
text: match[3] ?? "",
|
|
1042
|
-
info: (match[2] ?? "").trim()
|
|
1043
|
-
};
|
|
1044
|
-
},
|
|
1045
|
-
renderer(token) {
|
|
1046
|
-
return renderMarkedCodeToken(token, token.raw);
|
|
1053
|
+
renderer(token) {
|
|
1054
|
+
return token.text;
|
|
1055
|
+
}
|
|
1056
|
+
},
|
|
1057
|
+
{
|
|
1058
|
+
name: "htmlLineBreak",
|
|
1059
|
+
level: "block",
|
|
1060
|
+
start(src) {
|
|
1061
|
+
return src.search(/^[ \t]*\{\.page-break\}[ \t]*(?:\n|$)/m);
|
|
1062
|
+
},
|
|
1063
|
+
tokenizer(src) {
|
|
1064
|
+
const match = /^[ \t]*\{\.page-break\}[ \t]*(?:\n|$)/.exec(src);
|
|
1065
|
+
if (match) {
|
|
1066
|
+
return { type: "htmlLineBreak", raw: match[0] };
|
|
1047
1067
|
}
|
|
1048
1068
|
},
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1069
|
+
renderer() {
|
|
1070
|
+
return "<div class=page-break></div>\n";
|
|
1071
|
+
}
|
|
1072
|
+
},
|
|
1073
|
+
{
|
|
1074
|
+
name: "preservedFence",
|
|
1075
|
+
level: "block",
|
|
1076
|
+
start(src) {
|
|
1077
|
+
return src.search(/^[ \t]*(`{3,}|~{3,})/m);
|
|
1078
|
+
},
|
|
1079
|
+
tokenizer(src) {
|
|
1080
|
+
const match = src.match(
|
|
1081
|
+
/^[ \t]*(`{3,}|~{3,})([^\r\n]*)\r?\n([\s\S]*?)\r?\n[ \t]*\1[ \t]*(?:\n|$)/
|
|
1082
|
+
);
|
|
1083
|
+
if (!match) return;
|
|
1084
|
+
return {
|
|
1085
|
+
type: "preservedFence",
|
|
1086
|
+
raw: match[0],
|
|
1087
|
+
text: match[3] ?? "",
|
|
1088
|
+
info: (match[2] ?? "").trim()
|
|
1089
|
+
};
|
|
1090
|
+
},
|
|
1091
|
+
renderer(token) {
|
|
1092
|
+
return renderMarkedCodeToken(token, token.raw);
|
|
1093
|
+
}
|
|
1094
|
+
},
|
|
1095
|
+
// RFM alert blocks: > [!TASK], > [!ACCORDION], etc.
|
|
1096
|
+
{
|
|
1097
|
+
name: "rfmBlock",
|
|
1098
|
+
level: "block",
|
|
1099
|
+
start(src) {
|
|
1100
|
+
return src.search(/^[ \t]*>+[ \t]?\[!/m);
|
|
1101
|
+
},
|
|
1102
|
+
tokenizer(src) {
|
|
1103
|
+
const raw = matchRfmBlock(src);
|
|
1104
|
+
if (raw !== void 0) {
|
|
1105
|
+
return { type: "rfmBlock", raw, text: raw };
|
|
1071
1106
|
}
|
|
1072
1107
|
},
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
return src.search(
|
|
1079
|
-
/<div\s+class="c-project-callout\s+c-project-callout--/
|
|
1080
|
-
);
|
|
1081
|
-
},
|
|
1082
|
-
tokenizer(src) {
|
|
1083
|
-
const match = /^<div\s+class="c-project-callout\s+c-project-callout--[a-z0-9-]+">[\s\S]*?<\/div>/.exec(
|
|
1084
|
-
src
|
|
1085
|
-
);
|
|
1086
|
-
if (match) {
|
|
1087
|
-
return { type: "rpfCallout", raw: match[0], text: match[0] };
|
|
1088
|
-
}
|
|
1089
|
-
},
|
|
1090
|
-
renderer(token) {
|
|
1091
|
-
return `${parseCalloutBlock(token.text).html}
|
|
1108
|
+
renderer(token) {
|
|
1109
|
+
const raw = token.text;
|
|
1110
|
+
const hintBlocks = splitTopLevelRfmHintBlocks(raw);
|
|
1111
|
+
if (hintBlocks.length > 1) {
|
|
1112
|
+
return `${renderHintsPanel(hintBlocks.map(getRfmHintBody))}
|
|
1092
1113
|
`;
|
|
1093
1114
|
}
|
|
1094
|
-
|
|
1095
|
-
// RPF task blocks: --- task --- ... --- /task ---
|
|
1096
|
-
{
|
|
1097
|
-
name: "rpfTask",
|
|
1098
|
-
level: "block",
|
|
1099
|
-
start(src) {
|
|
1100
|
-
return src.search(/^--- task ---$/m);
|
|
1101
|
-
},
|
|
1102
|
-
tokenizer(src) {
|
|
1103
|
-
const match = /^--- task ---\n[\s\S]*?\n--- \/task ---/.exec(src);
|
|
1104
|
-
if (match) {
|
|
1105
|
-
return { type: "rpfTask", raw: match[0], text: match[0] };
|
|
1106
|
-
}
|
|
1107
|
-
},
|
|
1108
|
-
renderer(token) {
|
|
1109
|
-
return `${parseTaskBlock(token.text).html}
|
|
1115
|
+
return `${parseRfmBlock(raw).html}
|
|
1110
1116
|
`;
|
|
1117
|
+
}
|
|
1118
|
+
},
|
|
1119
|
+
// RPF callout blocks: <div class="c-project-callout c-project-callout--{type}">
|
|
1120
|
+
{
|
|
1121
|
+
name: "rpfCallout",
|
|
1122
|
+
level: "block",
|
|
1123
|
+
start(src) {
|
|
1124
|
+
return src.search(
|
|
1125
|
+
/<div\s+class="c-project-callout\s+c-project-callout--/
|
|
1126
|
+
);
|
|
1127
|
+
},
|
|
1128
|
+
tokenizer(src) {
|
|
1129
|
+
const match = /^<div\s+class="c-project-callout\s+c-project-callout--[a-z0-9-]+">[\s\S]*?<\/div>/.exec(
|
|
1130
|
+
src
|
|
1131
|
+
);
|
|
1132
|
+
if (match) {
|
|
1133
|
+
return { type: "rpfCallout", raw: match[0], text: match[0] };
|
|
1111
1134
|
}
|
|
1112
1135
|
},
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
// RPF hints blocks: --- hints --- ... --- /hints ---
|
|
1116
|
-
createLegacyBlockExtension("rpfHints", "hints", parseHintsBlock),
|
|
1117
|
-
// RPF hint blocks: --- hint --- ... --- /hint ---
|
|
1118
|
-
createLegacyBlockExtension("rpfHint", "hint", parseHintBlock),
|
|
1119
|
-
// RPF challenge blocks: --- challenge --- ... --- /challenge ---
|
|
1120
|
-
createLegacyBlockExtension(
|
|
1121
|
-
"rpfChallenge",
|
|
1122
|
-
"challenge",
|
|
1123
|
-
parseChallengeBlock,
|
|
1124
|
-
false
|
|
1125
|
-
),
|
|
1126
|
-
// RPF print visibility blocks.
|
|
1127
|
-
createLegacyBlockExtension("rpfNoPrint", "no-print", parseNoPrintBlock),
|
|
1128
|
-
createLegacyBlockExtension(
|
|
1129
|
-
"rpfPrintOnly",
|
|
1130
|
-
"print-only",
|
|
1131
|
-
parsePrintOnlyBlock
|
|
1132
|
-
),
|
|
1133
|
-
// RPF save block: --- save ---
|
|
1134
|
-
{
|
|
1135
|
-
name: "rpfSave",
|
|
1136
|
-
level: "block",
|
|
1137
|
-
start(src) {
|
|
1138
|
-
return src.search(/^[ \t]*--- save ---$/m);
|
|
1139
|
-
},
|
|
1140
|
-
tokenizer(src) {
|
|
1141
|
-
const match = /^[ \t]*---\s+save\s+---[ \t]*(?:\n|$)/.exec(src);
|
|
1142
|
-
if (match) {
|
|
1143
|
-
return { type: "rpfSave", raw: match[0], text: match[0] };
|
|
1144
|
-
}
|
|
1145
|
-
},
|
|
1146
|
-
renderer() {
|
|
1147
|
-
return `${parseSaveBlock()}
|
|
1136
|
+
renderer(token) {
|
|
1137
|
+
return `${parseCalloutBlock(token.text).html}
|
|
1148
1138
|
`;
|
|
1139
|
+
}
|
|
1140
|
+
},
|
|
1141
|
+
// RPF task blocks: --- task --- ... --- /task ---
|
|
1142
|
+
{
|
|
1143
|
+
name: "rpfTask",
|
|
1144
|
+
level: "block",
|
|
1145
|
+
start(src) {
|
|
1146
|
+
return src.search(/^--- task ---$/m);
|
|
1147
|
+
},
|
|
1148
|
+
tokenizer(src) {
|
|
1149
|
+
const match = /^--- task ---\n[\s\S]*?\n--- \/task ---/.exec(src);
|
|
1150
|
+
if (match) {
|
|
1151
|
+
return { type: "rpfTask", raw: match[0], text: match[0] };
|
|
1149
1152
|
}
|
|
1150
1153
|
},
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
{
|
|
1154
|
-
name: "rpfCode",
|
|
1155
|
-
level: "block",
|
|
1156
|
-
start(src) {
|
|
1157
|
-
const bare = src.search(/^--- code ---$/m);
|
|
1158
|
-
const wrapped = src.search(/^<div class="c-project-code">/m);
|
|
1159
|
-
if (bare === -1) return wrapped;
|
|
1160
|
-
if (wrapped === -1) return bare;
|
|
1161
|
-
return Math.min(bare, wrapped);
|
|
1162
|
-
},
|
|
1163
|
-
tokenizer(src) {
|
|
1164
|
-
const wrappedMatch = /^<div class="c-project-code">\n(--- code ---\n[\s\S]*?\n--- \/code ---)\n\s*<\/div>/.exec(
|
|
1165
|
-
src
|
|
1166
|
-
);
|
|
1167
|
-
if (wrappedMatch) {
|
|
1168
|
-
return {
|
|
1169
|
-
type: "rpfCode",
|
|
1170
|
-
raw: wrappedMatch[0],
|
|
1171
|
-
text: wrappedMatch[1]
|
|
1172
|
-
};
|
|
1173
|
-
}
|
|
1174
|
-
const match = /^--- code ---\n[\s\S]*?\n--- \/code ---/.exec(src);
|
|
1175
|
-
if (match) {
|
|
1176
|
-
return { type: "rpfCode", raw: match[0], text: match[0] };
|
|
1177
|
-
}
|
|
1178
|
-
},
|
|
1179
|
-
renderer(token) {
|
|
1180
|
-
return `${parseRpfCodeBlock(token.text).html}
|
|
1154
|
+
renderer(token) {
|
|
1155
|
+
return `${parseTaskBlock(token.text).html}
|
|
1181
1156
|
`;
|
|
1157
|
+
}
|
|
1158
|
+
},
|
|
1159
|
+
// RPF collapse blocks: --- collapse --- ... --- /collapse ---
|
|
1160
|
+
createLegacyBlockExtension("rpfCollapse", "collapse", parseCollapseBlock),
|
|
1161
|
+
// RPF hints blocks: --- hints --- ... --- /hints ---
|
|
1162
|
+
createLegacyBlockExtension("rpfHints", "hints", parseHintsBlock),
|
|
1163
|
+
// RPF hint blocks: --- hint --- ... --- /hint ---
|
|
1164
|
+
createLegacyBlockExtension("rpfHint", "hint", parseHintBlock),
|
|
1165
|
+
// RPF challenge blocks: --- challenge --- ... --- /challenge ---
|
|
1166
|
+
createLegacyBlockExtension(
|
|
1167
|
+
"rpfChallenge",
|
|
1168
|
+
"challenge",
|
|
1169
|
+
parseChallengeBlock,
|
|
1170
|
+
false
|
|
1171
|
+
),
|
|
1172
|
+
// RPF print visibility blocks.
|
|
1173
|
+
createLegacyBlockExtension("rpfNoPrint", "no-print", parseNoPrintBlock),
|
|
1174
|
+
createLegacyBlockExtension(
|
|
1175
|
+
"rpfPrintOnly",
|
|
1176
|
+
"print-only",
|
|
1177
|
+
parsePrintOnlyBlock
|
|
1178
|
+
),
|
|
1179
|
+
// RPF save block: --- save ---
|
|
1180
|
+
{
|
|
1181
|
+
name: "rpfSave",
|
|
1182
|
+
level: "block",
|
|
1183
|
+
start(src) {
|
|
1184
|
+
return src.search(/^[ \t]*--- save ---$/m);
|
|
1185
|
+
},
|
|
1186
|
+
tokenizer(src) {
|
|
1187
|
+
const match = /^[ \t]*---\s+save\s+---[ \t]*(?:\n|$)/.exec(src);
|
|
1188
|
+
if (match) {
|
|
1189
|
+
return { type: "rpfSave", raw: match[0], text: match[0] };
|
|
1182
1190
|
}
|
|
1183
1191
|
},
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
name: "rpfOutput",
|
|
1187
|
-
level: "block",
|
|
1188
|
-
start(src) {
|
|
1189
|
-
return src.search(/^<div\s+class="c-project-output">\s*$/m);
|
|
1190
|
-
},
|
|
1191
|
-
tokenizer(src) {
|
|
1192
|
-
const match = /^<div\s+class="c-project-output">\s[\s\S]*?<\/div>/.exec(src);
|
|
1193
|
-
if (match) {
|
|
1194
|
-
return { type: "rpfOutput", raw: match[0], text: match[0] };
|
|
1195
|
-
}
|
|
1196
|
-
},
|
|
1197
|
-
renderer(token) {
|
|
1198
|
-
return `${parseOutputBlock(token.text)}
|
|
1192
|
+
renderer() {
|
|
1193
|
+
return `${parseSaveBlock()}
|
|
1199
1194
|
`;
|
|
1195
|
+
}
|
|
1196
|
+
},
|
|
1197
|
+
// RPF code blocks: --- code --- ... --- /code ---
|
|
1198
|
+
// Also handles the wrapped form: <div class="c-project-code">\n--- code ---\n...\n--- /code ---\n</div>
|
|
1199
|
+
{
|
|
1200
|
+
name: "rpfCode",
|
|
1201
|
+
level: "block",
|
|
1202
|
+
start(src) {
|
|
1203
|
+
const bare = src.search(/^--- code ---$/m);
|
|
1204
|
+
const wrapped = src.search(/^<div class="c-project-code">/m);
|
|
1205
|
+
if (bare === -1) return wrapped;
|
|
1206
|
+
if (wrapped === -1) return bare;
|
|
1207
|
+
return Math.min(bare, wrapped);
|
|
1208
|
+
},
|
|
1209
|
+
tokenizer(src) {
|
|
1210
|
+
const wrappedMatch = /^<div class="c-project-code">\n(--- code ---\n[\s\S]*?\n--- \/code ---)\n\s*<\/div>/.exec(
|
|
1211
|
+
src
|
|
1212
|
+
);
|
|
1213
|
+
if (wrappedMatch) {
|
|
1214
|
+
return {
|
|
1215
|
+
type: "rpfCode",
|
|
1216
|
+
raw: wrappedMatch[0],
|
|
1217
|
+
text: wrappedMatch[1]
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
const match = /^--- code ---\n[\s\S]*?\n--- \/code ---/.exec(src);
|
|
1221
|
+
if (match) {
|
|
1222
|
+
return { type: "rpfCode", raw: match[0], text: match[0] };
|
|
1200
1223
|
}
|
|
1224
|
+
},
|
|
1225
|
+
renderer(token) {
|
|
1226
|
+
return `${parseRpfCodeBlock(token.text).html}
|
|
1227
|
+
`;
|
|
1201
1228
|
}
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1229
|
+
},
|
|
1230
|
+
// RPF output blocks: <div class="c-project-output"> ... </div>
|
|
1231
|
+
{
|
|
1232
|
+
name: "rpfOutput",
|
|
1233
|
+
level: "block",
|
|
1234
|
+
start(src) {
|
|
1235
|
+
return src.search(/^<div\s+class="c-project-output">\s*$/m);
|
|
1207
1236
|
},
|
|
1208
|
-
|
|
1209
|
-
const
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
const hasBlankLineAfterMarker = firstNewline !== -1 && /^\n[ \t]*\n/.test(raw.slice(firstNewline));
|
|
1215
|
-
let body = "";
|
|
1216
|
-
for (let index = 0; index < itemTokens.length; index++) {
|
|
1217
|
-
const token = itemTokens[index];
|
|
1218
|
-
if (index === 0 && !item.task && !hasBlankLineAfterMarker && itemTokens.length > 1 && token?.type === "paragraph") {
|
|
1219
|
-
body += this.parser.parseInline(token.tokens ?? []);
|
|
1220
|
-
continue;
|
|
1221
|
-
}
|
|
1222
|
-
if (token?.type === "code" || token?.type === "preservedFence") {
|
|
1223
|
-
body += renderMarkedCodeToken(token, rawFenceBlocks[rawFenceIndex]);
|
|
1224
|
-
rawFenceIndex++;
|
|
1225
|
-
continue;
|
|
1226
|
-
}
|
|
1227
|
-
body += this.parser.parse([token]).trimStart();
|
|
1237
|
+
tokenizer(src) {
|
|
1238
|
+
const match = /^<div\s+class="c-project-output">\s[\s\S]*?<\/div>/.exec(
|
|
1239
|
+
src
|
|
1240
|
+
);
|
|
1241
|
+
if (match) {
|
|
1242
|
+
return { type: "rpfOutput", raw: match[0], text: match[0] };
|
|
1228
1243
|
}
|
|
1229
|
-
|
|
1244
|
+
},
|
|
1245
|
+
renderer(token) {
|
|
1246
|
+
return `${parseOutputBlock(token.text)}
|
|
1230
1247
|
`;
|
|
1231
1248
|
}
|
|
1249
|
+
}
|
|
1250
|
+
],
|
|
1251
|
+
renderer: {
|
|
1252
|
+
// Override fenced code blocks to use Prism with our custom options.
|
|
1253
|
+
code(token) {
|
|
1254
|
+
return renderMarkedCodeToken(token);
|
|
1232
1255
|
},
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1256
|
+
listitem(item) {
|
|
1257
|
+
const itemTokens = Array.isArray(item.tokens) ? item.tokens : [];
|
|
1258
|
+
const raw = typeof item.raw === "string" ? item.raw : "";
|
|
1259
|
+
const rawFenceBlocks = extractListItemFenceBlocks(raw);
|
|
1260
|
+
let rawFenceIndex = 0;
|
|
1261
|
+
const firstNewline = raw.indexOf("\n");
|
|
1262
|
+
const hasBlankLineAfterMarker = firstNewline !== -1 && /^\n[ \t]*\n/.test(raw.slice(firstNewline));
|
|
1263
|
+
let body = "";
|
|
1264
|
+
for (let index = 0; index < itemTokens.length; index++) {
|
|
1265
|
+
const token = itemTokens[index];
|
|
1266
|
+
if (index === 0 && !item.task && !hasBlankLineAfterMarker && itemTokens.length > 1 && token?.type === "paragraph") {
|
|
1267
|
+
body += this.parser.parseInline(token.tokens ?? []);
|
|
1268
|
+
continue;
|
|
1269
|
+
}
|
|
1270
|
+
if (token?.type === "code" || token?.type === "preservedFence") {
|
|
1271
|
+
body += renderMarkedCodeToken(token, rawFenceBlocks[rawFenceIndex]);
|
|
1272
|
+
rawFenceIndex++;
|
|
1273
|
+
continue;
|
|
1274
|
+
}
|
|
1275
|
+
body += this.parser.parse([token]).trimStart();
|
|
1236
1276
|
}
|
|
1277
|
+
return `<li>${body}</li>
|
|
1278
|
+
`;
|
|
1237
1279
|
}
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
|
|
1280
|
+
},
|
|
1281
|
+
hooks: {
|
|
1282
|
+
postprocess(html) {
|
|
1283
|
+
return applyInlineCodeClasses(html);
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
});
|
|
1287
|
+
renderer.use((0, import_marked_gfm_heading_id.gfmHeadingId)({ prefix: "" }));
|
|
1288
|
+
setMarkdownParser((input) => renderer.parse(input));
|
|
1289
|
+
function processEditorProject(content) {
|
|
1290
|
+
return renderer.parse(content);
|
|
1241
1291
|
}
|
|
1242
1292
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1243
1293
|
0 && (module.exports = {
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import {
|
|
2
|
+
import { Marked } from "marked";
|
|
3
3
|
import { gfmHeadingId } from "marked-gfm-heading-id";
|
|
4
4
|
|
|
5
5
|
// src/block_renderers.ts
|
|
@@ -11,12 +11,16 @@ function escapeHtml(value) {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
// src/block_renderers.ts
|
|
14
|
+
var parseMarkdown = (input) => marked.parse(input);
|
|
15
|
+
function setMarkdownParser(parse) {
|
|
16
|
+
parseMarkdown = parse;
|
|
17
|
+
}
|
|
14
18
|
var CALLOUT_HEADINGS = {
|
|
15
19
|
debug: "Debugging",
|
|
16
20
|
tip: "Tip"
|
|
17
21
|
};
|
|
18
22
|
function renderMarkdown(input) {
|
|
19
|
-
return
|
|
23
|
+
return parseMarkdown(input);
|
|
20
24
|
}
|
|
21
25
|
function renderAccordion({
|
|
22
26
|
modifier,
|
|
@@ -373,7 +377,8 @@ function getOrCreateScratchblocksRenderer() {
|
|
|
373
377
|
return rendererCache;
|
|
374
378
|
}
|
|
375
379
|
function parseScratchblocksBlock(fencedBlock) {
|
|
376
|
-
const
|
|
380
|
+
const normalizedBlock = fencedBlock.replace(/\r\n/g, "\n").trimEnd();
|
|
381
|
+
const fenceMatch = normalizedBlock.match(SCRATCHBLOCKS_FENCED_BLOCK_REGEX);
|
|
377
382
|
if (!fenceMatch) {
|
|
378
383
|
throw new Error(SCRATCHBLOCKS_PARSE_ERROR);
|
|
379
384
|
}
|
|
@@ -381,14 +386,14 @@ function parseScratchblocksBlock(fencedBlock) {
|
|
|
381
386
|
const code = fenceMatch[3] ?? "";
|
|
382
387
|
const style = mapFenceInfoToStyle(infoString);
|
|
383
388
|
try {
|
|
384
|
-
const
|
|
385
|
-
const doc =
|
|
386
|
-
const svgElement =
|
|
389
|
+
const renderer2 = getOrCreateScratchblocksRenderer();
|
|
390
|
+
const doc = renderer2.api.parse(code, { languages: ["en"] });
|
|
391
|
+
const svgElement = renderer2.api.render(doc, {
|
|
387
392
|
style,
|
|
388
393
|
languages: ["en"],
|
|
389
394
|
scale: 1
|
|
390
395
|
});
|
|
391
|
-
const svg = embedStylesIntoSvg(svgElement.outerHTML,
|
|
396
|
+
const svg = embedStylesIntoSvg(svgElement.outerHTML, renderer2.stylesHtml);
|
|
392
397
|
const html = `<div class="c-project-scratchblocks" data-style="${style}">${svg}</div>`;
|
|
393
398
|
return { html, style, svg };
|
|
394
399
|
} catch (error) {
|
|
@@ -941,268 +946,313 @@ function createLegacyBlockExtension(name, blockName, render, appendNewline = tru
|
|
|
941
946
|
}
|
|
942
947
|
};
|
|
943
948
|
}
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
)
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
949
|
+
var RAW_HTML_BLOCK_TAGS = "article|aside|blockquote|body|details|dialog|div|dl|fieldset|figure|footer|form|h[1-6]|head|header|hr|html|iframe|main|nav|ol|p|pre|script|section|style|svg|table|ul|video";
|
|
950
|
+
var RAW_HTML_VOID_TAGS = /* @__PURE__ */ new Set(["hr", "br", "img", "input", "wbr", "col"]);
|
|
951
|
+
var RAW_HTML_BLOCK_AT_START = new RegExp(
|
|
952
|
+
`^[ \\t]*(?:<!--|<!doctype\\b|<(?:${RAW_HTML_BLOCK_TAGS})(?=[\\s/>]))`,
|
|
953
|
+
"i"
|
|
954
|
+
);
|
|
955
|
+
var RAW_HTML_BLOCK_SEARCH = new RegExp(RAW_HTML_BLOCK_AT_START.source, "im");
|
|
956
|
+
function consumeTrailingNewline(src, end) {
|
|
957
|
+
const after = /^[ \t]*\r?\n/.exec(src.slice(end));
|
|
958
|
+
return src.slice(0, end + (after?.[0].length ?? 0));
|
|
959
|
+
}
|
|
960
|
+
function matchRawHtmlBlock(src) {
|
|
961
|
+
if (!RAW_HTML_BLOCK_AT_START.test(src)) return void 0;
|
|
962
|
+
const leading = /^[ \t]*/.exec(src)?.[0].length ?? 0;
|
|
963
|
+
const rest = src.slice(leading);
|
|
964
|
+
if (rest.startsWith("<!--")) {
|
|
965
|
+
const end = rest.indexOf("-->");
|
|
966
|
+
if (end === -1) return void 0;
|
|
967
|
+
return consumeTrailingNewline(src, leading + end + 3);
|
|
952
968
|
}
|
|
953
|
-
|
|
954
|
-
|
|
969
|
+
const doctype = /^<!doctype\b[^>]*>/i.exec(rest);
|
|
970
|
+
if (doctype) {
|
|
971
|
+
return consumeTrailingNewline(src, leading + doctype[0].length);
|
|
955
972
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
function processHtmlContent(content) {
|
|
962
|
-
if (typeof document === "undefined") {
|
|
963
|
-
return applyInlineCodeClasses(content);
|
|
973
|
+
const open = /^<([a-z][a-z0-9]*)(?=[\s/>])[^>]*>/i.exec(rest);
|
|
974
|
+
if (!open) return void 0;
|
|
975
|
+
const tag = (open[1] ?? "").toLowerCase();
|
|
976
|
+
if (open[0].endsWith("/>") || RAW_HTML_VOID_TAGS.has(tag)) {
|
|
977
|
+
return consumeTrailingNewline(src, leading + open[0].length);
|
|
964
978
|
}
|
|
965
|
-
const
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
979
|
+
const tagRe = new RegExp(`<(/?)${tag}(?=[\\s/>])[^>]*>`, "ig");
|
|
980
|
+
tagRe.lastIndex = leading;
|
|
981
|
+
let depth = 0;
|
|
982
|
+
let match;
|
|
983
|
+
while ((match = tagRe.exec(src)) !== null) {
|
|
984
|
+
if (match[1] === "/") {
|
|
985
|
+
depth--;
|
|
986
|
+
} else if (!match[0].endsWith("/>")) {
|
|
987
|
+
depth++;
|
|
988
|
+
}
|
|
989
|
+
if (depth === 0) {
|
|
990
|
+
return consumeTrailingNewline(src, tagRe.lastIndex);
|
|
991
|
+
}
|
|
972
992
|
}
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
993
|
+
return void 0;
|
|
994
|
+
}
|
|
995
|
+
var renderer = new Marked();
|
|
996
|
+
renderer.use(markedSmartypantsLite());
|
|
997
|
+
renderer.use({
|
|
998
|
+
extensions: [
|
|
999
|
+
// Generic raw HTML passthrough. marked registers extension tokenizers by
|
|
1000
|
+
// unshifting, so dispatch order is the reverse of this array — placing
|
|
1001
|
+
// this first means it is tried LAST, letting the specific HTML
|
|
1002
|
+
// transformers below (callouts, output, wrapped code) take precedence.
|
|
1003
|
+
// Any other top-level HTML element is captured whole and emitted verbatim
|
|
1004
|
+
// so its interior is never re-parsed as markdown.
|
|
1005
|
+
{
|
|
1006
|
+
name: "rawHtmlBlock",
|
|
1007
|
+
level: "block",
|
|
1008
|
+
start(src) {
|
|
1009
|
+
const index = src.search(RAW_HTML_BLOCK_SEARCH);
|
|
1010
|
+
return index < 0 ? void 0 : index;
|
|
1011
|
+
},
|
|
1012
|
+
tokenizer(src) {
|
|
1013
|
+
const raw = matchRawHtmlBlock(src);
|
|
1014
|
+
if (raw) {
|
|
1015
|
+
return { type: "rawHtmlBlock", raw, text: raw };
|
|
990
1016
|
}
|
|
991
1017
|
},
|
|
992
|
-
{
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
text: match[3] ?? "",
|
|
1007
|
-
info: (match[2] ?? "").trim()
|
|
1008
|
-
};
|
|
1009
|
-
},
|
|
1010
|
-
renderer(token) {
|
|
1011
|
-
return renderMarkedCodeToken(token, token.raw);
|
|
1018
|
+
renderer(token) {
|
|
1019
|
+
return token.text;
|
|
1020
|
+
}
|
|
1021
|
+
},
|
|
1022
|
+
{
|
|
1023
|
+
name: "htmlLineBreak",
|
|
1024
|
+
level: "block",
|
|
1025
|
+
start(src) {
|
|
1026
|
+
return src.search(/^[ \t]*\{\.page-break\}[ \t]*(?:\n|$)/m);
|
|
1027
|
+
},
|
|
1028
|
+
tokenizer(src) {
|
|
1029
|
+
const match = /^[ \t]*\{\.page-break\}[ \t]*(?:\n|$)/.exec(src);
|
|
1030
|
+
if (match) {
|
|
1031
|
+
return { type: "htmlLineBreak", raw: match[0] };
|
|
1012
1032
|
}
|
|
1013
1033
|
},
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1034
|
+
renderer() {
|
|
1035
|
+
return "<div class=page-break></div>\n";
|
|
1036
|
+
}
|
|
1037
|
+
},
|
|
1038
|
+
{
|
|
1039
|
+
name: "preservedFence",
|
|
1040
|
+
level: "block",
|
|
1041
|
+
start(src) {
|
|
1042
|
+
return src.search(/^[ \t]*(`{3,}|~{3,})/m);
|
|
1043
|
+
},
|
|
1044
|
+
tokenizer(src) {
|
|
1045
|
+
const match = src.match(
|
|
1046
|
+
/^[ \t]*(`{3,}|~{3,})([^\r\n]*)\r?\n([\s\S]*?)\r?\n[ \t]*\1[ \t]*(?:\n|$)/
|
|
1047
|
+
);
|
|
1048
|
+
if (!match) return;
|
|
1049
|
+
return {
|
|
1050
|
+
type: "preservedFence",
|
|
1051
|
+
raw: match[0],
|
|
1052
|
+
text: match[3] ?? "",
|
|
1053
|
+
info: (match[2] ?? "").trim()
|
|
1054
|
+
};
|
|
1055
|
+
},
|
|
1056
|
+
renderer(token) {
|
|
1057
|
+
return renderMarkedCodeToken(token, token.raw);
|
|
1058
|
+
}
|
|
1059
|
+
},
|
|
1060
|
+
// RFM alert blocks: > [!TASK], > [!ACCORDION], etc.
|
|
1061
|
+
{
|
|
1062
|
+
name: "rfmBlock",
|
|
1063
|
+
level: "block",
|
|
1064
|
+
start(src) {
|
|
1065
|
+
return src.search(/^[ \t]*>+[ \t]?\[!/m);
|
|
1066
|
+
},
|
|
1067
|
+
tokenizer(src) {
|
|
1068
|
+
const raw = matchRfmBlock(src);
|
|
1069
|
+
if (raw !== void 0) {
|
|
1070
|
+
return { type: "rfmBlock", raw, text: raw };
|
|
1036
1071
|
}
|
|
1037
1072
|
},
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
return src.search(
|
|
1044
|
-
/<div\s+class="c-project-callout\s+c-project-callout--/
|
|
1045
|
-
);
|
|
1046
|
-
},
|
|
1047
|
-
tokenizer(src) {
|
|
1048
|
-
const match = /^<div\s+class="c-project-callout\s+c-project-callout--[a-z0-9-]+">[\s\S]*?<\/div>/.exec(
|
|
1049
|
-
src
|
|
1050
|
-
);
|
|
1051
|
-
if (match) {
|
|
1052
|
-
return { type: "rpfCallout", raw: match[0], text: match[0] };
|
|
1053
|
-
}
|
|
1054
|
-
},
|
|
1055
|
-
renderer(token) {
|
|
1056
|
-
return `${parseCalloutBlock(token.text).html}
|
|
1073
|
+
renderer(token) {
|
|
1074
|
+
const raw = token.text;
|
|
1075
|
+
const hintBlocks = splitTopLevelRfmHintBlocks(raw);
|
|
1076
|
+
if (hintBlocks.length > 1) {
|
|
1077
|
+
return `${renderHintsPanel(hintBlocks.map(getRfmHintBody))}
|
|
1057
1078
|
`;
|
|
1058
1079
|
}
|
|
1059
|
-
|
|
1060
|
-
// RPF task blocks: --- task --- ... --- /task ---
|
|
1061
|
-
{
|
|
1062
|
-
name: "rpfTask",
|
|
1063
|
-
level: "block",
|
|
1064
|
-
start(src) {
|
|
1065
|
-
return src.search(/^--- task ---$/m);
|
|
1066
|
-
},
|
|
1067
|
-
tokenizer(src) {
|
|
1068
|
-
const match = /^--- task ---\n[\s\S]*?\n--- \/task ---/.exec(src);
|
|
1069
|
-
if (match) {
|
|
1070
|
-
return { type: "rpfTask", raw: match[0], text: match[0] };
|
|
1071
|
-
}
|
|
1072
|
-
},
|
|
1073
|
-
renderer(token) {
|
|
1074
|
-
return `${parseTaskBlock(token.text).html}
|
|
1080
|
+
return `${parseRfmBlock(raw).html}
|
|
1075
1081
|
`;
|
|
1082
|
+
}
|
|
1083
|
+
},
|
|
1084
|
+
// RPF callout blocks: <div class="c-project-callout c-project-callout--{type}">
|
|
1085
|
+
{
|
|
1086
|
+
name: "rpfCallout",
|
|
1087
|
+
level: "block",
|
|
1088
|
+
start(src) {
|
|
1089
|
+
return src.search(
|
|
1090
|
+
/<div\s+class="c-project-callout\s+c-project-callout--/
|
|
1091
|
+
);
|
|
1092
|
+
},
|
|
1093
|
+
tokenizer(src) {
|
|
1094
|
+
const match = /^<div\s+class="c-project-callout\s+c-project-callout--[a-z0-9-]+">[\s\S]*?<\/div>/.exec(
|
|
1095
|
+
src
|
|
1096
|
+
);
|
|
1097
|
+
if (match) {
|
|
1098
|
+
return { type: "rpfCallout", raw: match[0], text: match[0] };
|
|
1076
1099
|
}
|
|
1077
1100
|
},
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
// RPF hints blocks: --- hints --- ... --- /hints ---
|
|
1081
|
-
createLegacyBlockExtension("rpfHints", "hints", parseHintsBlock),
|
|
1082
|
-
// RPF hint blocks: --- hint --- ... --- /hint ---
|
|
1083
|
-
createLegacyBlockExtension("rpfHint", "hint", parseHintBlock),
|
|
1084
|
-
// RPF challenge blocks: --- challenge --- ... --- /challenge ---
|
|
1085
|
-
createLegacyBlockExtension(
|
|
1086
|
-
"rpfChallenge",
|
|
1087
|
-
"challenge",
|
|
1088
|
-
parseChallengeBlock,
|
|
1089
|
-
false
|
|
1090
|
-
),
|
|
1091
|
-
// RPF print visibility blocks.
|
|
1092
|
-
createLegacyBlockExtension("rpfNoPrint", "no-print", parseNoPrintBlock),
|
|
1093
|
-
createLegacyBlockExtension(
|
|
1094
|
-
"rpfPrintOnly",
|
|
1095
|
-
"print-only",
|
|
1096
|
-
parsePrintOnlyBlock
|
|
1097
|
-
),
|
|
1098
|
-
// RPF save block: --- save ---
|
|
1099
|
-
{
|
|
1100
|
-
name: "rpfSave",
|
|
1101
|
-
level: "block",
|
|
1102
|
-
start(src) {
|
|
1103
|
-
return src.search(/^[ \t]*--- save ---$/m);
|
|
1104
|
-
},
|
|
1105
|
-
tokenizer(src) {
|
|
1106
|
-
const match = /^[ \t]*---\s+save\s+---[ \t]*(?:\n|$)/.exec(src);
|
|
1107
|
-
if (match) {
|
|
1108
|
-
return { type: "rpfSave", raw: match[0], text: match[0] };
|
|
1109
|
-
}
|
|
1110
|
-
},
|
|
1111
|
-
renderer() {
|
|
1112
|
-
return `${parseSaveBlock()}
|
|
1101
|
+
renderer(token) {
|
|
1102
|
+
return `${parseCalloutBlock(token.text).html}
|
|
1113
1103
|
`;
|
|
1104
|
+
}
|
|
1105
|
+
},
|
|
1106
|
+
// RPF task blocks: --- task --- ... --- /task ---
|
|
1107
|
+
{
|
|
1108
|
+
name: "rpfTask",
|
|
1109
|
+
level: "block",
|
|
1110
|
+
start(src) {
|
|
1111
|
+
return src.search(/^--- task ---$/m);
|
|
1112
|
+
},
|
|
1113
|
+
tokenizer(src) {
|
|
1114
|
+
const match = /^--- task ---\n[\s\S]*?\n--- \/task ---/.exec(src);
|
|
1115
|
+
if (match) {
|
|
1116
|
+
return { type: "rpfTask", raw: match[0], text: match[0] };
|
|
1114
1117
|
}
|
|
1115
1118
|
},
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
{
|
|
1119
|
-
name: "rpfCode",
|
|
1120
|
-
level: "block",
|
|
1121
|
-
start(src) {
|
|
1122
|
-
const bare = src.search(/^--- code ---$/m);
|
|
1123
|
-
const wrapped = src.search(/^<div class="c-project-code">/m);
|
|
1124
|
-
if (bare === -1) return wrapped;
|
|
1125
|
-
if (wrapped === -1) return bare;
|
|
1126
|
-
return Math.min(bare, wrapped);
|
|
1127
|
-
},
|
|
1128
|
-
tokenizer(src) {
|
|
1129
|
-
const wrappedMatch = /^<div class="c-project-code">\n(--- code ---\n[\s\S]*?\n--- \/code ---)\n\s*<\/div>/.exec(
|
|
1130
|
-
src
|
|
1131
|
-
);
|
|
1132
|
-
if (wrappedMatch) {
|
|
1133
|
-
return {
|
|
1134
|
-
type: "rpfCode",
|
|
1135
|
-
raw: wrappedMatch[0],
|
|
1136
|
-
text: wrappedMatch[1]
|
|
1137
|
-
};
|
|
1138
|
-
}
|
|
1139
|
-
const match = /^--- code ---\n[\s\S]*?\n--- \/code ---/.exec(src);
|
|
1140
|
-
if (match) {
|
|
1141
|
-
return { type: "rpfCode", raw: match[0], text: match[0] };
|
|
1142
|
-
}
|
|
1143
|
-
},
|
|
1144
|
-
renderer(token) {
|
|
1145
|
-
return `${parseRpfCodeBlock(token.text).html}
|
|
1119
|
+
renderer(token) {
|
|
1120
|
+
return `${parseTaskBlock(token.text).html}
|
|
1146
1121
|
`;
|
|
1122
|
+
}
|
|
1123
|
+
},
|
|
1124
|
+
// RPF collapse blocks: --- collapse --- ... --- /collapse ---
|
|
1125
|
+
createLegacyBlockExtension("rpfCollapse", "collapse", parseCollapseBlock),
|
|
1126
|
+
// RPF hints blocks: --- hints --- ... --- /hints ---
|
|
1127
|
+
createLegacyBlockExtension("rpfHints", "hints", parseHintsBlock),
|
|
1128
|
+
// RPF hint blocks: --- hint --- ... --- /hint ---
|
|
1129
|
+
createLegacyBlockExtension("rpfHint", "hint", parseHintBlock),
|
|
1130
|
+
// RPF challenge blocks: --- challenge --- ... --- /challenge ---
|
|
1131
|
+
createLegacyBlockExtension(
|
|
1132
|
+
"rpfChallenge",
|
|
1133
|
+
"challenge",
|
|
1134
|
+
parseChallengeBlock,
|
|
1135
|
+
false
|
|
1136
|
+
),
|
|
1137
|
+
// RPF print visibility blocks.
|
|
1138
|
+
createLegacyBlockExtension("rpfNoPrint", "no-print", parseNoPrintBlock),
|
|
1139
|
+
createLegacyBlockExtension(
|
|
1140
|
+
"rpfPrintOnly",
|
|
1141
|
+
"print-only",
|
|
1142
|
+
parsePrintOnlyBlock
|
|
1143
|
+
),
|
|
1144
|
+
// RPF save block: --- save ---
|
|
1145
|
+
{
|
|
1146
|
+
name: "rpfSave",
|
|
1147
|
+
level: "block",
|
|
1148
|
+
start(src) {
|
|
1149
|
+
return src.search(/^[ \t]*--- save ---$/m);
|
|
1150
|
+
},
|
|
1151
|
+
tokenizer(src) {
|
|
1152
|
+
const match = /^[ \t]*---\s+save\s+---[ \t]*(?:\n|$)/.exec(src);
|
|
1153
|
+
if (match) {
|
|
1154
|
+
return { type: "rpfSave", raw: match[0], text: match[0] };
|
|
1147
1155
|
}
|
|
1148
1156
|
},
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
name: "rpfOutput",
|
|
1152
|
-
level: "block",
|
|
1153
|
-
start(src) {
|
|
1154
|
-
return src.search(/^<div\s+class="c-project-output">\s*$/m);
|
|
1155
|
-
},
|
|
1156
|
-
tokenizer(src) {
|
|
1157
|
-
const match = /^<div\s+class="c-project-output">\s[\s\S]*?<\/div>/.exec(src);
|
|
1158
|
-
if (match) {
|
|
1159
|
-
return { type: "rpfOutput", raw: match[0], text: match[0] };
|
|
1160
|
-
}
|
|
1161
|
-
},
|
|
1162
|
-
renderer(token) {
|
|
1163
|
-
return `${parseOutputBlock(token.text)}
|
|
1157
|
+
renderer() {
|
|
1158
|
+
return `${parseSaveBlock()}
|
|
1164
1159
|
`;
|
|
1160
|
+
}
|
|
1161
|
+
},
|
|
1162
|
+
// RPF code blocks: --- code --- ... --- /code ---
|
|
1163
|
+
// Also handles the wrapped form: <div class="c-project-code">\n--- code ---\n...\n--- /code ---\n</div>
|
|
1164
|
+
{
|
|
1165
|
+
name: "rpfCode",
|
|
1166
|
+
level: "block",
|
|
1167
|
+
start(src) {
|
|
1168
|
+
const bare = src.search(/^--- code ---$/m);
|
|
1169
|
+
const wrapped = src.search(/^<div class="c-project-code">/m);
|
|
1170
|
+
if (bare === -1) return wrapped;
|
|
1171
|
+
if (wrapped === -1) return bare;
|
|
1172
|
+
return Math.min(bare, wrapped);
|
|
1173
|
+
},
|
|
1174
|
+
tokenizer(src) {
|
|
1175
|
+
const wrappedMatch = /^<div class="c-project-code">\n(--- code ---\n[\s\S]*?\n--- \/code ---)\n\s*<\/div>/.exec(
|
|
1176
|
+
src
|
|
1177
|
+
);
|
|
1178
|
+
if (wrappedMatch) {
|
|
1179
|
+
return {
|
|
1180
|
+
type: "rpfCode",
|
|
1181
|
+
raw: wrappedMatch[0],
|
|
1182
|
+
text: wrappedMatch[1]
|
|
1183
|
+
};
|
|
1165
1184
|
}
|
|
1185
|
+
const match = /^--- code ---\n[\s\S]*?\n--- \/code ---/.exec(src);
|
|
1186
|
+
if (match) {
|
|
1187
|
+
return { type: "rpfCode", raw: match[0], text: match[0] };
|
|
1188
|
+
}
|
|
1189
|
+
},
|
|
1190
|
+
renderer(token) {
|
|
1191
|
+
return `${parseRpfCodeBlock(token.text).html}
|
|
1192
|
+
`;
|
|
1166
1193
|
}
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1194
|
+
},
|
|
1195
|
+
// RPF output blocks: <div class="c-project-output"> ... </div>
|
|
1196
|
+
{
|
|
1197
|
+
name: "rpfOutput",
|
|
1198
|
+
level: "block",
|
|
1199
|
+
start(src) {
|
|
1200
|
+
return src.search(/^<div\s+class="c-project-output">\s*$/m);
|
|
1172
1201
|
},
|
|
1173
|
-
|
|
1174
|
-
const
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
const hasBlankLineAfterMarker = firstNewline !== -1 && /^\n[ \t]*\n/.test(raw.slice(firstNewline));
|
|
1180
|
-
let body = "";
|
|
1181
|
-
for (let index = 0; index < itemTokens.length; index++) {
|
|
1182
|
-
const token = itemTokens[index];
|
|
1183
|
-
if (index === 0 && !item.task && !hasBlankLineAfterMarker && itemTokens.length > 1 && token?.type === "paragraph") {
|
|
1184
|
-
body += this.parser.parseInline(token.tokens ?? []);
|
|
1185
|
-
continue;
|
|
1186
|
-
}
|
|
1187
|
-
if (token?.type === "code" || token?.type === "preservedFence") {
|
|
1188
|
-
body += renderMarkedCodeToken(token, rawFenceBlocks[rawFenceIndex]);
|
|
1189
|
-
rawFenceIndex++;
|
|
1190
|
-
continue;
|
|
1191
|
-
}
|
|
1192
|
-
body += this.parser.parse([token]).trimStart();
|
|
1202
|
+
tokenizer(src) {
|
|
1203
|
+
const match = /^<div\s+class="c-project-output">\s[\s\S]*?<\/div>/.exec(
|
|
1204
|
+
src
|
|
1205
|
+
);
|
|
1206
|
+
if (match) {
|
|
1207
|
+
return { type: "rpfOutput", raw: match[0], text: match[0] };
|
|
1193
1208
|
}
|
|
1194
|
-
|
|
1209
|
+
},
|
|
1210
|
+
renderer(token) {
|
|
1211
|
+
return `${parseOutputBlock(token.text)}
|
|
1195
1212
|
`;
|
|
1196
1213
|
}
|
|
1214
|
+
}
|
|
1215
|
+
],
|
|
1216
|
+
renderer: {
|
|
1217
|
+
// Override fenced code blocks to use Prism with our custom options.
|
|
1218
|
+
code(token) {
|
|
1219
|
+
return renderMarkedCodeToken(token);
|
|
1197
1220
|
},
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1221
|
+
listitem(item) {
|
|
1222
|
+
const itemTokens = Array.isArray(item.tokens) ? item.tokens : [];
|
|
1223
|
+
const raw = typeof item.raw === "string" ? item.raw : "";
|
|
1224
|
+
const rawFenceBlocks = extractListItemFenceBlocks(raw);
|
|
1225
|
+
let rawFenceIndex = 0;
|
|
1226
|
+
const firstNewline = raw.indexOf("\n");
|
|
1227
|
+
const hasBlankLineAfterMarker = firstNewline !== -1 && /^\n[ \t]*\n/.test(raw.slice(firstNewline));
|
|
1228
|
+
let body = "";
|
|
1229
|
+
for (let index = 0; index < itemTokens.length; index++) {
|
|
1230
|
+
const token = itemTokens[index];
|
|
1231
|
+
if (index === 0 && !item.task && !hasBlankLineAfterMarker && itemTokens.length > 1 && token?.type === "paragraph") {
|
|
1232
|
+
body += this.parser.parseInline(token.tokens ?? []);
|
|
1233
|
+
continue;
|
|
1234
|
+
}
|
|
1235
|
+
if (token?.type === "code" || token?.type === "preservedFence") {
|
|
1236
|
+
body += renderMarkedCodeToken(token, rawFenceBlocks[rawFenceIndex]);
|
|
1237
|
+
rawFenceIndex++;
|
|
1238
|
+
continue;
|
|
1239
|
+
}
|
|
1240
|
+
body += this.parser.parse([token]).trimStart();
|
|
1201
1241
|
}
|
|
1242
|
+
return `<li>${body}</li>
|
|
1243
|
+
`;
|
|
1202
1244
|
}
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
|
|
1245
|
+
},
|
|
1246
|
+
hooks: {
|
|
1247
|
+
postprocess(html) {
|
|
1248
|
+
return applyInlineCodeClasses(html);
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
});
|
|
1252
|
+
renderer.use(gfmHeadingId({ prefix: "" }));
|
|
1253
|
+
setMarkdownParser((input) => renderer.parse(input));
|
|
1254
|
+
function processEditorProject(content) {
|
|
1255
|
+
return renderer.parse(content);
|
|
1206
1256
|
}
|
|
1207
1257
|
export {
|
|
1208
1258
|
processEditorProject
|