@jianwen-lang/parser 0.1.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.
Files changed (89) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +95 -0
  3. package/dist/cli/render.d.ts +1 -0
  4. package/dist/cli/render.js +300 -0
  5. package/dist/core/ast.d.ts +286 -0
  6. package/dist/core/ast.js +2 -0
  7. package/dist/core/block/rules/heading.d.ts +9 -0
  8. package/dist/core/block/rules/heading.js +75 -0
  9. package/dist/core/block/rules/horizontal-rule.d.ts +9 -0
  10. package/dist/core/block/rules/horizontal-rule.js +87 -0
  11. package/dist/core/block/rules/include.d.ts +9 -0
  12. package/dist/core/block/rules/include.js +64 -0
  13. package/dist/core/block/rules/index.d.ts +2 -0
  14. package/dist/core/block/rules/index.js +16 -0
  15. package/dist/core/block/rules/types.d.ts +21 -0
  16. package/dist/core/block/rules/types.js +2 -0
  17. package/dist/core/block/types.d.ts +10 -0
  18. package/dist/core/block/types.js +2 -0
  19. package/dist/core/block-parser.d.ts +3 -0
  20. package/dist/core/block-parser.js +1385 -0
  21. package/dist/core/clone.d.ts +9 -0
  22. package/dist/core/clone.js +32 -0
  23. package/dist/core/diagnostics.d.ts +12 -0
  24. package/dist/core/diagnostics.js +24 -0
  25. package/dist/core/errors.d.ts +12 -0
  26. package/dist/core/errors.js +2 -0
  27. package/dist/core/inline/rules/backtick.d.ts +4 -0
  28. package/dist/core/inline/rules/backtick.js +90 -0
  29. package/dist/core/inline/rules/bracket.d.ts +4 -0
  30. package/dist/core/inline/rules/bracket.js +721 -0
  31. package/dist/core/inline/rules/disabled.d.ts +2 -0
  32. package/dist/core/inline/rules/disabled.js +34 -0
  33. package/dist/core/inline/rules/escape.d.ts +2 -0
  34. package/dist/core/inline/rules/escape.js +11 -0
  35. package/dist/core/inline/rules/index.d.ts +2 -0
  36. package/dist/core/inline/rules/index.js +25 -0
  37. package/dist/core/inline/rules/marker-highlight.d.ts +4 -0
  38. package/dist/core/inline/rules/marker-highlight.js +56 -0
  39. package/dist/core/inline/rules/style.d.ts +4 -0
  40. package/dist/core/inline/rules/style.js +120 -0
  41. package/dist/core/inline/rules/types.d.ts +22 -0
  42. package/dist/core/inline/rules/types.js +2 -0
  43. package/dist/core/inline/types.d.ts +1 -0
  44. package/dist/core/inline/types.js +2 -0
  45. package/dist/core/inline-parser.d.ts +3 -0
  46. package/dist/core/inline-parser.js +52 -0
  47. package/dist/core/location.d.ts +9 -0
  48. package/dist/core/location.js +30 -0
  49. package/dist/core/parser.d.ts +9 -0
  50. package/dist/core/parser.js +423 -0
  51. package/dist/core/traverse.d.ts +7 -0
  52. package/dist/core/traverse.js +71 -0
  53. package/dist/html/convert.d.ts +29 -0
  54. package/dist/html/convert.js +61 -0
  55. package/dist/html/format.d.ts +1 -0
  56. package/dist/html/format.js +17 -0
  57. package/dist/html/render/blocks.d.ts +4 -0
  58. package/dist/html/render/blocks.js +433 -0
  59. package/dist/html/render/html.d.ts +8 -0
  60. package/dist/html/render/html.js +89 -0
  61. package/dist/html/render/inlines.d.ts +4 -0
  62. package/dist/html/render/inlines.js +110 -0
  63. package/dist/html/render/meta.d.ts +3 -0
  64. package/dist/html/render/meta.js +51 -0
  65. package/dist/html/render/utils.d.ts +31 -0
  66. package/dist/html/render/utils.js +213 -0
  67. package/dist/html/theme/base/css.d.ts +2 -0
  68. package/dist/html/theme/base/css.js +383 -0
  69. package/dist/html/theme/dark/css.d.ts +2 -0
  70. package/dist/html/theme/dark/css.js +108 -0
  71. package/dist/html/theme/default/colors.d.ts +13 -0
  72. package/dist/html/theme/default/colors.js +2 -0
  73. package/dist/html/theme/default/css.d.ts +2 -0
  74. package/dist/html/theme/default/css.js +6 -0
  75. package/dist/html/theme/default/runtime.d.ts +0 -0
  76. package/dist/html/theme/default/runtime.js +31 -0
  77. package/dist/html/theme/light/css.d.ts +2 -0
  78. package/dist/html/theme/light/css.js +55 -0
  79. package/dist/html/theme/preset/colors.d.ts +10 -0
  80. package/dist/html/theme/preset/colors.js +14 -0
  81. package/dist/html/theme/runtime.d.ts +0 -0
  82. package/dist/html/theme/runtime.js +31 -0
  83. package/dist/html/theme/theme.d.ts +9 -0
  84. package/dist/html/theme/theme.js +21 -0
  85. package/dist/lexer/lexer.d.ts +17 -0
  86. package/dist/lexer/lexer.js +47 -0
  87. package/dist/parser.d.ts +6 -0
  88. package/dist/parser.js +22 -0
  89. package/package.json +50 -0
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderMetaToHtml = renderMetaToHtml;
4
+ exports.buildDocumentWrapperAttributes = buildDocumentWrapperAttributes;
5
+ const utils_1 = require("./utils");
6
+ function renderMetaToHtml(meta) {
7
+ if (!meta) {
8
+ return '';
9
+ }
10
+ const parts = [];
11
+ if (meta.title) {
12
+ parts.push(`<h1 class="jw-meta-title">${(0, utils_1.escapeHtml)(meta.title)}</h1>`);
13
+ }
14
+ const bylineParts = [];
15
+ if (meta.author) {
16
+ const authorText = (0, utils_1.escapeHtml)(meta.author);
17
+ if (meta.authorUrl) {
18
+ const href = (0, utils_1.escapeAttr)(meta.authorUrl);
19
+ bylineParts.push(`<span class="jw-meta-author"><a href="${href}" class="jw-meta-author-link">${authorText}</a></span>`);
20
+ }
21
+ else {
22
+ bylineParts.push(`<span class="jw-meta-author">${authorText}</span>`);
23
+ }
24
+ }
25
+ if (meta.time) {
26
+ bylineParts.push(`<time class="jw-meta-time">${(0, utils_1.escapeHtml)(meta.time)}</time>`);
27
+ }
28
+ if (meta.addInfo) {
29
+ bylineParts.push(`<span class="jw-meta-add-info">${(0, utils_1.escapeHtml)(meta.addInfo)}</span>`);
30
+ }
31
+ if (bylineParts.length > 0) {
32
+ parts.push(`<div class="jw-meta-byline">${bylineParts.join(' · ')}</div>`);
33
+ }
34
+ return `<header class="jw-meta">${parts.join('')}</header>`;
35
+ }
36
+ function buildDocumentWrapperAttributes(meta) {
37
+ if (!meta) {
38
+ return '';
39
+ }
40
+ const attrs = [];
41
+ if (meta.tags && meta.tags.length > 0) {
42
+ attrs.push(`data-jw-tags="${(0, utils_1.escapeAttr)(meta.tags.join(','))}"`);
43
+ }
44
+ if (meta.globalFont && meta.globalFont.length > 0) {
45
+ attrs.push(`data-jw-global-font="${(0, utils_1.escapeAttr)(meta.globalFont.join(' '))}"`);
46
+ }
47
+ if (attrs.length === 0) {
48
+ return '';
49
+ }
50
+ return ` ${attrs.join(' ')}`;
51
+ }
@@ -0,0 +1,31 @@
1
+ import { BlockAttributes, ColorAttribute, InlineAttributes } from '../../core/ast';
2
+ import { DocumentTheme } from '../theme/theme';
3
+ export interface RenderHtmlOptions {
4
+ includeMeta?: boolean;
5
+ includeComments?: boolean;
6
+ resolveHtmlSource?: (source: string) => string | undefined;
7
+ resolveInclude?: (mode: 'file' | 'tag', target: string) => string | undefined;
8
+ resolveAssetPath?: (assetPath: string) => string | undefined;
9
+ sourceFilePath?: string;
10
+ outputFilePath?: string;
11
+ documentWrapperTag?: string | null;
12
+ documentTheme?: DocumentTheme;
13
+ format?: boolean;
14
+ suppressBlockWrapper?: boolean;
15
+ }
16
+ export declare function slugifyToId(text: string): string;
17
+ export declare function buildHeadingIdFromText(text: string): string;
18
+ export declare function buildTagIdFromName(name: string): string;
19
+ export declare function tryBuildHeadingAnchorFromHref(href: string): string | undefined;
20
+ export declare function tryBuildTagAnchorFromHref(href: string): string | undefined;
21
+ export declare function resolveLinkHref(href: string): string;
22
+ export declare function escapeHtml(text: string): string;
23
+ export declare function escapeAttr(text: string): string;
24
+ export declare function colorAttributeToCssColor(attr: ColorAttribute | undefined): string | undefined;
25
+ export declare function buildInlineStyleFromInlineAttributes(attrs: InlineAttributes | undefined): string | undefined;
26
+ export interface BlockAttrBuildOptions {
27
+ rawAttrs?: string[];
28
+ extraData?: Record<string, string>;
29
+ extraStyle?: string[] | string;
30
+ }
31
+ export declare function buildBlockAttributes(attrs: BlockAttributes | undefined, options?: BlockAttrBuildOptions): string;
@@ -0,0 +1,213 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.slugifyToId = slugifyToId;
4
+ exports.buildHeadingIdFromText = buildHeadingIdFromText;
5
+ exports.buildTagIdFromName = buildTagIdFromName;
6
+ exports.tryBuildHeadingAnchorFromHref = tryBuildHeadingAnchorFromHref;
7
+ exports.tryBuildTagAnchorFromHref = tryBuildTagAnchorFromHref;
8
+ exports.resolveLinkHref = resolveLinkHref;
9
+ exports.escapeHtml = escapeHtml;
10
+ exports.escapeAttr = escapeAttr;
11
+ exports.colorAttributeToCssColor = colorAttributeToCssColor;
12
+ exports.buildInlineStyleFromInlineAttributes = buildInlineStyleFromInlineAttributes;
13
+ exports.buildBlockAttributes = buildBlockAttributes;
14
+ const theme_1 = require("../theme/theme");
15
+ function slugifyToId(text) {
16
+ const trimmed = text.trim();
17
+ if (!trimmed) {
18
+ return 'section';
19
+ }
20
+ const lowered = trimmed.toLowerCase();
21
+ const replacedSpaces = lowered.replace(/\s+/g, '-');
22
+ const cleaned = replacedSpaces.replace(/[^a-z0-9_\-\u4e00-\u9fa5]+/g, '-');
23
+ const collapsed = cleaned.replace(/-+/g, '-');
24
+ const trimmedDashes = collapsed.replace(/^-|-$/g, '');
25
+ return trimmedDashes || 'section';
26
+ }
27
+ function buildHeadingIdFromText(text) {
28
+ return `jw-heading-${slugifyToId(text)}`;
29
+ }
30
+ function buildTagIdFromName(name) {
31
+ return `jw-tag-${slugifyToId(name)}`;
32
+ }
33
+ function tryBuildHeadingAnchorFromHref(href) {
34
+ const trimmed = href.trim();
35
+ const match = trimmed.match(/^#{1,6}\s+(.+)/);
36
+ if (!match || !match[1]) {
37
+ return undefined;
38
+ }
39
+ const titleText = match[1];
40
+ if (!titleText.trim()) {
41
+ return undefined;
42
+ }
43
+ return `#${buildHeadingIdFromText(titleText)}`;
44
+ }
45
+ function tryBuildTagAnchorFromHref(href) {
46
+ const trimmed = href.trim();
47
+ if (!trimmed) {
48
+ return undefined;
49
+ }
50
+ if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed)) {
51
+ return undefined;
52
+ }
53
+ if (trimmed.includes('/') || trimmed.includes('\\')) {
54
+ return undefined;
55
+ }
56
+ if (/\.[^\s.]+$/.test(trimmed)) {
57
+ return undefined;
58
+ }
59
+ return `#${buildTagIdFromName(trimmed)}`;
60
+ }
61
+ function resolveLinkHref(href) {
62
+ const trimmed = href.trim();
63
+ if (!trimmed) {
64
+ return '#';
65
+ }
66
+ const headingAnchor = tryBuildHeadingAnchorFromHref(trimmed);
67
+ if (headingAnchor) {
68
+ return headingAnchor;
69
+ }
70
+ if (trimmed.startsWith('#')) {
71
+ return trimmed;
72
+ }
73
+ if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed)) {
74
+ return trimmed;
75
+ }
76
+ const tagAnchor = tryBuildTagAnchorFromHref(trimmed);
77
+ if (tagAnchor) {
78
+ return tagAnchor;
79
+ }
80
+ return trimmed;
81
+ }
82
+ function escapeHtml(text) {
83
+ return text
84
+ .replace(/&/g, '&amp;')
85
+ .replace(/</g, '&lt;')
86
+ .replace(/>/g, '&gt;')
87
+ .replace(/"/g, '&quot;')
88
+ .replace(/'/g, '&#39;');
89
+ }
90
+ function escapeAttr(text) {
91
+ return escapeHtml(text).replace(/`/g, '&#96;');
92
+ }
93
+ function colorAttributeToCssColor(attr) {
94
+ if (!attr) {
95
+ return undefined;
96
+ }
97
+ if (attr.kind === 'hex') {
98
+ return attr.value;
99
+ }
100
+ const cleanValue = attr.value.replace(/[^a-zA-Z0-9_-]/g, '-');
101
+ const preset = theme_1.PRESET_COLORS[attr.value];
102
+ if (preset) {
103
+ return `var(--jw-color-${cleanValue}, ${preset})`;
104
+ }
105
+ return `var(--jw-color-${cleanValue})`;
106
+ }
107
+ function mapFontSizeToEm(fontSize) {
108
+ if (fontSize <= 1)
109
+ return 1;
110
+ if (fontSize >= 5)
111
+ return 2.5;
112
+ return 1 + (fontSize - 1) * 0.375;
113
+ }
114
+ function buildInlineStyleFromInlineAttributes(attrs) {
115
+ if (!attrs) {
116
+ return undefined;
117
+ }
118
+ const styleParts = [];
119
+ if (attrs.color) {
120
+ const cssColor = colorAttributeToCssColor(attrs.color);
121
+ if (cssColor) {
122
+ styleParts.push(`color:${cssColor}`);
123
+ }
124
+ }
125
+ if (typeof attrs.fontSize === 'number') {
126
+ const emValue = mapFontSizeToEm(attrs.fontSize);
127
+ styleParts.push(`font-size:${emValue}em`);
128
+ }
129
+ if (attrs.fontStyle && attrs.fontStyle.length > 0) {
130
+ let fontWeight;
131
+ let fontStyle;
132
+ let fontFamily;
133
+ for (const style of attrs.fontStyle) {
134
+ if (style === 'italic') {
135
+ fontStyle = 'italic';
136
+ }
137
+ else if (style === 'bold') {
138
+ fontWeight = 'bold';
139
+ }
140
+ else if (style === 'heavy') {
141
+ fontWeight = '900';
142
+ }
143
+ else if (style === 'slim') {
144
+ fontWeight = '300';
145
+ }
146
+ else if (style === 'serif') {
147
+ fontFamily = 'serif';
148
+ }
149
+ else if (style === 'mono') {
150
+ fontFamily = 'monospace';
151
+ }
152
+ }
153
+ if (fontWeight) {
154
+ styleParts.push(`font-weight:${fontWeight}`);
155
+ }
156
+ if (fontStyle) {
157
+ styleParts.push(`font-style:${fontStyle}`);
158
+ }
159
+ if (fontFamily) {
160
+ styleParts.push(`font-family:${fontFamily}`);
161
+ }
162
+ }
163
+ if (styleParts.length === 0) {
164
+ return undefined;
165
+ }
166
+ return styleParts.join(';');
167
+ }
168
+ function buildBlockAttributes(attrs, options = {}) {
169
+ const attrParts = [];
170
+ if (options.rawAttrs) {
171
+ attrParts.push(...options.rawAttrs);
172
+ }
173
+ if (options.extraData) {
174
+ for (const [key, value] of Object.entries(options.extraData)) {
175
+ attrParts.push(`${key}="${escapeAttr(value)}"`);
176
+ }
177
+ }
178
+ const styleParts = [];
179
+ if (options.extraStyle) {
180
+ if (Array.isArray(options.extraStyle)) {
181
+ styleParts.push(...options.extraStyle);
182
+ }
183
+ else {
184
+ styleParts.push(options.extraStyle);
185
+ }
186
+ }
187
+ if (attrs) {
188
+ if (attrs.align) {
189
+ attrParts.push(`data-jw-align="${escapeAttr(attrs.align)}"`);
190
+ styleParts.push(`text-align:${attrs.align}`);
191
+ }
192
+ if (attrs.position) {
193
+ attrParts.push(`data-jw-position="${escapeAttr(attrs.position)}"`);
194
+ }
195
+ if (attrs.truncateLeft) {
196
+ attrParts.push('data-jw-truncate-left="true"');
197
+ }
198
+ if (attrs.truncateRight) {
199
+ attrParts.push('data-jw-truncate-right="true"');
200
+ }
201
+ const inlineStyle = buildInlineStyleFromInlineAttributes(attrs);
202
+ if (inlineStyle) {
203
+ styleParts.push(inlineStyle);
204
+ }
205
+ }
206
+ if (styleParts.length > 0) {
207
+ attrParts.push(`style="${escapeAttr(styleParts.join(';'))}"`);
208
+ }
209
+ if (attrParts.length === 0) {
210
+ return '';
211
+ }
212
+ return ` ${attrParts.join(' ')}`;
213
+ }
@@ -0,0 +1,2 @@
1
+ export = BASE_CSS;
2
+ declare const BASE_CSS: "\nhtml,\nbody {\n margin: 0;\n padding: 0;\n}\n.jw-root {\n background-color: var(--jw-bg-color, transparent);\n color: var(--jw-text-color, inherit);\n display: flow-root;\n min-height: 100vh;\n}\n.jw-root > .jw-block + .jw-block { margin-top: var(--jw-block-spacing); }\n.jw-root > .jw-block { margin: 0; }\n.jw-table { border-collapse: collapse; }\n.jw-table-cell { border: 1px solid var(--jw-border-color); padding: 0.5em; }\n.jw-link { text-decoration: none; color: var(--jw-link-color, currentColor); }\n.jw-link:visited { color: var(--jw-link-color, currentColor); }\n.jw-link:hover { text-decoration: underline; }\n.jw-underline { text-decoration: underline; }\n.jw-strike { text-decoration: line-through; }\n.jw-wave { text-decoration: underline; text-decoration-style: wavy; }\n.jw-heading { font-weight: bold; margin: 0; }\n.jw-heading.level-1 { font-size: 2.5em; }\n.jw-heading.level-2 { font-size: 2em; }\n.jw-heading.level-3 { font-size: 1.75em; }\n.jw-heading.level-4 { font-size: 1.5em; }\n.jw-heading.level-5 { font-size: 1.25em; }\n.jw-foldable-section { margin: 1em 0; }\n.jw-foldable-section > summary {\n cursor: pointer;\n user-select: none;\n list-style: none;\n display: flex;\n align-items: center;\n}\n.jw-foldable-section > summary::-webkit-details-marker {\n display: none;\n}\n.jw-foldable-section > summary::before {\n content: '';\n display: block;\n width: 0.5em;\n height: 0.5em;\n margin-right: 0.4em;\n flex: 0 0 auto;\n background: currentColor;\n clip-path: polygon(0 0, 100% 50%, 0 100%);\n transform: rotate(0deg);\n opacity: 0.74;\n transition: transform 0.2s ease, opacity 0.2s ease;\n}\n.jw-foldable-section[open] > summary::before {\n transform: rotate(90deg);\n opacity: 0.85;\n}\n.jw-foldable-section > summary .jw-heading {\n display: inline;\n margin: 0;\n min-width: 0;\n}\n\n.jw-content-title {\n margin-top: 0.1em;\n margin-bottom: 0;\n font-size: 0.85em;\n color: var(--jw-text-muted);\n}\n.jw-content-title-gap {\n display: block;\n height: 0.8em;\n min-height: 8px;\n}\n.jw-quote {\n margin: 0;\n border-left: 4px solid var(--jw-quote-border-color);\n padding: 0 1em;\n background: var(--jw-quote-background);\n color: var(--jw-quote-text);\n}\n.jw-quote > .jw-paragraph { margin: var(--jw-quote-gap) 0; }\n.jw-quote > .jw-quote { margin: var(--jw-quote-gap) 0; }\n[data-jw-level=\"2\"].jw-quote { border-left-color: var(--jw-quote-border-color-2); }\n[data-jw-level=\"3\"].jw-quote { border-left-color: var(--jw-quote-border-color-3); }\n.jw-paragraph { white-space: pre-wrap; line-height: var(--jw-line-height-base); }\n.jw-paragraph-block { white-space: normal; }\n.jw-list { padding-left: 1.25em; }\n.jw-list[data-jw-list-kind=\"bullet\"] { list-style-type: disc; }\n.jw-hr { border: none; border-top: 1px solid currentColor; color: var(--jw-border-color); height: 0; background: none; padding: 0; margin: 0; }\n.jw-hr[data-jw-position=\"L\"] { width: 100%; }\n.jw-hr[data-jw-position=\"C\"] { width: 66.67%; }\n.jw-hr[data-jw-position=\"R\"] { width: 33.34%; }\n.jw-hr[data-jw-hr-style=\"dashed\"] { border-top-style: dashed; }\n.jw-hr[data-jw-hr-style=\"bold\"] { border-top-width: 4px; }\n.jw-hr[data-jw-hr-style=\"wavy\"] {\n border: none;\n height: 8px;\n color: var(--jw-border-color);\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 8' preserveAspectRatio='none'%3E%3Cpath d='M0 4 Q 8 0 16 4 T 32 4' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E\");\n background-size: 32px 8px;\n background-repeat: repeat-x;\n background-position: center bottom;\n background-color: transparent;\n}\n.jw-highlight-marker { background-color: var(--jw-highlight-marker); padding: 0; }\n.jw-highlight-frame { border: 1px solid var(--jw-border-color); padding: 0; border-radius: 3px; }\n.jw-highlight-block {\n display: block;\n padding: 0.75em 1em;\n white-space: normal;\n}\n.jw-list[data-jw-list-kind=\"task\"] { list-style: none; padding-left: 0; }\n.jw-list-item[data-jw-list-kind=\"task\"] { list-style: none; }\n.jw-list-item[data-jw-list-kind=\"task\"][data-jw-task-status] {\n position: relative;\n padding-left: 0;\n margin: 0.25em 0;\n}\n.jw-list-item {\n display: flex;\n flex-flow: row wrap;\n align-items: flex-start;\n}\n.jw-list-item[data-jw-list-kind=\"bullet\"] {\n display: list-item;\n}\n.jw-list-ordinal {\n margin-right: 0.5em;\n line-height: var(--jw-line-height-base);\n flex-shrink: 0;\n color: var(--jw-text-strong);\n font-variant-numeric: tabular-nums;\n}\n.jw-list-task-marker {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.25em;\n height: 1.25em;\n border: 1px solid var(--jw-border-color-strong);\n background: var(--jw-surface-task);\n border-radius: 2px;\n font-size: 0.85em;\n color: transparent;\n transition: all 0.2s ease;\n box-sizing: border-box;\n margin-right: 0.5em;\n flex-shrink: 0;\n margin-top: 0.375em;\n position: relative;\n line-height: 1;\n}\n.jw-list-task-marker::before,\n.jw-list-task-marker::after {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n content: '';\n}\n.jw-list-task-marker[data-jw-task-status=\"done\"] {\n content: '\u2714';\n}\n.jw-list-task-marker[data-jw-task-status=\"done\"]::before {\n content: '\u2714';\n color: var(--jw-task-done-text);\n}\n.jw-list-task-marker[data-jw-task-status=\"done\"] {\n background: var(--jw-task-done-bg);\n border-color: var(--jw-task-done-bg);\n}\n.jw-list-task-marker[data-jw-task-status=\"not_done\"]::before,\n.jw-list-task-marker[data-jw-task-status=\"not_done\"]::after {\n width: 0.85em;\n height: 0.12em;\n background: var(--jw-task-not-done-cross);\n border-radius: 999px;\n}\n.jw-list-task-marker[data-jw-task-status=\"not_done\"]::before {\n transform: translate(-50%, -50%) rotate(45deg);\n}\n.jw-list-task-marker[data-jw-task-status=\"not_done\"]::after {\n transform: translate(-50%, -50%) rotate(-45deg);\n}\n.jw-list-task-marker[data-jw-task-status=\"not_done\"] {\n background: var(--jw-border-color-strong);\n border-color: var(--jw-border-color-strong);\n}\n.jw-list-task-marker[data-jw-task-status=\"in_progress\"]::before {\n width: 0.7em;\n height: 0.7em;\n border: 0.12em solid var(--jw-task-in-progress);\n border-radius: 50%;\n}\n.jw-list-task-marker[data-jw-task-status=\"unknown\"] {\n /* Empty box */\n}\n.jw-list-item > .jw-paragraph {\n flex: 1 1 auto;\n margin: 0;\n min-width: 0;\n line-height: var(--jw-line-height-base);\n}\n.jw-list-item > .jw-list {\n flex-basis: 100%;\n width: 100%;\n padding-left: 2em;\n margin: var(--jw-nested-list-gap) 0 0 0;\n}\n.jw-list-item + .jw-list-item {\n margin-top: var(--jw-nested-list-gap);\n}\n.jw-list-item > .jw-code-block {\n flex-basis: 100%;\n width: 100%;\n margin-top: var(--jw-nested-list-gap);\n}\n.jw-list-item[data-jw-task-status=\"done\"] > .jw-paragraph {\n text-decoration: line-through;\n color: var(--jw-text-faint);\n}\n.jw-list-item[data-jw-list-kind=\"ordered\"] {\n list-style: none;\n}\n.jw-list-item[data-jw-list-kind=\"ordered\"]::marker {\n content: none;\n}\n\n.jw-list-item[data-jw-list-kind=\"foldable\"] { list-style: none; }\n.jw-foldable-list-item {\n display: block;\n}\n.jw-foldable-list-item > summary {\n cursor: pointer;\n user-select: none;\n list-style: none;\n position: relative;\n padding-left: 1.2em;\n}\n.jw-foldable-list-item > summary::-webkit-details-marker {\n display: none;\n}\n.jw-foldable-list-item > summary::before {\n content: '';\n position: absolute;\n left: 0;\n top: 50%;\n width: 0.45em;\n height: 0.45em;\n background: currentColor;\n clip-path: polygon(0 0, 100% 50%, 0 100%);\n transform: translateY(-50%) rotate(0deg);\n opacity: 0.68;\n transition: transform 0.2s ease, opacity 0.2s ease;\n}\n.jw-foldable-list-item[open] > summary::before {\n transform: translateY(-50%) rotate(90deg);\n opacity: 0.85;\n}\n.jw-foldable-list-item > summary .jw-paragraph {\n display: inline;\n}\n\n.jw-image-figure { }\n.jw-image { max-width: 100%; max-height: 600px; width: auto; height: auto; object-fit: contain; }\n.jw-image-caption { margin-top: 0.5em; font-size: 0.9em; color: var(--jw-text-subtle); }\n.jw-image-figure[data-jw-shape=\"rounded\"] .jw-image { border-radius: 8px; }\n.jw-code-block {\n position: relative;\n display: flex;\n flex-direction: column;\n padding: 0;\n background: var(--jw-surface-code);\n border: 1px solid var(--jw-border-color-subtle);\n border-radius: 4px;\n font-family: \"JetBrains Mono\", \"Fira Code\", Consolas, Menlo, monospace;\n font-size: 0.9em;\n line-height: 1.5;\n margin: 0;\n}\n.jw-code-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.5em 0.75em;\n border-bottom: 1px solid var(--jw-border-color-subtle);\n background: var(--jw-surface-overlay-1);\n}\n.jw-code-lang {\n padding: 0.1em 0.4em;\n background: var(--jw-surface-overlay-2);\n color: var(--jw-text-subtle);\n font-size: 0.75em;\n border-radius: 3px;\n font-weight: 500;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n.jw-code-copy {\n padding: 0.25em 0.5em;\n background: transparent;\n border: 1px solid var(--jw-border-color);\n border-radius: 3px;\n cursor: pointer;\n font-size: 0.75em;\n color: var(--jw-text-subtle);\n transition: background 0.2s, border-color 0.2s;\n}\n.jw-code-copy:hover {\n background: var(--jw-surface-overlay-2);\n border-color: var(--jw-border-color-strong);\n}\n.jw-code-copy:active {\n background: var(--jw-surface-overlay-3);\n}\n.jw-code-content {\n display: flex;\n padding: 1em 1em 1em 0;\n}\n.jw-line-numbers {\n flex-shrink: 0;\n display: flex;\n flex-direction: column;\n padding: 0 0.75em;\n border-right: 1px solid var(--jw-border-color-subtle);\n text-align: right;\n user-select: none;\n color: var(--jw-text-faint);\n font-variant-numeric: tabular-nums;\n}\n.jw-line-number {\n display: block;\n min-height: 1.5em;\n}\n.jw-code {\n flex: 1;\n display: block;\n overflow-x: auto;\n padding: 0 1em;\n background: transparent;\n font-family: inherit;\n white-space: pre;\n}\n.jw-code-line {\n display: block;\n white-space: pre;\n min-height: 1.5em;\n}\n.jw-disabled-block { font-family: inherit; white-space: pre-wrap; line-height: var(--jw-line-height-base); margin: 0; }\n[data-jw-position=\"C\"] { margin-left: 33.33%; }\n[data-jw-position=\"R\"] { margin-left: 66.66%; }\n[data-jw-truncate-left=\"true\"] { margin-left: 0; }\n[data-jw-truncate-right=\"true\"] { max-width: 33.33%; word-wrap: break-word; overflow-wrap: break-word; }\n[data-jw-position=\"C\"][data-jw-truncate-right=\"true\"] { max-width: 33.33%; }\n[data-jw-position=\"R\"][data-jw-truncate-right=\"true\"] { max-width: 33.33%; }\n.jw-hr[data-jw-truncate-right=\"true\"] { width: 33.33%; max-width: none; }\n[data-jw-align=\"center\"] { text-align: center; }\n[data-jw-align=\"right\"] { text-align: right; }\n.jw-same-line-row { display: flex; flex-wrap: nowrap; gap: 0; align-items: flex-start; max-width: 100%; overflow: hidden; }\n.jw-same-line-row > * { flex-shrink: 1; word-wrap: break-word; overflow-wrap: break-word; min-width: 0; }\n.jw-same-line-row > [data-jw-position=\"L\"]:not(:last-child),\n.jw-same-line-row > [data-jw-position=\"C\"]:not(:last-child),\n.jw-same-line-row > [data-jw-position=\"R\"]:not(:last-child) { flex-basis: 33.33%; max-width: 33.33%; }\n.jw-same-line-row > [data-jw-position=\"L\"]:last-child,\n.jw-same-line-row > [data-jw-position=\"C\"]:last-child,\n.jw-same-line-row > [data-jw-position=\"R\"]:last-child { flex-grow: 1; flex-basis: 0; }\n.jw-same-line-row > [data-jw-position] { margin-left: 0; }\n.jw-same-line-row > [data-jw-position=\"C\"]:first-child { margin-left: 33.33%; }\n.jw-same-line-row > [data-jw-position=\"R\"]:first-child { margin-left: 66.66%; }\n.jw-meta { margin: 0 0 var(--jw-block-spacing); }\n.jw-meta-time,\n.jw-meta-add-info,\n.jw-meta-tags,\n.jw-meta-author {\n color: var(--jw-quote-text);\n}\n.jw-meta-author-link { text-decoration: none; color: inherit; }\n.jw-meta-author-link:hover { text-decoration: underline; }\n.jw-comment-inline { display: none; }\n.jw-comment-block { display: none; }\n";