Skip to content

Commit ff33e87

Browse files
authored
Merge branch 'master' into test/musicutils-next-2-functions
2 parents af8298b + 943f215 commit ff33e87

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+7165
-3106
lines changed

.github/workflows/po-to-json-autocommit.yml renamed to .github/workflows/po-to-json-validation.yml

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
name: Auto-convert PO to JSON and commit
1+
name: Convert PO to JSON and verify changes
22

33
on:
44
push:
55
paths:
66
- 'po/**/*.po'
77

88
jobs:
9-
convert-and-commit:
9+
convert-and-verify:
1010
runs-on: ubuntu-latest
1111

1212
steps:
@@ -30,7 +30,7 @@ jobs:
3030
echo "$(cat changed_po_files.txt)" >> $GITHUB_OUTPUT
3131
echo "EOF" >> $GITHUB_OUTPUT
3232
33-
- name: Run conversion script
33+
- name: Convert PO files to JSON
3434
if: steps.find_po.outputs.po_files != ''
3535
run: |
3636
mkdir -p locales
@@ -39,18 +39,13 @@ jobs:
3939
python3 convert_po_to_json.py "$po_file" "locales"
4040
done < changed_po_files.txt
4141
42-
- name: Commit and push updated JSON
42+
- name: Verify JSON conversion changes
4343
if: steps.find_po.outputs.po_files != ''
4444
run: |
45-
git config --global user.name "github-actions[bot]"
46-
git config --global user.email "github-actions[bot]@users.noreply.github.com"
47-
48-
git add locales/*.json
49-
50-
if git diff --cached --quiet; then
51-
echo "✅ No JSON changes to commit."
45+
if ! git diff --quiet locales/; then
46+
echo "❌ Generated translation JSON files have changes that need to be committed."
47+
echo "Please run the PO-to-JSON conversion locally and commit the generated files in locales/."
48+
exit 1
5249
else
53-
git commit -m "chore(i18n): auto-update JSON files from updated PO files"
54-
git push origin ${{ github.ref }}
55-
echo "🚀 Pushed updated JSON files."
50+
echo "✅ Translation JSON files are verified and up to date."
5651
fi

Docs/GLOBAL_STATE_AUDIT.md

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
# Global State Audit - Logo Subsystem
2+
3+
## Purpose
4+
5+
This document audits implicit global state dependencies in the **Logo subsystem** as part of the architectural improvement initiative to reduce global coupling and improve testability.
6+
7+
## Scope
8+
9+
- **Subsystem**: Logo (`js/logo.js`)
10+
- **Related Issue**: Audit and Reduce Implicit Global State Usage
11+
- **Date**: 2026-02-04
12+
13+
## Current Architecture
14+
15+
### Logo Class Dependencies
16+
17+
The Logo class currently depends on a single `activity` object passed to its constructor, which acts as a facade to access multiple subsystems:
18+
19+
```javascript
20+
constructor(activity) {
21+
this.activity = activity;
22+
// ... initialization
23+
}
24+
```
25+
26+
### Identified Global Dependencies (via `this.activity`)
27+
28+
#### 1. **Blocks Subsystem** (`this.activity.blocks`)
29+
30+
- **Usage Count**: ~50+ references
31+
- **Access Pattern**: `this.activity.blocks.*`
32+
- **Key Methods Used**:
33+
- `blockList` - Direct property access
34+
- `unhighlightAll()` - Visual feedback
35+
- `bringToTop()` - Z-index management
36+
- `showBlocks()` / `hideBlocks()` - Visibility control
37+
- `sameGeneration()` - Block relationship queries
38+
- `visible` - State query
39+
40+
**Impact**: High - Logo heavily depends on Blocks for execution flow and visual feedback
41+
42+
#### 2. **Turtles Subsystem** (`this.activity.turtles`)
43+
44+
- **Usage Count**: ~80+ references
45+
- **Access Pattern**: `this.activity.turtles.*`
46+
- **Key Methods Used**:
47+
- `turtleList` - Direct iteration over turtles
48+
- `ithTurtle(index)` - Get specific turtle
49+
- `getTurtle(index)` - Get turtle by index
50+
- `getTurtleCount()` - Count query
51+
- `add()` - Create new turtle
52+
- `markAllAsStopped()` - State management
53+
54+
**Impact**: Critical - Logo is the execution engine for turtle commands
55+
56+
#### 3. **Stage** (`this.activity.stage`)
57+
58+
- **Usage Count**: ~10+ references
59+
- **Access Pattern**: `this.activity.stage.*`
60+
- **Key Methods Used**:
61+
- `addEventListener()` - Event handling
62+
- `removeEventListener()` - Cleanup
63+
64+
**Impact**: Medium - Used for interaction and event handling
65+
66+
#### 4. **UI/Error Handling**
67+
68+
- **Methods**:
69+
- `this.activity.errorMsg()` - Error display
70+
- `this.activity.hideMsgs()` - Message management
71+
- `this.activity.saveLocally()` - Persistence
72+
73+
**Impact**: Medium - UI feedback and state persistence
74+
75+
#### 5. **Configuration/State**
76+
77+
- **Properties**:
78+
- `this.activity.showBlocksAfterRun` - Boolean flag
79+
- `this.activity.onStopTurtle` - Callback
80+
- `this.activity.onRunTurtle` - Callback
81+
- `this.activity.meSpeak` - Speech synthesis
82+
83+
**Impact**: Low-Medium - Configuration and callbacks
84+
85+
## Dependency Graph
86+
87+
```
88+
Logo
89+
├─► Activity (facade)
90+
├─► Blocks (execution context, visual feedback)
91+
├─► Turtles (command execution, state management)
92+
├─► Stage (event handling)
93+
├─► ErrorMsg (UI feedback)
94+
└─► Configuration (flags, callbacks)
95+
```
96+
97+
## Analysis
98+
99+
### Current Problems
100+
101+
1. **Tight Coupling**: Logo cannot be instantiated or tested without a full Activity object
102+
2. **Hidden Dependencies**: The Activity facade obscures what Logo actually needs
103+
3. **Circular Dependencies**: Logo ↔ Activity ↔ Blocks ↔ Turtles creates complex initialization
104+
4. **Testing Difficulty**: Mocking requires recreating entire Activity object graph
105+
5. **Unclear Contracts**: No explicit interface defining what Logo needs from Activity
106+
107+
### Unavoidable Dependencies
108+
109+
Some dependencies are fundamental to Logo's role as the execution engine:
110+
111+
- **Turtles**: Logo executes turtle commands, so this dependency is core
112+
- **Blocks**: Logo interprets block programs, so this dependency is core
113+
114+
### Refactorable Dependencies
115+
116+
These could be made explicit or injected:
117+
118+
- **Stage**: Could be injected as an event bus interface
119+
- **Error handling**: Could be injected as a logger/error handler interface
120+
- **Configuration**: Could be passed as a config object
121+
- **Callbacks**: Could be injected explicitly
122+
123+
## Proposed Refactoring Strategy
124+
125+
### Phase 1: Document Current State ✅
126+
127+
- Create this audit document
128+
- Identify all `this.activity.*` references
129+
- Categorize by subsystem and impact
130+
131+
### Phase 2: Extract Interfaces (Small, Safe Changes)
132+
133+
Create explicit interfaces for Logo's dependencies:
134+
135+
```javascript
136+
// LogoDependencies interface
137+
class LogoDependencies {
138+
constructor({
139+
blocks, // Blocks subsystem
140+
turtles, // Turtles subsystem
141+
stage, // Event bus
142+
errorHandler, // Error display
143+
messageHandler, // Message display
144+
storage, // Persistence
145+
config, // Configuration
146+
callbacks // onStop, onRun, etc.
147+
}) {
148+
this.blocks = blocks;
149+
this.turtles = turtles;
150+
this.stage = stage;
151+
this.errorHandler = errorHandler;
152+
this.messageHandler = messageHandler;
153+
this.storage = storage;
154+
this.config = config;
155+
this.callbacks = callbacks;
156+
}
157+
}
158+
```
159+
160+
### Phase 3: Refactor Constructor (Backward Compatible)
161+
162+
```javascript
163+
constructor(activityOrDeps) {
164+
// Support both old and new patterns
165+
if (activityOrDeps.blocks && activityOrDeps.turtles) {
166+
// New explicit dependencies
167+
this.deps = activityOrDeps;
168+
} else {
169+
// Old activity facade - extract dependencies
170+
this.deps = new LogoDependencies({
171+
blocks: activityOrDeps.blocks,
172+
turtles: activityOrDeps.turtles,
173+
stage: activityOrDeps.stage,
174+
errorHandler: activityOrDeps.errorMsg.bind(activityOrDeps),
175+
// ... etc
176+
});
177+
}
178+
}
179+
```
180+
181+
### Phase 4: Bind Dependencies Locally for Readability
182+
183+
To reduce verbosity while maintaining explicit dependency injection, bind commonly-used dependencies as local properties in the constructor:
184+
185+
```javascript
186+
constructor(activityOrDeps) {
187+
// ... dependency injection setup ...
188+
189+
// Bind commonly-used dependencies locally for readability
190+
// This reduces verbosity while maintaining explicit dependency injection
191+
this.blocks = this.deps.blocks;
192+
this.turtles = this.deps.turtles;
193+
this.stage = this.deps.stage;
194+
}
195+
```
196+
197+
This allows using `this.blocks.doSomething()` instead of `this.deps.blocks.doSomething()` throughout the class, while still preserving:
198+
199+
- **Explicit dependency tracking**: All dependencies are declared in the constructor
200+
- **Grep-ability**: Can search for `this.blocks` to find all usages
201+
- **Auditability**: Clear what external dependencies the class uses
202+
- **No behavior change**: Functionally equivalent to `this.deps.*` access
203+
204+
**Note**: Both `this.deps.*` (maximum audit clarity) and locally bound references (improved readability) are acceptable patterns. Choose based on the specific needs of each subsystem.
205+
206+
### Phase 5: Add Tests
207+
208+
With explicit dependencies, Logo becomes testable:
209+
210+
```javascript
211+
const mockDeps = {
212+
blocks: createMockBlocks(),
213+
turtles: createMockTurtles()
214+
// ... minimal mocks
215+
};
216+
const logo = new Logo(mockDeps);
217+
```
218+
219+
## Benefits
220+
221+
1. **Explicit Contracts**: Clear interface showing what Logo needs
222+
2. **Improved Testability**: Can inject minimal mocks instead of full Activity
223+
3. **Better Documentation**: Dependencies are self-documenting
224+
4. **Reduced Coupling**: Logo depends on interfaces, not concrete Activity
225+
5. **Migration Path**: Backward compatible during transition
226+
6. **Foundation for v4**: Establishes pattern for future architecture
227+
228+
## Risks & Mitigation
229+
230+
### Risk 1: Breaking Changes
231+
232+
**Mitigation**: Use adapter pattern to support both old and new constructors
233+
234+
### Risk 2: Incomplete Refactoring
235+
236+
**Mitigation**: Keep scope limited to Logo subsystem only
237+
238+
### Risk 3: Testing Overhead
239+
240+
**Mitigation**: Create helper functions for common mock setups
241+
242+
## Success Metrics
243+
244+
- [x] All Logo dependencies explicitly documented
245+
- [x] Dependency interface created and documented
246+
- [x] Logo constructor supports explicit dependencies
247+
- [x] Backward compatibility maintained (both patterns supported)
248+
- [ ] No behavior changes (existing tests pass) - _needs verification_
249+
- [x] New unit tests for Logo with mocked dependencies
250+
- [x] Pattern documented for future subsystems
251+
252+
## Implementation Status
253+
254+
The Logo subsystem has been updated to support the `LogoDependencies` pattern. This implementation is backward-compatible with the existing `Activity` facade.
255+
256+
### Completed
257+
258+
- [x] Initial audit of global state in Logo subsystem
259+
- [x] Creation of `LogoDependencies` class
260+
- [x] Update to `Logo` constructor to support dependency injection
261+
- [x] Unit tests for `LogoDependencies` pattern
262+
263+
### Planned
264+
265+
- [ ] Gradual refactoring of internal `this.activity` references
266+
- [ ] Application of pattern to other core subsystems (Blocks, Turtles)
267+
268+
## References
269+
270+
- Issue #2767: Identify and/or fix high-level inconsistencies
271+
- Related work: PhraseMaker widget isolation
272+
- RequireJS migration considerations
273+
274+
---
275+
276+
**Status**: Phase 3 Complete
277+
**Owner**: @vanshika2720

Docs/troubleshooting.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,11 @@ The development server fails to start because port 3000 is already in use.
6060
Solution:
6161
Start the server on a different port:
6262

63-
npm start -- --port=3001
63+
PORT=3001 npm start
64+
65+
Or for development mode:
66+
67+
PORT=3001 npm run dev
6468

6569
Then open http://localhost:3001 in your browser.
6670

0 commit comments

Comments
 (0)