Skip to content

Workspaces POC: live-site assistant, previews, and sync#3526

Draft
dereksmart wants to merge 7 commits into
trunkfrom
try/workspaces
Draft

Workspaces POC: live-site assistant, previews, and sync#3526
dereksmart wants to merge 7 commits into
trunkfrom
try/workspaces

Conversation

@dereksmart
Copy link
Copy Markdown

Related issues

  • Related to the Workspaces v2 exploration / demo POC.

How AI was used in this PR

Codex was used to implement and iterate on this proof-of-concept branch across the workspace model, sidebar, shared shell, Dolly/Agenttic assistant surface, live preview, and sync setup flows. I reviewed and steered the implementation through each slice, including architecture pivots around workspace-level chats and target-specific tooling.

Proposed Changes

  • Introduce a workspace-first model that consolidates relevant Local, Production, and Staging targets into one Studio workspace.
  • Render one workspace row in the sidebar, with workspace-level recent chat history instead of separate target identities.
  • Add a shared workspace shell for local and remote-capable workspaces, including workspace-level tabs and target-aware content states.
  • Add Dolly support for live-site work, using workspace-level chats while passing explicit target context for Local, Staging, or Production work.
  • Incorporate the Agenttic UI direction for the assistant experience.
  • Add live preview chrome with target selection, browser controls, resizing, and local/remote preview handling.
  • Add workspace-level sync controls that lean on existing Studio local sync and WordPress.com Production/Staging sync mechanisms.
  • Add setup affordances when sync paths are missing, including local copy creation, site connection, staging-site creation, and eligibility states for unsupported/upgrade/permission cases.

This is intended as a demo / POC for the main product idea: anyone with a WordPress site should be able to do something useful with that site from Studio, whether they have a local site, a live site, staging, or some combination of those targets.

Not included, but feasible:

  • Getting the Studio code agent and Dolly working together as one coordinated local/live editing workflow.
  • Final production-grade backend contracts for all target-specific Dolly tools.
  • Full provisioning/progress UX for staging creation.
  • Final merge-ready design polish and complete end-to-end coverage for every preview/sync/assistant path.

Testing Instructions

For review/demo, enable the Workspaces feature flag and exercise these flows:

  • Open Studio with a mix of local-only, WP.com-only, production/staging, and linked local/remote sites.
  • Confirm the sidebar groups related targets into one workspace row.
  • Open a remote-only workspace and verify the workspace shell renders instead of the old no-local-sites empty state.
  • Switch preview targets between Local, Staging, and Production where available.
  • Send assistant messages against local and remote contexts and verify session continuity stays workspace-scoped.
  • Open the Sync tab for workspaces with missing links and verify setup actions appear only when eligible.
  • Run local/remote sync and Production/Staging sync flows in a safe test workspace.

Local verification run on this branch:

  • npx eslint --fix <modified files>
  • npm test -- apps/studio/src/components/tests/site-content-tabs.test.tsx apps/studio/src/stores/sync/staging-sync-slice.test.ts apps/studio/src/hooks/tests/use-add-site.test.tsx apps/studio/src/modules/sync/tests/index.test.tsx apps/studio/src/modules/workspaces/lib/build-studio-workspaces.test.ts apps/studio/src/components/tests/main-sidebar.test.tsx
  • npm run typecheck
  • git diff --check

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?

What changed.

Introduces the `enableWorkspaces` feature flag and a first-class `modules/workspaces` model layer. The new pure `buildStudioWorkspaces()` builder merges local Studio sites, WordPress.com sites, and connected-site metadata into one sorted workspace array with Local, Production, and Staging targets plus sync-link placeholders.

Extends `SyncSite` and the WP.com transform path to preserve staging relationship metadata: `productionSiteId`, `stagingSiteIds`, `isWpcomAtomic`, `canManageOptions`, `hasStagingSiteFeature`, and the raw `is_wpcom_staging_site` flag. This lets connected staging metadata attach to the production workspace even when the full WP.com site list does not include staging details.

Adds focused unit coverage for local-only, production-only, production/staging, local/remote combinations, connected staging with missing WP.com details, shared-local grouping, no duplicate local-backed workspace, no name-only grouping, and transform metadata preservation.

What stayed unchanged / what is intentionally deferred.

This slice does not add any sidebar, shell, preview, Dolly, sync/create, or routing UI. `enableWorkspaces` defaults off, so existing Studio behavior stays on the old local-site path.

Name-only grouping is intentionally omitted from the first model slice; grouping is limited to explicit production/staging relationships and shared local site IDs.

Tradeoffs and risks.

The model introduces new identity and grouping rules before there is UI consuming them. The main risk is incorrect workspace grouping when WP.com metadata is partial or inconsistent. The mitigation is keeping the builder pure and heavily covered with fixture-style unit tests, while preserving raw relationship metadata for later UI slices instead of inferring relationships from names.

