remark-notes-plugin 1.0.1 → 2.0.0
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/README.md +2 -3
- package/dist/index.d.ts +4 -1
- package/dist/index.js +51 -146
- package/dist/lib/icons-hast.d.ts +41 -0
- package/dist/lib/icons-hast.js +361 -0
- package/dist/lib/node-structure.d.ts +25 -0
- package/dist/lib/node-structure.js +94 -0
- package/dist/lib/styles.d.ts +6 -0
- package/dist/lib/styles.js +13 -0
- package/dist/lib/types/options.d.ts +93 -0
- package/dist/lib/types/options.js +6 -0
- package/dist/lib/validation.d.ts +54 -0
- package/dist/lib/validation.js +65 -0
- package/dist/styles.css +1 -1
- package/package.json +25 -6
- package/dist/test.d.ts +0 -1
- package/dist/test.js +0 -57
package/README.md
CHANGED
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
A powerful TypeScript remark plugin that transforms markdown blockquotes into beautifully styled note elements. Add professional-looking notes, tips, quotes, and more to your markdown documentation with minimal effort!
|
|
4
4
|
|
|
5
|
+

|
|
5
6
|

|
|
6
7
|

|
|
7
|
-
|
|
8
|
-
> [!note]
|
|
9
|
-
> This was built for my AstroJS website but should work with any project.
|
|
8
|
+

