···2424 run: npm install
25252626 - name: Test
2727+ run: npm test
2828+2929+ - name: Lint and typecheck
2730 env:
2831 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2932 USERNAME: ${{ secrets.USERNAME }}
+104
docs/BrainWaves Technical Implementation Plan (2026 Modernization).md
···11+# BrainWaves: Technical Implementation Plan (2026 Modernization)
22+33+**Author:** Manus AI
44+**Date:** March 2026
55+66+This document outlines a step-by-step technical implementation plan for AI agents working on the BrainWaves codebase. The goal is to modernize the stack, resolve deployment blockers, and replace the UI library, while establishing a robust testing harness.
77+88+*Note: The migration to Lab Streaming Layer (LSL) is explicitly excluded from this phase and will be addressed once the application is successfully building and deploying.*
99+1010+---
1111+1212+## Phase 1: Test Harness & Build Environment Setup
1313+1414+Before modifying the application logic, we must establish a reliable testing harness using Vitest and ensure the Electron build pipeline is functional. The current codebase uses a legacy Jest configuration (`"test": "cross-env jest --passWithNoTests"`) which must be replaced.
1515+1616+### Step 1.1: Install and Configure Vitest
1717+* **Action:** Remove Jest dependencies and install Vitest, `@testing-library/react`, `@testing-library/jest-dom`, and `jsdom`.
1818+* **Action:** Create a `vitest.config.ts` file configured for a React/DOM environment.
1919+* **Action:** Update `package.json` scripts to use Vitest (`"test": "vitest run"`, `"test:watch": "vitest"`).
2020+* **Acceptance Criteria:**
2121+ * A basic sanity test (`src/renderer/App.test.tsx`) rendering a simple component passes using `npm test`.
2222+ * The CI workflow (`.github/workflows/test.yml`) is updated to run `npm test` using Vitest.
2323+2424+### Step 1.2: Verify Electron Build Pipeline
2525+* **Action:** Run the existing `npm run build` and `npm run package-ci` commands to identify any immediate build failures caused by Node 22 or Vite 7.
2626+* **Action:** Fix any immediate compilation errors in `src/main/index.ts` or `vite.config.ts`.
2727+* **Acceptance Criteria:**
2828+ * `npm run build` completes without errors.
2929+ * An integration test (`tests/build.test.ts`) is added that programmatically verifies the existence of the `out/main/index.js` and `out/renderer/index.html` files after a build.
3030+3131+---
3232+3333+## Phase 2: Routing and State Synchronization Modernization
3434+3535+The codebase currently relies on `react-router-dom v5` and the abandoned `connected-react-router` (which syncs router state to Redux). This causes significant issues with modern React 18 and Redux Toolkit.
3636+3737+### Step 2.1: Remove `connected-react-router`
3838+* **Action:** Uninstall `connected-react-router`.
3939+* **Action:** Remove `routerMiddleware` and `connectRouter` from `src/renderer/store.ts` and `src/renderer/reducers/index.ts`.
4040+* **Action:** Remove `ConnectedRouter` from `src/renderer/containers/Root.tsx` and replace it with a standard `HashRouter` from `react-router-dom`.
4141+* **Acceptance Criteria:**
4242+ * The Redux store initializes successfully without the router reducer.
4343+ * Unit tests verify that the Redux store can be created with initial state (`tests/store.test.ts`).
4444+4545+### Step 2.2: Upgrade to React Router v6/v7
4646+* **Action:** Upgrade `react-router-dom` to the latest stable version.
4747+* **Action:** Refactor `src/renderer/routes.tsx` to use the new `<Routes>` and `<Route element={<Component />}>` syntax instead of the legacy `<Switch>` and `component={}` props.
4848+* **Action:** Refactor class components (e.g., `HomeComponent`, `DesignComponent`) that rely on `this.props.history.push`. Convert them to functional components using the `useNavigate` hook, or create a Higher-Order Component (HOC) `withRouter` wrapper if functional conversion is too extensive.
4949+* **Action:** Update `src/renderer/epics/experimentEpics.ts` which currently listens to `@@router/LOCATION_CHANGE`. Refactor the logic to trigger based on specific Redux actions rather than URL changes.
5050+* **Acceptance Criteria:**
5151+ * Unit tests (`tests/routing.test.tsx`) verify that navigation between the Home, Design, and Collect screens renders the correct components.
5252+5353+---
5454+5555+## Phase 3: Pyodide and Python Dependency Modernization
5656+5757+The application uses Pyodide `v0.21.0` (hardcoded in `internals/scripts/InstallPyodide.js`) and relies on deprecated Python libraries for data visualization.
5858+5959+### Step 3.1: Upgrade Pyodide
6060+* **Action:** Update `internals/scripts/InstallPyodide.js` to download a modern stable release of Pyodide (e.g., `v0.27.0`).
6161+* **Action:** Ensure the `vite.config.ts` `publicDir` setting still correctly serves the updated Pyodide WASM and JS files.
6262+* **Acceptance Criteria:**
6363+ * An integration test (`tests/pyodide.test.ts`) successfully instantiates the Pyodide web worker (`src/renderer/utils/pyodide/webworker.js`) and executes a simple `1 + 1` Python command.
6464+6565+### Step 3.2: Refactor Python Plotting Logic
6666+* **Action:** In `src/renderer/utils/pyodide/utils.py`, locate the usage of `sns.tsplot` (which was removed in Seaborn v0.10.0).
6767+* **Action:** Rewrite the `plot_conditions` function to use `sns.lineplot` or standard `matplotlib.pyplot.plot` with `fill_between` for confidence intervals.
6868+* **Acceptance Criteria:**
6969+ * A unit test (`tests/python_utils.test.ts`) loads `utils.py` into the Pyodide worker, passes mock EEG data, and verifies that the plotting function executes without throwing a Python `AttributeError`.
7070+7171+---
7272+7373+## Phase 4: UI Library Replacement (Semantic UI to Shadcn/ui)
7474+7575+`semantic-ui-react` is abandoned and throws deprecation warnings in React 18. We will replace it with Tailwind CSS and Shadcn/ui components.
7676+7777+### Step 4.1: Install Tailwind CSS and Shadcn/ui
7878+* **Action:** Install Tailwind CSS, PostCSS, and Autoprefixer. Configure `tailwind.config.js` and `postcss.config.js`.
7979+* **Action:** Initialize Shadcn/ui (`npx shadcn-ui@latest init`) and configure it to output components to `src/renderer/components/ui`.
8080+* **Acceptance Criteria:**
8181+ * A unit test verifies that a basic Shadcn/ui `Button` component renders correctly with Tailwind utility classes applied.
8282+8383+### Step 4.2: Component-by-Component Replacement
8484+* **Action:** Identify the ~26 files importing `semantic-ui-react`. The most heavily used components are `Segment`, `Button`, `Grid`, and `Header`.
8585+* **Action:** Replace `semantic-ui-react` components with their Shadcn/ui equivalents:
8686+ * `Button` -> Shadcn `Button`
8787+ * `Segment` -> Shadcn `Card` or a simple `div` with Tailwind borders/padding.
8888+ * `Grid` -> Tailwind CSS Grid (`grid grid-cols-12 gap-4`).
8989+ * `Header` -> Standard HTML `h1`-`h6` tags with Tailwind typography classes.
9090+ * `Modal` -> Shadcn `Dialog`.
9191+* **Action:** Remove `semantic-ui-css` from `src/renderer/index.tsx` and uninstall `semantic-ui-react`.
9292+* **Acceptance Criteria:**
9393+ * `grep -r "semantic-ui-react" src/` returns zero results.
9494+ * Visual regression or DOM snapshot tests (`tests/ui_migration.test.tsx`) for key screens (Home, Design, Collect) pass, ensuring the new components render without crashing.
9595+9696+---
9797+9898+## Phase 5: Final Build and Verification
9999+100100+### Step 5.1: End-to-End Build Verification
101101+* **Action:** Run the full build pipeline (`npm run package-all`).
102102+* **Acceptance Criteria:**
103103+ * The Electron application compiles and packages successfully for the target OS.
104104+ * All Vitest suites (Routing, Store, Pyodide, UI) pass with 100% success rate.
+353
docs/progress.md
···11+# BrainWaves Modernization — Implementation Progress
22+33+Tracking file for executing the [Technical Implementation Plan](./BrainWaves_%20Technical%20Implementation%20Plan%20(2026%20Modernization).md) across sessions.
44+55+**Last updated:** 2026-03-07
66+**Overall status:** PHASES 1–4 COMPLETE (Phase 5 pending: npm install + build verification)
77+88+---
99+1010+## Codebase Reconnaissance (completed)
1111+1212+Key files read and understood before starting implementation:
1313+1414+- `package.json` — current deps, jest config, scripts
1515+- `src/renderer/store.ts` — uses `connected-react-router` (`routerMiddleware`, `createHashHistory`)
1616+- `src/renderer/reducers/index.ts` — uses `connectRouter` from `connected-react-router`
1717+- `src/renderer/containers/Root.tsx` — uses `ConnectedRouter`, receives `history` prop
1818+- `src/renderer/index.tsx` — imports and passes `history` from store to Root
1919+- `src/renderer/routes.tsx` — React Router v5 `<Switch>` / `<Route component={}>` / custom `PropsRoute`
2020+- `src/renderer/epics/experimentEpics.ts` — `autoSaveEpic` and `navigationCleanupEpic` listen to `@@router/LOCATION_CHANGE`
2121+- `src/renderer/containers/TopNavBarContainer.ts` — maps `state.router.location` to props
2222+- `src/renderer/components/TopNavComponent/index.tsx` — class component, uses `this.props.location.pathname`
2323+- `src/renderer/components/HomeComponent/index.tsx` — class component, calls `this.props.history.push()`
2424+- `src/renderer/components/CollectComponent/index.tsx` — passes `history` prop to `ConnectModal` (unused there)
2525+- `src/renderer/components/CollectComponent/ConnectModal.tsx` — has `history` in Props but does NOT use it
2626+- `src/renderer/components/EEGExplorationComponent.tsx` — passes `history` to `ConnectModal` (unused)
2727+- `src/renderer/actions/experimentActions.ts` — RTK `createAction`, `typesafe-actions`
2828+- `src/renderer/utils/pyodide/utils.py` — uses `sns.tsplot` (removed in seaborn v0.10), seaborn import commented out
2929+- `src/renderer/utils/pyodide/webworker.js` — `importScripts('/pyodide/pyodide.js')`, loads matplotlib/mne/pandas
3030+- `internals/scripts/InstallPyodide.js` — downloads pyodide v0.21.0 tarball
3131+- `vite.config.ts` — `publicDir` serves `src/renderer/utils/pyodide/src/` as static assets
3232+- `.github/workflows/test.yml` — runs `npm run package-ci`, lint, tsc (no unit tests)
3333+- 26 files import `semantic-ui-react` (see list below)
3434+3535+---
3636+3737+## Phase 1: Test Harness & Vitest
3838+3939+**Status: COMPLETE**
4040+4141+### Step 1.1 — Install and configure Vitest
4242+4343+**What to do:**
4444+1. Edit `package.json`:
4545+ - Remove from `devDependencies`: `jest`, `@types/jest`, `identity-obj-proxy`, `react-test-renderer`
4646+ - Add to `devDependencies`: `vitest`, `@testing-library/react`, `@testing-library/jest-dom`, `jsdom`
4747+ - Change `"test": "cross-env jest --passWithNoTests"` → `"test": "vitest run"`
4848+ - Add `"test:watch": "vitest"`
4949+ - Remove the `"jest": { ... }` config block from package.json
5050+2. Create `vitest.config.ts` (project root)
5151+3. Create `src/test-setup.ts` (imports `@testing-library/jest-dom`)
5252+4. Create `src/renderer/App.test.tsx` (basic sanity render test)
5353+5. Run `npm install` to update node_modules
5454+5555+**Notes:**
5656+- Vitest needs `jsdom` environment for React component tests
5757+- The vite.config.ts babel plugins (decorators, class-properties) must also be present in vitest.config.ts
5858+- CSS modules: vitest handles them natively with `css.modules` config; no `identity-obj-proxy` needed
5959+6060+### Step 1.2 — Update CI workflow
6161+6262+**What to do:**
6363+1. Edit `.github/workflows/test.yml`: add `npm test` step before or after existing steps
6464+2. Create `tests/build.test.ts`: verifies `out/main/index.js` and `out/renderer/index.html` exist after build
6565+6666+---
6767+6868+## Phase 2: Routing Modernization
6969+7070+**Status: COMPLETE**
7171+7272+### Step 2.1 — Remove `connected-react-router`
7373+7474+**Package changes:**
7575+- Remove from `dependencies`: `connected-react-router`, `history`
7676+- Remove from `devDependencies`: `@types/history`, `@types/react-router`, `@types/react-router-dom`
7777+- Remove `overrides["connected-react-router"]` block
7878+7979+**File changes:**
8080+8181+| File | Change |
8282+|------|--------|
8383+| `src/renderer/store.ts` | Remove `createHashHistory`, `history` export, `routerMiddleware`, remove `router` from middleware |
8484+| `src/renderer/reducers/index.ts` | Remove `connectRouter`, `History` import, remove `router` from combineReducers, remove `router: any` from RootState |
8585+| `src/renderer/containers/Root.tsx` | Remove `ConnectedRouter`; use `HashRouter` from `react-router-dom`; remove `history` prop |
8686+| `src/renderer/index.tsx` | Remove `history` import; pass only `store` to `<Root>` |
8787+8888+### Step 2.2 — Upgrade to React Router v6
8989+9090+**Package changes:**
9191+- Change `react-router` and `react-router-dom`: `"^5.2.0"` → `"^6.x"` (v6 merges the two packages; keep both entries or consolidate)
9292+9393+**New file:**
9494+- `src/renderer/actions/routerActions.ts` — defines `RouterActions.RouteChanged(pathname: string)` action
9595+9696+**File changes:**
9797+9898+| File | Change |
9999+|------|--------|
100100+| `src/renderer/routes.tsx` | Replace `<Switch>/<Route component={}>` with `<Routes>/<Route element={<C />}>`. Remove `PropsRoute`. Pass `activeStep` directly as JSX prop on `<HomeContainer>`. |
101101+| `src/renderer/containers/App.tsx` | Add `NavigationTracker` functional component (uses `useLocation` + `useDispatch` to dispatch `RouterActions.RouteChanged` on location change) |
102102+| `src/renderer/epics/experimentEpics.ts` | Replace both `ofType('@@router/LOCATION_CHANGE')` epics to use `filter(isActionOf(RouterActions.RouteChanged))`. Access `action.payload` as pathname string directly. Remove `pluck('payload', 'pathname')` etc. |
103103+| `src/renderer/components/TopNavComponent/index.tsx` | Convert class → functional component. Replace `this.props.location.pathname` with `useLocation().pathname`. State becomes `useState`. Methods become callbacks. |
104104+| `src/renderer/containers/TopNavBarContainer.ts` | Remove `location: state.router.location` from mapStateToProps |
105105+| `src/renderer/components/HomeComponent/index.tsx` | Change `history: History` prop to `navigate: (path: string) => void`. Replace all `this.props.history.push(X)` with `this.props.navigate(X)`. Remove `import { History } from 'history'`. |
106106+| `src/renderer/containers/HomeContainer.ts` | Wrap exported component with `withNavigate` HOC that injects `useNavigate()` as `navigate` prop |
107107+| `src/renderer/components/CollectComponent/index.tsx` | Remove `history: History` from Props; remove passing `history` to `ConnectModal` |
108108+| `src/renderer/components/EEGExplorationComponent.tsx` | Remove `history: History` from Props; remove passing `history` to `ConnectModal` |
109109+| `src/renderer/components/CollectComponent/ConnectModal.tsx` | Remove `history: History` from Props interface |
110110+111111+**withNavigate HOC pattern (for HomeContainer.ts):**
112112+```tsx
113113+import React from 'react';
114114+import { useNavigate } from 'react-router-dom';
115115+116116+function withNavigate<P extends { navigate: (path: string) => void }>(
117117+ Component: React.ComponentType<P>
118118+) {
119119+ return function WithNavigate(props: Omit<P, 'navigate'>) {
120120+ const navigate = useNavigate();
121121+ return <Component {...(props as P)} navigate={navigate} />;
122122+ };
123123+}
124124+```
125125+126126+**NavigationTracker pattern (for App.tsx):**
127127+```tsx
128128+function NavigationTracker() {
129129+ const location = useLocation();
130130+ const dispatch = useDispatch();
131131+ useEffect(() => {
132132+ dispatch(RouterActions.RouteChanged(location.pathname));
133133+ }, [location.pathname, dispatch]);
134134+ return null;
135135+}
136136+```
137137+138138+**Tests to add:**
139139+- `tests/store.test.ts` — verifies store initializes without router reducer
140140+- `tests/routing.test.tsx` — verifies navigation between Home/Design/Collect renders correct components
141141+142142+---
143143+144144+## Phase 3: Pyodide Modernization
145145+146146+**Status: COMPLETE**
147147+148148+### Step 3.1 — Upgrade Pyodide version
149149+150150+**File:** `internals/scripts/InstallPyodide.js`
151151+152152+Changes:
153153+- `PYODIDE_VERSION`: `'0.21.0'` → `'0.27.0'`
154154+- `TAR_NAME`: `pyodide-build-${PYODIDE_VERSION}.tar.bz2` → `pyodide-${PYODIDE_VERSION}.tar.bz2`
155155+- `TAR_URL`: update to match new naming
156156+157157+**Note:** The tarball for 0.27.0 extracts to a `pyodide/` subdirectory, which is correct for the webworker's `importScripts('/pyodide/pyodide.js')` call. The old 0.21.0 tar was also `pyodide-build-*` but extracted to a `pyodide/` dir.
158158+159159+**Caution:** `mne` package availability in pyodide 0.27.0 needs verification. If not in the default package list, `webworker.js` `loadPackage(['matplotlib', 'mne', 'pandas'])` will fail. May need to load `mne` via `micropip.install('mne')` instead.
160160+161161+**Test to add:** `tests/pyodide.test.ts`
162162+163163+### Step 3.2 — Fix Python plotting (`utils.py`)
164164+165165+**File:** `src/renderer/utils/pyodide/utils.py`
166166+167167+The `plot_conditions` function currently:
168168+- Calls `sns.color_palette(...)` — but `import seaborn as sns` is commented out
169169+- Calls `sns.tsplot(...)` — removed from seaborn in v0.10.0
170170+- Calls `sns.despine()` — still exists but seaborn not imported
171171+172172+**Fix — replace `plot_conditions` body:**
173173+1. Replace `sns.color_palette(...)` with a hardcoded palette (consistent with `plot_topo`)
174174+2. Replace `sns.tsplot(...)` with manual bootstrap CI using numpy + `ax.plot()` + `ax.fill_between()`
175175+3. Replace `sns.despine()` with `ax.spines['top'].set_visible(False); ax.spines['right'].set_visible(False)`
176176+177177+**Bootstrap CI replacement for `sns.tsplot(X[...], time=times, color=color, n_boot=n_boot, ci=ci)`:**
178178+```python
179179+cond_data = X[y.isin(cond), ch_ind]
180180+mean = np.nanmean(cond_data, axis=0)
181181+n_samples = cond_data.shape[0]
182182+boot_means = np.array([
183183+ np.nanmean(cond_data[np.random.randint(0, n_samples, n_samples)], axis=0)
184184+ for _ in range(n_boot)
185185+])
186186+alpha = (100 - ci) / 2
187187+low = np.percentile(boot_means, alpha, axis=0)
188188+high = np.percentile(boot_means, 100 - alpha, axis=0)
189189+ax.plot(times, mean, color=color)
190190+ax.fill_between(times, low, high, color=color, alpha=0.3)
191191+```
192192+193193+**Test to add:** `tests/python_utils.test.ts`
194194+195195+---
196196+197197+## Phase 4: UI Library Replacement (Semantic UI → Tailwind + Shadcn/ui)
198198+199199+**Status: COMPLETE**
200200+201201+### Step 4.1 — Install Tailwind CSS and Shadcn/ui
202202+203203+**Package additions (devDependencies):**
204204+- `tailwindcss`, `postcss`, `autoprefixer`
205205+206206+**Package additions (dependencies):**
207207+- `@radix-ui/react-dialog`
208208+- `@radix-ui/react-dropdown-menu`
209209+- `@radix-ui/react-slot`
210210+- `class-variance-authority`
211211+- `clsx`
212212+- `tailwind-merge`
213213+214214+**Package removals (dependencies):**
215215+- `semantic-ui-react`
216216+- `semantic-ui-css`
217217+218218+**New config files:**
219219+- `tailwind.config.js`
220220+- `postcss.config.js`
221221+222222+**New UI component files** (in `src/renderer/components/ui/`):
223223+- `utils.ts` — `cn()` helper (`clsx` + `tailwind-merge`)
224224+- `button.tsx` — Shadcn Button (CVA variants)
225225+- `card.tsx` — Shadcn Card (replaces `Segment`)
226226+- `dialog.tsx` — Shadcn Dialog (replaces `Modal`)
227227+- `dropdown-menu.tsx` — Shadcn DropdownMenu (replaces `Dropdown`)
228228+- `table.tsx` — Shadcn Table (replaces `Table`)
229229+230230+**Update `src/renderer/app.global.css`:** add Tailwind directives (`@tailwind base/components/utilities`)
231231+232232+**Remove from `src/renderer/index.tsx`:** `import 'semantic-ui-css/semantic.min.css'`
233233+234234+### Step 4.2 — Component-by-component replacement
235235+236236+**26 files to update** (grep confirmed):
237237+238238+| File | Semantic UI components used | Status |
239239+|------|-----------------------------|--------|
240240+| `components/AnalyzeComponent.tsx` | Grid, Icon, Segment, Header, Dropdown, Divider, Button, Checkbox, Sidebar, DropdownProps | NOT STARTED |
241241+| `components/CleanComponent/CleanSidebar.tsx` | (need to read) | NOT STARTED |
242242+| `components/CleanComponent/index.tsx` | Grid, Button, Icon, Segment, Header, Dropdown, Sidebar, SidebarPusher, Divider, DropdownProps | NOT STARTED |
243243+| `components/CollectComponent/ConnectModal.tsx` | Modal, Button, Segment, List, Grid, Divider | NOT STARTED |
244244+| `components/CollectComponent/HelpSidebar.tsx` | (need to read) | NOT STARTED |
245245+| `components/CollectComponent/PreTestComponent.tsx` | (need to read) | NOT STARTED |
246246+| `components/CollectComponent/RunComponent.tsx` | (need to read) | NOT STARTED |
247247+| `components/CollectComponent/index.tsx` | (passes through, may be minimal) | NOT STARTED |
248248+| `components/DesignComponent/CustomDesignComponent.tsx` | (need to read) | NOT STARTED |
249249+| `components/DesignComponent/ParamSlider.tsx` | (need to read) | NOT STARTED |
250250+| `components/DesignComponent/StimuliDesignColumn.tsx` | (need to read) | NOT STARTED |
251251+| `components/DesignComponent/StimuliRow.tsx` | (need to read) | NOT STARTED |
252252+| `components/DesignComponent/index.tsx` | (need to read) | NOT STARTED |
253253+| `components/EEGExplorationComponent.tsx` | Grid, Button, Header, Segment, Image, Divider | NOT STARTED |
254254+| `components/HomeComponent/ExperimentCard.tsx` | (need to read) | NOT STARTED |
255255+| `components/HomeComponent/OverviewComponent.tsx` | (need to read) | NOT STARTED |
256256+| `components/HomeComponent/index.tsx` | Grid, Button, Header, Image, Table | NOT STARTED |
257257+| `components/InputCollect.tsx` | (need to read) | NOT STARTED |
258258+| `components/InputModal.tsx` | (need to read) | NOT STARTED |
259259+| `components/PreviewButtonComponent.tsx` | (need to read) | NOT STARTED |
260260+| `components/PreviewExperimentComponent.tsx` | (need to read) | NOT STARTED |
261261+| `components/PyodidePlotWidget.tsx` | (need to read) | NOT STARTED |
262262+| `components/SecondaryNavComponent/SecondaryNavSegment.tsx` | (need to read) | NOT STARTED |
263263+| `components/SecondaryNavComponent/index.tsx` | (need to read) | NOT STARTED |
264264+| `components/SignalQualityIndicatorComponent.tsx` | (need to read) | NOT STARTED |
265265+| `components/TopNavComponent/PrimaryNavSegment.tsx` | (need to read) | NOT STARTED |
266266+| `components/TopNavComponent/index.tsx` | Grid, Segment, Image, Dropdown | NOT STARTED (also being changed in Phase 2) |
267267+268268+**Replacement mapping:**
269269+| Semantic UI | Replacement |
270270+|-------------|-------------|
271271+| `<Grid>` | `<div className="grid ...">` (Tailwind) |
272272+| `<Grid.Row>` | `<div className="flex ...">` or grid row |
273273+| `<Grid.Column width={N}>` | Tailwind `col-span-N` |
274274+| `<Segment>` | `<div className="p-4 ...">` or `<Card>` |
275275+| `<Button>` | `<Button>` from `./ui/button` |
276276+| `<Button.Group>` | `<div className="flex gap-1">` |
277277+| `<Header as="h1">` | `<h1 className="...">` |
278278+| `<Image src={x}>` | `<img src={x} />` |
279279+| `<Dropdown>` | `<DropdownMenu>` from `./ui/dropdown-menu` |
280280+| `<Modal>` | `<Dialog>` from `./ui/dialog` |
281281+| `<List>/<List.Item>` | `<ul>/<li>` |
282282+| `<Divider>` | `<hr />` or `<div className="my-4">` |
283283+| `<Checkbox>` | `<input type="checkbox" />` |
284284+| `<Icon name="x">` | inline SVG or FontAwesome (already installed) |
285285+| `<Sidebar>/<Sidebar.Pushable>` | `<div>` with conditional className |
286286+| `<Table>` etc. | `<Table>` from `./ui/table` or HTML table |
287287+| `DropdownProps`, `SemanticICONS` | Remove type imports; use plain React event types |
288288+289289+---
290290+291291+## Phase 5: Final Build Verification
292292+293293+**Status: PENDING — run `npm install` then `npm run build`**
294294+295295+- Run `npm run build` — should complete without errors
296296+- Run `npm test` — all Vitest suites pass
297297+- Run `npm run package` — Electron app packages successfully
298298+299299+---
300300+301301+## Dependency Change Summary
302302+303303+### To remove
304304+| Package | Location |
305305+|---------|----------|
306306+| `connected-react-router` | dependencies |
307307+| `history` | dependencies |
308308+| `react-router` | dependencies (merged into react-router-dom v6) |
309309+| `semantic-ui-react` | dependencies |
310310+| `semantic-ui-css` | dependencies |
311311+| `jest` | devDependencies |
312312+| `@types/jest` | devDependencies |
313313+| `@types/history` | devDependencies |
314314+| `@types/react-router` | devDependencies |
315315+| `@types/react-router-dom` | devDependencies |
316316+| `identity-obj-proxy` | devDependencies |
317317+| `react-test-renderer` | devDependencies |
318318+319319+### To add
320320+| Package | Location | Purpose |
321321+|---------|----------|---------|
322322+| `vitest` | devDependencies | test runner |
323323+| `@testing-library/react` | devDependencies | React test utilities |
324324+| `@testing-library/jest-dom` | devDependencies | DOM matchers |
325325+| `jsdom` | devDependencies | DOM env for tests |
326326+| `react-router-dom` (upgrade to v6) | dependencies | routing |
327327+| `tailwindcss` | devDependencies | CSS framework |
328328+| `postcss` | devDependencies | CSS processing |
329329+| `autoprefixer` | devDependencies | CSS vendor prefixes |
330330+| `@radix-ui/react-dialog` | dependencies | Dialog primitive |
331331+| `@radix-ui/react-dropdown-menu` | dependencies | Dropdown primitive |
332332+| `@radix-ui/react-slot` | dependencies | Slot primitive (shadcn) |
333333+| `class-variance-authority` | dependencies | CVA for Button variants |
334334+| `clsx` | dependencies | className utility |
335335+| `tailwind-merge` | dependencies | Tailwind class merging |
336336+337337+---
338338+339339+## Session Notes
340340+341341+### Session 1 (2026-03-07)
342342+- Read all relevant source files
343343+- Created this progress tracker
344344+- No code changes made yet
345345+- Ready to begin Phase 1 (Vitest) in next session
346346+347347+### Session 2 (2026-03-07)
348348+- Completed Phases 1–4 in full
349349+- Phase 1: Vitest config, test-setup.ts, App.test.tsx, CI workflow update
350350+- Phase 2: Removed connected-react-router, upgraded to React Router v6, NavigationTracker pattern, withNavigate HOC for HomeContainer + ExperimentDesignContainer, routerActions.ts
351351+- Phase 3: Pyodide version 0.21→0.27, fixed sns.tsplot with manual bootstrap CI in utils.py
352352+- Phase 4: Created Tailwind config, postcss config, shadcn/ui components (button, card, dialog, dropdown-menu, table, utils). Replaced all 26 semantic-ui-react component files. Removed semantic-ui import from app.global.css, added Tailwind directives.
353353+- Next step: Run `npm install` then `npm run build` to verify (Phase 5)
···11import React, { Component } from 'react';
22-import { Segment } from 'semantic-ui-react';
32import { ExperimentWindow } from './ExperimentWindow';
43import styles from './styles/collect.module.css';
54···3231 if (!this.props.isPreviewing) {
3332 return (
3433 <div className={styles.previewPlaceholder}>
3535- <Segment basic> The experiment will be shown in the window </Segment>
3434+ <div className="p-2"> The experiment will be shown in the window </div>
3635 </div>
3736 );
3837 }