scratch-blocks 2.0.0-spork.3 → 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.
package/src/index.ts CHANGED
@@ -26,11 +26,10 @@ import "./css";
26
26
  import "./renderer/renderer";
27
27
  import * as contextMenuItems from "./context_menu_items";
28
28
  import {
29
- ContinuousToolbox,
30
- ContinuousFlyout,
29
+ registerContinuousToolbox,
31
30
  ContinuousMetrics,
32
31
  } from "@blockly/continuous-toolbox";
33
- import { CheckableContinuousFlyout } from "./checkable_continuous_flyout.js";
32
+ import { CheckableContinuousFlyout } from "./checkable_continuous_flyout";
34
33
  import { buildGlowFilter, glowStack } from "./glows";
35
34
  import { ScratchContinuousToolbox } from "./scratch_continuous_toolbox";
36
35
  import "./scratch_comment_icon";
@@ -38,6 +37,7 @@ import "./scratch_dragger";
38
37
  import "./scratch_variable_map";
39
38
  import "./scratch_variable_model";
40
39
  import "./scratch_connection_checker";
40
+ import "./scratch_insertion_marker_previewer";
41
41
  import "./flyout_checkbox_icon";
42
42
  import "./events/events_block_comment_change";
43
43
  import "./events/events_block_comment_collapse";
@@ -81,6 +81,7 @@ export {
81
81
  StatusIndicatorLabel,
82
82
  StatusButtonState,
83
83
  } from "./status_indicator_label";
84
+ export * from "./xml";
84
85
 
