-
-
Notifications
You must be signed in to change notification settings - Fork 16
Description
Summary
Display group name and expiry time in the menu bar when meshV2 extension is enabled, with visual connection status indicators.
Current State
Currently, the meshV2 extension has no menu bar presence. Connection state is tracked in the extension but not visibly displayed to users.
Connection state tracking (gui/scratch-vm/src/extensions/scratch3_mesh_v2/index.js):
- Line 57:
this.connectionStatetracks state ('disconnected', 'scanning', 'connecting', 'connected', 'error') - Line 259-287:
setConnectionState()method manages state transitions - Line 304-338:
connectedMessage()generates status message but only shown in connection modal
UI Design
When meshV2 extension is enabled, display in menu bar:
(アイコン) メッシュ ▽
└─ グループ名 (有効期限)
States
- Not connected: Display "未接続"
- Connected as host: Display "グループ名 (有効期限)"
- Connected as member: Display "グループ名 (有効期限)"
Behavior
- Clicking the menu item opens the meshV2 connection modal
- Icon changes based on connection state:
- Connected:
meshV2_connected.png - Disconnected:
meshV2_disconnected.png
- Connected:
Connection Status Icons
Icon Files Location
- Connected:
gui/smalruby3-gui/tmp/meshV2_connected.png(1024x1024) - Disconnected:
gui/smalruby3-gui/tmp/meshV2_disconnected.png(1024x1024)
Icon Processing Required
Resize to 21x21 pixels and move to appropriate location:
# Using ImageMagick
convert gui/smalruby3-gui/tmp/meshV2_connected.png -resize 21x21 gui/smalruby3-gui/src/components/menu-bar/icon--mesh-connected.png
convert gui/smalruby3-gui/tmp/meshV2_disconnected.png -resize 21x21 gui/smalruby3-gui/src/components/menu-bar/icon--mesh-disconnected.pngImplementation Details
1. Add Menu Bar State Management
File: gui/smalruby3-gui/src/reducers/menus.js
Add meshV2 menu state similar to other menus:
// Add to action types
const OPEN_MESH_V2_MENU = 'scratch-gui/menus/OPEN_MESH_V2_MENU';
const CLOSE_MESH_V2_MENU = 'scratch-gui/menus/CLOSE_MESH_V2_MENU';
// Add to initial state
const initialState = {
// ... existing state
meshV2: false
};
// Add to reducer
case OPEN_MESH_V2_MENU:
return Object.assign({}, state, {
meshV2: true
});
case CLOSE_MESH_V2_MENU:
return Object.assign({}, state, {
meshV2: false
});
// Add action creators
const openMeshV2Menu = () => ({type: OPEN_MESH_V2_MENU});
const closeMeshV2Menu = () => ({type: CLOSE_MESH_V2_MENU});
const meshV2MenuOpen = state => state.scratchGui.menus.meshV2;
// Export
export {
openMeshV2Menu,
closeMeshV2Menu,
meshV2MenuOpen
};2. Add Menu Bar Component
File: gui/smalruby3-gui/src/components/menu-bar/menu-bar.jsx
Import Icons (add near line 100):
import meshConnectedIcon from './icon--mesh-connected.png';
import meshDisconnectedIcon from './icon--mesh-disconnected.png';Import Menu Actions (add near line 88):
import {
openMeshV2Menu,
closeMeshV2Menu,
meshV2MenuOpen
} from '../../reducers/menus';Add Method to Get MeshV2 State (add near line 240):
getMeshV2Status () {
const vm = this.props.vm;
if (!vm.runtime || !vm.runtime.extensionManager) {
return {loaded: false};
}
const isLoaded = vm.runtime.extensionManager.isExtensionLoaded('meshV2');
if (!isLoaded) {
return {loaded: false};
}
// Get extension instance
const extension = vm.runtime.ext_meshV2;
if (!extension) {
return {loaded: true, connected: false};
}
const isConnected = extension.isConnected();
const message = extension.connectedMessage();
const connected = extension.connectionState === 'connected';
return {
loaded: true,
connected: connected,
message: message,
icon: connected ? meshConnectedIcon : meshDisconnectedIcon
};
}
handleMeshV2MenuClick () {
// Open connection modal
if (this.props.vm.runtime && this.props.vm.runtime.emit) {
this.props.vm.runtime.emit(
this.props.vm.runtime.constructor.PERIPHERAL_SCAN_START,
'meshV2'
);
}
}Add Menu Item in Render (add after koshien menu, around line 970):
{(() => {
const meshV2Status = this.getMeshV2Status();
if (!meshV2Status.loaded) return null;
return (
<div
className={classNames(styles.menuBarItem, styles.hoverable, {
[styles.active]: this.props.meshV2MenuOpen
})}
>
<img
className={styles.meshIcon}
src={meshV2Status.icon}
/>
<div
className={classNames(
styles.collapsibleMenuBarItem
)}
onMouseUp={this.props.onClickMeshV2}
>
<span className={styles.collapsibleLabel}>
<FormattedMessage
defaultMessage="Mesh"
description="Label for Mesh V2 menu"
id="gui.menuBar.meshV2"
/>
</span>
<img src={dropdownCaret} />
<MenuBarMenu
className={classNames(styles.menuBarMenu)}
open={this.props.meshV2MenuOpen}
place={this.props.isRtl ? 'left' : 'right'}
onRequestClose={this.props.onRequestCloseMeshV2}
>
<MenuItem onClick={this.handleMeshV2MenuClick}>
{meshV2Status.message}
</MenuItem>
</MenuBarMenu>
</div>
</div>
);
})()}3. Add CSS Styles
File: gui/smalruby3-gui/src/components/menu-bar/menu-bar.css
.mesh-icon {
width: 21px;
height: 21px;
margin-right: 0.5rem;
}4. Connect Redux State
File: gui/smalruby3-gui/src/components/menu-bar/menu-bar.jsx
Update mapStateToProps (around line 1000+):
const mapStateToProps = state => {
// ... existing mappings
meshV2MenuOpen: meshV2MenuOpen(state)
};Update mapDispatchToProps (around line 1050+):
const mapDispatchToProps = dispatch => ({
// ... existing dispatches
onClickMeshV2: () => dispatch(openMeshV2Menu()),
onRequestCloseMeshV2: () => dispatch(closeMeshV2Menu())
});5. Update Messages
File: gui/smalruby3-gui/src/locales/ja.js
'gui.menuBar.meshV2': 'メッシュ',
'mesh.notConnected': '未接続'Extension Connection Modal Integration
The menu item click should trigger the existing connection modal flow:
Current flow (gui/scratch-vm/src/extensions/scratch3_mesh_v2/index.js):
- User clicks extension in library → connection modal appears
- Extension calls
scan()method (line 159-209) - User selects host/group →
connect()called (line 212-253)
New flow from menu bar:
- User clicks menu bar item → emit
PERIPHERAL_SCAN_STARTevent - Same connection modal flow as above
Files to Modify
-
gui/smalruby3-gui/src/reducers/menus.js
- Add meshV2 menu state management
-
gui/smalruby3-gui/src/components/menu-bar/menu-bar.jsx
- Import icons and actions
- Add
getMeshV2Status()method - Add
handleMeshV2MenuClick()method - Add menu bar item in render
- Update mapStateToProps and mapDispatchToProps
-
gui/smalruby3-gui/src/components/menu-bar/menu-bar.css
- Add
.mesh-iconstyle
- Add
-
gui/smalruby3-gui/src/locales/ja.js (and other locales)
- Add
gui.menuBar.meshV2message - Add
mesh.notConnectedmessage (if not exists)
- Add
-
Create icon files:
gui/smalruby3-gui/src/components/menu-bar/icon--mesh-connected.png(21x21)gui/smalruby3-gui/src/components/menu-bar/icon--mesh-disconnected.png(21x21)
Testing
- Load meshV2 extension
- Verify menu bar shows "メッシュ ▽" with disconnected icon
- Click menu item → verify "未接続" appears
- Click "未接続" → verify connection modal opens
- Connect as host → verify icon changes to connected
- Verify menu shows "んたしたたに (14:55)" or similar
- Disconnect → verify icon returns to disconnected state
Visual Feedback
The icon provides clear visual feedback:
- Green/Connected icon: User is connected to a mesh network
- Gray/Disconnected icon: User is not connected
This helps users quickly understand their mesh connection status without opening menus.
Future Enhancements
After April 2026 when legacy mesh is removed:
- Remove " V2" from all labels
- This menu bar item becomes the primary mesh interface
- Legacy mesh menu bar presence (if any) is removed