Verification.

Verified with `npx eslint --fix <modified files>`, focused Vitest for the workspaces builder and WP.com transform tests, `npm run typecheck`, and `git diff --check`.
What changed.

Adds `useSidebarWorkspaces()` to gather local Studio sites, connected WordPress.com metadata, and fetched WP.com sites into one canonical workspace list for the sidebar when `enableWorkspaces` is on.

Updates `SiteMenu` so the flag-on path renders `sidebarWorkspaces.map(...)` instead of separate local and remote lists. Linked local plus production/staging workspaces render once, remote-only workspaces appear in the same sorted list, and local start/stop remains attached only to the Local target.

Adds workspace sidebar rows, accessible target indicators, and minimal per-workspace target selection state. Selecting Local still calls the existing `setSelectedSiteId()` path, while selecting Production or Staging updates workspace sidebar state only in this slice.

What stayed unchanged / what is intentionally deferred.

The flag-off sidebar path stays on the existing `sites.map(...)` behavior, including drag reorder and local start/stop controls.

Workspace rows disable drag reorder while the flag is on because remote-only rows do not yet have a persistence model. Remote content routing, shared shell UI, Dolly, preview, sync creation, and target creation remain deferred to later slices.

Tradeoffs and risks.

This slice creates a temporary split: the sidebar can select remote targets, but the content area still uses the old local-site surface until the shared shell lands. That is intentional to keep the PR reviewable and to avoid mixing list composition with content routing. The main risk is duplicate or inaccessible sidebar rows; focused tests cover duplicate prevention and translated target labels instead of letter-only indicators.

Verification.

Verified with `npx eslint --fix <modified files>`, `npm test -- apps/studio/src/components/tests/main-sidebar.test.tsx apps/studio/src/modules/workspaces/lib/build-studio-workspaces.test.ts`, `npm run typecheck`, and `git diff --check`.
What changed.

Adds a `WorkspaceSelectionProvider` that reuses the sidebar workspace loader, tracks the selected workspace and target, persists target-scoped tab selection, and keeps Local target selection wired to the existing Studio site selection.

Adds the shared workspace shell, header, target switcher, target-scoped tab model, and remote placeholder surfaces for Assistant, Sync, and Settings. Local targets continue to render the existing local tabs through the shared shell.

Updates `App`, `SiteMenu`, and `SiteContentTabs` so remote-only workspaces can render app content when workspaces are enabled. Adds shell-owned preview controls for workspace targets, including remote and local previews, resizable preview width, back/forward/reload/open/close controls, webview-based rendering, CSP frame allowance, and guarded webview history access after `dom-ready`.

What stayed unchanged / what is intentionally deferred.

The flag-off local site content path remains unchanged. Local Assistant still uses the existing `ContentTabAssistant`, and remote Assistant, Sync, and Settings are placeholders/details surfaces only.

This slice does not add Agenttic/Dolly transport, conversation state, staging creation, local target creation, or real sync/create actions. Preview is shell-owned and target-scoped, but Dolly preview tools are deferred.

Tradeoffs and risks.

The shell lands with non-mutating remote placeholder content so the layout and target-selection contract can be reviewed separately from Dolly and sync behavior. The largest risk is preview behavior inside Electron: remote and local targets need to render in-app without breaking renderer CSP or calling webview history APIs too early. The implementation uses the existing webview-enabled app boundary, adds the matching renderer `frame-src` allowance, and guards webview history reads until `dom-ready`.

Verification.

Verified with `npx eslint --fix <modified files>`, `npm test -- apps/studio/src/components/tests/site-content-tabs.test.tsx apps/studio/src/components/tests/main-sidebar.test.tsx apps/studio/src/components/tests/app.test.tsx apps/studio/src/modules/workspaces/lib/build-studio-workspaces.test.ts`, `npm run typecheck`, and `git diff --check`.
Replace the remote workspace Assistant placeholder with a Dolly-backed Agenttic chat while keeping Local targets on the existing ContentTabAssistant path.

Add target-scoped Dolly conversation/session state keyed by workspace, target, site, and conversation. This includes server history hydration, active turn tracking, target unread/thinking indicators, and preview/refresh abilities only for the selected remote target.

Add image upload support for Dolly chats using the selected target site media endpoint. Chat images render inline only for local blob/data previews or URLs whose host matches the active target site; other image links continue to open in preview.

Pin @automattic/agenttic-client and @automattic/agenttic-ui to 0.1.63, and add the PostCSS layer compatibility shim needed for Agenttic UI CSS.

