scratch-blocks 2.1.2 → 2.1.4

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,CAAA;IACf,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAA;CACxB,GAAG,IAAI,CACT,CAAA;AAED;;GAEG;AACH,aAAK,YAAY;IACf,MAAM,MAAM;IACZ,MAAM,MAAM;IACZ,OAAO,MAAM;CACd;AA+hCD,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,CAAA;IACf,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAA;CACxB,GAAG,IAAI,CACT,CAAA;AAED;;GAEG;AACH,aAAK,YAAY;IACf,MAAM,MAAM;IACZ,MAAM,MAAM;IACZ,OAAO,MAAM;CACd;AAgoCD,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":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AACvC,OAAO,iBAAiB,CAAA;AACxB,OAAO,kBAAkB,CAAA;AACzB,OAAO,eAAe,CAAA;AACtB,OAAO,gBAAgB,CAAA;AACvB,OAAO,gBAAgB,CAAA;AACvB,OAAO,eAAe,CAAA;AACtB,OAAO,iBAAiB,CAAA;AACxB,OAAO,iBAAiB,CAAA;AACxB,OAAO,eAAe,CAAA;AACtB,OAAO,oBAAoB,CAAA;AAC3B,OAAO,qBAAqB,CAAA;AAC5B,OAAO,kBAAkB,CAAA;AACzB,OAAO,gBAAgB,CAAA;AACvB,OAAO,eAAe,CAAA;AACtB,OAAO,8BAA8B,CAAA;AACrC,OAAO,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAA;AACzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,KAAK,gBAAgB,MAAM,sBAAsB,CAAA;AACxD,OAAO,OAAO,CAAA;AACd,OAAO,sCAAsC,CAAA;AAC7C,OAAO,wCAAwC,CAAA;AAC/C,OAAO,sCAAsC,CAAA;AAC7C,OAAO,sCAAsC,CAAA;AAC7C,OAAO,oCAAoC,CAAA;AAC3C,OAAO,sCAAsC,CAAA;AAC7C,OAAO,yCAAyC,CAAA;AAChD,OAAO,EAA6B,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AAE3F,OAAO,EAAqB,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAQlE,OAAO,wBAAwB,CAAA;AAC/B,OAAO,EAAmB,SAAS,EAAE,MAAM,SAAS,CAAA;AAEpD,OAAO,yBAAyB,CAAA;AAChC,OAAO,qBAAqB,CAAA;AAE5B,OAAO,KAAK,kBAAkB,MAAM,wBAAwB,CAAA;AAC5D,OAAO,wBAAwB,CAAA;AAC/B,OAAO,8BAA8B,CAAA;AAGrC,OAAO,mBAAmB,CAAA;AAC1B,OAAO,sCAAsC,CAAA;AAC7C,OAAO,wBAAwB,CAAA;AAC/B,OAAO,0BAA0B,CAAA;AAIjC,OAAO,KAAK,gBAAgB,MAAM,aAAa,CAAA;AAE/C,cAAc,cAAc,CAAA;AAC5B,cAAc,mBAAmB,CAAA;AACjC,cAAc,cAAc,CAAA;AAC5B,cAAc,wBAAwB,CAAA;AACtC,cAAc,aAAa,CAAA;AAC3B,OAAO,EAAE,SAAS,EAAE,CAAA;AACpB,OAAO,EAAE,kBAAkB,EAAE,CAAA;AAC7B,OAAO,EAAE,yBAAyB,EAAE,CAAA;AACpC,OAAO,EAAE,gBAAgB,EAAE,CAAA;AAC3B,OAAO,EAAE,gBAAgB,EAAE,CAAA;AAC3B,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,CAAA;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAC7D,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAClF,cAAc,OAAO,CAAA;AAErB,UAAU,oBAAqB,SAAQ,OAAO,CAAC,cAAc;IAC3D;;;OAGG;IACH,YAAY,CAAC,EAAE,kBAAkB,CAAA;CAClC;AASD,wBAAgB,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,oBAAoB,wBAmDvE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AACvC,OAAO,iBAAiB,CAAA;AACxB,OAAO,kBAAkB,CAAA;AACzB,OAAO,eAAe,CAAA;AACtB,OAAO,gBAAgB,CAAA;AACvB,OAAO,gBAAgB,CAAA;AACvB,OAAO,eAAe,CAAA;AACtB,OAAO,iBAAiB,CAAA;AACxB,OAAO,iBAAiB,CAAA;AACxB,OAAO,eAAe,CAAA;AACtB,OAAO,oBAAoB,CAAA;AAC3B,OAAO,qBAAqB,CAAA;AAC5B,OAAO,kBAAkB,CAAA;AACzB,OAAO,gBAAgB,CAAA;AACvB,OAAO,eAAe,CAAA;AACtB,OAAO,8BAA8B,CAAA;AACrC,OAAO,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAA;AACzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,KAAK,gBAAgB,MAAM,sBAAsB,CAAA;AACxD,OAAO,OAAO,CAAA;AACd,OAAO,sCAAsC,CAAA;AAC7C,OAAO,wCAAwC,CAAA;AAC/C,OAAO,sCAAsC,CAAA;AAC7C,OAAO,sCAAsC,CAAA;AAC7C,OAAO,oCAAoC,CAAA;AAC3C,OAAO,sCAAsC,CAAA;AAC7C,OAAO,yCAAyC,CAAA;AAChD,OAAO,EAA6B,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AAE3F,OAAO,EAAqB,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAQlE,OAAO,wBAAwB,CAAA;AAC/B,OAAO,EAAmB,SAAS,EAAE,MAAM,SAAS,CAAA;AAEpD,OAAO,yBAAyB,CAAA;AAChC,OAAO,qBAAqB,CAAA;AAE5B,OAAO,KAAK,kBAAkB,MAAM,wBAAwB,CAAA;AAC5D,OAAO,wBAAwB,CAAA;AAC/B,OAAO,8BAA8B,CAAA;AAGrC,OAAO,mBAAmB,CAAA;AAC1B,OAAO,sCAAsC,CAAA;AAC7C,OAAO,wBAAwB,CAAA;AAC/B,OAAO,0BAA0B,CAAA;AAGjC,OAAO,KAAK,gBAAgB,MAAM,aAAa,CAAA;AAE/C,cAAc,cAAc,CAAA;AAC5B,cAAc,mBAAmB,CAAA;AACjC,cAAc,cAAc,CAAA;AAC5B,cAAc,wBAAwB,CAAA;AACtC,cAAc,aAAa,CAAA;AAC3B,OAAO,EAAE,SAAS,EAAE,CAAA;AACpB,OAAO,EAAE,kBAAkB,EAAE,CAAA;AAC7B,OAAO,EAAE,yBAAyB,EAAE,CAAA;AACpC,OAAO,EAAE,gBAAgB,EAAE,CAAA;AAC3B,OAAO,EAAE,gBAAgB,EAAE,CAAA;AAC3B,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,CAAA;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAC7D,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAClF,cAAc,OAAO,CAAA;AAErB,UAAU,oBAAqB,SAAQ,OAAO,CAAC,cAAc;IAC3D;;;OAGG;IACH,YAAY,CAAC,EAAE,kBAAkB,CAAA;CAClC;AASD,wBAAgB,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,oBAAoB,wBAkDvE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scratch-blocks",
3
- "version": "2.1.2",
3
+ "version": "2.1.4",
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",
@@ -24,7 +24,8 @@
24
24
  "prepare": "husky || true",
