scratch-blocks 2.0.1 → 2.0.2

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.
Files changed (85) hide show
  1. package/AGENTS.md +140 -0
  2. package/dist/main.mjs +1 -1
  3. package/dist/types/src/blocks/procedures.d.ts +1 -1
  4. package/dist/types/src/blocks/procedures.d.ts.map +1 -1
  5. package/dist/types/src/events/events_block_comment_base.d.ts.map +1 -1
  6. package/dist/types/src/events/events_block_drag_end.d.ts.map +1 -1
  7. package/dist/types/src/events/events_block_drag_outside.d.ts.map +1 -1
  8. package/dist/types/src/fields/field_colour_slider.d.ts +4 -4
  9. package/dist/types/src/fields/field_colour_slider.d.ts.map +1 -1
  10. package/dist/types/src/fields/field_matrix.d.ts.map +1 -1
  11. package/dist/types/src/fields/field_note.d.ts.map +1 -1
  12. package/dist/types/src/fields/scratch_field_number.d.ts.map +1 -1
  13. package/dist/types/src/fields/scratch_field_variable.d.ts +1 -1
  14. package/dist/types/src/fields/scratch_field_variable.d.ts.map +1 -1
  15. package/dist/types/src/flyout_checkbox_icon.d.ts.map +1 -1
  16. package/dist/types/src/procedures.d.ts.map +1 -1
  17. package/dist/types/src/renderer/cat/cat_face.d.ts.map +1 -1
  18. package/dist/types/src/renderer/cat/drawer.d.ts +1 -1
  19. package/dist/types/src/renderer/cat/drawer.d.ts.map +1 -1
  20. package/dist/types/src/renderer/cat/render_info.d.ts.map +1 -1
  21. package/dist/types/src/renderer/cat/renderer.d.ts +1 -1
  22. package/dist/types/src/renderer/cat/renderer.d.ts.map +1 -1
  23. package/dist/types/src/renderer/constants.d.ts +41 -0
  24. package/dist/types/src/renderer/constants.d.ts.map +1 -0
  25. package/dist/types/src/renderer/drawer.d.ts +2 -2
  26. package/dist/types/src/renderer/drawer.d.ts.map +1 -1
  27. package/dist/types/src/renderer/render_info.d.ts.map +1 -1
  28. package/dist/types/src/renderer/renderer.d.ts +1 -1
  29. package/dist/types/src/renderer/renderer.d.ts.map +1 -1
  30. package/dist/types/src/scratch_comment_bubble.d.ts +2 -2
  31. package/dist/types/src/scratch_comment_bubble.d.ts.map +1 -1
  32. package/dist/types/src/scratch_comment_icon.d.ts +1 -1
  33. package/dist/types/src/scratch_comment_icon.d.ts.map +1 -1
  34. package/dist/types/src/scratch_dragger.d.ts +1 -1
  35. package/dist/types/src/scratch_variable_model.d.ts +1 -1
  36. package/dist/types/src/scratch_variable_model.d.ts.map +1 -1
  37. package/dist/types/tests/jsunit/connection_db_test.d.ts +3 -3
  38. package/package.json +8 -8
  39. package/src/block_reporting.ts +2 -2
  40. package/src/blocks/control.ts +9 -2
  41. package/src/blocks/data.ts +34 -6
  42. package/src/blocks/procedures.ts +49 -31
  43. package/src/context_menu_items.ts +7 -7
  44. package/src/data_category.ts +4 -4
  45. package/src/events/events_block_comment_base.ts +8 -5
  46. package/src/events/events_block_comment_change.ts +4 -4
  47. package/src/events/events_block_comment_collapse.ts +14 -2
  48. package/src/events/events_block_comment_create.ts +4 -1
  49. package/src/events/events_block_comment_delete.ts +4 -2
  50. package/src/events/events_block_comment_move.ts +4 -4
  51. package/src/events/events_block_comment_resize.ts +4 -4
  52. package/src/events/events_block_drag_end.ts +4 -4
  53. package/src/events/events_block_drag_outside.ts +2 -2
  54. package/src/events/events_scratch_variable_create.ts +20 -2
  55. package/src/fields/field_colour_slider.ts +53 -28
  56. package/src/fields/field_matrix.ts +9 -8
  57. package/src/fields/field_note.ts +34 -27
  58. package/src/fields/field_textinput_removable.ts +2 -2
  59. package/src/fields/field_variable_getter.ts +5 -5
  60. package/src/fields/field_vertical_separator.ts +1 -1
  61. package/src/fields/scratch_field_angle.ts +14 -14
  62. package/src/fields/scratch_field_dropdown.ts +2 -2
  63. package/src/fields/scratch_field_number.ts +13 -12
  64. package/src/fields/scratch_field_variable.ts +8 -5
  65. package/src/flyout_checkbox_icon.ts +1 -1
  66. package/src/glows.ts +2 -2
  67. package/src/procedures.ts +25 -17
  68. package/src/renderer/cat/cat_face.ts +1 -1
  69. package/src/renderer/cat/drawer.ts +3 -3
  70. package/src/renderer/cat/render_info.ts +2 -2
  71. package/src/renderer/cat/renderer.ts +2 -2
  72. package/src/renderer/constants.ts +8 -8
  73. package/src/renderer/drawer.ts +2 -2
  74. package/src/renderer/render_info.ts +7 -4
  75. package/src/renderer/renderer.ts +2 -2
  76. package/src/scratch_block_paster.ts +1 -1
  77. package/src/scratch_comment_bubble.ts +16 -14
  78. package/src/scratch_comment_icon.ts +1 -1
  79. package/src/scratch_dragger.ts +2 -2
  80. package/src/scratch_variable_model.ts +2 -2
  81. package/src/status_indicator_label.ts +3 -3
  82. package/src/status_indicator_label_flyout_inflater.ts +1 -1
  83. package/src/variables.ts +7 -7
  84. package/src/xml.ts +3 -3
  85. package/tsconfig.json +1 -1
