diff --git a/.claude/rules/scratch-gui/smalruby-prettier-files.md b/.claude/rules/scratch-gui/smalruby-prettier-files.md index ed071c31963..68aa5e9f575 100644 --- a/.claude/rules/scratch-gui/smalruby-prettier-files.md +++ b/.claude/rules/scratch-gui/smalruby-prettier-files.md @@ -265,6 +265,7 @@ upstream (Scratch) ファイルは対象外。 - `test/unit/lib/ruby-roundtrip-puts-concat.test.js` - `test/unit/lib/ruby-roundtrip-super.test.js` - `test/unit/lib/ruby-screenshot.test.js` +- `test/unit/lib/smalruby-original-sprites.test.js` - `test/unit/lib/smalrubot-firmware-flasher.test.js` - `test/unit/lib/ruby-script-preview.test.js` - `test/unit/lib/ruby-to-blocks-converter-version.test.js` diff --git a/.claude/skills/upstream-merge/phase2-conflicts.md b/.claude/skills/upstream-merge/phase2-conflicts.md index 7300eccfd09..cf9c186f6b1 100644 --- a/.claude/skills/upstream-merge/phase2-conflicts.md +++ b/.claude/skills/upstream-merge/phase2-conflicts.md @@ -160,6 +160,36 @@ upstream が追加した新しいファイルや、revert 対象外のファイ --- +### sprites.json / costumes.json (Smalruby 独自エントリ消失リスク) + +**コンフリクトとして検出されないケースが多い** ため要注意。upstream の +`packages/scratch-gui/src/lib/libraries/sprites.json` / +`packages/scratch-gui/src/lib/libraries/costumes.json` が更新されると、 +git の auto-merge が成功してしまい、**Smalruby 独自エントリ (Shimaraby, +Shimacat) が静かに消失する** ことがある (issue #688 で発生)。 + +**マージ後に必ず実行する確認**: + +```bash +# Shimaraby / Shimacat が残っていることを確認 +docker compose run --rm app bash -c "cd packages/scratch-gui && npm exec jest test/unit/lib/smalruby-original-sprites.test.js" + +# トレードマーク sprite が混入していないことを確認 +docker compose run --rm app bash -c "cd packages/scratch-gui && npm exec jest test/unit/lib/removed-trademarks.test.js" +``` + +**消失している場合の復元**: 過去コミット `f2b5c09e5b` (sprites) / +`1c3f91a216` (costumes) を参照し、エントリを再追加する。アセット PNG は +`packages/scratch-gui/static/smalruby-assets/` に保持されている。 + +**トレードマーク sprite が紛れ込んでいる場合**: 対象は +`Cat`, `Cat Flying`, `Gobo`, `Pico`, `Pico Walking`, `Nano`, `Tera`, +`Giga`, `Giga Walking`。`removed-trademarks.test.js` の `trademarkNames` +配列が現状のリストなので、増えた場合は配列を更新してから sprites.json / +costumes.json から削除する。 + +--- + ## Unexpected Conflicts 上記以外のファイルでコンフリクトが発生した場合: diff --git a/packages/scratch-gui/.prettierignore b/packages/scratch-gui/.prettierignore index 478b37bc496..b9f9f9dfaf4 100644 --- a/packages/scratch-gui/.prettierignore +++ b/packages/scratch-gui/.prettierignore @@ -349,6 +349,7 @@ test/unit/lib/* !test/unit/lib/rubytee-context.test.js !test/unit/lib/screen-utils.test.js !test/unit/lib/smalrubot-firmware-flasher.test.js +!test/unit/lib/smalruby-original-sprites.test.js !test/unit/lib/setup-prism.js !test/unit/lib/version-checker.test.js diff --git a/packages/scratch-gui/src/lib/libraries/costumes.json b/packages/scratch-gui/src/lib/libraries/costumes.json index fe54b3a0f76..9062af0e543 100644 --- a/packages/scratch-gui/src/lib/libraries/costumes.json +++ b/packages/scratch-gui/src/lib/libraries/costumes.json @@ -2000,36 +2000,6 @@ "mammal" ] }, - { - "name": "Cat Flying-a", - "assetId": "a1ab94c8172c3b97ed9a2bf7c32172cd", - "md5ext": "a1ab94c8172c3b97ed9a2bf7c32172cd.svg", - "dataFormat": "svg", - "bitmapResolution": 1, - "rotationCenterX": 55, - "rotationCenterY": 37, - "tags": [ - "animals", - "cat", - "kitty", - "kitten" - ] - }, - { - "name": "Cat Flying-b", - "assetId": "6667936a2793aade66c765c329379ad0", - "md5ext": "6667936a2793aade66c765c329379ad0.svg", - "dataFormat": "svg", - "bitmapResolution": 1, - "rotationCenterX": 44, - "rotationCenterY": 46, - "tags": [ - "animals", - "cat", - "kitty", - "kitten" - ] - }, { "name": "Catcher-a", "assetId": "895cdda4f2bd9d6f50ff07188e7ce395", @@ -10009,6 +9979,58 @@ "chomp" ] }, + { + "name": "Shimacat-a", + "tags": [ + "animals" + ], + "assetId": "851e679b8f113ee90e0d686c33fbc940", + "bitmapResolution": 2, + "dataFormat": "png", + "md5ext": "851e679b8f113ee90e0d686c33fbc940.png", + "rotationCenterX": 90, + "rotationCenterY": 100, + "rawURL": "static/smalruby-assets/851e679b8f113ee90e0d686c33fbc940.png" + }, + { + "name": "Shimacat-b", + "tags": [ + "animals" + ], + "assetId": "57613248603bb9c5b4b767b72cd4fdef", + "bitmapResolution": 2, + "dataFormat": "png", + "md5ext": "57613248603bb9c5b4b767b72cd4fdef.png", + "rotationCenterX": 90, + "rotationCenterY": 100, + "rawURL": "static/smalruby-assets/57613248603bb9c5b4b767b72cd4fdef.png" + }, + { + "name": "Shimaraby-a", + "tags": [ + "animals" + ], + "assetId": "ddaccfcda466a4887299feddc899fea7", + "bitmapResolution": 2, + "dataFormat": "png", + "md5ext": "ddaccfcda466a4887299feddc899fea7.png", + "rotationCenterX": 63, + "rotationCenterY": 100, + "rawURL": "static/smalruby-assets/ddaccfcda466a4887299feddc899fea7.png" + }, + { + "name": "Shimaraby-b", + "tags": [ + "animals" + ], + "assetId": "bd0ff11c925936ed5e0363112103cd0b", + "bitmapResolution": 2, + "dataFormat": "png", + "md5ext": "bd0ff11c925936ed5e0363112103cd0b.png", + "rotationCenterX": 63, + "rotationCenterY": 100, + "rawURL": "static/smalruby-assets/bd0ff11c925936ed5e0363112103cd0b.png" + }, { "name": "Shirt-a", "assetId": "43e916bbe0ba7cecd08407d25ac3d104", diff --git a/packages/scratch-gui/src/lib/libraries/sprites.json b/packages/scratch-gui/src/lib/libraries/sprites.json index 11b85f51e77..19869f15515 100644 --- a/packages/scratch-gui/src/lib/libraries/sprites.json +++ b/packages/scratch-gui/src/lib/libraries/sprites.json @@ -3768,61 +3768,6 @@ "variables": {}, "blocks": {} }, - { - "name": "Cat Flying", - "tags": [ - "animals", - "cat", - "kitty", - "kitten" - ], - "isStage": false, - "costumes": [ - { - "name": "Cat Flying-a", - "assetId": "a1ab94c8172c3b97ed9a2bf7c32172cd", - "md5ext": "a1ab94c8172c3b97ed9a2bf7c32172cd.svg", - "dataFormat": "svg", - "bitmapResolution": 1, - "rotationCenterX": 55, - "rotationCenterY": 37, - "tags": [ - "animals", - "cat", - "kitty", - "kitten" - ] - }, - { - "name": "Cat Flying-b", - "assetId": "6667936a2793aade66c765c329379ad0", - "md5ext": "6667936a2793aade66c765c329379ad0.svg", - "dataFormat": "svg", - "bitmapResolution": 1, - "rotationCenterX": 44, - "rotationCenterY": 46, - "tags": [ - "animals", - "cat", - "kitty", - "kitten" - ] - } - ], - "sounds": [ - { - "name": "Pop", - "assetId": "83a9787d4cb6f3b7632b4ddfebf74367", - "md5ext": "83a9787d4cb6f3b7632b4ddfebf74367.wav", - "dataFormat": "wav", - "tags": [], - "rate": 44100, - "sampleCount": 1032 - } - ], - "variables": {}, - "blocks": {} - }, { "name": "Catcher", "tags": [ @@ -17740,6 +17685,90 @@ "variables": {}, "blocks": {} }, + { + "name": "Shimacat", + "tags": [ + "animals" + ], + "isStage": false, + "variables": {}, + "costumes": [ + { + "assetId": "851e679b8f113ee90e0d686c33fbc940", + "name": "shimacat-a", + "bitmapResolution": 2, + "md5ext": "851e679b8f113ee90e0d686c33fbc940.png", + "dataFormat": "png", + "rotationCenterX": 90, + "rotationCenterY": 100, + "rawURL": "static/smalruby-assets/851e679b8f113ee90e0d686c33fbc940.png" + }, + { + "assetId": "57613248603bb9c5b4b767b72cd4fdef", + "name": "shimacat-b", + "bitmapResolution": 2, + "md5ext": "57613248603bb9c5b4b767b72cd4fdef.png", + "dataFormat": "png", + "rotationCenterX": 90, + "rotationCenterY": 100, + "rawURL": "static/smalruby-assets/57613248603bb9c5b4b767b72cd4fdef.png" + } + ], + "sounds": [ + { + "assetId": "83c36d806dc92327b9e7049a565c6bff", + "name": "Meow", + "dataFormat": "wav", + "format": "", + "rate": 44100, + "sampleCount": 37376, + "md5ext": "83c36d806dc92327b9e7049a565c6bff.wav" + } + ], + "blocks": {} + }, + { + "name": "Shimaraby", + "tags": [ + "animals" + ], + "isStage": false, + "variables": {}, + "costumes": [ + { + "assetId": "ddaccfcda466a4887299feddc899fea7", + "name": "shimaraby-a", + "bitmapResolution": 2, + "md5ext": "ddaccfcda466a4887299feddc899fea7.png", + "dataFormat": "png", + "rotationCenterX": 63, + "rotationCenterY": 100, + "rawURL": "static/smalruby-assets/ddaccfcda466a4887299feddc899fea7.png" + }, + { + "assetId": "bd0ff11c925936ed5e0363112103cd0b", + "name": "shimaraby-b", + "bitmapResolution": 2, + "md5ext": "bd0ff11c925936ed5e0363112103cd0b.png", + "dataFormat": "png", + "rotationCenterX": 63, + "rotationCenterY": 100, + "rawURL": "static/smalruby-assets/bd0ff11c925936ed5e0363112103cd0b.png" + } + ], + "sounds": [ + { + "assetId": "3b8236bbb288019d93ae38362e865972", + "name": "Chirp", + "dataFormat": "wav", + "format": "adpcm", + "rate": 22050, + "sampleCount": 6097, + "md5ext": "3b8236bbb288019d93ae38362e865972.wav" + } + ], + "blocks": {} + }, { "name": "Shirt", "tags": [ diff --git a/packages/scratch-gui/test/unit/lib/removed-trademarks.test.js b/packages/scratch-gui/test/unit/lib/removed-trademarks.test.js index 03277eb624d..92f2e03366e 100644 --- a/packages/scratch-gui/test/unit/lib/removed-trademarks.test.js +++ b/packages/scratch-gui/test/unit/lib/removed-trademarks.test.js @@ -7,7 +7,7 @@ import costumeLibraryContent from '../../../src/lib/libraries/costumes.json'; import spriteLibraryContent from '../../../src/lib/libraries/sprites.json'; -const trademarkNames = ['Cat', 'Cat-Flying', 'Gobo', 'Pico', 'Pico Walking', 'Nano', 'Tera', 'Giga', 'Giga Walking']; +const trademarkNames = ['Cat', 'Cat Flying', 'Gobo', 'Pico', 'Pico Walking', 'Nano', 'Tera', 'Giga', 'Giga Walking']; describe('Removed trademarks (ex: Scratch Cat)', () => { test('Removed trademark sprites', () => { diff --git a/packages/scratch-gui/test/unit/lib/smalruby-original-sprites.test.js b/packages/scratch-gui/test/unit/lib/smalruby-original-sprites.test.js new file mode 100644 index 00000000000..393382dbae6 --- /dev/null +++ b/packages/scratch-gui/test/unit/lib/smalruby-original-sprites.test.js @@ -0,0 +1,45 @@ +/** + * Verifies that Smalruby's original sprites/costumes (Shimaraby, Shimacat) are + * present in the sprite/costume libraries. Regression guard for issue #688: + * upstream merges silently overwrite these libraries and wipe the entries. + */ +import costumeLibraryContent from '../../../src/lib/libraries/costumes.json'; +import spriteLibraryContent from '../../../src/lib/libraries/sprites.json'; + +const requiredSprites = ['Shimaraby', 'Shimacat']; +const requiredCostumes = ['Shimaraby-a', 'Shimaraby-b', 'Shimacat-a', 'Shimacat-b']; + +describe('Smalruby original sprites/costumes', () => { + test('sprite library contains Shimaraby and Shimacat', () => { + const spriteNames = spriteLibraryContent.map((sprite) => sprite.name); + for (const name of requiredSprites) { + expect(spriteNames).toContain(name); + } + }); + + test('costume library contains Shimaraby-a/b and Shimacat-a/b', () => { + const costumeNames = costumeLibraryContent.map((costume) => costume.name); + for (const name of requiredCostumes) { + expect(costumeNames).toContain(name); + } + }); + + test('Shimaraby/Shimacat sprite costumes reference smalruby-assets PNGs', () => { + const expectedRawURLs = { + Shimaraby: [ + 'static/smalruby-assets/ddaccfcda466a4887299feddc899fea7.png', + 'static/smalruby-assets/bd0ff11c925936ed5e0363112103cd0b.png', + ], + Shimacat: [ + 'static/smalruby-assets/851e679b8f113ee90e0d686c33fbc940.png', + 'static/smalruby-assets/57613248603bb9c5b4b767b72cd4fdef.png', + ], + }; + for (const spriteName of requiredSprites) { + const sprite = spriteLibraryContent.find((s) => s.name === spriteName); + expect(sprite).toBeDefined(); + const rawURLs = sprite.costumes.map((c) => c.rawURL); + expect(rawURLs).toEqual(expectedRawURLs[spriteName]); + } + }); +});