Tradeoffs and risks: this slice intentionally uses Agenttic client/UI instead of adapting the existing local assistant UI because Dolly uses different transport, session, and history semantics. The main review risks are the new dependency and lockfile surface, the CSS/PostCSS compatibility shim, image CSP/rendering behavior, target-scoped session isolation, server history merge behavior, and hidden-target turn completion. Mitigations are exact dependency pins, keeping Local on the existing assistant path, limiting Dolly frontend tools to selected-target preview/open/refresh behavior, gating inline remote images to the active site hostname, and adding focused target-isolation tests.

Verification: eslint --fix on modified source files, focused Dolly/media tests, existing workspace regression tests, local assistant regression test, studio-app and workspace typechecks, scripts typecheck, electron-vite build, and git diff --check.
What changed.

Workspace selection now treats Local, Staging, and Production as capabilities inside one selected workspace instead of app/session partitions. The sidebar selects workspaces only, removes target switching, keeps local run controls attached to the Local target, and preserves workspace-level chat indicators and conversation state.

The shared workspace shell now uses the browser-bar target as the active environment context. The preview opens by default, supports resizing and back/forward/reload controls, keeps target selection in the browser chrome, normalizes stale preview URLs when switching targets, and keeps the tab IA stable for linked workspaces while showing local-only notices for Local-managed panels on remote targets.

Workspace Dolly chats are keyed by workspace conversation rather than target, while each message carries the currently selected preview target and URL context. Local target Assistant renders the legacy local assistant because Dolly does not support local Studio sites yet; remote targets continue to use the workspace Dolly assistant.

Local running state is updated immediately after start/stop events so the header button and sidebar status stay in sync with the site server.

What stayed unchanged / what is intentionally deferred.

The enableWorkspaces flag boundary remains intact: the flag-off local-site path keeps the existing sidebar and content behavior. The Slice 1 workspace model remains the source of target relationships.

Remote Sync and Settings are still lightweight workspace/target details surfaces. Real staging creation, sync actions, production mutation confirmation flows, and deeper remote settings management are deferred.

Dolly local-site tool support is not added in this checkpoint. Local work continues through the legacy local assistant until a proper local transport/tool bridge exists.

Tradeoffs and risks.

This is a large checkpoint commit that combines the workspace-level chat pivot with several UX and preview correctness fixes. That makes it useful for preserving branch progress, but it will likely need to be split or curated before review.

The Dolly backend remains WP.com site-scoped, so the selected remote site is still used as transport identity while workspace and target context are passed explicitly. Backend/tool support must honor that explicit target context before production-impacting operations are safe.

The remote content surfaces for local-only tabs are transitional notices rather than fully compatible remote implementations. This avoids misleading local context on live targets without designing every remote equivalent in this slice.

Verification.

corepack npx eslint --fix apps/studio/src/components/site-menu.tsx apps/studio/src/components/tests/main-sidebar.test.tsx apps/studio/src/components/tests/site-content-tabs.test.tsx apps/studio/src/hooks/tests/use-site-details.test.tsx apps/studio/src/hooks/use-site-details.tsx apps/studio/src/ipc-handlers.ts apps/studio/src/modules/workspaces/components/workspace-content-shell.tsx apps/studio/src/modules/workspaces/components/workspace-dolly-assistant.test.tsx apps/studio/src/modules/workspaces/components/workspace-dolly-assistant.tsx apps/studio/src/modules/workspaces/components/workspace-header.tsx apps/studio/src/modules/workspaces/components/workspace-preview.tsx apps/studio/src/modules/workspaces/components/workspace-sidebar-row.tsx apps/studio/src/modules/workspaces/components/workspace-target-indicators.tsx apps/studio/src/modules/workspaces/hooks/use-workspace-selection.tsx apps/studio/src/modules/workspaces/index.ts apps/studio/src/modules/workspaces/lib/dolly/api.ts apps/studio/src/modules/workspaces/lib/dolly/preview.ts apps/studio/src/modules/workspaces/lib/dolly/session.ts apps/studio/src/modules/workspaces/lib/dolly/transport.ts apps/studio/src/modules/workspaces/lib/dolly/turns.ts apps/studio/src/modules/workspaces/lib/dolly/types.ts apps/studio/src/modules/workspaces/lib/workspace-tabs.ts apps/studio/src/site-server.ts apps/studio/src/tests/site-server.test.ts

corepack npm test -- apps/studio/src/components/tests/main-sidebar.test.tsx apps/studio/src/components/tests/site-content-tabs.test.tsx apps/studio/src/modules/workspaces/components/workspace-dolly-assistant.test.tsx apps/studio/src/hooks/tests/use-site-details.test.tsx apps/studio/src/tests/site-server.test.ts

node "/Users/dsmart/.cache/node/corepack/v1/npm/11.13.0/bin/npm-cli.js" run typecheck --workspaces --if-present && ./node_modules/.bin/tsc -p scripts/tsconfig.json --noEmit

git diff --check
What changed.

