feat: updates basic and adds bean sweeper - #19
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
The new BASIC INPUT echo path in BasicScreen can still wrap/scroll near the right edge (anchor/room/echo behavior), which can break the “keep answer on its own row” invariant.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR updates the BASIC app so the screen window becomes the sole transcript (program output + INPUT echo/typing) and is opened alongside the editor, and it adds a new sandboxed “Bean Sweeper” package to pkgs/.
Changes:
- BASIC: remove the editor’s console/INPUT field and route output + INPUT entry through
BasicScreen/session.screen, including support for repeated INPUT prompts. - UI/layout: ensure the BASIC screen window opens with the editor and is positioned beside it when there’s room; update tests accordingly.
- Packages: add the
bean-sweepersandboxed package (manifest/icon/ES2020 entry) and whitelist it from thepkgs/**/gitignore rule.
File summaries
| File | Description |
|---|---|
| tests/shell.test.tsx | Updates Terminal integration test to expect BASIC editor + screen windows. |
| tests/basicapp.test.tsx | Reworks BASIC app tests to assert against session.screen and drive INPUT via BasicScreen. |
| src/lib/basic/session.ts | Extends session bridge with submitInput and INPUT-generation tracking. |
| src/lib/basic/interpreter.ts | Adjusts INPUT resume to avoid double-echoing the typed line. |
| src/apps/BasicScreen.tsx | Adds INPUT capture/echo on the screen surface; removes “answer in …” jump UI. |
| src/apps/basicscreen.css | Removes now-unused .bscreen-jump styling. |
| src/apps/Basic.tsx | Removes console UI; always opens/maintains the screen window; wires INPUT submission via the session bridge. |
| src/apps/basic.css | Removes console styling. |
| pkgs/bean-sweeper/manifest.json | Declares new sandboxed package metadata and fs permission. |
| pkgs/bean-sweeper/main.js | Implements the Bean Sweeper game logic/UI and persistence via bw.fs. |
| pkgs/bean-sweeper/icon.svg | Adds the package icon. |
| CLAUDE.md | Updates docs for BASIC behavior and test count. |
| .gitignore | Whitelists pkgs/bean-sweeper from the pkgs/**/ ignore rule. |
| .github/workflows/deploy-coffeeshop-reusable.yml | Increases coffeeshop health-check sleep interval. |
Review details
Suppressed comments (4)
src/apps/BasicScreen.tsx:169
- The INPUT anchor can be set to screen.cursorCol even when it is the last column. Writing a caret/space at that position will immediately wrap to the next row; clamping the anchor avoids accidental wrap/scroll when INPUT starts at the far right edge.
const { screen } = session
anchorRef.current = { row: screen.cursorRow, col: screen.cursorCol }
echo('', true)
src/apps/BasicScreen.tsx:200
- This room calculation doesn't fully prevent wrapping because the echo writes an extra caret (and at least one clearing space). It should reserve one cell for the caret so typing never forces Screen.write() into the last column.
// Two cells short of the right edge: the caret needs one, and writing
// the last one would wrap the cursor onto the next row.
const room = Math.max(0, session.screen.cols - anchorRef.current.col - 2)
if (bufferRef.current.length >= room) return
tests/basicapp.test.tsx:181
- This test creates another basic-screen window via openWindow(), but Basic already created one for the session. Rendering the existing session.screenWindow avoids duplicate windows and makes focus assertions more representative.
const { id } = mount()
const screenId = useDesktop.getState().openWindow({ appId: 'basic-screen' })
render(<BasicScreen windowId={screenId} args={{ owner: id }} />)
tests/basicapp.test.tsx:206
- Opening a new basic-screen window here can leave multiple screen windows associated with the same session during the test. Prefer rendering the screen window Basic opened for the session (getSession(id).screenWindow) to avoid duplicate desktop state.
const { id } = mount()
const screenId = useDesktop.getState().openWindow({ appId: 'basic-screen' })
render(<BasicScreen windowId={screenId} args={{ owner: id }} />)
- Files reviewed: 12/14 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| const echo = useCallback( | ||
| (text: string, caret: boolean) => { | ||
| const anchor = anchorRef.current | ||
| if (!session || !anchor) return | ||
| const { screen } = session | ||
| screen.locate(anchor.row, anchor.col, null, 0) | ||
| screen.write(caret ? `${text}_ ` : `${text} `) | ||
| }, | ||
| [session], | ||
| ) |
| const screenId = useDesktop.getState().openWindow({ appId: 'basic-screen' }) | ||
| render(<BasicScreen windowId={screenId} args={{ owner: id }} />) |
0a96ab2 to
5a752de
Compare
No description provided.