Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
62f3b89
chore: add tests for clipboard (#9254)
maribethb Aug 6, 2025
f9d0ec9
refactor: Associate comment bar buttons with the comment view. (#9278)
gonfunko Aug 6, 2025
7d1d745
fix: Drag immovable and shadow blocks along with their parent. (#9281)
gonfunko Aug 6, 2025
2e252a4
chore(deps): bump google-github-actions/deploy-appengine (#9273)
dependabot[bot] Aug 11, 2025
79d3140
chore(deps): bump actions/download-artifact from 4 to 5 (#9287)
dependabot[bot] Aug 11, 2025
b211c02
Change browser test timeout to 2 hours
RoboErikG Aug 11, 2025
fb63360
refactor: Remove duplicated method from contextmenu_items.ts. (#9289)
gonfunko Aug 12, 2025
e74910c
Update block-test version
RoboErikG Aug 12, 2025
4f4a450
Update dev-tools version in package.json
RoboErikG Aug 13, 2025
fba2231
Merge branch 'google:develop' into browser-test-2
RoboErikG Aug 13, 2025
7b784b5
Add a weekly schedule
RoboErikG Aug 13, 2025
34ea176
Update package-lock.json
RoboErikG Aug 13, 2025
d7efb28
Merge pull request #9297 from RoboErikG/browser-test-2
RoboErikG Aug 13, 2025
414f105
chore(deps): bump actions/first-interaction from 2 to 3
dependabot[bot] Aug 18, 2025
ef235cf
Merge pull request #9323 from google/dependabot/github_actions/develo…
RoboErikG Aug 18, 2025
a53db40
release: Merge branch 'develop' into rc/v12.3.0
gonfunko Aug 18, 2025
f682fa8
chore: lint error on only in mocha tests (#9300)
maribethb Aug 19, 2025
b5343f3
chore(deps): bump actions/checkout from 4 to 5 (#9320)
dependabot[bot] Aug 19, 2025
405f7da
fix: Fix positioning of pasted blocks and comments in RTL. (#9302)
gonfunko Aug 19, 2025
ac7619a
chore: Fix documentation generation warnings. (#9325)
gonfunko Aug 19, 2025
5cc95e4
fix: Show the delete cursor when dragging a block by an editable fiel…
gonfunko Aug 20, 2025
3e26b00
fix: Correct the alignment of narrow text in input fields. (#9327)
gonfunko Aug 21, 2025
c6730ab
fix: Fix bug that caused inadvertent scrolling when the `WidgetDiv` w…
gonfunko Aug 21, 2025
cacd358
chore(deps): bump eslint-plugin-prettier from 5.5.1 to 5.5.4 (#9319)
dependabot[bot] Aug 21, 2025
8024724
fix: pointercancel event is not handled (#9250)
nianxy Aug 21, 2025
86da7dc
release: Update version number to 12.3.0-beta.0
gonfunko Aug 21, 2025
dd05c8d
chore(deps): bump eslint-config-prettier from 10.1.5 to 10.1.8 (#9321)
dependabot[bot] Aug 22, 2025
3b498d1
fix: Allow reregistering fields. (#9290)
gonfunko Aug 22, 2025
8873e5f
chore(deps): bump chai from 5.2.1 to 6.0.1 (#9330)
dependabot[bot] Aug 26, 2025
e5eada8
chore(deps): bump eslint from 9.30.0 to 9.34.0 (#9329)
dependabot[bot] Aug 27, 2025
3d28ca8
chore: Add node.js v24 to CI build matrix (#9219)
cpcallen Aug 27, 2025
b2bbe96
fix: Prevent mocha tests failures when window does not have focus. (#…
gonfunko Aug 27, 2025
fd0aaed
fix: Fix bug that could cause errant line when rendering. (#9333)
gonfunko Aug 28, 2025
47307a9
refactor: Make focusable elements responsible for scrolling themselve…
gonfunko Aug 28, 2025
84933b9
chore: lint error on only in mocha tests (#9300)
maribethb Aug 19, 2025
29d5b43
chore(deps): bump actions/checkout from 4 to 5 (#9320)
dependabot[bot] Aug 19, 2025
10b1d1e
fix: Fix positioning of pasted blocks and comments in RTL. (#9302)
gonfunko Aug 19, 2025
9e1db9e
chore: Fix documentation generation warnings. (#9325)
gonfunko Aug 19, 2025
4a0b710
fix: Show the delete cursor when dragging a block by an editable fiel…
gonfunko Aug 20, 2025
cf93f07
fix: Correct the alignment of narrow text in input fields. (#9327)
gonfunko Aug 21, 2025
4891659
fix: Fix bug that caused inadvertent scrolling when the `WidgetDiv` w…
gonfunko Aug 21, 2025
c32f6db
chore(deps): bump eslint-plugin-prettier from 5.5.1 to 5.5.4 (#9319)
dependabot[bot] Aug 21, 2025
be5f5a2
fix: pointercancel event is not handled (#9250)
nianxy Aug 21, 2025
e358f4e
chore(deps): bump eslint-config-prettier from 10.1.5 to 10.1.8 (#9321)
dependabot[bot] Aug 22, 2025
cb69892
fix: Allow reregistering fields. (#9290)
gonfunko Aug 22, 2025
aeb3e5e
chore(deps): bump chai from 5.2.1 to 6.0.1 (#9330)
dependabot[bot] Aug 26, 2025
90580a8
chore(deps): bump eslint from 9.30.0 to 9.34.0 (#9329)
dependabot[bot] Aug 27, 2025
f10454c
chore: Add node.js v24 to CI build matrix (#9219)
cpcallen Aug 27, 2025
b0569c4
fix: Prevent mocha tests failures when window does not have focus. (#…
gonfunko Aug 27, 2025
e51efe4
fix: Fix bug that could cause errant line when rendering. (#9333)
gonfunko Aug 28, 2025
5afc0d6
refactor: Make focusable elements responsible for scrolling themselve…
gonfunko Aug 28, 2025
5f21e9b
release: Update version number to 12.3.0
gonfunko Aug 28, 2025
2bd8b63
Merge pull request #9335 from google/rc/v12.3.0
gonfunko Aug 28, 2025
55f5d64
release: Merge `master` back into `develop`.
gonfunko Aug 28, 2025
9b60088
fix: Fix bug that could caused variable map to be left in an inconsis…
gonfunko Sep 2, 2025
10e6df3
fix: Allow cross origin requests for Blockly assets. (#9342)
gonfunko Sep 5, 2025
0a4bb5c
chore(deps): bump prettier-plugin-organize-imports from 4.1.0 to 4.2.0
dependabot[bot] Sep 8, 2025
dd460f2
Merge pull request #9343 from google/dependabot/npm_and_yarn/develop/…
RoboErikG Sep 8, 2025
2c46686
fix: minor fixes to translation files (#9350)
maribethb Sep 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/appengine_deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
steps:
# Checks-out the repository under $GITHUB_WORKSPACE.
# When running manually this checks out the master branch.
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Prepare demo files
# Install all dependencies, then copy all the files needed for demos.
Expand All @@ -36,13 +36,13 @@ jobs:
needs: prepare
steps:
- name: Download prepared files
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: appengine_files
path: _deploy/

- name: Deploy to App Engine
uses: google-github-actions/deploy-appengine@v2.1.5
uses: google-github-actions/deploy-appengine@v2.1.7
# For parameters see:
# https://github.com/google-github-actions/deploy-appengine#inputs
with:
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/browser_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ name: Run browser manually

on:
workflow_dispatch:
schedule:
- cron: '0 6 * * 1' # Runs every Monday at 06:00 UTC

permissions:
contents: read

jobs:
build:
timeout-minutes: 10
timeout-minutes: 120
runs-on: ${{ matrix.os }}

strategy:
Expand All @@ -24,7 +26,7 @@ jobs:
# https://nodejs.org/en/about/releases/

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false

Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ jobs:
# TODO (#2114): re-enable osx build.
# os: [ubuntu-latest, macos-latest]
os: [ubuntu-latest]
node-version: [18.x, 20.x, 22.x]
node-version: [18.x, 20.x, 22.x, 24.x]
# See supported Node.js release schedule at
# https://nodejs.org/en/about/releases/

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false

Expand Down Expand Up @@ -54,7 +54,7 @@ jobs:
timeout-minutes: 5
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Use Node.js 20.x
uses: actions/setup-node@v4
Expand All @@ -71,7 +71,7 @@ jobs:
timeout-minutes: 5
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Use Node.js 20.x
uses: actions/setup-node@v4
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/keyboard_plugin_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ jobs:

steps:
- name: Checkout core Blockly
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
path: core-blockly

- name: Checkout keyboard navigation plugin
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: 'google/blockly-keyboard-experimentation'
ref: 'main'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/welcome_new_contributors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
permissions:
pull-requests: write
steps:
- uses: actions/first-interaction@v2
- uses: actions/first-interaction@v3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
pr-message: >
Expand Down
2 changes: 2 additions & 0 deletions appengine/app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ handlers:
# Blockly files.
- url: /static
static_dir: static
http_headers:
Access-Control-Allow-Origin: "*"
secure: always

# Storage API.
Expand Down
44 changes: 27 additions & 17 deletions core/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -501,22 +501,32 @@ export class Block {
// Detach this block from the parent's tree.
this.previousConnection.disconnect();
}
const nextBlock = this.getNextBlock();
if (opt_healStack && nextBlock && !nextBlock.isShadow()) {
// Disconnect the next statement.
const nextTarget = this.nextConnection?.targetConnection ?? null;
nextTarget?.disconnect();
if (
previousTarget &&
this.workspace.connectionChecker.canConnect(
previousTarget,
nextTarget,
false,
)
) {
// Attach the next statement to the previous statement.
previousTarget.connect(nextTarget!);
}

if (!opt_healStack) return;

// Immovable or shadow next blocks need to move along with the block; keep
// going until we encounter a normal block or run off the end of the stack.
let nextBlock = this.getNextBlock();
while (nextBlock && (nextBlock.isShadow() || !nextBlock.isMovable())) {
nextBlock = nextBlock.getNextBlock();
}
if (!nextBlock) return;

// Disconnect the next statement.
const nextTarget =
nextBlock.previousConnection?.targetBlock()?.nextConnection
?.targetConnection ?? null;
nextTarget?.disconnect();
if (
previousTarget &&
this.workspace.connectionChecker.canConnect(
previousTarget,
nextTarget,
false,
)
) {
// Attach the next statement to the previous statement.
previousTarget.connect(nextTarget!);
}
}

Expand Down Expand Up @@ -1116,7 +1126,7 @@ export class Block {
/**
* Returns a generator that provides every field on the block.
*
* @yields A generator that can be used to iterate the fields on the block.
* @returns A generator that can be used to iterate the fields on the block.
*/
*getFields(): Generator<Field, undefined, void> {
for (const input of this.inputList) {
Expand Down
3 changes: 3 additions & 0 deletions core/block_svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1844,6 +1844,9 @@ export class BlockSvg
/** See IFocusableNode.onNodeFocus. */
onNodeFocus(): void {
this.select();
this.workspace.scrollBoundsIntoView(
this.getBoundingRectangleWithoutChildren(),
);
}

/** See IFocusableNode.onNodeBlur. */
Expand Down
4 changes: 4 additions & 0 deletions core/bubbles/bubble.ts
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,10 @@ export abstract class Bubble implements IBubble, ISelectable, IFocusableNode {
onNodeFocus(): void {
this.select();
this.bringToFront();
const xy = this.getRelativeToSurfaceXY();
const size = this.getSize();
const bounds = new Rect(xy.y, xy.y + size.height, xy.x, xy.x + size.width);
this.workspace.scrollBoundsIntoView(bounds);
}

/** See IFocusableNode.onNodeBlur. */
Expand Down
3 changes: 3 additions & 0 deletions core/clipboard/block_paster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ export function moveBlockToNotConflict(
block: BlockSvg,
originalPosition: Coordinate,
) {
if (block.workspace.RTL) {
originalPosition.x = block.workspace.getWidth() - originalPosition.x;
}
const workspace = block.workspace;
const snapRadius = config.snapRadius;
const bumpOffset = Coordinate.difference(
Expand Down
9 changes: 5 additions & 4 deletions core/comments/collapse_comment_bar_button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import * as dom from '../utils/dom.js';
import {Svg} from '../utils/svg.js';
import type {WorkspaceSvg} from '../workspace_svg.js';
import {CommentBarButton} from './comment_bar_button.js';
import type {CommentView} from './comment_view.js';

/**
* Magic string appended to the comment ID to create a unique ID for this button.
Expand Down Expand Up @@ -42,8 +43,9 @@ export class CollapseCommentBarButton extends CommentBarButton {
protected readonly id: string,
protected readonly workspace: WorkspaceSvg,
protected readonly container: SVGGElement,
protected readonly commentView: CommentView,
) {
super(id, workspace, container);
super(id, workspace, container, commentView);

this.icon = dom.createSvgElement(
Svg.IMAGE,
Expand Down Expand Up @@ -86,14 +88,13 @@ export class CollapseCommentBarButton extends CommentBarButton {
override performAction(e?: Event) {
touch.clearTouchIdentifier();

const comment = this.getParentComment();
comment.view.bringToFront();
this.getCommentView().bringToFront();
if (e && e instanceof PointerEvent && browserEvents.isRightButton(e)) {
e.stopPropagation();
return;
}

comment.setCollapsed(!comment.isCollapsed());
this.getCommentView().setCollapsed(!this.getCommentView().isCollapsed());
this.workspace.hideChaff();

e?.stopPropagation();
Expand Down
24 changes: 12 additions & 12 deletions core/comments/comment_bar_button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import type {IFocusableNode} from '../interfaces/i_focusable_node.js';
import {Rect} from '../utils/rect.js';
import type {WorkspaceSvg} from '../workspace_svg.js';
import type {RenderedWorkspaceComment} from './rendered_workspace_comment.js';
import type {CommentView} from './comment_view.js';

/**
* Button displayed on a comment's top bar.
Expand All @@ -29,6 +29,7 @@ export abstract class CommentBarButton implements IFocusableNode {
protected readonly id: string,
protected readonly workspace: WorkspaceSvg,
protected readonly container: SVGGElement,
protected readonly commentView: CommentView,
) {}

/**
Expand All @@ -39,17 +40,10 @@ export abstract class CommentBarButton implements IFocusableNode {
}

/**
* Returns the parent comment of this comment bar button.
* Returns the parent comment view of this comment bar button.
*/
getParentComment(): RenderedWorkspaceComment {
const comment = this.workspace.getCommentById(this.id);
if (!comment) {
throw new Error(
`Comment bar button ${this.id} has no corresponding comment`,
);
}

return comment;
getCommentView(): CommentView {
return this.commentView;
}

/** Adjusts the position of this button within its parent container. */
Expand Down Expand Up @@ -93,7 +87,13 @@ export abstract class CommentBarButton implements IFocusableNode {
}

/** Called when this button's focusable DOM element gains focus. */
onNodeFocus() {}
onNodeFocus() {
const commentView = this.getCommentView();
const xy = commentView.getRelativeToSurfaceXY();
const size = commentView.getSize();
const bounds = new Rect(xy.y, xy.y + size.height, xy.x, xy.x + size.width);
commentView.workspace.scrollBoundsIntoView(bounds);
}

/** Called when this button's focusable DOM element loses focus. */
onNodeBlur() {}
Expand Down
13 changes: 12 additions & 1 deletion core/comments/comment_editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import {IFocusableNode} from '../interfaces/i_focusable_node.js';
import {IFocusableTree} from '../interfaces/i_focusable_tree.js';
import * as touch from '../touch.js';
import * as dom from '../utils/dom.js';
import {Rect} from '../utils/rect.js';
import {Size} from '../utils/size.js';
import {Svg} from '../utils/svg.js';
import * as svgMath from '../utils/svg_math.js';
import {WorkspaceSvg} from '../workspace_svg.js';

/**
Expand Down Expand Up @@ -188,7 +190,16 @@ export class CommentEditor implements IFocusableNode {
getFocusableTree(): IFocusableTree {
return this.workspace;
}
onNodeFocus(): void {}
onNodeFocus(): void {
const bbox = Rect.from(this.foreignObject.getBoundingClientRect());
this.workspace.scrollBoundsIntoView(
Rect.createFromPoint(
svgMath.screenToWsCoordinates(this.workspace, bbox.getOrigin()),
bbox.getWidth(),
bbox.getHeight(),
),
);
}
onNodeBlur(): void {}
canBeFocused(): boolean {
if (this.id) return true;
Expand Down
16 changes: 12 additions & 4 deletions core/comments/comment_view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export class CommentView implements IRenderedElement {

constructor(
readonly workspace: WorkspaceSvg,
private commentId: string,
readonly commentId: string,
) {
this.svgRoot = dom.createSvgElement(Svg.G, {
'class': 'blocklyComment blocklyEditable blocklyDraggable',
Expand Down Expand Up @@ -176,12 +176,18 @@ export class CommentView implements IRenderedElement {
this.commentId,
this.workspace,
topBarGroup,
this,
);
const foldoutButton = new CollapseCommentBarButton(
this.commentId,
this.workspace,
topBarGroup,
this,
);
this.addDisposeListener(() => {
deleteButton.dispose();
foldoutButton.dispose();
});
const textPreview = dom.createSvgElement(
Svg.TEXT,
{
Expand Down Expand Up @@ -362,7 +368,10 @@ export class CommentView implements IRenderedElement {

const textPreviewWidth =
size.width - foldoutSize.getWidth() - deleteSize.getWidth();
this.textPreview.setAttribute('x', `${foldoutSize.getWidth()}`);
this.textPreview.setAttribute(
'x',
`${(this.workspace.RTL ? -1 : 1) * foldoutSize.getWidth()}`,
);
this.textPreview.setAttribute(
'y',
`${textPreviewMargin + textPreviewSize.height / 2}`,
Expand Down Expand Up @@ -612,13 +621,12 @@ export class CommentView implements IRenderedElement {
/** Disposes of this comment view. */
dispose() {
this.disposing = true;
this.foldoutButton.dispose();
this.deleteButton.dispose();
dom.removeNode(this.svgRoot);
// Loop through listeners backwards in case they remove themselves.
for (let i = this.disposeListeners.length - 1; i >= 0; i--) {
this.disposeListeners[i]();
}
this.disposeListeners.length = 0;
this.disposed = true;
}

Expand Down
Loading
Loading