85
86
  export function inject(container: Element, options: Blockly.BlocklyOptions) {
86
87
  registerScratchFieldAngle();
@@ -120,6 +121,7 @@ export function inject(container: Element, options: Blockly.BlocklyOptions) {
120
121
  return workspace;
121
122
  }
122
123
 
124
+ registerContinuousToolbox();
123
125
  Blockly.Scrollbar.scrollbarThickness = Blockly.Touch.TOUCH_ENABLED ? 14 : 11;
124
126
  Blockly.FlyoutButton.TEXT_MARGIN_X = 40;
125
127
  Blockly.FlyoutButton.TEXT_MARGIN_Y = 10;
@@ -128,6 +130,7 @@ Blockly.ContextMenuRegistry.registry.unregister("blockInline");
128
130
  Blockly.ContextMenuItems.registerCommentOptions();
129
131
  Blockly.ContextMenuRegistry.registry.unregister("blockDelete");
130
132
  contextMenuItems.registerDeleteBlock();
133
+ contextMenuItems.registerDuplicateBlock();
131
134
  Blockly.ContextMenuRegistry.registry.unregister("workspaceDelete");
132
135
  contextMenuItems.registerDeleteAll();
133
136
  Blockly.comments.CommentView.defaultCommentSize = new Blockly.utils.Size(
@@ -6,21 +6,12 @@
6
6
 
7
7
  import * as Blockly from "blockly/core";
8
8
  import { CheckboxBubble } from "./checkbox_bubble";
9
+ import { RecyclableBlockFlyoutInflater as BlocklyRecyclableBlockFlyoutInflater } from "@blockly/continuous-toolbox";
9
10
 
10
11
  /**
11
12
  * A block inflater that caches and reuses blocks to improve performance.
12
13
  */
13
- export class RecyclableBlockFlyoutInflater extends Blockly.BlockFlyoutInflater {
14
- /**
15
- * Whether or not block recycling is enabled.
16
- */
17
- recyclingEnabled = true;
18
-
19
- /**
20
- * Map from block type to block instance.
21
- */
22
- recycledBlocks = new Map<string, Blockly.BlockSvg>();
23
-
14
+ export class RecyclableBlockFlyoutInflater extends BlocklyRecyclableBlockFlyoutInflater {
24
15
  /**
25
16
  * Creates a block on the flyout workspace from the given block definition.
26
17
  *
@@ -31,152 +22,18 @@ export class RecyclableBlockFlyoutInflater extends Blockly.BlockFlyoutInflater {
31
22
  load(
32
23
  state: Blockly.utils.toolbox.BlockInfo,
33
24
  flyoutWorkspace: Blockly.WorkspaceSvg
34
- ): Blockly.IBoundedElement {
35
- const block = super.load(state, flyoutWorkspace);
25
+ ): Blockly.FlyoutItem {
26
+ const flyoutItem = super.load(state, flyoutWorkspace);
27
+ const block = flyoutItem.getElement();
36
28
  if ("checkboxInFlyout" in block && block.checkboxInFlyout) {
37
29
  block.moveBy(
38
- CheckboxBubble.CHECKBOX_SIZE + CheckboxBubble.CHECKBOX_MARGIN,
30
+ (flyoutWorkspace.RTL ? -1 : 1) *
31
+ (CheckboxBubble.CHECKBOX_SIZE + CheckboxBubble.CHECKBOX_MARGIN),
39
32
  0
40
33
  );
41
34
  }
42
35
 
43
- return block;
44
- }
45
-
46
- /**
47
- * Toggles whether or not recycling is enabled.
48
- *
49
- * @param enabled True if recycling should be enabled.
50
- */
51
- setRecyclingEnabled(enabled: boolean) {
52
- this.recyclingEnabled = enabled;
53
- }
54
-
55
- /**
56
- * Creates a new block from the given block definition.
57
- *
58
- * @param blockDefinition The definition to create a block from.
59
- * @returns The newly created block.
60
- */
61
- createBlock(
62
- blockDefinition: Blockly.utils.toolbox.BlockInfo
63
- ): Blockly.BlockSvg {
64
- const blockType = this.getTypeFromDefinition(blockDefinition);
65
- return (
66
- this.getRecycledBlock(blockType) ??
67
- super.createBlock(blockDefinition, this.flyoutWorkspace)
68
- );
69
- }
70
-
71
- /**
72
- * Returns the type of a block from an XML or JSON block definition.
73
- *
74
- * @param blockDefinition The block definition to parse.
75
- * @returns The block type.
76
- */
77
- getTypeFromDefinition(
78
- blockDefinition: Blockly.utils.toolbox.BlockInfo
79
- ): string {
80
- if (blockDefinition["blockxml"]) {
81
- const xml =
82
- typeof blockDefinition["blockxml"] === "string"
83
- ? Blockly.utils.xml.textToDom(blockDefinition["blockxml"])
84
- : (blockDefinition["blockxml"] as Element);
85
- return xml.getAttribute("type");
86
- } else {
87
- return blockDefinition["type"];
88
- }
89
- }
90
-
91
- /**
92
- * Puts a previously created block into the recycle bin and moves it to the
93
- * top of the workspace. Used during large workspace swaps to limit the number
94
- * of new DOM elements we need to create.
95
- *
96
- * @param block The block to recycle.
97
- */
98
- recycleBlock(block: Blockly.BlockSvg) {
99
- const xy = block.getRelativeToSurfaceXY();
100
- block.moveBy(-xy.x, -xy.y);
101
- this.recycledBlocks.set(block.type, block);
102
- }
103
-
104
- /**
105
- * Returns a block from the cache of recycled blocks with the given type, or
106
- * undefined if one cannot be found.
107
- *
108
- * @param blockType The type of the block to try to recycle.
109
- * @returns The recycled block, or undefined if one could not be recycled.
110
- */
111
- getRecycledBlock(blockType: string): Blockly.BlockSvg | undefined {
112
- const block = this.recycledBlocks.get(blockType);
113
- this.recycledBlocks.delete(blockType);
114
- return block;
115
- }
116
-
117
- /**
118
- * Returns whether the given block can be recycled or not.
119
- *
120
- * @param block The block to check for recyclability.
121
- * @returns True if the block can be recycled. False otherwise.
122
- */
123
- blockIsRecyclable(block: Blockly.Block): boolean {
124
- if (!this.recyclingEnabled) {
125
- return false;
126
- }
127
-
128
- // If the block needs to parse mutations, never recycle.
129
- if (block.mutationToDom && block.domToMutation) {
130
- return false;
131
- }
132
-
133
- if (!block.isEnabled()) {
134
- return false;
135
- }
136
-
137
- for (const input of block.inputList) {
138
- for (const field of input.fieldRow) {
139
- // No variables.
140
- if (field.referencesVariables()) {
141
- return false;
142
- }
143
- if (field instanceof Blockly.FieldDropdown) {
144
- if (field.isOptionListDynamic()) {
145
- return false;
146
- }
147
- }
148
- }
149
- // Check children.
150
- if (input.connection) {
151
- const targetBlock = input.connection.targetBlock();
152
- if (targetBlock && !this.blockIsRecyclable(targetBlock)) {
153
- return false;
154
- }
155
- }
156
- }
157
- return true;
158
- }
159
-
160
- /**
161
- * Disposes of the provided block.
162
- *
163
- * @param element The block to dispose of.
164
- */
165
- disposeElement(element: Blockly.BlockSvg) {
166
- if (this.blockIsRecyclable(element)) {
167
- this.removeListeners(element.id);
168
- this.recycleBlock(element);
169
- } else {
170
- super.disposeElement(element);
171
- }
172
- }
173
-
174
- /**
175
- * Clears the cache of recycled blocks.
176
- */
177
- emptyRecycledBlocks() {
178
- this.recycledBlocks.forEach((block) => block.dispose(false, false));
179
- this.recycledBlocks.clear();
36
+ return flyoutItem;
180
37
  }
181
38
  }
182
39
 
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import * as Blockly from "blockly/core";
8
+ import type { ScratchCommentIcon } from "./scratch_comment_icon";
8
9
 
9
10
  /**
10
11
  * Class responsible for handling the pasting of copied blocks.
@@ -31,6 +32,15 @@ class ScratchBlockPaster extends Blockly.clipboard.BlockPaster {
31
32
  block.setDragStrategy(new Blockly.dragging.BlockDragStrategy(block));
32
33
  }
33
34
 
35
+ // Deserialization of blocks suppresses events, so even though this gets
36
+ // fired for blocks with comments, the VM will never receive it, causing its
37
+ // state to get out of sync. Manually fire it here (after suppression has
38
+ // been turned off) if needed.
39
+ const commentIcon = block.getIcon(Blockly.icons.IconType.COMMENT);
40
+ if (commentIcon) {
41
+ (commentIcon as ScratchCommentIcon).fireCreateEvent();
42
+ }
43
+
34
44
  return block;
35
45
  }
36
46
  }
@@ -11,17 +11,28 @@ import * as Blockly from "blockly/core";
11
11
  * @implements {IBubble}
12
12
  * @implements {ISelectable}
13
13
  */
14
- export class ScratchCommentBubble extends Blockly.comments.CommentView {
15
- constructor(sourceBlock) {
16
- super(sourceBlock.workspace);
14
+ export class ScratchCommentBubble
15
+ extends Blockly.comments.CommentView
16
+ implements Blockly.IBubble, Blockly.ISelectable
17
+ {
18
+ id: string;
19
+ private sourceBlock: Blockly.BlockSvg;
20
+ private anchor?: Blockly.utils.Coordinate;
21
+ private anchorChain?: SVGLineElement;
22
+ private dragStartLocation?: Blockly.utils.Coordinate;
23
+
24
+ constructor(sourceBlock: Blockly.BlockSvg) {
25
+ const commentId = `${sourceBlock.id}_comment`;
26
+ super(sourceBlock.workspace, commentId);
17
27
  this.sourceBlock = sourceBlock;
18
28
  this.disposing = false;
19
- this.id = Blockly.utils.idGenerator.genUid();
29
+ this.id = commentId;
20
30
  this.setPlaceholderText(Blockly.Msg.WORKSPACE_COMMENT_DEFAULT_TEXT);
21
31
  this.getSvgRoot().setAttribute(
22
32
  "style",
23
33
  `--colour-commentBorder: ${sourceBlock.getColourTertiary()};`
24
34
  );
35
+ this.getSvgRoot().setAttribute("id", this.id);
25
36
 
26
37
  Blockly.browserEvents.conditionalBind(
27
38
  this.getSvgRoot(),
@@ -34,15 +45,15 @@ export class ScratchCommentBubble extends Blockly.comments.CommentView {
34
45
  this.getSvgRoot(),
35
46
  "wheel",
36
47
  this,
37
- (e) => {
48
+ (e: WheelEvent) => {
38
49
  e.stopPropagation();
39
50
  }
40
51
  );
41
52
  }
42
53
 
43
- setDeleteStyle(enable) {}
54
+ setDeleteStyle(enable: boolean) {}
44
55
  showContextMenu() {}
45
- setDragging(start) {}
56
+ setDragging(start: boolean) {}
46
57
  select() {}
47
58
  unselect() {}
48
59
 
@@ -50,11 +61,13 @@ export class ScratchCommentBubble extends Blockly.comments.CommentView {
50
61
  return true;
51
62
  }
52
63
 
53
- moveDuringDrag(newLocation) {
64
+ moveDuringDrag(newLocation: Blockly.utils.Coordinate) {
54
65
  this.moveTo(newLocation);
55
66
  }
56
67
 
57
- moveTo(xOrCoordinate, y) {
68
+ moveTo(xOrCoordinate: number, y: number): void;
69
+ moveTo(xOrCoordinate: Blockly.utils.Coordinate): void;
70
+ moveTo(xOrCoordinate: Blockly.utils.Coordinate | number, y?: number) {
58
71
  const destination =
59
72
  xOrCoordinate instanceof Blockly.utils.Coordinate
60
73
  ? xOrCoordinate
@@ -63,22 +76,22 @@ export class ScratchCommentBubble extends Blockly.comments.CommentView {
63
76
  this.redrawAnchorChain();
64
77
  }
65
78
 
66
- startGesture(e) {
79
+ startGesture(e: PointerEvent) {
67
80
  const gesture = this.workspace.getGesture(e);
68
81
  if (gesture) {
69
- gesture.handleCommentStart(e, this);
82
+ gesture.handleBubbleStart(e, this);
70
83
  Blockly.common.setSelected(this);
71
84
  }
72
85
  }
73
86
 
74
- startDrag(event) {
87
+ startDrag(event: PointerEvent) {
75
88
  this.dragStartLocation = this.getRelativeToSurfaceXY();
76
89
  this.workspace.setResizesEnabled(false);
77
90
  this.workspace.getLayerManager()?.moveToDragLayer(this);
78
91
  Blockly.utils.dom.addClass(this.getSvgRoot(), "blocklyDragging");
79
92
  }
80
93
 
81
- drag(newLocation, event) {
94
+ drag(newLocation: Blockly.utils.Coordinate, event: Event) {
82
95
  this.moveTo(newLocation);
83
96
  }
84
97
 
@@ -101,7 +114,7 @@ export class ScratchCommentBubble extends Blockly.comments.CommentView {
101
114
  this.moveTo(this.dragStartLocation);
102
115
  }
103
116
 
104
- setAnchorLocation(newAnchor) {
117
+ setAnchorLocation(newAnchor: Blockly.utils.Coordinate) {
105
118
  const oldAnchor = this.anchor;
106
119
  const alreadyAnchored = !!this.anchor;
107
120
  this.anchor = newAnchor;
@@ -116,15 +129,19 @@ export class ScratchCommentBubble extends Blockly.comments.CommentView {
116
129
  }
117
130
 
118
131
  dropAnchor() {
119
- this.moveTo(this.anchor.x + 40, this.anchor.y - 16);
132
+ const verticalOffset = 16;
133
+ this.moveTo(
134
+ this.anchor.x + 40 * (this.workspace.RTL ? -1 : 1),
135
+ this.anchor.y - verticalOffset
136
+ );
120
137
  const location = this.getRelativeToSurfaceXY();
121
138
  this.anchorChain = Blockly.utils.dom.createSvgElement(
122
139
  Blockly.utils.Svg.LINE,
123
140
  {
124
141
  x1: this.anchor.x - location.x,
125
142
  y1: this.anchor.y - location.y,
126
- x2: this.getSize().width / 2,
127
- y2: 16,
143
+ x2: (this.getSize().width / 2) * (this.workspace.RTL ? -1 : 1),
144
+ y2: verticalOffset,
128
145
  style: `stroke: ${this.sourceBlock.getColourTertiary()}; stroke-width: 1`,
129
146
  },
130
147
  this.getSvgRoot()
@@ -139,8 +156,8 @@ export class ScratchCommentBubble extends Blockly.comments.CommentView {
139
156
  if (!this.anchorChain) return;
140
157
 
141
158
  const location = this.getRelativeToSurfaceXY();
142
- this.anchorChain.setAttribute("x1", this.anchor.x - location.x);
143
- this.anchorChain.setAttribute("y1", this.anchor.y - location.y);
159
+ this.anchorChain.setAttribute("x1", `${this.anchor.x - location.x}`);
160
+ this.anchorChain.setAttribute("y1", `${this.anchor.y - location.y}`);
144
161
  }
145
162
 
146
163
  getId() {
@@ -166,4 +183,20 @@ export class ScratchCommentBubble extends Blockly.comments.CommentView {
166
183
  }
167
184
  super.dispose();
168
185
  }
186
+
187
+ getFocusableElement() {
188
+ return this.getSvgRoot();
189
+ }
190
+
191
+ getFocusableTree() {
192
+ return this.workspace;
193
+ }
194
+
195
+ onNodeFocus() {}
196
+
197
+ onNodeBlur() {}
198
+
199
+ canBeFocused() {
200
+ return true;
201
+ }
169
202
  }
@@ -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(
@@ -7,6 +7,7 @@
7
7
  import * as Blockly from "blockly/core";
8
8
  import { ContinuousToolbox } from "@blockly/continuous-toolbox";
9
9
  import { ScratchContinuousCategory } from "./scratch_continuous_category";
10
+ import { STATUS_INDICATOR_LABEL_TYPE } from "./status_indicator_label_flyout_inflater";
10
11
 
11
12
  /**
12
13
  * A toolbox that displays items from all categories in one scrolling list.
@@ -23,36 +24,25 @@ export class ScratchContinuousToolbox extends ContinuousToolbox {
23
24
  }
24
25
 
25
26
  /**
26
- * Gets the contents that should be shown in the flyout.
27
+ * Converts the given toolbox item to a corresponding array of items that
28
+ * should appear in the flyout.
27
29
  *
28
- * @returns Flyout contents.
30
+ * @param toolboxItem The toolbox item to convert.
31
+ * @returns An array of flyout item definitions.
29
32
  */
30
- getInitialFlyoutContents_(): Blockly.utils.toolbox.FlyoutItemInfoArray {
31
- // TODO(#211) Clean this up when the continuous toolbox plugin is updated.
32
- let contents: Blockly.utils.toolbox.FlyoutItemInfoArray = [];
33
- for (const toolboxItem of this.getToolboxItems()) {
34
- if (toolboxItem instanceof ScratchContinuousCategory) {
35
- if (toolboxItem.shouldShowStatusButton()) {
36
- contents.push({
37
- kind: "STATUS_INDICATOR_LABEL",
38
- id: toolboxItem.getId(),
39
- text: toolboxItem.getName(),
40
- });
41
- } else {
42
- // Create a label node to go at the top of the category
43
- contents.push({ kind: "LABEL", text: toolboxItem.getName() });
44
- }
45
- let itemContents = toolboxItem.getContents();
46
-
47
- // Handle custom categories (e.g. variables and functions)
48
- if (typeof itemContents === "string") {
49
- itemContents = {
50
- custom: itemContents,
51
- kind: "CATEGORY",
52
- };
53
- }
54
- contents = contents.concat(itemContents);
55
- }
33
+ protected convertToolboxItemToFlyoutItems(
34
+ toolboxItem: Blockly.IToolboxItem
35
+ ): Blockly.utils.toolbox.FlyoutItemInfoArray {
36
+ const contents = super.convertToolboxItemToFlyoutItems(toolboxItem);
37
+ if (
38
+ toolboxItem instanceof ScratchContinuousCategory &&
39
+ toolboxItem.shouldShowStatusButton()
40
+ ) {
41
+ contents.splice(0, 1, {
42
+ kind: STATUS_INDICATOR_LABEL_TYPE,
43
+ id: toolboxItem.getId(),
44
+ text: toolboxItem.getName(),
45
+ });
56
46
  }
57
47
  return contents;
58
48
  }
@@ -62,12 +52,12 @@ export class ScratchContinuousToolbox extends ContinuousToolbox {
62
52
  */
63
53
  forceRerender() {
64
54
  const selectedCategoryName = this.selectedItem_?.getName();
65
- super.refreshSelection();
55
+ this.getFlyout().show(this.getInitialFlyoutContents());
56
+ this.selectCategoryByName(selectedCategoryName);
66
57
  let callback;
67
58
  while ((callback = this.postRenderCallbacks.shift())) {
68
59
  callback();
69
60
  }
70
- this.selectCategoryByName(selectedCategoryName);
71
61
  }
72
62
 
73
63
  /**
@@ -77,4 +67,20 @@ export class ScratchContinuousToolbox extends ContinuousToolbox {
77
67
  runAfterRerender(callback: () => void) {
78
68
  this.postRenderCallbacks.push(callback);
79
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
+ }
80
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
+ );
@@ -7,6 +7,8 @@
7
7
  import * as Blockly from "blockly/core";
8
8
  import { StatusIndicatorLabel } from "./status_indicator_label";
9
9
 
10
+ export const STATUS_INDICATOR_LABEL_TYPE = "status_indicator_label";
11
+
10
12
  /**
11
13
  * Flyout inflater responsible for creating status indicator labels.
12
14
  */
@@ -20,15 +22,15 @@ class StatusIndicatorLabelFlyoutInflater extends Blockly.LabelFlyoutInflater {
20
22
  */
21
23
  load(
22
24
  state: Blockly.utils.toolbox.LabelInfo,
23
- flyoutWorkspace: Blockly.WorkspaceSvg
24
- ): StatusIndicatorLabel {
25
+ flyout: Blockly.IFlyout
26
+ ): Blockly.FlyoutItem {
25
27
  const label = new StatusIndicatorLabel(
26
- flyoutWorkspace,
27
- flyoutWorkspace.targetWorkspace,
28
+ flyout.getWorkspace(),
29
+ flyout.targetWorkspace,
28
30
  state
29
31
  );
30
32
  label.show();
31
- return label;
33
+ return new Blockly.FlyoutItem(label, STATUS_INDICATOR_LABEL_TYPE);
32
34
  }
33
35
  }
34
36
 
@@ -38,7 +40,7 @@ class StatusIndicatorLabelFlyoutInflater extends Blockly.LabelFlyoutInflater {
38
40
  export function registerStatusIndicatorLabelFlyoutInflater() {
39
41
  Blockly.registry.register(
40
42
  Blockly.registry.Type.FLYOUT_INFLATER,
41
- "status_indicator_label",
43
+ STATUS_INDICATOR_LABEL_TYPE,
42
44
  StatusIndicatorLabelFlyoutInflater
43
45
  );
44
46
  }
package/src/variables.ts CHANGED
@@ -29,7 +29,7 @@ import {
29
29
  } from "./constants";
30
30
  import { ScratchVariableModel } from "./scratch_variable_model";
31
31
  import { ScratchContinuousToolbox } from "./scratch_continuous_toolbox";
32
- import { CheckableContinuousFlyout } from "./checkable_continuous_flyout.js";
32
+ import { CheckableContinuousFlyout } from "./checkable_continuous_flyout";
33
33
 
34
34
  /**
35
35
  * Constant prefix to differentiate cloud variable names from other types of