scratchblocks-plus 1.0.2 → 1.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.
- package/README.md +24 -9
- package/build/scratchblocks-plus.min.es.js +2 -2
- package/build/scratchblocks-plus.min.es.js.map +1 -1
- package/build/scratchblocks-plus.min.js +2 -2
- package/build/scratchblocks-plus.min.js.map +1 -1
- package/build/translations-all-es.js +1 -1
- package/build/translations-all.js +1 -1
- package/build/translations-es.js +1 -1
- package/build/translations.js +1 -1
- package/node-ssr.js +81 -0
- package/package.json +25 -4
- package/scratch2/blocks.js +21 -3
- package/scratch3/blocks.js +38 -6
- package/scratch3/style.css.js +6 -2
- package/build/scratchblocks.min.es.js +0 -9254
- package/build/scratchblocks.min.es.js.map +0 -1
- package/build/scratchblocks.min.js +0 -8925
- package/build/scratchblocks.min.js.map +0 -1
- package/build/translations-all-es.js.map +0 -1
- package/build/translations-all.js.map +0 -1
- package/build/translations-es.js.map +0 -1
- package/build/translations.js.map +0 -1
package/build/translations-es.js
CHANGED
package/build/translations.js
CHANGED
package/node-ssr.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import init from "./index.js"
|
|
2
|
+
import { parse } from "./syntax/index.js"
|
|
3
|
+
|
|
4
|
+
let nodeWindow
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
const { JSDOM } = await import("jsdom")
|
|
8
|
+
const dom = new JSDOM()
|
|
9
|
+
nodeWindow = dom.window
|
|
10
|
+
} catch {
|
|
11
|
+
try {
|
|
12
|
+
let createCanvas
|
|
13
|
+
const { DOMParser, XMLSerializer, DOMImplementation } =
|
|
14
|
+
await import("@xmldom/xmldom")
|
|
15
|
+
const nodeDocument = new DOMImplementation().createDocument(
|
|
16
|
+
"http://www.w3.org/2000/svg",
|
|
17
|
+
null,
|
|
18
|
+
null,
|
|
19
|
+
)
|
|
20
|
+
try {
|
|
21
|
+
createCanvas = (await import("@napi-rs/canvas")).createCanvas
|
|
22
|
+
} catch {
|
|
23
|
+
try {
|
|
24
|
+
createCanvas = (await import("canvas")).createCanvas
|
|
25
|
+
} catch {
|
|
26
|
+
// pass
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (createCanvas) {
|
|
30
|
+
const origCreateElement = nodeDocument.createElement.bind(nodeDocument)
|
|
31
|
+
nodeDocument.createElement = tagName =>
|
|
32
|
+
tagName === "canvas"
|
|
33
|
+
? createCanvas(300, 150)
|
|
34
|
+
: origCreateElement(tagName)
|
|
35
|
+
}
|
|
36
|
+
nodeWindow = {
|
|
37
|
+
document: nodeDocument,
|
|
38
|
+
DOMParser,
|
|
39
|
+
XMLSerializer,
|
|
40
|
+
}
|
|
41
|
+
} catch {
|
|
42
|
+
// pass
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const sb = init(nodeWindow)
|
|
47
|
+
|
|
48
|
+
export const {
|
|
49
|
+
allLanguages,
|
|
50
|
+
loadLanguages,
|
|
51
|
+
Label,
|
|
52
|
+
Icon,
|
|
53
|
+
Input,
|
|
54
|
+
Block,
|
|
55
|
+
Comment,
|
|
56
|
+
Script,
|
|
57
|
+
Document,
|
|
58
|
+
newView,
|
|
59
|
+
render,
|
|
60
|
+
} = sb
|
|
61
|
+
|
|
62
|
+
export { parse }
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Parse Scratch block code and render it directly to an SVG XML string.
|
|
66
|
+
*
|
|
67
|
+
* @param {string} code - Scratch block source, e.g. "move (10) steps"
|
|
68
|
+
* @param {object} [options] - Same options accepted by scratchblocks.render()
|
|
69
|
+
* - style: "scratch3" | "scratch3-high-contrast" | "scratch2" (default: "scratch3")
|
|
70
|
+
* - languages: string[] (default: ["en"])
|
|
71
|
+
* - scale: number (default: 1)
|
|
72
|
+
* @returns {string} Complete SVG XML string
|
|
73
|
+
*/
|
|
74
|
+
export function renderToSVGString(code, options = {}) {
|
|
75
|
+
options = { style: "scratch3", ...options }
|
|
76
|
+
const doc = parse(code, options)
|
|
77
|
+
const view = sb.newView(doc, options)
|
|
78
|
+
const svg = view.render()
|
|
79
|
+
svg.setAttribute("class", `scratchblocks-style-${options.style}`)
|
|
80
|
+
return view.exportSVGString()
|
|
81
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scratchblocks-plus",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Make pictures of Scratch blocks from text.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Lu Yifei",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
23
|
"build": "rollup -c --environment buildTarget:PROD",
|
|
24
|
-
"fmt": "prettier --cache --write *.js syntax/*.js scratch2/*.js scratch3/*.js locales-src/*.js snapshots/*.js
|
|
24
|
+
"fmt": "prettier --cache --write *.js syntax/*.js scratch2/*.js scratch3/*.js locales-src/*.js snapshots/*.js tests/*.js",
|
|
25
25
|
"lint:staged": "lint-staged",
|
|
26
26
|
"lint": "eslint *.js syntax/*.js scratch2/*.js scratch3/*.js locales-src/*.js snapshots/*.js tests/*.js",
|
|
27
27
|
"locales": "node locales-src/build-locales.js",
|
|
@@ -37,25 +37,46 @@
|
|
|
37
37
|
"@babel/plugin-external-helpers": "^7.27.1",
|
|
38
38
|
"@babel/preset-env": "^7.28.5",
|
|
39
39
|
"@eslint/js": "^9.39.1",
|
|
40
|
+
"@napi-rs/canvas": "^0.1.96",
|
|
41
|
+
"@resvg/resvg-js": "^2.6.2",
|
|
40
42
|
"@rollup/plugin-babel": "^6.1.0",
|
|
41
43
|
"@rollup/plugin-json": "^6.1.0",
|
|
42
44
|
"@rollup/plugin-terser": "^0.4.4",
|
|
45
|
+
"@xmldom/xmldom": "^0.8.11",
|
|
43
46
|
"cross-env": "^10.1.0",
|
|
44
47
|
"csso": "^5.0.5",
|
|
45
48
|
"eslint": "^9.39.1",
|
|
46
|
-
"express": "^5.1.0",
|
|
47
49
|
"globals": "^16.5.0",
|
|
48
50
|
"jest": "^30.2.0",
|
|
49
51
|
"lint-staged": "^16.2.6",
|
|
50
52
|
"prettier": "^3.6.2",
|
|
51
53
|
"prettier-package-json": "^2.8.0",
|
|
52
|
-
"puppeteer": "^24.30.0",
|
|
53
54
|
"rollup": "^4.53.2",
|
|
54
55
|
"rollup-plugin-license": "^3.6.0",
|
|
55
56
|
"rollup-plugin-serve": "^3.0.0",
|
|
56
57
|
"scratch-l10n": "^6.1.23",
|
|
57
58
|
"scratch-translate-extension-languages": "^1.0.7"
|
|
58
59
|
},
|
|
60
|
+
"peerDependencies": {
|
|
61
|
+
"@napi-rs/canvas": "*",
|
|
62
|
+
"@xmldom/xmldom": ">=0.8.0",
|
|
63
|
+
"canvas": ">=2.0.0",
|
|
64
|
+
"jsdom": ">=16.0.0"
|
|
65
|
+
},
|
|
66
|
+
"peerDependenciesMeta": {
|
|
67
|
+
"@xmldom/xmldom": {
|
|
68
|
+
"optional": true
|
|
69
|
+
},
|
|
70
|
+
"canvas": {
|
|
71
|
+
"optional": true
|
|
72
|
+
},
|
|
73
|
+
"@napi-rs/canvas": {
|
|
74
|
+
"optional": true
|
|
75
|
+
},
|
|
76
|
+
"jsdom": {
|
|
77
|
+
"optional": true
|
|
78
|
+
}
|
|
79
|
+
},
|
|
59
80
|
"keywords": [
|
|
60
81
|
"scratch"
|
|
61
82
|
],
|
package/scratch2/blocks.js
CHANGED
|
@@ -24,6 +24,20 @@ const {
|
|
|
24
24
|
darkFilter,
|
|
25
25
|
} = style
|
|
26
26
|
|
|
27
|
+
// to be compatible with node.js xmldom
|
|
28
|
+
function addClass(el, className) {
|
|
29
|
+
if (el.classList) {
|
|
30
|
+
el.classList.add(className)
|
|
31
|
+
} else {
|
|
32
|
+
const current = el.getAttribute("class") || ""
|
|
33
|
+
const classes = current.split(/\s+/).filter(Boolean)
|
|
34
|
+
if (!classes.includes(className)) {
|
|
35
|
+
classes.push(className)
|
|
36
|
+
el.setAttribute("class", classes.join(" "))
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
27
41
|
export class LabelView {
|
|
28
42
|
constructor(label) {
|
|
29
43
|
Object.assign(this, label)
|
|
@@ -184,7 +198,7 @@ class MatrixView {
|
|
|
184
198
|
if (isFilled) {
|
|
185
199
|
rect.setAttribute("fill", "#FFFFFF")
|
|
186
200
|
} else {
|
|
187
|
-
rect
|
|
201
|
+
addClass(rect, `sb-${parent.info.category}`)
|
|
188
202
|
}
|
|
189
203
|
|
|
190
204
|
elements.push(rect)
|
|
@@ -852,7 +866,9 @@ class DocumentView {
|
|
|
852
866
|
* Build the element map by finding all elements with data-block-path
|
|
853
867
|
*/
|
|
854
868
|
_buildElementMap() {
|
|
855
|
-
if (!this.el)
|
|
869
|
+
if (!this.el || !this.el.querySelectorAll) {
|
|
870
|
+
return
|
|
871
|
+
}
|
|
856
872
|
|
|
857
873
|
this.elementMap.clear()
|
|
858
874
|
const blocks = this.el.querySelectorAll("[data-block-path]")
|
|
@@ -881,7 +897,9 @@ class DocumentView {
|
|
|
881
897
|
*/
|
|
882
898
|
highlightBlock(path, options = {}) {
|
|
883
899
|
const el = this.getElementByPath(path)
|
|
884
|
-
if (!el)
|
|
900
|
+
if (!el) {
|
|
901
|
+
return false
|
|
902
|
+
}
|
|
885
903
|
|
|
886
904
|
// Add highlight class to the first child (the shape element)
|
|
887
905
|
const shapeEl = el.firstElementChild
|
package/scratch3/blocks.js
CHANGED
|
@@ -22,6 +22,34 @@ const {
|
|
|
22
22
|
iconName,
|
|
23
23
|
} = style
|
|
24
24
|
|
|
25
|
+
// to be compatible with node.js xmldom
|
|
26
|
+
function addClass(el, className) {
|
|
27
|
+
if (el.classList) {
|
|
28
|
+
el.classList.add(className)
|
|
29
|
+
} else {
|
|
30
|
+
const current = el.getAttribute("class") || ""
|
|
31
|
+
const classes = current.split(/\s+/).filter(Boolean)
|
|
32
|
+
if (!classes.includes(className)) {
|
|
33
|
+
classes.push(className)
|
|
34
|
+
el.setAttribute("class", classes.join(" "))
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function removeClass(el, className) {
|
|
40
|
+
if (el.classList) {
|
|
41
|
+
el.classList.remove(className)
|
|
42
|
+
} else {
|
|
43
|
+
const current = el.getAttribute("class") || ""
|
|
44
|
+
const classes = current.split(/\s+/).filter(cls => cls !== className)
|
|
45
|
+
if (classes.length) {
|
|
46
|
+
el.setAttribute("class", classes.join(" "))
|
|
47
|
+
} else {
|
|
48
|
+
el.removeAttribute("class")
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
25
53
|
export class LabelView {
|
|
26
54
|
constructor(label) {
|
|
27
55
|
Object.assign(this, label)
|
|
@@ -216,7 +244,7 @@ export class MatrixView {
|
|
|
216
244
|
if (isFilled) {
|
|
217
245
|
rect.setAttribute("fill", "#FFFFFF")
|
|
218
246
|
} else {
|
|
219
|
-
rect
|
|
247
|
+
addClass(rect, `sb3-${parent.info.category}`)
|
|
220
248
|
}
|
|
221
249
|
|
|
222
250
|
elements.push(rect)
|
|
@@ -329,7 +357,7 @@ export class InputView {
|
|
|
329
357
|
})
|
|
330
358
|
}
|
|
331
359
|
} else if (this.shape === "number-dropdown") {
|
|
332
|
-
el
|
|
360
|
+
addClass(el, `sb3-${parent.info.category}-alt`)
|
|
333
361
|
|
|
334
362
|
// custom colors
|
|
335
363
|
if (parent.info.color) {
|
|
@@ -339,8 +367,8 @@ export class InputView {
|
|
|
339
367
|
})
|
|
340
368
|
}
|
|
341
369
|
} else if (this.shape === "boolean") {
|
|
342
|
-
el
|
|
343
|
-
el
|
|
370
|
+
removeClass(el, `sb3-${parent.info.category}`)
|
|
371
|
+
addClass(el, `sb3-${parent.info.category}-dark`)
|
|
344
372
|
|
|
345
373
|
// custom colors
|
|
346
374
|
if (parent.info.color) {
|
|
@@ -969,7 +997,9 @@ class DocumentView {
|
|
|
969
997
|
* Build the element map by finding all elements with data-block-path
|
|
970
998
|
*/
|
|
971
999
|
_buildElementMap() {
|
|
972
|
-
if (!this.el)
|
|
1000
|
+
if (!this.el || !this.el.querySelectorAll) {
|
|
1001
|
+
return
|
|
1002
|
+
}
|
|
973
1003
|
|
|
974
1004
|
this.elementMap.clear()
|
|
975
1005
|
const blocks = this.el.querySelectorAll("[data-block-path]")
|
|
@@ -1000,7 +1030,9 @@ class DocumentView {
|
|
|
1000
1030
|
*/
|
|
1001
1031
|
highlightBlock(path, options = {}) {
|
|
1002
1032
|
const el = this.getElementByPath(path)
|
|
1003
|
-
if (!el)
|
|
1033
|
+
if (!el) {
|
|
1034
|
+
return false
|
|
1035
|
+
}
|
|
1004
1036
|
|
|
1005
1037
|
// Add highlight class to the first child (the shape element)
|
|
1006
1038
|
const shapeEl = el.firstElementChild
|
package/scratch3/style.css.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
const common = `
|
|
4
4
|
.sb3-label {
|
|
5
|
-
font: 500
|
|
5
|
+
font-weight: 500;
|
|
6
|
+
font-size: 12pt;
|
|
7
|
+
font-family: Helvetica Neue, Helvetica, sans-serif;
|
|
6
8
|
}
|
|
7
9
|
|
|
8
10
|
.sb3-literal-number,
|
|
@@ -63,7 +65,9 @@ const commonOverride = `
|
|
|
63
65
|
}
|
|
64
66
|
/* specificity */
|
|
65
67
|
.sb3-comment-label, .sb3-label.sb3-comment-label {
|
|
66
|
-
font: 400
|
|
68
|
+
font-weight: 400;
|
|
69
|
+
font-size: 12pt;
|
|
70
|
+
font-family: Helvetica Neue, Helvetica, sans-serif;
|
|
67
71
|
fill: #000;
|
|
68
72
|
word-spacing: 0;
|
|
69
73
|
}`
|