Two dependency problems, and only one of them was real. ── Real: two majors of the same library ── The root asked for `react-resizable-panels@^4.0.15` (4.6.5) while `src/workspaces/components` asked for `^2.1.7` (2.1.9). The workspace code targets 2.x, whose API differs — v4 has no `PanelGroup` export, no `order` prop and no `ref` on `Panel` — so a file that resolved the root's copy failed to typecheck in a way that read like a broken extraction. Nothing imported the root's version. It was vestigial, and removing it collapses the repository to one: every importer now resolves 2.1.9 and the lockfile has zero v4 entries. ── Not real: the second React ── Declaring react/react-dom in the package created `src/workspaces/workspaces/node_modules/react`, which looks exactly like the duplicate-instance bug that silently breaks hooks. It is not one. Both locations resolve to the SAME file in bun's store — the local entry is a symlink — which I established by resolving the specifier from each place instead of reading directory names, having already reported it as a hazard on the strength of an `ls`. Declared as optional peers regardless: that is the right metadata for a consumer, and it stops bun installing peers it does not need to. Notes corrected to match, since the wrong version of that story was written down. tsgo clean, package 114/114, full suite 950 pass / 7 fail unchanged, frontend builds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
84 lines
4.7 KiB
Markdown
84 lines
4.7 KiB
Markdown
# Workspaces extraction — phase 1
|
|
|
|
Branch `workspaces-extraction`. The Workspace/Panel framework is now `src/workspaces/workspaces`, a package
|
|
that knows nothing about Officer.
|
|
|
|
## Where it got to
|
|
|
|
**The package renders under a bare React tree.** Its own test suite (114 tests) mounts `WorkspaceView` with
|
|
no provider of any kind — no react-query, no router, no Officer hooks. That is the proof: a host needs to
|
|
supply a registry and a layout, and nothing else.
|
|
|
|
**Two halves.** `core/` is the tree and every operation on it — pure, dependency-free, no React. `react/`
|
|
is one renderer. Phase 2 is a second renderer over the same core.
|
|
|
|
**Officer is unchanged.** All 24 `WorkspaceView` sites and 8 `WorkspaceLayout` sites still call exactly
|
|
what they called before. `officerdev/.../Workspace/index.ts` is now an adapter that supplies the three
|
|
things that are Officer's: the app registry, fullscreen persistence in sessionStorage, and the handshake
|
|
that hides the nav header for a fullscreen panel.
|
|
|
|
**Persistence was already injected.** `WorkspaceView` always received `{ key, value, setValue, isLoaded }`
|
|
as a prop and called `setValue` with an updater. The headline requirement of phase 1 was mostly already
|
|
true — the coupling was elsewhere.
|
|
|
|
**~175 lines of dead code deleted**: `DragOverlay`, `LayoutEditor`, `WorkspaceHeader`, referenced by
|
|
nothing.
|
|
|
|
## What actually had to move
|
|
|
|
| Coupling | Resolution |
|
|
|---|---|
|
|
| `useAppRegistry` | `registry` is now a required prop |
|
|
| `useSessionState` (fullscreen) | controlled/uncontrolled pair; Officer passes its sessionStorage state |
|
|
| `panel-fullscreen` / `useGlobal` | the package reports via callback; Officer acts |
|
|
| `use-panel-memory` / `useDashboardState` | stayed in Officer — it is host storage, not layout |
|
|
| `@/components/{Card,ErrorBoundary,ui/*}` | vendored into `react/internal/` |
|
|
| `hooks/useIsMobile` | vendored (27 lines) |
|
|
| `helpers/cn`, `@/lib/utils` | vendored (2 lines) |
|
|
|
|
## Two traps, both worth knowing before touching dependencies
|
|
|
|
**A second React — which turned out not to be one.** Declaring `react`/`react-dom` created
|
|
`src/workspaces/workspaces/node_modules/react`, and a directory of that name looks exactly like the
|
|
duplicate-instance bug that breaks hooks. It was not: both locations RESOLVE to the same file in bun's
|
|
store (`.bun/react@19.2.4/…/react/index.js`), because the local entry is a symlink. Checked by resolving
|
|
the specifier from both places rather than by reading directory names.
|
|
|
|
They are declared as `peerDependencies` with `peerDependenciesMeta.optional` anyway — correct metadata for
|
|
a consumer, and it keeps bun from installing peers it does not need to.
|
|
|
|
**Two versions of `react-resizable-panels` — real, and now fixed.** The root asked for `^4.0.15` (4.6.5)
|
|
while `src/workspaces/components` asked for `^2.1.7` (2.1.9), and this code targets 2.x, whose API differs
|
|
(v4 has no `PanelGroup` export, no `order` prop, no `ref` on `Panel`). Inheriting the root's resolution
|
|
produced a screenful of type errors that read like a broken extraction.
|
|
|
|
Nothing imported the root's copy — it was vestigial. Removed, so the repository now carries one version and
|
|
the lockfile has zero v4 entries.
|
|
|
|
## Open
|
|
|
|
**1. Panel-to-panel communication.** `usePanelChannel` is a two-line wrapper over `useGlobal`, so it is
|
|
react-query underneath. Six channels exist; three are genuine refresh signals (`files:refresh-signal`,
|
|
`preview:refresh`, `background-job:changed`) and two are SELECTION (`chat:selected-session`,
|
|
`chat:active-session`), which `docs/navigation-audit.md` says belongs in the URL. Deferred by decision: the
|
|
mechanics will live in the package, configured from outside, probably as callbacks. The real requirement
|
|
may be smaller than it looks.
|
|
|
|
**2. The package still speaks Officer's theme.** `PanelSlot` uses `duck-teal` classes and `--panel-*` CSS
|
|
variables. Harmless inside Officer, wrong for a standalone module: a consumer would need those variables
|
|
defined. Options are to accept a class-name map, ship a minimal stylesheet, or document the variables as
|
|
part of the contract.
|
|
|
|
**3. `cwd`/`root` are in the context.** They are Officer concepts (a workspace points at a directory) that
|
|
the package currently carries. Not harmful, not general.
|
|
|
|
**4. Phase 2.** A non-React renderer over `core`. What will not port is the resize *interaction* — drag
|
|
handles, pointer maths, constraints — supplied today by `react-resizable-panels`. Either find a vanilla
|
|
equivalent or write one; the tree logic comes across untouched, which was the point of the split.
|
|
|
|
## Verifying
|
|
|
|
`bun test src/workspaces/workspaces/` — 114 tests, no host.
|
|
Full suite unchanged at 950 pass / 7 fail. Screenshots: 9 of 16 byte-identical to before the extraction,
|
|
the rest being live data (chat sessions, system metrics) and the rotating wallpaper.
|