From a024d3879c0c437e8f91ccc6f6e1f0a4c8ccbd19 Mon Sep 17 00:00:00 2001 From: Kouji Takao Date: Fri, 16 Jan 2026 17:45:11 +0900 Subject: [PATCH 1/3] feat: update MicroBit More tilt gesture labels to match microbit extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update Japanese translations for tilt gestures to use "に" instead of "へ": - "上へ傾いた" → "上に傾いた" - "下へ傾いた" → "下に傾いた" - "左へ傾いた" → "左に傾いた" - "右へ傾いた" → "右に傾いた" This aligns the MicroBit More extension labels with the original microbit extension for consistency. Refs smalruby/smalruby3-develop#20 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/locales/ja-Hira.js | 8 +++++++- src/locales/ja.js | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/locales/ja-Hira.js b/src/locales/ja-Hira.js index fa23ee50320..77ed818c791 100644 --- a/src/locales/ja-Hira.js +++ b/src/locales/ja-Hira.js @@ -210,5 +210,11 @@ export default { 'gui.smalruby3.blockDisplayModal.operator_mod': '( ) を ( ) でわったあまり', 'gui.smalruby3.blockDisplayModal.operator_round': '( ) をししゃごにゅう', 'gui.smalruby3.blockDisplayModal.operator_mathop': '( ) の [ぜったいち▼]', - 'gui.extensionLibrary.showAllExtensions': 'すべてのかくちょうきのうをひょうじ' + 'gui.extensionLibrary.showAllExtensions': 'すべてのかくちょうきのうをひょうじ', + + // MicroBit More - Tilt gesture labels (override to match microbit extension) + 'mbitMore.gesturesMenu.tiltUp': 'うえにかたむいた', + 'mbitMore.gesturesMenu.tiltDown': 'したにかたむいた', + 'mbitMore.gesturesMenu.tiltLeft': 'ひだりにかたむいた', + 'mbitMore.gesturesMenu.tiltRight': 'みぎにかたむいた' }; diff --git a/src/locales/ja.js b/src/locales/ja.js index 12312bed5d1..8caa949a8fb 100644 --- a/src/locales/ja.js +++ b/src/locales/ja.js @@ -247,5 +247,11 @@ export default { 'gui.smalruby3.blockDisplayModal.operator_mod': '( ) を ( ) で割った余り', 'gui.smalruby3.blockDisplayModal.operator_round': '( ) を四捨五入', 'gui.smalruby3.blockDisplayModal.operator_mathop': '( ) の [絶対値▼]', - 'gui.extensionLibrary.showAllExtensions': 'すべての拡張機能を表示' + 'gui.extensionLibrary.showAllExtensions': 'すべての拡張機能を表示', + + // MicroBit More - Tilt gesture labels (override to match microbit extension) + 'mbitMore.gesturesMenu.tiltUp': '上に傾いた', + 'mbitMore.gesturesMenu.tiltDown': '下に傾いた', + 'mbitMore.gesturesMenu.tiltLeft': '左に傾いた', + 'mbitMore.gesturesMenu.tiltRight': '右に傾いた' }; From 33b4f8e4a4b75c2069270fc94128a99f0127620f Mon Sep 17 00:00:00 2001 From: Kouji Takao Date: Sat, 17 Jan 2026 02:05:01 +0900 Subject: [PATCH 2/3] feat: add Ruby conversion for MicroBit More tilt and pin blocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement bidirectional conversion between MicroBit More blocks and Ruby: Block to Ruby conversions: - whenPinConnected: microbit_more.when_pin_connected(pin) - whenGesture(MOVED): microbit_more.when("moved") - whenGesture(TILTED): microbit_more.when("tilted_any") - whenGesture(TILT_UP/DOWN/LEFT/RIGHT): microbit_more.when("tilted_front/back/left/right") - isTilted: microbit_more.tilted?(direction) - getTiltAngle: microbit_more.tilt_angle(direction) Ruby to Block conversions: - All above conversions work bidirectionally Snippet updates: - Updated button_pressed? description to match block label - Changed default gesture from "shake" to "moved" - Added snippets for when_pin_connected, tilted?, tilt_angle Fixes #20 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../ruby-tab/microbit-more-snippets.json | 21 +++- src/lib/ruby-generator/microbit_more.js | 37 +++++- .../ruby-to-blocks-converter/microbit_more.js | 91 +++++++++++++- .../ruby-tab/extension_microbit_more.test.js | 45 +++++++ .../lib/ruby-generator/microbit_more.test.js | 90 +++++++++++++ .../microbit_more.test.js | 118 ++++++++++++++++++ 6 files changed, 389 insertions(+), 13 deletions(-) create mode 100644 test/unit/lib/ruby-generator/microbit_more.test.js create mode 100644 test/unit/lib/ruby-to-blocks-converter/microbit_more.test.js diff --git a/src/containers/ruby-tab/microbit-more-snippets.json b/src/containers/ruby-tab/microbit-more-snippets.json index 45e72a7a2d2..79df2adce1f 100644 --- a/src/containers/ruby-tab/microbit-more-snippets.json +++ b/src/containers/ruby-tab/microbit-more-snippets.json @@ -11,7 +11,7 @@ }, "microbit_more.button_pressed?": { "snippet": "microbit_more.button_pressed?(\"${1:A}\")", - "description": "ボタン [A▼] が押されている", + "description": "ボタン [A▼] が押された", "type": "value" }, "microbit_more.when_pin_is": { @@ -24,11 +24,26 @@ "description": "ピン [ログ▼] がタッチされている", "type": "value" }, + "microbit_more.when_pin_connected": { + "snippet": "microbit_more.when_pin_connected(${1:0}) do\n\t${2}\nend", + "description": "ピン [0▼] がつながったとき", + "type": "event" + }, "microbit_more.when": { - "snippet": "microbit_more.when(\"${1:shake}\") do\n\t${2}\nend", - "description": "[ゆさぶられた▼] とき", + "snippet": "microbit_more.when(\"${1:moved}\") do\n\t${2}\nend", + "description": "[動いた▼] とき", "type": "event" }, + "microbit_more.tilted?": { + "snippet": "microbit_more.tilted?(\"${1:any}\")", + "description": "[どれかの向き▼] に傾いた", + "type": "value" + }, + "microbit_more.tilt_angle": { + "snippet": "microbit_more.tilt_angle(\"${1:front}\")", + "description": "[前▼] 方向の傾き", + "type": "variable" + }, "microbit_more.display_pattern": { "snippet": "microbit_more.display_pattern(\n\t\"${1:.1.1.}\",\n\t\"${2:1.1.1}\",\n\t\"${3:1...1}\",\n\t\"${4:.1.1.}\",\n\t\"${5:..1..}\"\n)", "description": "パターン (♡▼) を表示する", diff --git a/src/lib/ruby-generator/microbit_more.js b/src/lib/ruby-generator/microbit_more.js index b271a00529b..392901c506d 100644 --- a/src/lib/ruby-generator/microbit_more.js +++ b/src/lib/ruby-generator/microbit_more.js @@ -40,18 +40,26 @@ export default function (Generator) { return [`microbit_more.pin_is_touched?(${name})`, Generator.ORDER_FUNCTION_CALL]; }; + Generator.microbitMore_whenPinConnected = function (block) { + block.isStatement = true; + const pin = Generator.getFieldValue(block, 'PIN', 'P0').replace('P', ''); + return `microbit_more.when_pin_connected(${pin}) do\n`; + }; + const GestureLabel = { - TILT_UP: 'tilt up', - TILT_DOWN: 'tilt down', - TILT_LEFT: 'tilt left', - TILT_RIGHT: 'tilt right', + TILT_UP: 'tilted_front', + TILT_DOWN: 'tilted_back', + TILT_LEFT: 'tilted_left', + TILT_RIGHT: 'tilted_right', FACE_UP: 'face up', FACE_DOWN: 'face down', FREEFALL: 'freefall', G3: '3G', G6: '6G', G8: '8G', - SHAKE: 'shake' + SHAKE: 'shake', + MOVED: 'moved', + TILTED: 'tilted_any' }; Generator.microbitMore_whenGesture = function (block) { block.isStatement = true; @@ -60,6 +68,25 @@ export default function (Generator) { return `microbit_more.when(${gestureLabel}) do\n`; }; + const TiltedDirectionLabel = { + ANY: 'any', + TILT_UP: 'front', + TILT_DOWN: 'back', + TILT_LEFT: 'left', + TILT_RIGHT: 'right' + }; + Generator.microbitMore_isTilted = function (block) { + const direction = Generator.getFieldValue(block, 'DIRECTION', 'ANY'); + const directionLabel = Generator.quote_(TiltedDirectionLabel[direction]); + return [`microbit_more.tilted?(${directionLabel})`, Generator.ORDER_FUNCTION_CALL]; + }; + + Generator.microbitMore_getTiltAngle = function (block) { + const direction = Generator.getFieldValue(block, 'DIRECTION', 'FRONT').toLowerCase(); + const directionLabel = Generator.quote_(direction); + return [`microbit_more.tilt_angle(${directionLabel})`, Generator.ORDER_FUNCTION_CALL]; + }; + const makeMatrixArgs = function (matrix) { matrix = matrix.replace(/0/g, '.'); matrix = matrix.match(/.{5}/g).map(s => Generator.quote_(s)); diff --git a/src/lib/ruby-to-blocks-converter/microbit_more.js b/src/lib/ruby-to-blocks-converter/microbit_more.js index d3a5f01f5d5..f4a3721bbcb 100644 --- a/src/lib/ruby-to-blocks-converter/microbit_more.js +++ b/src/lib/ruby-to-blocks-converter/microbit_more.js @@ -30,17 +30,19 @@ const TouchEventMenu = { }; const GestureMenu = { - TILT_UP: 'tilt up', - TILT_DOWN: 'tilt down', - TILT_LEFT: 'tilt left', - TILT_RIGHT: 'tilt right', + TILT_UP: 'tilted_front', + TILT_DOWN: 'tilted_back', + TILT_LEFT: 'tilted_left', + TILT_RIGHT: 'tilted_right', FACE_UP: 'face up', FACE_DOWN: 'face down', FREEFALL: 'freefall', G3: '3G', G6: '6G', G8: '8G', - SHAKE: 'shake' + SHAKE: 'shake', + MOVED: 'moved', + TILTED: 'tilted_any' }; const GestureMenuLower = Object.entries(GestureMenu).map(x => x[1].toLowerCase()); const GestureMenuValue = Object.entries(GestureMenu).map(x => x[0]); @@ -97,6 +99,32 @@ const ConnectionStateMenu = [ 'disconnected' ]; +const TouchPinIDMenu = { + 0: 'P0', + 1: 'P1', + 2: 'P2' +}; +const TouchPinIDMenuLower = Object.keys(TouchPinIDMenu); +const TouchPinIDMenuValue = Object.values(TouchPinIDMenu); + +const TiltDirectionMenu = { + any: 'ANY', + front: 'TILT_UP', + back: 'TILT_DOWN', + left: 'TILT_LEFT', + right: 'TILT_RIGHT' +}; +const TiltDirectionMenuLower = Object.keys(TiltDirectionMenu); +const TiltDirectionMenuValue = Object.values(TiltDirectionMenu); + +const TiltAngleDirectionMenu = [ + 'FRONT', + 'BACK', + 'LEFT', + 'RIGHT' +]; +const TiltAngleDirectionMenuLower = TiltAngleDirectionMenu.map(x => x.toLowerCase()); + /** * MicrobitMore converter */ @@ -231,6 +259,59 @@ const MicrobitMoreConverter = { return block; }); + converter.registerOnSendWithBlock(MicrobitMore, 'when_pin_connected', 1, 0, params => { + const {receiver, args, rubyBlock} = params; + + if (converter.isNumber(args[0])) { + const index = TouchPinIDMenuLower.indexOf(args[0].toString()); + if (index < 0) return null; + + args[0] = new Primitive('str', TouchPinIDMenuValue[index], args[0].node); + } else { + return null; + } + + const block = converter.changeRubyExpressionBlock(receiver, 'microbitMore_whenPinConnected', 'hat'); + converter.addField(block, 'PIN', args[0]); + converter.setParent(rubyBlock, block); + return block; + }); + + converter.registerOnSend(MicrobitMore, 'tilted?', 1, params => { + const {receiver, args} = params; + + if (converter.isString(args[0])) { + const index = TiltDirectionMenuLower.indexOf(args[0].toString().toLowerCase()); + if (index < 0) return null; + + args[0] = new Primitive('str', TiltDirectionMenuValue[index], args[0].node); + } else { + return null; + } + + const block = + converter.changeRubyExpressionBlock(receiver, 'microbitMore_isTilted', 'value_boolean'); + converter.addField(block, 'DIRECTION', args[0]); + return block; + }); + + converter.registerOnSend(MicrobitMore, 'tilt_angle', 1, params => { + const {receiver, args} = params; + + if (converter.isString(args[0])) { + const index = TiltAngleDirectionMenuLower.indexOf(args[0].toString().toLowerCase()); + if (index < 0) return null; + + args[0] = new Primitive('str', TiltAngleDirectionMenu[index], args[0].node); + } else { + return null; + } + + const block = converter.changeRubyExpressionBlock(receiver, 'microbitMore_getTiltAngle', 'value'); + converter.addField(block, 'DIRECTION', args[0]); + return block; + }); + converter.registerOnSend(MicrobitMore, 'display_pattern', 5, params => { const {receiver, args} = params; diff --git a/test/integration/ruby-tab/extension_microbit_more.test.js b/test/integration/ruby-tab/extension_microbit_more.test.js index 9c443e55ae6..ae326eed381 100644 --- a/test/integration/ruby-tab/extension_microbit_more.test.js +++ b/test/integration/ruby-tab/extension_microbit_more.test.js @@ -51,6 +51,15 @@ describe('Ruby Tab: Microbit More v2 extension blocks', () => { microbit_more.when_pin_is("P2", "tapped") do end + microbit_more.when_pin_connected(0) do + end + + microbit_more.when_pin_connected(1) do + end + + microbit_more.when_pin_connected(2) do + end + microbit_more.pin_is_touched?("LOGO") microbit_more.pin_is_touched?("P2") @@ -61,6 +70,34 @@ describe('Ruby Tab: Microbit More v2 extension blocks', () => { microbit_more.when("6G") do end + microbit_more.when("moved") do + end + + microbit_more.when("tilted_any") do + end + + microbit_more.when("tilted_front") do + end + + microbit_more.when("tilted_back") do + end + + microbit_more.when("tilted_left") do + end + + microbit_more.when("tilted_right") do + end + + microbit_more.tilted?("any") + + microbit_more.tilted?("front") + + microbit_more.tilted?("back") + + microbit_more.tilted?("left") + + microbit_more.tilted?("right") + microbit_more.display_pattern( ".1.1.", "1.1.1", @@ -89,6 +126,14 @@ describe('Ruby Tab: Microbit More v2 extension blocks', () => { microbit_more.roll + microbit_more.tilt_angle("front") + + microbit_more.tilt_angle("back") + + microbit_more.tilt_angle("left") + + microbit_more.tilt_angle("right") + microbit_more.sound_level microbit_more.magnetic_force("absolute") diff --git a/test/unit/lib/ruby-generator/microbit_more.test.js b/test/unit/lib/ruby-generator/microbit_more.test.js new file mode 100644 index 00000000000..196040d98e6 --- /dev/null +++ b/test/unit/lib/ruby-generator/microbit_more.test.js @@ -0,0 +1,90 @@ +import RubyGenerator from '../../../../src/lib/ruby-generator'; +import MicrobitMoreBlocks from '../../../../src/lib/ruby-generator/microbit_more'; + +describe('RubyGenerator/MicrobitMore', () => { + beforeEach(() => { + RubyGenerator.cache_ = {}; + RubyGenerator.definitions_ = {}; + RubyGenerator.functionNames_ = {}; + RubyGenerator.currentTarget = null; + MicrobitMoreBlocks(RubyGenerator); + }); + + test('microbitMore_whenPinConnected', () => { + const block = { + opcode: 'microbitMore_whenPinConnected', + fields: { + PIN: { + value: 'P0' + } + } + }; + const expected = 'microbit_more.when_pin_connected(0) do\n'; + expect(RubyGenerator.microbitMore_whenPinConnected(block)).toEqual(expected); + }); + + test('microbitMore_isTilted', () => { + const block = { + opcode: 'microbitMore_isTilted', + fields: { + DIRECTION: { + value: 'ANY' + } + } + }; + const result = RubyGenerator.microbitMore_isTilted(block); + expect(result[0]).toEqual('microbit_more.tilted?("any")'); + }); + + test('microbitMore_getTiltAngle', () => { + const block = { + opcode: 'microbitMore_getTiltAngle', + fields: { + DIRECTION: { + value: 'FRONT' + } + } + }; + const result = RubyGenerator.microbitMore_getTiltAngle(block); + expect(result[0]).toEqual('microbit_more.tilt_angle("front")'); + }); + + test('microbitMore_whenGesture(MOVED)', () => { + const block = { + opcode: 'microbitMore_whenGesture', + fields: { + GESTURE: { + value: 'MOVED' + } + } + }; + const expected = 'microbit_more.when("moved") do\n'; + expect(RubyGenerator.microbitMore_whenGesture(block)).toEqual(expected); + }); + + test('microbitMore_whenGesture(TILTED)', () => { + const block = { + opcode: 'microbitMore_whenGesture', + fields: { + GESTURE: { + value: 'TILTED' + } + } + }; + const expected = 'microbit_more.when("tilted_any") do\n'; + expect(RubyGenerator.microbitMore_whenGesture(block)).toEqual(expected); + }); + + test('microbitMore_whenGesture(TILT_UP)', () => { + const block = { + opcode: 'microbitMore_whenGesture', + fields: { + GESTURE: { + value: 'TILT_UP' + } + } + }; + const expected = 'microbit_more.when("tilted_front") do\n'; + expect(RubyGenerator.microbitMore_whenGesture(block)).toEqual(expected); + }); +}); diff --git a/test/unit/lib/ruby-to-blocks-converter/microbit_more.test.js b/test/unit/lib/ruby-to-blocks-converter/microbit_more.test.js new file mode 100644 index 00000000000..1ae0d879e3a --- /dev/null +++ b/test/unit/lib/ruby-to-blocks-converter/microbit_more.test.js @@ -0,0 +1,118 @@ +import RubyToBlocksConverter from '../../../../src/lib/ruby-to-blocks-converter'; +import { + convertAndExpectToEqualBlocks, + rubyToExpected +} from '../../../helpers/expect-to-equal-blocks'; + +describe('RubyToBlocksConverter/MicrobitMore', () => { + let converter; + let target; + let code; + let expected; + + beforeEach(() => { + converter = new RubyToBlocksConverter(null); + target = null; + code = null; + expected = null; + }); + + test('microbit_more.when_pin_connected', () => { + code = 'microbit_more.when_pin_connected(0) do; end'; + expected = [ + { + opcode: 'microbitMore_whenPinConnected', + fields: [ + { + name: 'PIN', + value: 'P0' + } + ], + next: null, + parent: null, + topLevel: true + } + ]; + convertAndExpectToEqualBlocks(converter, target, code, expected); + }); + + test('microbit_more.tilted?', () => { + code = 'microbit_more.tilted?("any")'; + expected = [ + { + opcode: 'microbitMore_isTilted', + fields: [ + { + name: 'DIRECTION', + value: 'ANY' + } + ] + } + ]; + convertAndExpectToEqualBlocks(converter, target, code, expected); + }); + + test('microbit_more.tilt_angle', () => { + code = 'microbit_more.tilt_angle("front")'; + expected = [ + { + opcode: 'microbitMore_getTiltAngle', + fields: [ + { + name: 'DIRECTION', + value: 'FRONT' + } + ] + } + ]; + convertAndExpectToEqualBlocks(converter, target, code, expected); + }); + + test('microbit_more.when(moved)', () => { + code = 'microbit_more.when("moved") do; end'; + expected = [ + { + opcode: 'microbitMore_whenGesture', + fields: [ + { + name: 'GESTURE', + value: 'MOVED' + } + ] + } + ]; + convertAndExpectToEqualBlocks(converter, target, code, expected); + }); + + test('microbit_more.when(tilted_any)', () => { + code = 'microbit_more.when("tilted_any") do; end'; + expected = [ + { + opcode: 'microbitMore_whenGesture', + fields: [ + { + name: 'GESTURE', + value: 'TILTED' + } + ] + } + ]; + convertAndExpectToEqualBlocks(converter, target, code, expected); + }); + + test('microbit_more.when(tilted_front)', () => { + code = 'microbit_more.when("tilted_front") do; end'; + expected = [ + { + opcode: 'microbitMore_whenGesture', + fields: [ + { + name: 'GESTURE', + value: 'TILT_UP' + } + ] + } + ]; + convertAndExpectToEqualBlocks(converter, target, code, expected); + }); +}); From 37343229bd0be501d7f7b8261ae0c3442c2d57e2 Mon Sep 17 00:00:00 2001 From: Kouji Takao Date: Sat, 17 Jan 2026 02:51:36 +0900 Subject: [PATCH 3/3] feat: update scratch-vm dependency to latest commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Updated package-lock.json to reference latest scratch-vm changes - Ensures compatibility with recent scratch-vm modifications 🤖 Generated with [Gemini Code](https://gemini.google.com/code) Co-Authored-By: Gemini --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index c6ac9879525..2ac050482ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29135,7 +29135,7 @@ }, "node_modules/scratch-vm": { "version": "5.0.300", - "resolved": "git+ssh://git@github.com/smalruby/scratch-vm.git#ce74dd03da27f7dcc5e040d76524b0756c2611e8", + "resolved": "git+ssh://git@github.com/smalruby/scratch-vm.git#cc73cc0d84886c93c685205a8e3ea0e79832c71b", "license": "AGPL-3.0-only", "dependencies": { "@vernier/godirect": "^1.5.0",