|
|
10
9
|
|
|
11
10
|
## ✨ Features
|
|
12
11
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
import type { Node } from 'unist';
|
|
2
|
-
|
|
2
|
+
import type { RemarkNotesOptions } from './lib/types/options.js';
|
|
3
|
+
export type { RemarkNotesOptions } from './lib/types/options.js';
|
|
4
|
+
export type { ValidNoteType } from './lib/icons-hast.js';
|
|
5
|
+
export default function remarkNotes(options?: RemarkNotesOptions): (tree: Node) => void;
|
package/dist/index.js
CHANGED
|
@@ -1,166 +1,71 @@
|
|
|
1
1
|
import { visit } from 'unist-util-visit';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
<g stroke-linecap="round" stroke-linejoin="round" stroke-width="0.048">
|
|
11
|
-
</g>
|
|
12
|
-
<g>
|
|
13
|
-
<path d="M8 2V5" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round">
|
|
14
|
-
</path>
|
|
15
|
-
<path d="M16 2V5" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round">
|
|
16
|
-
</path>
|
|
17
|
-
<path d="M21 8.5V17C21 20 19.5 22 16 22H8C4.5 22 3 20 3 17V8.5C3 5.5 4.5 3.5 8 3.5H16C19.5 3.5 21 5.5 21 8.5Z"
|
|
18
|
-
stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round">
|
|
19
|
-
</path>
|
|
20
|
-
<path d="M8 11H16" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round">
|
|
21
|
-
</path>
|
|
22
|
-
<path d="M8 16H12" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round">
|
|
23
|
-
</path>
|
|
24
|
-
</g>
|
|
25
|
-
</svg>`,
|
|
26
|
-
type: 'note'
|
|
27
|
-
},
|
|
28
|
-
'tip': {
|
|
29
|
-
icon: `<svg viewBox="-0.5 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
30
|
-
<g stroke-linecap="round" stroke-linejoin="round"></g>
|
|
31
|
-
<g>
|
|
32
|
-
<path
|
|
33
|
-
d="M19.0006 9.03002C19.0007 8.10058 18.8158 7.18037 18.4565 6.32317C18.0972 5.46598 17.5709 4.68895 16.9081 4.03734C16.2453 3.38574 15.4594 2.87265 14.5962 2.52801C13.7331 2.18336 12.8099 2.01409 11.8806 2.03002C10.0966 2.08307 8.39798 2.80604 7.12302 4.05504C5.84807 5.30405 5.0903 6.98746 5.00059 8.77001C4.95795 9.9595 5.21931 11.1402 5.75999 12.2006C6.30067 13.2609 7.10281 14.1659 8.09058 14.83C8.36897 15.011 8.59791 15.2584 8.75678 15.5499C8.91565 15.8415 8.99945 16.168 9.00059 16.5V18.03H15.0006V16.5C15.0006 16.1689 15.0829 15.843 15.24 15.5515C15.3971 15.26 15.6241 15.0121 15.9006 14.83C16.8528 14.1911 17.6336 13.328 18.1741 12.3167C18.7147 11.3054 18.9985 10.1767 19.0006 9.03002Z"
|
|
34
|
-
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
35
|
-
<path d="M15 21.04C14.1345 21.6891 13.0819 22.04 12 22.04C10.9181 22.04 9.86548 21.6891 9 21.04"
|
|
36
|
-
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
37
|
-
<path
|
|
38
|
-
d="M11.9901 5.64001L10.3301 8.41998C10.2549 8.54184 10.2138 8.68167 10.2111 8.82483C10.2084 8.96799 10.2441 9.10925 10.3146 9.23389C10.3851 9.35852 10.4877 9.46195 10.6118 9.53339C10.7359 9.60482 10.8769 9.64165 11.0201 9.64001H13.0201C13.1617 9.63947 13.301 9.67657 13.4237 9.7475C13.5463 9.81843 13.6479 9.92063 13.7181 10.0437C13.7883 10.1668 13.8245 10.3063 13.8231 10.4479C13.8217 10.5896 13.7827 10.7283 13.7101 10.85L12.0301 13.64"
|
|
39
|
-
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
40
|
-
</g>
|
|
41
|
-
</svg>`,
|
|
42
|
-
type: 'tip'
|
|
43
|
-
},
|
|
44
|
-
'important': {
|
|
45
|
-
icon: `<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg" fill="currentColor">
|
|
46
|
-
<g stroke-width="0"></g>
|
|
47
|
-
<g stroke-linecap="round" stroke-linejoin="round"></g>
|
|
48
|
-
<g>
|
|
49
|
-
<path fill="currentColor" d="M9,14a1.5,1.5,0,1,1,1.5068-1.5A1.5035,1.5035,0,0,1,9,14Z"></path>
|
|
50
|
-
<path fill="currentColor" d="M9,2A7,7,0,1,1,2,9,7.0079,7.0079,0,0,1,9,2M9,0a9,9,0,1,0,9,9A9,9,0,0,0,9,0Z"></path>
|
|
51
|
-
<path fill="currentColor" d="M10,4H8a1,1,0,0,0-.97,1.2425l1,4a1,1,0,0,0,1.94,0l1-4A1,1,0,0,0,10,4Zm0,2h0Z"></path>
|
|
52
|
-
</g>
|
|
53
|
-
</svg>`,
|
|
54
|
-
type: 'important'
|
|
55
|
-
},
|
|
56
|
-
'quote': {
|
|
57
|
-
icon: `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
58
|
-
<g stroke-linecap="round" stroke-linejoin="round"></g>
|
|
59
|
-
<g>
|
|
60
|
-
<path
|
|
61
|
-
d="M14 15V14C14 13.0681 14 12.6022 14.1522 12.2346C14.3552 11.7446 14.7446 11.3552 15.2346 11.1522C15.6022 11 16.0681 11 17 11H17.5C18.9045 11 19.6067 11 20.1111 11.3371C20.3295 11.483 20.517 11.6705 20.6629 11.8889C21 12.3933 21 13.0955 21 14.5V15.3431C21 16.1606 21 16.5694 20.8478 16.9369C20.6955 17.3045 20.4065 17.5935 19.8284 18.1716L19.2396 18.7604C18.7822 19.2178 18 18.8938 18 18.2469V17.8787C18 17.3934 17.6066 17 17.1213 17H16C14.8954 17 14 16.1046 14 15Z"
|
|
62
|
-
stroke-width="1.5" stroke-linejoin="round"></path>
|
|
63
|
-
<path
|
|
64
|
-
d="M3 9V8C3 7.06812 3 6.60218 3.15224 6.23463C3.35523 5.74458 3.74458 5.35523 4.23463 5.15224C4.60218 5 5.06812 5 6 5H6.5C7.90446 5 8.60669 5 9.11114 5.33706C9.32952 5.48298 9.51702 5.67048 9.66294 5.88886C10 6.39331 10 7.09554 10 8.5V9.34315C10 10.1606 10 10.5694 9.84776 10.9369C9.69552 11.3045 9.40649 11.5935 8.82843 12.1716L8.23965 12.7604C7.78219 13.2178 7 12.8938 7 12.2469V11.8787C7 11.3934 6.6066 11 6.12132 11H5C3.89543 11 3 10.1046 3 9Z"
|
|
65
|
-
stroke-width="1.5" stroke-linejoin="round"></path>
|
|
66
|
-
</g>
|
|
67
|
-
</svg>`,
|
|
68
|
-
type: 'quote'
|
|
69
|
-
},
|
|
70
|
-
'bonus': {
|
|
71
|
-
icon: `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
72
|
-
<g stroke-linecap="round" stroke-linejoin="round"></g>
|
|
73
|
-
<g>
|
|
74
|
-
<path
|
|
75
|
-
d="M9.23163 8.61762C7.26389 9.06284 6.28001 9.28545 6.04594 10.0382C5.81186 10.7909 6.4826 11.5753 7.82408 13.1439L8.17113 13.5498C8.55234 13.9955 8.74294 14.2184 8.82869 14.4942C8.91444 14.7699 8.88562 15.0673 8.82799 15.662L8.77552 16.2035C8.5727 18.2965 8.4713 19.343 9.08412 19.8082C9.69694 20.2734 10.6181 19.8492 12.4605 19.0009L12.9372 18.7815C13.4607 18.5404 13.7225 18.4199 14 18.4199C14.2775 18.4199 14.5393 18.5404 15.0628 18.7815L15.5395 19.0009C17.3819 19.8492 18.3031 20.2734 18.9159 19.8082C19.5287 19.343 19.4273 18.2965 19.2245 16.2035M20.1759 13.1439C21.5174 11.5753 22.1881 10.7909 21.9541 10.0382C21.72 9.28545 20.7361 9.06284 18.7684 8.61762L18.2593 8.50244C17.7001 8.37592 17.4205 8.31266 17.196 8.14225C16.9716 7.97183 16.8276 7.71355 16.5396 7.19699L16.2775 6.7267C15.2641 4.9089 14.7575 4 14 4C13.2425 4 12.7359 4.9089 11.7225 6.7267"
|
|
76
|
-
stroke-width="1.5" stroke-linecap="round"></path>
|
|
77
|
-
<path d="M2.08887 16C3.20445 15.121 4.68639 14.7971 6.08887 15.1257" stroke-width="1.5"
|
|
78
|
-
stroke-linecap="round"></path>
|
|
79
|
-
<path d="M2.08887 10.5C3.08887 10 3.37862 10.0605 4.08887 10" stroke-width="1.5"
|
|
80
|
-
stroke-linecap="round"></path>
|
|
81
|
-
<path d="M2 5.60867L2.20816 5.48676C4.41383 4.19506 6.75032 3.84687 8.95304 4.48161L9.16092 4.54152"
|
|
82
|
-
stroke-width="1.5" stroke-linecap="round"></path>
|
|
83
|
-
</g>
|
|
84
|
-
</svg>`,
|
|
85
|
-
type: 'bonus'
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
// Get the styles content
|
|
89
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
90
|
-
const __dirname = dirname(__filename);
|
|
91
|
-
// Look for styles.css in the package root (one level up from dist)
|
|
92
|
-
const stylesPath = join(__dirname, 'styles.css');
|
|
93
|
-
const styles = readFileSync(stylesPath, 'utf-8');
|
|
94
|
-
export default function remarkNotes() {
|
|
2
|
+
import { styles } from './lib/styles.js';
|
|
3
|
+
import { createNoteStructure } from './lib/node-structure.js';
|
|
4
|
+
import { isValidNoteType } from './lib/validation.js';
|
|
5
|
+
export default function remarkNotes(options = {}) {
|
|
6
|
+
// Extract options with defaults
|
|
7
|
+
const classPrefix = options.classPrefix ?? '';
|
|
8
|
+
const injectStyles = options.injectStyles ?? true;
|
|
9
|
+
let hasInjectedStyles = false;
|
|
95
10
|
return (tree) => {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
11
|
+
// Inject styles at the beginning of the document (only once) if enabled
|
|
12
|
+
// Using proper mdast node instead of HTML string for MDX compatibility
|
|
13
|
+
if (injectStyles && !hasInjectedStyles) {
|
|
14
|
+
const root = tree;
|
|
15
|
+
if (root.children) {
|
|
16
|
+
// Create a proper mdast node that will be transformed to <style> tag
|
|
17
|
+
root.children.unshift({
|
|
18
|
+
type: 'paragraph',
|
|
19
|
+
data: {
|
|
20
|
+
hName: 'style',
|
|
21
|
+
hProperties: {}
|
|
22
|
+
},
|
|
23
|
+
children: [{
|
|
24
|
+
type: 'text',
|
|
25
|
+
value: styles
|
|
26
|
+
}]
|
|
103
27
|
});
|
|
104
28
|
hasInjectedStyles = true;
|
|
105
29
|
}
|
|
106
|
-
}
|
|
107
|
-
visit(tree, 'blockquote', (node) => {
|
|
30
|
+
}
|
|
31
|
+
visit(tree, 'blockquote', (node, index, parent) => {
|
|
32
|
+
// Validate we have parent and index for proper node replacement
|
|
33
|
+
if (!parent || index === null || index === undefined)
|
|
34
|
+
return;
|
|
108
35
|
const firstParagraph = node.children[0];
|
|
109
|
-
if (!firstParagraph)
|
|
36
|
+
if (!firstParagraph || firstParagraph.type !== 'paragraph')
|
|
110
37
|
return;
|
|
111
38
|
const firstChild = firstParagraph.children[0];
|
|
112
39
|
if (!firstChild || firstChild.type !== 'text')
|
|
113
40
|
return;
|
|
114
41
|
const textNode = firstChild;
|
|
115
|
-
|
|
116
|
-
return;
|
|
117
|
-
// Updated pattern to match [!type]
|
|
42
|
+
// Match [!type] pattern
|
|
118
43
|
const match = textNode.value.match(/^\[!(\w+)\]/);
|
|
119
44
|
if (!match)
|
|
120
45
|
return;
|
|
121
|
-
const noteType = match[1].toLowerCase();
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
46
|
+
const noteType = match[1].toLowerCase();
|
|
47
|
+
// Validate note type - skip transformation if invalid (graceful degradation)
|
|
48
|
+
if (!isValidNoteType(noteType))
|
|
49
|
+
return;
|
|
50
|
+
// Clone children to preserve original markdown structure
|
|
51
|
+
const children = [...node.children];
|
|
52
|
+
// Process the first paragraph to remove the note marker
|
|
53
|
+
if (children.length > 0 && children[0].type === 'paragraph') {
|
|
54
|
+
const firstPara = children[0];
|
|
55
|
+
const firstTextNode = firstPara.children[0];
|
|
56
|
+
if (firstTextNode && firstTextNode.type === 'text') {
|
|
57
|
+
// Remove the [!type] marker and any trailing whitespace
|
|
58
|
+
firstTextNode.value = firstTextNode.value.replace(/^\[!\w+\]\s*/, '');
|
|
59
|
+
// If the text node is now empty and it's the only child, remove the paragraph
|
|
60
|
+
if (firstTextNode.value === '' && firstPara.children.length === 1) {
|
|
61
|
+
children.shift();
|
|
137
62
|
}
|
|
138
63
|
}
|
|
139
|
-
// Convert the modified markdown structure to HTML
|
|
140
|
-
const contentHast = toHast({
|
|
141
|
-
type: 'root',
|
|
142
|
-
children: children
|
|
143
|
-
});
|
|
144
|
-
// Convert hast to HTML string
|
|
145
|
-
const contentHtml = contentHast ? toHtml(contentHast) : '';
|
|
146
|
-
// Create HTML structure using classes
|
|
147
|
-
const html = {
|
|
148
|
-
type: 'html',
|
|
149
|
-
value: `
|
|
150
|
-
<div class="remark-note ${noteConfig.type}">
|
|
151
|
-
<div class="remark-note-header">
|
|
152
|
-
<span class="remark-note-icon" data-type="${noteConfig.type}">${noteConfig.icon}</span>
|
|
153
|
-
<span class="remark-note-title">${noteType}</span>
|
|
154
|
-
</div>
|
|
155
|
-
<div class="remark-note-content">
|
|
156
|
-
${contentHtml}
|
|
157
|
-
</div>
|
|
158
|
-
</div>
|
|
159
|
-
`
|
|
160
|
-
};
|
|
161
|
-
// Replace the blockquote with our HTML
|
|
162
|
-
Object.assign(node, html);
|
|
163
64
|
}
|
|
65
|
+
// Create the note structure using proper mdast nodes (with hast-converted SVG icons)
|
|
66
|
+
const noteContainer = createNoteStructure(noteType, children, classPrefix);
|
|
67
|
+
// Replace the blockquote with the container
|
|
68
|
+
parent.children[index] = noteContainer;
|
|
164
69
|
});
|
|
165
70
|
};
|
|
166
71
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hast (Hypertext Abstract Syntax Tree) representations of note icons
|
|
3
|
+
*
|
|
4
|
+
* This module defines SVG icons as proper hast element nodes instead of HTML strings.
|
|
5
|
+
* These can be converted to mdast using hast-util-to-mdast for proper AST manipulation.
|
|
6
|
+
*
|
|
7
|
+
* @module lib/icons-hast
|
|
8
|
+
*/
|
|
9
|
+
import type { Element } from 'hast';
|
|
10
|
+
/**
|
|
11
|
+
* Valid note types supported by the plugin
|
|
12
|
+
*/
|
|
13
|
+
export declare const VALID_NOTE_TYPES: readonly ["note", "tip", "important", "quote", "bonus"];
|
|
14
|
+
/**
|
|
15
|
+
* Type for valid note type strings
|
|
16
|
+
*/
|
|
17
|
+
export type ValidNoteType = typeof VALID_NOTE_TYPES[number];
|
|
18
|
+
/**
|
|
19
|
+
* Note icon - Calendar/document icon
|
|
20
|
+
*/
|
|
21
|
+
export declare const noteIcon: Element;
|
|
22
|
+
/**
|
|
23
|
+
* Tip icon - Lightbulb icon
|
|
24
|
+
*/
|
|
25
|
+
export declare const tipIcon: Element;
|
|
26
|
+
/**
|
|
27
|
+
* Important icon - Exclamation circle icon
|
|
28
|
+
*/
|
|
29
|
+
export declare const importantIcon: Element;
|
|
30
|
+
/**
|
|
31
|
+
* Quote icon - Quote marks icon
|
|
32
|
+
*/
|
|
33
|
+
export declare const quoteIcon: Element;
|
|
34
|
+
/**
|
|
35
|
+
* Bonus icon - Star/sparkle icon
|
|
36
|
+
*/
|
|
37
|
+
export declare const bonusIcon: Element;
|
|
38
|
+
/**
|
|
39
|
+
* Map of note types to their hast icon representations
|
|
40
|
+
*/
|
|
41
|
+
export declare const ICON_HAST_MAP: Record<ValidNoteType, Element>;
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hast (Hypertext Abstract Syntax Tree) representations of note icons
|
|
3
|
+
*
|
|
4
|
+
* This module defines SVG icons as proper hast element nodes instead of HTML strings.
|
|
5
|
+
* These can be converted to mdast using hast-util-to-mdast for proper AST manipulation.
|
|
6
|
+
*
|
|
7
|
+
* @module lib/icons-hast
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Valid note types supported by the plugin
|
|
11
|
+
*/
|
|
12
|
+
export const VALID_NOTE_TYPES = ['note', 'tip', 'important', 'quote', 'bonus'];
|
|
13
|
+
/**
|
|
14
|
+
* Note icon - Calendar/document icon
|
|
15
|
+
*/
|
|
16
|
+
export const noteIcon = {
|
|
17
|
+
type: 'element',
|
|
18
|
+
tagName: 'svg',
|
|
19
|
+
properties: {
|
|
20
|
+
viewBox: '0 0 24 24',
|
|
21
|
+
fill: 'none',
|
|
22
|
+
xmlns: 'http://www.w3.org/2000/svg'
|
|
23
|
+
},
|
|
24
|
+
children: [
|
|
25
|
+
{
|
|
26
|
+
type: 'element',
|
|
27
|
+
tagName: 'g',
|
|
28
|
+
properties: {
|
|
29
|
+
strokeLinecap: 'round',
|
|
30
|
+
strokeLinejoin: 'round',
|
|
31
|
+
strokeWidth: '0.048'
|
|
32
|
+
},
|
|
33
|
+
children: []
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
type: 'element',
|
|
37
|
+
tagName: 'g',
|
|
38
|
+
properties: {},
|
|
39
|
+
children: [
|
|
40
|
+
{
|
|
41
|
+
type: 'element',
|
|
42
|
+
tagName: 'path',
|
|
43
|
+
properties: {
|
|
44
|
+
d: 'M8 2V5',
|
|
45
|
+
strokeWidth: '1.5',
|
|
46
|
+
strokeMiterlimit: '10',
|
|
47
|
+
strokeLinecap: 'round',
|
|
48
|
+
strokeLinejoin: 'round'
|
|
49
|
+
},
|
|
50
|
+
children: []
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
type: 'element',
|
|
54
|
+
tagName: 'path',
|
|
55
|
+
properties: {
|
|
56
|
+
d: 'M16 2V5',
|
|
57
|
+
strokeWidth: '1.5',
|
|
58
|
+
strokeMiterlimit: '10',
|
|
59
|
+
strokeLinecap: 'round',
|
|
60
|
+
strokeLinejoin: 'round'
|
|
61
|
+
},
|
|
62
|
+
children: []
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
type: 'element',
|
|
66
|
+
tagName: 'path',
|
|
67
|
+
properties: {
|
|
68
|
+
d: 'M21 8.5V17C21 20 19.5 22 16 22H8C4.5 22 3 20 3 17V8.5C3 5.5 4.5 3.5 8 3.5H16C19.5 3.5 21 5.5 21 8.5Z',
|
|
69
|
+
strokeWidth: '1.5',
|
|
70
|
+
strokeMiterlimit: '10',
|
|
71
|
+
strokeLinecap: 'round',
|
|
72
|
+
strokeLinejoin: 'round'
|
|
73
|
+
},
|
|
74
|
+
children: []
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
type: 'element',
|
|
78
|
+
tagName: 'path',
|
|
79
|
+
properties: {
|
|
80
|
+
d: 'M8 11H16',
|
|
81
|
+
strokeWidth: '1.5',
|
|
82
|
+
strokeMiterlimit: '10',
|
|
83
|
+
strokeLinecap: 'round',
|
|
84
|
+
strokeLinejoin: 'round'
|
|
85
|
+
},
|
|
86
|
+
children: []
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
type: 'element',
|
|
90
|
+
tagName: 'path',
|
|
91
|
+
properties: {
|
|
92
|
+
d: 'M8 16H12',
|
|
93
|
+
strokeWidth: '1.5',
|
|
94
|
+
strokeMiterlimit: '10',
|
|
95
|
+
strokeLinecap: 'round',
|
|
96
|
+
strokeLinejoin: 'round'
|
|
97
|
+
},
|
|
98
|
+
children: []
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Tip icon - Lightbulb icon
|
|
106
|
+
*/
|
|
107
|
+
export const tipIcon = {
|
|
108
|
+
type: 'element',
|
|
109
|
+
tagName: 'svg',
|
|
110
|
+
properties: {
|
|
111
|
+
viewBox: '-0.5 0 25 25',
|
|
112
|
+
fill: 'none',
|
|
113
|
+
xmlns: 'http://www.w3.org/2000/svg'
|
|
114
|
+
},
|
|
115
|
+
children: [
|
|
116
|
+
{
|
|
117
|
+
type: 'element',
|
|
118
|
+
tagName: 'g',
|
|
119
|
+
properties: {
|
|
120
|
+
strokeLinecap: 'round',
|
|
121
|
+
strokeLinejoin: 'round'
|
|
122
|
+
},
|
|
123
|
+
children: []
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
type: 'element',
|
|
127
|
+
tagName: 'g',
|
|
128
|
+
properties: {},
|
|
129
|
+
children: [
|
|
130
|
+
{
|
|
131
|
+
type: 'element',
|
|
132
|
+
tagName: 'path',
|
|
133
|
+
properties: {
|
|
134
|
+
d: 'M19.0006 9.03002C19.0007 8.10058 18.8158 7.18037 18.4565 6.32317C18.0972 5.46598 17.5709 4.68895 16.9081 4.03734C16.2453 3.38574 15.4594 2.87265 14.5962 2.52801C13.7331 2.18336 12.8099 2.01409 11.8806 2.03002C10.0966 2.08307 8.39798 2.80604 7.12302 4.05504C5.84807 5.30405 5.0903 6.98746 5.00059 8.77001C4.95795 9.9595 5.21931 11.1402 5.75999 12.2006C6.30067 13.2609 7.10281 14.1659 8.09058 14.83C8.36897 15.011 8.59791 15.2584 8.75678 15.5499C8.91565 15.8415 8.99945 16.168 9.00059 16.5V18.03H15.0006V16.5C15.0006 16.1689 15.0829 15.843 15.24 15.5515C15.3971 15.26 15.6241 15.0121 15.9006 14.83C16.8528 14.1911 17.6336 13.328 18.1741 12.3167C18.7147 11.3054 18.9985 10.1767 19.0006 9.03002Z',
|
|
135
|
+
strokeWidth: '1.5',
|
|
136
|
+
strokeLinecap: 'round',
|
|
137
|
+
strokeLinejoin: 'round'
|
|
138
|
+
},
|
|
139
|
+
children: []
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
type: 'element',
|
|
143
|
+
tagName: 'path',
|
|
144
|
+
properties: {
|
|
145
|
+
d: 'M15 21.04C14.1345 21.6891 13.0819 22.04 12 22.04C10.9181 22.04 9.86548 21.6891 9 21.04',
|
|
146
|
+
strokeWidth: '1.5',
|
|
147
|
+
strokeLinecap: 'round',
|
|
148
|
+
strokeLinejoin: 'round'
|
|
149
|
+
},
|
|
150
|
+
children: []
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
type: 'element',
|
|
154
|
+
tagName: 'path',
|
|
155
|
+
properties: {
|
|
156
|
+
d: 'M11.9901 5.64001L10.3301 8.41998C10.2549 8.54184 10.2138 8.68167 10.2111 8.82483C10.2084 8.96799 10.2441 9.10925 10.3146 9.23389C10.3851 9.35852 10.4877 9.46195 10.6118 9.53339C10.7359 9.60482 10.8769 9.64165 11.0201 9.64001H13.0201C13.1617 9.63947 13.301 9.67657 13.4237 9.7475C13.5463 9.81843 13.6479 9.92063 13.7181 10.0437C13.7883 10.1668 13.8245 10.3063 13.8231 10.4479C13.8217 10.5896 13.7827 10.7283 13.7101 10.85L12.0301 13.64',
|
|
157
|
+
strokeWidth: '1.5',
|
|
158
|
+
strokeLinecap: 'round',
|
|
159
|
+
strokeLinejoin: 'round'
|
|
160
|
+
},
|
|
161
|
+
children: []
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
}
|
|
165
|
+
]
|
|
166
|
+
};
|
|
167
|
+
/**
|
|
168
|
+
* Important icon - Exclamation circle icon
|
|
169
|
+
*/
|
|
170
|
+
export const importantIcon = {
|
|
171
|
+
type: 'element',
|
|
172
|
+
tagName: 'svg',
|
|
173
|
+
properties: {
|
|
174
|
+
viewBox: '0 0 18 18',
|
|
175
|
+
xmlns: 'http://www.w3.org/2000/svg',
|
|
176
|
+
fill: 'currentColor'
|
|
177
|
+
},
|
|
178
|
+
children: [
|
|
179
|
+
{
|
|
180
|
+
type: 'element',
|
|
181
|
+
tagName: 'g',
|
|
182
|
+
properties: {
|
|
183
|
+
strokeWidth: '0'
|
|
184
|
+
},
|
|
185
|
+
children: []
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
type: 'element',
|
|
189
|
+
tagName: 'g',
|
|
190
|
+
properties: {
|
|
191
|
+
strokeLinecap: 'round',
|
|
192
|
+
strokeLinejoin: 'round'
|
|
193
|
+
},
|
|
194
|
+
children: []
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
type: 'element',
|
|
198
|
+
tagName: 'g',
|
|
199
|
+
properties: {},
|
|
200
|
+
children: [
|
|
201
|
+
{
|
|
202
|
+
type: 'element',
|
|
203
|
+
tagName: 'path',
|
|
204
|
+
properties: {
|
|
205
|
+
fill: 'currentColor',
|
|
206
|
+
d: 'M9,14a1.5,1.5,0,1,1,1.5068-1.5A1.5035,1.5035,0,0,1,9,14Z'
|
|
207
|
+
},
|
|
208
|
+
children: []
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
type: 'element',
|
|
212
|
+
tagName: 'path',
|
|
213
|
+
properties: {
|
|
214
|
+
fill: 'currentColor',
|
|
215
|
+
d: 'M9,2A7,7,0,1,1,2,9,7.0079,7.0079,0,0,1,9,2M9,0a9,9,0,1,0,9,9A9,9,0,0,0,9,0Z'
|
|
216
|
+
},
|
|
217
|
+
children: []
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
type: 'element',
|
|
221
|
+
tagName: 'path',
|
|
222
|
+
properties: {
|
|
223
|
+
fill: 'currentColor',
|
|
224
|
+
d: 'M10,4H8a1,1,0,0,0-.97,1.2425l1,4a1,1,0,0,0,1.94,0l1-4A1,1,0,0,0,10,4Zm0,2h0Z'
|
|
225
|
+
},
|
|
226
|
+
children: []
|
|
227
|
+
}
|
|
228
|
+
]
|
|
229
|
+
}
|
|
230
|
+
]
|
|
231
|
+
};
|
|
232
|
+
/**
|
|
233
|
+
* Quote icon - Quote marks icon
|
|
234
|
+
*/
|
|
235
|
+
export const quoteIcon = {
|
|
236
|
+
type: 'element',
|
|
237
|
+
tagName: 'svg',
|
|
238
|
+
properties: {
|
|
239
|
+
viewBox: '0 0 24 24',
|
|
240
|
+
fill: 'none',
|
|
241
|
+
xmlns: 'http://www.w3.org/2000/svg'
|
|
242
|
+
},
|
|
243
|
+
children: [
|
|
244
|
+
{
|
|
245
|
+
type: 'element',
|
|
246
|
+
tagName: 'g',
|
|
247
|
+
properties: {
|
|
248
|
+
strokeLinecap: 'round',
|
|
249
|
+
strokeLinejoin: 'round'
|
|
250
|
+
},
|
|
251
|
+
children: []
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
type: 'element',
|
|
255
|
+
tagName: 'g',
|
|
256
|
+
properties: {},
|
|
257
|
+
children: [
|
|
258
|
+
{
|
|
259
|
+
type: 'element',
|
|
260
|
+
tagName: 'path',
|
|
261
|
+
properties: {
|
|
262
|
+
d: 'M14 15V14C14 13.0681 14 12.6022 14.1522 12.2346C14.3552 11.7446 14.7446 11.3552 15.2346 11.1522C15.6022 11 16.0681 11 17 11H17.5C18.9045 11 19.6067 11 20.1111 11.3371C20.3295 11.483 20.517 11.6705 20.6629 11.8889C21 12.3933 21 13.0955 21 14.5V15.3431C21 16.1606 21 16.5694 20.8478 16.9369C20.6955 17.3045 20.4065 17.5935 19.8284 18.1716L19.2396 18.7604C18.7822 19.2178 18 18.8938 18 18.2469V17.8787C18 17.3934 17.6066 17 17.1213 17H16C14.8954 17 14 16.1046 14 15Z',
|
|
263
|
+
strokeWidth: '1.5',
|
|
264
|
+
strokeLinejoin: 'round'
|
|
265
|
+
},
|
|
266
|
+
children: []
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
type: 'element',
|
|
270
|
+
tagName: 'path',
|
|
271
|
+
properties: {
|
|
272
|
+
d: 'M3 9V8C3 7.06812 3 6.60218 3.15224 6.23463C3.35523 5.74458 3.74458 5.35523 4.23463 5.15224C4.60218 5 5.06812 5 6 5H6.5C7.90446 5 8.60669 5 9.11114 5.33706C9.32952 5.48298 9.51702 5.67048 9.66294 5.88886C10 6.39331 10 7.09554 10 8.5V9.34315C10 10.1606 10 10.5694 9.84776 10.9369C9.69552 11.3045 9.40649 11.5935 8.82843 12.1716L8.23965 12.7604C7.78219 13.2178 7 12.8938 7 12.2469V11.8787C7 11.3934 6.6066 11 6.12132 11H5C3.89543 11 3 10.1046 3 9Z',
|
|
273
|
+
strokeWidth: '1.5',
|
|
274
|
+
strokeLinejoin: 'round'
|
|
275
|
+
},
|
|
276
|
+
children: []
|
|
277
|
+
}
|
|
278
|
+
]
|
|
279
|
+
}
|
|
280
|
+
]
|
|
281
|
+
};
|
|
282
|
+
/**
|
|
283
|
+
* Bonus icon - Star/sparkle icon
|
|
284
|
+
*/
|
|
285
|
+
export const bonusIcon = {
|
|
286
|
+
type: 'element',
|
|
287
|
+
tagName: 'svg',
|
|
288
|
+
properties: {
|
|
289
|
+
viewBox: '0 0 24 24',
|
|
290
|
+
fill: 'none',
|
|
291
|
+
xmlns: 'http://www.w3.org/2000/svg'
|
|
292
|
+
},
|
|
293
|
+
children: [
|
|
294
|
+
{
|
|
295
|
+
type: 'element',
|
|
296
|
+
tagName: 'g',
|
|
297
|
+
properties: {
|
|
298
|
+
strokeLinecap: 'round',
|
|
299
|
+
strokeLinejoin: 'round'
|
|
300
|
+
},
|
|
301
|
+
children: []
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
type: 'element',
|
|
305
|
+
tagName: 'g',
|
|
306
|
+
properties: {},
|
|
307
|
+
children: [
|
|
308
|
+
{
|
|
309
|
+
type: 'element',
|
|
310
|
+
tagName: 'path',
|
|
311
|
+
properties: {
|
|
312
|
+
d: 'M9.23163 8.61762C7.26389 9.06284 6.28001 9.28545 6.04594 10.0382C5.81186 10.7909 6.4826 11.5753 7.82408 13.1439L8.17113 13.5498C8.55234 13.9955 8.74294 14.2184 8.82869 14.4942C8.91444 14.7699 8.88562 15.0673 8.82799 15.662L8.77552 16.2035C8.5727 18.2965 8.4713 19.343 9.08412 19.8082C9.69694 20.2734 10.6181 19.8492 12.4605 19.0009L12.9372 18.7815C13.4607 18.5404 13.7225 18.4199 14 18.4199C14.2775 18.4199 14.5393 18.5404 15.0628 18.7815L15.5395 19.0009C17.3819 19.8492 18.3031 20.2734 18.9159 19.8082C19.5287 19.343 19.4273 18.2965 19.2245 16.2035M20.1759 13.1439C21.5174 11.5753 22.1881 10.7909 21.9541 10.0382C21.72 9.28545 20.7361 9.06284 18.7684 8.61762L18.2593 8.50244C17.7001 8.37592 17.4205 8.31266 17.196 8.14225C16.9716 7.97183 16.8276 7.71355 16.5396 7.19699L16.2775 6.7267C15.2641 4.9089 14.7575 4 14 4C13.2425 4 12.7359 4.9089 11.7225 6.7267',
|
|
313
|
+
strokeWidth: '1.5',
|
|
314
|
+
strokeLinecap: 'round'
|
|
315
|
+
},
|
|
316
|
+
children: []
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
type: 'element',
|
|
320
|
+
tagName: 'path',
|
|
321
|
+
properties: {
|
|
322
|
+
d: 'M2.08887 16C3.20445 15.121 4.68639 14.7971 6.08887 15.1257',
|
|
323
|
+
strokeWidth: '1.5',
|
|
324
|
+
strokeLinecap: 'round'
|
|
325
|
+
},
|
|
326
|
+
children: []
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
type: 'element',
|
|
330
|
+
tagName: 'path',
|
|
331
|
+
properties: {
|
|
332
|
+
d: 'M2.08887 10.5C3.08887 10 3.37862 10.0605 4.08887 10',
|
|
333
|
+
strokeWidth: '1.5',
|
|
334
|
+
strokeLinecap: 'round'
|
|
335
|
+
},
|
|
336
|
+
children: []
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
type: 'element',
|
|
340
|
+
tagName: 'path',
|
|
341
|
+
properties: {
|
|
342
|
+
d: 'M2 5.60867L2.20816 5.48676C4.41383 4.19506 6.75032 3.84687 8.95304 4.48161L9.16092 4.54152',
|
|
343
|
+
strokeLinecap: 'round',
|
|
344
|
+
strokeWidth: '1.5'
|
|
345
|
+
},
|
|
346
|
+
children: []
|
|
347
|
+
}
|
|
348
|
+
]
|
|
349
|
+
}
|
|
350
|
+
]
|
|
351
|
+
};
|
|
352
|
+
/**
|
|
353
|
+
* Map of note types to their hast icon representations
|
|
354
|
+
*/
|
|
355
|
+
export const ICON_HAST_MAP = {
|
|
356
|
+
note: noteIcon,
|
|
357
|
+
tip: tipIcon,
|
|
358
|
+
important: importantIcon,
|
|
359
|
+
quote: quoteIcon,
|
|
360
|
+
bonus: bonusIcon
|
|
361
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates the mdast node structure for a note callout
|
|
3
|
+
*
|
|
4
|
+
* This module handles the creation of proper mdast nodes with transformation hints
|
|
5
|
+
* that tell remark-rehype how to convert them to the appropriate HTML structure.
|
|
6
|
+
*
|
|
7
|
+
* @module lib/create-note-structure
|
|
8
|
+
*/
|
|
9
|
+
import type { Node } from 'unist';
|
|
10
|
+
import { ValidNoteType } from './icons-hast.js';
|
|
11
|
+
/**
|
|
12
|
+
* Creates a properly structured mdast node for a note callout
|
|
13
|
+
*
|
|
14
|
+
* The structure uses standard mdast node types (paragraph, text) with data hints
|
|
15
|
+
* (hName, hProperties) to tell remark-rehype how to transform them into the
|
|
16
|
+
* desired HTML structure without requiring manual handlers.
|
|
17
|
+
*
|
|
18
|
+
* SVG icons are embedded as hast nodes using data.hast property for direct transformation.
|
|
19
|
+
*
|
|
20
|
+
* @param noteType - The type of note (note, tip, important, etc.)
|
|
21
|
+
* @param children - The content nodes (markdown) to include in the note
|
|
22
|
+
* @param classPrefix - The prefix for all CSS class names (default: 'remark-note')
|
|
23
|
+
* @returns A mdast node that will be transformed to the note HTML structure
|
|
24
|
+
*/
|
|
25
|
+
export declare function createNoteStructure(noteType: ValidNoteType, children: Node[], classPrefix?: string): any;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates the mdast node structure for a note callout
|
|
3
|
+
*
|
|
4
|
+
* This module handles the creation of proper mdast nodes with transformation hints
|
|
5
|
+
* that tell remark-rehype how to convert them to the appropriate HTML structure.
|
|
6
|
+
*
|
|
7
|
+
* @module lib/create-note-structure
|
|
8
|
+
*/
|
|
9
|
+
import { ICON_HAST_MAP } from './icons-hast.js';
|
|
10
|
+
/**
|
|
11
|
+
* Creates a properly structured mdast node for a note callout
|
|
12
|
+
*
|
|
13
|
+
* The structure uses standard mdast node types (paragraph, text) with data hints
|
|
14
|
+
* (hName, hProperties) to tell remark-rehype how to transform them into the
|
|
15
|
+
* desired HTML structure without requiring manual handlers.
|
|
16
|
+
*
|
|
17
|
+
* SVG icons are embedded as hast nodes using data.hast property for direct transformation.
|
|
18
|
+
*
|
|
19
|
+
* @param noteType - The type of note (note, tip, important, etc.)
|
|
20
|
+
* @param children - The content nodes (markdown) to include in the note
|
|
21
|
+
* @param classPrefix - The prefix for all CSS class names (default: 'remark-note')
|
|
22
|
+
* @returns A mdast node that will be transformed to the note HTML structure
|
|
23
|
+
*/
|
|
24
|
+
export function createNoteStructure(noteType, children, classPrefix = '') {
|
|
25
|
+
// Build class names: prefix is prepended to standard 'remark-note' names
|
|
26
|
+
// Default: 'remark-note-icon', With prefix 'my': 'my-remark-note-icon'
|
|
27
|
+
const baseClass = classPrefix ? `${classPrefix}-remark-note` : 'remark-note';
|
|
28
|
+
const makeClass = (suffix) => classPrefix ? `${classPrefix}-remark-note-${suffix}` : `remark-note-${suffix}`;
|
|
29
|
+
// Get the hast representation of the icon
|
|
30
|
+
const iconHast = ICON_HAST_MAP[noteType];
|
|
31
|
+
// Create icon span with embedded hast node
|
|
32
|
+
// Using data.hast tells remark-rehype to use this hast node directly
|
|
33
|
+
const iconNode = {
|
|
34
|
+
type: 'paragraph',
|
|
35
|
+
data: {
|
|
36
|
+
hName: 'span',
|
|
37
|
+
hProperties: {
|
|
38
|
+
className: [makeClass('icon')]
|
|
39
|
+
},
|
|
40
|
+
hChildren: [iconHast] // Embed hast node directly
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
// Create title span
|
|
44
|
+
const titleNode = {
|
|
45
|
+
type: 'paragraph',
|
|
46
|
+
data: {
|
|
47
|
+
hName: 'span',
|
|
48
|
+
hProperties: {
|
|
49
|
+
className: [makeClass('title')]
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
children: [
|
|
53
|
+
{
|
|
54
|
+
type: 'text',
|
|
55
|
+
value: noteType
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
};
|
|
59
|
+
// Create header div
|
|
60
|
+
const headerNode = {
|
|
61
|
+
type: 'paragraph',
|
|
62
|
+
data: {
|
|
63
|
+
hName: 'div',
|
|
64
|
+
hProperties: {
|
|
65
|
+
className: [makeClass('header')]
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
children: [iconNode, titleNode]
|
|
69
|
+
};
|
|
70
|
+
// Create content wrapper div
|
|
71
|
+
const contentNode = {
|
|
72
|
+
type: 'paragraph',
|
|
73
|
+
data: {
|
|
74
|
+
hName: 'div',
|
|
75
|
+
hProperties: {
|
|
76
|
+
className: [makeClass('content')]
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
children: children
|
|
80
|
+
};
|
|
81
|
+
// Build the structure: wrap everything in a blockquote container with data hints
|
|
82
|
+
// Using blockquote for semantic HTML since the source is a blockquote
|
|
83
|
+
const noteContainer = {
|
|
84
|
+
type: 'paragraph', // Use a standard mdast type
|
|
85
|
+
data: {
|
|
86
|
+
hName: 'blockquote', // Transform to blockquote for semantic HTML
|
|
87
|
+
hProperties: {
|
|
88
|
+
className: [baseClass, makeClass(noteType)]
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
children: [headerNode, contentNode]
|
|
92
|
+
};
|
|
93
|
+
return noteContainer;
|
|
94
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS styles for remark-notes
|
|
3
|
+
* Loaded once at module initialization
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync } from 'fs';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import { dirname, join } from 'path';
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = dirname(__filename);
|
|
10
|
+
// Read CSS file once at module load time
|
|
11
|
+
const cssPath = join(__dirname, '..', 'styles.css');
|
|
12
|
+
export const styles = readFileSync(cssPath, 'utf-8');
|
|
13
|
+
export default styles;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration options for the remark-notes plugin
|
|
3
|
+
*
|
|
4
|
+
* @module lib/types/options
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Options for configuring the remark-notes plugin behavior
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import remarkNotes from 'remark-notes-plugin'
|
|
12
|
+
*
|
|
13
|
+
* // Using default options
|
|
14
|
+
* unified().use(remarkNotes)
|
|
15
|
+
*
|
|
16
|
+
* // With custom class prefix
|
|
17
|
+
* unified().use(remarkNotes, { classPrefix: 'my-callout' })
|
|
18
|
+
*
|
|
19
|
+
* // Disable automatic style injection
|
|
20
|
+
* unified().use(remarkNotes, { injectStyles: false })
|
|
21
|
+
*
|
|
22
|
+
* // Both options
|
|
23
|
+
* unified().use(remarkNotes, {
|
|
24
|
+
* classPrefix: 'custom-note',
|
|
25
|
+
* injectStyles: false
|
|
26
|
+
* })
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export interface RemarkNotesOptions {
|
|
30
|
+
/**
|
|
31
|
+
* Custom prefix for all generated CSS class names
|
|
32
|
+
*
|
|
33
|
+
* This prefix is **prepended** to the standard 'remark-note' class names.
|
|
34
|
+
*
|
|
35
|
+
* **Default (no prefix):**
|
|
36
|
+
* - Container: `remark-note` and `remark-note-{noteType}`
|
|
37
|
+
* - Elements: `remark-note-header`, `remark-note-icon`, etc.
|
|
38
|
+
*
|
|
39
|
+
* **With prefix (e.g., 'my'):**
|
|
40
|
+
* - Container: `my-remark-note` and `my-remark-note-{noteType}`
|
|
41
|
+
* - Elements: `my-remark-note-header`, `my-remark-note-icon`, etc.
|
|
42
|
+
*
|
|
43
|
+
* @default '' (empty string - no prefix)
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* // No prefix (default)
|
|
48
|
+
* { classPrefix: '' }
|
|
49
|
+
* // Generates: class="remark-note remark-note-tip"
|
|
50
|
+
*
|
|
51
|
+
* // Custom prefix
|
|
52
|
+
* { classPrefix: 'my' }
|
|
53
|
+
* // Generates: class="my-remark-note my-remark-note-tip"
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @remarks
|
|
57
|
+
* The shipped CSS uses attribute selectors (e.g., `[class*="remark-note-icon"]`)
|
|
58
|
+
* and will work with any prefix automatically.
|
|
59
|
+
*/
|
|
60
|
+
classPrefix?: string;
|
|
61
|
+
/**
|
|
62
|
+
* Controls whether the plugin automatically injects styles into the document
|
|
63
|
+
*
|
|
64
|
+
* When `true` (default), the plugin injects a `<style>` tag containing the note styles
|
|
65
|
+
* directly into the AST. This is convenient for most use cases.
|
|
66
|
+
*
|
|
67
|
+
* When `false`, styles are not injected and you must manually import the CSS file:
|
|
68
|
+
* `import 'remark-notes-plugin/styles.css'`
|
|
69
|
+
*
|
|
70
|
+
* Set to `false` when:
|
|
71
|
+
* - Using Server-Side Rendering (SSR) with separate CSS extraction
|
|
72
|
+
* - Building with tools that handle CSS imports separately (Vite, Webpack, etc.)
|
|
73
|
+
* - Providing completely custom styles
|
|
74
|
+
* - You want more control over style loading order
|
|
75
|
+
*
|
|
76
|
+
* @default true
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* // Automatic style injection (default)
|
|
81
|
+
* { injectStyles: true }
|
|
82
|
+
*
|
|
83
|
+
* // Manual CSS import
|
|
84
|
+
* { injectStyles: false }
|
|
85
|
+
* // Then in your code:
|
|
86
|
+
* // import 'remark-notes-plugin/styles.css'
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* @remarks
|
|
90
|
+
* The CSS file is available at the package export: `remark-notes-plugin/styles.css`
|
|
91
|
+
*/
|
|
92
|
+
injectStyles?: boolean;
|
|
93
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation utilities for note types and error message generation
|
|
3
|
+
*
|
|
4
|
+
* @module lib/validation
|
|
5
|
+
*/
|
|
6
|
+
import type { Position } from 'unist';
|
|
7
|
+
import { ValidNoteType } from './icons-hast.js';
|
|
8
|
+
/**
|
|
9
|
+
* Type guard to check if a string is a valid note type
|
|
10
|
+
*
|
|
11
|
+
* @param type - The string to check
|
|
12
|
+
* @returns True if the type is a valid note type, false otherwise
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* if (isValidNoteType(noteType)) {
|
|
17
|
+
* // TypeScript now knows noteType is ValidNoteType
|
|
18
|
+
* createNoteStructure(noteType, ...)
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function isValidNoteType(type: string): type is ValidNoteType;
|
|
23
|
+
/**
|
|
24
|
+
* Creates a descriptive error message for invalid note types
|
|
25
|
+
*
|
|
26
|
+
* @param invalidType - The invalid note type that was encountered
|
|
27
|
+
* @param position - Optional position information from the AST node
|
|
28
|
+
* @returns A formatted error message with context
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const error = createInvalidTypeError('warn', node.position)
|
|
33
|
+
* // Returns: "Invalid note type 'warn' at line 5, column 3. Valid types are: note, tip, important, quote, bonus"
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare function createInvalidTypeError(invalidType: string, position?: Position): string;
|
|
37
|
+
/**
|
|
38
|
+
* Validates a note type and throws an error if invalid
|
|
39
|
+
*
|
|
40
|
+
* @param type - The note type to validate
|
|
41
|
+
* @param position - Optional position information for error messages
|
|
42
|
+
* @throws {Error} If the note type is invalid
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* try {
|
|
47
|
+
* validateNoteType(noteType, node.position)
|
|
48
|
+
* // If we get here, noteType is valid
|
|
49
|
+
* } catch (error) {
|
|
50
|
+
* console.error(error.message)
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare function validateNoteType(type: string, position?: Position): asserts type is ValidNoteType;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation utilities for note types and error message generation
|
|
3
|
+
*
|
|
4
|
+
* @module lib/validation
|
|
5
|
+
*/
|
|
6
|
+
import { VALID_NOTE_TYPES } from './icons-hast.js';
|
|
7
|
+
/**
|
|
8
|
+
* Type guard to check if a string is a valid note type
|
|
9
|
+
*
|
|
10
|
+
* @param type - The string to check
|
|
11
|
+
* @returns True if the type is a valid note type, false otherwise
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* if (isValidNoteType(noteType)) {
|
|
16
|
+
* // TypeScript now knows noteType is ValidNoteType
|
|
17
|
+
* createNoteStructure(noteType, ...)
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export function isValidNoteType(type) {
|
|
22
|
+
return VALID_NOTE_TYPES.includes(type);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Creates a descriptive error message for invalid note types
|
|
26
|
+
*
|
|
27
|
+
* @param invalidType - The invalid note type that was encountered
|
|
28
|
+
* @param position - Optional position information from the AST node
|
|
29
|
+
* @returns A formatted error message with context
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const error = createInvalidTypeError('warn', node.position)
|
|
34
|
+
* // Returns: "Invalid note type 'warn' at line 5, column 3. Valid types are: note, tip, important, quote, bonus"
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export function createInvalidTypeError(invalidType, position) {
|
|
38
|
+
const validTypes = VALID_NOTE_TYPES.join(', ');
|
|
39
|
+
const positionInfo = position?.start
|
|
40
|
+
? ` at line ${position.start.line}, column ${position.start.column}`
|
|
41
|
+
: '';
|
|
42
|
+
return `Invalid note type '${invalidType}'${positionInfo}. Valid types are: ${validTypes}`;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Validates a note type and throws an error if invalid
|
|
46
|
+
*
|
|
47
|
+
* @param type - The note type to validate
|
|
48
|
+
* @param position - Optional position information for error messages
|
|
49
|
+
* @throws {Error} If the note type is invalid
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* try {
|
|
54
|
+
* validateNoteType(noteType, node.position)
|
|
55
|
+
* // If we get here, noteType is valid
|
|
56
|
+
* } catch (error) {
|
|
57
|
+
* console.error(error.message)
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export function validateNoteType(type, position) {
|
|
62
|
+
if (!isValidNoteType(type)) {
|
|
63
|
+
throw new Error(createInvalidTypeError(type, position));
|
|
64
|
+
}
|
|
65
|
+
}
|
package/dist/styles.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
blockquote[class*=remark-note]{border:1px solid;border-radius:.5rem;font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-style:normal;font-weight:400;margin:1.5rem 0;padding:1.25rem;quotes:none;transition:all .2s ease-in-out}[class*=remark-note-header]{align-items:center;display:flex;gap:.75rem;margin-bottom:1rem}[class*=remark-note-icon]{align-items:center;border-radius:.5rem;display:flex;font-size:1.25rem;height:1.25rem;justify-content:center;width:1.25rem}[class*=remark-note-icon]>svg{height:100%;width:auto;fill:none}[class*=remark-note-title]{font-size:.85rem;font-weight:700;letter-spacing:.025em;line-height:.85rem;text-transform:uppercase}[class*=remark-note-content]{color:#1f2937;font-size:1rem;line-height:1.625}blockquote[class*=remark-note-note]{background-color:#318ce710;border-color:#318ce7;box-shadow:0 4px 6px -1px rgba(59,130,246,.1),0 2px 4px -2px rgba(59,130,246,.1)}blockquote[class*=remark-note-note] [class*=remark-note-icon]{stroke:#318ce7}blockquote[class*=remark-note-note] [class*=remark-note-title]{color:#318ce7}blockquote[class*=remark-note-important]{background-color:#ef9b0f10;border-color:#ef9b0f;box-shadow:0 4px 6px -1px rgba(147,51,234,.1),0 2px 4px -2px rgba(147,51,234,.1)}blockquote[class*=remark-note-important] [class*=remark-note-icon],blockquote[class*=remark-note-important] [class*=remark-note-title]{color:#ef9b0f}blockquote[class*=remark-note-quote]{background-color:#8c92ac10;border-color:#8c92ac;box-shadow:0 4px 6px -1px rgba(75,85,99,.1),0 2px 4px -2px rgba(75,85,99,.1)}blockquote[class*=remark-note-quote] [class*=remark-note-icon]{stroke:#8c92ac}blockquote[class*=remark-note-quote] [class*=remark-note-title]{color:#8c92ac}blockquote[class*=remark-note-bonus]{background-color:#9966cc10;border-color:#96c;box-shadow:0 4px 6px -1px rgba(219,39,119,.1),0 2px 4px -2px rgba(219,39,119,.1)}blockquote[class*=remark-note-bonus] [class*=remark-note-icon]{stroke:#96c}blockquote[class*=remark-note-bonus] [class*=remark-note-title]{color:#96c}blockquote[class*=remark-note-tip]{background-color:#7ccf0010;border-color:#7ccf00;box-shadow:0 4px 6px -1px rgba(59,130,246,.1),0 2px 4px -2px rgba(59,130,246,.1)}blockquote[class*=remark-note-tip] [class*=remark-note-icon]>svg{stroke:#7ccf00}blockquote[class*=remark-note-tip] [class*=remark-note-title]{color:#7ccf00}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "remark-notes-plugin",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "A remark plugin that transforms markdown quotes into styled note elements",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/rishichawda/remark-notes-plugin/issues"
|
|
8
8
|
},
|
|
9
|
-
"homepage": "https://github.
|
|
9
|
+
"homepage": "https://rishichawda.github.io/remark-notes-plugin/",
|
|
10
10
|
"author": "rishichawda",
|
|
11
11
|
"repository": {
|
|
12
12
|
"url": "https://github.com/rishichawda/remark-notes-plugin.git"
|
|
@@ -14,6 +14,15 @@
|
|
|
14
14
|
"type": "module",
|
|
15
15
|
"main": "dist/index.js",
|
|
16
16
|
"types": "dist/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.js",
|
|
21
|
+
"require": "./dist/index.js",
|
|
22
|
+
"default": "./dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./styles.css": "./dist/styles.css"
|
|
25
|
+
},
|
|
17
26
|
"files": [
|
|
18
27
|
"dist/",
|
|
19
28
|
"LICENSE",
|
|
@@ -26,7 +35,13 @@
|
|
|
26
35
|
"watch:css": "postcss styles.css -o dist/styles.css --watch",
|
|
27
36
|
"build": "yarn build:ts && yarn build:css",
|
|
28
37
|
"watch": "yarn watch:ts & yarn watch:css",
|
|
29
|
-
"test": "node --loader ts-node/esm
|
|
38
|
+
"test": "node --loader ts-node/esm __tests__/index.ts",
|
|
39
|
+
"test:styles": "node --loader ts-node/esm __tests__/styles.ts",
|
|
40
|
+
"test:custom-prefix": "node --loader ts-node/esm __tests__/custom-prefix.ts",
|
|
41
|
+
"test:validation": "node --loader ts-node/esm __tests__/validation.ts",
|
|
42
|
+
"test:edge-cases": "node --loader ts-node/esm __tests__/edge-cases.ts",
|
|
43
|
+
"test:all": "yarn test && yarn test:styles && yarn test:custom-prefix && yarn test:validation && yarn test:edge-cases",
|
|
44
|
+
"generate:fixtures": "node --loader ts-node/esm __tests__/generate-fixtures.ts"
|
|
30
45
|
},
|
|
31
46
|
"keywords": [
|
|
32
47
|
"remark",
|
|
@@ -36,11 +51,10 @@
|
|
|
36
51
|
"astro"
|
|
37
52
|
],
|
|
38
53
|
"dependencies": {
|
|
39
|
-
"hast
|
|
54
|
+
"hast": "^1.0.0",
|
|
40
55
|
"mdast": "^3.0.0",
|
|
41
|
-
"mdast-util-to-hast": "^13.2.0",
|
|
42
|
-
"remark": "^15.0.1",
|
|
43
56
|
"unified": "^11.0.4",
|
|
57
|
+
"unist": "^0.0.1",
|
|
44
58
|
"unist-util-visit": "^5.0.0"
|
|
45
59
|
},
|
|
46
60
|
"devDependencies": {
|
|
@@ -49,6 +63,11 @@
|
|
|
49
63
|
"cssnano": "^7.0.6",
|
|
50
64
|
"postcss": "^8.5.3",
|
|
51
65
|
"postcss-cli": "^11.0.1",
|
|
66
|
+
"rehype-parse": "^9.0.1",
|
|
67
|
+
"rehype-raw": "^7.0.0",
|
|
68
|
+
"rehype-stringify": "^10.0.1",
|
|
69
|
+
"remark-parse": "^11.0.0",
|
|
70
|
+
"remark-rehype": "^11.1.2",
|
|
52
71
|
"ts-node": "^10.9.2",
|
|
53
72
|
"typescript": "^5.3.3"
|
|
54
73
|
}
|
package/dist/test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/test.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { unified } from 'unified';
|
|
2
|
-
import remarkParse from 'remark-parse';
|
|
3
|
-
import remarkStringify from 'remark-stringify';
|
|
4
|
-
import remarkNotes from './index.js';
|
|
5
|
-
const markdown = `
|
|
6
|
-
# Test Document
|
|
7
|
-
|
|
8
|
-
> [!Note]
|
|
9
|
-
> This is a note about something important.
|
|
10
|
-
|
|
11
|
-
> [!tip]
|
|
12
|
-
> Here's a helpful tip for you.
|
|
13
|
-
|
|
14
|
-
> [!important]
|
|
15
|
-
> This is a very important message!
|
|
16
|
-
|
|
17
|
-
> [!quote]
|
|
18
|
-
> Here's a memorable quote.
|
|
19
|
-
|
|
20
|
-
> [!bonus]
|
|
21
|
-
> Here's some extra content for you!
|
|
22
|
-
`;
|
|
23
|
-
async function main() {
|
|
24
|
-
try {
|
|
25
|
-
// Convert to HTML
|
|
26
|
-
const file = await unified()
|
|
27
|
-
.use(remarkParse)
|
|
28
|
-
.use(remarkNotes)
|
|
29
|
-
.use(remarkStringify)
|
|
30
|
-
.process(markdown);
|
|
31
|
-
const output = String(file);
|
|
32
|
-
console.log('Final output:', output);
|
|
33
|
-
// Basic validation
|
|
34
|
-
if (!output.includes('<style>')) {
|
|
35
|
-
throw new Error('Styles were not injected');
|
|
36
|
-
}
|
|
37
|
-
if (!output.includes('remark-note')) {
|
|
38
|
-
throw new Error('Note classes were not properly added');
|
|
39
|
-
}
|
|
40
|
-
// Test for content
|
|
41
|
-
if (!output.includes('This is a note about something important')) {
|
|
42
|
-
throw new Error('Note content is missing');
|
|
43
|
-
}
|
|
44
|
-
// Test for each note type
|
|
45
|
-
['note', 'tip', 'important', 'quote', 'bonus'].forEach(type => {
|
|
46
|
-
if (!output.includes(`remark-note ${type}`)) {
|
|
47
|
-
throw new Error(`${type} note was not properly transformed`);
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
console.log('✅ Test passed: Styles were injected and notes were transformed with content');
|
|
51
|
-
}
|
|
52
|
-
catch (error) {
|
|
53
|
-
console.error('❌ Test failed:', error);
|
|
54
|
-
process.exit(1);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
main().catch(console.error);
|