@@ -39,7 +39,7 @@ enum LEDState {
39
39
  * Class for a matrix field.
40
40
  */
41
41
  class FieldMatrix extends Blockly.Field<string> {
42
- private originalStyle?: string;
42
+ private originalStyle = "";
43
43
 
44
44
  /**
45
45
  * Array of SVGElement<rect> for matrix thumbnail image on block field.
@@ -215,7 +215,7 @@ class FieldMatrix extends Blockly.Field<string> {
215
215
  this.arrow_.setAttributeNS(
216
216
  "http://www.w3.org/1999/xlink",
217
217
  "xlink:href",
218
- this.getConstants().FIELD_DROPDOWN_SVG_ARROW_DATAURI
218
+ this.getConstants()!.FIELD_DROPDOWN_SVG_ARROW_DATAURI
219
219
  );
220
220
  this.arrow_.style.cursor = "default";
221
221
  }
@@ -348,7 +348,7 @@ class FieldMatrix extends Blockly.Field<string> {
348
348
  }
349
349
 
350
350
  dropdownDispose_() {
351
- const sourceBlock = this.getSourceBlock();
351
+ const sourceBlock = this.getSourceBlock()!;
352
352
  if (sourceBlock.isShadow()) {
353
353
  sourceBlock.setStyle(this.originalStyle);
354
354
  }
@@ -396,8 +396,8 @@ class FieldMatrix extends Blockly.Field<string> {
396
396
  * Redraw the matrix with the current value.
397
397
  */
398
398
  private updateMatrix_() {
399
- const matrix = this.getValue();
400
- const sourceBlock = this.getSourceBlock() as Blockly.BlockSvg;
399
+ const matrix = this.getValue()!;
400
+ const sourceBlock = this.getSourceBlock()! as Blockly.BlockSvg;
401
401
  for (let i = 0; i < matrix.length; i++) {
402
402
  if (matrix[i] === LEDState.OFF) {
403
403
  this.fillMatrixNode_(
@@ -451,7 +451,7 @@ class FieldMatrix extends Blockly.Field<string> {
451
451
 
452
452
  setLEDNode_(led: number, state: LEDState) {
453
453
  if (led < 0 || led > 24) return;
454
- const oldMatrix = this.getValue();
454
+ const oldMatrix = this.getValue()!;
455
455
  const newMatrix =
456
456
  oldMatrix.substr(0, led) + state + oldMatrix.substr(led + 1);
457
457
  this.setValue(newMatrix);
@@ -469,7 +469,7 @@ class FieldMatrix extends Blockly.Field<string> {
469
469
 
470
470
  toggleLEDNode_(led: number) {
471
471
  if (led < 0 || led > 24) return;
472
- if (this.getValue().charAt(led) === LEDState.OFF) {
472
+ if (this.getValue()!.charAt(led) === LEDState.OFF) {
473
473
  this.setLEDNode_(led, LEDState.ON);
474
474
  } else {
475
475
  this.setLEDNode_(led, LEDState.OFF);
@@ -496,7 +496,7 @@ class FieldMatrix extends Blockly.Field<string> {
496
496
  );
497
497
  const ledHit = this.checkForLED_(e);
498
498
  if (ledHit > -1) {
499
- if (this.getValue().charAt(ledHit) === LEDState.OFF) {
499
+ if (this.getValue()!.charAt(ledHit) === LEDState.OFF) {
500
500
  this.paintStyle_ = PaintStyle.FILL;
501
501
  } else {
502
502
  this.paintStyle_ = PaintStyle.CLEAR;
@@ -548,6 +548,7 @@ class FieldMatrix extends Blockly.Field<string> {
548
548
  * @returns The matching matrix node or -1 for none.
549
549
  */
550
550
  checkForLED_(e: PointerEvent): number {
551
+ if (!this.matrixStage_) return -1;
551
552
  const bBox = this.matrixStage_.getBoundingClientRect();
552
553
  const nodeSize = FieldMatrix.MATRIX_NODE_SIZE;
553
554
  const nodePad = FieldMatrix.MATRIX_NODE_PAD;
@@ -369,7 +369,7 @@ export class FieldNote extends Blockly.FieldTextInput {
369
369
  "line",
370
370
  {
371
371
  stroke: (
372
- this.sourceBlock_.getParent() as Blockly.BlockSvg
372
+ this.sourceBlock_!.getParent() as Blockly.BlockSvg
373
373
  ).getColourTertiary(),
374
374
  x1: 0,
375
375
  y1: FieldNote.TOP_MENU_HEIGHT,
@@ -407,7 +407,7 @@ export class FieldNote extends Blockly.FieldTextInput {
407
407
  octaveDownButton,
408
408
  "mousedown",
409
409
  this,
410
- function () {
410
+ () => {
411
411
  this.changeOctaveBy_(-1);
412
412
  }
413
413
  );
@@ -415,14 +415,14 @@ export class FieldNote extends Blockly.FieldTextInput {
415
415
  octaveUpButton,
416
416
  "mousedown",
417
417
  this,
418
- function () {
418
+ () => {
419
419
  this.changeOctaveBy_(1);
420
420
  }
421
421
  );
422
422
  const sourceBlock = this.getSourceBlock() as Blockly.BlockSvg;
423
423
  Blockly.DropDownDiv.setColour(
424
- sourceBlock.getParent().getColour(),
425
- sourceBlock.getParent().getColourTertiary()
424
+ (sourceBlock.getParent() as Blockly.BlockSvg).getColour(),
425
+ (sourceBlock.getParent() as Blockly.BlockSvg).getColourTertiary()
426
426
  );
427
427
  Blockly.DropDownDiv.showPositionedByBlock(this, sourceBlock);
428
428
 
@@ -441,7 +441,7 @@ export class FieldNote extends Blockly.FieldTextInput {
441
441
  x: number,
442
442
  whiteKeyGroup: SVGElement,
443
443
  blackKeyGroup: SVGElement,
444
- keySVGarray: SVGElement[]
444
+ keySVGarray: SVGElement[] | null
445
445
  ) {
446
446
  let xIncrement, width, height, fill, stroke, group;
447
447
  x += FieldNote.EDGE_PADDING / 2;
@@ -463,7 +463,7 @@ export class FieldNote extends Blockly.FieldTextInput {
463
463
  height = FieldNote.WHITE_KEY_HEIGHT;
464
464
  fill = FieldNote.WHITE_KEY_COLOR;
465
465
  stroke = (
466
- this.sourceBlock_.getParent() as Blockly.BlockSvg
466
+ this.sourceBlock_!.getParent() as Blockly.BlockSvg
467
467
  ).getColourTertiary();
468
468
  group = whiteKeyGroup;
469
469
  }
@@ -594,7 +594,7 @@ export class FieldNote extends Blockly.FieldTextInput {
594
594
  "line",
595
595
  {
596
596
  stroke: (
597
- this.sourceBlock_.getParent() as Blockly.BlockSvg
597
+ this.sourceBlock_!.getParent() as Blockly.BlockSvg
598
598
  ).getColourTertiary(),
599
599
  x1: x - FieldNote.INSET,
600
600
  y1: 0,
@@ -643,13 +643,15 @@ export class FieldNote extends Blockly.FieldTextInput {
643
643
  * @param visible If true, set labels to be visible.
644
644
  */
645
645
  private setCKeyLabelsVisible_(visible: boolean) {
646
- if (visible) {
647
- this.fadeSvgToOpacity_(this.lowCText_, 1);
648
- this.fadeSvgToOpacity_(this.highCText_, 1);
649
- } else {
650
- this.fadeSvgToOpacity_(this.lowCText_, 0);
651
- this.fadeSvgToOpacity_(this.highCText_, 0);
646
+ if (!this.lowCText_ || !this.highCText_) {
647
+ console.warn(
648
+ "FieldNote.setCKeyLabelsVisible_: C-key label DOM is not fully initialized"
649
+ );
650
+ return;
652
651
  }
652
+ const opacity = visible ? 1 : 0;
653
+ this.fadeSvgToOpacity_(this.lowCText_, opacity);
654
+ this.fadeSvgToOpacity_(this.highCText_, opacity);
653
655
  }
654
656
 
655
657
  /**
@@ -686,8 +688,10 @@ export class FieldNote extends Blockly.FieldTextInput {
686
688
  */
687
689
  private onMouseUp_() {
688
690
  this.mouseIsDown_ = false;
689
- Blockly.browserEvents.unbind(this.mouseUpWrapper_);
690
- this.mouseUpWrapper_ = null;
691
+ if (this.mouseUpWrapper_) {
692
+ Blockly.browserEvents.unbind(this.mouseUpWrapper_);
693
+ this.mouseUpWrapper_ = null;
694
+ }
691
695
  }
692
696
 
693
697
  /**
@@ -709,7 +713,7 @@ export class FieldNote extends Blockly.FieldTextInput {
709
713
  private selectNoteWithMouseEvent_(e: PointerEvent) {
710
714
  const newNoteNum =
711
715
  Number((e.target as HTMLElement).getAttribute("data-pitch")) +
712
- this.displayedOctave_ * 12;
716
+ (this.displayedOctave_ ?? 0) * 12;
713
717
  this.setEditorValue_(newNoteNum);
714
718
  this.playNoteInternal_();
715
719
  }
@@ -719,7 +723,7 @@ export class FieldNote extends Blockly.FieldTextInput {
719
723
  */
720
724
  private playNoteInternal_() {
721
725
  if (FieldNote.playNote_) {
722
- FieldNote.playNote_(Number(this.getValue()), "Music");
726
+ FieldNote.playNote_(Number(this.getValue()!), "Music");
723
727
  }
724
728
  }
725
729
 
@@ -740,7 +744,7 @@ export class FieldNote extends Blockly.FieldTextInput {
740
744
  * @param octaves The number of octaves to change by.
741
745
  */
742
746
  private changeOctaveBy_(octaves: number) {
743
- this.displayedOctave_ += octaves;
747
+ this.displayedOctave_ = (this.displayedOctave_ ?? 0) + octaves;
744
748
  if (this.displayedOctave_ < 0) {
745
749
  this.displayedOctave_ = 0;
746
750
  return;
@@ -766,7 +770,7 @@ export class FieldNote extends Blockly.FieldTextInput {
766
770
  private stepOctaveAnimation_() {
767
771
  const absDiff = Math.abs(this.animationPos_ - this.animationTarget_);
768
772
  if (absDiff < 1) {
769
- this.pianoSVG_.setAttribute("transform", "translate(0, 0)");
773
+ this.pianoSVG_?.setAttribute("transform", "translate(0, 0)");
770
774
  this.setCKeyLabelsVisible_(true);
771
775
  this.playNoteInternal_();
772
776
  return;
@@ -774,7 +778,7 @@ export class FieldNote extends Blockly.FieldTextInput {
774
778
  this.animationPos_ +=
775
779
  (this.animationTarget_ - this.animationPos_) *
776
780
  FieldNote.ANIMATION_FRACTION;
777
- this.pianoSVG_.setAttribute(
781
+ this.pianoSVG_?.setAttribute(
778
782
  "transform",
779
783
  "translate(" + this.animationPos_ + ",0)"
780
784
  );
@@ -799,7 +803,7 @@ export class FieldNote extends Blockly.FieldTextInput {
799
803
  * @returns The index of the piano key.
800
804
  */
801
805
  private noteNumToKeyIndex_(noteNum: number): number {
802
- return Math.floor(noteNum) - this.displayedOctave_ * 12;
806
+ return Math.floor(noteNum) - (this.displayedOctave_ ?? 0) * 12;
803
807
  }
804
808
 
805
809
  /**
@@ -833,12 +837,15 @@ export class FieldNote extends Blockly.FieldTextInput {
833
837
  this.keySVGs_[index].setAttribute("fill", FieldNote.KEY_SELECTED_COLOR);
834
838
  // Update the note name text
835
839
  const noteName = FieldNote.KEY_INFO[index].name;
836
- this.noteNameText_.textContent =
837
- noteName + " (" + Math.floor(noteNum) + ")";
840
+ if (this.noteNameText_) {
841
+ this.noteNameText_.textContent =
842
+ noteName + " (" + Math.floor(noteNum) + ")";
843
+ }
838
844
  // Update the low and high C note names
839
- const lowCNum = this.displayedOctave_ * 12;
840
- this.lowCText_.textContent = "C(" + lowCNum + ")";
841
- this.highCText_.textContent = "C(" + (lowCNum + 12) + ")";
845
+ const lowCNum = (this.displayedOctave_ ?? 0) * 12;
846
+ if (this.lowCText_) this.lowCText_.textContent = "C(" + lowCNum + ")";
847
+ if (this.highCText_)
848
+ this.highCText_.textContent = "C(" + (lowCNum + 12) + ")";
842
849
  }
843
850
  }
844
851
 
@@ -40,13 +40,13 @@ export class FieldTextInputRemovable extends Blockly.FieldTextInput {
40
40
  Blockly.renderManagement.finishQueuedRenders().then(() => {
41
41
  super.showEditor_();
42
42
 
43
- const div = Blockly.WidgetDiv.getDiv();
43
+ const div = Blockly.WidgetDiv.getDiv()!;
44
44
  div.className += " removableTextInput";
45
45
  const removeButton = document.createElement("img");
46
46
  removeButton.className = "blocklyTextRemoveIcon";
47
47
  removeButton.setAttribute(
48
48
  "src",
49
- this.sourceBlock_.workspace.options.pathToMedia + "icons/remove.svg"
49
+ this.sourceBlock_!.workspace.options.pathToMedia + "icons/remove.svg"
50
50
  );
51
51
  this.removeButtonMouseWrapper_ = Blockly.browserEvents.bind(
52
52
  removeButton,
@@ -78,7 +78,7 @@ class FieldVariableGetter extends Blockly.FieldLabel {
78
78
  */
79
79
  doValueUpdate_(newVariableId: string) {
80
80
  super.doValueUpdate_(newVariableId);
81
- const workspace = this.getSourceBlock().workspace;
81
+ const workspace = this.getSourceBlock()!.workspace;
82
82
  this.variable = Blockly.Variables.getVariable(workspace, newVariableId);
83
83
  }
84
84
 
@@ -97,13 +97,13 @@ class FieldVariableGetter extends Blockly.FieldLabel {
97
97
  }
98
98
 
99
99
  fromXml(element: Element) {
100
- this.setValue(element.getAttribute("id"));
100
+ this.setValue(element.getAttribute("id")!);
101
101
  }
102
102
 
103
103
  toXml(element: Element): Element {
104
- element.setAttribute("id", this.variable.getId());
105
- element.setAttribute("variabletype", this.variable.getType());
106
- element.textContent = this.variable.getName();
104
+ element.setAttribute("id", this.variable!.getId());
105
+ element.setAttribute("variabletype", this.variable!.getType());
106
+ element.textContent = this.variable!.getName();
107
107
  return element;
108
108
  }
109
109
  }
@@ -76,7 +76,7 @@ class FieldVerticalSeparator extends Blockly.Field {
76
76
  * @package
77
77
  */
78
78
  setLineHeight(newHeight: number) {
79
- this.lineElement.setAttribute("y2", `${newHeight}`);
79
+ this.lineElement!.setAttribute("y2", `${newHeight}`);
80
80
  }
81
81
 
82
82
  /**
@@ -51,17 +51,17 @@ class ScratchFieldAngle extends Blockly.FieldNumber {
51
51
  /**
52
52
  * Opaque identifier used to unbind event listener in dispose().
53
53
  */
54
- private mouseDownWrapper_: Blockly.browserEvents.Data;
54
+ private mouseDownWrapper_!: Blockly.browserEvents.Data;
55
55
 
56
56
  /**
57
57
  * Opaque identifier used to unbind event listener in dispose().
58
58
  */
59
- private mouseMoveWrapper: Blockly.browserEvents.Data;
59
+ private mouseMoveWrapper!: Blockly.browserEvents.Data;
60
60
 
61
61
  /**
62
62
  * Opaque identifier used to unbind event listener in dispose().
63
63
  */
64
- private mouseUpWrapper: Blockly.browserEvents.Data;
64
+ private mouseUpWrapper!: Blockly.browserEvents.Data;
65
65
 
66
66
  /**
67
67
  * Round angles to the nearest 15 degrees when using mouse.
@@ -141,7 +141,7 @@ class ScratchFieldAngle extends Blockly.FieldNumber {
141
141
  */
142
142
  dispose() {
143
143
  super.dispose();
144
- this.gauge = null;
144
+ this.gauge = undefined;
145
145
  if (this.mouseDownWrapper_) {
146
146
  Blockly.browserEvents.unbind(this.mouseDownWrapper_);
147
147
  }
@@ -190,10 +190,10 @@ class ScratchFieldAngle extends Blockly.FieldNumber {
190
190
  cy: this.HALF,
191
191
  r: this.RADIUS,
192
192
  fill: (
193
- this.getSourceBlock().getParent() as Blockly.BlockSvg
193
+ this.getSourceBlock()!.getParent() as Blockly.BlockSvg
194
194
  ).getColourSecondary(),
195
195
  stroke: (
196
- this.getSourceBlock().getParent() as Blockly.BlockSvg
196
+ this.getSourceBlock()!.getParent() as Blockly.BlockSvg
197
197
  ).getColourTertiary(),
198
198
  class: "blocklyAngleCircle",
199
199
  },
@@ -284,9 +284,9 @@ class ScratchFieldAngle extends Blockly.FieldNumber {
284
284
  );
285
285
 
286
286
  Blockly.DropDownDiv.setColour(
287
- this.getSourceBlock().getParent().getColour(),
287
+ (this.getSourceBlock()!.getParent() as Blockly.BlockSvg).getColour(),
288
288
  (
289
- this.getSourceBlock().getParent() as Blockly.BlockSvg
289
+ this.getSourceBlock()!.getParent() as Blockly.BlockSvg
290
290
  ).getColourTertiary()
291
291
  );
292
292
  Blockly.DropDownDiv.showPositionedByBlock(
@@ -336,7 +336,7 @@ class ScratchFieldAngle extends Blockly.FieldNumber {
336
336
  */
337
337
  onMouseMove(e: PointerEvent) {
338
338
  e.preventDefault();
339
- const bBox = this.gauge.ownerSVGElement.getBoundingClientRect();
339
+ const bBox = this.gauge!.ownerSVGElement!.getBoundingClientRect();
340
340
  const dx = e.clientX - bBox.left - this.HALF;
341
341
  const dy = e.clientY - bBox.top - this.HALF;
342
342
  let angle = Math.atan(-dy / dx);
@@ -418,12 +418,12 @@ class ScratchFieldAngle extends Blockly.FieldNumber {
418
418
  } else {
419
419
  imageRotation = -angleDegrees;
420
420
  }
421
- this.arrow.setAttribute("transform", "rotate(" + imageRotation + ")");
421
+ this.arrow!.setAttribute("transform", "rotate(" + imageRotation + ")");
422
422
  }
423
- this.gauge.setAttribute("d", path.join(""));
424
- this.line.setAttribute("x2", `${x2}`);
425
- this.line.setAttribute("y2", `${y2}`);
426
- this.handle.setAttribute("transform", "translate(" + x2 + "," + y2 + ")");
423
+ this.gauge!.setAttribute("d", path.join(""));
424
+ this.line!.setAttribute("x2", `${x2}`);
425
+ this.line!.setAttribute("y2", `${y2}`);
426
+ this.handle!.setAttribute("transform", "translate(" + x2 + "," + y2 + ")");
427
427
  }
428
428
 
429
429
  /**
@@ -7,7 +7,7 @@
7
7
  import * as Blockly from "blockly/core";
8
8
 
9
9
  class ScratchFieldDropdown extends Blockly.FieldDropdown {
10
- private originalStyle: string;
10
+ private originalStyle!: string;
11
11
 
12
12
  showEditor_(event: PointerEvent) {
13
13
  super.showEditor_(event);
@@ -28,7 +28,7 @@ class ScratchFieldDropdown extends Blockly.FieldDropdown {
28
28
 
29
29
  dropdownDispose_() {
30
30
  super.dropdownDispose_();
31
- const sourceBlock = this.getSourceBlock();
31
+ const sourceBlock = this.getSourceBlock()!;
32
32
  if (sourceBlock.isShadow()) {
33
33
  sourceBlock.setStyle(this.originalStyle);
34
34
  }
@@ -141,7 +141,7 @@ class ScratchFieldNumber extends Blockly.FieldTextInput {
141
141
 
142
142
  // Show a numeric keypad in the drop-down on touch
143
143
  if (showNumPad) {
144
- this.htmlInput_.select();
144
+ this.htmlInput_!.select();
145
145
  this.showNumPad_();
146
146
  }
147
147
  }
@@ -173,7 +173,7 @@ class ScratchFieldNumber extends Blockly.FieldTextInput {
173
173
  // Set colour and size of drop-down
174
174
  const sourceBlock = this.getSourceBlock() as Blockly.BlockSvg;
175
175
  Blockly.DropDownDiv.setColour(
176
- sourceBlock.getParent().getColour(),
176
+ (sourceBlock.getParent() as Blockly.BlockSvg).getColour(),
177
177
  sourceBlock.getColourTertiary()
178
178
  );
179
179
  contentDiv.style.width = ScratchFieldNumber.DROPDOWN_WIDTH + "px";
@@ -205,11 +205,12 @@ class ScratchFieldNumber extends Blockly.FieldTextInput {
205
205
  );
206
206
  Blockly.DropDownDiv.show(
207
207
  this,
208
- this.getSourceBlock().RTL,
208
+ this.getSourceBlock()!.RTL,
209
209
  primaryX,
210
210
  primaryY,
211
211
  secondaryX,
212
212
  secondaryY,
213
+ false,
213
214
  this.onHide_.bind(this)
214
215
  );
215
216
  }
@@ -222,8 +223,8 @@ class ScratchFieldNumber extends Blockly.FieldTextInput {
222
223
  */
223
224
  private addButtons_(contentDiv: Element) {
224
225
  const sourceBlock = this.getSourceBlock() as Blockly.BlockSvg;
225
- const buttonColour = sourceBlock.getParent().getColour();
226
- const buttonBorderColour = sourceBlock.getParent().getColourTertiary();
226
+ const buttonColour = (sourceBlock.getParent() as Blockly.BlockSvg).getColour();
227
+ const buttonBorderColour = (sourceBlock.getParent() as Blockly.BlockSvg).getColourTertiary();
227
228
 
228
229
  // Add numeric keypad buttons
229
230
  const buttons = ScratchFieldNumber.NUMPAD_BUTTONS;
@@ -298,10 +299,10 @@ class ScratchFieldNumber extends Blockly.FieldTextInput {
298
299
  // String of the button (e.g., '7')
299
300
  const spliceValue = (e.target as HTMLElement).innerText;
300
301
  // Old value of the text field
301
- const oldValue = this.htmlInput_.value;
302
+ const oldValue = this.htmlInput_!.value;
302
303
  // Determine the selected portion of the text field
303
- const selectionStart = this.htmlInput_.selectionStart;
304
- const selectionEnd = this.htmlInput_.selectionEnd;
304
+ const selectionStart = this.htmlInput_!.selectionStart ?? 0;
305
+ const selectionEnd = this.htmlInput_!.selectionEnd ?? 0;
305
306
 
306
307
  // Splice in the new value
307
308
  const newValue =
@@ -327,10 +328,10 @@ class ScratchFieldNumber extends Blockly.FieldTextInput {
327
328
  */
328
329
  numPadEraseButtonTouch(e: PointerEvent) {
329
330
  // Old value of the text field
330
- const oldValue = this.htmlInput_.value;
331
+ const oldValue = this.htmlInput_!.value;
331
332
  // Determine what is selected to erase (if anything)
332
- let selectionStart = this.htmlInput_.selectionStart;
333
- const selectionEnd = this.htmlInput_.selectionEnd;
333
+ let selectionStart = this.htmlInput_!.selectionStart ?? 0;
334
+ const selectionEnd = this.htmlInput_!.selectionEnd ?? 0;
334
335
 
335
336
  // If selection is zero-length, shift start to the left 1 character
336
337
  if (selectionStart == selectionEnd) {
@@ -359,7 +360,7 @@ class ScratchFieldNumber extends Blockly.FieldTextInput {
359
360
  private updateDisplay_(newValue: string, newSelection: number) {
360
361
  this.setEditorValue_(newValue);
361
362
  // Resize and scroll the text field appropriately
362
- const htmlInput = this.htmlInput_;
363
+ const htmlInput = this.htmlInput_!;
363
364
  htmlInput.setSelectionRange(newSelection, newSelection);
364
365
  htmlInput.scrollLeft = htmlInput.scrollWidth;
365
366
  }
@@ -29,7 +29,7 @@ import { createVariable, renameVariable } from "../variables";
29
29
  import type { ScratchVariableModel } from "../scratch_variable_model";
30
30
 
31
31
  export class ScratchFieldVariable extends Blockly.FieldVariable {
32
- private originalStyle: string;
32
+ private originalStyle!: string;
33
33
 
34
34
  constructor(
35
35
  varName: string | null | typeof Blockly.Field.SKIP_SETUP,
@@ -39,7 +39,10 @@ export class ScratchFieldVariable extends Blockly.FieldVariable {
39
39
  config?: Blockly.FieldVariableConfig
40
40
  ) {
41
41
  super(varName, validator, variableTypes, defaultType, config);
42
- this.menuGenerator_ = ScratchFieldVariable.dropdownCreate;
42
+ // dropdownCreate returns MenuOption[] rather than Blockly.MenuGenerator's
43
+ // MenuOption[][] variant; the cast is needed to satisfy FieldVariable's
44
+ // menuGenerator_ type while the actual runtime shape is compatible.
45
+ this.menuGenerator_ = ScratchFieldVariable.dropdownCreate as unknown as Blockly.MenuGenerator;
43
46
  }
44
47
 
45
48
  initModel() {
@@ -73,7 +76,7 @@ export class ScratchFieldVariable extends Blockly.FieldVariable {
73
76
  */
74
77
  initFlyoutBroadcast(
75
78
  workspace: Blockly.WorkspaceSvg
76
- ): Blockly.IVariableModel<Blockly.IVariableState> {
79
+ ): Blockly.IVariableModel<Blockly.IVariableState> | undefined {
77
80
  const broadcastVars = workspace.getVariablesOfType(
78
81
  Constants.BROADCAST_MESSAGE_VARIABLE_TYPE
79
82
  );
@@ -154,7 +157,7 @@ export class ScratchFieldVariable extends Blockly.FieldVariable {
154
157
 
155
158
  showEditor_(event: PointerEvent) {
156
159
  super.showEditor_(event);
157
- const sourceBlock = this.getSourceBlock();
160
+ const sourceBlock = this.getSourceBlock()!;
158
161
  const styleName = sourceBlock.getStyleName();
159
162
  const style = (sourceBlock.workspace as Blockly.WorkspaceSvg)
160
163
  .getRenderer()
@@ -175,7 +178,7 @@ export class ScratchFieldVariable extends Blockly.FieldVariable {
175
178
 
176
179
  dropdownDispose_() {
177
180
  super.dropdownDispose_();
178
- const sourceBlock = this.getSourceBlock();
181
+ const sourceBlock = this.getSourceBlock()!;
179
182
  if (sourceBlock.isShadow()) {
180
183
  sourceBlock.setStyle(this.originalStyle);
181
184
  }
@@ -14,7 +14,7 @@ export class FlyoutCheckboxIcon
14
14
  extends Blockly.icons.Icon
15
15
  implements Blockly.IHasBubble
16
16
  {
17
- private checkboxBubble: CheckboxBubble;
17
+ private checkboxBubble!: CheckboxBubble;
18
18
  private type = new Blockly.icons.IconType("checkbox");
19
19
 
20
20
  constructor(protected override sourceBlock: Blockly.BlockSvg) {
package/src/glows.ts CHANGED
@@ -17,8 +17,8 @@ export function glowStack(id: string, isGlowingStack: boolean) {
17
17
  const block = (Blockly.getMainWorkspace().getBlockById(id) ||
18
18
  (Blockly.getMainWorkspace() as Blockly.WorkspaceSvg)
19
19
  .getFlyout()
20
- .getWorkspace()
21
- .getBlockById(id)) as Blockly.BlockSvg;
20
+ ?.getWorkspace()
21
+ ?.getBlockById(id)) as Blockly.BlockSvg;
22
22
  if (!block) {
23
23
  throw "Tried to glow block that does not exist.";
24
24
  }
package/src/procedures.ts CHANGED
@@ -36,7 +36,7 @@ function allProcedureMutations(root: Blockly.WorkspaceSvg): Element[] {
36
36
  const blocks = root.getAllBlocks();
37
37
  return blocks
38
38
  .filter((b) => b.type === Constants.PROCEDURES_PROTOTYPE_BLOCK_TYPE)
39
- .map((b) => b.mutationToDom(/* opt_generateShadows */ true));
39
+ .map((b) => b.mutationToDom!(/* opt_generateShadows */ true) as Element);
40
40
  }
41
41
 
42
42
  /**
@@ -47,8 +47,8 @@ function allProcedureMutations(root: Blockly.WorkspaceSvg): Element[] {
47
47
  */
48
48
  function sortProcedureMutations(mutations: Element[]): Element[] {
49
49
  return mutations.slice().sort(function (a, b) {
50
- const procCodeA = a.getAttribute("proccode");
51
- const procCodeB = b.getAttribute("proccode");
50
+ const procCodeA = a.getAttribute("proccode")!;
51
+ const procCodeB = b.getAttribute("proccode")!;
52
52
 
53
53
  return scratchBlocksUtils.compareStrings(procCodeA, procCodeB);
54
54
  });
@@ -163,10 +163,10 @@ function mutateCallersAndPrototype(
163
163
  callers.push(prototypeBlock);
164
164
  Blockly.Events.setGroup(true);
165
165
  callers.forEach((caller) => {
166
- const oldMutationDom = caller.mutationToDom();
166
+ const oldMutationDom = caller.mutationToDom!();
167
167
  const oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom);
168
- caller.domToMutation(mutation);
169
- const newMutationDom = caller.mutationToDom();
168
+ caller.domToMutation!(mutation);
169
+ const newMutationDom = caller.mutationToDom!();
170
170
  const newMutation = newMutationDom && Blockly.Xml.domToText(newMutationDom);
171
171
  if (oldMutation !== newMutation) {
172
172
  Blockly.Events.fire(
@@ -197,8 +197,8 @@ function getDefineBlock(
197
197
  return workspace.getTopBlocks(false).find((block) => {
198
198
  if (block.type === Constants.PROCEDURES_DEFINITION_BLOCK_TYPE) {
199
199
  const prototypeBlock = block
200
- .getInput("custom_block")
201
- .connection.targetBlock() as Blockly.BlockSvg;
200
+ .getInput("custom_block")!
201
+ .connection!.targetBlock() as Blockly.BlockSvg;
202
202
  return (
203
203
  isProcedureBlock(prototypeBlock) &&
204
204
  prototypeBlock.getProcCode() === procCode
@@ -222,8 +222,8 @@ function getPrototypeBlock(
222
222
  const defineBlock = getDefineBlock(procCode, workspace);
223
223
  if (defineBlock) {
224
224
  return defineBlock
225
- .getInput("custom_block")
226
- .connection.targetBlock() as Blockly.BlockSvg;
225
+ .getInput("custom_block")!
226
+ .connection!.targetBlock() as Blockly.BlockSvg;
227
227
  }
228
228
  return undefined;
229
229
  }
@@ -243,7 +243,7 @@ function newProcedureMutation(): Element {
243
243
  warp="false">
244
244
  </mutation>
245
245
  </xml>`;
246
- return Blockly.utils.xml.textToDom(mutationText).firstElementChild;
246
+ return Blockly.utils.xml.textToDom(mutationText).firstElementChild!;
247
247
  }
248
248
 
249
249
  /**
@@ -251,7 +251,7 @@ function newProcedureMutation(): Element {
251
251
  * @param workspace The workspace to create the new procedure on.
252
252
  */
253
253
  function createProcedureDefCallback(workspace: Blockly.WorkspaceSvg) {
254
- ScratchProcedures.externalProcedureDefCallback(
254
+ ScratchProcedures.externalProcedureDefCallback!(
255
255
  newProcedureMutation(),
256
256
  createProcedureCallbackFactory(workspace)
257
257
  );
@@ -278,7 +278,7 @@ function createProcedureCallbackFactory(
278
278
  </statement>
279
279
  </block>
280
280
  </xml>`;
281
- const blockDom = Blockly.utils.xml.textToDom(blockText).firstElementChild;
281
+ const blockDom = Blockly.utils.xml.textToDom(blockText).firstElementChild!;
282
282
  Blockly.Events.setGroup(true);
283
283
  const block = Blockly.Xml.domToBlock(
284
284
  blockDom,
@@ -338,15 +338,23 @@ function editProcedureCallback(block: Blockly.BlockSvg) {
338
338
  // This is a call block, find the prototype corresponding to the procCode.
339
339
  // Make sure to search the correct workspace, call block can be in flyout.
340
340
  const workspaceToSearch = block.workspace.isFlyout
341
- ? block.workspace.targetWorkspace
341
+ ? block.workspace.targetWorkspace!
342
342
  : block.workspace;
343
- prototypeBlock = getPrototypeBlock(block.getProcCode(), workspaceToSearch);
343
+ const foundBlock = getPrototypeBlock(block.getProcCode(), workspaceToSearch);
344
+ if (!foundBlock) {
345
+ console.warn(
346
+ "editProcedureCallback: could not find prototype for",
347
+ block.getProcCode()
348
+ );
349
+ return;
350
+ }
351
+ prototypeBlock = foundBlock;
344
352
  } else {
345
353
  prototypeBlock = block;
346
354
  }
347
355
  // Block now refers to the procedure prototype block, it is safe to proceed.
348
- ScratchProcedures.externalProcedureDefCallback(
349
- prototypeBlock.mutationToDom(),
356
+ ScratchProcedures.externalProcedureDefCallback!(
357
+ prototypeBlock.mutationToDom!(),
350
358
  editProcedureCallbackFactory(prototypeBlock)
351
359
  );
352
360
  }
@@ -36,7 +36,7 @@ const setVisibility = (element: SVGElement, visible: boolean) => {
36
36
  * Owned by the PathObject with similar lifetime.
37
37
  */
38
38
  export class CatFace {
39
- faceGroup_: SVGElement;
39
+ faceGroup_!: SVGElement;
40
40
  parts_ = {} as Record<FacePart, SVGElement>;
41
41
  pathEarState: CatPathState;
42
42
  constants_: ConstantProvider;