Added a workspace Sync panel behind enableWorkspaces that renders Local <-> Production, Local <-> Staging, and Production <-> Staging rows from the selected StudioWorkspace targets instead of falling back to a generic empty state when links are missing.

Reused Studio's existing remote-site creation/pull flow through a new createSiteFromRemoteSite() helper so remote-only workspaces can create a local copy without duplicating the add-site workflow.

Added staging sync state and thunks for Production/Staging push, pull, sync-state polling, and staging-site creation through the WordPress.com staging API.

Extracted shared sync modal pieces and file-selection controls so workspace environment sync uses the same confirmation shape as the existing local sync dialog, including granular file selection where the API supports path sync.

Added setup-state handling for sites that cannot support local sync or staging creation: upgrade plan, enable hosting, missing permissions, unsupported, deleted, and already-connected states now show specific affordances instead of a misleading create/connect action.

What stayed unchanged / intentionally deferred.

The enableWorkspaces-off path and existing local sync behavior remain unchanged.

This does not replace the existing SyncConnectedSiteControls for local/remote sync; workspace rows delegate to them when a target is already connected.

This does not solve backend Production/Staging content-copy gaps observed in WordPress.com; Studio now calls the same environment sync endpoints and exposes supported file/database options.

Staging creation is started and the workspace list is refreshed, but detailed provisioning/progress UI is deferred.

Dolly/tool-driven sync actions and production mutation confirmation flows remain separate future work.

Tradeoffs and risks.

Local setup eligibility currently follows the existing SyncSite.syncSupport model. If WordPress.com exposes a more precise 'supports local copy' capability later, this UI should move to that explicit field.

Staging setup eligibility is derived from preserved WP.com metadata such as hasStagingSiteFeature, canManageOptions, and isWpcomAtomic. Missing metadata is treated optimistically to avoid hiding setup on older responses.

Granular Production/Staging file sync depends on Rewind file-tree path IDs being available for the source environment.

Verification.

npx eslint --fix apps/studio/src/hooks/use-add-site.ts apps/studio/src/hooks/tests/use-add-site.test.tsx apps/studio/src/modules/workspaces/hooks/use-workspace-selection.tsx apps/studio/src/modules/workspaces/hooks/use-sidebar-workspaces.ts apps/studio/src/modules/sync/components/workspace-sync-panel.tsx apps/studio/src/stores/sync/staging-sync-slice.ts apps/studio/src/stores/sync/staging-sync-slice.test.ts apps/studio/src/components/tests/site-content-tabs.test.tsx

npm test -- apps/studio/src/components/tests/site-content-tabs.test.tsx apps/studio/src/stores/sync/staging-sync-slice.test.ts apps/studio/src/hooks/tests/use-add-site.test.tsx apps/studio/src/modules/sync/tests/index.test.tsx apps/studio/src/modules/workspaces/lib/build-studio-workspaces.test.ts apps/studio/src/components/tests/main-sidebar.test.tsx

npm run typecheck

git diff --check
What changed.

- Reuse the shared Studio tab structure for workspace targets so Assistant stays in the same IA instead of using a separate remote-only tab set.
- Move the active Local/Staging/Production context into the workspace header and keep the URL bar focused on the preview URL.
- Add live-site overview and settings surfaces backed by WordPress.com site/theme/settings data.
- Rework workspace sync rows to reuse the connected-site sync presentation for local/live links and add the production/staging environment sync affordance in that same structure.
- Keep preview state workspace-scoped, default previews closed, preserve opened preview size across target switches, and prevent the closed preview controls from hiding the Assistant tab.
- Keep Dolly workspace chats active while passing the currently selected preview target context.

What stayed unchanged / intentionally deferred.

- The enableWorkspaces=false path stays on the existing local-site sidebar and content behavior.
- Local-only workflows remain backed by the existing local tab panels and legacy local assistant.
- Dolly still uses the existing remote transport shape; deeper target-aware backend routing is not solved here.
- This does not make live Import / Export or Previews fully functional; unsupported target content remains a workspace-shell affordance for now.

Tradeoffs and risks.

- The live overview/settings panels are read-oriented and depend on available WordPress.com metadata, so some fields may be absent or partial for specific sites.
- The production/staging sync UI leans on existing environment-sync APIs and status conventions instead of introducing a new workspace sync model.
- The closed preview URL bar reserves tab-strip space using a scoped CSS variable so the tab layout remains stable, but the final visual balance may still need product polish.

Verification.

- npx eslint --fix <staged modified files>
- npm test -- apps/studio/src/components/tests/main-sidebar.test.tsx apps/studio/src/components/tests/site-content-tabs.test.tsx apps/studio/src/modules/workspaces/components/workspace-dolly-assistant.test.tsx apps/studio/src/modules/workspaces/lib/build-studio-workspaces.test.ts
- npm run typecheck
- git diff --cached --check
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant