@ai-matrx/print 0.5.10 → 0.6.1
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/CHANGELOG.md +53 -0
- package/README.md +14 -0
- package/dist/directives.cjs +66 -0
- package/dist/directives.cjs.map +1 -0
- package/dist/directives.d.cts +43 -0
- package/dist/directives.d.ts +43 -0
- package/dist/directives.js +45 -0
- package/dist/directives.js.map +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/markdown.cjs +723 -321
- package/dist/markdown.cjs.map +1 -1
- package/dist/markdown.d.cts +36 -10
- package/dist/markdown.d.ts +36 -10
- package/dist/markdown.js +723 -321
- package/dist/markdown.js.map +1 -1
- package/dist/pdf.cjs +86 -33
- package/dist/pdf.cjs.map +1 -1
- package/dist/pdf.d.cts +1 -0
- package/dist/pdf.d.ts +1 -0
- package/dist/pdf.js +84 -33
- package/dist/pdf.js.map +1 -1
- package/package.json +11 -1
package/dist/markdown.cjs
CHANGED
|
@@ -24,15 +24,21 @@ __export(markdown_exports, {
|
|
|
24
24
|
MARKDOWN_ARTICLE_TOKENS_CSS: () => MARKDOWN_ARTICLE_TOKENS_CSS,
|
|
25
25
|
MARKDOWN_DOCUMENT_CSS: () => MARKDOWN_DOCUMENT_CSS,
|
|
26
26
|
MARKDOWN_DOCUMENT_TOKENS_CSS: () => MARKDOWN_DOCUMENT_TOKENS_CSS,
|
|
27
|
+
MARKDOWN_PAGE_CSS: () => MARKDOWN_PAGE_CSS,
|
|
28
|
+
MARKDOWN_SHARED_CSS: () => MARKDOWN_SHARED_CSS,
|
|
29
|
+
PAGE_BREAK_CLASS: () => PAGE_BREAK_CLASS,
|
|
30
|
+
PAGE_BREAK_MARKDOWN: () => PAGE_BREAK_MARKDOWN,
|
|
27
31
|
PRINT_ACTIONS_CSS: () => PRINT_ACTIONS_CSS,
|
|
28
32
|
getMarkdownStylesheet: () => getMarkdownStylesheet,
|
|
29
33
|
hasThinkingContent: () => hasThinkingContent,
|
|
34
|
+
isPageBreakLine: () => isPageBreakLine,
|
|
30
35
|
markdownToArticleHtml: () => markdownToArticleHtml,
|
|
31
36
|
markdownToHtml: () => markdownToHtml,
|
|
32
37
|
markdownToHtmlForSkin: () => markdownToHtmlForSkin,
|
|
33
38
|
printMarkdown: () => printMarkdown,
|
|
34
39
|
removeThinkingContent: () => removeThinkingContent,
|
|
35
|
-
renderMarkdownDocument: () => renderMarkdownDocument
|
|
40
|
+
renderMarkdownDocument: () => renderMarkdownDocument,
|
|
41
|
+
splitAtPageBreaks: () => splitAtPageBreaks
|
|
36
42
|
});
|
|
37
43
|
module.exports = __toCommonJS(markdown_exports);
|
|
38
44
|
|
|
@@ -791,6 +797,265 @@ var MARKDOWN_DOCUMENT_CSS = `/*
|
|
|
791
797
|
}
|
|
792
798
|
`;
|
|
793
799
|
var MARKDOWN_ARTICLE_CSS = `*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}body{font-family:Georgia,'Times New Roman',serif;font-size:11pt;line-height:1.7;color:var(--matrx-print-fg, #1a1a1a);background:var(--matrx-print-bg, #fff);max-width:780px;margin:0 auto;padding:32px 40px}.print-actions{display:flex;gap:10px;margin-bottom:28px;padding-bottom:20px;border-bottom:2px solid var(--matrx-print-border, #e5e7eb)}.print-btn{display:inline-flex;align-items:center;gap:6px;padding:8px 20px;border-radius:8px;font-size:13px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;cursor:pointer;border:none;font-weight:600;transition:opacity .15s}.print-btn:hover{opacity:.85}.print-btn-primary{background:var(--matrx-print-accent, #2563eb);color:var(--matrx-print-bg, #fff)}.print-btn-secondary{background:var(--matrx-print-surface-sunken, #f3f4f6);color:var(--matrx-print-fg-muted, #374151);border:1px solid var(--matrx-print-border-muted, #d1d5db)}h1,h2,h3,h4,h5,h6{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','Helvetica Neue',sans-serif;font-weight:700;line-height:1.3;color:var(--matrx-print-heading, #111827);margin-top:1.6em;margin-bottom:.5em;page-break-after:avoid}h1{font-size:24pt;border-bottom:2px solid var(--matrx-print-border, #e5e7eb);padding-bottom:.3em;margin-top:0}h2{font-size:18pt;border-bottom:1px solid var(--matrx-print-border, #e5e7eb);padding-bottom:.2em}h3{font-size:14pt}p{margin-bottom:.9em;orphans:3;widows:3}a{color:var(--matrx-print-accent, #2563eb);text-decoration:underline;word-break:break-word}ul,ol{margin:.6em 0 .9em 0;padding-left:1.8em}li{margin-bottom:.3em;page-break-inside:avoid}blockquote{margin:1em 0;padding:.7em 1em .7em 1.2em;border-left:4px solid var(--matrx-print-quote-border, #6366f1);background:var(--matrx-print-quote-bg, #f5f3ff);border-radius:0 6px 6px 0;color:var(--matrx-print-fg-muted, #374151);font-style:italic;page-break-inside:avoid}.inline-code{font-family:'JetBrains Mono','Fira Code',monospace;font-size:9.5pt;background:var(--matrx-print-code-bg, #f1f5f9);color:var(--matrx-print-ink, #0f172a);padding:1px 5px;border-radius:4px;border:1px solid var(--matrx-print-line, #e2e8f0)}.code-block-wrapper{position:relative;margin:1em 0 1.2em;border-radius:8px;overflow:hidden;border:1px solid var(--matrx-print-line, #e2e8f0);page-break-inside:avoid}.code-lang{display:block;font-family:-apple-system,sans-serif;font-size:9pt;font-weight:600;color:var(--matrx-print-fg-subtle, #64748b);background:var(--matrx-print-surface, #f8fafc);padding:4px 12px;border-bottom:1px solid var(--matrx-print-line, #e2e8f0);text-transform:uppercase;letter-spacing:.05em}.code-block{margin:0;padding:14px 16px;background:var(--matrx-print-ink, #0f172a);color:var(--matrx-print-line, #e2e8f0);font-family:'JetBrains Mono',monospace;font-size:9pt;line-height:1.6;overflow-x:auto;white-space:pre;tab-size:2}.print-table{width:100%;border-collapse:collapse;margin:1em 0 1.2em;font-size:10pt;page-break-inside:avoid}.print-table th{background:var(--matrx-print-table-head-bg, #1e293b);color:var(--matrx-print-surface, #f8fafc);font-family:-apple-system,sans-serif;font-weight:600;font-size:9.5pt;text-align:left;padding:8px 12px;border:1px solid var(--matrx-print-table-head-border, #334155)}.print-table td{padding:7px 12px;border:1px solid var(--matrx-print-line, #e2e8f0);vertical-align:top}.print-table tr:nth-child(even) td{background:var(--matrx-print-surface, #f8fafc)}hr{border:none;border-top:2px solid var(--matrx-print-border, #e5e7eb);margin:1.5em 0}.print-figure{margin:1em 0 1.2em;page-break-inside:avoid}.print-image{display:block;max-width:100%;height:auto;margin:0 auto;border-radius:6px}.print-figure figcaption{margin-top:.4em;text-align:center;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;font-size:9.5pt;color:var(--matrx-print-fg-subtle, #64748b)}@media print{body{max-width:100%;padding:0;font-size:10.5pt}.print-actions{display:none!important}.code-block,.print-table th,blockquote{color-adjust:exact;-webkit-print-color-adjust:exact;print-color-adjust:exact}a[href]::after{content:" ("attr(href)")";font-size:9pt;color:var(--matrx-print-fg-subtle, #64748b)}a[href^="#"]::after,a[href^="javascript:"]::after{content:""}h1,h2,h3{page-break-after:avoid}pre,blockquote,table{page-break-inside:avoid}}`;
|
|
800
|
+
var MARKDOWN_SHARED_CSS = `
|
|
801
|
+
.matrx-page-break {
|
|
802
|
+
position: relative;
|
|
803
|
+
clear: both;
|
|
804
|
+
height: 0;
|
|
805
|
+
margin: 2.75rem 0;
|
|
806
|
+
border: 0;
|
|
807
|
+
border-top: 2px dashed var(--matrx-print-border-muted, #d1d5db);
|
|
808
|
+
}
|
|
809
|
+
.matrx-page-break::after {
|
|
810
|
+
content: attr(data-label);
|
|
811
|
+
position: absolute;
|
|
812
|
+
left: 50%;
|
|
813
|
+
top: 0;
|
|
814
|
+
transform: translate(-50%, -50%);
|
|
815
|
+
padding: 0 0.75rem;
|
|
816
|
+
background: var(--matrx-print-bg, #ffffff);
|
|
817
|
+
color: var(--matrx-print-fg-subtle, #6b7280);
|
|
818
|
+
font: 600 10px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
819
|
+
letter-spacing: 0.08em;
|
|
820
|
+
text-transform: uppercase;
|
|
821
|
+
white-space: nowrap;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
.matrx-artifact {
|
|
825
|
+
margin: 1.25em 0;
|
|
826
|
+
border: 1px solid var(--matrx-print-border, #e5e7eb);
|
|
827
|
+
border-radius: 8px;
|
|
828
|
+
overflow: hidden;
|
|
829
|
+
}
|
|
830
|
+
.matrx-artifact-header {
|
|
831
|
+
display: flex;
|
|
832
|
+
align-items: baseline;
|
|
833
|
+
gap: 0.6em;
|
|
834
|
+
padding: 0.45em 0.9em;
|
|
835
|
+
background: var(--matrx-print-surface, #f8fafc);
|
|
836
|
+
border-bottom: 1px solid var(--matrx-print-border, #e5e7eb);
|
|
837
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
838
|
+
}
|
|
839
|
+
.matrx-artifact-kind {
|
|
840
|
+
font-size: 0.72em;
|
|
841
|
+
font-weight: 700;
|
|
842
|
+
letter-spacing: 0.06em;
|
|
843
|
+
text-transform: uppercase;
|
|
844
|
+
color: var(--matrx-print-accent, #2563eb);
|
|
845
|
+
}
|
|
846
|
+
.matrx-artifact-title {
|
|
847
|
+
font-weight: 600;
|
|
848
|
+
color: var(--matrx-print-fg, #1a1a1a);
|
|
849
|
+
}
|
|
850
|
+
.matrx-artifact-body {
|
|
851
|
+
padding: 0.8em 0.9em;
|
|
852
|
+
}
|
|
853
|
+
.matrx-artifact-body > :first-child {
|
|
854
|
+
margin-top: 0;
|
|
855
|
+
}
|
|
856
|
+
.matrx-artifact-body > :last-child {
|
|
857
|
+
margin-bottom: 0;
|
|
858
|
+
}
|
|
859
|
+
/* The panel's kind chip already names these languages. */
|
|
860
|
+
.matrx-artifact:is([data-artifact-type="html"], [data-artifact-type="react"], [data-artifact-type="svg"], [data-artifact-type="mermaid"], [data-artifact-type="diff"], [data-artifact-type="iframe"]) .code-lang {
|
|
861
|
+
display: none;
|
|
862
|
+
}
|
|
863
|
+
.matrx-artifact-empty {
|
|
864
|
+
margin: 0;
|
|
865
|
+
font-style: italic;
|
|
866
|
+
color: var(--matrx-print-fg-subtle, #6b7280);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
li.matrx-task-item {
|
|
870
|
+
list-style: none;
|
|
871
|
+
}
|
|
872
|
+
/* The box IS the marker \u2014 the document skin's custom bullet must not sit on it. */
|
|
873
|
+
:is(.matrx-content-container, .content) li.matrx-task-item::before {
|
|
874
|
+
content: none;
|
|
875
|
+
}
|
|
876
|
+
.matrx-task-box {
|
|
877
|
+
position: relative;
|
|
878
|
+
display: inline-block;
|
|
879
|
+
width: 0.9em;
|
|
880
|
+
height: 0.9em;
|
|
881
|
+
margin: 0 0.5em 0 -1.35em;
|
|
882
|
+
border: 1.5px solid var(--matrx-print-fg-muted, #4a4a4a);
|
|
883
|
+
border-radius: 3px;
|
|
884
|
+
vertical-align: -0.1em;
|
|
885
|
+
}
|
|
886
|
+
.matrx-task-box.is-checked::after {
|
|
887
|
+
content: "\\2713";
|
|
888
|
+
position: absolute;
|
|
889
|
+
inset: 0;
|
|
890
|
+
font-size: 0.8em;
|
|
891
|
+
font-weight: 700;
|
|
892
|
+
line-height: 1.1em;
|
|
893
|
+
text-align: center;
|
|
894
|
+
color: var(--matrx-print-fg, #1a1a1a);
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
:is(.matrx-content-container, .content) del {
|
|
898
|
+
color: var(--matrx-print-fg-muted, #4a4a4a);
|
|
899
|
+
text-decoration: line-through;
|
|
900
|
+
}
|
|
901
|
+
:is(.matrx-content-container, .content) hr.matrx-hr-accent {
|
|
902
|
+
height: 2px;
|
|
903
|
+
margin: 1.75em 0;
|
|
904
|
+
border: 0;
|
|
905
|
+
border-radius: 2px;
|
|
906
|
+
background: var(--matrx-print-accent, #2563eb);
|
|
907
|
+
opacity: 0.6;
|
|
908
|
+
}
|
|
909
|
+
:is(.matrx-content-container, .content) hr.matrx-hr-heavy {
|
|
910
|
+
height: 4px;
|
|
911
|
+
margin: 2.25em 0;
|
|
912
|
+
border: 0;
|
|
913
|
+
border-radius: 4px;
|
|
914
|
+
background: var(--matrx-print-accent, #2563eb);
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
.matrx-flashcards-print {
|
|
918
|
+
margin: 1em 0 1.2em;
|
|
919
|
+
}
|
|
920
|
+
.matrx-fc {
|
|
921
|
+
display: grid;
|
|
922
|
+
grid-template-columns: 1fr 1fr;
|
|
923
|
+
margin-bottom: 0.5em;
|
|
924
|
+
border: 1px solid var(--matrx-print-border, #e5e7eb);
|
|
925
|
+
border-radius: 6px;
|
|
926
|
+
}
|
|
927
|
+
.matrx-fc-front,
|
|
928
|
+
.matrx-fc-back {
|
|
929
|
+
padding: 0.6em 0.8em;
|
|
930
|
+
}
|
|
931
|
+
.matrx-fc-front {
|
|
932
|
+
font-weight: 600;
|
|
933
|
+
border-right: 1px dashed var(--matrx-print-border-muted, #d1d5db);
|
|
934
|
+
}
|
|
935
|
+
.matrx-fc-num {
|
|
936
|
+
display: inline-block;
|
|
937
|
+
min-width: 1.6em;
|
|
938
|
+
font-size: 0.85em;
|
|
939
|
+
color: var(--matrx-print-fg-subtle, #6b7280);
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
@media print {
|
|
943
|
+
.matrx-page-break {
|
|
944
|
+
margin: 0;
|
|
945
|
+
border: 0;
|
|
946
|
+
break-after: page;
|
|
947
|
+
page-break-after: always;
|
|
948
|
+
}
|
|
949
|
+
.matrx-page-break::after {
|
|
950
|
+
content: none;
|
|
951
|
+
}
|
|
952
|
+
.matrx-page-break + * {
|
|
953
|
+
margin-top: 0;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/* Scoped to the rendered markdown (.matrx-content-container = document
|
|
957
|
+
skin, .content = print window) so a host page that carries this sheet
|
|
958
|
+
\u2014 a WordPress theme \u2014 keeps its own print rules everywhere else. */
|
|
959
|
+
|
|
960
|
+
/* Long tables flow across pages: the header row repeats, rows never split. */
|
|
961
|
+
:is(.matrx-content-container, .content) thead {
|
|
962
|
+
display: table-header-group;
|
|
963
|
+
}
|
|
964
|
+
:is(.matrx-content-container, .content) :is(tr, img, figure),
|
|
965
|
+
.matrx-fc,
|
|
966
|
+
.matrx-task-item {
|
|
967
|
+
break-inside: avoid;
|
|
968
|
+
page-break-inside: avoid;
|
|
969
|
+
}
|
|
970
|
+
:is(.matrx-content-container, .content) :is(h1, h2, h3, h4, h5, h6),
|
|
971
|
+
.matrx-artifact-header {
|
|
972
|
+
break-after: avoid;
|
|
973
|
+
page-break-after: avoid;
|
|
974
|
+
break-inside: avoid;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
/* Paper cannot scroll: long code lines wrap instead of being cut off. */
|
|
978
|
+
:is(.matrx-content-container, .content) :is(pre, pre code) {
|
|
979
|
+
white-space: pre-wrap !important;
|
|
980
|
+
overflow-wrap: anywhere;
|
|
981
|
+
overflow: visible !important;
|
|
982
|
+
}
|
|
983
|
+
.code-block-wrapper,
|
|
984
|
+
.matrx-artifact {
|
|
985
|
+
overflow: visible;
|
|
986
|
+
}
|
|
987
|
+
:is(.matrx-content-container, .content) img {
|
|
988
|
+
max-width: 100% !important;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
.matrx-artifact-header,
|
|
992
|
+
.matrx-task-box {
|
|
993
|
+
-webkit-print-color-adjust: exact;
|
|
994
|
+
print-color-adjust: exact;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
a.matrx-link[href^="http"]::after {
|
|
998
|
+
content: " (" attr(href) ")";
|
|
999
|
+
font-size: 0.85em;
|
|
1000
|
+
color: var(--matrx-print-fg-subtle, #6b7280);
|
|
1001
|
+
overflow-wrap: anywhere;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
`;
|
|
1005
|
+
var MARKDOWN_PAGE_CSS = `
|
|
1006
|
+
@page {
|
|
1007
|
+
margin: 0.6in 0.65in 0.7in;
|
|
1008
|
+
@bottom-center {
|
|
1009
|
+
content: counter(page) " / " counter(pages);
|
|
1010
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
1011
|
+
font-size: 8pt;
|
|
1012
|
+
color: var(--matrx-print-fg-subtle, #6b7280);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
`;
|
|
1016
|
+
var MARKDOWN_PAGE_PLAIN_CSS = `
|
|
1017
|
+
@page {
|
|
1018
|
+
margin: 0.6in 0.65in 0.7in;
|
|
1019
|
+
}
|
|
1020
|
+
`;
|
|
1021
|
+
|
|
1022
|
+
// src/directives.ts
|
|
1023
|
+
var PAGE_BREAK_MARKDOWN = "<!-- pagebreak -->";
|
|
1024
|
+
var PAGE_BREAK_CLASS = "matrx-page-break";
|
|
1025
|
+
var PAGE_BREAK_LABEL = "Page break";
|
|
1026
|
+
var COMMENT_FORM = /^<!--\s*(?:page[\s_-]?break|new[\s_-]?page)\s*-->$/i;
|
|
1027
|
+
var LATEX_FORM = /^\\(?:pagebreak|newpage|clearpage)\s*(?:\{\s*\})?$/i;
|
|
1028
|
+
var DIV_FORM = /^<div\b([^>]*)>\s*<\/div>$|^<div\b([^>]*)\/>$/i;
|
|
1029
|
+
var DIV_BREAK_ATTR = /(?:page-break-(?:after|before)\s*:\s*always|break-(?:after|before)\s*:\s*page|class\s*=\s*["'](?:[^"']*\s)?page-?break(?:\s[^"']*)?["'])/i;
|
|
1030
|
+
function isPageBreakLine(line) {
|
|
1031
|
+
const t = line.trim();
|
|
1032
|
+
if (t.length < 8 || t.length > 200) return false;
|
|
1033
|
+
if (COMMENT_FORM.test(t) || LATEX_FORM.test(t)) return true;
|
|
1034
|
+
const div = DIV_FORM.exec(t);
|
|
1035
|
+
if (!div) return false;
|
|
1036
|
+
return DIV_BREAK_ATTR.test(div[1] ?? div[2] ?? "");
|
|
1037
|
+
}
|
|
1038
|
+
function splitAtPageBreaks(markdown) {
|
|
1039
|
+
const lines = markdown.split(/\r?\n/);
|
|
1040
|
+
const pieces = [];
|
|
1041
|
+
let current = [];
|
|
1042
|
+
let fence = null;
|
|
1043
|
+
for (const line of lines) {
|
|
1044
|
+
const fenceMatch = /^\s*(`{3,}|~{3,})/.exec(line);
|
|
1045
|
+
if (fenceMatch) {
|
|
1046
|
+
const marker = fenceMatch[1];
|
|
1047
|
+
if (fence === null) fence = marker;
|
|
1048
|
+
else if (marker[0] === fence[0] && marker.length >= fence.length) fence = null;
|
|
1049
|
+
} else if (fence === null && isPageBreakLine(line)) {
|
|
1050
|
+
if (current.join("\n").trim()) pieces.push(current.join("\n"));
|
|
1051
|
+
current = [];
|
|
1052
|
+
continue;
|
|
1053
|
+
}
|
|
1054
|
+
current.push(line);
|
|
1055
|
+
}
|
|
1056
|
+
if (current.join("\n").trim() || pieces.length === 0) pieces.push(current.join("\n"));
|
|
1057
|
+
return pieces;
|
|
1058
|
+
}
|
|
794
1059
|
|
|
795
1060
|
// src/markdown.ts
|
|
796
1061
|
function removeThinkingContent(content) {
|
|
@@ -803,80 +1068,166 @@ function hasThinkingContent(content) {
|
|
|
803
1068
|
if (!content) return false;
|
|
804
1069
|
return /<thinking>|<think>|<reasoning>/i.test(content);
|
|
805
1070
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
if (rows.length > 0) {
|
|
825
|
-
tableHtml += '<tbody class="matrx-table-body">\n';
|
|
826
|
-
for (const row of rows) {
|
|
827
|
-
if (row.length === 0) continue;
|
|
828
|
-
tableHtml += '<tr class="matrx-table-row">\n';
|
|
829
|
-
for (const cell of row) {
|
|
830
|
-
tableHtml += `<td class="matrx-table-cell">${cell}</td>
|
|
831
|
-
`;
|
|
832
|
-
}
|
|
833
|
-
tableHtml += "</tr>\n";
|
|
834
|
-
}
|
|
835
|
-
tableHtml += "</tbody>\n";
|
|
836
|
-
}
|
|
837
|
-
return `${tableHtml}</table>
|
|
838
|
-
`;
|
|
1071
|
+
var TOKEN_OPEN = "\uE000";
|
|
1072
|
+
var TOKEN_CLOSE = "\uE001";
|
|
1073
|
+
var BLOCK_TOKEN = (i) => `${TOKEN_OPEN}B${i}${TOKEN_CLOSE}`;
|
|
1074
|
+
var INLINE_TOKEN = (i) => `${TOKEN_OPEN}I${i}${TOKEN_CLOSE}`;
|
|
1075
|
+
var TOKEN_RE = /\uE000([BI])(\d+)\uE001/g;
|
|
1076
|
+
var PARAGRAPH_WRAPPED_BLOCK_RE = /<p[^>]*>\s*(\uE000B\d+\uE001)\s*<\/p>/g;
|
|
1077
|
+
var Stash = class {
|
|
1078
|
+
items = [];
|
|
1079
|
+
block(html) {
|
|
1080
|
+
return BLOCK_TOKEN(this.items.push(html) - 1);
|
|
1081
|
+
}
|
|
1082
|
+
inline(html) {
|
|
1083
|
+
return INLINE_TOKEN(this.items.push(html) - 1);
|
|
1084
|
+
}
|
|
1085
|
+
restore(html) {
|
|
1086
|
+
let out = html;
|
|
1087
|
+
for (let pass = 0; pass < 8 && out.includes(TOKEN_OPEN); pass++) {
|
|
1088
|
+
out = out.replace(PARAGRAPH_WRAPPED_BLOCK_RE, "$1").replace(TOKEN_RE, (_m, _k, i) => this.items[Number(i)] ?? "");
|
|
839
1089
|
}
|
|
840
|
-
|
|
1090
|
+
return out;
|
|
1091
|
+
}
|
|
1092
|
+
};
|
|
1093
|
+
var DOCUMENT_GRAMMAR = {
|
|
1094
|
+
skin: "document",
|
|
1095
|
+
table: "matrx-table",
|
|
1096
|
+
thead: "matrx-table-head",
|
|
1097
|
+
tbody: "matrx-table-body",
|
|
1098
|
+
tr: "matrx-table-row",
|
|
1099
|
+
th: "matrx-table-header",
|
|
1100
|
+
td: "matrx-table-cell",
|
|
1101
|
+
ul: "matrx-list matrx-bullet-list",
|
|
1102
|
+
ol: "matrx-list matrx-numbered-list",
|
|
1103
|
+
ulNested: "matrx-list matrx-bullet-list matrx-nested-list",
|
|
1104
|
+
olNested: "matrx-list matrx-numbered-list matrx-nested-list",
|
|
1105
|
+
li: "matrx-list-item",
|
|
1106
|
+
hr: "matrx-hr",
|
|
1107
|
+
del: "matrx-del",
|
|
1108
|
+
strong: "matrx-strong",
|
|
1109
|
+
em: "matrx-em",
|
|
1110
|
+
inlineCode: "matrx-inline-code"
|
|
1111
|
+
};
|
|
1112
|
+
var ARTICLE_GRAMMAR = {
|
|
1113
|
+
skin: "article",
|
|
1114
|
+
table: "print-table",
|
|
1115
|
+
thead: "",
|
|
1116
|
+
tbody: "",
|
|
1117
|
+
tr: "",
|
|
1118
|
+
th: "",
|
|
1119
|
+
td: "",
|
|
1120
|
+
ul: "",
|
|
1121
|
+
ol: "",
|
|
1122
|
+
ulNested: "",
|
|
1123
|
+
olNested: "",
|
|
1124
|
+
li: "",
|
|
1125
|
+
hr: "",
|
|
1126
|
+
del: "",
|
|
1127
|
+
strong: "",
|
|
1128
|
+
em: "",
|
|
1129
|
+
inlineCode: "inline-code"
|
|
1130
|
+
};
|
|
1131
|
+
var cls = (...names) => {
|
|
1132
|
+
const joined = names.filter(Boolean).join(" ");
|
|
1133
|
+
return joined ? ` class="${joined}"` : "";
|
|
1134
|
+
};
|
|
1135
|
+
var PAGE_BREAK_HTML = `<div class="${PAGE_BREAK_CLASS}" role="separator" aria-label="${PAGE_BREAK_LABEL}" data-label="${PAGE_BREAK_LABEL}"></div>`;
|
|
1136
|
+
function codeBlockHtml(g, code, language) {
|
|
1137
|
+
const body = (0, import_html_escape2.escapeHtml)(code.replace(/\n$/, ""));
|
|
1138
|
+
const lang = language.trim();
|
|
1139
|
+
if (g.skin === "article") {
|
|
1140
|
+
const label = lang ? `<span class="code-lang">${(0, import_html_escape2.escapeHtml)(lang)}</span>` : "";
|
|
1141
|
+
return `<div class="code-block-wrapper">${label}<pre class="code-block"><code>${body}</code></pre></div>`;
|
|
1142
|
+
}
|
|
1143
|
+
const dataLang = lang ? ` data-language="${(0, import_html_escape2.escapeHtml)(lang)}"` : "";
|
|
1144
|
+
return `<pre class="matrx-code-block"${dataLang}><code class="matrx-code">${body}</code></pre>`;
|
|
1145
|
+
}
|
|
1146
|
+
var CODE_ARTIFACT_LANGUAGE = {
|
|
1147
|
+
html: "html",
|
|
1148
|
+
react: "tsx",
|
|
1149
|
+
svg: "svg",
|
|
1150
|
+
mermaid: "mermaid",
|
|
1151
|
+
diff: "diff",
|
|
1152
|
+
iframe: "html",
|
|
1153
|
+
code: ""
|
|
1154
|
+
};
|
|
1155
|
+
var ARTIFACT_TYPE_LABEL = {
|
|
1156
|
+
html: "HTML",
|
|
1157
|
+
svg: "SVG",
|
|
1158
|
+
react: "React",
|
|
1159
|
+
iframe: "Embed"
|
|
1160
|
+
};
|
|
1161
|
+
function parseTagAttributes(tag) {
|
|
1162
|
+
const attrs = {};
|
|
1163
|
+
for (const m of tag.matchAll(/([a-zA-Z_][\w-]*)\s*=\s*(?:"([^"]*)"|'([^']*)')/g)) {
|
|
1164
|
+
attrs[m[1].toLowerCase()] = m[2] ?? m[3] ?? "";
|
|
1165
|
+
}
|
|
1166
|
+
return attrs;
|
|
1167
|
+
}
|
|
1168
|
+
function artifactLabel(type) {
|
|
1169
|
+
if (!type) return "Artifact";
|
|
1170
|
+
return ARTIFACT_TYPE_LABEL[type] ?? type.replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
1171
|
+
}
|
|
1172
|
+
function looksLikeJson(body) {
|
|
1173
|
+
const t = body.trim();
|
|
1174
|
+
if (!/^[[{]/.test(t)) return false;
|
|
1175
|
+
try {
|
|
1176
|
+
JSON.parse(t);
|
|
1177
|
+
return true;
|
|
1178
|
+
} catch {
|
|
1179
|
+
return false;
|
|
1180
|
+
}
|
|
841
1181
|
}
|
|
842
|
-
function
|
|
843
|
-
const
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
1182
|
+
function artifactHtml(g, attrs, body, ctx) {
|
|
1183
|
+
const type = (attrs.type ?? "").toLowerCase();
|
|
1184
|
+
const title = attrs.title?.trim() ?? "";
|
|
1185
|
+
const header = `<div class="matrx-artifact-header"><span class="matrx-artifact-kind">${(0, import_html_escape2.escapeHtml)(
|
|
1186
|
+
artifactLabel(type)
|
|
1187
|
+
)}</span>${title ? `<span class="matrx-artifact-title">${(0, import_html_escape2.escapeHtml)(title)}</span>` : ""}</div>`;
|
|
1188
|
+
let inner;
|
|
1189
|
+
const trimmed = (body ?? "").replace(/^\n+|\n+$/g, "");
|
|
1190
|
+
if (body === null || !trimmed.trim()) {
|
|
1191
|
+
inner = `<p class="matrx-artifact-empty">Saved artifact \u2014 open it in AI Matrx to view its content.</p>`;
|
|
1192
|
+
} else if (/^\s*(`{3,}|~{3,})/.test(trimmed)) {
|
|
1193
|
+
inner = convertBody(g.skin, trimmed, ctx);
|
|
1194
|
+
} else if (type in CODE_ARTIFACT_LANGUAGE) {
|
|
1195
|
+
inner = codeBlockHtml(g, trimmed, CODE_ARTIFACT_LANGUAGE[type] || attrs.language || attrs.lang || "");
|
|
1196
|
+
} else if (looksLikeJson(trimmed)) {
|
|
1197
|
+
inner = codeBlockHtml(g, JSON.stringify(JSON.parse(trimmed), null, 2), "json");
|
|
1198
|
+
} else {
|
|
1199
|
+
inner = convertBody(g.skin, trimmed, ctx);
|
|
851
1200
|
}
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
const
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
1201
|
+
return `<section class="matrx-artifact" data-artifact-type="${(0, import_html_escape2.escapeHtml)(type || "artifact")}">${header}<div class="matrx-artifact-body">${inner}</div></section>`;
|
|
1202
|
+
}
|
|
1203
|
+
function parseFlashcards(content, notice) {
|
|
1204
|
+
const cards = [];
|
|
1205
|
+
for (const card of content.split(/\n*---\n*/g).map((c) => c.trim()).filter(Boolean)) {
|
|
1206
|
+
const front = card.match(/Front:\s*(.+?)(?=\n|$)/i)?.[1]?.trim();
|
|
1207
|
+
const back = card.match(/Back:\s*([\s\S]+?)$/i)?.[1]?.trim();
|
|
1208
|
+
if (!front || !back) {
|
|
1209
|
+
notice?.({
|
|
1210
|
+
code: "flashcard-malformed",
|
|
1211
|
+
message: `A flashcard was skipped: it is missing a ${front ? "Back:" : "Front:"} line.`,
|
|
1212
|
+
remedy: 'Every card inside <flashcards> needs both "Front: \u2026" and "Back: \u2026", separated from the next card by ---.'
|
|
1213
|
+
});
|
|
1214
|
+
continue;
|
|
1215
|
+
}
|
|
1216
|
+
cards.push({ front, back });
|
|
1217
|
+
}
|
|
1218
|
+
return cards;
|
|
1219
|
+
}
|
|
1220
|
+
function flashcardsHtml(g, cards, ctx) {
|
|
1221
|
+
if (g.skin === "article") {
|
|
1222
|
+
return `<div class="matrx-flashcards-print">${cards.map(
|
|
1223
|
+
(c, i) => `<div class="matrx-fc"><div class="matrx-fc-front"><span class="matrx-fc-num">${i + 1}</span>${c.front}</div><div class="matrx-fc-back">${c.back}</div></div>`
|
|
1224
|
+
).join("")}</div>`;
|
|
1225
|
+
}
|
|
1226
|
+
let out = '<div class="matrx-flashcards-container">\n';
|
|
1227
|
+
for (const { front, back } of cards) {
|
|
1228
|
+
ctx.cardIndex++;
|
|
1229
|
+
const cardId = `card${ctx.cardIndex}`;
|
|
1230
|
+
out += ` <div class="matrx-flashcard">
|
|
880
1231
|
<input type="checkbox" class="matrx-flashcard-input" id="${cardId}">
|
|
881
1232
|
<label for="${cardId}" class="matrx-flashcard-label"></label>
|
|
882
1233
|
<div class="matrx-flashcard-inner">
|
|
@@ -896,154 +1247,318 @@ function markdownToHtml(markdown, options = {}) {
|
|
|
896
1247
|
</div>
|
|
897
1248
|
|
|
898
1249
|
`;
|
|
1250
|
+
}
|
|
1251
|
+
return `${out}</div>
|
|
1252
|
+
`;
|
|
1253
|
+
}
|
|
1254
|
+
var FENCE_OPEN = /^(\s*)(`{3,}|~{3,})\s*([^\s`]*)[^`]*$/;
|
|
1255
|
+
function protectRegions(markdown, g, stash, ctx) {
|
|
1256
|
+
const lines = markdown.split(/\r?\n/);
|
|
1257
|
+
const out = [];
|
|
1258
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1259
|
+
const line = lines[i];
|
|
1260
|
+
const fence = FENCE_OPEN.exec(line);
|
|
1261
|
+
if (fence) {
|
|
1262
|
+
const marker = fence[2];
|
|
1263
|
+
const code = [];
|
|
1264
|
+
let j = i + 1;
|
|
1265
|
+
for (; j < lines.length; j++) {
|
|
1266
|
+
const close = /^\s*(`{3,}|~{3,})\s*$/.exec(lines[j]);
|
|
1267
|
+
if (close && close[1][0] === marker[0] && close[1].length >= marker.length) break;
|
|
1268
|
+
code.push(lines[j]);
|
|
899
1269
|
}
|
|
900
|
-
|
|
901
|
-
const
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
1270
|
+
const indent = fence[1].length;
|
|
1271
|
+
const dedented = code.map((l) => l.slice(Math.min(indent, l.length - l.trimStart().length)));
|
|
1272
|
+
out.push(stash.block(codeBlockHtml(g, dedented.join("\n"), fence[3] ?? "")));
|
|
1273
|
+
i = j;
|
|
1274
|
+
continue;
|
|
1275
|
+
}
|
|
1276
|
+
if (isPageBreakLine(line)) {
|
|
1277
|
+
out.push(stash.block(PAGE_BREAK_HTML));
|
|
1278
|
+
continue;
|
|
905
1279
|
}
|
|
1280
|
+
const open = /^\s*<artifact\b((?:[^>"']|"[^"]*"|'[^']*')*?)(\/?)>(.*)$/i.exec(line);
|
|
1281
|
+
if (open) {
|
|
1282
|
+
const attrs = parseTagAttributes(open[1] ?? "");
|
|
1283
|
+
if (open[2] === "/") {
|
|
1284
|
+
out.push(stash.block(artifactHtml(g, attrs, null, ctx)));
|
|
1285
|
+
if (open[3]?.trim()) out.push(open[3]);
|
|
1286
|
+
continue;
|
|
1287
|
+
}
|
|
1288
|
+
const bodyLines = [];
|
|
1289
|
+
let rest = open[3] ?? "";
|
|
1290
|
+
let after = "";
|
|
1291
|
+
let j = i;
|
|
1292
|
+
let inFence = null;
|
|
1293
|
+
for (; ; ) {
|
|
1294
|
+
const closeAt = inFence ? -1 : rest.search(/<\/artifact\s*>/i);
|
|
1295
|
+
if (closeAt >= 0) {
|
|
1296
|
+
bodyLines.push(rest.slice(0, closeAt));
|
|
1297
|
+
after = rest.slice(closeAt).replace(/^<\/artifact\s*>/i, "");
|
|
1298
|
+
break;
|
|
1299
|
+
}
|
|
1300
|
+
bodyLines.push(rest);
|
|
1301
|
+
const f = /^\s*(`{3,}|~{3,})/.exec(rest);
|
|
1302
|
+
if (f) {
|
|
1303
|
+
const m = f[1];
|
|
1304
|
+
if (inFence === null) inFence = m;
|
|
1305
|
+
else if (m[0] === inFence[0] && m.length >= inFence.length) inFence = null;
|
|
1306
|
+
}
|
|
1307
|
+
j++;
|
|
1308
|
+
if (j >= lines.length) break;
|
|
1309
|
+
rest = lines[j];
|
|
1310
|
+
}
|
|
1311
|
+
const html = ctx.depth >= 3 ? codeBlockHtml(g, bodyLines.join("\n"), "") : artifactHtml(g, attrs, bodyLines.join("\n"), { ...ctx, depth: ctx.depth + 1 });
|
|
1312
|
+
out.push(stash.block(html));
|
|
1313
|
+
if (after.trim()) out.push(after);
|
|
1314
|
+
i = j;
|
|
1315
|
+
continue;
|
|
1316
|
+
}
|
|
1317
|
+
out.push(line);
|
|
1318
|
+
}
|
|
1319
|
+
let text = out.join("\n");
|
|
1320
|
+
text = text.replace(
|
|
1321
|
+
/`([^`\n]+)`/g,
|
|
1322
|
+
(_m, code) => stash.inline(`<code${cls(g.inlineCode)}>${(0, import_html_escape2.escapeHtml)(code)}</code>`)
|
|
906
1323
|
);
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
1324
|
+
text = text.replace(/<!--[\s\S]*?-->/g, "");
|
|
1325
|
+
const before = text;
|
|
1326
|
+
for (let pass = 0; pass < 8; pass++) {
|
|
1327
|
+
const next = text.replace(/<(script|style|head)\b[^>]*>[\s\S]*?<\/\1\s*>/gi, "").replace(/<\/?(?:script|style|head|html|body)\b[^>]*>/gi, "").replace(/<!DOCTYPE[^>]*>/gi, "");
|
|
1328
|
+
if (next === text) break;
|
|
1329
|
+
text = next;
|
|
1330
|
+
}
|
|
1331
|
+
text = sanitizeRawTags(text);
|
|
1332
|
+
if (text !== before && !ctx.strippedAnnounced) {
|
|
1333
|
+
ctx.strippedAnnounced = true;
|
|
1334
|
+
ctx.options.onNotice?.({
|
|
1335
|
+
code: "html-stripped",
|
|
1336
|
+
message: "Unsafe or page-level markup (a <script>, <style>, <head>, an <html>/<body> shell, an on\u2026 event handler, or a javascript: URL) was removed from the rendered copy.",
|
|
1337
|
+
remedy: 'To show a full HTML page, put it in an ```html fence or an <artifact type="html"> block \u2014 it is printed as its source.'
|
|
1338
|
+
});
|
|
1339
|
+
}
|
|
1340
|
+
text = text.replace(/<flashcards>([\s\S]*?)<\/flashcards>/gi, (_m, content) => {
|
|
1341
|
+
const cards = parseFlashcards(content, ctx.options.onNotice);
|
|
1342
|
+
return cards.length ? `
|
|
1343
|
+
${stash.block(flashcardsHtml(g, cards, ctx))}
|
|
1344
|
+
` : "";
|
|
1345
|
+
});
|
|
1346
|
+
return text;
|
|
1347
|
+
}
|
|
1348
|
+
var URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "action", "formaction", "xlink:href", "poster", "background"]);
|
|
1349
|
+
function decodeForSchemeCheck(value) {
|
|
1350
|
+
return value.replace(/&#x([0-9a-f]+);?/gi, (_m, h) => String.fromCharCode(parseInt(h, 16))).replace(/&#(\d+);?/g, (_m, d) => String.fromCharCode(Number(d))).replace(/:/gi, ":").replace(/[\u0000-\u0020]+/g, "").toLowerCase();
|
|
1351
|
+
}
|
|
1352
|
+
function sanitizeRawTags(text) {
|
|
1353
|
+
return text.replace(
|
|
1354
|
+
/<([a-zA-Z][\w:-]*)((?:[^>"']|"[^"]*"|'[^']*')*)>/g,
|
|
1355
|
+
(whole, tag, rawAttrs) => {
|
|
1356
|
+
if (!/[=\s]/.test(rawAttrs)) return whole;
|
|
1357
|
+
let changed = false;
|
|
1358
|
+
const attrs = rawAttrs.replace(
|
|
1359
|
+
/(\s+)([^\s"'=<>/]+)(\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+)))?/g,
|
|
1360
|
+
(m, _sp, name, _eq, dq, sq, bare) => {
|
|
1361
|
+
const lower = name.toLowerCase();
|
|
1362
|
+
if (lower.startsWith("on") || lower === "srcdoc") {
|
|
1363
|
+
changed = true;
|
|
1364
|
+
return "";
|
|
1365
|
+
}
|
|
1366
|
+
if (URL_ATTRS.has(lower)) {
|
|
1367
|
+
const value = decodeForSchemeCheck(dq ?? sq ?? bare ?? "");
|
|
1368
|
+
if (/^(?:javascript|vbscript):|^data:text\/html/.test(value)) {
|
|
1369
|
+
changed = true;
|
|
1370
|
+
return ` ${name}="#"`;
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
return m;
|
|
1374
|
+
}
|
|
1375
|
+
);
|
|
1376
|
+
return changed ? `<${tag}${attrs}>` : whole;
|
|
921
1377
|
}
|
|
922
1378
|
);
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
1379
|
+
}
|
|
1380
|
+
function safeUrl(url) {
|
|
1381
|
+
const u = url.trim();
|
|
1382
|
+
if (/^(?:javascript|vbscript):|^data:text\/html/.test(decodeForSchemeCheck(u))) return "#";
|
|
1383
|
+
return u.replace(/"/g, "%22");
|
|
1384
|
+
}
|
|
1385
|
+
function convertEmphasis(html, g) {
|
|
1386
|
+
const strong = `<strong${cls(g.strong)}>`;
|
|
1387
|
+
const em = `<em${cls(g.em)}>`;
|
|
1388
|
+
return html.replace(/\*\*\*(?!\s)(.+?)(?<!\s)\*\*\*/g, `${strong}${em}$1</em></strong>`).replace(/\*\*(?!\s)(.+?)(?<!\s)\*\*/g, `${strong}$1</strong>`).replace(/(?<![\w*])\*(?![\s*])(.+?)(?<![\s*])\*(?![\w*])/g, `${em}$1</em>`).replace(/(?<![\w_])___(?!\s)(.+?)(?<!\s)___(?![\w_])/g, `${strong}${em}$1</em></strong>`).replace(/(?<![\w_])__(?!\s)(.+?)(?<!\s)__(?![\w_])/g, `${strong}$1</strong>`).replace(/(?<![\w_])_(?![\s_])(.+?)(?<![\s_])_(?![\w_])/g, `${em}$1</em>`).replace(/~~(?!\s)(.+?)(?<!\s)~~/g, `<del${cls(g.del)}>$1</del>`);
|
|
1389
|
+
}
|
|
1390
|
+
var LINK_URL = String.raw`((?:[^()\s]|\([^()\s]*\))+)(?:\s+"[^"]*")?`;
|
|
1391
|
+
var IMAGE_RE = new RegExp(String.raw`!\[([^\]]*)\]\(` + LINK_URL + String.raw`\)`, "g");
|
|
1392
|
+
var LINK_RE = new RegExp(String.raw`\[([^\]]+)\]\(` + LINK_URL + String.raw`\)`, "g");
|
|
1393
|
+
function convertRules(html, g) {
|
|
1394
|
+
return html.replace(/^\s{0,3}#\s*={3,}\s*$/gm, `<hr${cls(g.hr, "matrx-hr-heavy")}>`).replace(/^\s{0,3}\*(?:\s*\*){2,}\s*$/gm, `<hr${cls(g.hr, "matrx-hr-accent")}>`).replace(/^\s{0,3}(?:-\s*){3,}$/gm, `<hr${cls(g.hr)}>`).replace(/^\s{0,3}(?:_\s*){3,}$/gm, `<hr${cls(g.hr)}>`);
|
|
1395
|
+
}
|
|
1396
|
+
function splitTableRow(row) {
|
|
1397
|
+
let r = row.trim();
|
|
1398
|
+
if (r.startsWith("|")) r = r.slice(1);
|
|
1399
|
+
if (r.endsWith("|") && !r.endsWith("\\|")) r = r.slice(0, -1);
|
|
1400
|
+
return r.split(/(?<!\\)\|/).map((c) => c.trim().replace(/\\\|/g, "|"));
|
|
1401
|
+
}
|
|
1402
|
+
function cellAlign(separator) {
|
|
1403
|
+
const s = separator.trim();
|
|
1404
|
+
const left = s.startsWith(":");
|
|
1405
|
+
const right = s.endsWith(":");
|
|
1406
|
+
if (left && right) return ' style="text-align:center"';
|
|
1407
|
+
if (right) return ' style="text-align:right"';
|
|
1408
|
+
return "";
|
|
1409
|
+
}
|
|
1410
|
+
function convertTables(html, g) {
|
|
1411
|
+
const tableRegex = /^(\|.*\|)\s*\n(\|[-\s:|]*\|)\s*\n((?:\|.*\|\s*\n?)*)/gm;
|
|
1412
|
+
return html.replace(
|
|
1413
|
+
tableRegex,
|
|
1414
|
+
(_m, headerRow, separatorRow, bodyRows) => {
|
|
1415
|
+
const headers = splitTableRow(headerRow);
|
|
1416
|
+
const aligns = splitTableRow(separatorRow).map(cellAlign);
|
|
1417
|
+
const rows = bodyRows.trim().split("\n").filter((r) => r.trim()).map(splitTableRow);
|
|
1418
|
+
let t = `<table${cls(g.table)}>
|
|
1419
|
+
<thead${cls(g.thead)}>
|
|
1420
|
+
<tr${cls(g.tr)}>
|
|
1421
|
+
`;
|
|
1422
|
+
headers.forEach((h, i) => {
|
|
1423
|
+
t += `<th${cls(g.th)}${aligns[i] ?? ""}>${h}</th>
|
|
1424
|
+
`;
|
|
932
1425
|
});
|
|
933
|
-
|
|
934
|
-
|
|
1426
|
+
t += "</tr>\n</thead>\n";
|
|
1427
|
+
if (rows.length > 0) {
|
|
1428
|
+
t += `<tbody${cls(g.tbody)}>
|
|
1429
|
+
`;
|
|
1430
|
+
for (const row of rows) {
|
|
1431
|
+
t += `<tr${cls(g.tr)}>
|
|
1432
|
+
`;
|
|
1433
|
+
row.forEach((c, i) => {
|
|
1434
|
+
t += `<td${cls(g.td)}${aligns[i] ?? ""}>${c}</td>
|
|
1435
|
+
`;
|
|
1436
|
+
});
|
|
1437
|
+
t += "</tr>\n";
|
|
1438
|
+
}
|
|
1439
|
+
t += "</tbody>\n";
|
|
1440
|
+
}
|
|
1441
|
+
return `${t}</table>
|
|
1442
|
+
`;
|
|
935
1443
|
}
|
|
936
1444
|
);
|
|
937
|
-
|
|
938
|
-
|
|
1445
|
+
}
|
|
1446
|
+
function convertLists(html, g) {
|
|
939
1447
|
const lines = html.split("\n");
|
|
940
|
-
let
|
|
1448
|
+
let processed = "";
|
|
941
1449
|
const listStack = [];
|
|
942
1450
|
let lastListItem = "";
|
|
1451
|
+
const li = `<li${cls(g.li)}>`;
|
|
943
1452
|
const top = () => listStack[listStack.length - 1];
|
|
944
|
-
const
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
if (!closing) break;
|
|
952
|
-
processedHtml += closing.type === "ol" ? "</ol>\n" : "</ul>\n";
|
|
953
|
-
if (listStack.length > 0) processedHtml += "</li>\n";
|
|
1453
|
+
const openTag = (type, nested) => type === "ol" ? `<ol${cls(nested ? g.olNested : g.ol)}>
|
|
1454
|
+
` : `<ul${cls(nested ? g.ulNested : g.ul)}>
|
|
1455
|
+
`;
|
|
1456
|
+
const popOne = () => {
|
|
1457
|
+
if (lastListItem) {
|
|
1458
|
+
processed += "</li>\n";
|
|
1459
|
+
lastListItem = "";
|
|
954
1460
|
}
|
|
1461
|
+
const closing = listStack.pop();
|
|
1462
|
+
if (!closing) return;
|
|
1463
|
+
processed += closing.type === "ol" ? "</ol>\n" : "</ul>\n";
|
|
1464
|
+
if (listStack.length > 0) processed += "</li>\n";
|
|
1465
|
+
};
|
|
1466
|
+
const closeDeeperThan = (indent) => {
|
|
1467
|
+
while (listStack.length > 0 && top().indent > indent) popOne();
|
|
955
1468
|
};
|
|
956
1469
|
for (const line of lines) {
|
|
957
|
-
const
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
const
|
|
1470
|
+
const numbered = line.match(/^(\s*)(\d+)[.)]\s+(.+)$/);
|
|
1471
|
+
const bullet = numbered ? null : line.match(/^(\s*)[*\-+]\s+(.+)$/);
|
|
1472
|
+
if (numbered || bullet) {
|
|
1473
|
+
const type = numbered ? "ol" : "ul";
|
|
1474
|
+
const indent = (numbered ?? bullet)[1]?.length ?? 0;
|
|
1475
|
+
const content = (numbered ? numbered[3] : bullet[2]) ?? "";
|
|
961
1476
|
const current = top();
|
|
962
1477
|
if (current && indent > current.indent) {
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
lastListItem = "ol";
|
|
1478
|
+
processed += `
|
|
1479
|
+
${openTag(type, true)}`;
|
|
1480
|
+
listStack.push({ type, indent });
|
|
967
1481
|
} else {
|
|
968
1482
|
closeDeeperThan(indent);
|
|
969
|
-
if (lastListItem && top()?.indent === indent)
|
|
1483
|
+
if (lastListItem && top()?.indent === indent) processed += "</li>\n";
|
|
970
1484
|
const now = top();
|
|
971
|
-
if (
|
|
972
|
-
|
|
973
|
-
|
|
1485
|
+
if (now && now.indent === indent && now.type !== type) {
|
|
1486
|
+
const closing = listStack.pop();
|
|
1487
|
+
processed += closing.type === "ol" ? "</ol>\n" : "</ul>\n";
|
|
974
1488
|
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
}
|
|
980
|
-
const bulletMatch = line.match(/^(\s*)[*\-+]\s+(.+)$/);
|
|
981
|
-
if (bulletMatch) {
|
|
982
|
-
const indent = (bulletMatch[1] ?? "").length;
|
|
983
|
-
const content = bulletMatch[2] ?? "";
|
|
984
|
-
const current = top();
|
|
985
|
-
if (current && indent > current.indent) {
|
|
986
|
-
processedHtml += '\n<ul class="matrx-list matrx-bullet-list matrx-nested-list">\n';
|
|
987
|
-
listStack.push({ type: "ul", indent });
|
|
988
|
-
processedHtml += `<li class="matrx-list-item">${content}`;
|
|
989
|
-
lastListItem = "ul";
|
|
990
|
-
} else {
|
|
991
|
-
closeDeeperThan(indent);
|
|
992
|
-
if (lastListItem && top()?.indent === indent) processedHtml += "</li>\n";
|
|
993
|
-
const now = top();
|
|
994
|
-
if (!now || now.indent !== indent || now.type !== "ul") {
|
|
995
|
-
processedHtml += '<ul class="matrx-list matrx-bullet-list">\n';
|
|
996
|
-
listStack.push({ type: "ul", indent });
|
|
1489
|
+
const after = top();
|
|
1490
|
+
if (!after || after.indent !== indent || after.type !== type) {
|
|
1491
|
+
processed += openTag(type, listStack.length > 0);
|
|
1492
|
+
listStack.push({ type, indent });
|
|
997
1493
|
}
|
|
998
|
-
processedHtml += `<li class="matrx-list-item">${content}`;
|
|
999
|
-
lastListItem = "ul";
|
|
1000
1494
|
}
|
|
1495
|
+
processed += `${li}${content}`;
|
|
1496
|
+
lastListItem = type;
|
|
1001
1497
|
continue;
|
|
1002
1498
|
}
|
|
1003
1499
|
if (line.trim() !== "") {
|
|
1004
|
-
while (listStack.length > 0)
|
|
1005
|
-
if (lastListItem) {
|
|
1006
|
-
processedHtml += "</li>\n";
|
|
1007
|
-
lastListItem = "";
|
|
1008
|
-
}
|
|
1009
|
-
const closing = listStack.pop();
|
|
1010
|
-
if (!closing) break;
|
|
1011
|
-
processedHtml += closing.type === "ol" ? "</ol>\n" : "</ul>\n";
|
|
1012
|
-
if (listStack.length > 0) processedHtml += "</li>\n";
|
|
1013
|
-
}
|
|
1500
|
+
while (listStack.length > 0) popOne();
|
|
1014
1501
|
}
|
|
1015
|
-
|
|
1502
|
+
processed += `${line}
|
|
1016
1503
|
`;
|
|
1017
1504
|
}
|
|
1018
|
-
while (listStack.length > 0)
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1505
|
+
while (listStack.length > 0) popOne();
|
|
1506
|
+
return processed.replace(
|
|
1507
|
+
/<li( class="([^"]*)")?>\s*\[([ xX])\]\s+/g,
|
|
1508
|
+
(_m, _c, classes, mark) => `<li${cls(classes ?? "", "matrx-task-item")}><span class="matrx-task-box${mark === " " ? "" : " is-checked"}" aria-hidden="true"></span>`
|
|
1509
|
+
);
|
|
1510
|
+
}
|
|
1511
|
+
function convertBody(skin, markdown, ctx) {
|
|
1512
|
+
return skin === "article" ? articleBody(markdown, ctx) : documentBody(markdown, ctx);
|
|
1513
|
+
}
|
|
1514
|
+
function scrubTokens(markdown) {
|
|
1515
|
+
return markdown.replace(/[\uE000\uE001]/g, "");
|
|
1516
|
+
}
|
|
1517
|
+
function newContext(options) {
|
|
1518
|
+
return { options, depth: 0, cardIndex: 0, strippedAnnounced: false };
|
|
1519
|
+
}
|
|
1520
|
+
function markdownToHtml(markdown, options = {}) {
|
|
1521
|
+
if (!markdown) {
|
|
1522
|
+
options.onNotice?.({
|
|
1523
|
+
code: "empty-output",
|
|
1524
|
+
message: "markdownToHtml received an empty string.",
|
|
1525
|
+
remedy: "Check the content is loaded before converting; nothing was rendered."
|
|
1526
|
+
});
|
|
1527
|
+
return "";
|
|
1027
1528
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1529
|
+
const source = scrubTokens(options.includeThinking ? markdown : removeThinkingContent(markdown));
|
|
1530
|
+
return `<div class="matrx-content-container">${documentBody(source, newContext(options))}</div>`;
|
|
1531
|
+
}
|
|
1532
|
+
function documentBody(markdown, ctx) {
|
|
1533
|
+
const g = DOCUMENT_GRAMMAR;
|
|
1534
|
+
const stash = new Stash();
|
|
1535
|
+
let html = protectRegions(markdown, g, stash, ctx);
|
|
1536
|
+
html = convertRules(html, g);
|
|
1030
1537
|
html = html.replace(
|
|
1031
|
-
|
|
1032
|
-
|
|
1538
|
+
IMAGE_RE,
|
|
1539
|
+
(_m, alt, url) => stash.block(`<img class="matrx-image" src="${safeUrl(url)}" alt="${(0, import_html_escape2.escapeHtml)(alt)}" />`)
|
|
1033
1540
|
);
|
|
1034
|
-
html = html.replace(/`([^`]+)`/g, '<code class="matrx-inline-code">$1</code>');
|
|
1035
|
-
html = convertMarkdownTables(html);
|
|
1036
|
-
html = html.replace(/<table>/g, '<table class="matrx-table">').replace(/<thead>/g, '<thead class="matrx-table-head">').replace(/<tbody>/g, '<tbody class="matrx-table-body">').replace(/<tr>/g, '<tr class="matrx-table-row">').replace(/<th>/g, '<th class="matrx-table-header">').replace(/<td>/g, '<td class="matrx-table-cell">');
|
|
1037
|
-
html = html.replace(/<img([^>]*)>/g, '<img class="matrx-image"$1>');
|
|
1038
|
-
html = html.replace(/<div(?!\s+class="matrx-)([^>]*)>/g, '<div class="matrx-div"$1>');
|
|
1039
|
-
html = html.replace(/<span([^>]*)>/g, '<span class="matrx-span"$1>');
|
|
1040
|
-
html = html.replace(/^([^<\n].+)$/gm, '<p class="matrx-paragraph">$1</p>');
|
|
1041
1541
|
html = html.replace(
|
|
1042
|
-
|
|
1043
|
-
|
|
1542
|
+
LINK_RE,
|
|
1543
|
+
(_m, text, url) => stash.inline(`<a class="matrx-link" href="${safeUrl(url)}">${convertEmphasis(text, g)}</a>`)
|
|
1044
1544
|
);
|
|
1545
|
+
html = html.replace(/^# (.+)$/gm, '<h1 class="matrx-h1">$1</h1>').replace(/^## (.+)$/gm, '<h2 class="matrx-h2">$1</h2>').replace(/^### (.+)$/gm, '<h3 class="matrx-h3">$1</h3>').replace(/^#### (.+)$/gm, '<h4 class="matrx-h4">$1</h4>').replace(/^##### (.+)$/gm, '<h5 class="matrx-h5">$1</h5>').replace(/^###### (.+)$/gm, '<h6 class="matrx-h6">$1</h6>');
|
|
1546
|
+
html = convertLists(html, g);
|
|
1547
|
+
html = convertEmphasis(html, g);
|
|
1548
|
+
html = html.replace(/^> ?(.+)$/gm, '<blockquote class="matrx-blockquote">$1</blockquote>');
|
|
1549
|
+
html = convertTables(html, g);
|
|
1550
|
+
html = html.replace(/<table>/g, '<table class="matrx-table">').replace(/<thead>/g, '<thead class="matrx-table-head">').replace(/<tbody>/g, '<tbody class="matrx-table-body">').replace(/<tr>/g, '<tr class="matrx-table-row">').replace(/<th>/g, '<th class="matrx-table-header">').replace(/<td>/g, '<td class="matrx-table-cell">');
|
|
1551
|
+
html = html.replace(/<img(?![^>]*class=)([^>]*)>/g, '<img class="matrx-image"$1>');
|
|
1552
|
+
html = html.replace(/<div\b([^>]*)>/g, (m, attrs) => {
|
|
1553
|
+
if (/\bclass="matrx-/.test(attrs)) return m;
|
|
1554
|
+
if (/\bclass="/.test(attrs)) return `<div${attrs.replace(/\bclass="/, 'class="matrx-div ')}>`;
|
|
1555
|
+
return `<div class="matrx-div"${attrs}>`;
|
|
1556
|
+
});
|
|
1557
|
+
html = html.replace(/<span(?![^>]*class=)([^>]*)>/g, '<span class="matrx-span"$1>');
|
|
1558
|
+
html = html.replace(/^([^<\n].+)$/gm, '<p class="matrx-paragraph">$1</p>');
|
|
1559
|
+
html = html.replace(/<\/p>\s*<p class="matrx-paragraph">/g, '</p><p class="matrx-paragraph">');
|
|
1045
1560
|
html = html.replace(
|
|
1046
|
-
/(<h1 class="matrx-h1">[\s\S]*?<\/h1>\s*)<p class="matrx-paragraph">([\s\S]*?)<\/p>/g,
|
|
1561
|
+
/(<h1 class="matrx-h1">[\s\S]*?<\/h1>\s*)<p class="matrx-paragraph">((?:(?!\uE000B)[\s\S])*?)<\/p>/g,
|
|
1047
1562
|
'$1<p class="matrx-intro">$2</p>'
|
|
1048
1563
|
);
|
|
1049
1564
|
html = html.replace(
|
|
@@ -1070,42 +1585,7 @@ function markdownToHtml(markdown, options = {}) {
|
|
|
1070
1585
|
/(<div class="matrx-faq-answer">)<p class="matrx-paragraph">([\s\S]*?)<\/p>(<\/div>)/g,
|
|
1071
1586
|
"$1$2$3"
|
|
1072
1587
|
);
|
|
1073
|
-
|
|
1074
|
-
if (html.includes(placeholder)) html = html.replaceAll(placeholder, imageHtml);
|
|
1075
|
-
}
|
|
1076
|
-
html = html.replace(/<p class="matrx-paragraph">\s*(<img[^>]*>)\s*<\/p>/g, "$1");
|
|
1077
|
-
for (const { placeholder, html: linkHtml } of linkPlaceholders) {
|
|
1078
|
-
if (html.includes(placeholder)) html = html.replaceAll(placeholder, linkHtml);
|
|
1079
|
-
}
|
|
1080
|
-
for (const { placeholder, html: cardHtml } of flashcardPlaceholders) {
|
|
1081
|
-
if (html.includes(placeholder)) html = html.replaceAll(placeholder, cardHtml);
|
|
1082
|
-
}
|
|
1083
|
-
return startWrapper + html + endWrapper;
|
|
1084
|
-
}
|
|
1085
|
-
function convertArticleTables(html) {
|
|
1086
|
-
const tableRegex = /^(\|.*\|)\s*\n(\|[-\s:|]*\|)\s*\n((?:\|.*\|\s*\n?)*)/gm;
|
|
1087
|
-
return html.replace(
|
|
1088
|
-
tableRegex,
|
|
1089
|
-
(_match, headerRow, _sep, bodyRows) => {
|
|
1090
|
-
const headers = headerRow.split("|").map((c) => c.trim()).filter((c) => c !== "");
|
|
1091
|
-
const rows = bodyRows.trim().split("\n").filter((r) => r.trim()).map(
|
|
1092
|
-
(r) => r.split("|").map((c) => c.trim()).filter((c) => c !== "")
|
|
1093
|
-
);
|
|
1094
|
-
let t = '<table class="print-table">\n';
|
|
1095
|
-
if (headers.length) {
|
|
1096
|
-
t += `<thead><tr>${headers.map((h) => `<th>${h}</th>`).join("")}</tr></thead>
|
|
1097
|
-
`;
|
|
1098
|
-
}
|
|
1099
|
-
if (rows.length) {
|
|
1100
|
-
t += `<tbody>${rows.map(
|
|
1101
|
-
(r) => r.length ? `<tr>${r.map((c) => `<td>${c}</td>`).join("")}</tr>` : ""
|
|
1102
|
-
).join("\n")}</tbody>
|
|
1103
|
-
`;
|
|
1104
|
-
}
|
|
1105
|
-
return `${t}</table>
|
|
1106
|
-
`;
|
|
1107
|
-
}
|
|
1108
|
-
);
|
|
1588
|
+
return stash.restore(html);
|
|
1109
1589
|
}
|
|
1110
1590
|
function markdownToArticleHtml(markdown, options = {}) {
|
|
1111
1591
|
if (!markdown) {
|
|
@@ -1116,117 +1596,38 @@ function markdownToArticleHtml(markdown, options = {}) {
|
|
|
1116
1596
|
});
|
|
1117
1597
|
return "";
|
|
1118
1598
|
}
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1599
|
+
const source = scrubTokens(options.includeThinking ? markdown : removeThinkingContent(markdown));
|
|
1600
|
+
return articleBody(source, newContext(options));
|
|
1601
|
+
}
|
|
1602
|
+
function articleBody(markdown, ctx) {
|
|
1603
|
+
const g = ARTICLE_GRAMMAR;
|
|
1604
|
+
const stash = new Stash();
|
|
1605
|
+
let html = protectRegions(markdown, g, stash, ctx);
|
|
1606
|
+
html = convertTables(html, g);
|
|
1607
|
+
html = convertRules(html, g);
|
|
1123
1608
|
html = html.replace(
|
|
1124
|
-
|
|
1125
|
-
(_m,
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
codeBlockPlaceholders.push({
|
|
1129
|
-
placeholder: ph,
|
|
1130
|
-
html: `<div class="code-block-wrapper">${langLabel}<pre class="code-block"><code>${(0, import_html_escape2.escapeHtml)(
|
|
1131
|
-
code.trim()
|
|
1132
|
-
)}</code></pre></div>`
|
|
1133
|
-
});
|
|
1134
|
-
codeIdx++;
|
|
1135
|
-
return ph;
|
|
1136
|
-
}
|
|
1137
|
-
);
|
|
1138
|
-
const imagePlaceholders = [];
|
|
1139
|
-
let imageIdx = 0;
|
|
1140
|
-
html = html.replace(
|
|
1141
|
-
/!\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g,
|
|
1142
|
-
(_m, alt, url) => {
|
|
1143
|
-
const ph = `\u03A9IMAGE\u03A9${imageIdx}\u03A9IMAGE\u03A9`;
|
|
1144
|
-
imagePlaceholders.push({
|
|
1145
|
-
placeholder: ph,
|
|
1146
|
-
html: `<figure class="print-figure"><img class="print-image" src="${url}" alt="${(0, import_html_escape2.escapeHtml)(alt)}" />${alt ? `<figcaption>${(0, import_html_escape2.escapeHtml)(alt)}</figcaption>` : ""}</figure>`
|
|
1147
|
-
});
|
|
1148
|
-
imageIdx++;
|
|
1149
|
-
return ph;
|
|
1150
|
-
}
|
|
1609
|
+
IMAGE_RE,
|
|
1610
|
+
(_m, alt, url) => stash.block(
|
|
1611
|
+
`<figure class="print-figure"><img class="print-image" src="${safeUrl(url)}" alt="${(0, import_html_escape2.escapeHtml)(alt)}" />${alt ? `<figcaption>${(0, import_html_escape2.escapeHtml)(alt)}</figcaption>` : ""}</figure>`
|
|
1612
|
+
)
|
|
1151
1613
|
);
|
|
1152
|
-
const linkPlaceholders = [];
|
|
1153
|
-
let linkIdx = 0;
|
|
1154
1614
|
html = html.replace(
|
|
1155
|
-
|
|
1156
|
-
(_m, text, url) => {
|
|
1157
|
-
const ph = `\u03A9LINK\u03A9${linkIdx}\u03A9LINK\u03A9`;
|
|
1158
|
-
linkPlaceholders.push({ placeholder: ph, html: `<a href="${url}">${text}</a>` });
|
|
1159
|
-
linkIdx++;
|
|
1160
|
-
return ph;
|
|
1161
|
-
}
|
|
1615
|
+
LINK_RE,
|
|
1616
|
+
(_m, text, url) => stash.inline(`<a href="${safeUrl(url)}">${convertEmphasis(text, g)}</a>`)
|
|
1162
1617
|
);
|
|
1163
1618
|
html = html.replace(/^#{6}\s+(.+)$/gm, "<h6>$1</h6>").replace(/^#{5}\s+(.+)$/gm, "<h5>$1</h5>").replace(/^#{4}\s+(.+)$/gm, "<h4>$1</h4>").replace(/^#{3}\s+(.+)$/gm, "<h3>$1</h3>").replace(/^#{2}\s+(.+)$/gm, "<h2>$1</h2>").replace(/^#{1}\s+(.+)$/gm, "<h1>$1</h1>");
|
|
1164
|
-
html = html.replace(/^> (.+)$/gm, "<blockquote>$1</blockquote>");
|
|
1165
|
-
html = html
|
|
1166
|
-
html = html
|
|
1167
|
-
html = html.replace(/`([^`]+)`/g, '<code class="inline-code">$1</code>');
|
|
1168
|
-
const lines = html.split("\n");
|
|
1169
|
-
const result = [];
|
|
1170
|
-
let inUl = false;
|
|
1171
|
-
let inOl = false;
|
|
1172
|
-
for (const line of lines) {
|
|
1173
|
-
const ulMatch = line.match(/^(\s*)[-*+]\s+(.+)$/);
|
|
1174
|
-
const olMatch = line.match(/^(\s*)\d+\.\s+(.+)$/);
|
|
1175
|
-
if (ulMatch) {
|
|
1176
|
-
if (inOl) {
|
|
1177
|
-
result.push("</ol>");
|
|
1178
|
-
inOl = false;
|
|
1179
|
-
}
|
|
1180
|
-
if (!inUl) {
|
|
1181
|
-
result.push("<ul>");
|
|
1182
|
-
inUl = true;
|
|
1183
|
-
}
|
|
1184
|
-
result.push(`<li>${ulMatch[2] ?? ""}</li>`);
|
|
1185
|
-
} else if (olMatch) {
|
|
1186
|
-
if (inUl) {
|
|
1187
|
-
result.push("</ul>");
|
|
1188
|
-
inUl = false;
|
|
1189
|
-
}
|
|
1190
|
-
if (!inOl) {
|
|
1191
|
-
result.push("<ol>");
|
|
1192
|
-
inOl = true;
|
|
1193
|
-
}
|
|
1194
|
-
result.push(`<li>${olMatch[2] ?? ""}</li>`);
|
|
1195
|
-
} else {
|
|
1196
|
-
if (inUl) {
|
|
1197
|
-
result.push("</ul>");
|
|
1198
|
-
inUl = false;
|
|
1199
|
-
}
|
|
1200
|
-
if (inOl) {
|
|
1201
|
-
result.push("</ol>");
|
|
1202
|
-
inOl = false;
|
|
1203
|
-
}
|
|
1204
|
-
result.push(line);
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1207
|
-
if (inUl) result.push("</ul>");
|
|
1208
|
-
if (inOl) result.push("</ol>");
|
|
1209
|
-
html = result.join("\n");
|
|
1619
|
+
html = html.replace(/^> ?(.+)$/gm, "<blockquote>$1</blockquote>");
|
|
1620
|
+
html = convertLists(html, g);
|
|
1621
|
+
html = convertEmphasis(html, g);
|
|
1210
1622
|
html = html.split("\n").map((line) => {
|
|
1211
1623
|
const t = line.trim();
|
|
1212
1624
|
if (!t) return "";
|
|
1213
|
-
if (/^<\/?(h[1-6]|ul|ol|li|blockquote|pre|hr|table|thead|tbody|tr|th|td|div|figure)/.test(t) ||
|
|
1625
|
+
if (/^<\/?(h[1-6]|ul|ol|li|blockquote|pre|hr|table|thead|tbody|tr|th|td|div|figure|section)/.test(t) || /^\uE000B\d+\uE001$/.test(t)) {
|
|
1214
1626
|
return line;
|
|
1215
1627
|
}
|
|
1216
1628
|
return `<p>${t}</p>`;
|
|
1217
1629
|
}).join("\n");
|
|
1218
|
-
|
|
1219
|
-
html = html.replace(`<p>${placeholder}</p>`, ph);
|
|
1220
|
-
html = html.replace(placeholder, ph);
|
|
1221
|
-
}
|
|
1222
|
-
for (const { placeholder, html: ph } of imagePlaceholders) {
|
|
1223
|
-
html = html.replaceAll(`<p>${placeholder}</p>`, ph);
|
|
1224
|
-
html = html.replaceAll(placeholder, ph);
|
|
1225
|
-
}
|
|
1226
|
-
for (const { placeholder, html: ph } of linkPlaceholders) {
|
|
1227
|
-
html = html.replaceAll(placeholder, ph);
|
|
1228
|
-
}
|
|
1229
|
-
return html;
|
|
1630
|
+
return stash.restore(html).replace(/\n{3,}/g, "\n\n").trim();
|
|
1230
1631
|
}
|
|
1231
1632
|
var PRINT_ACTIONS_CSS = `
|
|
1232
1633
|
.print-actions { display: flex; gap: 10px; margin: 0 auto 28px; max-width: 800px; padding: 0 2rem 20px; border-bottom: 2px solid var(--matrx-print-border, #e1e1e1); }
|
|
@@ -1240,7 +1641,8 @@ function getMarkdownStylesheet(options = {}) {
|
|
|
1240
1641
|
const base = skin === "article" ? `${MARKDOWN_ARTICLE_TOKENS_CSS}
|
|
1241
1642
|
${MARKDOWN_ARTICLE_CSS}` : `${MARKDOWN_DOCUMENT_TOKENS_CSS}
|
|
1242
1643
|
${MARKDOWN_DOCUMENT_CSS}`;
|
|
1243
|
-
|
|
1644
|
+
const page = options.pageNumbers === false ? MARKDOWN_PAGE_PLAIN_CSS : MARKDOWN_PAGE_CSS;
|
|
1645
|
+
return [base, MARKDOWN_SHARED_CSS, page, options.tokens ?? "", options.extraCss ?? ""].filter((part) => part.trim().length > 0).join("\n");
|
|
1244
1646
|
}
|
|
1245
1647
|
function markdownToHtmlForSkin(markdown, skin, options = {}) {
|
|
1246
1648
|
return skin === "article" ? markdownToArticleHtml(markdown, options) : markdownToHtml(markdown, options);
|