@particle-academy/fancy-code 0.9.1 → 0.9.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/CHANGELOG.md +68 -0
- package/dist/index.cjs +20 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +20 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@particle-academy/fancy-code` are documented here.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
|
+
|
|
7
|
+
> **Pre-1.0:** breaking changes land in MINOR releases. Until 1.0 the minor
|
|
8
|
+
> number is not a compatibility promise — read the entry, not the version.
|
|
9
|
+
|
|
10
|
+
> This file starts here. Earlier releases predate it and were never written up;
|
|
11
|
+
> `git log` is the record for those. It is not backfilled rather than
|
|
12
|
+
> guessed-at, because a changelog that invents its own history is worse than one
|
|
13
|
+
> that admits where it begins.
|
|
14
|
+
|
|
15
|
+
## [Unreleased]
|
|
16
|
+
|
|
17
|
+
## [0.9.3] — 2026-10-18
|
|
18
|
+
|
|
19
|
+
The 0.9.2 fix below, actually published. **0.9.2 was tagged but never reached
|
|
20
|
+
npm**: the publish workflow's install step crashed on the runner's npm 10 (an
|
|
21
|
+
arborist peer-set bug). It now installs with npm 11, the version it already
|
|
22
|
+
published with. This line also publishes under the `maint-0.4` dist-tag, so
|
|
23
|
+
`latest` stays on the current line.
|
|
24
|
+
|
|
25
|
+
## [0.9.2] — 2026-10-18 (tagged, never published)
|
|
26
|
+
|
|
27
|
+
### Security
|
|
28
|
+
|
|
29
|
+
- **`<MarkdownEditor>`'s preview no longer renders a link or image whose URL
|
|
30
|
+
can run script.** Backported from 0.12.1. The preview is inserted with
|
|
31
|
+
`innerHTML`, where React's `javascript:` guard does not apply, and URL schemes
|
|
32
|
+
were never checked, so `[x](javascript:...)` was a live script link. A link
|
|
33
|
+
now needs a relative URL or `http`, `https`, `mailto` or `tel`; an image a
|
|
34
|
+
relative URL, `http(s)` or a raster `data:image/...`. Anything else renders as
|
|
35
|
+
its text or alt text. **Upgrade if any markdown you preview was written by
|
|
36
|
+
someone else.** Nothing else changes.
|
|
37
|
+
|
|
38
|
+
## [0.9.1] — 2026-07-28
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- **`CodeEditorPanel` crashed when the editor engine arrived after first
|
|
43
|
+
render.** `if (!engine) return null` sat above the `useMemo` that builds the
|
|
44
|
+
gutter, so a render without an engine ran zero hooks and the next ran one —
|
|
45
|
+
React desyncs on the changed count and throws from inside itself.
|
|
46
|
+
`_engineReturn` is typed `| null` and starts null, so any host rendering the
|
|
47
|
+
panel before the engine settles hit this.
|
|
48
|
+
|
|
49
|
+
**No action needed** — the fix is internal and the API is unchanged.
|
|
50
|
+
|
|
51
|
+
### Added
|
|
52
|
+
|
|
53
|
+
- `jsdom` as a devDependency, and the first component test in this package.
|
|
54
|
+
There was previously no way to render a component in a test here at all, which
|
|
55
|
+
is precisely how the bug above shipped.
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
|
|
59
|
+
- Widened the `@particle-academy/fancy-file-commons` requirement from `^0.2.0` to
|
|
60
|
+
`>=0.2 <2.0`, so a sibling minor release is an upgrade and not a resolver
|
|
61
|
+
conflict. **No action needed** — widening a range only adds candidates; the
|
|
62
|
+
version you have today still resolves.
|
|
63
|
+
|
|
64
|
+
A caret on a `0.x` range locks the MINOR, so this pinned a sibling at
|
|
65
|
+
whatever it happened to be on the day it was written, and each sibling
|
|
66
|
+
release then read as a conflict to the resolver rather than an upgrade.
|
|
67
|
+
Nothing here was using an API the newer minors removed — the range was the
|
|
68
|
+
whole problem.
|
package/dist/index.cjs
CHANGED
|
@@ -1643,8 +1643,27 @@ function escapeHtml(s) {
|
|
|
1643
1643
|
function escapeAttr(s) {
|
|
1644
1644
|
return s.replace(/"/g, """).replace(/'/g, "'");
|
|
1645
1645
|
}
|
|
1646
|
+
var LINK_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "mailto", "tel"]);
|
|
1647
|
+
function schemeOf(url) {
|
|
1648
|
+
let s = "";
|
|
1649
|
+
for (const ch of url) {
|
|
1650
|
+
const code = ch.codePointAt(0) ?? 0;
|
|
1651
|
+
if (code === 9 || code === 10 || code === 13) continue;
|
|
1652
|
+
if (s === "" && code <= 32) continue;
|
|
1653
|
+
s += ch;
|
|
1654
|
+
}
|
|
1655
|
+
const m = /^([a-zA-Z][a-zA-Z0-9+.-]*):/.exec(s);
|
|
1656
|
+
return m ? m[1].toLowerCase() : null;
|
|
1657
|
+
}
|
|
1658
|
+
function safeUrl(url, kind) {
|
|
1659
|
+
const scheme = schemeOf(url);
|
|
1660
|
+
if (scheme === null) return true;
|
|
1661
|
+
if (scheme === "http" || scheme === "https") return true;
|
|
1662
|
+
if (kind === "link") return LINK_SCHEMES.has(scheme);
|
|
1663
|
+
return scheme === "data" && /^data:image\/(png|jpe?g|gif|webp|avif|bmp);/i.test(url.trim());
|
|
1664
|
+
}
|
|
1646
1665
|
function inline(text) {
|
|
1647
|
-
return text.replace(/!\[([^\]]*)\]\(([^)\s]+)(?:\s+"([^"]*)")?\)/g, (_m, alt, src, title) => `<img src="${escapeAttr(src)}" alt="${escapeAttr(alt)}"${title ? ` title="${escapeAttr(title)}"` : ""}>`).replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"([^"]*)")?\)/g, (_m, t, href, title) => `<a href="${escapeAttr(href)}"${title ? ` title="${escapeAttr(title)}"` : ""} rel="noopener">${t}</a>`).replace(/`([^`]+)`/g, (_m, c) => `<code>${c}</code>`).replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g, "<strong>$2</strong>").replace(/(\*|_)(?=\S)([\s\S]*?\S)\1/g, "<em>$2</em>");
|
|
1666
|
+
return text.replace(/!\[([^\]]*)\]\(([^)\s]+)(?:\s+"([^"]*)")?\)/g, (_m, alt, src, title) => safeUrl(src, "image") ? `<img src="${escapeAttr(src)}" alt="${escapeAttr(alt)}"${title ? ` title="${escapeAttr(title)}"` : ""}>` : alt).replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"([^"]*)")?\)/g, (_m, t, href, title) => safeUrl(href, "link") ? `<a href="${escapeAttr(href)}"${title ? ` title="${escapeAttr(title)}"` : ""} rel="noopener">${t}</a>` : t).replace(/`([^`]+)`/g, (_m, c) => `<code>${c}</code>`).replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g, "<strong>$2</strong>").replace(/(\*|_)(?=\S)([\s\S]*?\S)\1/g, "<em>$2</em>");
|
|
1648
1667
|
}
|
|
1649
1668
|
function renderMarkdown(markdown) {
|
|
1650
1669
|
const lines = markdown.replace(/\r\n?/g, "\n").split("\n");
|