scratch-blocks 2.0.0-spork.4 → 2.0.0-spork.5

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.
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import * as Blockly from "blockly/core";
8
- import { ScratchCommentBubble } from "./scratch_comment_bubble.js";
8
+ import { ScratchCommentBubble } from "./scratch_comment_bubble";
9
9
 
10
10
  interface CommentState {
11
11
  text: string;
@@ -19,7 +19,7 @@ interface CommentState {
19
19
  /**
20
20
  * Custom comment icon that draws no icon indicator, used for block comments.
21
21
  */
22
- class ScratchCommentIcon
22
+ export class ScratchCommentIcon
23
23
  extends Blockly.icons.Icon
24
24
  implements Blockly.ISerializable, Blockly.IHasBubble
25
25
  {
@@ -34,9 +34,7 @@ class ScratchCommentIcon
34
34
  constructor(protected sourceBlock: Blockly.BlockSvg) {
35
35
  super(sourceBlock);
36
36
  this.commentBubble = new ScratchCommentBubble(this.sourceBlock);
37
- Blockly.Events.fire(
38
- new (Blockly.Events.get("block_comment_create"))(this.commentBubble)
39
- );
37
+ this.fireCreateEvent();
40
38
  this.onTextChangedListener = this.onTextChanged.bind(this);
41
39
  this.onSizeChangedListener = this.onSizeChanged.bind(this);
42
40
  this.onCollapseListener = this.onCollapsed.bind(this);
@@ -198,10 +196,27 @@ class ScratchCommentIcon
198
196
  this.commentBubble.setCollapsed(!visible);
199
197
  }
200
198
 
199
+ getBubble() {
200
+ return this.commentBubble;
201
+ }
202
+
201
203
  dispose() {
202
204
  this.commentBubble.dispose();
203
205
  super.dispose();
204
206
  }
207
+
208
+ canBeFocused() {
209
+ return false;
210
+ }
211
+
212
+ /**
213
+ * Fires a block comment create event corresponding to this icon's comment.
214
+ */
215
+ fireCreateEvent() {
216
+ Blockly.Events.fire(
217
+ new (Blockly.Events.get("block_comment_create"))(this.commentBubble)
218
+ );
219
+ }
205
220
  }
206
221
 
207
222
  Blockly.registry.register(
@@ -67,4 +67,20 @@ export class ScratchContinuousToolbox extends ContinuousToolbox {
67
67
  runAfterRerender(callback: () => void) {
68
68
  this.postRenderCallbacks.push(callback);
69
69
  }
70
+
71
+ /**
72
+ * Returns whether or not the given item should be deselected.
73
+ * Prevents items from being deselected without a replacement.
74
+ *
75
+ * @param oldItem The item that was previously selected.
76
+ * @param newItem The item that is proposed to be selected instead.
77
+ * @returns True if the old item should be allowed to be deselected.
78
+ */
79
+ shouldDeselectItem_(
80
+ oldItem: Blockly.ISelectableToolboxItem | null,
81
+ newItem: Blockly.ISelectableToolboxItem | null
82
+ ) {
83
+ if (!newItem) return false;
84
+ return super.shouldDeselectItem_(oldItem, newItem);
85
+ }
70
86
  }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import * as Blockly from "blockly/core";
8
+
9
+ /**
10
+ * Displays an indicator of where a block being dragged will be connected.
11
+ */
12
+ class ScratchInsertionMarkerPreviewer extends Blockly.InsertionMarkerPreviewer {
13
+ /**
14
+ * Transforms the given block into a JSON representation used to construct an
15
+ * insertion marker.
16
+ *
17
+ * @param block The block to serialize and use as an insertion marker.
18
+ * @returns A JSON-formatted string corresponding to a serialized
19
+ * representation of the given block suitable for use as an insertion
20
+ * marker.
21
+ */
22
+ protected override serializeBlockToInsertionMarker(block: Blockly.BlockSvg) {
23
+ const blockJson = Blockly.serialization.blocks.save(block, {
24
+ addCoordinates: false,
25
+ addInputBlocks: true,
26
+ addNextBlocks: false,
27
+ doFullSerialization: false,
28
+ });
29
+
30
+ if (!blockJson) {
31
+ throw new Error(
32
+ `Failed to serialize source block. ${block.toDevString()}`
33
+ );
34
+ }
35
+
36
+ return blockJson;
37
+ }
38
+ }
39
+
40
+ Blockly.registry.register(
41
+ Blockly.registry.Type.CONNECTION_PREVIEWER,
42
+ Blockly.registry.DEFAULT,
43
+ ScratchInsertionMarkerPreviewer,
44
+ true
45
+ );
@@ -22,15 +22,15 @@ class StatusIndicatorLabelFlyoutInflater extends Blockly.LabelFlyoutInflater {
22
22
  */
23
23
  load(
24
24
  state: Blockly.utils.toolbox.LabelInfo,
25
- flyoutWorkspace: Blockly.WorkspaceSvg
25
+ flyout: Blockly.IFlyout
26
26
  ): Blockly.FlyoutItem {
27
27
  const label = new StatusIndicatorLabel(
28
- flyoutWorkspace,
29
- flyoutWorkspace.targetWorkspace,
28
+ flyout.getWorkspace(),
29
+ flyout.targetWorkspace,
30
30
  state
31
31
  );
32
32
  label.show();
33
- return new Blockly.FlyoutItem(label, STATUS_INDICATOR_LABEL_TYPE, true);
33
+ return new Blockly.FlyoutItem(label, STATUS_INDICATOR_LABEL_TYPE);
34
34
  }
35
35
  }
36
36
 
package/src/xml.ts ADDED
@@ -0,0 +1,57 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import * as Blockly from "blockly/core";
8
+ import { ScratchVariableModel } from "./scratch_variable_model";
9
+
10
+ /**
11
+ * Clears the workspace and loads the given serialized state.
12
+ *
13
+ * @param xml XML representation of a Blockly workspace.
14
+ * @param workspace The workspace to load the serialized data onto.
15
+ */
16
+ export function clearWorkspaceAndLoadFromXml(
17
+ xml: Element,
18
+ workspace: Blockly.WorkspaceSvg
19
+ ): string[] {
20
+ workspace.setResizesEnabled(false);
21
+ Blockly.Events.setGroup(true);
22
+ workspace.clear();
23
+
24
+ // Manually load variables to include the cloud and local properties that core
25
+ // Blockly is unaware of.
26
+ for (const variable of xml.querySelectorAll("variables variable")) {
27
+ const id = variable.getAttribute("id");
28
+ if (!id) continue;
29
+ const type = variable.getAttribute("type");
30
+ const name = variable.textContent;
31
+ const isLocal = variable.getAttribute("islocal") === "true";
32
+ const isCloud = variable.getAttribute("iscloud") === "true";
33
+
34
+ const variableModel = new ScratchVariableModel(
35
+ workspace,
36
+ name,
37
+ type,
38
+ id,
39
+ isLocal,
40
+ isCloud
41
+ );
42
+ Blockly.Events.fire(
43
+ new (Blockly.Events.get(Blockly.Events.VAR_CREATE))(variableModel)
44
+ );
45
+ workspace.getVariableMap().addVariable(variableModel);
46
+ }
47
+
48
+ // Remove the `variables` element from the XML to prevent Blockly from
49
+ // throwing or stomping on the variables we created.
50
+ xml.querySelector("variables").remove();
51
+
52
+ // Defer to core for the rest of the deserialization.
53
+ const blockIds = Blockly.Xml.domToWorkspace(xml, workspace);
54
+
55
+ workspace.setResizesEnabled(true);
56
+ return blockIds;
57
+ }