|
| 1 | +--- |
| 2 | +pr: sst/opencode#24984 |
| 3 | +sha: 62b7694465283e95adb556b60acbd6ac004dcf23 |
| 4 | +verdict: merge-after-nits |
| 5 | +reviewed_at: 2026-04-29T18:31:00Z |
| 6 | +--- |
| 7 | + |
| 8 | +# fix(core): reconnect editor context for session directory |
| 9 | + |
| 10 | +## Context |
| 11 | + |
| 12 | +Before this PR, `EditorContextProvider` in `packages/opencode/src/cli/cmd/tui/context/editor.ts` resolved |
| 13 | +the editor lock file once at process startup using `process.cwd()`. After a |
| 14 | +session switch, opencode kept talking to whatever editor server was attached |
| 15 | +when the binary first started — wrong file tree, wrong selection ranges, sometimes |
| 16 | +a dead socket. The author moves the connection state out of `onMount` into |
| 17 | +provider-scoped `let` bindings and adds an explicit `directory` variable so |
| 18 | +the provider can re-resolve `resolveEditorConnection(directory)` whenever |
| 19 | +the active session changes. |
| 20 | + |
| 21 | +## What's good |
| 22 | + |
| 23 | +- The lift from `onMount` closure to provider-scope (`let socket`, |
| 24 | + `let directory = process.cwd()`, etc.) is the right shape — re-mounting |
| 25 | + was never the trigger; *session change* is. The reconnect call site |
| 26 | + ends up colocated with `setStore("server", …)`, which is the only place |
| 27 | + state actually needs to be invalidated. |
| 28 | +- `WebSocketImpl` injection (`init: (props: { WebSocketImpl?: typeof WebSocket })`) |
| 29 | + is the testability hook the existing test suite needed — the new |
| 30 | + `editor-context.test.tsx` (referenced in the PR body) can stub the socket |
| 31 | + without monkey-patching the global. Good restraint: it falls back to the |
| 32 | + real `WebSocket` at the call site rather than at module load. |
| 33 | +- Same-directory short-circuit (avoid socket churn when nothing changed) |
| 34 | + is mentioned in the PR body and is the right safety: editor servers |
| 35 | + often have warm-up costs, and an unconditional reconnect on every |
| 36 | + session activation would be a regression. |
| 37 | + |
| 38 | +## Concerns / nits |
| 39 | + |
| 40 | +1. The `socket.readyState !== 1` literal in the new `send()` replaces |
| 41 | + `WebSocket.OPEN`. That works but loses self-documentation and risks |
| 42 | + diverging if `WebSocketImpl` is a polyfill that uses a different enum. |
| 43 | + Prefer `WebSocketImpl.OPEN` (or pull it onto a local `const OPEN = WebSocketImpl.OPEN`). |
| 44 | +2. `lastSubmittedEditorSelectionKey` in `prompt/index.tsx` is a closure-scoped |
| 45 | + `let` inside `Prompt(props)`. If `<Prompt>` ever remounts (route change, |
| 46 | + keyed re-render), the dedupe state resets and the same selection will be |
| 47 | + re-submitted as a fresh part. Worth a comment or a `createSignal` so |
| 48 | + intent is explicit. |
| 49 | +3. The PR drops `editor.clearSelection()` after submit and replaces it with |
| 50 | + the dedupe-key assignment. That changes semantics: the editor selection |
| 51 | + stays "live" across submits. Confirm with the design owner that this is |
| 52 | + intentional — for users who want each prompt to start clean, this is a |
| 53 | + subtle UX shift. |
| 54 | + |
| 55 | +## Verdict |
| 56 | + |
| 57 | +`merge-after-nits` — fix the `WebSocket.OPEN` literal, add a one-line |
| 58 | +comment explaining the `lastSubmittedEditorSelectionKey` lifecycle, and |
| 59 | +double-check the `clearSelection` removal is intentional. |
0 commit comments