From 7f4ec3a9ea554edde1a91f3f6d27e340ff2dea1d Mon Sep 17 00:00:00 2001 From: Aaron Dodson Date: Mon, 12 Jan 2026 15:12:26 -0800 Subject: [PATCH 1/2] fix: Normalize Zelos connection indicators --- core/block_svg.ts | 30 ++++----- core/insertion_marker_previewer.ts | 8 +++ core/rendered_connection.ts | 1 + core/renderers/common/path_object.ts | 23 ------- core/renderers/common/renderer.ts | 7 +- core/renderers/zelos/constants.ts | 99 ---------------------------- core/renderers/zelos/path_object.ts | 21 ------ core/renderers/zelos/renderer.ts | 15 +++++ core/theme.ts | 2 - 9 files changed, 40 insertions(+), 166 deletions(-) diff --git a/core/block_svg.ts b/core/block_svg.ts index b3fdeb2d6b6..269081a6e3c 100644 --- a/core/block_svg.ts +++ b/core/block_svg.ts @@ -781,6 +781,13 @@ export class BlockSvg } } + /** + * Returns whether or not this block is currently being dragged. + */ + isDragging() { + return this.dragging; + } + /** * Set whether this block is movable or not. * @@ -1728,24 +1735,11 @@ export class BlockSvg * @internal */ fadeForReplacement(add: boolean) { - // TODO (7204): Remove these internal methods. - (this.pathObject as AnyDuringMigration).updateReplacementFade(add); - } - - /** - * Visual effect to show that if the dragging block is dropped it will connect - * to this input. - * - * @param conn The connection on the input to highlight. - * @param add True if highlighting should be added. - * @internal - */ - highlightShapeForInput(conn: RenderedConnection, add: boolean) { - // TODO (7204): Remove these internal methods. - (this.pathObject as AnyDuringMigration).updateShapeForInputHighlight( - conn, - add, - ); + if (add) { + this.addClass('blocklyReplaceable'); + } else { + this.removeClass('blocklyReplaceable'); + } } /** diff --git a/core/insertion_marker_previewer.ts b/core/insertion_marker_previewer.ts index 8b5b82468c5..2a31327ce6b 100644 --- a/core/insertion_marker_previewer.ts +++ b/core/insertion_marker_previewer.ts @@ -92,6 +92,10 @@ export class InsertionMarkerPreviewer implements IConnectionPreviewer { staticConn.highlight(); } + if (this.workspace.getRenderer().shouldHighlightConnection(draggedConn)) { + draggedConn.highlight(); + } + this.draggedConn = draggedConn; this.staticConn = staticConn; } finally { @@ -224,6 +228,10 @@ export class InsertionMarkerPreviewer implements IConnectionPreviewer { this.staticConn.unhighlight(); this.staticConn = null; } + if (this.draggedConn) { + this.draggedConn.unhighlight(); + this.draggedConn = null; + } if (this.fadedBlock) { this.fadedBlock.fadeForReplacement(false); this.fadedBlock = null; diff --git a/core/rendered_connection.ts b/core/rendered_connection.ts index 4a53048bc84..033a074f70f 100644 --- a/core/rendered_connection.ts +++ b/core/rendered_connection.ts @@ -332,6 +332,7 @@ export class RenderedConnection const highlightSvg = this.findHighlightSvg(); if (highlightSvg) { highlightSvg.style.display = ''; + highlightSvg.parentElement?.appendChild(highlightSvg); } } diff --git a/core/renderers/common/path_object.ts b/core/renderers/common/path_object.ts index f6291b9f0fa..50ea9252df5 100644 --- a/core/renderers/common/path_object.ts +++ b/core/renderers/common/path_object.ts @@ -7,7 +7,6 @@ // Former goog.module ID: Blockly.blockRendering.PathObject import type {BlockSvg} from '../../block_svg.js'; -import type {Connection} from '../../connection.js'; import {RenderedConnection} from '../../rendered_connection.js'; import type {BlockStyle} from '../../theme.js'; import {Coordinate} from '../../utils/coordinate.js'; @@ -192,28 +191,6 @@ export class PathObject implements IPathObject { this.setClass_('blocklyDraggable', enable); } - /** - * Add or remove styling that shows that if the dragging block is dropped, - * this block will be replaced. If a shadow block, it will disappear. - * Otherwise it will bump. - * - * @param enable True if styling should be added. - */ - updateReplacementFade(enable: boolean) { - this.setClass_('blocklyReplaceable', enable); - } - - /** - * Add or remove styling that shows that if the dragging block is dropped, - * this block will be connected to the input. - * - * @param _conn The connection on the input to highlight. - * @param _enable True if styling should be added. - */ - updateShapeForInputHighlight(_conn: Connection, _enable: boolean) { - // NOOP - } - /** Adds the given path as a connection highlight for the given connection. */ addConnectionHighlight( connection: RenderedConnection, diff --git a/core/renderers/common/renderer.ts b/core/renderers/common/renderer.ts index 5b7e687c25e..a05dea79ebc 100644 --- a/core/renderers/common/renderer.ts +++ b/core/renderers/common/renderer.ts @@ -11,6 +11,7 @@ import type {BlockSvg} from '../../block_svg.js'; import {Connection} from '../../connection.js'; import {ConnectionType} from '../../connection_type.js'; import type {IRegistrable} from '../../interfaces/i_registrable.js'; +import type {RenderedConnection} from '../../rendered_connection.js'; import type {BlockStyle, Theme} from '../../theme.js'; import {ConstantProvider} from './constants.js'; import {Drawer} from './drawer.js'; @@ -188,11 +189,11 @@ export class Renderer implements IRegistrable { /** * Determine whether or not to highlight a connection. * - * @param _conn The connection to determine whether or not to highlight. + * @param connection The connection to determine whether or not to highlight. * @returns True if we should highlight the connection. */ - shouldHighlightConnection(_conn: Connection): boolean { - return true; + shouldHighlightConnection(connection: RenderedConnection): boolean { + return !connection.getSourceBlock().isDragging(); } /** diff --git a/core/renderers/zelos/constants.ts b/core/renderers/zelos/constants.ts index 8cd36e02589..ac06de5018e 100644 --- a/core/renderers/zelos/constants.ts +++ b/core/renderers/zelos/constants.ts @@ -105,12 +105,6 @@ export class ConstantProvider extends BaseConstantProvider { /** The size of the selected glow. */ SELECTED_GLOW_SIZE = 0.5; - /** The replacement glow colour. */ - REPLACEMENT_GLOW_COLOUR = '#fff200'; - - /** The size of the selected glow. */ - REPLACEMENT_GLOW_SIZE = 2; - /** * The ID of the selected glow filter, or the empty string if no filter is * set. @@ -122,17 +116,6 @@ export class ConstantProvider extends BaseConstantProvider { */ private selectedGlowFilter: SVGElement | null = null; - /** - * The ID of the replacement glow filter, or the empty string if no filter - * is set. - */ - replacementGlowFilterId = ''; - - /** - * The element to use for a replacement glow, or null if not set. - */ - private replacementGlowFilter: SVGElement | null = null; - /** * The object containing information about the hexagon used for a boolean * reporter block. Null before init is called. @@ -269,16 +252,6 @@ export class ConstantProvider extends BaseConstantProvider { selectedGlowSize && !isNaN(selectedGlowSize) ? selectedGlowSize : this.SELECTED_GLOW_SIZE; - this.REPLACEMENT_GLOW_COLOUR = - theme.getComponentStyle('replacementGlowColour') || - this.REPLACEMENT_GLOW_COLOUR; - const replacementGlowSize = Number( - theme.getComponentStyle('replacementGlowSize'), - ); - this.REPLACEMENT_GLOW_SIZE = - replacementGlowSize && !isNaN(replacementGlowSize) - ? replacementGlowSize - : this.REPLACEMENT_GLOW_SIZE; } override dispose() { @@ -286,9 +259,6 @@ export class ConstantProvider extends BaseConstantProvider { if (this.selectedGlowFilter) { dom.removeNode(this.selectedGlowFilter); } - if (this.replacementGlowFilter) { - dom.removeNode(this.replacementGlowFilter); - } } override makeStartHat() { @@ -740,67 +710,6 @@ export class ConstantProvider extends BaseConstantProvider { this.selectedGlowFilterId = selectedGlowFilter.id; this.selectedGlowFilter = selectedGlowFilter; - // Using a dilate distorts the block shape. - // Instead use a gaussian blur, and then set all alpha to 1 with a transfer. - const replacementGlowFilter = dom.createSvgElement( - Svg.FILTER, - { - 'id': 'blocklyReplacementGlowFilter' + this.randomIdentifier, - 'height': '160%', - 'width': '180%', - 'y': '-30%', - 'x': '-40%', - }, - defs, - ); - dom.createSvgElement( - Svg.FEGAUSSIANBLUR, - {'in': 'SourceGraphic', 'stdDeviation': this.REPLACEMENT_GLOW_SIZE}, - replacementGlowFilter, - ); - // Set all gaussian blur pixels to 1 opacity before applying flood - const replacementComponentTransfer = dom.createSvgElement( - Svg.FECOMPONENTTRANSFER, - {'result': 'outBlur'}, - replacementGlowFilter, - ); - dom.createSvgElement( - Svg.FEFUNCA, - {'type': 'table', 'tableValues': '0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1'}, - replacementComponentTransfer, - ); - // Color the highlight - dom.createSvgElement( - Svg.FEFLOOD, - { - 'flood-color': this.REPLACEMENT_GLOW_COLOUR, - 'flood-opacity': 1, - 'result': 'outColor', - }, - replacementGlowFilter, - ); - dom.createSvgElement( - Svg.FECOMPOSITE, - { - 'in': 'outColor', - 'in2': 'outBlur', - 'operator': 'in', - 'result': 'outGlow', - }, - replacementGlowFilter, - ); - dom.createSvgElement( - Svg.FECOMPOSITE, - { - 'in': 'SourceGraphic', - 'in2': 'outGlow', - 'operator': 'over', - }, - replacementGlowFilter, - ); - this.replacementGlowFilterId = replacementGlowFilter.id; - this.replacementGlowFilter = replacementGlowFilter; - if (injectionDivIfIsParent) { // If this renderer is for the parent workspace, add CSS variables scoped // to the injection div referencing the created patterns so that CSS can @@ -809,10 +718,6 @@ export class ConstantProvider extends BaseConstantProvider { '--blocklySelectedGlowFilter', `url(#${this.selectedGlowFilterId})`, ); - injectionDivIfIsParent.style.setProperty( - '--blocklyReplacementGlowFilter', - `url(#${this.replacementGlowFilterId})`, - ); } } @@ -904,10 +809,6 @@ export class ConstantProvider extends BaseConstantProvider { `fill: none;`, `filter: var(--blocklySelectedGlowFilter);`, `}`, - - `${selector} .blocklyReplaceable>.blocklyPath {`, - `filter: var(--blocklyReplacementGlowFilter);`, - `}`, ]; } } diff --git a/core/renderers/zelos/path_object.ts b/core/renderers/zelos/path_object.ts index 3c304fd6bf8..6484be4e066 100644 --- a/core/renderers/zelos/path_object.ts +++ b/core/renderers/zelos/path_object.ts @@ -7,7 +7,6 @@ // Former goog.module ID: Blockly.zelos.PathObject import type {BlockSvg} from '../../block_svg.js'; -import type {Connection} from '../../connection.js'; import {FocusManager} from '../../focus_manager.js'; import type {BlockStyle} from '../../theme.js'; import * as dom from '../../utils/dom.js'; @@ -113,26 +112,6 @@ export class PathObject extends BasePathObject { } } - override updateReplacementFade(enable: boolean) { - this.setClass_('blocklyReplaceable', enable); - } - - override updateShapeForInputHighlight(conn: Connection, enable: boolean) { - const name = conn.getParentInput()!.name; - const outlinePath = this.getOutlinePath(name); - if (!outlinePath) { - return; - } - if (enable) { - outlinePath.setAttribute( - 'filter', - 'url(#' + this.constants.replacementGlowFilterId + ')', - ); - } else { - outlinePath.removeAttribute('filter'); - } - } - /** * Method that's called when the drawer is about to draw the block. */ diff --git a/core/renderers/zelos/renderer.ts b/core/renderers/zelos/renderer.ts index 367d96faf51..98ad5e74106 100644 --- a/core/renderers/zelos/renderer.ts +++ b/core/renderers/zelos/renderer.ts @@ -7,6 +7,8 @@ // Former goog.module ID: Blockly.zelos.Renderer import type {BlockSvg} from '../../block_svg.js'; +import {ConnectionType} from '../../connection_type.js'; +import type {RenderedConnection} from '../../rendered_connection.js'; import type {BlockStyle} from '../../theme.js'; import * as blockRendering from '../common/block_rendering.js'; import type {RenderInfo as BaseRenderInfo} from '../common/info.js'; @@ -86,6 +88,19 @@ export class Renderer extends BaseRenderer { override getConstants(): ConstantProvider { return this.constants_; } + + /** + * Determine whether or not to highlight a connection. + * + * @param connection The connection to determine whether or not to highlight. + * @returns True if we should highlight the connection. + */ + override shouldHighlightConnection(connection: RenderedConnection): boolean { + return ( + super.shouldHighlightConnection(connection) || + connection.type === ConnectionType.INPUT_VALUE + ); + } } blockRendering.register('zelos', Renderer); diff --git a/core/theme.ts b/core/theme.ts index e52eb1da9b3..023aee1fce0 100644 --- a/core/theme.ts +++ b/core/theme.ts @@ -215,8 +215,6 @@ export namespace Theme { cursorColour?: string; selectedGlowColour?: string; selectedGlowOpacity?: number; - replacementGlowColour?: string; - replacementGlowOpacity?: number; } export interface FontStyle { From b6db9fec8702482540e9ca63fc212c9c8dfdc71b Mon Sep 17 00:00:00 2001 From: Aaron Dodson Date: Tue, 13 Jan 2026 15:13:55 -0800 Subject: [PATCH 2/2] feat: Add `IPathObject.updateReplacing()` --- core/block_svg.ts | 6 +----- core/renderers/common/i_path_object.ts | 9 +++++++++ core/renderers/common/path_object.ts | 11 +++++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/core/block_svg.ts b/core/block_svg.ts index 269081a6e3c..231af949d02 100644 --- a/core/block_svg.ts +++ b/core/block_svg.ts @@ -1735,11 +1735,7 @@ export class BlockSvg * @internal */ fadeForReplacement(add: boolean) { - if (add) { - this.addClass('blocklyReplaceable'); - } else { - this.removeClass('blocklyReplaceable'); - } + this.pathObject.updateReplacing?.(add); } /** diff --git a/core/renderers/common/i_path_object.ts b/core/renderers/common/i_path_object.ts index a68c3a41148..a7379bbea54 100644 --- a/core/renderers/common/i_path_object.ts +++ b/core/renderers/common/i_path_object.ts @@ -106,4 +106,13 @@ export interface IPathObject { * @param blockStyle The block style to use. */ setStyle?(blockStyle: BlockStyle): void; + + /** + * Add or remove styling indicating that a block will be bumped out and + * replaced by another block that is mid-move. + * + * @param replacing True if the block is at risk of being replaced, false + * otherwise. + */ + updateReplacing?(replacing: boolean): void; } diff --git a/core/renderers/common/path_object.ts b/core/renderers/common/path_object.ts index 50ea9252df5..e6cb4962f22 100644 --- a/core/renderers/common/path_object.ts +++ b/core/renderers/common/path_object.ts @@ -191,6 +191,17 @@ export class PathObject implements IPathObject { this.setClass_('blocklyDraggable', enable); } + /** + * Add or remove styling indicating that a block will be bumped out and + * replaced by another block that is mid-move. + * + * @param replacing True if the block is at risk of being replaced, false + * otherwise. + */ + updateReplacing(replacing: boolean) { + this.setClass_('blocklyReplaceable', replacing); + } + /** Adds the given path as a connection highlight for the given connection. */ addConnectionHighlight( connection: RenderedConnection,