@memorysmithapp/markdown-renderer 0.3.0 → 0.3.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/Markdown.js +3 -2
- package/dist/diagnose.js +1 -1
- package/dist/properties.d.ts +4 -1
- package/dist/properties.js +6 -2
- package/dist/remark-callouts.js +7 -3
- package/dist/remark-ring.js +7 -0
- package/dist/slots.js +10 -2
- package/dist/theme.d.ts +2 -0
- package/dist/theme.js +1 -0
- package/dist/transclusion.d.ts +3 -0
- package/dist/transclusion.js +10 -1
- package/package.json +2 -2
- package/structure.css +7 -0
- package/theme-default.css +16 -0
package/dist/Markdown.js
CHANGED
|
@@ -142,7 +142,7 @@ function MarkdownCode({ className, children, node: _node, ...rest }) {
|
|
|
142
142
|
const inBlock = useInBlock();
|
|
143
143
|
const inlineSlot = useSlot('inlineCode');
|
|
144
144
|
if (className?.includes('language-mermaid')) {
|
|
145
|
-
return _jsx(MermaidDiagram, { code: String(children).trim(), scheme: scheme });
|
|
145
|
+
return _jsx(MermaidDiagram, { code: String(children ?? '').trim(), scheme: scheme });
|
|
146
146
|
}
|
|
147
147
|
if (!inBlock && inlineSlot)
|
|
148
148
|
return drawSlot(inlineSlot, { children });
|
|
@@ -151,7 +151,8 @@ function MarkdownCode({ className, children, node: _node, ...rest }) {
|
|
|
151
151
|
// The source is read and never rewritten: what is on the page is the
|
|
152
152
|
// bytes, split into spans. React-markdown hands the children of a fence as
|
|
153
153
|
// a single string ending in the newline before the closing fence.
|
|
154
|
-
|
|
154
|
+
// An empty fence has no children at all, and is no code rather than the word `undefined`.
|
|
155
|
+
const code = String(children ?? '').replace(/\n$/, '');
|
|
155
156
|
return (_jsx("code", { className: className, ...rest, children: toJsxRuntime(highlightTree(code, language), { Fragment, jsx, jsxs }) }));
|
|
156
157
|
}
|
|
157
158
|
return (_jsx("code", { className: className, ...rest, children: children }));
|
package/dist/diagnose.js
CHANGED
|
@@ -25,7 +25,7 @@ export const NOTICES = {
|
|
|
25
25
|
'diagram-invalid': { severity: 'notice', section: '7.2', message: 'This diagram cannot be read, and is shown as its source or its error.' },
|
|
26
26
|
'frontmatter-unclosed': { severity: 'notice', section: '6.1', message: 'This frontmatter is never closed by a ---: the note has no properties, and its lines are body text.' },
|
|
27
27
|
'name-missing': { severity: 'notice', section: '6.5', message: 'The note has no name: a link reaches it only through an alias.' },
|
|
28
|
-
'name-unusable': { severity: 'notice', section: '6.5', message: 'This name is not a single text value, so the note is unnamed.' },
|
|
28
|
+
'name-unusable': { severity: 'notice', section: '6.5', message: 'This name is not a single text value, or carries one of #, [, ] and |, so the note is unnamed.' },
|
|
29
29
|
'reserved-not-a-list': { severity: 'notice', section: '6.4', message: 'This value is not a list, so it is an ordinary attribute.' },
|
|
30
30
|
'aliases-missing': { severity: 'hint', section: '6.4', message: 'The note has no aliases: only its name catches a link.' },
|
|
31
31
|
'tags-missing': { severity: 'hint', section: '6.4', message: 'The note has no tags: it has no subjects to be filtered by.' },
|
package/dist/properties.d.ts
CHANGED
|
@@ -29,7 +29,10 @@ export interface PropertyEntry {
|
|
|
29
29
|
}
|
|
30
30
|
/** The frontmatter, read. */
|
|
31
31
|
export interface NoteProperties {
|
|
32
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* The name of the note: `name:` when it is a single text value (§6.5) that carries none of
|
|
34
|
+
* `#`, `[`, `]` and `|` (§5.3), else `null`.
|
|
35
|
+
*/
|
|
33
36
|
readonly name: string | null;
|
|
34
37
|
/** Every other entry, in the order written. */
|
|
35
38
|
readonly entries: readonly PropertyEntry[];
|
package/dist/properties.js
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
* resolved by the host as a link of the body is. That is a decision of the
|
|
15
15
|
* reading surface; it adds no edge, and what an Indexer reads is unchanged.
|
|
16
16
|
*/
|
|
17
|
+
/** The four delimiters of the form that addresses a note: a name carrying one is no name (§5.3). */
|
|
18
|
+
const UNADDRESSABLE = /[#[\]|]/;
|
|
17
19
|
/** Above this a single value is prose rather than a category (§6.3). */
|
|
18
20
|
const CATEGORY_CEILING = 40;
|
|
19
21
|
const KEY = /^([A-Za-z0-9_-]+)\s*:\s*(.*)$/;
|
|
@@ -117,9 +119,11 @@ export function readProperties(markdown) {
|
|
|
117
119
|
const read = [];
|
|
118
120
|
for (const { key, raw, items } of entries) {
|
|
119
121
|
if (key === 'name') {
|
|
120
|
-
// Only a single text value names a note; any other shape leaves it unnamed (§6.5)
|
|
122
|
+
// Only a single text value names a note; any other shape leaves it unnamed (§6.5), and so
|
|
123
|
+
// does one carrying a delimiter of the form that addresses it (§5.3). The subset has no
|
|
124
|
+
// comments (§6.2): `name: Incident #4711` is that whole value, and it carries a `#`.
|
|
121
125
|
const value = items === null ? unquote(raw) : '';
|
|
122
|
-
name = value.length > 0 ? value : null;
|
|
126
|
+
name = value.length > 0 && !UNADDRESSABLE.test(value) ? value : null;
|
|
123
127
|
continue;
|
|
124
128
|
}
|
|
125
129
|
// A list written with no item, `[]`, is an empty value, never a list (§6.3).
|
package/dist/remark-callouts.js
CHANGED
|
@@ -18,8 +18,12 @@
|
|
|
18
18
|
* reads: a `[!note]` inside a code fence is not a callout, and only the parser
|
|
19
19
|
* can tell the difference.
|
|
20
20
|
*/
|
|
21
|
-
/**
|
|
22
|
-
|
|
21
|
+
/**
|
|
22
|
+
* `[!type]`, an optional fold marker, and whatever titles it. The type is a word
|
|
23
|
+
* in any script (§7.1): it opens with a letter and goes on in letters, marks,
|
|
24
|
+
* digits, `_` and `-`, so `[!atenção]` is a type as much as `[!warning]` is.
|
|
25
|
+
*/
|
|
26
|
+
const MARKER = /^\[!(\p{L}[\p{L}\p{M}\p{N}_-]*)\]([+-]?)[ \t]*(.*)$/u;
|
|
23
27
|
export function remarkCallouts(options = {}) {
|
|
24
28
|
return (tree) => descend(tree, options);
|
|
25
29
|
}
|
|
@@ -41,7 +45,7 @@ function rewrite(quote, options) {
|
|
|
41
45
|
const marker = MARKER.exec(broke === -1 ? lead.value : lead.value.slice(0, broke));
|
|
42
46
|
if (!marker)
|
|
43
47
|
return;
|
|
44
|
-
const kind = (marker[1] ?? '').toLowerCase();
|
|
48
|
+
const kind = (marker[1] ?? '').normalize('NFC').toLowerCase();
|
|
45
49
|
const { title, body } = split(marker[3] ?? '', broke === -1 ? null : lead.value.slice(broke + 1), inline);
|
|
46
50
|
quote.data = {
|
|
47
51
|
hName: 'div',
|
package/dist/remark-ring.js
CHANGED
|
@@ -54,8 +54,15 @@ export function remarkHighlight() {
|
|
|
54
54
|
* plugin only keeps the marker off the page.
|
|
55
55
|
*/
|
|
56
56
|
const BLOCK_ID = /[ \t]*\^([A-Za-z0-9-]+)[ \t]*$/;
|
|
57
|
+
/**
|
|
58
|
+
* The nodes a block ends in. The last text of a link, an emphasis or any other
|
|
59
|
+
* inline is not the end of a block: `[[Note#^id]]` keeps its `^id` on the label.
|
|
60
|
+
*/
|
|
61
|
+
const BLOCKS = new Set(['paragraph', 'heading', 'tableCell']);
|
|
57
62
|
export function remarkBlockIds() {
|
|
58
63
|
return (tree) => walk(tree, (parent) => {
|
|
64
|
+
if (!BLOCKS.has(parent.type))
|
|
65
|
+
return;
|
|
59
66
|
const children = parent.children ?? [];
|
|
60
67
|
const last = children[children.length - 1];
|
|
61
68
|
if (!last || !isText(last) || !last.value?.includes('^'))
|
package/dist/slots.js
CHANGED
|
@@ -21,9 +21,17 @@ export function drawSlot(slot, props) {
|
|
|
21
21
|
/** Whether a code element sits in a fenced block, which is what tells a block from a span. */
|
|
22
22
|
export const InBlock = createContext(false);
|
|
23
23
|
// ── Callouts ─────────────────────────────────────────────────────────────────
|
|
24
|
+
/**
|
|
25
|
+
* The words of a title, in one element: the title is laid out as a row beside
|
|
26
|
+
* its icon, and every text node and every `<strong>` of it would otherwise be
|
|
27
|
+
* an item of that row of its own, drawn as columns. One run wraps as a sentence.
|
|
28
|
+
*/
|
|
29
|
+
function CalloutTitleText({ children }) {
|
|
30
|
+
return _jsx("span", { className: "callout-title-text", children: children });
|
|
31
|
+
}
|
|
24
32
|
/** The title `remarkCallouts` built, carried to the callout that holds it. */
|
|
25
33
|
function CalloutTitle({ children }) {
|
|
26
|
-
return _jsx("div", { className: "callout-title", children: children });
|
|
34
|
+
return (_jsx("div", { className: "callout-title", children: _jsx(CalloutTitleText, { children: children }) }));
|
|
27
35
|
}
|
|
28
36
|
function Callout({ kind, fold, children }) {
|
|
29
37
|
const { theme } = useHost();
|
|
@@ -42,7 +50,7 @@ function Callout({ kind, fold, children }) {
|
|
|
42
50
|
const title = at === -1 ? null : parts[at].props.children;
|
|
43
51
|
const titled = icon === null ? (_jsx(CalloutTitle, { children: title })) : (
|
|
44
52
|
// An icon of the theme's takes the place of the one the stylesheet draws.
|
|
45
|
-
_jsxs("div", { className: "callout-title has-icon", children: [_jsx("span", { className: "callout-icon", "aria-hidden": "true", children: icon }), title] }));
|
|
53
|
+
_jsxs("div", { className: "callout-title has-icon", children: [_jsx("span", { className: "callout-icon", "aria-hidden": "true", children: icon }), _jsx(CalloutTitleText, { children: title })] }));
|
|
46
54
|
if (slot) {
|
|
47
55
|
const body = parts.filter((_, index) => index !== at);
|
|
48
56
|
return drawSlot(slot, { kind, fold, title, icon, children: body });
|
package/dist/theme.d.ts
CHANGED
|
@@ -53,6 +53,8 @@ export interface ThemeProperties {
|
|
|
53
53
|
readonly calloutStop?: string;
|
|
54
54
|
readonly calloutAside?: string;
|
|
55
55
|
readonly calloutQuote?: string;
|
|
56
|
+
readonly calloutTitleLinkDecoration?: string;
|
|
57
|
+
readonly calloutTitleLinkOffset?: string;
|
|
56
58
|
readonly iconNote?: string;
|
|
57
59
|
readonly iconInfo?: string;
|
|
58
60
|
readonly iconSummary?: string;
|
package/dist/theme.js
CHANGED
|
@@ -26,6 +26,7 @@ export const THEME_PROPERTIES = Object.keys({
|
|
|
26
26
|
codeBg: 1, codeColor: 1, codeBorder: 1, inlineCodeBg: 1, inlineCodePadding: 1,
|
|
27
27
|
tokenKeyword: 1, tokenString: 1, tokenNumber: 1, tokenComment: 1, tokenPunctuation: 1,
|
|
28
28
|
calloutInfo: 1, calloutIdea: 1, calloutDone: 1, calloutWarn: 1, calloutStop: 1, calloutAside: 1, calloutQuote: 1,
|
|
29
|
+
calloutTitleLinkDecoration: 1, calloutTitleLinkOffset: 1,
|
|
29
30
|
iconNote: 1, iconInfo: 1, iconSummary: 1, iconTip: 1, iconSuccess: 1, iconQuestion: 1, iconWarning: 1,
|
|
30
31
|
iconFailure: 1, iconDanger: 1, iconExample: 1, iconQuote: 1,
|
|
31
32
|
fontSans: 1, fontDisplay: 1, fontMono: 1, fontHeading: 1,
|
package/dist/transclusion.d.ts
CHANGED
|
@@ -72,5 +72,8 @@ export declare function isBlockAnchor(anchor: string): boolean;
|
|
|
72
72
|
* The section of a document, cut syntactically: from the heading whose slug
|
|
73
73
|
* matches the anchor to the next heading of equal or higher level. No notebook
|
|
74
74
|
* convention takes part in this: the cut is syntactic.
|
|
75
|
+
*
|
|
76
|
+
* A line inside a fence is code and never a heading, so the `# Step 1` comment
|
|
77
|
+
* of a shell block neither opens a section nor ends one.
|
|
75
78
|
*/
|
|
76
79
|
export declare function sectionOf(markdown: string, anchor: string): string | null;
|
package/dist/transclusion.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* and never blocks (§4.1). Wherever expansion cannot happen the embed becomes
|
|
17
17
|
* a link and is never dropped, which is what `demoteEmbeds` does.
|
|
18
18
|
*/
|
|
19
|
-
import { codeRegions, headingKey, insideCode, insideTable, outsideCode, tableRegions } from './markdown.js';
|
|
19
|
+
import { codeRegions, fenceRegions, headingKey, insideCode, insideTable, outsideCode, tableRegions } from './markdown.js';
|
|
20
20
|
/** Same shape the link extractor matches, plus the leading `!`. */
|
|
21
21
|
const EMBED = /!\[\[([^\]|#]+?)(?:#([^\]|]+?))?(?:\|[^\]]*?)?\]\]/g;
|
|
22
22
|
/**
|
|
@@ -136,13 +136,22 @@ function escapeRegExp(value) {
|
|
|
136
136
|
* The section of a document, cut syntactically: from the heading whose slug
|
|
137
137
|
* matches the anchor to the next heading of equal or higher level. No notebook
|
|
138
138
|
* convention takes part in this: the cut is syntactic.
|
|
139
|
+
*
|
|
140
|
+
* A line inside a fence is code and never a heading, so the `# Step 1` comment
|
|
141
|
+
* of a shell block neither opens a section nor ends one.
|
|
139
142
|
*/
|
|
140
143
|
export function sectionOf(markdown, anchor) {
|
|
141
144
|
const wanted = headingKey(anchor);
|
|
142
145
|
const lines = markdown.split('\n');
|
|
146
|
+
const fences = fenceRegions(markdown);
|
|
143
147
|
let start = -1;
|
|
144
148
|
let level = 0;
|
|
149
|
+
let at = 0;
|
|
145
150
|
for (let index = 0; index < lines.length; index++) {
|
|
151
|
+
const offset = at;
|
|
152
|
+
at += (lines[index] ?? '').length + 1;
|
|
153
|
+
if (insideCode(fences, offset))
|
|
154
|
+
continue;
|
|
146
155
|
const heading = HEADING.exec(lines[index] ?? '');
|
|
147
156
|
if (!heading)
|
|
148
157
|
continue;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memorysmithapp/markdown-renderer",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "A React component that draws a MemorySmith note exactly as MemorySmith's reading page does.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"unified": "11.0.5"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"@memorysmithapp/markdown-spec": "0.3.
|
|
70
|
+
"@memorysmithapp/markdown-spec": "0.3.2",
|
|
71
71
|
"@types/hast": "3.0.5",
|
|
72
72
|
"@types/react": "19.2.18",
|
|
73
73
|
"@types/react-dom": "19.2.4",
|
package/structure.css
CHANGED
|
@@ -37,6 +37,13 @@
|
|
|
37
37
|
border: 0;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/* The words of a callout's title are one item of its row, so a title that
|
|
41
|
+
carries emphasis wraps as one sentence beside the icon, never as columns. */
|
|
42
|
+
.markdown .callout-title-text {
|
|
43
|
+
flex: 1 1 auto;
|
|
44
|
+
min-width: 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
40
47
|
/* An icon a theme draws as an element takes the place of the one the
|
|
41
48
|
stylesheet draws as a mask, at the size of the title it sits in. */
|
|
42
49
|
.markdown .callout-title.has-icon::before {
|
package/theme-default.css
CHANGED
|
@@ -61,6 +61,11 @@
|
|
|
61
61
|
--ms-callout-stop: var(--markdown-callout-stop, 180, 35, 24);
|
|
62
62
|
--ms-callout-aside: var(--markdown-callout-aside, 109, 40, 217);
|
|
63
63
|
--ms-callout-quote: var(--markdown-callout-quote, 84, 93, 115);
|
|
64
|
+
/* A link in a callout title is underlined: the title is in the callout's
|
|
65
|
+
colour and weight, and the blue family's colour is the accent's, so a link
|
|
66
|
+
there would read as more of the title. */
|
|
67
|
+
--ms-callout-title-link-decoration: var(--markdown-callout-title-link-decoration, underline 1px);
|
|
68
|
+
--ms-callout-title-link-offset: var(--markdown-callout-title-link-offset, 0.15em);
|
|
64
69
|
--ms-font-sans: var(--markdown-font-sans, 'Inter', 'Segoe UI', system-ui, sans-serif);
|
|
65
70
|
--ms-font-display: var(--markdown-font-display, 'Space Grotesk', 'Segoe UI', system-ui, sans-serif);
|
|
66
71
|
--ms-font-mono: var(--markdown-font-mono, ui-monospace, 'Cascadia Code', Consolas, monospace);
|
|
@@ -360,6 +365,10 @@
|
|
|
360
365
|
/* Room for the button, so the first line is never under it. */
|
|
361
366
|
padding-right: 6.5rem;
|
|
362
367
|
}
|
|
368
|
+
/* An empty block keeps the height of one line, so its button fits inside it. */
|
|
369
|
+
.markdown .code-block > pre > code:empty::before {
|
|
370
|
+
content: '\200b';
|
|
371
|
+
}
|
|
363
372
|
.markdown pre code {
|
|
364
373
|
background: none;
|
|
365
374
|
color: var(--ms-text);
|
|
@@ -579,6 +588,13 @@
|
|
|
579
588
|
font-weight: 600;
|
|
580
589
|
line-height: var(--ms-line-height-tight);
|
|
581
590
|
}
|
|
591
|
+
/* A link in the title is marked by more than its colour, which may be the
|
|
592
|
+
title's own; a pending link keeps its dashed rule, and a footnote's number
|
|
593
|
+
its own look. */
|
|
594
|
+
.markdown .callout-title :is(a, .wikilink):not(.wikilink-pending, [data-footnote-ref]) {
|
|
595
|
+
text-decoration: var(--ms-callout-title-link-decoration);
|
|
596
|
+
text-underline-offset: var(--ms-callout-title-link-offset);
|
|
597
|
+
}
|
|
582
598
|
.markdown .callout-title::before {
|
|
583
599
|
content: '';
|
|
584
600
|
flex: 0 0 auto;
|