Skip to content

Upgrade Blockly to v12.4.1#44

Open
badvision wants to merge 14 commits intomasterfrom
necromancy
Open

Upgrade Blockly to v12.4.1#44
badvision wants to merge 14 commits intomasterfrom
necromancy

Conversation

@badvision
Copy link
Copy Markdown
Owner

This upgrades us from the pre-1.0 version of Blockly to the most recent V12 version, incorporating over 9 years of fixes and improvements.

Editing

  • Copy/paste across scripts — blocks can be copied to clipboard and pasted into another script window
  • Improved undo/redo — more reliable; covers variable creation/deletion which the old version often missed
  • Block comments — collapsible comment bubbles on blocks (you may have noticed the "Describe this function..." default already
    appearing)
  • Workspace comments — floating sticky notes directly on the canvas (new in v5+)
  • Collapse blocks — right-click → Collapse to hide a complex subtree as a single labelled block

Variable & Function Management

  • "Create variable..." dialog — proper modal; the old version had a fragile prompt hack
  • Variable renaming — rename from the variable block's context menu; propagates everywhere in the workspace
  • Function editing — procedures_defreturn mutation dialog for adding/removing parameters is more robust

Performance

  • Lazy rendering — only visible blocks are rendered; large scripts (like the 48-block Door script) scroll and load noticeably faster
  • Better memory management — old version leaked event listeners on block disposal

Brendan Robert and others added 5 commits February 22, 2026 15:25
- Replace vendored blockly_compressed.js, blocks_compressed.js, media/, and
  msg/en.js with Blockly v12.4.1 UMD bundles
- Rewrite mythos_uncompressed.js for v12 API: DOMParser instead of
  Blockly.Xml.textToDom, fixed domToWorkspace arg order, workspace.getTopBlocks(),
  ES6 FieldTextArea class, removed goog.* dependencies
- Fix block rendering in JavaFX: add BLOCK_CREATE listener with setTimeout(0)
  to re-queue all descendants after placement (works around JavaFX immediate-
  render mode dequeuing children before they initialise)
- Convert 39 toolbox <block type="text"> to <shadow> in value positions
- Fix applyChanges(): strip Blockly v12 <variables> prefix before JAXB parse;
  broaden catch to Exception so JSException cannot prevent window close
- Fix OutlawSchema.xsd: change block @id from xs:integer to xs:string (Blockly
  v12 generates alphanumeric IDs); add <shadow> element to value type so shadow
  default blocks survive save/reload
- Implement addCustomVariables() using workspace.createVariable(); call after
  domToWorkspace so variables survive ws.clear()
- Switch Variables toolbox category to custom="VARIABLE" for dynamic Blockly-
  managed list with "Create variable..." button
- Forward scroll wheel events from JavaFX to Blockly workspace via setOnScroll
  interceptor executing ws.scroll() JS; correct Y-axis direction for macOS
- Add duplicate-window guard to MythosEditor.show() via static activeEditors map
- Add AGENTS.md to PackPartitions documenting the flag/state/block pipeline

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each script editor window runs in its own JavaFX WebView with an isolated
JS heap, so Blockly v12's module-level clipboard stash (stashedCopyData /
stashedWorkspace) is invisible across windows.

Solution: add a static Java-side clipboard (`MythosEditor.sharedBlockClipboard`)
shared by all editor instances.

- MythosEditor.setClipboard(json) / getClipboard() expose the shared stash
  to JS via the existing Mythos.editor bridge.
- editor.html registers a keydown capture-phase listener:
    Copy  (Ctrl/Cmd+C) – after Blockly's internal copy handler runs
                          (deferred via setTimeout), serialise the selected
                          block's toCopyData() JSON and push it to Java.
    Paste (Ctrl/Cmd+V) – before Blockly's paste handler fires, pull the
                          Java clipboard JSON and inject it into Blockly's
                          stash via Blockly.clipboard.setLastCopiedData() /
                          setLastCopiedWorkspace(), pointing the workspace
                          at the current window.  Same-window paste is
                          unaffected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