25
25
  "start": "webpack serve --open --mode development",
26
26
  "test": "echo \"Error: no test specified\" && exit 1",
27
- "test:lint": "eslint && prettier --check ."
27
+ "test:lint": "eslint && prettier --check .",
28
+ "watch": "webpack --mode development --watch"
28
29
  },
29
30
  "dependencies": {
30
31
  "@blockly/continuous-toolbox": "^7.0.8",
@@ -32,10 +33,10 @@
32
33
  "blockly": "^12.4.1"
33
34
  },
34
35
  "devDependencies": {
35
- "@commitlint/cli": "20.4.4",
36
- "@commitlint/config-conventional": "20.4.4",
36
+ "@commitlint/cli": "20.5.0",
37
+ "@commitlint/config-conventional": "20.5.0",
37
38
  "eslint": "9.39.4",
38
- "eslint-config-scratch": "14.0.8",
39
+ "eslint-config-scratch": "14.0.11",
39
40
  "husky": "9.1.7",
40
41
  "prettier": "3.8.1",
41
42
  "scratch-semantic-release-config": "4.0.1",
@@ -79,13 +79,20 @@ class DelegateToParentDraggable implements Blockly.IDraggable {
79
79
  }
80
80
 
81
81
  /**
82
- * Class representing a draggable block that copies itself on drag.
82
+ * Class representing a draggable block that copies itself on drag, but only
83
+ * when the block is directly connected to a procedures_prototype block.
84
+ * When dragged from any other position, it behaves like a normal block.
83
85
  */
84
86
  class DuplicateOnDragDraggable implements Blockly.IDraggable {
85
87
  /**
86
- * The newly-created duplicate block.
88
+ * The block being dragged: a newly-created duplicate when dragging from a
89
+ * prototype, or the original block when dragging from elsewhere.
87
90
  */
88
91
  private copy?: Blockly.BlockSvg
92
+ /**
93
+ * Whether this drag is duplicating the block (true) or moving it (false).
94
+ */
95
+ private isDuplicating_ = false
89
96
  constructor(private block: Blockly.BlockSvg) {}
90
97
 
91
98
  /**
@@ -97,23 +104,41 @@ class DuplicateOnDragDraggable implements Blockly.IDraggable {
97
104
  }
98
105
 
99
106
  /**
100
- * Handles the start of a drag.
107
+ * Handles the start of a drag. If the block is directly connected to a
108
+ * procedures_prototype, creates a duplicate and drags that. Otherwise,
109
+ * switches to a normal drag strategy and drags the original block.
101
110
  * @param e The event that triggered the drag.
102
111
  */
103
112
  startDrag(e: PointerEvent) {
104
- const data = this.block.toCopyData()
105
- if (!data) {
106
- console.warn(
107
- 'DuplicateOnDragDraggable.startDrag: failed to serialize block for copy',
108
- this.block.type,
109
- this.block.id,
110
- )
111
- return
113
+ const parent = this.block.getParent()
114
+ this.isDuplicating_ = parent?.type === 'procedures_prototype'
115
+
116
+ if (this.isDuplicating_) {
117
+ const data = this.block.toCopyData()
118
+ if (!data) {
119
+ console.warn(
120
+ 'DuplicateOnDragDraggable.startDrag: failed to serialize block for copy',
121
+ this.block.type,
122
+ this.block.id,
123
+ )
124
+ return
125
+ }
126
+ this.copy = Blockly.clipboard.paste(data, this.block.workspace) as Blockly.BlockSvg
127
+ this.copy.setDeletable(true)
128
+ this.copy.setDragStrategy(new Blockly.dragging.BlockDragStrategy(this.copy))
129
+ this.copy.startDrag(e)
130
+ } else {
131
+ // Not in a prototype: drag the original block normally and replace this
132
+ // drag strategy so future drags also behave normally.
133
+ // Also ensure the block is deletable — reporters created by createArgumentReporter_
134
+ // are non-deletable by default, but one that has escaped a prototype should be
135
+ // cleanable by the user.
136
+ this.block.setDeletable(true)
137
+ const normalStrategy = new Blockly.dragging.BlockDragStrategy(this.block)
138
+ this.block.setDragStrategy(normalStrategy)
139
+ this.copy = this.block
140
+ normalStrategy.startDrag(e)
112
141
  }
113
- this.copy = Blockly.clipboard.paste(data, this.block.workspace) as Blockly.BlockSvg
114
- this.copy.setDeletable(true)
115
- this.copy.setDragStrategy(new Blockly.dragging.BlockDragStrategy(this.copy))
116
- this.copy.startDrag(e)
117
142
  }
118
143
 
119
144
  drag(newLoc: Blockly.utils.Coordinate, e?: PointerEvent) {
@@ -135,7 +160,11 @@ class DuplicateOnDragDraggable implements Blockly.IDraggable {
135
160
  }
136
161
 
137
162
  revertDrag() {
138
- this.copy?.dispose()
163
+ if (this.isDuplicating_) {
164
+ this.copy?.dispose()
165
+ } else {
166
+ this.copy?.revertDrag()
167
+ }
139
168
  }
140
169
 
141
170
  getRelativeToSurfaceXY() {
@@ -210,7 +239,34 @@ function definitionDomToMutation(this: ProcedurePrototypeBlock | ProcedureDeclar
210
239
  this.argumentIds_ = JSON.parse(xmlElement.getAttribute('argumentids')!)
211
240
  this.displayNames_ = JSON.parse(xmlElement.getAttribute('argumentnames')!)
212
241
  this.argumentDefaults_ = JSON.parse(xmlElement.getAttribute('argumentdefaults')!)
213
- this.updateDisplay_()
242
+
243
+ // During full XML deserialization (Blockly.Xml.domToWorkspace), the mutation element
244
+ // is part of the parsed XML tree and its parent element also contains <value> children
245
+ // for the argument reporters. Blockly will connect those child blocks AFTER the
246
+ // mutation runs. To avoid creating duplicate reporters here that would immediately
247
+ // be orphaned when the XML children connect, skip reporter creation and let the
248
+ // XML children provide them.
249
+ // We detect this case by checking that the parent element has <value> children.
250
+ // This distinguishes full deserialization from:
251
+ // - Programmatic mutation (xmlElement.parentElement is null — freshly created element)
252
+ // - Block creation from partial XML with no <value> children (e.g. createProcedureCallbackFactory)
253
+ // - JSON serialization / undo-redo (xmlElement.parentElement is null — textToDom result)
254
+ const xmlParent = xmlElement.parentElement
255
+ const hasXmlArgReporters =
256
+ this.type === 'procedures_prototype' &&
257
+ xmlParent !== null &&
258
+ Array.from(xmlParent.children).some((el) => el.tagName.toLowerCase() === 'value')
259
+ if (hasXmlArgReporters) {
260
+ ;(this as ProcedurePrototypeBlock).skipArgumentReporters_ = true
261
+ }
262
+ try {
263
+ this.updateDisplay_()
264
+ } finally {
265
+ if (hasXmlArgReporters) {
266
+ ;(this as ProcedurePrototypeBlock).skipArgumentReporters_ = false
267
+ }
268
+ }
269
+
214
270
  if ('updateArgumentReporterNames_' in this) {
215
271
  this.updateArgumentReporterNames_(prevArgIds, prevDisplayNames)
216
272
  }
@@ -521,6 +577,12 @@ function populateArgumentOnPrototype_(
521
577
  id: string,
522
578
  input: Blockly.Input,
523
579
  ) {
580
+ // During XML deserialization, skip connecting argument reporters here.
581
+ // The block's <value> XML children will connect the reporters after the mutation runs.
582
+ if (this.skipArgumentReporters_) {
583
+ return
584
+ }
585
+
524
586
  let oldBlock: Blockly.BlockSvg | null = null
525
587
  if (connectionMap && id in connectionMap) {
526
588
  const saveInfo = connectionMap[id]
@@ -876,6 +938,26 @@ function updateArgumentReporterNames_(
876
938
  }
877
939
  }
878
940
 
941
+ /**
942
+ * Override showContextMenu on an argument reporter block so that right-clicks
943
+ * while the block is seated inside a procedures_prototype delegate up to the
944
+ * prototype (which in turn delegates to the parent procedures_definition).
945
+ * When the reporter has been dragged out and lives elsewhere, it keeps its
946
+ * normal context menu.
947
+ * @param block The argument reporter block to configure.
948
+ */
949
+ function delegateContextMenuToPrototypeParent(block: Blockly.BlockSvg) {
950
+ const origShowContextMenu = block.showContextMenu.bind(block)
951
+ block.showContextMenu = function (this: Blockly.BlockSvg, e: Event) {
952
+ const parent = this.getParent()
953
+ if (parent?.type === 'procedures_prototype') {
954
+ parent.showContextMenu(e)
955
+ } else {
956
+ origShowContextMenu(e)
957
+ }
958
+ }
959
+ }
960
+
879
961
  Blockly.Blocks.procedures_definition = {
880
962
  /**
881
963
  * Block for defining a procedure with no return value.
@@ -943,12 +1025,25 @@ Blockly.Blocks.procedures_prototype = {
943
1025
  this.setDeletable(false)
944
1026
  this.setDragStrategy(new DelegateToParentDraggable(this))
945
1027
 
1028
+ // Delegate right-clicks to the parent define block so that "Add Comment",
1029
+ // "Edit", etc. act on the definition rather than the prototype itself.
1030
+ const protoOrigShowContextMenu = this.showContextMenu.bind(this)
1031
+ this.showContextMenu = function (this: Blockly.BlockSvg, e: Event) {
1032
+ const parent = this.getParent()
1033
+ if (parent) {
1034
+ parent.showContextMenu(e)
1035
+ } else {
1036
+ protoOrigShowContextMenu(e)
1037
+ }
1038
+ }
1039
+
946
1040
  /* Data known about the procedure. */
947
1041
  this.procCode_ = ''
948
1042
  this.displayNames_ = []
949
1043
  this.argumentIds_ = []
950
1044
  this.argumentDefaults_ = []
951
1045
  this.warp_ = false
1046
+ this.skipArgumentReporters_ = false
952
1047
 
953
1048
  // Shared.
954
1049
  this.getProcCode = getProcCode.bind(this)
@@ -1038,6 +1133,7 @@ Blockly.Blocks.argument_reporter_boolean = {
1038
1133
  extensions: ['colours_more', 'output_boolean'],
1039
1134
  })
1040
1135
  this.setDragStrategy(new DuplicateOnDragDraggable(this))
1136
+ delegateContextMenuToPrototypeParent(this)
1041
1137
  },
1042
1138
  }
1043
1139
 
@@ -1055,6 +1151,7 @@ Blockly.Blocks.argument_reporter_string_number = {
1055
1151
  extensions: ['colours_more', 'output_number', 'output_string'],
1056
1152
  })
1057
1153
  this.setDragStrategy(new DuplicateOnDragDraggable(this))
1154
+ delegateContextMenuToPrototypeParent(this)
1058
1155
  },
1059
1156
  }
1060
1157
 
@@ -1139,6 +1236,7 @@ interface ProcedureCallBlock extends ProcedureBlock {
1139
1236
  interface ProcedurePrototypeBlock extends ProcedureBlock {
1140
1237
  displayNames_: string[]
1141
1238
  argumentDefaults_: string[]
1239
+ skipArgumentReporters_: boolean
1142
1240
  createArgumentReporter_: (argumentType: ArgumentType, displayName: string) => Blockly.BlockSvg
1143
1241
  updateArgumentReporterNames_: (prevArgIds: string[], prevDisplayNames: string[]) => void
1144
1242
  }
@@ -182,7 +182,7 @@ const PROCEDURE_DEF_CONTEXTMENU = function (this: Blockly.Block) {
182
182
 
183
183
  // Find and remove the duplicate option
184
184
  for (let i = 0, option; (option = menuOptions[i]); i++) {
185
- if (option.text == Blockly.Msg.DUPLICATE) {
185
+ if (option.text == Blockly.Msg.DUPLICATE_BLOCK) {
186
186
  menuOptions.splice(i, 1)
187
187
  break
188
188
  }
package/src/css.ts CHANGED
@@ -17,6 +17,7 @@
17
17
  * limitations under the License.
18
18
  */
19
19
  import * as Blockly from 'blockly/core'
20
+ import { Colours } from './colours'
20
21
 
21
22
  const styles = `
22
23
  .blocklySvg {
@@ -287,9 +288,12 @@ const styles = `
287
288
  cursor: -moz-grabbing;
288
289
  }
289
290
 
290
- /* All the blocks being dragged get the blocklyDragging class, so match only the root one */
291
+ /* All the blocks being dragged get the blocklyDragging class, so match only the root one.
292
+ Using a CSS drop-shadow rather than an SVG filter reference avoids a Safari bug where
293
+ url("#blocklyDragShadowFilter") fails to resolve across SVG document boundaries when
294
+ the block is on the drag surface (a separate <svg> from the main workspace). */
291
295
  :not(.blocklyDragging) > .blocklyDragging {
292
- filter: url(#blocklyDragShadowFilter);
296
+ filter: drop-shadow(0px 0px 6px rgba(0, 0, 0, ${Colours.dragShadowOpacity}));
293
297
  }
294
298
 
295
299
  /* Changes cursor on mouse down. Not effective in Firefox because of
@@ -1208,6 +1212,15 @@ const styles = `
1208
1212
  .blocklyInsertionMarker > g:not(:last-child) {
1209
1213
  visibility: hidden;
1210
1214
  }
1215
+
1216
+ /* Safari does not expand SVG elements to fill their absolutely-positioned
1217
+ container using top/left/right/bottom alone — it uses the SVG intrinsic
1218
+ size (300x150) instead. Explicit width/height ensures the drag surface SVG
1219
+ fills the injection div so dragged blocks are not clipped. */
1220
+ .blocklyBlockDragSurface, .blocklyAnimationLayer {
1221
+ width: 100%;
1222
+ height: 100%;
1223
+ }
1211
1224
  `
1212
1225
 
1213
1226
  Blockly.Css.register(styles)
package/src/index.ts CHANGED
@@ -56,7 +56,6 @@ import './scratch_insertion_marker_previewer'
56
56
  import './scratch_variable_map'
57
57
  import './scratch_variable_model'
58
58
  import { ScratchZoomControls } from './scratch_zoom_controls'
59
- import { buildShadowFilter } from './shadows'
60
59
  import { registerStatusIndicatorLabelFlyoutInflater } from './status_indicator_label_flyout_inflater'
61
60
  import * as ScratchVariables from './variables'
62
61
 
@@ -120,7 +119,6 @@ export function inject(container: Element, options: ScratchBlocksOptions) {
120
119
  const workspace = Blockly.inject(container, options)
121
120
 
122
121
  buildGlowFilter(workspace)
123
- buildShadowFilter(workspace)
124
122
 
125
123
  // Replace Blockly's sprite-sheet zoom controls with SVG-file-based ones so
126
124
  // each button is a separate file and the correct set is picked up via
@@ -167,6 +165,16 @@ Blockly.FlyoutButton.TEXT_MARGIN_Y = 10
167
165
  Blockly.ContextMenuRegistry.registry.unregister('blockDisable')
168
166
  Blockly.ContextMenuRegistry.registry.unregister('blockInline')
169
167
  Blockly.ContextMenuItems.registerCommentOptions()
168
+ // Blockly hides "Add Comment" for simple reporters because comments can't be
169
+ // read in the default renderer. In Scratch they're shown differently, so
170
+ // remove that restriction by dropping the isFullBlockField check.
171
+ Blockly.ContextMenuRegistry.registry.getItem('blockComment')!.preconditionFn = (scope) => {
172
+ const block = scope.block
173
+ if (block && !block.isInFlyout && block.workspace.options.comments && !block.isCollapsed() && block.isEditable()) {
174
+ return 'enabled'
175
+ }
176
+ return 'hidden'
177
+ }
170
178
  Blockly.ContextMenuRegistry.registry.unregister('blockDelete')
171
179
  contextMenuItems.registerDeleteBlock()
172
180
  contextMenuItems.registerDuplicateBlock()
@@ -38,6 +38,13 @@ class ScratchConnectionChecker extends Blockly.ConnectionChecker {
38
38
  return false
39
39
  }
40
40
 
41
+ // Prevent dragging any block into a procedures_prototype input. Argument
42
+ // reporters inside the prototype are managed programmatically and should
43
+ // not be displaceable by user drag-and-drop.
44
+ if (b.getSourceBlock().type === 'procedures_prototype') {
45
+ return false
46
+ }
47
+
41
48
  return super.doDragChecks(a, b, distance)
42
49
  }
43
50
  }
@@ -1,12 +0,0 @@
1
- /**
2
- * Copyright 2024 Google LLC
3
- * SPDX-License-Identifier: Apache-2.0
4
- */
5
- import * as Blockly from 'blockly/core';
6
- /**
7
- * Creates an SVG filter to apply drop shadows to blocks being dragged and
8
- * inserts it into the DOM.
9
- * @param workspace The workspace whose SVG will receive the filter definition.
10
- */
11
- export declare function buildShadowFilter(workspace: Blockly.WorkspaceSvg): void;
12
- //# sourceMappingURL=shadows.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"shadows.d.ts","sourceRoot":"","sources":["../../../src/shadows.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAGvC;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,OAAO,CAAC,YAAY,QA+ChE"}
package/src/shadows.ts DELETED
@@ -1,60 +0,0 @@
1
- /**
2
- * Copyright 2024 Google LLC
3
- * SPDX-License-Identifier: Apache-2.0
4
- */
5
- import * as Blockly from 'blockly/core'
6
- import { Colours } from './colours'
7
-
8
- /**
9
- * Creates an SVG filter to apply drop shadows to blocks being dragged and
10
- * inserts it into the DOM.
11
- * @param workspace The workspace whose SVG will receive the filter definition.
12
- */
13
- export function buildShadowFilter(workspace: Blockly.WorkspaceSvg) {
14
- const svg = workspace.getParentSvg()
15
- const defs = Blockly.utils.dom.createSvgElement(Blockly.utils.Svg.DEFS, {}, svg)
16
- // Adjust these width/height, x/y properties to stop the shadow from clipping
17
- const dragShadowFilter = Blockly.utils.dom.createSvgElement(
18
- 'filter',
19
- {
20
- id: 'blocklyDragShadowFilter',
21
- height: '140%',
22
- width: '140%',
23
- y: '-20%',
24
- x: '-20%',
25
- },
26
- defs,
27
- )
28
- Blockly.utils.dom.createSvgElement(
29
- 'feGaussianBlur',
30
- {
31
- in: 'SourceAlpha',
32
- stdDeviation: '6',
33
- },
34
- dragShadowFilter,
35
- )
36
- const componentTransfer = Blockly.utils.dom.createSvgElement(
37
- 'feComponentTransfer',
38
- { result: 'offsetBlur' },
39
- dragShadowFilter,
40
- )
41
- // Shadow opacity is specified in the adjustable colour library,
42
- // since the darkness of the shadow largely depends on the workspace colour.
43
- Blockly.utils.dom.createSvgElement(
44
- 'feFuncA',
45
- {
46
- type: 'linear',
47
- slope: Colours.dragShadowOpacity,
48
- },
49
- componentTransfer,
50
- )
51
- Blockly.utils.dom.createSvgElement(
52
- 'feComposite',
53
- {
54
- in: 'SourceGraphic',
55
- in2: 'offsetBlur',
56
- operator: 'over',
57
- },
58
- dragShadowFilter,
59
- )
60
- }