scratch-blocks 2.0.0 → 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.
- package/AGENTS.md +140 -0
- package/dist/main.mjs +1 -1
- package/dist/types/src/blocks/procedures.d.ts +1 -1
- package/dist/types/src/blocks/procedures.d.ts.map +1 -1
- package/dist/types/src/events/events_block_comment_base.d.ts.map +1 -1
- package/dist/types/src/events/events_block_drag_end.d.ts.map +1 -1
- package/dist/types/src/events/events_block_drag_outside.d.ts.map +1 -1
- package/dist/types/src/fields/field_colour_slider.d.ts +4 -4
- package/dist/types/src/fields/field_colour_slider.d.ts.map +1 -1
- package/dist/types/src/fields/field_matrix.d.ts.map +1 -1
- package/dist/types/src/fields/field_note.d.ts.map +1 -1
- package/dist/types/src/fields/scratch_field_number.d.ts.map +1 -1
- package/dist/types/src/fields/scratch_field_variable.d.ts +1 -1
- package/dist/types/src/fields/scratch_field_variable.d.ts.map +1 -1
- package/dist/types/src/flyout_checkbox_icon.d.ts.map +1 -1
- package/dist/types/src/procedures.d.ts.map +1 -1
- package/dist/types/src/renderer/cat/cat_face.d.ts.map +1 -1
- package/dist/types/src/renderer/cat/drawer.d.ts +1 -1
- package/dist/types/src/renderer/cat/drawer.d.ts.map +1 -1
- package/dist/types/src/renderer/cat/render_info.d.ts.map +1 -1
- package/dist/types/src/renderer/cat/renderer.d.ts +1 -1
- package/dist/types/src/renderer/cat/renderer.d.ts.map +1 -1
- package/dist/types/src/renderer/constants.d.ts +41 -0
- package/dist/types/src/renderer/constants.d.ts.map +1 -0
- package/dist/types/src/renderer/drawer.d.ts +2 -2
- package/dist/types/src/renderer/drawer.d.ts.map +1 -1
- package/dist/types/src/renderer/render_info.d.ts.map +1 -1
- package/dist/types/src/renderer/renderer.d.ts +1 -1
- package/dist/types/src/renderer/renderer.d.ts.map +1 -1
- package/dist/types/src/scratch_comment_bubble.d.ts +2 -2
- package/dist/types/src/scratch_comment_bubble.d.ts.map +1 -1
- package/dist/types/src/scratch_comment_icon.d.ts +1 -1
- package/dist/types/src/scratch_comment_icon.d.ts.map +1 -1
- package/dist/types/src/scratch_dragger.d.ts +1 -1
- package/dist/types/src/scratch_variable_model.d.ts +1 -1
- package/dist/types/src/scratch_variable_model.d.ts.map +1 -1
- package/dist/types/tests/jsunit/connection_db_test.d.ts +3 -3
- package/package.json +9 -9
- package/src/block_reporting.ts +2 -2
- package/src/blocks/control.ts +9 -2
- package/src/blocks/data.ts +34 -6
- package/src/blocks/procedures.ts +49 -31
- package/src/context_menu_items.ts +7 -7
- package/src/data_category.ts +4 -4
- package/src/events/events_block_comment_base.ts +8 -5
- package/src/events/events_block_comment_change.ts +4 -4
- package/src/events/events_block_comment_collapse.ts +14 -2
- package/src/events/events_block_comment_create.ts +4 -1
- package/src/events/events_block_comment_delete.ts +4 -2
- package/src/events/events_block_comment_move.ts +4 -4
- package/src/events/events_block_comment_resize.ts +4 -4
- package/src/events/events_block_drag_end.ts +4 -4
- package/src/events/events_block_drag_outside.ts +2 -2
- package/src/events/events_scratch_variable_create.ts +20 -2
- package/src/fields/field_colour_slider.ts +53 -28
- package/src/fields/field_matrix.ts +9 -8
- package/src/fields/field_note.ts +34 -27
- package/src/fields/field_textinput_removable.ts +2 -2
- package/src/fields/field_variable_getter.ts +5 -5
- package/src/fields/field_vertical_separator.ts +1 -1
- package/src/fields/scratch_field_angle.ts +14 -14
- package/src/fields/scratch_field_dropdown.ts +2 -2
- package/src/fields/scratch_field_number.ts +13 -12
- package/src/fields/scratch_field_variable.ts +8 -5
- package/src/flyout_checkbox_icon.ts +1 -1
- package/src/glows.ts +2 -2
- package/src/procedures.ts +25 -17
- package/src/renderer/cat/cat_face.ts +1 -1
- package/src/renderer/cat/drawer.ts +3 -3
- package/src/renderer/cat/render_info.ts +2 -2
- package/src/renderer/cat/renderer.ts +2 -2
- package/src/renderer/constants.ts +8 -8
- package/src/renderer/drawer.ts +2 -2
- package/src/renderer/render_info.ts +7 -4
- package/src/renderer/renderer.ts +2 -2
- package/src/scratch_block_paster.ts +1 -1
- package/src/scratch_comment_bubble.ts +16 -14
- package/src/scratch_comment_icon.ts +1 -1
- package/src/scratch_dragger.ts +2 -2
- package/src/scratch_variable_model.ts +2 -2
- package/src/status_indicator_label.ts +3 -3
- package/src/status_indicator_label_flyout_inflater.ts +1 -1
- package/src/variables.ts +7 -7
- package/src/xml.ts +3 -3
- package/tsconfig.json +1 -1
package/src/blocks/procedures.ts
CHANGED
|
@@ -33,7 +33,7 @@ type ConnectionMap = {
|
|
|
33
33
|
[key: string]: {
|
|
34
34
|
shadow: Element;
|
|
35
35
|
block: Blockly.BlockSvg;
|
|
36
|
-
};
|
|
36
|
+
} | null;
|
|
37
37
|
};
|
|
38
38
|
|
|
39
39
|
/**
|
|
@@ -68,6 +68,14 @@ class DuplicateOnDragDraggable implements Blockly.IDraggable {
|
|
|
68
68
|
*/
|
|
69
69
|
startDrag(e: PointerEvent) {
|
|
70
70
|
const data = this.block.toCopyData();
|
|
71
|
+
if (!data) {
|
|
72
|
+
console.warn(
|
|
73
|
+
"DuplicateOnDragDraggable.startDrag: failed to serialize block for copy",
|
|
74
|
+
this.block.type,
|
|
75
|
+
this.block.id
|
|
76
|
+
);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
71
79
|
this.copy = Blockly.clipboard.paste(
|
|
72
80
|
data,
|
|
73
81
|
this.block.workspace
|
|
@@ -76,9 +84,19 @@ class DuplicateOnDragDraggable implements Blockly.IDraggable {
|
|
|
76
84
|
}
|
|
77
85
|
|
|
78
86
|
drag(newLoc: Blockly.utils.Coordinate, e?: PointerEvent) {
|
|
79
|
-
(
|
|
80
|
-
|
|
81
|
-
|
|
87
|
+
const gesture = this.block.workspace.getGesture(e);
|
|
88
|
+
if (!gesture || !this.copy) {
|
|
89
|
+
console.warn(
|
|
90
|
+
"DuplicateOnDragDraggable.drag: missing gesture or copied block",
|
|
91
|
+
{
|
|
92
|
+
hasGesture: Boolean(gesture),
|
|
93
|
+
hasCopy: Boolean(this.copy),
|
|
94
|
+
blockId: this.block.id,
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
(gesture.getCurrentDragger() as ScratchDragger).setDraggable(this.copy);
|
|
82
100
|
this.copy.drag(newLoc, e);
|
|
83
101
|
}
|
|
84
102
|
|
|
@@ -120,12 +138,12 @@ function callerMutationToDom(this: ProcedureCallBlock): Element {
|
|
|
120
138
|
* @param xmlElement XML storage element.
|
|
121
139
|
*/
|
|
122
140
|
function callerDomToMutation(this: ProcedureCallBlock, xmlElement: Element) {
|
|
123
|
-
this.procCode_ = xmlElement.getAttribute("proccode")
|
|
141
|
+
this.procCode_ = xmlElement.getAttribute("proccode")!;
|
|
124
142
|
this.generateShadows_ = JSON.parse(
|
|
125
|
-
xmlElement.getAttribute("generateshadows")
|
|
143
|
+
xmlElement.getAttribute("generateshadows")!
|
|
126
144
|
);
|
|
127
|
-
this.argumentIds_ = JSON.parse(xmlElement.getAttribute("argumentids"));
|
|
128
|
-
this.warp_ = JSON.parse(xmlElement.getAttribute("warp"));
|
|
145
|
+
this.argumentIds_ = JSON.parse(xmlElement.getAttribute("argumentids")!);
|
|
146
|
+
this.warp_ = JSON.parse(xmlElement.getAttribute("warp")!);
|
|
129
147
|
this.updateDisplay_();
|
|
130
148
|
}
|
|
131
149
|
|
|
@@ -167,16 +185,16 @@ function definitionDomToMutation(
|
|
|
167
185
|
this: ProcedurePrototypeBlock | ProcedureDeclarationBlock,
|
|
168
186
|
xmlElement: Element
|
|
169
187
|
) {
|
|
170
|
-
this.procCode_ = xmlElement.getAttribute("proccode")
|
|
171
|
-
this.warp_ = JSON.parse(xmlElement.getAttribute("warp"));
|
|
188
|
+
this.procCode_ = xmlElement.getAttribute("proccode")!;
|
|
189
|
+
this.warp_ = JSON.parse(xmlElement.getAttribute("warp")!);
|
|
172
190
|
|
|
173
191
|
const prevArgIds = this.argumentIds_;
|
|
174
192
|
const prevDisplayNames = this.displayNames_;
|
|
175
193
|
|
|
176
|
-
this.argumentIds_ = JSON.parse(xmlElement.getAttribute("argumentids"));
|
|
177
|
-
this.displayNames_ = JSON.parse(xmlElement.getAttribute("argumentnames"));
|
|
194
|
+
this.argumentIds_ = JSON.parse(xmlElement.getAttribute("argumentids")!);
|
|
195
|
+
this.displayNames_ = JSON.parse(xmlElement.getAttribute("argumentnames")!);
|
|
178
196
|
this.argumentDefaults_ = JSON.parse(
|
|
179
|
-
xmlElement.getAttribute("argumentdefaults")
|
|
197
|
+
xmlElement.getAttribute("argumentdefaults")!
|
|
180
198
|
);
|
|
181
199
|
this.updateDisplay_();
|
|
182
200
|
if ("updateArgumentReporterNames_" in this) {
|
|
@@ -224,7 +242,7 @@ function disconnectOldBlocks_(this: ProcedureBlock): ConnectionMap {
|
|
|
224
242
|
if (input.connection) {
|
|
225
243
|
const target = input.connection.targetBlock() as Blockly.BlockSvg;
|
|
226
244
|
const saveInfo = {
|
|
227
|
-
shadow: input.connection.getShadowDom(true),
|
|
245
|
+
shadow: input.connection.getShadowDom(true) as Element,
|
|
228
246
|
block: target,
|
|
229
247
|
};
|
|
230
248
|
connectionMap[input.name] = saveInfo;
|
|
@@ -420,7 +438,7 @@ function attachShadow_(
|
|
|
420
438
|
new (Blockly.Events.get(Blockly.Events.BLOCK_CREATE))(newBlock)
|
|
421
439
|
);
|
|
422
440
|
}
|
|
423
|
-
newBlock.outputConnection
|
|
441
|
+
newBlock.outputConnection!.connect(input.connection!);
|
|
424
442
|
}
|
|
425
443
|
}
|
|
426
444
|
|
|
@@ -486,21 +504,21 @@ function populateArgumentOnCaller_(
|
|
|
486
504
|
id: string,
|
|
487
505
|
input: Blockly.Input
|
|
488
506
|
) {
|
|
489
|
-
let oldBlock: Blockly.BlockSvg;
|
|
490
|
-
let oldShadow: Element;
|
|
507
|
+
let oldBlock: Blockly.BlockSvg | undefined;
|
|
508
|
+
let oldShadow: Element | undefined;
|
|
491
509
|
if (connectionMap && id in connectionMap) {
|
|
492
510
|
const saveInfo = connectionMap[id];
|
|
493
|
-
oldBlock = saveInfo["block"];
|
|
494
|
-
oldShadow = saveInfo["shadow"];
|
|
511
|
+
oldBlock = saveInfo?.["block"];
|
|
512
|
+
oldShadow = saveInfo?.["shadow"];
|
|
495
513
|
}
|
|
496
514
|
|
|
497
515
|
if (connectionMap && oldBlock) {
|
|
498
516
|
// Reattach the old block and shadow DOM.
|
|
499
517
|
connectionMap[input.name] = null;
|
|
500
|
-
oldBlock.outputConnection
|
|
518
|
+
oldBlock.outputConnection!.connect(input.connection!);
|
|
501
519
|
if (type !== ArgumentType.BOOLEAN && this.generateShadows_) {
|
|
502
520
|
const shadowDom = oldShadow || this.buildShadowDom_(type);
|
|
503
|
-
input.connection
|
|
521
|
+
input.connection!.setShadowDom(shadowDom);
|
|
504
522
|
}
|
|
505
523
|
} else if (this.generateShadows_) {
|
|
506
524
|
this.attachShadow_(input, type);
|
|
@@ -526,10 +544,10 @@ function populateArgumentOnPrototype_(
|
|
|
526
544
|
id: string,
|
|
527
545
|
input: Blockly.Input
|
|
528
546
|
) {
|
|
529
|
-
let oldBlock = null;
|
|
547
|
+
let oldBlock: Blockly.BlockSvg | null = null;
|
|
530
548
|
if (connectionMap && id in connectionMap) {
|
|
531
549
|
const saveInfo = connectionMap[id];
|
|
532
|
-
oldBlock = saveInfo["block"];
|
|
550
|
+
oldBlock = saveInfo?.["block"] ?? null;
|
|
533
551
|
}
|
|
534
552
|
|
|
535
553
|
const oldTypeMatches = checkOldTypeMatches_(oldBlock, type);
|
|
@@ -548,7 +566,7 @@ function populateArgumentOnPrototype_(
|
|
|
548
566
|
}
|
|
549
567
|
|
|
550
568
|
// Attach the block.
|
|
551
|
-
input.connection
|
|
569
|
+
input.connection!.connect(argumentReporter.outputConnection!);
|
|
552
570
|
}
|
|
553
571
|
|
|
554
572
|
/**
|
|
@@ -570,10 +588,10 @@ function populateArgumentOnDeclaration_(
|
|
|
570
588
|
id: string,
|
|
571
589
|
input: Blockly.Input
|
|
572
590
|
) {
|
|
573
|
-
let oldBlock = null;
|
|
591
|
+
let oldBlock: Blockly.BlockSvg | null = null;
|
|
574
592
|
if (connectionMap && id in connectionMap) {
|
|
575
593
|
const saveInfo = connectionMap[id];
|
|
576
|
-
oldBlock = saveInfo["block"];
|
|
594
|
+
oldBlock = saveInfo?.["block"] ?? null;
|
|
577
595
|
}
|
|
578
596
|
|
|
579
597
|
// TODO: This always returns false, because it checks for argument reporter
|
|
@@ -593,7 +611,7 @@ function populateArgumentOnDeclaration_(
|
|
|
593
611
|
}
|
|
594
612
|
|
|
595
613
|
// Attach the block.
|
|
596
|
-
input.connection
|
|
614
|
+
input.connection!.connect(argumentEditor.outputConnection!);
|
|
597
615
|
}
|
|
598
616
|
|
|
599
617
|
/**
|
|
@@ -686,7 +704,7 @@ function updateDeclarationProcCode_(this: ProcedureDeclarationBlock) {
|
|
|
686
704
|
this.procCode_ += input.fieldRow[0].getValue();
|
|
687
705
|
} else if (input.type === Blockly.inputs.inputTypes.VALUE) {
|
|
688
706
|
// Inspect the argument editor.
|
|
689
|
-
const target = input.connection
|
|
707
|
+
const target = input.connection!.targetBlock()!;
|
|
690
708
|
this.displayNames_.push(target.getFieldValue("TEXT"));
|
|
691
709
|
this.argumentIds_.push(input.name);
|
|
692
710
|
if (target.type === "argument_editor_boolean") {
|
|
@@ -712,8 +730,8 @@ function focusLastEditor_(this: ProcedureDeclarationBlock) {
|
|
|
712
730
|
newInput.fieldRow[0].showEditor();
|
|
713
731
|
} else if (newInput.type === Blockly.inputs.inputTypes.VALUE) {
|
|
714
732
|
// Inspect the argument editor.
|
|
715
|
-
const target = newInput.connection
|
|
716
|
-
target.getField("TEXT")
|
|
733
|
+
const target = newInput.connection!.targetBlock()!;
|
|
734
|
+
target.getField("TEXT")!.showEditor();
|
|
717
735
|
}
|
|
718
736
|
}
|
|
719
737
|
}
|
|
@@ -791,7 +809,7 @@ function removeFieldCallback(
|
|
|
791
809
|
for (var n = 0; n < this.inputList.length; n++) {
|
|
792
810
|
var input = this.inputList[n];
|
|
793
811
|
if (input.connection) {
|
|
794
|
-
var target = input.connection
|
|
812
|
+
var target = input.connection!.targetBlock()!;
|
|
795
813
|
if (field.name && target.getField(field.name) === field) {
|
|
796
814
|
inputNameToRemove = input.name;
|
|
797
815
|
}
|
|
@@ -12,20 +12,20 @@ import * as Blockly from "blockly/core";
|
|
|
12
12
|
export function registerDeleteBlock() {
|
|
13
13
|
const deleteOption = {
|
|
14
14
|
displayText(scope: Blockly.ContextMenuRegistry.Scope) {
|
|
15
|
-
const descendantCount = getDeletableBlocksInStack(scope.block).length;
|
|
15
|
+
const descendantCount = getDeletableBlocksInStack(scope.block!).length;
|
|
16
16
|
return descendantCount === 1
|
|
17
17
|
? Blockly.Msg["DELETE_BLOCK"]
|
|
18
18
|
: Blockly.Msg["DELETE_X_BLOCKS"].replace("%1", `${descendantCount}`);
|
|
19
19
|
},
|
|
20
20
|
preconditionFn(scope: Blockly.ContextMenuRegistry.Scope) {
|
|
21
|
-
if (!scope.block
|
|
21
|
+
if (!scope.block!.isInFlyout && scope.block!.isDeletable()) {
|
|
22
22
|
return "enabled";
|
|
23
23
|
}
|
|
24
24
|
return "hidden";
|
|
25
25
|
},
|
|
26
26
|
callback(scope: Blockly.ContextMenuRegistry.Scope) {
|
|
27
27
|
Blockly.Events.setGroup(true);
|
|
28
|
-
scope.block
|
|
28
|
+
scope.block!.dispose(true, true);
|
|
29
29
|
Blockly.Events.setGroup(false);
|
|
30
30
|
},
|
|
31
31
|
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
|
|
@@ -42,7 +42,7 @@ function getDeletableBlocksInStack(
|
|
|
42
42
|
if (block.getNextBlock()) {
|
|
43
43
|
// Next blocks are not deleted.
|
|
44
44
|
const nextDescendants = block
|
|
45
|
-
.getNextBlock()
|
|
45
|
+
.getNextBlock()!
|
|
46
46
|
.getDescendants(false)
|
|
47
47
|
.filter(isDeletable);
|
|
48
48
|
descendants = descendants.filter((b) => !nextDescendants.includes(b));
|
|
@@ -161,10 +161,10 @@ function deleteNext(deleteList: Blockly.BlockSvg[], eventGroup?: string) {
|
|
|
161
161
|
*/
|
|
162
162
|
export function registerDuplicateBlock() {
|
|
163
163
|
const original =
|
|
164
|
-
Blockly.ContextMenuRegistry.registry.getItem("blockDuplicate")
|
|
164
|
+
Blockly.ContextMenuRegistry.registry.getItem("blockDuplicate")!;
|
|
165
165
|
const duplicateOption = {
|
|
166
|
-
displayText: original.displayText
|
|
167
|
-
preconditionFn: original.preconditionFn
|
|
166
|
+
displayText: original.displayText!,
|
|
167
|
+
preconditionFn: original.preconditionFn!,
|
|
168
168
|
callback(scope: Blockly.ContextMenuRegistry.Scope) {
|
|
169
169
|
if (!scope.block) return;
|
|
170
170
|
const data = scope.block.toCopyData(true);
|
package/src/data_category.ts
CHANGED
|
@@ -481,14 +481,14 @@ function addCreateButton(
|
|
|
481
481
|
let msg = Blockly.Msg.NEW_VARIABLE;
|
|
482
482
|
let callbackKey = "CREATE_VARIABLE";
|
|
483
483
|
let callback = function (button: Blockly.FlyoutButton) {
|
|
484
|
-
createVariable(button.getTargetWorkspace(),
|
|
484
|
+
createVariable(button.getTargetWorkspace(), undefined, SCALAR_VARIABLE_TYPE);
|
|
485
485
|
};
|
|
486
486
|
|
|
487
487
|
if (type === "LIST") {
|
|
488
488
|
msg = Blockly.Msg.NEW_LIST;
|
|
489
489
|
callbackKey = "CREATE_LIST";
|
|
490
490
|
callback = function (button: Blockly.FlyoutButton) {
|
|
491
|
-
createVariable(button.getTargetWorkspace(),
|
|
491
|
+
createVariable(button.getTargetWorkspace(), undefined, LIST_VARIABLE_TYPE);
|
|
492
492
|
};
|
|
493
493
|
}
|
|
494
494
|
button.setAttribute("text", msg);
|
|
@@ -551,7 +551,7 @@ function addBlock(
|
|
|
551
551
|
${secondValueField}
|
|
552
552
|
</block>
|
|
553
553
|
</xml>`;
|
|
554
|
-
var block = Blockly.utils.xml.textToDom(blockText).firstElementChild
|
|
554
|
+
var block = Blockly.utils.xml.textToDom(blockText).firstElementChild!;
|
|
555
555
|
xmlList.push(block);
|
|
556
556
|
}
|
|
557
557
|
}
|
|
@@ -617,6 +617,6 @@ function createValue(valueName: string, type: string, value: string): string {
|
|
|
617
617
|
*/
|
|
618
618
|
function addSep(xmlList: Element[]) {
|
|
619
619
|
const sepText = `<xml><sep gap="36"/></xml>`;
|
|
620
|
-
const sep = Blockly.utils.xml.textToDom(sepText).firstElementChild
|
|
620
|
+
const sep = Blockly.utils.xml.textToDom(sepText).firstElementChild!;
|
|
621
621
|
xmlList.push(sep);
|
|
622
622
|
}
|
|
@@ -9,9 +9,9 @@ import type { ScratchCommentBubble } from "../scratch_comment_bubble";
|
|
|
9
9
|
|
|
10
10
|
export class BlockCommentBase extends Blockly.Events.Abstract {
|
|
11
11
|
isBlank = true;
|
|
12
|
-
commentId
|
|
13
|
-
blockId
|
|
14
|
-
workspaceId
|
|
12
|
+
commentId!: string;
|
|
13
|
+
blockId!: string;
|
|
14
|
+
workspaceId!: string;
|
|
15
15
|
|
|
16
16
|
constructor(opt_blockComment?: ScratchCommentBubble) {
|
|
17
17
|
super();
|
|
@@ -20,8 +20,11 @@ export class BlockCommentBase extends Blockly.Events.Abstract {
|
|
|
20
20
|
if (!opt_blockComment) return;
|
|
21
21
|
|
|
22
22
|
this.commentId = opt_blockComment.getId();
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
const sourceBlock = opt_blockComment.getSourceBlock();
|
|
24
|
+
if (sourceBlock) {
|
|
25
|
+
this.blockId = sourceBlock.id;
|
|
26
|
+
this.workspaceId = sourceBlock.workspace.id;
|
|
27
|
+
}
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
toJson(): BlockCommentBaseJson {
|
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
import type { ScratchCommentBubble } from "../scratch_comment_bubble";
|
|
13
13
|
|
|
14
14
|
class BlockCommentChange extends BlockCommentBase {
|
|
15
|
-
oldContents_
|
|
16
|
-
newContents_
|
|
15
|
+
oldContents_!: string;
|
|
16
|
+
newContents_!: string;
|
|
17
17
|
|
|
18
18
|
constructor(
|
|
19
19
|
opt_blockComment?: ScratchCommentBubble,
|
|
@@ -22,8 +22,8 @@ class BlockCommentChange extends BlockCommentBase {
|
|
|
22
22
|
) {
|
|
23
23
|
super(opt_blockComment);
|
|
24
24
|
this.type = "block_comment_change";
|
|
25
|
-
this.oldContents_ = oldContents;
|
|
26
|
-
this.newContents_ = newContents;
|
|
25
|
+
if (oldContents !== undefined) this.oldContents_ = oldContents;
|
|
26
|
+
if (newContents !== undefined) this.newContents_ = newContents;
|
|
27
27
|
// Disable undo because Blockly already tracks changes to comment text for
|
|
28
28
|
// undo purposes; this event exists solely to keep the Scratch VM apprised
|
|
29
29
|
// of the state of things.
|
|
@@ -12,12 +12,12 @@ import {
|
|
|
12
12
|
import type { ScratchCommentBubble } from "../scratch_comment_bubble";
|
|
13
13
|
|
|
14
14
|
class BlockCommentCollapse extends BlockCommentBase {
|
|
15
|
-
newCollapsed
|
|
15
|
+
newCollapsed!: boolean;
|
|
16
16
|
|
|
17
17
|
constructor(opt_blockComment?: ScratchCommentBubble, collapsed?: boolean) {
|
|
18
18
|
super(opt_blockComment);
|
|
19
19
|
this.type = "block_comment_collapse";
|
|
20
|
-
this.newCollapsed = collapsed;
|
|
20
|
+
if (collapsed !== undefined) this.newCollapsed = collapsed;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
toJson(): BlockCommentCollapseJson {
|
|
@@ -45,7 +45,19 @@ class BlockCommentCollapse extends BlockCommentBase {
|
|
|
45
45
|
run(forward: boolean) {
|
|
46
46
|
const workspace = this.getEventWorkspace_();
|
|
47
47
|
const block = workspace.getBlockById(this.blockId);
|
|
48
|
+
if (!block) {
|
|
49
|
+
console.warn(
|
|
50
|
+
"BlockCommentCollapse.run: block not found",
|
|
51
|
+
this.blockId,
|
|
52
|
+
this.workspaceId
|
|
53
|
+
);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
48
56
|
const comment = block.getIcon(Blockly.icons.IconType.COMMENT);
|
|
57
|
+
if (!comment) {
|
|
58
|
+
console.warn("BlockCommentCollapse.run: comment icon not found", block.id);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
49
61
|
comment.setBubbleVisible(forward ? !this.newCollapsed : this.newCollapsed);
|
|
50
62
|
}
|
|
51
63
|
}
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
import type { ScratchCommentBubble } from "../scratch_comment_bubble";
|
|
13
13
|
|
|
14
14
|
class BlockCommentCreate extends BlockCommentBase {
|
|
15
|
-
json
|
|
15
|
+
json!: {
|
|
16
16
|
x: number;
|
|
17
17
|
y: number;
|
|
18
18
|
width: number;
|
|
@@ -22,6 +22,9 @@ class BlockCommentCreate extends BlockCommentBase {
|
|
|
22
22
|
constructor(opt_blockComment?: ScratchCommentBubble) {
|
|
23
23
|
super(opt_blockComment);
|
|
24
24
|
this.type = "block_comment_create";
|
|
25
|
+
// opt_blockComment is absent when this class is instantiated by fromJson.
|
|
26
|
+
// In that case fromJson sets this.json directly, so return early here.
|
|
27
|
+
if (!opt_blockComment) return;
|
|
25
28
|
const size = opt_blockComment.getSize();
|
|
26
29
|
const location = opt_blockComment.getRelativeToSurfaceXY();
|
|
27
30
|
this.json = {
|
|
@@ -15,8 +15,10 @@ class BlockCommentDelete extends BlockCommentBase {
|
|
|
15
15
|
) {
|
|
16
16
|
super(opt_blockComment);
|
|
17
17
|
this.type = "block_comment_delete";
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
if (sourceBlock) {
|
|
19
|
+
this.blockId = sourceBlock.id;
|
|
20
|
+
this.workspaceId = sourceBlock.workspace.id;
|
|
21
|
+
}
|
|
20
22
|
// Disable undo because Blockly already tracks comment deletion for
|
|
21
23
|
// undo purposes; this event exists solely to keep the Scratch VM apprised
|
|
22
24
|
// of the state of things.
|
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
import type { ScratchCommentBubble } from "../scratch_comment_bubble";
|
|
13
13
|
|
|
14
14
|
class BlockCommentMove extends BlockCommentBase {
|
|
15
|
-
oldCoordinate_
|
|
16
|
-
newCoordinate_
|
|
15
|
+
oldCoordinate_!: Blockly.utils.Coordinate;
|
|
16
|
+
newCoordinate_!: Blockly.utils.Coordinate;
|
|
17
17
|
|
|
18
18
|
constructor(
|
|
19
19
|
opt_blockComment?: ScratchCommentBubble,
|
|
@@ -22,8 +22,8 @@ class BlockCommentMove extends BlockCommentBase {
|
|
|
22
22
|
) {
|
|
23
23
|
super(opt_blockComment);
|
|
24
24
|
this.type = "block_comment_move";
|
|
25
|
-
this.oldCoordinate_ = oldCoordinate;
|
|
26
|
-
this.newCoordinate_ = newCoordinate;
|
|
25
|
+
if (oldCoordinate !== undefined) this.oldCoordinate_ = oldCoordinate;
|
|
26
|
+
if (newCoordinate !== undefined) this.newCoordinate_ = newCoordinate;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
toJson(): BlockCommentMoveJson {
|
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
import type { ScratchCommentBubble } from "../scratch_comment_bubble";
|
|
13
13
|
|
|
14
14
|
class BlockCommentResize extends BlockCommentBase {
|
|
15
|
-
oldSize
|
|
16
|
-
newSize
|
|
15
|
+
oldSize!: Blockly.utils.Size;
|
|
16
|
+
newSize!: Blockly.utils.Size;
|
|
17
17
|
|
|
18
18
|
constructor(
|
|
19
19
|
opt_blockComment?: ScratchCommentBubble,
|
|
@@ -22,8 +22,8 @@ class BlockCommentResize extends BlockCommentBase {
|
|
|
22
22
|
) {
|
|
23
23
|
super(opt_blockComment);
|
|
24
24
|
this.type = "block_comment_resize";
|
|
25
|
-
this.oldSize = oldSize;
|
|
26
|
-
this.newSize = newSize;
|
|
25
|
+
if (oldSize !== undefined) this.oldSize = oldSize;
|
|
26
|
+
if (newSize !== undefined) this.newSize = newSize;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
toJson(): BlockCommentResizeJson {
|
|
@@ -7,15 +7,15 @@
|
|
|
7
7
|
import * as Blockly from "blockly/core";
|
|
8
8
|
|
|
9
9
|
export class BlockDragEnd extends Blockly.Events.BlockBase {
|
|
10
|
-
isOutside
|
|
11
|
-
xml
|
|
10
|
+
isOutside!: boolean;
|
|
11
|
+
xml!: Element | DocumentFragment;
|
|
12
12
|
|
|
13
13
|
constructor(block?: Blockly.Block, isOutside?: boolean) {
|
|
14
14
|
super(block);
|
|
15
15
|
this.type = "endDrag";
|
|
16
|
-
this.isOutside = isOutside;
|
|
16
|
+
if (isOutside !== undefined) this.isOutside = isOutside;
|
|
17
17
|
this.recordUndo = false;
|
|
18
|
-
this.xml = Blockly.Xml.blockToDom(block, true);
|
|
18
|
+
if (block) this.xml = Blockly.Xml.blockToDom(block, true);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
toJson(): BlockDragEndJson {
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
import * as Blockly from "blockly/core";
|
|
8
8
|
|
|
9
9
|
export class BlockDragOutside extends Blockly.Events.BlockBase {
|
|
10
|
-
isOutside
|
|
10
|
+
isOutside!: boolean;
|
|
11
11
|
|
|
12
12
|
constructor(block?: Blockly.Block, isOutside?: boolean) {
|
|
13
13
|
super(block);
|
|
14
14
|
this.type = "dragOutside";
|
|
15
|
-
this.isOutside = isOutside;
|
|
15
|
+
if (isOutside !== undefined) this.isOutside = isOutside;
|
|
16
16
|
this.recordUndo = false;
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -8,8 +8,8 @@ import * as Blockly from "blockly/core";
|
|
|
8
8
|
import { ScratchVariableModel } from "../scratch_variable_model";
|
|
9
9
|
|
|
10
10
|
class ScratchVariableCreate extends Blockly.Events.VarCreate {
|
|
11
|
-
isLocal
|
|
12
|
-
isCloud
|
|
11
|
+
isLocal!: boolean;
|
|
12
|
+
isCloud!: boolean;
|
|
13
13
|
|
|
14
14
|
constructor(variable?: ScratchVariableModel) {
|
|
15
15
|
super(variable);
|
|
@@ -46,6 +46,17 @@ class ScratchVariableCreate extends Blockly.Events.VarCreate {
|
|
|
46
46
|
const workspace = this.getEventWorkspace_();
|
|
47
47
|
const variableMap = workspace.getVariableMap();
|
|
48
48
|
if (forward) {
|
|
49
|
+
if (this.varName == null || this.varType == null || this.varId == null) {
|
|
50
|
+
console.error(
|
|
51
|
+
"ScratchVariableCreate.run: missing variable data for create",
|
|
52
|
+
{
|
|
53
|
+
varName: this.varName,
|
|
54
|
+
varType: this.varType,
|
|
55
|
+
varId: this.varId,
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
49
60
|
const variable = new ScratchVariableModel(
|
|
50
61
|
workspace,
|
|
51
62
|
this.varName,
|
|
@@ -59,6 +70,13 @@ class ScratchVariableCreate extends Blockly.Events.VarCreate {
|
|
|
59
70
|
new (Blockly.Events.get(Blockly.Events.VAR_CREATE))(variable)
|
|
60
71
|
);
|
|
61
72
|
} else {
|
|
73
|
+
if (this.varId == null) {
|
|
74
|
+
console.error(
|
|
75
|
+
"ScratchVariableCreate.run: missing varId for delete",
|
|
76
|
+
this
|
|
77
|
+
);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
62
80
|
const variable = variableMap.getVariableById(this.varId);
|
|
63
81
|
if (variable) {
|
|
64
82
|
variableMap.deleteVariable(variable);
|
|
@@ -41,9 +41,9 @@ export class FieldColourSlider extends FieldColour {
|
|
|
41
41
|
* The button calls this function with a callback to update the field value.
|
|
42
42
|
* BEWARE: This is not a stable API. It may change.
|
|
43
43
|
*/
|
|
44
|
-
static activateEyedropper_
|
|
44
|
+
static activateEyedropper_?: (
|
|
45
45
|
callback: (colour: string) => void
|
|
46
|
-
) => void
|
|
46
|
+
) => void;
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* Path to the eyedropper svg icon.
|
|
@@ -61,9 +61,9 @@ export class FieldColourSlider extends FieldColour {
|
|
|
61
61
|
private hueReadout_?: Element;
|
|
62
62
|
private saturationReadout_?: Element;
|
|
63
63
|
private brightnessReadout_?: Element;
|
|
64
|
-
private hue_
|
|
65
|
-
private saturation_
|
|
66
|
-
private brightness_
|
|
64
|
+
private hue_ = 0;
|
|
65
|
+
private saturation_ = 0;
|
|
66
|
+
private brightness_ = 0;
|
|
67
67
|
private eyedropperEventData_?: Blockly.browserEvents.Data;
|
|
68
68
|
|
|
69
69
|
/**
|
|
@@ -133,34 +133,58 @@ export class FieldColourSlider extends FieldColour {
|
|
|
133
133
|
* Update the readouts and slider backgrounds after value has changed.
|
|
134
134
|
*/
|
|
135
135
|
private updateDom_() {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
136
|
+
const hueSlider = this.hueSlider_;
|
|
137
|
+
const saturationSlider = this.saturationSlider_;
|
|
138
|
+
const brightnessSlider = this.brightnessSlider_;
|
|
139
|
+
const hueReadout = this.hueReadout_;
|
|
140
|
+
const saturationReadout = this.saturationReadout_;
|
|
141
|
+
const brightnessReadout = this.brightnessReadout_;
|
|
142
|
+
if (
|
|
143
|
+
!hueSlider ||
|
|
144
|
+
!saturationSlider ||
|
|
145
|
+
!brightnessSlider ||
|
|
146
|
+
!hueReadout ||
|
|
147
|
+
!saturationReadout ||
|
|
148
|
+
!brightnessReadout
|
|
149
|
+
) {
|
|
150
|
+
console.warn(
|
|
151
|
+
"FieldColourSlider.updateDom_: slider/readout DOM is not fully initialized"
|
|
152
|
+
);
|
|
153
|
+
return;
|
|
152
154
|
}
|
|
155
|
+
|
|
156
|
+
this.setGradient_(hueSlider, ColourChannel.HUE);
|
|
157
|
+
this.setGradient_(saturationSlider, ColourChannel.SATURATION);
|
|
158
|
+
this.setGradient_(brightnessSlider, ColourChannel.BRIGHTNESS);
|
|
159
|
+
|
|
160
|
+
hueReadout.textContent = Math.floor(
|
|
161
|
+
(100 * this.hue_) / 360
|
|
162
|
+
).toFixed(0);
|
|
163
|
+
saturationReadout.textContent = Math.floor(
|
|
164
|
+
100 * this.saturation_
|
|
165
|
+
).toFixed(0);
|
|
166
|
+
brightnessReadout.textContent = Math.floor(
|
|
167
|
+
(100 * this.brightness_) / 255
|
|
168
|
+
).toFixed(0);
|
|
153
169
|
}
|
|
154
170
|
|
|
155
171
|
/**
|
|
156
172
|
* Update the slider handle positions from the current field value.
|
|
157
173
|
*/
|
|
158
174
|
private updateSliderHandles_() {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
175
|
+
const hueSlider = this.hueSlider_;
|
|
176
|
+
const saturationSlider = this.saturationSlider_;
|
|
177
|
+
const brightnessSlider = this.brightnessSlider_;
|
|
178
|
+
if (!hueSlider || !saturationSlider || !brightnessSlider) {
|
|
179
|
+
console.warn(
|
|
180
|
+
"FieldColourSlider.updateSliderHandles_: slider DOM is not fully initialized"
|
|
181
|
+
);
|
|
182
|
+
return;
|
|
163
183
|
}
|
|
184
|
+
|
|
185
|
+
hueSlider.value = `${this.hue_}`;
|
|
186
|
+
saturationSlider.value = `${this.saturation_}`;
|
|
187
|
+
brightnessSlider.value = `${this.brightness_}`;
|
|
164
188
|
}
|
|
165
189
|
|
|
166
190
|
/**
|
|
@@ -219,7 +243,7 @@ export class FieldColourSlider extends FieldColour {
|
|
|
219
243
|
* Activate the eyedropper, passing in a callback for setting the field value.
|
|
220
244
|
*/
|
|
221
245
|
private activateEyedropperInternal_() {
|
|
222
|
-
FieldColourSlider.activateEyedropper_((chosenColour: string) => {
|
|
246
|
+
FieldColourSlider.activateEyedropper_?.((chosenColour: string) => {
|
|
223
247
|
// Update the internal hue/saturation/brightness values so sliders update.
|
|
224
248
|
const components = Blockly.utils.colour.hexToRgb(chosenColour);
|
|
225
249
|
const { hue, saturation, value } = this.rgbToHsv(
|
|
@@ -245,7 +269,8 @@ export class FieldColourSlider extends FieldColour {
|
|
|
245
269
|
|
|
246
270
|
// Init color component values that are used while the editor is open
|
|
247
271
|
// in order to keep the slider values stable.
|
|
248
|
-
const
|
|
272
|
+
const currentValue = this.getValue() ?? "#000000";
|
|
273
|
+
const components = Blockly.utils.colour.hexToRgb(currentValue);
|
|
249
274
|
const { hue, saturation, value } = this.rgbToHsv(
|
|
250
275
|
components[0],
|
|
251
276
|
components[1],
|
|
@@ -314,7 +339,7 @@ export class FieldColourSlider extends FieldColour {
|
|
|
314
339
|
|
|
315
340
|
// Set value updates the slider positions
|
|
316
341
|
// Do this before attaching callbacks to avoid extra events from initial set
|
|
317
|
-
this.setValue(this.getValue());
|
|
342
|
+
this.setValue(this.getValue() ?? currentValue);
|
|
318
343
|
|
|
319
344
|
this.hueChangeEventKey_ = Blockly.browserEvents.bind(
|
|
320
345
|
this.hueSlider_,
|