scratch-blocks 2.1.11 → 2.1.13

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.
@@ -1 +1 @@
1
- {"version":3,"file":"procedures.d.ts","sourceRoot":"","sources":["../../../../src/blocks/procedures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH;;GAEG;AACH,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAIvC;;GAEG;AACH,KAAK,aAAa,GAAG,MAAM,CACzB,MAAM,EACN;IACE,MAAM,EAAE,OAAO,GAAG,SAAS,CAAA;IAC3B,KAAK,EAAE,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAA;CAC/B,GAAG,IAAI,CACT,CAAA;AAED;;GAEG;AACH,aAAK,YAAY;IACf,MAAM,MAAM;IACZ,MAAM,MAAM;IACZ,OAAO,MAAM;CACd;AA8tCD,UAAU,cAAe,SAAQ,OAAO,CAAC,QAAQ;IAC/C,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,KAAK,EAAE,OAAO,CAAA;IACd,WAAW,EAAE,MAAM,MAAM,CAAA;IACzB,gBAAgB,EAAE,MAAM,IAAI,CAAA;IAC5B,oBAAoB,EAAE,MAAM,aAAa,CAAA;IACzC,sBAAsB,EAAE,CAAC,aAAa,EAAE,aAAa,KAAK,IAAI,CAAA;IAC9D,gBAAgB,EAAE,CAAC,aAAa,EAAE,aAAa,KAAK,IAAI,CAAA;IACxD,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B,iBAAiB,EAAE,CACjB,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,aAAa,EAC5B,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,OAAO,CAAC,KAAK,KACjB,IAAI,CAAA;IACT,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAC3C;AAED,MAAM,WAAW,yBAA0B,SAAQ,cAAc;IAC/D,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,iBAAiB,EAAE,MAAM,EAAE,CAAA;IAC3B,mBAAmB,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,KAAK,IAAI,CAAA;IACnD,qBAAqB,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAA;IAC5F,gBAAgB,EAAE,MAAM,IAAI,CAAA;IAC5B,OAAO,EAAE,MAAM,OAAO,CAAA;IACtB,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IAChC,gBAAgB,EAAE,MAAM,IAAI,CAAA;IAC5B,kBAAkB,EAAE,MAAM,IAAI,CAAA;IAC9B,uBAAuB,EAAE,MAAM,IAAI,CAAA;IACnC,UAAU,EAAE,MAAM,IAAI,CAAA;CACvB"}
1
+ {"version":3,"file":"procedures.d.ts","sourceRoot":"","sources":["../../../../src/blocks/procedures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH;;GAEG;AACH,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAIvC;;GAEG;AACH,KAAK,aAAa,GAAG,MAAM,CACzB,MAAM,EACN;IACE,MAAM,EAAE,OAAO,GAAG,SAAS,CAAA;IAC3B,KAAK,EAAE,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAA;CAC/B,GAAG,IAAI,CACT,CAAA;AAED;;GAEG;AACH,aAAK,YAAY;IACf,MAAM,MAAM;IACZ,MAAM,MAAM;IACZ,OAAO,MAAM;CACd;AA8vCD,UAAU,cAAe,SAAQ,OAAO,CAAC,QAAQ;IAC/C,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,KAAK,EAAE,OAAO,CAAA;IACd,WAAW,EAAE,MAAM,MAAM,CAAA;IACzB,gBAAgB,EAAE,MAAM,IAAI,CAAA;IAC5B,oBAAoB,EAAE,MAAM,aAAa,CAAA;IACzC,sBAAsB,EAAE,CAAC,aAAa,EAAE,aAAa,KAAK,IAAI,CAAA;IAC9D,gBAAgB,EAAE,CAAC,aAAa,EAAE,aAAa,KAAK,IAAI,CAAA;IACxD,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B,iBAAiB,EAAE,CACjB,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,aAAa,EAC5B,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,OAAO,CAAC,KAAK,KACjB,IAAI,CAAA;IACT,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAC3C;AAED,MAAM,WAAW,yBAA0B,SAAQ,cAAc;IAC/D,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,iBAAiB,EAAE,MAAM,EAAE,CAAA;IAC3B,mBAAmB,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,KAAK,IAAI,CAAA;IACnD,qBAAqB,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAA;IAC5F,gBAAgB,EAAE,MAAM,IAAI,CAAA;IAC5B,OAAO,EAAE,MAAM,OAAO,CAAA;IACtB,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IAChC,gBAAgB,EAAE,MAAM,IAAI,CAAA;IAC5B,kBAAkB,EAAE,MAAM,IAAI,CAAA;IAC9B,uBAAuB,EAAE,MAAM,IAAI,CAAA;IACnC,UAAU,EAAE,MAAM,IAAI,CAAA;CACvB"}
@@ -1 +1 @@
1
- {"version":3,"file":"scratch_field_dropdown.d.ts","sourceRoot":"","sources":["../../../../src/fields/scratch_field_dropdown.ts"],"names":[],"mappings":"AAqCA;;GAEG;AACH,wBAAgB,4BAA4B,SAG3C"}
1
+ {"version":3,"file":"scratch_field_dropdown.d.ts","sourceRoot":"","sources":["../../../../src/fields/scratch_field_dropdown.ts"],"names":[],"mappings":"AAiFA;;GAEG;AACH,wBAAgB,4BAA4B,SAG3C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scratch-blocks",
3
- "version": "2.1.11",
3
+ "version": "2.1.13",
4
4
  "description": "Scratch Blocks is a library for building creative computing interfaces.",
5
5
  "author": "Massachusetts Institute of Technology",
6
6
  "license": "Apache-2.0",
@@ -41,7 +41,7 @@
41
41
  "@vitest/browser": "4.1.4",
42
42
  "@vitest/browser-playwright": "4.1.4",
43
43
  "eslint": "9.39.4",
44
- "eslint-config-scratch": "14.1.6",
44
+ "eslint-config-scratch": "14.1.7",
45
45
  "husky": "9.1.7",
46
46
  "playwright": "1.59.1",
47
47
  "prettier": "3.8.2",
@@ -97,6 +97,38 @@ function parseRequiredMutationJson<T>(
97
97
  return parse(parsedValue, name)
98
98
  }
99
99
 
100
+ /**
101
+ * Parse an optional mutation attribute as JSON, returning a fallback when the
102
+ * attribute is absent. Use this only for attributes that can be safely
103
+ * defaulted in isolation, without invalidating structural invariants that
104
+ * relate them to other attributes on the same mutation. A present-but-malformed
105
+ * attribute still throws, since that indicates corruption rather than an older
106
+ * schema.
107
+ * @param xmlElement The mutation element that may contain the attribute.
108
+ * @param name The specific mutation attribute to retrieve and parse.
109
+ * @param parse Validates and narrows the parsed JSON value.
110
+ * @param fallback Value to return when the attribute is absent.
111
+ * @returns Parsed and validated mutation attribute value, or `fallback`.
112
+ */
113
+ function parseOptionalMutationJson<T>(
114
+ xmlElement: Element,
115
+ name: string,
116
+ parse: (value: unknown, name: string) => T,
117
+ fallback: T,
118
+ ): T {
119
+ const rawValue = xmlElement.getAttribute(name)
120
+ if (rawValue === null) {
121
+ return fallback
122
+ }
123
+ let parsedValue: unknown
124
+ try {
125
+ parsedValue = JSON.parse(rawValue)
126
+ } catch {
127
+ throw new Error(`Invalid JSON in mutation attribute: ${name}`)
128
+ }
129
+ return parse(parsedValue, name)
130
+ }
131
+
100
132
  /**
101
133
  * Validate a parsed mutation value as a boolean.
102
134
  * @param value Parsed mutation value.
@@ -277,7 +309,7 @@ function callerDomToMutation(this: ProcedureCallBlock, xmlElement: Element) {
277
309
  const generateshadows = xmlElement.getAttribute('generateshadows')
278
310
  this.generateShadows_ = generateshadows !== null ? JSON.parse(generateshadows) === true : false
279
311
  this.argumentIds_ = parseRequiredMutationJson(xmlElement, 'argumentids', parseStringArrayMutationValue)
280
- this.warp_ = parseRequiredMutationJson(xmlElement, 'warp', parseBooleanMutationValue)
312
+ this.warp_ = parseOptionalMutationJson(xmlElement, 'warp', parseBooleanMutationValue, false)
281
313
  this.updateDisplay_()
282
314
  }
283
315
 
@@ -312,7 +344,7 @@ function definitionMutationToDom(
312
344
  */
313
345
  function definitionDomToMutation(this: ProcedurePrototypeBlock | ProcedureDeclarationBlock, xmlElement: Element) {
314
346
  this.procCode_ = getRequiredMutationAttribute(xmlElement, 'proccode')
315
- this.warp_ = parseRequiredMutationJson(xmlElement, 'warp', parseBooleanMutationValue)
347
+ this.warp_ = parseOptionalMutationJson(xmlElement, 'warp', parseBooleanMutationValue, false)
316
348
 
317
349
  const prevArgIds = this.argumentIds_
318
350
  const prevDisplayNames = this.displayNames_
@@ -7,6 +7,50 @@ import * as Blockly from 'blockly/core'
7
7
  class ScratchFieldDropdown extends Blockly.FieldDropdown {
8
8
  private originalStyle!: string
9
9
 
10
+ /**
11
+ * Accept string values even when they are not in the current options list.
12
+ * Scratch populates some dropdowns from dynamic data (e.g. the sprite list
13
+ * for motion_goto_menu, motion_pointtowards_menu, sensing_touchingobject_menu)
14
+ * and deliberately excludes the currently-editing sprite. A block moved into
15
+ * the sprite it now targets carries a stored value that is therefore absent
16
+ * from the option list for this editing context, but is still valid — the
17
+ * runtime reads the stored value directly. Preserve that value so the UI
18
+ * shows what the block actually does, rather than silently reverting to the
19
+ * first option.
20
+ * @param newValue The value to validate.
21
+ * @returns The value unchanged if it is a string; otherwise null to reject
22
+ * the update.
23
+ */
24
+ protected override doClassValidation_(newValue?: string): string | null {
25
+ if (typeof newValue !== 'string') {
26
+ return null
27
+ }
28
+ return newValue
29
+ }
30
+
31
+ /**
32
+ * When the stored value is not in the current options list, display the raw
33
+ * value. Blockly's base implementation reads from the last-matched option,
34
+ * which stays stale when the incoming value does not appear in the list, so
35
+ * the default would otherwise render as the first option's text.
36
+ * @returns Text to display for the currently-selected value.
37
+ */
38
+ protected override getText_(): string | null {
39
+ const value = this.getValue()
40
+ if (value === null) {
41
+ return super.getText_()
42
+ }
43
+ for (const option of this.getOptions(true)) {
44
+ if (option === 'separator') continue
45
+ if (option[1] === value) {
46
+ // Delegate to the base for the matched case so image/HTMLElement
47
+ // option types render correctly.
48
+ return super.getText_()
49
+ }
50
+ }
51
+ return value
52
+ }
53
+
10
54
  showEditor_(event: PointerEvent) {
11
55
  super.showEditor_(event)
12
56
  const sourceBlock = this.getSourceBlock() as Blockly.BlockSvg