flet-box 1.1.2 → 1.1.3
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/AGENTS_FOR_CODE_GEN.md +284 -0
- package/CONTRIBUTING.md +238 -0
- package/README.md +2 -2
- package/bin/cli.js +88 -1
- package/bin/commands/auditFix.js +181 -27
- package/bin/commands/checkApi.js +14 -3
- package/bin/commands/checkStructure.js +368 -0
- package/bin/commands/checkSyntax.js +293 -0
- package/bin/commands/checkUi.js +666 -14
- package/bin/commands/create.js +14 -0
- package/bin/commands/createBundle.js +2 -2
- package/bin/commands/metrics.js +448 -0
- package/bin/commands/release.js +18 -0
- package/bin/commands/report.js +849 -0
- package/bin/commands/tools.js +76 -0
- package/bin/commands/unpack.js +39 -11
- package/bin/commands/verify.js +879 -0
- package/bin/mcp.js +280 -0
- package/bin/utils/contract.js +27 -0
- package/bin/utils/dtsProps.js +5 -1
- package/bin/utils/migrationGuide.js +146 -0
- package/bin/utils/moduleExports.js +9 -5
- package/bin/utils/templates.js +225 -18
- package/bin/utils/toolkit.js +191 -0
- package/docs/README.md +5 -1
- package/docs/arquitectura.md +1 -1
- package/docs/cli/README.md +168 -2
- package/docs/guides/README.md +6 -0
- package/docs/guides/accessibility.md +2 -1
- package/docs/guides/ai-toolkit.md +166 -0
- package/docs/guides/compatibility.md +1 -1
- package/docs/guides/migration.md +92 -0
- package/docs/guides/minification-and-protection.md +2 -1
- package/docs/guides/mobile-and-platforms.md +1 -1
- package/docs/guides/online-updates.md +2 -0
- package/docs/server/deployment.md +1 -1
- package/docs/tools/styling.md +2 -2
- package/docs/tools/theme.md +139 -4
- package/docs/widget/Accordion.md +74 -8
- package/docs/widget/AdMob.md +68 -3
- package/docs/widget/AdSense.md +67 -2
- package/docs/widget/AlertDialog.md +1 -1
- package/docs/widget/AnimatedBox.md +80 -39
- package/docs/widget/AnimatedText.md +74 -18
- package/docs/widget/Audio.md +73 -14
- package/docs/widget/Avatar.md +69 -4
- package/docs/widget/Badge.md +68 -3
- package/docs/widget/BottomSheet.md +2 -2
- package/docs/widget/Button.md +69 -3
- package/docs/widget/Card.md +69 -4
- package/docs/widget/Carousel.md +67 -2
- package/docs/widget/Chart.md +67 -2
- package/docs/widget/Checkbox.md +74 -8
- package/docs/widget/Chip.md +68 -3
- package/docs/widget/CircularBar.md +74 -15
- package/docs/widget/CircularChart.md +71 -10
- package/docs/widget/CodeViewer.md +67 -2
- package/docs/widget/Column.md +68 -3
- package/docs/widget/Container.md +68 -3
- package/docs/widget/DataTable.md +67 -2
- package/docs/widget/Divider.md +67 -2
- package/docs/widget/DraggBox.md +67 -2
- package/docs/widget/Dropdown.md +76 -10
- package/docs/widget/DroppBox.md +67 -2
- package/docs/widget/FloatingActionButton.md +68 -3
- package/docs/widget/GridView.md +67 -2
- package/docs/widget/Icon.md +71 -5
- package/docs/widget/Image.md +68 -3
- package/docs/widget/Input.md +68 -3
- package/docs/widget/Inspector.md +2 -2
- package/docs/widget/InstallButton.md +73 -13
- package/docs/widget/ListTile.md +69 -3
- package/docs/widget/ListView.md +67 -2
- package/docs/widget/Maps.md +68 -3
- package/docs/widget/Markdown.md +74 -15
- package/docs/widget/MatrixRain.md +72 -11
- package/docs/widget/Menu.md +209 -0
- package/docs/widget/Modal.md +3 -3
- package/docs/widget/Pagination.md +68 -3
- package/docs/widget/ParallaxBox.md +72 -12
- package/docs/widget/ProgressBar.md +69 -4
- package/docs/widget/QRCode.md +67 -2
- package/docs/widget/README.md +54 -50
- package/docs/widget/Radio.md +76 -10
- package/docs/widget/Rating.md +70 -6
- package/docs/widget/Row.md +68 -3
- package/docs/widget/Skeleton.md +68 -3
- package/docs/widget/Slider.md +75 -9
- package/docs/widget/SnackBar.md +2 -2
- package/docs/widget/Stack.md +68 -3
- package/docs/widget/Stepper.md +68 -3
- package/docs/widget/Switch.md +74 -8
- package/docs/widget/Text.md +71 -4
- package/docs/widget/ThemePicker.md +197 -0
- package/docs/widget/Tooltip.md +72 -7
- package/docs/widget/TreeView.md +70 -10
- package/docs/widget/Video.md +71 -10
- package/package.json +10 -3
- package/skills/fletbox-design/SKILL.md +89 -5
- package/skills/fletbox-motion/SKILL.md +1 -1
- package/skills/fletbox-rules/SKILL.md +56 -4
- package/src/core/runApp.js +12 -0
- package/src/index.d.ts +374 -101
- package/src/index.js +9 -0
- package/src/navigations/AdaptiveScaffold.js +2 -2
- package/src/navigations/BottomNavigation.js +1 -1
- package/src/navigations/Scaffold.js +18 -6
- package/src/tools/useState.js +11 -4
- package/src/utils/colors.js +275 -0
- package/src/utils/errorBoundary.js +288 -0
- package/src/utils/index.js +10 -2
- package/src/utils/stackPosition.js +39 -24
- package/src/utils/syntaxHighlight.js +4 -1
- package/src/utils/themes.js +279 -3
- package/src/widget-factory/addChildren.js +17 -10
- package/src/widget-factory/assignProps.js +18 -6
- package/src/widget-factory/createWidget.js +7 -0
- package/src/widget-factory/deprecations.js +77 -0
- package/src/widget-factory/translateProps.js +11 -1
- package/src/widget-factory/warnings.js +1 -27
- package/src/widget-factory/widgetFactory.js +59 -17
- package/src/widgets/Accordion.js +21 -7
- package/src/widgets/Badge.js +11 -2
- package/src/widgets/Button.js +15 -3
- package/src/widgets/Carousel.js +12 -4
- package/src/widgets/Chart.js +7 -2
- package/src/widgets/Checkbox.js +1 -2
- package/src/widgets/Chip.js +24 -10
- package/src/widgets/CircularBar.js +9 -3
- package/src/widgets/CircularChart.js +5 -3
- package/src/widgets/Container.js +6 -0
- package/src/widgets/Divider.js +3 -1
- package/src/widgets/DraggBox.js +9 -0
- package/src/widgets/Dropdown.js +3 -3
- package/src/widgets/DroppBox.js +5 -0
- package/src/widgets/FloatingActionButton.js +1 -1
- package/src/widgets/Icon.js +5 -1
- package/src/widgets/InstallButton.js +4 -2
- package/src/widgets/Markdown.js +171 -103
- package/src/widgets/Menu.js +340 -0
- package/src/widgets/Modal.js +7 -1
- package/src/widgets/Pagination.js +10 -5
- package/src/widgets/ProgressBar.js +5 -4
- package/src/widgets/QRCode.js +4 -0
- package/src/widgets/Radio.js +1 -2
- package/src/widgets/Rating.js +9 -0
- package/src/widgets/Slider.js +4 -1
- package/src/widgets/SnackBar.js +2 -2
- package/src/widgets/Switch.js +1 -2
- package/src/widgets/Text.js +9 -5
- package/src/widgets/ThemePicker.js +234 -0
- package/src/widgets/TreeView.js +2 -1
- package/src/widgets/index.js +2 -0
- package/docs/CONTRIBUTING.md +0 -156
package/src/widgets/Markdown.js
CHANGED
|
@@ -6,7 +6,11 @@
|
|
|
6
6
|
import { WidgetFactory } from "../widget-factory/index.js";
|
|
7
7
|
import { colors } from "../utils/themes.js";
|
|
8
8
|
import { composeUpdate } from "../utils/composeUpdate.js";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
generateHighlightedHtml,
|
|
11
|
+
highlightColors,
|
|
12
|
+
syntaxPaletteFor,
|
|
13
|
+
} from "../utils/syntaxHighlight.js";
|
|
10
14
|
import { escapeHtml } from "../helpers/html.js";
|
|
11
15
|
import { toREM } from "../utils/units.js";
|
|
12
16
|
|
|
@@ -79,12 +83,23 @@ const sanitizeMarkdown = (html) => {
|
|
|
79
83
|
// has a single import boundary and the parsers are never pulled into unrelated
|
|
80
84
|
// dependency graphs.
|
|
81
85
|
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
/**
|
|
87
|
+
* The two ink maps the per-language highlighters paint with, one per code-plate
|
|
88
|
+
* lightness. `muted`, `ink` and `hairline` are the fence header's chrome (the
|
|
89
|
+
* language label, the copy button and its border), which sits on a band that
|
|
90
|
+
* shades the plate by 18 % black — a stricter background than the plate itself.
|
|
91
|
+
*
|
|
92
|
+
* Every value is the ratio it ships, measured on the plate it lands on: the
|
|
93
|
+
* framework's own `colors.gray100` (#fafafa light / #27272a dark) for the tokens
|
|
94
|
+
* and that plate under the header band for the chrome. The dark comment used to
|
|
95
|
+
* be One Dark's `#5c6370` — 2.46:1 on a dark card, and a comment is still a
|
|
96
|
+
* sentence someone has to read.
|
|
97
|
+
*/
|
|
98
|
+
const SH_DARK = {
|
|
99
|
+
keyword: "#c678dd", // 5.06:1 on #1b2740, 5.33:1 on #27272a
|
|
85
100
|
string: "#98c379",
|
|
86
101
|
number: "#d19a66",
|
|
87
|
-
comment: "#
|
|
102
|
+
comment: "#8b949e", // 4.85:1 on #1b2740 — the grey GitHub paints comments with
|
|
88
103
|
tag: "#e06c75",
|
|
89
104
|
attr: "#d19a66",
|
|
90
105
|
selector: "#e06c75",
|
|
@@ -95,6 +110,46 @@ const SH = {
|
|
|
95
110
|
punctuation: "#abb2bf",
|
|
96
111
|
operator: "#56b6c2",
|
|
97
112
|
variable: "#e5c07b",
|
|
113
|
+
muted: "#c9d1d9", // 10.2:1 on the header band
|
|
114
|
+
ink: "#ffffff",
|
|
115
|
+
hairline: "rgba(255, 255, 255, 0.15)",
|
|
116
|
+
hairlineHover: "rgba(255, 255, 255, 0.4)",
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/** The same roles for a light plate — every one is 4.7:1 or better on #eef2f7. */
|
|
120
|
+
const SH_LIGHT = {
|
|
121
|
+
keyword: "#cf222e", // 4.75:1
|
|
122
|
+
string: "#0a3069", // 11.32:1
|
|
123
|
+
number: "#0550ae", // 6.73:1
|
|
124
|
+
comment: "#57606a", // 5.65:1
|
|
125
|
+
tag: "#116329", // 6.53:1
|
|
126
|
+
attr: "#953800", // 6.54:1
|
|
127
|
+
selector: "#116329",
|
|
128
|
+
property: "#0550ae",
|
|
129
|
+
value: "#0a3069",
|
|
130
|
+
function: "#6f42c1", // 5.76:1
|
|
131
|
+
builtin: "#0e7490", // 4.74:1
|
|
132
|
+
punctuation: "#57606a",
|
|
133
|
+
operator: "#cf222e",
|
|
134
|
+
variable: "#953800",
|
|
135
|
+
muted: "#24292f", // 8.6:1 on the header band
|
|
136
|
+
ink: "#0d1117",
|
|
137
|
+
hairline: "rgba(36, 41, 47, 0.25)",
|
|
138
|
+
hairlineHover: "rgba(36, 41, 47, 0.55)",
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* The two colour maps one code plate gets: the JS tokenizer's own, and the role
|
|
143
|
+
* map for the other languages. `highlightColors` is `syntaxHighlight.js`'s dark
|
|
144
|
+
* map, so identity with it is the same lightness question the JS branch already
|
|
145
|
+
* answered — one plate, one answer, and the two highlighters cannot disagree
|
|
146
|
+
* about what "a dark code block" means.
|
|
147
|
+
*
|
|
148
|
+
* @param {string} background - The fence's own `preBgColor`.
|
|
149
|
+
*/
|
|
150
|
+
const syntaxFor = (background) => {
|
|
151
|
+
const js = syntaxPaletteFor(background);
|
|
152
|
+
return { js, sh: js === highlightColors ? SH_DARK : SH_LIGHT };
|
|
98
153
|
};
|
|
99
154
|
|
|
100
155
|
/**
|
|
@@ -136,99 +191,105 @@ const applyRules = (line, rules) => {
|
|
|
136
191
|
};
|
|
137
192
|
|
|
138
193
|
// ── Per-language rule tables ─────────────────────────────────────────────────
|
|
194
|
+
//
|
|
195
|
+
// Patterns are keyed by *role*, never by colour: a regex rebuilt per line throws
|
|
196
|
+
// away the engine's own compilation cache, and a colour baked into the table is
|
|
197
|
+
// exactly how this widget came to read a light page with dark-theme ink.
|
|
198
|
+
const PATTERNS = {
|
|
199
|
+
python: [
|
|
200
|
+
[/(#.*)$/, "comment"],
|
|
201
|
+
[/("""[\s\S]*?"""|'''[\s\S]*?'''|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/, "string"],
|
|
202
|
+
[/\b(def|class|if|elif|else|for|while|try|except|finally|with|as|import|from|return|yield|pass|break|continue|raise|lambda|not|and|or|in|is|None|True|False|async|await|global|nonlocal|del)\b/, "keyword"],
|
|
203
|
+
[/\b(print|len|range|type|str|int|float|bool|list|dict|tuple|set|enumerate|zip|map|filter|sorted|reversed|open|super|self|cls)\b/, "builtin"],
|
|
204
|
+
[/\b([0-9]+(?:\.[0-9]+)?)\b/, "number"],
|
|
205
|
+
[/([+\-*/=<>!&|^~%]+)/, "operator"],
|
|
206
|
+
],
|
|
207
|
+
css: [
|
|
208
|
+
[/(\/\*[\s\S]*?\*\/)/, "comment"],
|
|
209
|
+
[/("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/, "string"],
|
|
210
|
+
[/(#[0-9a-fA-F]{3,8})\b/, "value"],
|
|
211
|
+
[/\b([0-9]+(?:\.[0-9]+)?(?:px|em|rem|%|vh|vw|vmin|vmax|pt|cm|mm|deg|s|ms)?)\b/, "number"],
|
|
212
|
+
[/(:[a-zA-Z-]+\(|:[a-zA-Z-]+\b|::[a-zA-Z-]+)/, "selector"],
|
|
213
|
+
[/([.#]?[a-zA-Z][a-zA-Z0-9_-]*)(?=\s*\{)/, "selector"],
|
|
214
|
+
[/\b(animation|background|border|color|display|flex|font|gap|grid|height|justify|margin|max|min|opacity|overflow|padding|position|text|transform|transition|width|z-index|align|content|cursor|float|outline|pointer|shadow|top|bottom|left|right)\b/, "property"],
|
|
215
|
+
[/\b(inherit|initial|none|auto|normal|bold|italic|relative|absolute|fixed|sticky|block|inline|flex|grid|center|left|right|top|bottom|middle|start|end|solid|dashed|dotted|transparent|var)\b/, "keyword"],
|
|
216
|
+
[/([{}:;,])/, "punctuation"],
|
|
217
|
+
],
|
|
218
|
+
html: [
|
|
219
|
+
[/(<!--[\s\S]*?-->)/, "comment"],
|
|
220
|
+
[/("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/, "string"],
|
|
221
|
+
[/(<\/?)([a-zA-Z][a-zA-Z0-9-]*)/, "tag"], // tag name
|
|
222
|
+
[/\b([a-zA-Z:][a-zA-Z0-9_:-]*)(?=\s*=)/, "attr"], // attribute name
|
|
223
|
+
[/([<>/=])/, "punctuation"],
|
|
224
|
+
],
|
|
225
|
+
bash: [
|
|
226
|
+
[/(#.*)$/, "comment"],
|
|
227
|
+
[/("(?:[^"\\$]|\\.|\$[^{(]|\$\{[^}]*\}|\$\([^)]*\))*"|'[^']*')/, "string"],
|
|
228
|
+
[/\b(if|then|else|elif|fi|for|while|do|done|case|esac|in|function|return|exit|local|export|readonly|declare|source|echo|cd|ls|mkdir|rm|mv|cp|cat|grep|sed|awk|find|curl|wget|git|npm|node|python|pip|sudo|chmod|chown|eval|exec|trap|set|unset|shift|read|printf|test)\b/, "keyword"],
|
|
229
|
+
[/(\$\{[^}]*\}|\$[A-Za-z_][A-Za-z0-9_]*|\$[0-9@#*!?$-])/, "variable"],
|
|
230
|
+
[/\b([0-9]+)\b/, "number"],
|
|
231
|
+
[/([|&;<>(){}[\]])/, "operator"],
|
|
232
|
+
],
|
|
233
|
+
json: [
|
|
234
|
+
[/("(?:[^"\\]|\\.)*")(?=\s*:)/, "property"],
|
|
235
|
+
[/("(?:[^"\\]|\\.)*")/, "string"],
|
|
236
|
+
[/\b(true|false|null)\b/, "keyword"],
|
|
237
|
+
[/(-?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?)/, "number"],
|
|
238
|
+
[/([{}[\]:,])/, "punctuation"],
|
|
239
|
+
],
|
|
240
|
+
};
|
|
139
241
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
[/("""[\s\S]*?"""|'''[\s\S]*?'''|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/, SH.string],
|
|
143
|
-
[/\b(def|class|if|elif|else|for|while|try|except|finally|with|as|import|from|return|yield|pass|break|continue|raise|lambda|not|and|or|in|is|None|True|False|async|await|global|nonlocal|del)\b/, SH.keyword],
|
|
144
|
-
[/\b(print|len|range|type|str|int|float|bool|list|dict|tuple|set|enumerate|zip|map|filter|sorted|reversed|open|super|self|cls)\b/, SH.builtin],
|
|
145
|
-
[/\b([0-9]+(?:\.[0-9]+)?)\b/, SH.number],
|
|
146
|
-
[/([+\-*/=<>!&|^~%]+)/, SH.operator],
|
|
147
|
-
];
|
|
148
|
-
|
|
149
|
-
const RULES_CSS = [
|
|
150
|
-
[/(\/\*[\s\S]*?\*\/)/, SH.comment],
|
|
151
|
-
[/("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/, SH.string],
|
|
152
|
-
[/(#[0-9a-fA-F]{3,8})\b/, SH.value],
|
|
153
|
-
[/\b([0-9]+(?:\.[0-9]+)?(?:px|em|rem|%|vh|vw|vmin|vmax|pt|cm|mm|deg|s|ms)?)\b/, SH.number],
|
|
154
|
-
[/(:[a-zA-Z-]+\(|:[a-zA-Z-]+\b|::[a-zA-Z-]+)/, SH.selector],
|
|
155
|
-
[/([.#]?[a-zA-Z][a-zA-Z0-9_-]*)(?=\s*\{)/, SH.selector],
|
|
156
|
-
[/\b(animation|background|border|color|display|flex|font|gap|grid|height|justify|margin|max|min|opacity|overflow|padding|position|text|transform|transition|width|z-index|align|content|cursor|float|outline|pointer|shadow|top|bottom|left|right)\b/, SH.property],
|
|
157
|
-
[/\b(inherit|initial|none|auto|normal|bold|italic|relative|absolute|fixed|sticky|block|inline|flex|grid|center|left|right|top|bottom|middle|start|end|solid|dashed|dotted|transparent|var)\b/, SH.keyword],
|
|
158
|
-
[/([{}:;,])/, SH.punctuation],
|
|
159
|
-
];
|
|
160
|
-
|
|
161
|
-
const RULES_HTML = [
|
|
162
|
-
[/(<!--[\s\S]*?-->)/, SH.comment],
|
|
163
|
-
[/("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/, SH.string],
|
|
164
|
-
[/(<\/?)([a-zA-Z][a-zA-Z0-9-]*)/, SH.tag], // tag name
|
|
165
|
-
[/\b([a-zA-Z:][a-zA-Z0-9_:-]*)(?=\s*=)/, SH.attr], // attribute name
|
|
166
|
-
[/([<>/=])/, SH.punctuation],
|
|
167
|
-
];
|
|
168
|
-
|
|
169
|
-
const RULES_BASH = [
|
|
170
|
-
[/(#.*)$/, SH.comment],
|
|
171
|
-
[/("(?:[^"\\$]|\\.|\$[^{(]|\$\{[^}]*\}|\$\([^)]*\))*"|'[^']*')/, SH.string],
|
|
172
|
-
[/\b(if|then|else|elif|fi|for|while|do|done|case|esac|in|function|return|exit|local|export|readonly|declare|source|echo|cd|ls|mkdir|rm|mv|cp|cat|grep|sed|awk|find|curl|wget|git|npm|node|python|pip|sudo|chmod|chown|eval|exec|trap|set|unset|shift|read|printf|test)\b/, SH.keyword],
|
|
173
|
-
[/(\$\{[^}]*\}|\$[A-Za-z_][A-Za-z0-9_]*|\$[0-9@#*!?$-])/, SH.variable],
|
|
174
|
-
[/\b([0-9]+)\b/, SH.number],
|
|
175
|
-
[/([|&;<>(){}[\]])/, SH.operator],
|
|
176
|
-
];
|
|
177
|
-
|
|
178
|
-
const RULES_JSON = [
|
|
179
|
-
[/("(?:[^"\\]|\\.)*")(?=\s*:)/, SH.property],
|
|
180
|
-
[/("(?:[^"\\]|\\.)*")/, SH.string],
|
|
181
|
-
[/\b(true|false|null)\b/, SH.keyword],
|
|
182
|
-
[/(-?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?)/, SH.number],
|
|
183
|
-
[/([{}[\]:,])/, SH.punctuation],
|
|
184
|
-
];
|
|
242
|
+
/** @type {Map<object, Record<string, Array<[RegExp, string]>>>} Coloured tables, one per palette. */
|
|
243
|
+
const ruleTables = new Map();
|
|
185
244
|
|
|
186
245
|
/**
|
|
187
|
-
*
|
|
188
|
-
*
|
|
246
|
+
* The `[pattern, colour]` rules for one language on one code plate, built once
|
|
247
|
+
* per palette so the regexes themselves are never recompiled.
|
|
189
248
|
*
|
|
190
|
-
* @param {string}
|
|
191
|
-
* @param {string}
|
|
192
|
-
* @returns {string}
|
|
249
|
+
* @param {string} lang - Normalised language identifier (lower-case).
|
|
250
|
+
* @param {Record<string, string>} sh - The role map the plate chose.
|
|
251
|
+
* @returns {Array<[RegExp, string]> | null} `null` for a language with no rules.
|
|
193
252
|
*/
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
return generateHighlightedHtml(line);
|
|
204
|
-
|
|
205
|
-
case "py":
|
|
206
|
-
case "python":
|
|
207
|
-
return applyRules(line, RULES_PYTHON);
|
|
208
|
-
|
|
209
|
-
case "css":
|
|
210
|
-
case "scss":
|
|
211
|
-
case "less":
|
|
212
|
-
return applyRules(line, RULES_CSS);
|
|
213
|
-
|
|
214
|
-
case "html":
|
|
215
|
-
case "xml":
|
|
216
|
-
case "svg":
|
|
217
|
-
return applyRules(line, RULES_HTML);
|
|
218
|
-
|
|
219
|
-
case "sh":
|
|
220
|
-
case "bash":
|
|
221
|
-
case "shell":
|
|
222
|
-
case "zsh":
|
|
223
|
-
return applyRules(line, RULES_BASH);
|
|
224
|
-
|
|
225
|
-
case "json":
|
|
226
|
-
case "jsonc":
|
|
227
|
-
return applyRules(line, RULES_JSON);
|
|
228
|
-
|
|
229
|
-
default:
|
|
230
|
-
return escapeHtml(line);
|
|
253
|
+
const rulesFor = (lang, sh) => {
|
|
254
|
+
if (!PATTERNS[lang]) return null;
|
|
255
|
+
let table = ruleTables.get(sh);
|
|
256
|
+
if (!table) {
|
|
257
|
+
table = {};
|
|
258
|
+
for (const [name, rows] of Object.entries(PATTERNS)) {
|
|
259
|
+
table[name] = rows.map(([pattern, role]) => [pattern, sh[role]]);
|
|
260
|
+
}
|
|
261
|
+
ruleTables.set(sh, table);
|
|
231
262
|
}
|
|
263
|
+
return table[lang];
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
/** Language aliases a fence's info string may use, mapped to a `PATTERNS` key. */
|
|
268
|
+
const LANGUAGES = {
|
|
269
|
+
py: "python", python: "python",
|
|
270
|
+
css: "css", scss: "css", less: "css",
|
|
271
|
+
html: "html", xml: "html", svg: "html",
|
|
272
|
+
sh: "bash", bash: "bash", shell: "bash", zsh: "bash",
|
|
273
|
+
json: "json", jsonc: "json",
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
/** The languages the shared tokenizer owns; everything else uses `PATTERNS`. */
|
|
277
|
+
const TOKENISED = new Set(["js", "jsx", "javascript", "ts", "tsx", "typescript"]);
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Highlights one source line with the highlighter that speaks its language,
|
|
281
|
+
* using the colours the code plate asked for. Unknown languages are escaped and
|
|
282
|
+
* left plain: a wrong guess is worse than no highlighting.
|
|
283
|
+
*
|
|
284
|
+
* @param {string} line - One line of source code (no trailing newline).
|
|
285
|
+
* @param {string} lang - Normalised language identifier (lower-case).
|
|
286
|
+
* @param {{js: object, sh: object}} syntax - The plate's two colour maps.
|
|
287
|
+
* @returns {string} HTML fragment.
|
|
288
|
+
*/
|
|
289
|
+
const highlightLine = (line, lang, syntax) => {
|
|
290
|
+
if (TOKENISED.has(lang)) return generateHighlightedHtml(line, syntax.js);
|
|
291
|
+
const key = LANGUAGES[lang];
|
|
292
|
+
return key ? applyRules(line, rulesFor(key, syntax.sh)) : escapeHtml(line);
|
|
232
293
|
};
|
|
233
294
|
|
|
234
295
|
/**
|
|
@@ -275,9 +336,10 @@ const legacyCopy = (text) => {
|
|
|
275
336
|
* content node after mount (see the Markdown body).
|
|
276
337
|
*
|
|
277
338
|
* @param {{ lang: string, code: string }} block
|
|
339
|
+
* @param {{js: object, sh: object}} syntax - The plate's two colour maps.
|
|
278
340
|
* @returns {string} HTML fragment.
|
|
279
341
|
*/
|
|
280
|
-
const renderFencedBlock = (block) => {
|
|
342
|
+
const renderFencedBlock = (block, syntax) => {
|
|
281
343
|
const lang = block.lang.replace(/[^A-Za-z0-9#+._-]/g, "").toLowerCase();
|
|
282
344
|
const cls = lang ? ` class="language-${lang}"` : "";
|
|
283
345
|
const label = lang ? `<span class="md-lang-label">${escapeHtml(lang)}</span>` : "";
|
|
@@ -287,7 +349,7 @@ const renderFencedBlock = (block) => {
|
|
|
287
349
|
// doubling the visual gap regardless of line-height.
|
|
288
350
|
const lines = block.code.split("\n");
|
|
289
351
|
const highlighted = lines
|
|
290
|
-
.map((line) => highlightLine(line, lang))
|
|
352
|
+
.map((line) => highlightLine(line, lang, syntax))
|
|
291
353
|
.join("\n");
|
|
292
354
|
|
|
293
355
|
// The raw code is embedded in a hidden <textarea> so the copy handler can
|
|
@@ -541,10 +603,11 @@ const extractRawHtml = (text) => {
|
|
|
541
603
|
* replacing this with a battle-tested library such as `marked` or `micromark`.
|
|
542
604
|
*
|
|
543
605
|
* @param {string} text - Raw Markdown string to convert.
|
|
606
|
+
* @param {{js: object, sh: object}} syntax - Colours for the fences inside it.
|
|
544
607
|
* @returns {string} HTML string. Output may contain tags but no `<script>` or event handlers
|
|
545
608
|
* unless `allowDangerousHtml` is explicitly set on the widget.
|
|
546
609
|
*/
|
|
547
|
-
const parseMarkdown = (text) => {
|
|
610
|
+
const parseMarkdown = (text, syntax) => {
|
|
548
611
|
if (!text) return "";
|
|
549
612
|
|
|
550
613
|
let html = text.replace(/\r\n?/g, "\n");
|
|
@@ -668,7 +731,7 @@ const parseMarkdown = (text) => {
|
|
|
668
731
|
(_match, rawIndex) => {
|
|
669
732
|
const block = blocks[Number(rawIndex)];
|
|
670
733
|
if (!block) return "";
|
|
671
|
-
return renderFencedBlock(block);
|
|
734
|
+
return renderFencedBlock(block, syntax);
|
|
672
735
|
},
|
|
673
736
|
);
|
|
674
737
|
|
|
@@ -861,8 +924,13 @@ export const Markdown = (props) => {
|
|
|
861
924
|
// so the last mounted widget's style props leaked into all the others.
|
|
862
925
|
const scope = `markdown-scope-${++markdownScopeUid}`;
|
|
863
926
|
|
|
927
|
+
// The fence colours are chosen from the plate the app actually paints, not
|
|
928
|
+
// from the reader's theme: `preBgColor` is a public prop, so a light palette
|
|
929
|
+
// can hand its code blocks a dark plate and vice versa.
|
|
930
|
+
const syntax = syntaxFor(preBgColor);
|
|
931
|
+
|
|
864
932
|
// Parse Markdown to raw HTML
|
|
865
|
-
const rawHtml = parseMarkdown(markdownText);
|
|
933
|
+
const rawHtml = parseMarkdown(markdownText, syntax);
|
|
866
934
|
|
|
867
935
|
// Outer container
|
|
868
936
|
const container = WidgetFactory({
|
|
@@ -1108,7 +1176,7 @@ export const Markdown = (props) => {
|
|
|
1108
1176
|
.markdown-content .md-lang-label {
|
|
1109
1177
|
font-family: ${codeFontFamily};
|
|
1110
1178
|
font-size: 0.75em;
|
|
1111
|
-
color: ${
|
|
1179
|
+
color: ${syntax.sh.muted};
|
|
1112
1180
|
text-transform: uppercase;
|
|
1113
1181
|
letter-spacing: 0.06em;
|
|
1114
1182
|
user-select: none;
|
|
@@ -1116,9 +1184,9 @@ export const Markdown = (props) => {
|
|
|
1116
1184
|
|
|
1117
1185
|
.markdown-content .md-copy-btn {
|
|
1118
1186
|
background: none;
|
|
1119
|
-
border: 1px solid
|
|
1187
|
+
border: 1px solid ${syntax.sh.hairline};
|
|
1120
1188
|
border-radius: 4px;
|
|
1121
|
-
color: ${
|
|
1189
|
+
color: ${syntax.sh.muted};
|
|
1122
1190
|
cursor: pointer;
|
|
1123
1191
|
font-size: 0.72em;
|
|
1124
1192
|
padding: 0.15em 0.55em;
|
|
@@ -1127,8 +1195,8 @@ export const Markdown = (props) => {
|
|
|
1127
1195
|
font-family: ${codeFontFamily};
|
|
1128
1196
|
}
|
|
1129
1197
|
.markdown-content .md-copy-btn:hover {
|
|
1130
|
-
color:
|
|
1131
|
-
border-color:
|
|
1198
|
+
color: ${syntax.sh.ink};
|
|
1199
|
+
border-color: ${syntax.sh.hairlineHover};
|
|
1132
1200
|
}
|
|
1133
1201
|
|
|
1134
1202
|
/* Hidden textarea holding the raw source for the copy handler */
|
|
@@ -1235,7 +1303,7 @@ export const Markdown = (props) => {
|
|
|
1235
1303
|
* @param {string} newText - The updated Markdown string.
|
|
1236
1304
|
*/
|
|
1237
1305
|
container.updateContent = (newText) => {
|
|
1238
|
-
const newHtml = parseMarkdown(newText);
|
|
1306
|
+
const newHtml = parseMarkdown(newText, syntax);
|
|
1239
1307
|
contentDiv.innerHTML = allowDangerousHtml
|
|
1240
1308
|
? newHtml
|
|
1241
1309
|
: sanitizeMarkdown(newHtml);
|