scratch-blocks 2.1.3 → 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;AAikCD,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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scratch-blocks",
3
- "version": "2.1.3",
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",
@@ -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]
@@ -981,6 +1043,7 @@ Blockly.Blocks.procedures_prototype = {
981
1043
  this.argumentIds_ = []
982
1044
  this.argumentDefaults_ = []
983
1045
  this.warp_ = false
1046
+ this.skipArgumentReporters_ = false
984
1047
 
985
1048
  // Shared.
986
1049
  this.getProcCode = getProcCode.bind(this)
@@ -1173,6 +1236,7 @@ interface ProcedureCallBlock extends ProcedureBlock {
1173
1236
  interface ProcedurePrototypeBlock extends ProcedureBlock {
1174
1237
  displayNames_: string[]
1175
1238
  argumentDefaults_: string[]
1239
+ skipArgumentReporters_: boolean
1176
1240
  createArgumentReporter_: (argumentType: ArgumentType, displayName: string) => Blockly.BlockSvg
1177
1241
  updateArgumentReporterNames_: (prevArgIds: string[], prevDisplayNames: string[]) => void
1178
1242
  }
package/src/index.ts CHANGED
@@ -165,6 +165,16 @@ Blockly.FlyoutButton.TEXT_MARGIN_Y = 10
165
165
  Blockly.ContextMenuRegistry.registry.unregister('blockDisable')
166
166
  Blockly.ContextMenuRegistry.registry.unregister('blockInline')
167
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
+ }
168
178
  Blockly.ContextMenuRegistry.registry.unregister('blockDelete')
169
179
  contextMenuItems.registerDeleteBlock()
170
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
  }