In Blockly v12, registerDefaultOptions() only registers workspace and
block options; it deliberately omits registerCommentOptions(), which
registers the three workspace-comment context-menu items (Add Comment,
Duplicate Comment, Delete Comment) and the "commentCreate" item that
appears on right-click of the canvas to create a floating sticky note.

Call registerCommentOptions() once after Blockly.inject() so that
right-clicking the canvas surface shows "Add Comment", allowing users
to place freestanding workspace comment notes.  The options.comments
flag was already true (inherited from hasCategories(toolbox)), so no
inject option change is needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs fixed in MythosFieldTextArea.showEditor_():

1. Editor invisible until key press: WidgetDiv.show() was called without
   the workspace argument (4th param) and with the default takeFocus=true
   (5th param). In Blockly v12 this causes takeEphemeralFocus() to focus
   the WidgetDiv container div, which in JavaFX WebView's immediate-render
   mode prevents the textarea from receiving input until a key re-fires
   focus. Fixed by passing workspace and false for takeFocus so focus goes
   directly to the textarea via htmlInput.focus().

2. Editor appears at bottom of screen: No positioning was applied after
   show(). The blocklyWidgetDiv is position:absolute with no left/top set,
   so it rendered at its default DOM position. Fixed by reading the field's
   screen coordinates via getScaledBBox() and writing left/top directly on
   the div, placing the editor just below the clicked field.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@8bitweapon
Copy link
Copy Markdown
Collaborator

8bitweapon commented Feb 22, 2026 via email

Brendan Robert and others added 9 commits February 22, 2026 16:17
- Replace htmlInput.select() with setSelectionRange(len, len) so the
  editor opens with cursor at end of text rather than all text selected
