Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
480 changes: 480 additions & 0 deletions docs/tutorial/improvement-plan.md

Large diffs are not rendered by default.

68 changes: 68 additions & 0 deletions docs/tutorial/progress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# チュートリアル拡充 進捗トラッカー

> **🚧 一時ドキュメント** — チュートリアル拡充 4 Phase 完了時に削除予定。Issue [#682](https://github.com/smalruby/smalruby3-editor/issues/682) の進捗をリアルタイムに反映するためのワーキングドキュメント。
>
> Phase の最終形・設計判断は [`improvement-plan.md`](improvement-plan.md) を参照。

## サマリー

| Phase | Issue | 状態 | 規模 | 画像 |
|---|---|---|---|---|
| Phase 1 — Mesh 再分類 | [#678](https://github.com/smalruby/smalruby3-editor/issues/678) | 🟢 実装完了 (レビュー待ち) | 1 PR | 不要 |
| 基盤 — `setup` プロパティ | (Phase 2 sub-issue 内) | ⚪️ 未着手 | 1 PR | 不要 |
| Phase 2 — Ruby 拡充 | [#679](https://github.com/smalruby/smalruby3-editor/issues/679) | ⚪️ 未着手 | 2〜3 PR | ~50 枚 |
| Phase 3 — Block 4 シリーズ | [#680](https://github.com/smalruby/smalruby3-editor/issues/680) | ⚪️ 未着手 (書誌情報待ち) | 4 PR | ~76 枚 |
| Phase 4 — DNCL | [#681](https://github.com/smalruby/smalruby3-editor/issues/681) | ⚪️ 未着手 | 3〜4 PR | ~70 枚 |

凡例: ⚪️ 未着手 / 🟡 進行中 / 🟢 完了 (PR レビュー待ち含む) / ✅ マージ済み / ❌ 中断

## Phase 1: Mesh チュートリアルを 3 カテゴリ × 3 lv に再分類

**Issue**: [#678](https://github.com/smalruby/smalruby3-editor/issues/678) / **ブランチ**: `feature/tutorial-mesh-recategorize`

### 完了項目

- [x] `tutorial-tags.js` — `CATEGORIES.chatApp` を削除、`meshStep1/2/3` を追加
- [x] `library.jsx` — local messages 定義から `[CATEGORIES.chatApp]` を削除、`[CATEGORIES.meshStep1/2/3]` を追加
- [x] `decks/index.jsx` — 9 deck の `category:` 参照を新キーに更新
- `chat-1-basic-{1,2,3}` → `meshStep1`
- `chat-2-sprites-{1,2,3}` → `meshStep2`
- `chat-3-mesh-{1,2,3}` → `meshStep3`
- [x] `locales/en.js` — `gui.library.chatApp` を削除、`gui.library.meshStep1/2/3` を追加
- [x] `locales/ja.js` — 同上 (日本語ベース)
- [x] `locales/ja-Hira.js` — 同上 (ひらがなベース)
- [x] `docs/tutorial/improvement-plan.md` — Phase 1〜4 の設計を集約
- [x] `docs/tutorial/progress.md` — 本ドキュメント新規作成

### 残項目 (本 PR で対応)

- [ ] `npm run lint` でエラー・警告ゼロを確認
- [ ] Playwright で `tipsLibrary` を開き、3 カテゴリが想定順序で表示されることを確認
- [ ] 各カテゴリ配下に Lv1/Lv2/Lv3 が並ぶことを確認
- [ ] PR 作成

### 設計判断メモ

- **`tag-messages.js` は更新しない** — `gui.libraryCategories.*` のメッセージ ID は library.jsx の rendering path から参照されておらず、`tag-messages.js` の `gettingStarted` / `chatApp` エントリは vestigial。新カテゴリも追加しない。本物の rendering 経路 (`gui.library.*` を持つ local messages in library.jsx) のみを更新する。
- **deck ID はリネームしない** — `chat-1-basic-1` 等の既存 ID は URL 互換性のためそのまま維持。`category:` 参照のみ更新。
- **`tags: ['mesh']` も維持** — タグフィルタは引き続き "Mesh" 1 つで全 9 deck をまとめて絞り込めることを期待する利用者がいる可能性があるため、新カテゴリ追加と並行してタグは維持する。
- **カテゴリ名は「通信入門」共通プレフィックス + ① ② ③ 番号付け** — PR レビューで「3 カテゴリの関係性が伝わらない」と指摘を受け、ストーリー型単独名から番号付きシリーズ名に変更。Step 3 の Lv1 deck 名も「メッシュ拡張機能でつながろう」→「メッシュでつながろう」に短縮し Lv2/Lv3 と表記揃え。

## Phase 2 以降の TODO 抜粋

### 基盤 (Phase 2 前半): `setup` プロパティ

- [ ] deck 定義の type 拡張 (`{ tab, rubyMode, extensions, rubyVersion }`)
- [ ] `tips-library.jsx` または `cards` reducer で deck 起動時に setup を適用
- [ ] `activateTab` / `setDnclMode` / `vm.extensionManager.loadExtensionURL` の冪等な呼び出し
- [ ] ロード失敗時のグレースフルデグレード
- [ ] ふりがなフラグも考慮した rubyMode の動作 (`smalruby:furiganaEnabled` との同期)

### Phase 3 着手前に必要 (外部要因)

- [ ] 書籍「キラキラRuby」(仮称) の正式タイトル・出版社・ISBN・購入リンク確定
- [ ] 著者 (藤村健吾氏) からの書籍引用許諾

## 削除タイミング

全 4 Phase の Issue (#678/679/680/681) が close され、`improvement-plan.md` の内容が `docs/tutorial/README.md` 等の正規ドキュメントに統合されたら本ファイル (`progress.md`) は削除する。`improvement-plan.md` も同タイミングで削除予定。
18 changes: 14 additions & 4 deletions packages/scratch-gui/src/components/library/library.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,20 @@ const messages = defineMessages({
defaultMessage: 'Getting Started',
description: 'Label for getting started category'
},
[CATEGORIES.chatApp]: {
id: `gui.library.chatApp`,
defaultMessage: 'Build a Chat App',
description: 'Label for chat app tutorial category'
[CATEGORIES.meshStep1]: {
id: `gui.library.meshStep1`,
defaultMessage: '通信入門 ① メッセージを送ってみよう',
description: 'Label for Mesh tutorial step 1 — send a message between same-sprite scripts'
},
[CATEGORIES.meshStep2]: {
id: `gui.library.meshStep2`,
defaultMessage: '通信入門 ② ふたりで会話しよう',
description: 'Label for Mesh tutorial step 2 — chat between two sprites'
},
[CATEGORIES.meshStep3]: {
id: `gui.library.meshStep3`,
defaultMessage: '通信入門 ③ みんなで会話しよう (メッシュ)',
description: 'Label for Mesh tutorial step 3 — chat across devices via Mesh extension'
},
membershipTag: {
defaultMessage: 'Membership',
Expand Down
24 changes: 12 additions & 12 deletions packages/scratch-gui/src/lib/libraries/decks/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import libraryChat1Basic3 from './thumbnails/chat-1-basic-3.jpg';
import libraryChat2Sprites1 from './thumbnails/chat-2-sprites-1.jpg';
import libraryChat2Sprites2 from './thumbnails/chat-2-sprites-2.jpg';
import libraryChat2Sprites3 from './thumbnails/chat-2-sprites-3.jpg';
// Chat Tutorial 3 Mesh 1: メッシュ拡張機能でつながろう
// Chat Tutorial 3 Mesh 1: メッシュでつながろう
import libraryChat3Mesh1 from './thumbnails/chat-3-mesh-1.jpg';
import libraryChat3Mesh2 from './thumbnails/chat-3-mesh-2.jpg';
import libraryChat3Mesh3 from './thumbnails/chat-3-mesh-3.jpg';
Expand Down Expand Up @@ -112,7 +112,7 @@ end`,
/>
),
tags: ['mesh'],
category: CATEGORIES.chatApp,
category: CATEGORIES.meshStep1,
img: libraryChat1Basic1,
nameMessageId: 'gui.howtos.chat-1-basic-1.name',
allowedBlocks: {
Expand Down Expand Up @@ -242,7 +242,7 @@ end`,
/>
),
tags: ['mesh'],
category: CATEGORIES.chatApp,
category: CATEGORIES.meshStep1,
img: libraryChat1Basic2,
nameMessageId: 'gui.howtos.chat-1-basic-2.name',
allowedBlocks: {
Expand Down Expand Up @@ -355,7 +355,7 @@ end`,
/>
),
tags: ['mesh'],
category: CATEGORIES.chatApp,
category: CATEGORIES.meshStep1,
img: libraryChat1Basic3,
nameMessageId: 'gui.howtos.chat-1-basic-3.name',
allowedBlocks: {
Expand Down Expand Up @@ -474,7 +474,7 @@ end`,
/>
),
tags: ['mesh'],
category: CATEGORIES.chatApp,
category: CATEGORIES.meshStep2,
img: libraryChat2Sprites1,
nameMessageId: 'gui.howtos.chat-2-sprites-1.name',
allowedBlocks: {
Expand Down Expand Up @@ -607,7 +607,7 @@ end`,
/>
),
tags: ['mesh'],
category: CATEGORIES.chatApp,
category: CATEGORIES.meshStep2,
img: libraryChat2Sprites2,
nameMessageId: 'gui.howtos.chat-2-sprites-2.name',
allowedBlocks: {
Expand Down Expand Up @@ -729,7 +729,7 @@ end`,
/>
),
tags: ['mesh'],
category: CATEGORIES.chatApp,
category: CATEGORIES.meshStep2,
img: libraryChat2Sprites3,
nameMessageId: 'gui.howtos.chat-2-sprites-3.name',
allowedBlocks: {
Expand Down Expand Up @@ -861,17 +861,17 @@ end`,
urlId: 'chat2Sprites3'
},

// ─── Chat Tutorial 3 Mesh 1: メッシュ拡張機能でつながろう ───────────────
// ─── Chat Tutorial 3 Mesh 1: メッシュでつながろう ───────────────
'chat-3-mesh-1': {
name: (
<FormattedMessage
defaultMessage="メッシュ拡張機能でつながろう"
defaultMessage="メッシュでつながろう"
description="Name for Chat Tutorial 3 Mesh 1"
id="gui.howtos.chat-3-mesh-1.name"
/>
),
tags: ['mesh'],
category: CATEGORIES.chatApp,
category: CATEGORIES.meshStep3,
img: libraryChat3Mesh1,
nameMessageId: 'gui.howtos.chat-3-mesh-1.name',
allowedBlocks: {
Expand Down Expand Up @@ -1012,7 +1012,7 @@ end`,
/>
),
tags: ['mesh'],
category: CATEGORIES.chatApp,
category: CATEGORIES.meshStep3,
img: libraryChat3Mesh2,
nameMessageId: 'gui.howtos.chat-3-mesh-2.name',
allowedBlocks: {
Expand Down Expand Up @@ -1131,7 +1131,7 @@ end`,
/>
),
tags: ['mesh'],
category: CATEGORIES.chatApp,
category: CATEGORIES.meshStep3,
img: libraryChat3Mesh3,
nameMessageId: 'gui.howtos.chat-3-mesh-3.name',
allowedBlocks: {
Expand Down
8 changes: 7 additions & 1 deletion packages/scratch-gui/src/lib/libraries/tutorial-tags.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ import messages from './tag-messages.js';

export const CATEGORIES = {
gettingStarted: 'gettingStarted',
chatApp: 'chatApp' // チャットアプリをつくろう / Build a Chat App
// Mesh tutorial series — split from the former single `chatApp` category
// into three numbered "通信入門" steps so that the sequential nature is
// visible in the tutorial library (see docs/tutorial/improvement-plan.md
// Phase 1).
meshStep1: 'meshStep1', // 通信入門 ① メッセージを送ってみよう
meshStep2: 'meshStep2', // 通信入門 ② ふたりで会話しよう
meshStep3: 'meshStep3' // 通信入門 ③ みんなで会話しよう (メッシュ)
};

export default [
Expand Down
6 changes: 4 additions & 2 deletions packages/scratch-gui/src/locales/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,9 @@ export default {
'gui.libraryTags.mesh': 'Mesh',
'gui.libraryTags.firstTime': 'First Time',
'gui.libraryCategories.gettingStarted': 'Getting Started',
'gui.library.chatApp': 'Build a Chat App',
'gui.library.meshStep1': 'Intro to Communication ① Send a Message',
'gui.library.meshStep2': 'Intro to Communication ② Chat Between Two Sprites',
'gui.library.meshStep3': 'Intro to Communication ③ Chat Across Devices (Mesh)',
'gui.howtos.chat-1-basic-1.name': "Let's Send a Message!",
'gui.howtos.chat-1-basic-1.step1.title': "Let's send a message to a remote block!",
'gui.howtos.chat-1-basic-1.step2.title': 'First, insert the code and run the program',
Expand Down Expand Up @@ -594,7 +596,7 @@ export default {
'gui.howtos.chat-2-sprites-3.step7.title': 'Press "Insert Ruby code" for Penguin — message names are reversed!',
'gui.howtos.chat-2-sprites-3.step8.title': 'Click Cat or Penguin to run!',
// Chat Tutorial 3 Mesh 1
'gui.howtos.chat-3-mesh-1.name': "Let's Connect with the Mesh Extension!",
'gui.howtos.chat-3-mesh-1.name': "Let's Connect with Mesh!",
'gui.howtos.chat-3-mesh-1.step1.title': "Connect to other people's Smalruby using the Mesh extension",
'gui.howtos.chat-3-mesh-1.step2.title':
'Form a group and select the Mesh extension (even one person can do it with two Smalruby windows)',
Expand Down
6 changes: 4 additions & 2 deletions packages/scratch-gui/src/locales/ja-Hira.js
Original file line number Diff line number Diff line change
Expand Up @@ -912,7 +912,7 @@ export default {
'ペンギンのRubyコードを「ルビーをにゅうりょくする」でにゅうりょくしよう — メッセージめいがネコとぎゃくになっているよ',
'gui.howtos.chat-2-sprites-3.step8.title': 'ネコやペンギンをおしてじっこうしよう!',
// Chat Tutorial 3 Mesh 1
'gui.howtos.chat-3-mesh-1.name': 'メッシュかくちょうきのうでつながろう',
'gui.howtos.chat-3-mesh-1.name': 'メッシュでつながろう',
'gui.howtos.chat-3-mesh-1.step1.title': 'メッシュかくちょうきのうをつかってほかのひとのスモウルビーとつながろう',
'gui.howtos.chat-3-mesh-1.step2.title':
'2にんいじょうのグループをつくってメッシュかくちょうきのうをえらぶ(1にんでも2つのスモウルビーをつかえばできる)',
Expand Down Expand Up @@ -966,7 +966,9 @@ export default {
'gui.libraryTags.ruby': 'ルビー',
'gui.libraryTags.firstTime': 'はじめて',
'gui.libraryCategories.gettingStarted': 'はじめましょう',
'gui.library.chatApp': 'チャットアプリをつくろう',
'gui.library.meshStep1': 'つうしんにゅうもん ① メッセージをおくってみよう',
'gui.library.meshStep2': 'つうしんにゅうもん ② ふたりでかいわしよう',
'gui.library.meshStep3': 'つうしんにゅうもん ③ みんなでかいわしよう (メッシュ)',
'gui.menuBar.updateTooltip': 'あたらしいスモウルビーをつかってみよう!',
'gui.menuBar.updateConfirm':
'あたらしいバージョンのスモウルビーがりようかのうです。いますぐこうしんするばあいは「OK」を、あとにするばあいは「キャンセル」をおしてください。',
Expand Down
6 changes: 4 additions & 2 deletions packages/scratch-gui/src/locales/ja.js
Original file line number Diff line number Diff line change
Expand Up @@ -885,7 +885,7 @@ export default {
'ペンギンのRubyコードを「ルビーを入力する」で入力しよう — メッセージ名がネコと逆になっているよ',
'gui.howtos.chat-2-sprites-3.step8.title': 'ネコやペンギンを押して実行しよう!',
// Chat Tutorial 3 Mesh 1
'gui.howtos.chat-3-mesh-1.name': 'メッシュ拡張機能でつながろう',
'gui.howtos.chat-3-mesh-1.name': 'メッシュでつながろう',
'gui.howtos.chat-3-mesh-1.step1.title': 'メッシュ拡張機能を使って他の人のスモウルビーとつながろう',
'gui.howtos.chat-3-mesh-1.step2.title':
'2人以上のグループをつくってメッシュ拡張機能を選ぶ(1人でも2つのスモウルビーを使えばできる)',
Expand Down Expand Up @@ -941,7 +941,9 @@ export default {
'gui.libraryTags.ruby': 'ルビー',
'gui.libraryTags.firstTime': 'はじめて',
'gui.libraryCategories.gettingStarted': '始めましょう',
'gui.library.chatApp': 'チャットアプリをつくろう',
'gui.library.meshStep1': '通信入門 ① メッセージを送ってみよう',
'gui.library.meshStep2': '通信入門 ② ふたりで会話しよう',
'gui.library.meshStep3': '通信入門 ③ みんなで会話しよう (メッシュ)',
'gui.menuBar.updateTooltip': '新しいスモウルビーを使ってみよう!',
'gui.menuBar.updateConfirm':
'新しいバージョンのスモウルビーが利用可能です。いますぐ更新する場合は「OK」を、あとにする場合は「キャンセル」を押してください。',
Expand Down
78 changes: 78 additions & 0 deletions tools/playwright-verify/verify-tutorial-mesh-categories.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { chromium } from 'playwright';

const URL = 'http://localhost:8601/?no_beforeunload=1&welcome=1';
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
viewport: { width: 1280, height: 800 },
locale: 'ja-JP',
});
const page = await context.newPage();
const log = (...args) => console.log('[verify]', ...args);

page.on('pageerror', (err) => console.error('[pageerror]', err.message));

await page.goto(URL);
await page.evaluate(() => localStorage.clear());
await page.reload();
await page.waitForTimeout(800);

// Welcome modal exposes a 'Start the first tutorial' CTA that opens tipsLibrary.
// First close any welcome tooltip balloon, then click the modal's start CTA.
const tooltipDismiss = page.locator('[data-testid="welcome-tooltip"] [aria-label*="閉じる"], [data-testid="welcome-tooltip"] [aria-label*="dismiss"]');
if (await tooltipDismiss.count()) {
try { await tooltipDismiss.first().click({ force: true }); } catch {}
}

const startBtn = page.locator('[data-testid="welcome-modal-start-tutorial"]');
if (await startBtn.count()) {
log('clicking welcome-modal-start-tutorial');
await startBtn.click();
} else {
log('welcome modal CTA not found, dismiss it and use menu bar');
const laterBtn = page.locator('[data-testid="welcome-modal-later"]');
if (await laterBtn.count()) {
await laterBtn.click();
await page.waitForTimeout(300);
}
// Click About menu (?) → use Tutorials in dropdown, or directly find tutorials button.
// Smalruby has a question-mark button that opens the About menu containing Tutorials.
const aboutBtn = page.locator('[aria-label="About"], [aria-label="アバウト"], [class*=menuBarItem][class*=about]').first();
if (await aboutBtn.count()) {
await aboutBtn.click();
await page.waitForTimeout(300);
const tutItems = await page.locator('[role=menuitem]').allTextContents();
log('about menu items:', JSON.stringify(tutItems));
}
}

await page.waitForTimeout(3000);

// Take screenshot regardless for debugging
await page.screenshot({ path: 'tmp/tutorial-mesh-categories-debug.png', fullPage: true });

// CSS Modules emits classes like `library_library-category-title_<hash>`.
// Find by partial match on `library-category-title`.
const titles = await page.evaluate(() => {
const nodes = document.querySelectorAll('[class*="library-category-title"]');
return Array.from(nodes).map((n) => n.textContent.trim());
});
log('category titles =', JSON.stringify(titles));

await page.screenshot({ path: 'tmp/tutorial-mesh-categories.png', fullPage: true });

const expected = [
'通信入門 ① メッセージを送ってみよう',
'通信入門 ② ふたりで会話しよう',
'通信入門 ③ みんなで会話しよう (メッシュ)',
];
const missing = expected.filter((t) => !titles.includes(t));
if (missing.length) {
console.error('MISSING titles:', missing);
await page.screenshot({ path: 'tmp/tutorial-mesh-categories-failed.png', fullPage: true });
await browser.close();
process.exit(1);
}

log('OK: all 3 mesh-step categories visible');
await page.screenshot({ path: 'tmp/tutorial-mesh-categories.png', fullPage: true });
await browser.close();
Loading