- Add border (2px solid #ccc), border-radius, padding, box-shadow, and
  white background to the textarea so the editor has a visible frame
- Set resize: vertical so users can drag the textarea taller if needed
- Set WidgetDiv background to transparent to avoid a double-background

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy fix: read Blockly's already-stashed copy data via
getLastCopiedData() instead of re-calling toCopyData() on the
(possibly-unfocused) selected block.  Blockly v12 uses a FocusManager
whose focused node may differ from or outlive what getSelected()
returns, so reading the stash directly is more reliable.

Paste fix: after injecting the Java-side JSON into Blockly's stash,
call Blockly.clipboard.paste(copyData, Mythos.workspace) directly and
stopPropagation so Blockly's shortcut handler does not run a second
paste.  The original code only set the stash and relied on Blockly's
preconditionFn to allow the paste, but that precondition checks the
stashed workspace origin in ways that could silently block the paste.

Undo-delete fix: patch BlockSvg.prototype.dispose (the rendered
subclass) instead of Block.prototype.dispose.  BlockSvg.dispose calls
this.unplug() before delegating to super, so patching the base class
was too late to capture the parent connection; patching BlockSvg
ensures captureConnInfo() runs before unplug() clears the connection.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…osition

When deleting B from chain A→B→C, the undo reconnection was calling
pConn.connect(bPrev) while A.nextConnection was already occupied by C
(from the healStack). Blockly v12 walks to the end of the chain in this
case and appends B there rather than inserting it between A and C.

Fix the middle-of-chain case by:
1. Explicitly disconnecting C from A.nextConnection before connecting B
2. Connecting B to A.nextConnection
3. Re-attaching C to B.nextConnection

Also capture nextBlockId in captureConnInfo (though the fix uses
targetBlock() at undo time, which is more reliable). Apply the same
explicit-disconnect pattern to value-input reconnection.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three changes to the cross-window clipboard bridge in editor.html:

1. Call Blockly.clipboard.paste(Mythos.workspace) with ONE argument
   instead of paste(copyData, Mythos.workspace) with two.  The confirmed
   paste() signature is paste(a,b,c): when a&&b are both truthy it calls
   pasteFromData(a,b) directly, which short-circuits the stash entirely
   and ignores setLastCopiedWorkspace().  With one arg b is undefined so
   a&&b is false and paste() reads from stashedCopyData/stashedWorkspace
   -- the stash we just primed with setLastCopiedData()+
   setLastCopiedWorkspace().  This is exactly how Blockly's own Ctrl+V
   shortcut handler drives paste internally.

2. Move e.preventDefault()/e.stopPropagation() to after the data checks,
   so a same-window paste where getClipboard() returns null still lets
   Blockly's own shortcut handler fire normally.

3. Add Mythos.editor.log() calls: one to confirm getClipboard() returned
   data across the JS/Java bridge, and one to surface any paste errors
   that were previously swallowed by catch(err){}.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CommentView.setSizeWithoutFiringEvents() calls getBBox() on SVG child
elements (topbar background RECT, resize-handle IMAGE, delete/collapse
button icons) to compute the exact pixel geometry for layout.  In
JavaFX's embedded WebKit, getBBox() returns {width:0, height:0} for
elements that are in the DOM but have not yet completed a browser
layout/paint cycle.

The constructor calls setSizeWithoutFiringEvents() synchronously
immediately after appending the SVG root to the layer, so getBBox()
always returns zeros.  This produces three layout defects:

  - foreignObject y=0 instead of y=24: the textarea overlaps the topbar
  - resize handle placed at (120,100) instead of (108,88): off the edge
  - delete button icon positioned at x=0 because container bbox is 0

Fix: add a COMMENT_CREATE workspace event listener that defers a second
setSizeWithoutFiringEvents() call via setTimeout(0), identical in
structure to the existing BLOCK_CREATE re-render workaround.  One
browser paint cycle is enough for getBBox() to return the real
CSS-computed dimensions.  view.setSizeWithoutFiringEvents is called
directly (rather than comment.setSize) to avoid emitting a spurious
COMMENT_RESIZE undo event during initialisation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Undo reconnection was using parentBlock.nextConnection for ALL
previousConnection-attached blocks.  For blocks at the head of an IF
THEN or ELSE branch the parent connection is the statement input
(DO0/DO1/etc.), not nextConnection.  captureConnInfo now detects
statement-input parents by comparing the connection object identity
against parentBlock.nextConnection, falls back to walking inputList to
find the input name, and stores it as stmtInputName.  The BlockDelete
undo patch resolves pConn via getInput(stmtInputName).connection when
stmtInputName is set.

COMMENT_CREATE handler: increase defer to 50ms and add console.warn
traces to diagnose remaining layout issues.

Copy handler: add console.warn traces to confirm Java bridge is reached.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…oach

Comments – two bugs fixed:
  1. Position: MouseEvent.x/y undefined in JavaFX WebKit → every new comment
     appeared at workspace (0,0) regardless of right-click position.  Fixed
     by polyfilling MouseEvent.prototype.x/y → clientX/clientY.
  2. Layout: topBarBackground <rect> height set via CSS only; getBBox().height
     returns 0 in JavaFX WebKit (getBBox ignores CSS).  CommentEditor.updateSize
     uses that to set foreignObject.y, so the textarea overlapped the topbar.
     Fixed by stamping height='24' attribute on the rect before calling
     setSizeWithoutFiringEvents so getBBox returns the real topbar height.

Cross-window paste – replaced unreliable JS→Java bridge with Java-side approach:
  - Cmd+C: addEventFilter reads Blockly's copy stash from JS via executeScript
    and stores in shared static field (fallback for JS-side copy handler).
  - Cmd+V: addEventFilter reads shared static field (pure Java), injects JSON
    into Blockly stash and calls paste() via executeScript. Consumes the JavaFX
    event only when cross-window data is present, so same-window paste is
    unaffected.  Bypasses the JS→Java bridge entirely on the paste side.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Object.defineProperty on host object prototypes (like MouseEvent.prototype)
throws in the WebKit version embedded in JavaFX because those prototypes are
non-extensible.  The uncaught TypeError stopped script execution before
Blockly.inject(), leaving Mythos.workspace undefined and crashing every
subsequent call.

Wrapping in try/catch means the polyfill gracefully no-ops on this platform.
The comment position fix via the polyfill will not apply; the topBarBackground
height fix (which is independent) still runs normally.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removes:
- registerCommentOptions() call (context menu "Add Comment" item)
- COMMENT_CREATE change-listener handler
- blocklyCommentForeignObject / blocklyComment CSS rules
- MouseEvent.x/y polyfill

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants